// Netiquette Indonesia Homepage — interactive shell // Renders the live regions (hero medallions, trust counters, solution tabs) // over static HTML. Modeled on websg/assets/js/app.jsx, trimmed to AMS+IMS only. const MODULES = [ { id: "ams", short: "AMS", name: "Akuntansi", color: "#2E7D32", tint: "#e8f5e9", icon: "ti-book", tag: "Accounting Management System", desc: "Kelola pembukuan bisnis Anda, pantau arus kas, dan hasilkan laporan keuangan secara real-time, tanpa perlu lagi mencocokkan spreadsheet secara manual.", features: ["Pembukuan double-entry lengkap", "Laporan keuangan detail (Laba Rugi, Neraca, dan lainnya)", "Invoicing & penagihan otomatis", "Terintegrasi langsung dengan modul Inventaris", "Visibilitas real-time atas kinerja keuangan bisnis"], img: "assets/module-ams.png" }, { id: "ims", short: "IMS", name: "Inventaris", color: "#C62828", tint: "#fce4ec", icon: "ti-package", tag: "Inventory Management System", desc: "Pantau stok secara real-time, kelola pembelian dan penjualan di semua lokasi, dan hindari selisih stok yang selama ini bikin repot dicatat manual.", features: ["Manajemen pembelian & penjualan", "Stok real-time di berbagai lokasi/gudang", "Dukungan lokasi, batch, kedaluwarsa, & matrix item", "Otomatis terhubung ke modul Akuntansi", "Laporan inventaris lengkap"], img: "assets/module-ims.png" }]; // ─────────────────────────────────────────────── // Hero carousel (brand orbit variant, AMS + IMS) // ─────────────────────────────────────────────── function HeroCarousel() { // Two modules only, placed left/right around the center hub. const positions = [Math.PI, 0]; // AMS = left, IMS = right return (
Netiquette
{MODULES.map((mod, idx) => { const ang = positions[idx]; const r = 42; const x = 50 + Math.cos(ang) * r; const y = 50 + Math.sin(ang) * r; return (
{mod.name}
{mod.name}
{mod.short}
); })}
1 Sistem. 2 Solusi Terintegrasi. Untuk UKM Indonesia.
); } // ─────────────────────────────────────────────── // Animated counter // ─────────────────────────────────────────────── function Counter({ to, suffix = "", duration = 1400 }) { const [val, setVal] = React.useState(0); const ref = React.useRef(null); const started = React.useRef(false); React.useEffect(() => { const el = ref.current; if (!el) return; const io = new IntersectionObserver((entries) => { entries.forEach((e) => { if (e.isIntersecting && !started.current) { started.current = true; const start = performance.now(); const tick = (now) => { const t = Math.min(1, (now - start) / duration); const eased = 1 - Math.pow(1 - t, 3); setVal(Math.round(to * eased)); if (t < 1) requestAnimationFrame(tick); }; requestAnimationFrame(tick); } }); }, { threshold: 0.3 }); io.observe(el); return () => io.disconnect(); }, [to]); const formatted = to >= 1000 ? val.toLocaleString() : val.toString(); return {formatted}{suffix}; } // ─────────────────────────────────────────────── // Solutions tabs (AMS / IMS) // ─────────────────────────────────────────────── function SolutionsTabs() { const [active, setActive] = React.useState(MODULES[0].id); const m = MODULES.find((x) => x.id === active); return ( <>
{MODULES.map((mod) => )}
{m.tag}

{m.name}

{m.desc}

Jelajahi {m.name}
{`${m.name}
); } // ─────────────────────────────────────────────── // Scroll reveal // ─────────────────────────────────────────────── function ScrollReveal() { React.useEffect(() => { const els = document.querySelectorAll(".reveal"); const io = new IntersectionObserver((entries) => { entries.forEach((e) => { if (e.isIntersecting) { e.target.classList.add("in"); io.unobserve(e.target); } }); }, { threshold: 0.12 }); els.forEach((el) => io.observe(el)); return () => io.disconnect(); }, []); return null; } // ─────────────────────────────────────────────── // Main app — mounts pieces into placeholders // ─────────────────────────────────────────────── function App() { return ( <> {/* Mount interactive regions via portals into placeholders in index.html */} {document.getElementById("hero-visual-mount") && ReactDOM.createPortal(, document.getElementById("hero-visual-mount"))} {document.getElementById("trust-mount") && ReactDOM.createPortal(
Tahun Pengalaman
Bisnis Dilayani
Singapura, Malaysia, Hong Kong, Thailand & Indonesia
Negara Termasuk Indonesia
Modul Terintegrasi (AMS & IMS)
Akses Cloud Kapan Saja
, document.getElementById("trust-mount") )} {document.getElementById("solutions-mount") && ReactDOM.createPortal(, document.getElementById("solutions-mount"))} ); } ReactDOM.createRoot(document.getElementById("app-root")).render();