// web-owner.jsx — Pemilik: dashboard, karyawan, laporan (live data) const { useState: useSO, useEffect: useEO } = React; function BarsO({ data, max = 8, height = 92 }) { return (
{data.map((v, i) => (
= max - 1 ? 'var(--accent)' : v <= max - 3 ? 'var(--warn)' : 'color-mix(in srgb, var(--accent) 55%, var(--surface-3))', minHeight: 4, transition: 'height .8s cubic-bezier(.2,.8,.2,1)' }}>
))}
); } function OwnerHeaderW({ title, sub, onLogout }) { return (
{title}
{sub}
); } function WebDashboard({ data, onLogout, onOpenEmp }) { if (!data) return
; const c = data.counts, total = data.today.length, hadirTotal = (c.hadir || 0) + (c.telat || 0); const pct = total ? Math.round((hadirTotal / total) * 100) : 0; const feed = data.today.filter(t => t.in && t.in !== '—').sort((a, b) => a.in.localeCompare(b.in)); const countCard = (label, val, status) => (
{val || 0}
{label}
); return (
Kehadiran Hari Ini
{hadirTotal} dari {total} karyawan
Update {MA.hhmm()}
{c.telat || 0} Telat {c.izin || 0} Izin {c.absen || 0} Absen
{countCard('Tepat waktu', c.hadir, 'hadir')}{countCard('Terlambat', c.telat, 'telat')} {countCard('Izin / Sakit', c.izin, 'izin')}{countCard('Tidak hadir', c.absen, 'absen')}
Tren Kehadiran
{data.monthLabel} · per hari kerja
Aktivitas Hari Ini
{feed.length} check-in
{feed.length === 0 &&
Belum ada yang absen hari ini
} {feed.map((a, i) => (
onOpenEmp(a.id)} style={{ cursor: 'pointer' }}>
{a.name}
{a.loc}
{a.in}
))}
); } function WebKaryawan({ report, today, onLogout, onOpenEmp }) { const [q, setQ] = useSO(''); const rows = (report ? report.perEmployee : []).map(e => ({ ...e, status: (today.find(t => t.id === e.id) || {}).status || 'belum' })); const list = rows.filter(e => e.name.toLowerCase().includes(q.toLowerCase()) || e.jabatan.toLowerCase().includes(q.toLowerCase())); return (
setQ(e.target.value)} placeholder="Cari nama atau jabatan…" style={{ border: 'none', outline: 'none', background: 'transparent', flex: 1, fontSize: 14, fontWeight: 500, color: 'var(--text)', fontFamily: 'inherit' }} />
{list.map(e => (
onOpenEmp(e.id)} style={{ cursor: 'pointer', padding: 15 }}>
{e.name}
{e.jabatan}
Kehadiran bulan ini{e.pct}%
= 95 ? 'var(--success)' : e.pct >= 90 ? 'var(--accent)' : 'var(--warn)'} />
))}
); } function WebDetail({ emp, today, onBack }) { const t = today.find(x => x.id === emp.id) || {}; return (
Detail Karyawan
{emp.name}
{emp.jabatan} · {emp.id}
Catatan Hari Ini
{[['clock', 'var(--success)', 'Jam masuk', t.in || '—'], ['logout', 'var(--accent)', 'Jam pulang', t.out || '—'], ['pin', 'var(--text-mute)', 'Lokasi', t.loc || '—']].map(([ic, col, lab, val], i) => ( {i > 0 &&
}
{lab}{val}
))}
); } function WebLaporan({ report, onLogout }) { const [sheet, setSheet] = useSO(false); const [toast, setToast] = useSO(null); if (!report) return
; const flash = (m) => { setToast(m); setTimeout(() => setToast(null), 2800); }; const doExport = (kind) => { setSheet(false); if (kind === 'Excel') { if (MA.CONFIG.SHEET_URL) { window.open(MA.CONFIG.SHEET_URL, '_blank'); flash('Membuka Google Sheet…'); } else flash('Atur SHEET_URL di api.js untuk membuka Sheet'); } else { window.print(); } }; return (
Kehadiran Harian
{report.totHadir}/{report.totHari} hari-orang
Rekap Per Karyawan
{report.perEmployee.map(e => (
{e.name}
{e.hadir}/{e.total} hari · {e.hours}j · lembur {e.ot}j
= 95 ? 'var(--success)' : e.pct >= 90 ? 'var(--text)' : 'var(--warn)' }}>{e.pct}%
{e.late > 0 &&
{e.late}x telat
}
))}
{sheet && (
setSheet(false)}>
e.stopPropagation()}>
Export Laporan
Rekap {report.monthLabel} · {report.perEmployee.length} karyawan
)} {toast && {toast}}
); } function WebOwner({ user, onLogout }) { const [tab, setTab] = useSO('dashboard'); const [dash, setDash] = useSO(null); const [report, setReport] = useSO(null); const [empId, setEmpId] = useSO(null); useEO(() => { MA.dashboard().then(r => r.ok && setDash(r)); MA.report().then(r => r.ok && setReport(r)); }, []); const today = dash ? dash.today : []; const empObj = report && empId ? report.perEmployee.find(e => e.id === empId) : null; const tabs = [{ id: 'dashboard', icon: 'grid', label: 'Dashboard' }, { id: 'karyawan', icon: 'users', label: 'Karyawan' }, { id: 'laporan', icon: 'chart', label: 'Laporan' }]; if (empObj) return setEmpId(null)} />; return ( <>
{tab === 'dashboard' && } {tab === 'karyawan' && } {tab === 'laporan' && }
); } const sheetBtn = { display: 'flex', alignItems: 'center', gap: 13, padding: 14, borderRadius: 14, background: 'var(--surface-2)', border: '1px solid var(--border)' }; const sheetIco = { width: 42, height: 42, borderRadius: 11, display: 'flex', alignItems: 'center', justifyContent: 'center' }; window.WebOwner = WebOwner;