// ============ TaskQuest — Modals ============ const { useState, useEffect, useRef } = React; const { ROLES: R5, PROJECTS: P5, CONSULTANTS: C5, COLUMNS: COLS5 } = window.TQ; function ModalShell({ icon, iconColor, title, desc, onClose, children, footer, wide }) { useEffect(() => { function esc(e) { if (e.key === "Escape") onClose(); } document.addEventListener("keydown", esc); return () => document.removeEventListener("keydown", esc); }, []); return (
e.stopPropagation()}>
{icon && {React.createElement(icon, { size: 18 })}}
{title}
{desc &&
{desc}
}
{children}
{footer &&
{footer}
}
); } function ProjectTag({ pk }) { const p = P5[pk]; return {p.name}; } // ---------- Task detail ---------- function TaskDetailModal({ task, role, onClose, onComment, onApproveDate, onRejectDate, onApproveDone, onAwardBonus, onReassign, onDelete, onEdit, onMoveBackHint }) { const { fmtDateLong, relDue, daysUntil } = window.TQ; const r = R5[role]; const isManager = r.type === "pm" || r.type === "dm"; const col = COLS5.find(c => c.key === task.status); const [text, setText] = useState(""); const [editDate, setEditDate] = useState(task.request ? task.request.newDate : task.due); const [reassignOpen, setReassignOpen] = useState(false); const [confirmDelete, setConfirmDelete] = useState(false); const [editing, setEditing] = useState(false); // edit form state const SUBCATS = window.TQ.SUBCATEGORIES; const [editName, setEditName] = useState(task.name); const [editDue, setEditDue] = useState(task.due); const [editAssignee, setEditAssignee] = useState(task.assignee); const [editSubcat, setEditSubcat] = useState(task.subcategory || ""); const [editDesc, setEditDesc] = useState(task.description || ""); const overdue = task.status !== "done" && daysUntil(task.due) < 0; const subcat = task.subcategory && SUBCATS.find(s => s.key === task.subcategory); const canReassign = isManager && (task.status === "happy" || task.status === "unhappy"); const reassignPool = C5.filter(c => R5[c] && window.TQ.projectsOf(R5[c]).indexOf(task.project) >= 0); const pool = reassignPool.length ? reassignPool : C5; const datePending = task.request && task.request.status === "pending"; const donePending = task.completion && task.completion.status === "pending"; function submitComment() { if (!text.trim()) return; onComment(task, text.trim()); setText(""); } return ( {task.id}·} onClose={onClose} footer={ <> {r.type === "dm" && ( )} {isManager && !editing && ( )} {isManager && editing && (
)} }> {/* Inline edit form — managers only */} {isManager && editing && (
setEditName(e.target.value)} />
{SUBCATS.map(s => ( ))}
(R5[c] && R5[c].name) || c} renderOption={c => ({R5[c].name}{R5[c].label})} />