(() => { const { Card, Badge, Button, AvatarStack, Avatar, ProgressBar, StatusPill, Icon, Modal, IconButton, Field, Input, Select } = window.TaskFlowDesignSystem_dd53c2; const PROJECT_CATEGORIES = ["Dokumen", "Operasional", "Kepegawaian", "Kegiatan", "Sarana & Prasarana", "Keuangan"]; /* Detail sheet for one project: who works on what, what they uploaded, the note they left for Admin, and — when the task bounced back — the revision points. */ const PRIO_TONE = { High: "coral", Medium: "gold", Low: "neutral" }; const PrioBadge = ({ value }) => (value ? {value} : null); function ProjectDetailModal({ project, onClose, onComment, member, meId, onStart }) { // preview = { task, file } — opened by the View button on an evidence row. const [preview, setPreview] = React.useState(null); if (!project) return null; const fmtKb = (n) => `${Math.max(1, Math.round(n / 1024))} KB`; const Viewer = window.PdfViewerModal; const imgSrc = (f) => fileSrc(f) || (f.file && typeof URL !== "undefined" ? URL.createObjectURL(f.file) : null); return ( {member && }}>
Project Prioritas berlaku untuk seluruh task di project ini. {project.tasks.map((t) => { const u = userById(t.user); const evidences = t.evidences && t.evidences.length ? t.evidences : t.evidence ? [t.evidence] : []; return (
{t.type} {u.name} · {u.role} {meId != null && meId !== 0 && t.user === meId && t.status === "todo" && onStart && ( )} {meId != null && meId !== 0 && t.user !== meId && ( Lihat saja )} {t.progress}%
Hasil yang diupload {evidences.length === 0 ? Belum ada dokumen diunggah. : evidences.map((f, i) => ( {f.name} {fmtKb(f.compressed || f.size)} ))} {t.submitted && Dikirim {t.submitted}} {t.versions && t.versions.length > 0 && ( Riwayat versi ({t.versions.length}) {t.versions.map((v) => ( v{v.round} {v.files[0].name}{v.note ? " · " + v.note : ""} {v.at} ))} )}
Catatan untuk Direktur {t.userNote || "Tidak ada catatan."} {t.delay && Alasan terlambat: {t.delay}}
{t.status === "revision" && (
Perlu Revisi {t.revision || "Poin revisi belum dituliskan."} {t.delegate != null && Dibantu {userById(t.delegate).name} · {t.delegateStatus === "done" ? "revisi selesai" : "sedang dikerjakan"}}
)}
); })}
{preview && preview.file.kind === "image" && imgSrc(preview.file) && ( setPreview(null)} title={preview.file.name} description={`${preview.task.type} · ${fmtKb(preview.file.compressed || preview.file.size)} setelah kompresi`} footer={}>
{preview.file.name}
)} {preview && !(preview.file.kind === "image" && imgSrc(preview.file)) && Viewer && ( setPreview(null)} /> )}
); } /* Projects are created automatically when one task is assigned to 2+ users (PRD §5.3). Progress stays per-user; the box shows the aggregate. */ function ProjectPanel({ tasks, onComment, commentCount, meId, onStart, onEditProject, onDeleteProject }) { const all = projectsFrom(tasks); const [detail, setDetail] = React.useState(null); const [edit, setEdit] = React.useState(null); // {project, form} const [remove, setRemove] = React.useState(null); // project yang akan dihapus const isAdmin = meId == null || meId === 0; // A user only sees the projects they were assigned to; Admin (id 0) sees all. const projects = meId == null || meId === 0 ? all : all.filter((p) => p.members.includes(meId)); if (projects.length === 0) return null; const open = detail ? projects.find((p) => p.name === detail) : null; return (

Project

{projects.length} Klik project untuk melihat detail per user
{projects.map((p) => { const member = meId == null || meId === 0 || p.members.includes(meId); return ( setDetail(p.name)}>
Project {p.category}
{p.name}
({ name: userById(id).name, src: avatarOf(id) }))} size={30} max={4} />
Progress project {p.doneCount}/{p.tasks.length} user selesai · {p.progress}%
{p.tasks.map((t) => { const u = userById(t.user); return (
{u.name}
); })}
Target {p.target} e.stopPropagation()}> {isAdmin && (onEditProject || onDeleteProject) && ( {onEditProject && setEdit({ project: p, form: { name: p.name, category: p.category, priority: p.priority || "Medium", target: p.target } })} />} {onDeleteProject && setRemove(p)} />} )}
); })}
{edit && ( setEdit(null)} title="Edit Project" description="Perubahan berlaku untuk seluruh task di dalam project ini." footer={<> }>
setEdit({ ...edit, form: { ...edit.form, name: e.target.value } })} />
setEdit({ ...edit, form: { ...edit.form, priority: e.target.value } })} options={["Low", "Medium", "High"]} />
setEdit({ ...edit, form: { ...edit.form, target: e.target.value } })} /> {edit.project.tasks.length} task milik {edit.project.members.length} user akan ikut diperbarui.
)} {remove && ( setRemove(null)} title="Hapus Project" description={`${remove.name} beserta ${remove.tasks.length} task di dalamnya akan dihapus.`} footer={<> }> Task yang sudah dikerjakan user ikut hilang dari daftar. Tindakan ini tercatat di Log Activity. )} {open && setDetail(null)} onComment={onComment} meId={meId} onStart={onStart} member={meId == null || meId === 0 || open.members.includes(meId)} />}
); } Object.assign(window, { ProjectPanel, ProjectDetailModal }); })();