- New CoverageFlag enum: SUPPLY_LAPSED, VISIT_REQUIRED, TRANSFER_PENDING, RENEWAL_CRITICAL/ELEVATED/SOON, RESUPPLY_READY, ACTIVE - Doc state machine: 5-item payer-dependent status per patient (SWO, Visit, PECOS, PA, Diagnosis) with cascade chain - Confirm Visit endpoint: staff enters prescriber-confirmed date, persisted in Supabase confirmed_visits table, survives all future CSV imports - Supabase migration: 001_add_confirmed_visits.sql (run manually in SQL editor) - Frontend: Badge rebuilt for 8 flags, DocStatusBar 5-dot display, ConfirmVisitModal, expandable WorklistTable rows - Legal: LOI, NDA, BAA drafts at pitch/legal/ for Nixon Law Group review - Compliance docs: privacy policy, incident response, data handling - CSV generator: market_data.json + PA/NJ generator scripts - 15/15 tests passing Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
66 lines
No EOL
2.2 KiB
JavaScript
66 lines
No EOL
2.2 KiB
JavaScript
import { useAuth } from "@clerk/react";
|
|
import { exportFromBackend } from "../lib/api";
|
|
import { getFlagLabel, getFlagAction, getDeviceDisplay } from "../lib/coverage";
|
|
|
|
function downloadBlob(blob, filename) {
|
|
const url = URL.createObjectURL(blob);
|
|
const a = document.createElement("a");
|
|
a.href = url;
|
|
a.download = filename;
|
|
document.body.appendChild(a);
|
|
a.click();
|
|
document.body.removeChild(a);
|
|
URL.revokeObjectURL(url);
|
|
}
|
|
|
|
export default function CSVExport({ records, batchId }) {
|
|
const { getToken } = useAuth();
|
|
const today = new Date().toISOString().slice(0, 10);
|
|
const filename = `signal-work-queue-${today}.csv`;
|
|
|
|
const exportCSV = async () => {
|
|
if (!records || records.length === 0) return;
|
|
|
|
if (batchId) {
|
|
const token = await getToken().catch(() => null);
|
|
const blob = await exportFromBackend(records, batchId, token);
|
|
if (blob) {
|
|
downloadBlob(blob, filename);
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Fallback: client-side CSV (no batchId or backend unavailable)
|
|
const headers = [
|
|
"Patient ID", "Device", "Payer", "Status", "Priority Score",
|
|
"Days Until Resupply End", "Recommended Action", "Resupply End Date", "Reason",
|
|
"Visit Confidence",
|
|
];
|
|
const rows = records.map((r) => [
|
|
r.patient_id,
|
|
r.device_display || getDeviceDisplay(r.device_type),
|
|
r.payer,
|
|
r.status_label || getFlagLabel(r.flag),
|
|
r.priority_score ?? r.priority,
|
|
r.days_until_coverage_end ?? r.daysUntilEnd,
|
|
r.action || getFlagAction(r.flag),
|
|
r.coverage_end_date || r.coverageEndDate || "",
|
|
r.reason || "",
|
|
r.visit_date_confidence || "estimated",
|
|
]);
|
|
const csv = [headers, ...rows]
|
|
.map((row) => row.map((v) => `"${String(v).replace(/"/g, '""')}"`).join(","))
|
|
.join("\r\n");
|
|
downloadBlob(new Blob([csv], { type: "text/csv;charset=utf-8;" }), filename);
|
|
};
|
|
|
|
return (
|
|
<button
|
|
onClick={exportCSV}
|
|
className="bg-[var(--brand)] text-warm-white px-4 py-[7px] rounded-md text-[13px] font-medium cursor-pointer transition-colors hover:bg-[var(--brand-hover)] font-body"
|
|
disabled={!records || records.length === 0}
|
|
>
|
|
↓ Export Work Queue
|
|
</button>
|
|
);
|
|
} |