"use client";
import { FormEvent, useEffect, useMemo, useState } from "react";
import DocumentBuilder from "./document-builder";
import FinanceDashboard from "./finance-dashboard";
import ProjectServices from "./project-services";
import BalanceSheet from "./balance-sheet";
import FinancialForecast from "./financial-forecast";
import { SERVICE_NAMES } from "./service-catalog";
import { ClientDirectory,CompactTransactions,StandardProfitReport } from "./operational-views";

type Client={id:number;company:string;contact:string;phone:string;email:string;address:string;taxId:string};
type Project={id:number;code:string;name:string;serviceType?:string;clientId:number|null;clientName?:string;contractValue:number;budget:number;progress:number;status:string;startDate:string;endDate:string};
type Entry={id:number;date:string;dueDate:string;documentNo:string;category:string;direction:string;description:string;amount:number;paidAmount:number;status:string;notificationDismissed:number;projectId:number|null;clientId:number|null;projectName?:string;clientName?:string;notes:string};
type Asset={id:number;code:string;name:string;category:string;acquisitionDate:string;acquisitionValue:number;usefulLife:number;condition:string;location:string};
type Projection={id:number;period:string;scenario:string;projectedIncome:number;projectedExpense:number;notes:string};
type Approval={id:number;reportType:"Neraca"|"Laba Rugi";period:string;status:string;approvedBy:string;approvedAt:string;notes:string};
type Session={username:string;role:"director"|"manager";name:string};
type Data={clients:Client[];projects:Project[];entries:Entry[];assets:Asset[];projections:Projection[];approvals:Approval[]};
const empty:Data={clients:[],projects:[],entries:[],assets:[],projections:[],approvals:[]};
const menus=[["dashboard","Ringkasan","⌂"],["transactions","Transaksi","↕"],["balance","Neraca","▤"],["offers","Buat Penawaran","⌑"],["billing","Tagihan & Kwitansi","▣"],["projects","Profit Proyek","◫"],["forecast","Proyeksi Keuangan","⌁"],["clients","Data Klien","♙"],["reports","Laporan Laba Rugi","▥"],["notifications","Notifikasi","●"]];
const rp=(n:number)=>new Intl.NumberFormat("id-ID",{style:"currency",currency:"IDR",maximumFractionDigits:0}).format(n||0);
const shortRp=(n:number)=>n>=1e9?`Rp ${(n/1e9).toFixed(1)} M`:n>=1e6?`Rp ${(n/1e6).toFixed(0)} jt`:rp(n);
const today=new Date().toISOString().slice(0,10); const fmt=(d:string)=>d?new Intl.DateTimeFormat("id-ID",{day:"2-digit",month:"short",year:"numeric"}).format(new Date(d+"T00:00:00")):"—";

export default function Home(){
 const[tab,setTab]=useState("dashboard"),[data,setData]=useState<Data>(empty),[loading,setLoading]=useState(true),[session,setSession]=useState<Session|null>(null),[authReady,setAuthReady]=useState(false),[modal,setModal]=useState<string|null>(null),[toast,setToast]=useState(""),[query,setQuery]=useState(""),[period,setPeriod]=useState("all");
 const reload=async()=>{setLoading(true);try{const r=await fetch("/api/finance",{cache:"no-store"});if(r.status===401){setSession(null);return}setData(await r.json())}finally{setLoading(false)}};
 useEffect(()=>{fetch("/api/auth",{cache:"no-store"}).then(async r=>{if(r.ok){const j=await r.json();setSession(j.user);await reload()}}).finally(()=>{setAuthReady(true);setLoading(false)})},[]);
 const post=async(kind:string,payload:Record<string,unknown>)=>{const r=await fetch("/api/finance",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({kind,...payload})});if(!r.ok)throw new Error((await r.json()).error||"Gagal menyimpan");await reload();setModal(null);setToast("Data berhasil disimpan ke database");setTimeout(()=>setToast(""),3000)};
 const remove=async(kind:"client"|"project",id:number,name:string)=>{if(!window.confirm(`Hapus ${name}? Data yang sudah terhubung dengan transaksi tidak dapat dihapus.`))return;const r=await fetch(`/api/finance?kind=${kind}&id=${id}`,{method:"DELETE"}),j=await r.json();if(!r.ok){setToast(j.error||"Gagal menghapus data");setTimeout(()=>setToast(""),4500);return}await reload();setToast(`${name} berhasil dihapus.`);setTimeout(()=>setToast(""),3000)};
 const deleteEntry=async(id:number,name:string)=>{if(!window.confirm(`Hapus transaksi ${name}?`))return;const r=await fetch(`/api/finance?kind=entry&id=${id}`,{method:"DELETE"}),j=await r.json();setToast(r.ok?"Transaksi berhasil dihapus.":j.error||"Gagal menghapus transaksi");if(r.ok)await reload();setTimeout(()=>setToast(""),4500)};
 const updateEntryStatus=async(entry:Entry,status:string)=>{const r=await fetch("/api/finance",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({kind:"entryStatus",id:entry.id,status,amount:entry.amount,paidAmount:entry.paidAmount})}),j=await r.json();setToast(r.ok?`Tahapan diperbarui menjadi ${status}.`:j.error||"Gagal memperbarui tahapan");if(r.ok)await reload();setTimeout(()=>setToast(""),3000)};
 const dismissNotification=async(id:number)=>{await fetch(`/api/finance?kind=notification&id=${id}`,{method:"DELETE"});await reload();setToast("Notifikasi dihapus tanpa menghapus transaksi sumber.");setTimeout(()=>setToast(""),3000)};
 const updateProject=async(id:number,payload:Record<string,unknown>)=>{const r=await fetch("/api/finance",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({kind:"projectUpdate",id,...payload})}),j=await r.json();setToast(r.ok?"Data proyek berhasil diperbarui.":j.error||"Gagal memperbarui proyek");if(r.ok)await reload();setTimeout(()=>setToast(""),3500);return r.ok};
 const approve=async(reportType:"Neraca"|"Laba Rugi",approvalPeriod:string)=>{const r=await fetch("/api/finance",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({kind:"approval",reportType,period:approvalPeriod})}),j=await r.json();setToast(r.ok?`${reportType} periode ${approvalPeriod} telah disetujui.`:j.error||"Persetujuan gagal");if(r.ok)await reload();setTimeout(()=>setToast(""),4000)};
 const logout=async()=>{await fetch("/api/auth",{method:"DELETE"});setSession(null);setData(empty);setTab("dashboard")};
 const now=new Date();const entries=useMemo(()=>data.entries.filter(e=>{const s=`${e.documentNo} ${e.description} ${e.clientName} ${e.projectName}`.toLowerCase().includes(query.toLowerCase());if(period==="all")return s;const d=new Date(e.date+"T00:00:00");if(period==="week"){const x=new Date(now);x.setDate(now.getDate()-7);return s&&d>=x}if(period==="month")return s&&d.getMonth()===now.getMonth()&&d.getFullYear()===now.getFullYear();return s&&d.getFullYear()===Number(period)}),[data.entries,query,period]);
 const income=data.entries.filter(e=>e.direction==="Masuk"&&e.category!=="Penawaran").reduce((a,e)=>a+e.paidAmount,0),expense=data.entries.filter(e=>e.direction==="Keluar").reduce((a,e)=>a+e.paidAmount,0);
 const receivable=data.entries.filter(e=>e.direction==="Masuk"&&["Invoice","Piutang"].includes(e.category)).reduce((a,e)=>a+Math.max(0,e.amount-e.paidAmount),0),payable=data.entries.filter(e=>e.direction==="Keluar"&&["Hutang","Tagihan"].includes(e.category)).reduce((a,e)=>a+Math.max(0,e.amount-e.paidAmount),0);
 const alerts=data.entries.filter(e=>e.dueDate&&!e.notificationDismissed&&!["Lunas","Ditolak","Telah Dibayarkan"].includes(e.status)).map(e=>({...e,days:Math.ceil((new Date(e.dueDate+"T00:00:00").getTime()-new Date(today+"T00:00:00").getTime())/86400000)})).filter(e=>e.days<=14).sort((a,b)=>a.days-b.days);
 const addKind=tab==="clients"?"client":tab==="projects"?"project":tab==="assets"?"asset":tab==="forecast"?"projection":"entry";
 if(!authReady)return <div className="auth-loading">Menyiapkan keamanan JLBB…</div>;
 if(!session)return <Login onLogin={async user=>{setSession(user);setAuthReady(true);await reload()}}/>;
 const visibleMenus=session.role==="director"?menus.filter(m=>["dashboard","balance","reports"].includes(m[0])):menus;
 return <div className="app-shell"><aside className="sidebar"><div className="brand"><img src="/logo.png" alt="Logo PT JLBB"/><div><strong>JLBB</strong><span>Finance & Profit Control</span></div></div><nav>{visibleMenus.map(([id,label,icon])=><button key={id} className={tab===id?"active":""} onClick={()=>setTab(id)}><i>{icon}</i><span>{label}</span>{id==="notifications"&&alerts.length>0&&<b>{alerts.length}</b>}</button>)}</nav><div className="side-foot"><span>{session.role==="director"?"DP":"MK"}</span><div><strong>{session.name}</strong><small>{session.role==="director"?"Direktur · Persetujuan":"Manager · Operasional"}</small></div><button className="logout-button" onClick={logout} title="Keluar">↪</button></div></aside>
 <main className="workspace"><header className="topbar"><div><p>JLBB Finance & Profit Control</p><h1>{menus.find(m=>m[0]===tab)?.[1]}</h1></div><div className="top-actions"><label className="search">⌕<input placeholder="Cari dokumen, klien, proyek..." value={query} onChange={e=>setQuery(e.target.value)}/></label>{session.role==="manager"&&<button className="bell" onClick={()=>setTab("notifications")}>♢{alerts.length>0&&<em>{alerts.length}</em>}</button>}{session.role==="manager"&&<button className="primary" onClick={()=>["offers","billing"].includes(tab)?window.dispatchEvent(new Event("open-document-builder")):setModal(addKind)}>＋ {["offers","billing"].includes(tab)?"Dokumen Baru":"Tambah Data"}</button>}</div></header>
 {loading?<div className="loading">Menghubungkan data keuangan…</div>:<div className="content">
 {tab==="dashboard"&&<FinanceDashboard data={data} income={income} expense={expense} receivable={receivable} payable={payable} alerts={alerts} go={setTab}/>} 
 {tab==="transactions"&&<CompactTransactions entries={data.entries} onDelete={deleteEntry}/>} 
 {tab==="offers"&&<DocumentBuilder mode="Penawaran" clients={data.clients} projects={data.projects}/>} 
 {tab==="billing"&&<DocumentBuilder mode="Tagihan" clients={data.clients} projects={data.projects}/>} 
 {tab==="balance"&&<BalanceSheet entries={data.entries} assets={data.assets} approvals={data.approvals} role={session.role} onApprove={approve} onStatus={updateEntryStatus} onAdd={kind=>setModal(kind)}/>} 
 {tab==="projects"&&<ProjectServices projects={data.projects} entries={data.entries} onDelete={remove} onEdit={updateProject}/>} {tab==="forecast"&&<FinancialForecast entries={data.entries} saved={data.projections}/>} {tab==="clients"&&<ClientDirectory items={data.clients} projects={data.projects} onDelete={remove}/>} {tab==="reports"&&<StandardProfitReport entries={data.entries} projects={data.projects} approvals={data.approvals} role={session.role} onApprove={approve}/>} {tab==="notifications"&&<Notifications alerts={alerts} onDismiss={dismissNotification}/>}</div>}</main>
 {modal&&<Modal kind={modal} data={data} close={()=>setModal(null)} save={post}/>} {toast&&<div className="toast">✓ {toast}</div>}</div>
}

function Login({onLogin}:{onLogin:(user:Session)=>Promise<void>}){const[username,setUsername]=useState(""),[password,setPassword]=useState(""),[error,setError]=useState(""),[busy,setBusy]=useState(false);const submit=async(e:FormEvent)=>{e.preventDefault();setBusy(true);setError("");try{const r=await fetch("/api/auth",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({username,password})}),j=await r.json();if(!r.ok)throw new Error(j.error||"Login gagal");await onLogin(j.user)}catch(x){setError(x instanceof Error?x.message:"Login gagal")}finally{setBusy(false)}};return <main className="login-page"><section className="login-card"><div className="login-brand"><img src="/logo.png" alt="PT JLBB"/><span>JLBB Finance & Profit Control</span><h1>Masuk ke sistem keuangan</h1><p>Akses data, dokumen, laporan, dan persetujuan sesuai peran akun.</p></div><form onSubmit={submit}><label><span>Nama pengguna</span><input autoFocus autoComplete="username" value={username} onChange={e=>setUsername(e.target.value)} placeholder="Masukkan nama pengguna"/></label><label><span>Kata sandi</span><input type="password" autoComplete="current-password" value={password} onChange={e=>setPassword(e.target.value)} placeholder="Masukkan kata sandi"/></label>{error&&<p className="login-error">{error}</p>}<button disabled={busy||!username||!password}>{busy?"Memeriksa…":"Masuk Aman"}</button><small>Sesi otomatis berakhir setelah 8 jam. Gunakan akun sesuai wewenang kerja.</small></form></section></main>}

function Dashboard({data,income,expense,receivable,payable,alerts,go}:{data:Data;income:number;expense:number;receivable:number;payable:number;alerts:(Entry&{days:number})[];go:(s:string)=>void}){
 const months=Array.from({length:6},(_,i)=>{const d=new Date();d.setMonth(d.getMonth()-5+i);const k=d.toISOString().slice(0,7);return{label:d.toLocaleDateString("id-ID",{month:"short"}),inc:data.entries.filter(e=>e.date.startsWith(k)&&e.direction==="Masuk").reduce((a,e)=>a+e.paidAmount,0),out:data.entries.filter(e=>e.date.startsWith(k)&&e.direction==="Keluar").reduce((a,e)=>a+e.paidAmount,0)}}),max=Math.max(1,...months.flatMap(m=>[m.inc,m.out]));
 return <><section className="welcome"><div><span>POSISI KEUANGAN TERKINI</span><h2>Kendali keuangan proyek dalam satu layar.</h2><p>Ringkasan otomatis dari transaksi, invoice, hutang/piutang, dan progres proyek.</p></div><div className="date-chip"><small>Per hari ini</small><strong>{fmt(today)}</strong></div></section><section className="kpi-grid"><Kpi tone="blue" icon="↗" label="Kas Masuk Tercatat" value={rp(income)} meta="Realisasi penerimaan"/><Kpi tone="orange" icon="↘" label="Kas Keluar Tercatat" value={rp(expense)} meta="Realisasi pengeluaran"/><Kpi tone="green" icon="◎" label="Piutang Berjalan" value={rp(receivable)} meta="Belum diterima"/><Kpi tone="red" icon="◇" label="Hutang Berjalan" value={rp(payable)} meta="Belum dibayar"/></section>
 <section className="dash-grid"><article className="panel chart-panel"><Head title="Arus Kas 6 Bulan" sub="Penerimaan dan pengeluaran"/><div className="bars">{months.map(m=><div className="bar-group" key={m.label}><div className="bar-pair"><span className="bar in" style={{height:`${Math.max(3,m.inc/max*150)}px`}} title={rp(m.inc)}/><span className="bar out" style={{height:`${Math.max(3,m.out/max*150)}px`}} title={rp(m.out)}/></div><small>{m.label}</small></div>)}</div><div className="legend"><i className="in"/>Masuk <i className="out"/>Keluar</div></article><article className="panel"><div className="panel-head"><Head title="Jatuh Tempo Terdekat" sub="Perlu ditindaklanjuti"/><button className="link" onClick={()=>go("notifications")}>Lihat semua</button></div><div className="alert-list">{alerts.slice(0,4).map(a=><div className="alert-row" key={a.id}><span className={a.days<0?"late":a.days<=3?"soon":"normal"}>{a.days<0?"!":a.days}</span><div><strong>{a.description}</strong><small>{a.clientName||a.projectName||a.category} • {fmt(a.dueDate)}</small></div><b>{shortRp(a.amount-a.paidAmount)}</b></div>)}{!alerts.length&&<Empty text="Tidak ada jatuh tempo dekat."/>}</div></article></section>
 <section className="dash-grid bottom"><article className="panel"><div className="panel-head"><Head title="Kinerja Proyek" sub="Kontrak aktif dan progres"/><button className="link" onClick={()=>go("projects")}>Detail</button></div><div className="project-mini">{data.projects.slice(0,4).map(p=><div key={p.id}><div><strong>{p.name}</strong><span>{p.progress}%</span></div><div className="progress"><i style={{width:`${p.progress}%`}}/></div><small>{p.code} • {shortRp(p.contractValue)}</small></div>)}{!data.projects.length&&<Empty text="Tambahkan proyek pertama."/>}</div></article><article className="panel quick"><h3>Aksi Cepat</h3><div>{[["offers","⌑","Penawaran"],["billing","▣","Invoice"],["clients","♙","Klien"],["reports","▤","Laporan"]].map(x=><button key={x[0]} onClick={()=>go(x[0])}>{x[1]}<span>{x[2]}</span></button>)}</div></article></section></>
}
function Head({title,sub}:{title:string;sub:string}){return <div><h3>{title}</h3><p>{sub}</p></div>}function Kpi({tone,icon,label,value,meta}:{tone:string;icon:string;label:string;value:string;meta:string}){return <article className={`kpi ${tone}`}><span>{icon}</span><div><p>{label}</p><h3>{value}</h3><small>{meta}</small></div></article>}
function PageHead({title,subtitle,period,setPeriod}:{title:string;subtitle:string;period:string;setPeriod:(s:string)=>void}){return <div className="page-head"><div><h2>{title}</h2><p>{subtitle}</p></div><div className="filters"><select value={period} onChange={e=>setPeriod(e.target.value)}><option value="all">Semua periode</option><option value="week">7 hari terakhir</option><option value="month">Bulan berjalan</option><option value="2026">Tahun 2026</option><option value="2025">Tahun 2025</option></select><button onClick={()=>window.print()}>⇩ Cetak / PDF</button></div></div>}
function Entries({title,subtitle,entries,period,setPeriod}:{title:string;subtitle:string;entries:Entry[];period:string;setPeriod:(s:string)=>void}){return <section className="panel page-panel"><PageHead title={title} subtitle={subtitle} period={period} setPeriod={setPeriod}/><div className="table-wrap"><table><thead><tr><th>Tanggal / Dokumen</th><th>Uraian</th><th>Klien / Proyek</th><th>Nilai</th><th>Terbayar</th><th>Sisa</th><th>Status</th></tr></thead><tbody>{entries.map(e=><tr key={e.id}><td><strong>{fmt(e.date)}</strong><small>{e.documentNo||"Tanpa nomor"}</small></td><td><strong>{e.description}</strong><small>{e.category}</small></td><td>{e.clientName||"—"}<small>{e.projectName||"Umum"}</small></td><td>{rp(e.amount)}</td><td>{rp(e.paidAmount)}</td><td className={e.amount-e.paidAmount>0?"money-due":""}>{rp(Math.max(0,e.amount-e.paidAmount))}</td><td><Badge text={e.status}/></td></tr>)}{!entries.length&&<tr><td colSpan={7}><Empty text="Belum ada data pada periode ini."/></td></tr>}</tbody></table></div></section>}
function Projects({projects,entries}:{projects:Project[];entries:Entry[]}){return <section><div className="page-title"><h2>Profitabilitas Proyek</h2><p>Pendapatan, biaya aktual, dan estimasi laba per pekerjaan</p></div><div className="cards-grid">{projects.map(p=>{const inc=entries.filter(e=>e.projectId===p.id&&e.direction==="Masuk").reduce((a,e)=>a+e.paidAmount,0),out=entries.filter(e=>e.projectId===p.id&&e.direction==="Keluar").reduce((a,e)=>a+e.paidAmount,0);return <article className="project-card" key={p.id}><div className="project-top"><span>{p.code}</span><Badge text={p.status}/></div><h3>{p.name}</h3><p>{p.clientName||"Tanpa klien"}</p><div className="metric-row"><div><small>Nilai kontrak</small><strong>{shortRp(p.contractValue)}</strong></div><div><small>Laba kas</small><strong className={inc-out>=0?"positive":"negative"}>{shortRp(inc-out)}</strong></div></div><div className="progress"><i style={{width:`${p.progress}%`}}/></div><div className="project-foot"><span>Progres {p.progress}%</span><span>Target {fmt(p.endDate)}</span></div></article>})}{!projects.length&&<Empty text="Belum ada proyek."/>}</div></section>}
function Forecast({items}:{items:Projection[]}){const max=Math.max(1,...items.flatMap(p=>[p.projectedIncome,p.projectedExpense]));return <section className="panel page-panel"><div className="page-head"><div><h2>Rencana & Proyeksi Keuangan</h2><p>Skenario penerimaan, pengeluaran, dan saldo proyeksi</p></div></div><div className="forecast-summary">{items.map(p=><article key={p.id}><div><span>{p.period}</span><Badge text={p.scenario}/></div><div className="forecast-bars"><i className="income" style={{width:`${p.projectedIncome/max*100}%`}}/><i className="expense" style={{width:`${p.projectedExpense/max*100}%`}}/></div><div><small>Pendapatan {rp(p.projectedIncome)}</small><small>Biaya {rp(p.projectedExpense)}</small><strong>Surplus {rp(p.projectedIncome-p.projectedExpense)}</strong></div><p>{p.notes}</p></article>)}</div>{!items.length&&<Empty text="Tambahkan proyeksi keuangan."/>}</section>}
function Assets({items}:{items:Asset[]}){return <section className="panel page-panel"><div className="page-head"><div><h2>Register Aset & Aktiva</h2><p>Inventaris, nilai perolehan, kondisi, dan lokasi aset</p></div><div className="asset-total"><small>Total nilai perolehan</small><strong>{rp(items.reduce((a,x)=>a+x.acquisitionValue,0))}</strong></div></div><div className="asset-grid">{items.map(a=>{const years=Math.max(0,(Date.now()-new Date(a.acquisitionDate).getTime())/31557600000),book=Math.max(0,a.acquisitionValue*(1-years/a.usefulLife));return <article key={a.id}><div><span>{a.code}</span><Badge text={a.condition}/></div><h3>{a.name}</h3><p>{a.category} • {a.location}</p><dl><div><dt>Nilai perolehan</dt><dd>{rp(a.acquisitionValue)}</dd></div><div><dt>Estimasi nilai buku</dt><dd>{rp(book)}</dd></div><div><dt>Tanggal perolehan</dt><dd>{fmt(a.acquisitionDate)}</dd></div></dl></article>})}</div>{!items.length&&<Empty text="Belum ada aset tercatat."/>}</section>}
function Clients({items,projects}:{items:Client[];projects:Project[]}){return <section className="panel page-panel"><div className="page-head"><div><h2>Database Klien & Perusahaan</h2><p>Rekaman kontak, alamat, dan portofolio pekerjaan</p></div><span className="count-chip">{items.length} perusahaan</span></div><div className="client-grid">{items.map(c=><article key={c.id}><div className="avatar">{c.company.replace("PT.","").trim().slice(0,2).toUpperCase()}</div><div><h3>{c.company}</h3><p>{c.contact||"Kontak belum diisi"}</p><small>{c.phone||"—"} • {c.email||"—"}</small><span>{projects.filter(p=>p.clientId===c.id).length} proyek tercatat</span></div></article>)}</div>{!items.length&&<Empty text="Belum ada data klien."/>}</section>}
function Reports({entries,projects,period,setPeriod}:{entries:Entry[];projects:Project[];period:string;setPeriod:(s:string)=>void}){const rev=entries.filter(e=>e.direction==="Masuk"&&e.category!=="Penawaran").reduce((a,e)=>a+e.paidAmount,0),cost=entries.filter(e=>e.direction==="Keluar").reduce((a,e)=>a+e.paidAmount,0);return <section className="report-sheet"><div className="letterhead"><img src="/logo.png" alt="PT JLBB"/><div><h2>PT. JASA LINGKUNGAN BAHARI BATAM</h2><p>Komplek Ruko Golden City Blok RI 17, Batam, Kepulauan Riau</p></div></div><PageHead title="Laporan Laba Rugi" subtitle="Ringkasan berbasis transaksi pada periode terpilih" period={period} setPeriod={setPeriod}/><div className="report-meta"><span>Periode: {period==="all"?"Seluruh transaksi":period}</span><span>Tanggal cetak: {fmt(today)}</span></div><table className="profit-table"><tbody><tr><th>PENDAPATAN</th><td/></tr><tr><td>Penerimaan proyek dan pendapatan lain</td><td>{rp(rev)}</td></tr><tr className="total"><td>Total Pendapatan</td><td>{rp(rev)}</td></tr><tr><th>BEBAN OPERASIONAL & PROYEK</th><td/></tr><tr><td>Biaya proyek dan operasional terbayar</td><td>({rp(cost)})</td></tr><tr className="grand"><td>LABA / (RUGI) BERSIH</td><td className={rev-cost>=0?"positive":"negative"}>{rp(rev-cost)}</td></tr></tbody></table><div className="report-projects"><h3>Lampiran Kinerja Proyek</h3>{projects.map(p=><div key={p.id}><span>{p.code} — {p.name}</span><span>{p.progress}%</span><span>{rp(p.contractValue)}</span></div>)}</div><div className="signature"><div><p>Disusun oleh,</p><br/><br/><strong>Bagian Keuangan</strong></div><div><p>Batam, {fmt(today)}<br/>Disetujui oleh,</p><br/><strong>Andika Pratama, ST</strong><small>Direktur</small></div></div></section>}
function Notifications({alerts,onDismiss}:{alerts:(Entry&{days:number})[];onDismiss:(id:number)=>void}){return <section className="panel page-panel"><div className="page-head"><div><h2>Pusat Notifikasi Jatuh Tempo</h2><p>Pengingat transaksi, tagihan, hutang, dan piutang</p></div><span className="count-chip warning">{alerts.length} perlu perhatian</span></div><div className="notification-list">{alerts.map(a=><article key={a.id} className={a.days<0?"overdue":""}><span>{a.days<0?"TERLAMBAT":a.days===0?"HARI INI":`${a.days} HARI LAGI`}</span><div><h3>{a.description}</h3><p>{a.documentNo} • {a.clientName||a.projectName||"Transaksi umum"}</p></div><div><strong>{rp(a.amount-a.paidAmount)}</strong><small>Jatuh tempo {fmt(a.dueDate)}</small><button className="dismiss-btn" onClick={()=>onDismiss(a.id)}>Hapus Notifikasi</button></div></article>)}{!alerts.length&&<Empty text="Semua transaksi aman."/>}</div></section>}
function Badge({text}:{text:string}){const cls=/lunas|selesai|disetujui|baik/i.test(text)?"ok":/terlambat|ditolak|rusak/i.test(text)?"bad":/sebagian|proses|menunggu|moderat/i.test(text)?"wait":"neutral";return <span className={`badge ${cls}`}>{text}</span>}function Empty({text}:{text:string}){return <div className="empty"><span>◎</span><p>{text}</p></div>}

function Modal({kind,data,close,save}:{kind:string;data:Data;close:()=>void;save:(k:string,p:Record<string,unknown>)=>Promise<void>}){const[saving,setSaving]=useState(false),[error,setError]=useState("");const submit=async(e:FormEvent<HTMLFormElement>)=>{e.preventDefault();setSaving(true);setError("");try{await save(kind,Object.fromEntries(new FormData(e.currentTarget).entries()))}catch(x){setError(x instanceof Error?x.message:"Gagal menyimpan");setSaving(false)}};return <div className="modal-backdrop" onMouseDown={e=>e.target===e.currentTarget&&close()}><form className="modal" onSubmit={submit}><div className="modal-head"><div><span>FORMULIR DATABASE</span><h2>Tambah {kind==="entry"?"Catatan Keuangan":kind==="client"?"Klien":kind==="project"?"Proyek":kind==="asset"?"Aset":"Proyeksi"}</h2></div><button type="button" onClick={close}>×</button></div><div className="form-grid">
 {kind==="entry"&&<><Field label="Tanggal" name="date" type="date" value={today}/><Field label="Jatuh tempo" name="dueDate" type="date"/><label><span>Nomor transaksi</span><input value="Otomatis saat disimpan" disabled/></label><Select label="Kategori" name="category" options={["Penerimaan","Pengeluaran","Penawaran","Invoice","Piutang","Hutang","Tagihan"]}/><Select label="Arah transaksi" name="direction" options={["Masuk","Keluar"]}/><Select label="Status" name="status" options={["Draft","Diajukan","Menunggu","Disetujui","Sebagian","Lunas","Ditolak"]}/><Field wide label="Uraian" name="description" required/><Field label="Nilai transaksi" name="amount" type="number" required/><Field label="Sudah dibayar" name="paidAmount" type="number" value="0"/><Select label="Klien" name="clientId" options={["",...data.clients.map(c=>`${c.id}|${c.company}`)]}/><Select label="Proyek" name="projectId" options={["",...data.projects.map(p=>`${p.id}|${p.name}`)]}/><Field wide label="Catatan" name="notes"/></>}
 {kind==="client"&&<><Field wide label="Nama perusahaan" name="company" required/><Field label="Nama kontak / PIC" name="contact"/><Field label="Telepon / WhatsApp" name="phone"/><Field label="Email" name="email" type="email"/><Field label="NPWP" name="taxId"/><Field wide label="Alamat" name="address"/></>}
 {kind==="project"&&<><label><span>Kode proyek</span><input value="Otomatis saat disimpan" disabled/></label><Select label="Klien" name="clientId" options={["",...data.clients.map(c=>`${c.id}|${c.company}`)]}/><Select wide label="Jenis layanan proyek" name="serviceType" options={["",...SERVICE_NAMES]}/><Field wide label="Nama proyek" name="name" required/><Field label="Nilai kontrak" name="contractValue" type="number"/><Field label="Anggaran biaya" name="budget" type="number"/><Field label="Progres (%)" name="progress" type="number" value="0"/><Select label="Status" name="status" options={["Perencanaan","Berjalan","Tertunda","Selesai","Berhenti / Tidak Dilanjutkan"]}/><Field label="Mulai" name="startDate" type="date"/><Field label="Target selesai" name="endDate" type="date"/></>}
 {kind==="asset"&&<><label><span>Kode aset</span><input value="Otomatis saat disimpan" disabled/></label><Field label="Nama aset" name="name" required/><Select label="Kategori" name="category" options={["Kendaraan","Peralatan Kantor","Peralatan Lapangan","Komputer & Elektronik","Aktiva Lain"]}/><Field label="Tanggal perolehan" name="acquisitionDate" type="date" required/><Field label="Nilai perolehan" name="acquisitionValue" type="number"/><Field label="Umur manfaat (tahun)" name="usefulLife" type="number" value="4"/><Select label="Kondisi" name="condition" options={["Baik","Perlu Perawatan","Rusak"]}/><Field label="Lokasi" name="location" value="Kantor JLBB"/></>}
 {kind==="projection"&&<><Field label="Periode" name="period" type="month" required/><Select label="Skenario" name="scenario" options={["Konservatif","Moderat","Agresif"]}/><Field label="Proyeksi pendapatan" name="projectedIncome" type="number"/><Field label="Proyeksi pengeluaran" name="projectedExpense" type="number"/><Field wide label="Asumsi atau catatan" name="notes"/></>}
 </div>{error&&<p className="form-error">{error}</p>}<div className="modal-actions"><button type="button" onClick={close}>Batal</button><button className="primary" disabled={saving}>{saving?"Menyimpan…":"Simpan ke Database"}</button></div></form></div>}
function Field({label,name,type="text",value,placeholder,required,wide}:{label:string;name:string;type?:string;value?:string;placeholder?:string;required?:boolean;wide?:boolean}){return <label className={wide?"wide":""}><span>{label}{required&&" *"}</span><input name={name} type={type} defaultValue={value} placeholder={placeholder} required={required}/></label>}function Select({label,name,options,wide}:{label:string;name:string;options:string[];wide?:boolean}){return <label className={wide?"wide":""}><span>{label}</span><select name={name}>{options.map(o=>{const[v,t]=o.includes("|")?o.split("|"):[o,o||"— Pilih —"];return <option key={o} value={v}>{t}</option>})}</select></label>}

