(() => { const { Modal, Button, Badge, Icon, IconButton, Field, Input } = window.TaskFlowDesignSystem_dd53c2; /* Mock PDF page — same chrome as the viewer, but it accepts a signature stamp. Admin clicks (or drags) to place the PNG signature uploaded once in Profile. */ function SignPdfModal({ open, task, signature, readOnly, onClose, onSign }) { const [pdfPages, setPdfPages] = React.useState(0); const [pageSize, setPageSize] = React.useState(null); const docH = React.useMemo(() => Math.max(240, Math.min(430, window.innerHeight - 330)), []); // Jumlah halaman = jumlah halaman file yang diunggah (tanpa batas/asumsi). const pages = pdfPages || 1; const [page, setPage] = React.useState(task && task.signature ? task.signature.page : 1); // Halaman TTD yang berlaku, diklem ke jumlah halaman dokumen. const sigPage = task && task.signature ? Math.min(Math.max(1, task.signature.page || 1), pages) : null; React.useEffect(() => { if (pdfPages) setPage((n) => Math.min(Math.max(1, n), pdfPages)); }, [pdfPages]); React.useEffect(() => { if (sigPage) setPage(sigPage); }, [sigPage]); const [pos, setPos] = React.useState(task && task.signature ? { x: task.signature.x, y: task.signature.y } : null); const [width, setWidth] = React.useState(task && task.signature ? task.signature.width : 34); const [drag, setDrag] = React.useState(false); const sheetRef = React.useRef(null); React.useEffect(() => { if (!open) return; setPage(task && task.signature ? task.signature.page : 1); setPos(task && task.signature ? { x: task.signature.x, y: task.signature.y } : null); setWidth(task && task.signature ? task.signature.width : 34); }, [open, task && task.id]); if (!open || !task) return null; const ev = task.evidence || (task.evidences && task.evidences[0]) || { name: "bukti-task.pdf", compressed: 0 }; const docSrc = fileSrc(ev); const isImage = ev.kind === "image" || /\.(png|jpe?g|webp)$/i.test(ev.name || ""); // Berkas hasil unduhan ber-TTD sudah memuat tanda tangan di halamannya. // Menandatangani ulang berkas ini membuat TTD tampak dobel/menumpuk. const preSigned = /-ttd(\.[a-z0-9]+)?$/i.test(String(ev.name || "")); const place = (e) => { if (readOnly) return; const r = sheetRef.current.getBoundingClientRect(); const x = Math.min(96, Math.max(4, ((e.clientX - r.left) / r.width) * 100)); const y = Math.min(96, Math.max(4, ((e.clientY - r.top) / r.height) * 100)); setPos({ x, y }); }; const onMove = (e) => { if (drag && !readOnly) place(e); }; return ( 1 ? "inline-flex" : "none", alignItems: "center", gap: 8 }}> setPage((p) => Math.max(1, p - 1))} /> {`${page} / ${pages}`} setPage((p) => Math.min(pages, p + 1))} /> {readOnly && task.signature && ( )} {!readOnly && ( )} }>
{!signature && !readOnly && ( Belum ada file tanda tangan. Unggah sekali di Profile → Tanda Tangan Digital. )} {!readOnly && preSigned && ( Berkas ini sudah memuat tanda tangan (nama file berakhiran -ttd). Bila ditandatangani lagi, TTD akan menumpuk — minta user mengunggah dokumen asli tanpa TTD. )} {!readOnly && signature && ( Tanda tangan Admin {pos ? "Tanda tangan sudah ditempatkan — geser untuk menyesuaikan." : "Klik dokumen untuk menempatkan."} Ukuran setWidth(Number(e.target.value))} style={{ width: 110, accentColor: "var(--teal-500)" }} /> {pos && } )}
{docSrc && !isImage && }
{docSrc ? (isImage ? {ev.name} : null) :
Berkas bukti belum tersimpan — minta user mengunggah ulang. {task.type}
} {/* Lapisan penempatan TTD — menangkap klik walau dokumen dirender di dalam iframe. */}
setDrag(false)} onMouseLeave={() => setDrag(false)} style={{ position: "absolute", inset: 0, cursor: readOnly ? "default" : "crosshair", pointerEvents: readOnly ? "none" : "auto" }} /> {pos && signature && (readOnly ? page === sigPage : true) && ( { e.stopPropagation(); setDrag(true); }} style={{ position: "absolute", left: `${pos.x}%`, top: `${pos.y}%`, transform: "translate(-50%,-50%)", width: `${width}%`, padding: 4, borderRadius: 6, border: readOnly ? "1px solid transparent" : "1px dashed var(--teal-400)", background: readOnly ? "transparent" : "rgba(19,168,140,.06)", cursor: readOnly ? "default" : "grab" }}> Tanda tangan )}
{readOnly && task.signature && ( Ditandatangani Direktur Klinik pada {task.signature.at} · halaman {sigPage} dari {pages} )}
); } /* Profile card: the signature PNG is uploaded once and reused for every task. */ function SignatureCard({ signature, onChange }) { const { Card } = window.TaskFlowDesignSystem_dd53c2; const [err, setErr] = React.useState(""); const pick = (e) => { const f = e.target.files && e.target.files[0]; e.target.value = ""; if (!f) return; if (!/png$/i.test(f.type) && !/\.png$/i.test(f.name)) { setErr("File tanda tangan harus PNG (latar transparan)."); return; } setErr(""); const r = new FileReader(); r.onload = () => onChange(r.result); // data URL → tetap ada setelah refresh r.readAsDataURL(f); }; return (

Tanda Tangan Digital

Cukup diunggah sekali (PNG, sebaiknya latar transparan) — dipakai untuk semua task yang butuh tanda tangan.

{signature ? "Siap dipakai" : "Belum ada"}
{signature ? Tanda tangan Admin : } {signature && } {err || "Task yang menandai \u201cperlu tanda tangan\u201d hanya menerima bukti berformat PDF."}
); } Object.assign(window, { SignPdfModal, SignatureCard }); })();