diff --git a/CLAUDE.md b/CLAUDE.md index beab225..4d75064 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -64,7 +64,7 @@ and stays predictable. Whitepaper: `/Users/sttil-solutions/Documents/Obsidian_Vault/STTIL-Vault/Projects/2026-05-18-pilot-readiness-whitepaper.md` -### Checklist Status (as of 2026-06-07) — 45-50% pilot-ready +### Checklist Status (as of 2026-06-09) — 65% pilot-ready | Checklist Item | Status | |---|---| @@ -74,9 +74,9 @@ Whitepaper: `/Users/sttil-solutions/Documents/Obsidian_Vault/STTIL-Vault/Project | 25 CSV variants pass ingestion tests | PASS — 50 new files generated (PA + NJ sets), 100% normalizer pass rate 2026-06-07 | | Import flow: mapping, validation, warnings | PARTIAL — CSVImport exists, full mapping review unconfirmed | | Report generation calculates from backend | PASS | -| Each status has reason + recommended action | PARTIAL — payer bug fixed; visit date still uses shipment proxy | +| Each status has reason + recommended action | PASS — visit date proxy bug fixed 2026-06-09; `_resolve_visit_date()` implements full priority chain (CSV column > Supabase confirmed > shipment-30d estimate) | | Export CSV works and opens cleanly | NOT VERIFIED end to end | -| Placeholder content removed | NOT VERIFIED | +| Placeholder content removed | PARTIAL — StatCard undefined variables fixed 2026-06-09; Sidebar + sweep in progress | | Browser smoke test passes | NOT RUN | | Data handling rules documented | PASS — privacy-policy.md + data-handling.md written 2026-06-07; minimum necessary fields, CSV disposition policy, retention, and payer_rules.json cadence all documented | | Real PHI blocked | PARTIAL — architecture confirmed; Supabase plan tier (Pro vs Team) not yet verified | diff --git a/pitch/legal/gaboro-nda.md b/pitch/legal/gaboro-nda.md index caf71e0..054827b 100644 --- a/pitch/legal/gaboro-nda.md +++ b/pitch/legal/gaboro-nda.md @@ -3,8 +3,8 @@ **Effective Date:** June __, 2026 **Between:** -- **STTIL Solutions LLC** ("STTIL"), a limited liability company, with its principal place of business in [City, State] -- **Gaboro DME** ("Gaboro"), with its principal place of business in [City, State] +- **STTIL Solutions LLC** ("STTIL"), a limited liability company, with its principal place of business in Jacksonville, Florida +- **Gaboro DME** ("Gaboro"), with its principal place of business in Philadelphia, Pennsylvania Each a "Party" and together the "Parties." @@ -118,4 +118,4 @@ By: ___________________________ Name: Robert Robinson Title: Co-Founder and Managing Partner Date: _________________________ -Email: ___________________________ +Email: robertr@gaboromed.com diff --git a/python-backend/api/main.py b/python-backend/api/main.py index 5ce0f91..35b2a29 100644 --- a/python-backend/api/main.py +++ b/python-backend/api/main.py @@ -469,6 +469,20 @@ async def confirm_visit( if confirmed > date_type.today(): raise HTTPException(status_code=400, detail="Confirmed date cannot be in the future.") + # Validate: visit must be within 6 months (183 days) before the order/shipment date. + # 183 days = ~6 months, consistent with CMS CGM qualifying visit window. + from datetime import timedelta as _td + six_months_before = shipment - _td(days=183) + if confirmed < six_months_before: + raise HTTPException( + status_code=400, + detail=( + f"Visit date {confirmed.isoformat()} is more than 6 months before " + f"the order date {shipment.isoformat()}. A qualifying visit must fall " + f"within the 6-month window before each supply order." + ), + ) + # Get org clerk_org_id = claims.get("o", {}).get("id") if isinstance(claims.get("o"), dict) else None org_id = get_or_create_org(clerk_org_id=clerk_org_id) diff --git a/signal-ui/src/App.jsx b/signal-ui/src/App.jsx index e10098e..05ec988 100644 --- a/signal-ui/src/App.jsx +++ b/signal-ui/src/App.jsx @@ -29,8 +29,12 @@ function AppInner() { const transferPending = records.filter((r) => r.flag === "TRANSFER_PENDING").length; const refill = records.filter((r) => r.flag === "RESUPPLY_READY").length; const okCount = records.filter((r) => r.flag === "ACTIVE").length; + // escalateCount = VISIT_REQUIRED (qualifying visit past due) + RENEWAL_CRITICAL (<=45 days) const escalateCount = visitRequired + renewalCritical; - const outreachCount = renewalSoon + renewalElevated; + // outreachCount = RENEWAL_ELEVATED (<=60d) + RENEWAL_SOON (<=90d) + const outreachCount = renewalElevated + renewalSoon; + const atRiskCount = supplyLapsed + visitRequired + renewalCritical; + const actionNeededCount = renewalSoon + renewalElevated + transferPending; const handleResults = useCallback(async (file) => { const label = new Date().toLocaleDateString("en-US", { @@ -60,7 +64,7 @@ function AppInner() { const rows = parseCSV(e.target.result); const { results, skipped } = processBatch(rows); setRecords(results); - setImportLabel(`${file.name} · ${label} · local processing`); + setImportLabel(`${file.name} · ${label}`); let msg = `Loaded ${results.length} patient${results.length !== 1 ? "s" : ""} from ${file.name}`; if (skipped.length) msg += ` · ${skipped.length} skipped`; showToast(msg); @@ -72,9 +76,10 @@ function AppInner() {
Signal is operated by STTIL Solutions LLC.{" "} - signal.sttilsolutions.com + sttilsolutions.com/signal {" "}· We may update this policy from time to time. We will notify active customers of material changes by email. Continued use of Signal following notice constitutes acceptance. diff --git a/signal-ui/src/components/ConfirmVisitModal.jsx b/signal-ui/src/components/ConfirmVisitModal.jsx index d4312fb..26cc60b 100644 --- a/signal-ui/src/components/ConfirmVisitModal.jsx +++ b/signal-ui/src/components/ConfirmVisitModal.jsx @@ -1,7 +1,9 @@ import { useState } from "react"; +import { useAuth } from "@clerk/react"; import { confirmVisit } from "../lib/api"; export default function ConfirmVisitModal({ record, onClose, onConfirmed }) { + const { getToken } = useAuth(); const [visitDate, setVisitDate] = useState(""); const [error, setError] = useState(""); const [saving, setSaving] = useState(false); @@ -29,9 +31,20 @@ export default function ConfirmVisitModal({ record, onClose, onConfirmed }) { setError("Visit date cannot be in the future."); return; } + // Validate: visit must be within 6 months before the order/shipment date + const shipmentDateStr = computeShipmentDate(); + const shipmentDateObj = new Date(shipmentDateStr + "T00:00:00"); + const visitDateObj = new Date(visitDate + "T00:00:00"); + const sixMonthsBeforeShipment = new Date(shipmentDateObj); + sixMonthsBeforeShipment.setMonth(sixMonthsBeforeShipment.getMonth() - 6); + if (visitDateObj < sixMonthsBeforeShipment) { + setError("Visit date must be within 6 months of the order date. A visit older than 6 months does not satisfy the qualifying visit requirement."); + return; + } setSaving(true); try { + const token = await getToken().catch(() => null); const updated = await confirmVisit({ patient_id: record.patient_id, confirmed_date: visitDate, @@ -40,7 +53,7 @@ export default function ConfirmVisitModal({ record, onClose, onConfirmed }) { device_type: record.device_type, quantity: 1, component: record.component || "sensor", - }); + }, token); onConfirmed(record.patient_id, updated); onClose(); } catch (e) { diff --git a/signal-ui/src/components/Sidebar.jsx b/signal-ui/src/components/Sidebar.jsx index a41d230..c37ba3b 100644 --- a/signal-ui/src/components/Sidebar.jsx +++ b/signal-ui/src/components/Sidebar.jsx @@ -1,11 +1,23 @@ +import { useOrganization } from "@clerk/react"; + export default function Sidebar({ - supplyLapsedCount, - escalateCount, + atRiskCount, + actionNeededCount, clearToShipCount, + onTrackCount, activeFilter, onFilterChange, onImportClick, }) { + const { organization } = useOrganization(); + const orgName = organization?.name ?? "My Organization"; + const initials = orgName + .split(" ") + .slice(0, 2) + .map((w) => w[0]) + .join("") + .toUpperCase(); + const navItems = [ { label: "All Patients", @@ -14,17 +26,17 @@ export default function Sidebar({ badge: null, }, { - label: "Docs Required", - key: "SUPPLY_LAPSED", + label: "At Risk", + key: "at-risk", icon: "!", - badge: supplyLapsedCount, + badge: atRiskCount, badgeWarn: true, }, { - label: "Escalate", - key: "escalate", + label: "Action Needed", + key: "action-needed", icon: "!", - badge: escalateCount, + badge: actionNeededCount, badgeWarn: true, }, { @@ -33,6 +45,12 @@ export default function Sidebar({ icon: "✓", badge: clearToShipCount, }, + { + label: "On Track", + key: "ACTIVE", + icon: "✓", + badge: onTrackCount, + }, ]; return ( @@ -116,11 +134,11 @@ export default function Sidebar({