fix(wiring): multi-tenant isolation, in-calendar quick scheduler, telehealth note sync, and superbill history

This commit is contained in:
2026-09-07 12:09:00 -07:00
parent 02a861a833
commit d449dd2411
8 changed files with 914 additions and 209 deletions
+210 -82
View File
@@ -349,6 +349,127 @@ export default function Home() {
setActiveTenantId(tenantId);
setPortalMode('clinic');
setClinicTab('calendar');
// Automatically sync active patient, note, and superbills for the selected clinic
const tenantPatients = patients.filter((p) => p.tenantId === tenantId);
if (tenantPatients.length > 0) {
setActivePatient(tenantPatients[0]);
const matchingSoap =
soapNotes.find((s) => s.patientId === tenantPatients[0].id) ||
soapNotes.find((s) => s.tenantId === tenantId);
setActiveSoapNote(matchingSoap);
}
const tenantSuperbills = superbills.filter((sb) => sb.tenantId === tenantId);
if (tenantSuperbills.length > 0) {
setActiveSuperbill(tenantSuperbills[0]);
}
};
const handleQuickScheduleAppointment = (newAptData: any) => {
clinicalAudio.playSuccess();
const newApt: Appointment = {
id: `apt-${Date.now()}`,
...newAptData,
};
const existingPatient = patients.find((p) => p.id === newAptData.patientId);
if (!existingPatient) {
const nameParts = (newAptData.patientName || 'New Patient').split(' ');
const firstName = nameParts[0] || 'Walk-In';
const lastName = nameParts.slice(1).join(' ') || 'Patient';
const newPatient: Patient = {
id: newAptData.patientId,
tenantId: activeTenant.id,
firstName,
lastName,
email: `${firstName.toLowerCase()}.${lastName.toLowerCase()}@example.com`,
phone: newAptData.patientPhone || '(555) 000-0000',
dob: '1990-01-01',
gender: 'Undisclosed',
address: activeTenant.cityStateZip,
insuranceName: 'Direct / Stripe Connect',
insuranceId: 'SELF-PAY',
chiefComplaint: newAptData.notes || newAptData.serviceType,
daysSinceLastVisit: 0,
lastVisitDate: newAptData.date,
nextAppointmentDate: newAptData.date,
status: 'active',
carePlan: {
title: 'Acute Relief & Biomechanical Alignment',
totalVisits: 6,
completedVisits: 1,
frequency: '1-2x / week',
targetCondition: newAptData.serviceType,
startDate: newAptData.date,
status: 'on_track',
},
};
setPatients((prev) => [newPatient, ...prev]);
setActivePatient(newPatient);
} else {
setActivePatient(existingPatient);
}
setAppointments((prev) => [newApt, ...prev]);
addToast(
'success',
'Encounter Scheduled',
`${newApt.patientName} scheduled for ${newApt.time} (${newApt.serviceType})`
);
};
const handleEndTelehealthCall = (consultData: {
subjective: string;
objective: string;
assessment: string;
plan: string;
duration: string;
}) => {
clinicalAudio.playSuccess();
const today = new Date().toISOString().substring(0, 10);
const updatedNote: SoapNote = {
id: activeSoapNote?.id || `soap-${Date.now()}`,
tenantId: activeTenant.id,
patientId: activePatient.id,
patientName: `${activePatient.firstName} ${activePatient.lastName}`,
providerId: activeProvider.id,
providerName: activeProvider.name,
date: today,
status: 'draft',
discipline: 'chiropractic',
vasScore: 4,
subjective: consultData.subjective,
objective: consultData.objective,
assessment: consultData.assessment,
plan: `${consultData.plan}\n\n[Telehealth Video Consult • Duration: ${consultData.duration} • Encrypted WebRTC Session]`,
spinalAdjustments: activeSoapNote?.spinalAdjustments || [],
icd10Codes: activeSoapNote?.icd10Codes || [
{ code: 'M99.01', description: 'Segmental and somatic dysfunction of cervical region' },
],
cptCodes: [
{ code: '99203', description: 'Office/Telehealth Outpatient Visit, 30 min', fee: 110 },
{ code: '97110', description: 'Therapeutic Exercises & Ergonomic Protocol', fee: 45 },
],
};
setSoapNotes((prev) => {
const idx = prev.findIndex((s) => s.patientId === activePatient.id);
if (idx >= 0) {
const copy = [...prev];
copy[idx] = updatedNote;
return copy;
}
return [updatedNote, ...prev];
});
setActiveSoapNote(updatedNote);
setClinicTab('charting');
addToast(
'success',
'Telehealth Note Synced to Chart',
`Clinical findings for ${activePatient.firstName} ${activePatient.lastName} transferred to SOAP editor (${consultData.duration})`
);
};
const handleUpdateProductStock = (productId: string, newStock: number) => {
@@ -464,7 +585,7 @@ export default function Home() {
<div className="relative">
<select
value={activeTenantId}
onChange={(e) => setActiveTenantId(e.target.value)}
onChange={(e) => handleSwitchTenant(e.target.value)}
className="appearance-none bg-slate-50 hover:bg-slate-100 border border-slate-300 text-slate-800 text-xs font-bold rounded-lg px-3 py-1.5 pr-8 cursor-pointer focus:outline-none focus:border-sky-500 transition shadow-2xs"
>
{tenants.map((t) => (
@@ -742,94 +863,101 @@ export default function Home() {
{/* Main Container */}
<main className="flex-1 max-w-7xl w-full mx-auto p-4 sm:p-6 lg:p-8">
{portalMode === 'clinic' && (
<div>
{clinicTab === 'calendar' && (
<CalendarView
appointments={appointments}
providers={activeProviders}
activeTenant={activeTenant}
waitlistCount={waitlist.length}
onSelectAppointment={handleSelectAppointment}
onUpdateStatus={handleUpdateAppointmentStatus}
onNewAppointmentClick={() => setPortalMode('patient')}
onOpenWaitlist={() => setIsWaitlistOpen(true)}
/>
)}
{portalMode === 'clinic' && (() => {
const tenantPatients = patients.filter((p) => !p.tenantId || p.tenantId === activeTenant.id);
const tenantSuperbills = superbills.filter((s) => !s.tenantId || s.tenantId === activeTenant.id);
{clinicTab === 'charting' && staffRole !== 'front_desk' && (
<SoapChartEditor
patient={activePatient}
provider={activeProvider}
initialSoapNote={activeSoapNote}
onSaveSoapNote={handleSaveSoapNote}
onGenerateSuperbill={handleGenerateSuperbillFromNote}
onLaunchTelehealth={() => setClinicTab('telehealth')}
onBackToCalendar={() => setClinicTab('calendar')}
onOpenCourtVault={() => setIsCourtVaultOpen(true)}
/>
)}
return (
<div>
{clinicTab === 'calendar' && (
<CalendarView
appointments={appointments}
providers={activeProviders}
activeTenant={activeTenant}
patients={tenantPatients}
waitlistCount={waitlist.length}
onSelectAppointment={handleSelectAppointment}
onUpdateStatus={handleUpdateAppointmentStatus}
onNewAppointmentClick={() => setPortalMode('patient')}
onQuickSchedule={handleQuickScheduleAppointment}
onOpenWaitlist={() => setIsWaitlistOpen(true)}
/>
)}
{clinicTab === 'telehealth' && staffRole !== 'front_desk' && (
<TelehealthRoom
patient={activePatient}
provider={activeProvider}
onEndCall={(noteData) => {
setClinicTab('charting');
}}
onClose={() => setClinicTab('calendar')}
/>
)}
{clinicTab === 'charting' && staffRole !== 'front_desk' && (
<SoapChartEditor
patient={activePatient}
provider={activeProvider}
initialSoapNote={activeSoapNote}
onSaveSoapNote={handleSaveSoapNote}
onGenerateSuperbill={handleGenerateSuperbillFromNote}
onLaunchTelehealth={() => setClinicTab('telehealth')}
onBackToCalendar={() => setClinicTab('calendar')}
onOpenCourtVault={() => setIsCourtVaultOpen(true)}
/>
)}
{clinicTab === 'retail' && (
<RetailInventoryView
products={products}
activeTenant={activeTenant}
onUpdateStock={handleUpdateProductStock}
/>
)}
{clinicTab === 'telehealth' && staffRole !== 'front_desk' && (
<TelehealthRoom
patient={activePatient}
provider={activeProvider}
onEndCall={handleEndTelehealthCall}
onClose={() => setClinicTab('calendar')}
/>
)}
{clinicTab === 'memberships' && (
<MembershipsView
memberships={memberships}
packages={packages}
patients={patients}
activeTenant={activeTenant}
onEnrollPatient={handleEnrollPatientInMembership}
/>
)}
{clinicTab === 'retail' && (
<RetailInventoryView
products={products}
activeTenant={activeTenant}
onUpdateStock={handleUpdateProductStock}
/>
)}
{clinicTab === 'billing' && (
<SuperbillView
superbill={activeSuperbill}
activeTenant={activeTenant}
onBack={() => setClinicTab('calendar')}
onMarkPaid={handleMarkPaid}
/>
)}
{clinicTab === 'memberships' && (
<MembershipsView
memberships={memberships}
packages={packages}
patients={tenantPatients}
activeTenant={activeTenant}
onEnrollPatient={handleEnrollPatientInMembership}
/>
)}
{clinicTab === 'retention' && (
<RetentionView
patients={patients}
activeTenant={activeTenant}
onSelectPatientChart={(patient) => {
setActivePatient(patient);
const existing = soapNotes.find((s) => s.patientId === patient.id);
setActiveSoapNote(existing);
setClinicTab('charting');
}}
/>
)}
{clinicTab === 'billing' && (
<SuperbillView
superbill={activeSuperbill}
superbills={tenantSuperbills}
activeTenant={activeTenant}
onBack={() => setClinicTab('calendar')}
onSelectSuperbill={(sb) => setActiveSuperbill(sb)}
onMarkPaid={handleMarkPaid}
/>
)}
{clinicTab === 'growth' && (
<ClinicGrowthView
activeTenant={activeTenant}
activeProvider={activeProvider}
onOpenBookingPortal={() => setPortalMode('patient')}
/>
)}
</div>
)}
{clinicTab === 'retention' && (
<RetentionView
patients={tenantPatients}
activeTenant={activeTenant}
onSelectPatientChart={(patient) => {
setActivePatient(patient);
const existing = soapNotes.find((s) => s.patientId === patient.id);
setActiveSoapNote(existing);
setClinicTab('charting');
}}
/>
)}
{clinicTab === 'growth' && (
<ClinicGrowthView
activeTenant={activeTenant}
activeProvider={activeProvider}
onOpenBookingPortal={() => setPortalMode('patient')}
/>
)}
</div>
);
})()}
{portalMode === 'patient' && (
<PatientPortalView