(() => { const { Card, Select, Input, Field, Button, DataTable, StatusPill, Badge, DonutProgress, EmptyState, Avatar, Tabs, ProgressBar } = window.TaskFlowDesignSystem_dd53c2; const MODES = [{ id: "user", label: "Per User" }, { id: "project", label: "Per Project" }, { id: "category", label: "Per Kategori" }]; /* Rekap dihitung sekali per perubahan filter (useMemo) — satu lintasan array, tanpa request tambahan, supaya halaman tetap ringan. */ function rollup(rows, keyOfRow) { const map = new Map(); rows.forEach((t) => { const k = keyOfRow(t); if (!k) return; let g = map.get(k); if (!g) { g = { key: k, total: 0, done: 0, late: 0, running: 0, users: new Set(), sum: 0 }; map.set(k, g); } g.total++; g.sum += t.status === "done" ? 100 : t.progress || 0; g.users.add(t.user); if (t.status === "done") g.done++; else g.running++; if (t.done ? isOverdue(t.target, t.done) : isOverdue(t.target)) g.late++; }); return [...map.values()].map((g) => ({ ...g, users: g.users.size, avg: Math.round(g.sum / g.total) })).sort((a, b) => b.total - a.total); } function ReportScreen({ tasks = TASKS }) { const bp = useBreakpoint(); const md = monthRange(); const [userId, setUserId] = React.useState(""); const [from, setFrom] = React.useState(md.from); const [to, setTo] = React.useState(md.to); const [applied, setApplied] = React.useState({ from: md.from, to: md.to }); const [note, setNote] = React.useState(""); const [mode, setMode] = React.useState("user"); const rows = tasks.filter((t) => String(t.user) === userId && inRange(t.target, applied.from, applied.to)); const u = userId ? userById(Number(userId)) : null; const periodRows = React.useMemo(() => tasks.filter((t) => inRange(t.target, applied.from, applied.to)), [tasks, applied]); const groups = React.useMemo(() => (mode === "user" ? [] : rollup(periodRows, (t) => (mode === "project" ? (t.project || (periodRows.filter((x) => x.type === t.type).length > 1 ? t.type : null)) : t.category))), [periodRows, mode]); const donutSvg = (pct, color) => { const r = 54, c = 2 * Math.PI * r; return ` ${pct}%`; }; const printDoc = (doc, msg) => { const frame = document.createElement("iframe"); frame.style.cssText = "position:fixed;right:0;bottom:0;width:0;height:0;border:0"; document.body.appendChild(frame); frame.srcdoc = doc; frame.onload = () => { setTimeout(() => { frame.contentWindow.focus(); frame.contentWindow.print(); setTimeout(() => frame.remove(), 1500); }, 500); }; setNote(msg); }; const exportGroupPdf = () => { if (groups.length === 0) { setNote("Belum ada data pada rentang ini."); return; } const label = mode === "project" ? "Project" : "Kategori"; const bar = (v) => `
`; const tot = groups.reduce((a, g) => ({ total: a.total + g.total, done: a.done + g.done, late: a.late + g.late }), { total: 0, done: 0, late: 0 }); const stat = (l, v, c) => `
${l}
${v}
`; const rowsHtml = groups.map((g) => `
${g.key}
${g.total} task · ${g.users} user
${bar(g.avg)}
${g.avg}%
${g.done} ${g.running} ${g.late} `).join(""); printDoc(`Rekap ${label}

Rekap Capaian per ${label}

Periode ${applied.from} s/d ${applied.to} · dicetak ${TODAY}
${stat(label, groups.length, "#14201B")}${stat("Total task", tot.total, "#0FA582")}${stat("Selesai", tot.done, "#63A32E")}${stat("Terlambat", tot.late, "#DC2626")}
${rowsHtml}
${label}Rata-rata ProgressSelesaiBerjalanTerlambat
Klinik BBMC — Task Management System · Powered By Klinik BBMC 2026
`, `Rekap ${label.toLowerCase()} siap dicetak — pilih "Save as PDF" pada dialog cetak (${groups.length} baris).`); }; const exportPdf = () => { if (!u) { setNote("Pilih user terlebih dahulu sebelum export."); return; } const bar = (v, color) => `
`; const stat = (label, value, color) => `
${label}
${value}
`; const rowsHtml = rows.map((t) => { const late = t.done && isOverdue(t.target, t.done); return `
${t.type}
${t.category}
${late ? `
Alasan: ${t.delay || "-"}
` : ""} ${t.assigned} ${t.target} ${t.done || "—"} ${bar(t.status === "done" ? 100 : t.progress, "#13A88C")}
${t.status === "done" ? 100 : t.progress}%
${late ? "Terlambat" : String(t.status).replace(/_/g, " ")} `; }).join(""); const doc = `Report ${u.name}

Report Penyelesaian Task

Periode ${applied.from} s/d ${applied.to} · dicetak ${TODAY}
${donutSvg(progressOf(u.id), "#13A88C")}
${u.name}
${u.email} · ${u.role}
${stat("Total task", rows.length, "#14201B")} ${stat("Diterima", rows.filter((r) => r.status === "done").length, "#63A32E")} ${stat("Berjalan", rows.filter((r) => r.status === "in_progress").length, "#0FA582")} ${stat("Terlambat", rows.filter((r) => r.done && isOverdue(r.target, r.done)).length, "#DC2626")}
${rowsHtml || ''}
Jenis TaskDiberikanTargetSelesaiProgressStatus
Tidak ada task pada rentang ini.
Klinik BBMC — Task Management System · Powered By Klinik BBMC 2026
`; const frame = document.createElement("iframe"); frame.style.cssText = "position:fixed;right:0;bottom:0;width:0;height:0;border:0"; document.body.appendChild(frame); frame.srcdoc = doc; frame.onload = () => { setTimeout(() => { frame.contentWindow.focus(); frame.contentWindow.print(); setTimeout(() => frame.remove(), 1500); }, 500); }; setNote(`Report PDF dibuat — pilih "Save as PDF" pada dialog cetak (${rows.length} baris).`); }; const saveCsv = (name, table) => { const csv = table.map((r) => r.map((c) => `"${String(c).replace(/"/g, '""')}"`).join(",")).join("\n"); const url = URL.createObjectURL(new Blob(["\uFEFF" + csv], { type: "text/csv;charset=utf-8" })); const a = document.createElement("a"); a.href = url; a.download = name; document.body.appendChild(a); a.click(); a.remove(); setTimeout(() => URL.revokeObjectURL(url), 1000); }; const exportGroupCsv = () => { const label = mode === "project" ? "Project" : "Kategori"; saveCsv(`rekap-${mode}-${applied.from}_${applied.to}.csv`, [[label, "Total Task", "Selesai", "Berjalan", "Terlambat", "User Terlibat", "Rata-rata Progress"], ...groups.map((g) => [g.key, g.total, g.done, g.running, g.late, g.users, g.avg + "%"])]); setNote(`${groups.length} baris rekap ${label.toLowerCase()} diexport ke CSV.`); }; const exportCsv = () => { if (mode !== "user") return exportGroupCsv(); if (!u) { setNote("Pilih user terlebih dahulu sebelum export."); return; } const head = ["Jenis Task", "Kategori", "Diberikan", "Target", "Selesai", "Status", "Alasan Keterlambatan"]; const body = rows.map((t) => [t.type, t.category, t.assigned, t.target, t.done || "-", t.status, t.delay || "-"]); const csv = [head, ...body].map((r) => r.map((c) => `"${String(c).replace(/"/g, '""')}"`).join(",")).join("\n"); const url = URL.createObjectURL(new Blob(["\uFEFF" + csv], { type: "text/csv;charset=utf-8" })); const a = document.createElement("a"); a.href = url; a.download = `report-${u.name.toLowerCase().replace(/\s+/g, "-")}-${applied.from}_${applied.to}.csv`; document.body.appendChild(a); a.click(); a.remove(); setTimeout(() => URL.revokeObjectURL(url), 1000); setNote(`${rows.length} baris diexport ke CSV.`); }; return (
{ setMode(id); setNote(""); }} />
{mode === "user" &&
setFrom(e.target.value)} />
setTo(e.target.value)} />
{note && {note}}
{mode !== "user" && ( groups.length === 0 ? :
({ _highlight: g.late > 0, name: {g.key} {g.total} task , prog: {g.avg}% , done: {g.done}, run: {g.running}, late: g.late ? {g.late} : —, user: {g.users}, }))} />
)} {mode === "user" && !u && ( )} {mode === "user" && u && (
{u.name}
{u.email} · {u.role}
{rows.filter((r) => r.status === "done").length} selesai {rows.filter((r) => r.status === "in_progress").length} berjalan {rows.filter((r) => r.done && r.done > r.target).length} terlambat
{ const late = t.done && t.done > t.target; return { _highlight: late, task: ( {t.type} {late && Alasan: {t.delay}} ), target: {t.target}, done: {t.done || "—"}, dur: {t.status === "done" ? "9 hari" : "berjalan"}, st: late ? : , }; })} />
)} ); } Object.assign(window, { ReportScreen }); })();