(() => {
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 ``;
};
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) => ``;
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")}
| ${label} | Rata-rata Progress | Selesai | Berjalan | Terlambat |
${rowsHtml}
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) => ``;
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")}
| Jenis Task | Diberikan | Target | Selesai | Progress | Status |
${rowsHtml || '| 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" &&
}
{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 });
})();