import { useState, useEffect } from "react"; import { useTheme } from "../useTheme"; import Badge from "./Badge"; import DocStatusBar from "./DocStatusBar"; import ConfirmVisitModal from "./ConfirmVisitModal"; function daysLabel(days) { if (days === undefined || days === null) return ; if (days < 0) return Expired {Math.abs(days)}d ago; if (days <= 7) return {days} days; if (days <= 30) return {days} days; return {days} days; } function scoreClass(priority) { if (priority >= 1000) return "text-[#B00000]"; if (priority >= 500) return "text-[var(--accent-text)]"; if (priority >= 200) return "text-[var(--text-secondary)]"; return "text-[var(--text-muted)]"; } const FILTERS = [ { key: "all", label: "All" }, { key: "at-risk", label: "At Risk" }, { key: "action-needed", label: "Action Needed" }, { key: "RESUPPLY_READY", label: "Clear to Ship" }, { key: "ACTIVE", label: "On Track" }, ]; const AT_RISK_FLAGS = ["SUPPLY_LAPSED", "VISIT_REQUIRED", "RENEWAL_CRITICAL"]; const ACTION_NEEDED_FLAGS = ["RENEWAL_ELEVATED", "RENEWAL_SOON", "TRANSFER_PENDING"]; const HIGH_PRIORITY_FLAGS = ["SUPPLY_LAPSED", "VISIT_REQUIRED", "RENEWAL_CRITICAL", "TRANSFER_PENDING"]; const CONFIRM_VISIT_FLAGS = ["VISIT_REQUIRED", "RENEWAL_CRITICAL", "RENEWAL_ELEVATED", "RENEWAL_SOON"]; export default function WorklistTable({ records, activeFilter, onFilterChange }) { const { dark } = useTheme(); const [expandedRow, setExpandedRow] = useState(null); const [confirmingRecord, setConfirmingRecord] = useState(null); const [localRecords, setLocalRecords] = useState(records); useEffect(() => { setLocalRecords(records); }, [records]); function handleVisitConfirmed(patientId, updatedRecord) { setLocalRecords(prev => prev.map(r => r.patient_id === patientId ? { ...r, ...updatedRecord } : r) .sort((a, b) => (b.priority_score || b.priority || 0) - (a.priority_score || a.priority || 0)) ); } const filtered = activeFilter === "all" ? localRecords : activeFilter === "at-risk" ? localRecords.filter((r) => AT_RISK_FLAGS.includes(r.flag)) : activeFilter === "action-needed" ? localRecords.filter((r) => ACTION_NEEDED_FLAGS.includes(r.flag)) : localRecords.filter((r) => r.flag === activeFilter); const todayStr = new Date().toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric", }); return (
{/* Header */}
Outreach Worklist
{localRecords.length} patients · sorted by priority score · {todayStr}
{FILTERS.map((f) => ( ))}
{/* Table */} {["Patient ID", "Device", "Payer", "Days Left", "Status", "Priority", "Action"].map(h => ( ))} {filtered.map((r, i) => { const hp = HIGH_PRIORITY_FLAGS.includes(r.flag); const rowKey = r.patient_id + "-" + i; const isExpanded = expandedRow === rowKey; const showConfirmVisit = CONFIRM_VISIT_FLAGS.includes(r.flag) || r.visit_date_confidence === "estimated"; // Support both API shape (days_until_coverage_end) and legacy local shape (daysUntilEnd) const daysLeft = r.days_until_coverage_end ?? r.daysUntilEnd; const priority = r.priority_score ?? r.priority; const deviceDisplay = r.device_display || r.device_type; return ( <> setExpandedRow(isExpanded ? null : rowKey)} > {/* Expanded row — cascade + doc checklist */} {isExpanded && ( )} ); })} {filtered.length === 0 && ( )}
{h}
{r.patient_id}
{i === 0 && (
★ TOP PRIORITY
)} {r.visit_date_confidence === "estimated" && (
Estimated visit date
)}
{deviceDisplay} {r.payer} {daysLabel(daysLeft)}
{r.doc_state && }
{r.reason && (
{r.reason}
)} {r.flag === "ACTIVE" && r.next_visit_due_date && (
Next visit due: {new Date(r.next_visit_due_date + "T00:00:00").toLocaleDateString("en-US", { month: "short", year: "numeric" })}
)}
{priority} e.stopPropagation()}> {showConfirmVisit ? ( ) : ( )}
{r.cascade && r.cascade.length > 0 && (
Documentation Cascade
{r.cascade.join(" → ")}
)} {r.doc_state && (
Documentation Checklist
)} {!r.doc_state && !r.cascade?.length && (
No documentation details available. Upload a CSV with doc columns to see the full checklist.
)}
No patients match this filter.
{/* Footer */}
PHI-safe — patient names and DOBs never stored. Crosswalk: patient_id only. {activeFilter === "all" ? `${localRecords.length} patients · all results shown` : `${filtered.length} of ${localRecords.length} · filtered`}
{/* Confirm Visit Modal */} {confirmingRecord && ( setConfirmingRecord(null)} onConfirmed={handleVisitConfirmed} /> )}
); } function ActionButton({ flag }) { const base = "bg-transparent border border-[var(--border-color)] text-[var(--text-secondary)] px-[13px] py-[5px] rounded-md text-xs cursor-pointer font-body whitespace-nowrap transition-all hover:border-[var(--brand)] hover:text-[var(--brand)]"; if (flag === "SUPPLY_LAPSED" || flag === "VISIT_REQUIRED") { return ; } if (flag === "TRANSFER_PENDING") { return ; } if (flag === "RESUPPLY_READY") { return ; } return ; } function DocItem({ label, value }) { const isGood = value && ( value === "On File" || value === "Verified" || value === "Not Required" || value.startsWith("Confirmed") || value.startsWith("Approved") || value === "N/A" ); const isBad = value && ( value === "Missing" || value === "Not Verified" || value === "Expired" || value === "Denied" || value === "Required — Not Started" ); const color = isGood ? "text-[#1A8040]" : isBad ? "text-[#CC2222]" : "text-[#CB6B20]"; return (
{label}: {value || "Unknown"}
); }