feat: Doctor Onboarding Portal with statutory BAA execution and enclave deployment
This commit is contained in:
+108
-4
@@ -24,6 +24,7 @@ import {
|
|||||||
PrePaidPackage,
|
PrePaidPackage,
|
||||||
WaitlistEntry,
|
WaitlistEntry,
|
||||||
StaffRole,
|
StaffRole,
|
||||||
|
Provider,
|
||||||
} from '@/types/clinical';
|
} from '@/types/clinical';
|
||||||
import { CalendarView } from '@/components/calendar/CalendarView';
|
import { CalendarView } from '@/components/calendar/CalendarView';
|
||||||
import { SoapChartEditor } from '@/components/charting/SoapChartEditor';
|
import { SoapChartEditor } from '@/components/charting/SoapChartEditor';
|
||||||
@@ -39,6 +40,7 @@ import { CourtAuditVaultModal } from '@/components/compliance/CourtAuditVaultMod
|
|||||||
import { CommandPalette } from '@/components/ui/CommandPalette';
|
import { CommandPalette } from '@/components/ui/CommandPalette';
|
||||||
import { InactivityLockoutModal } from '@/components/ui/InactivityLockoutModal';
|
import { InactivityLockoutModal } from '@/components/ui/InactivityLockoutModal';
|
||||||
import { ClinicalToastContainer, ToastMessage } from '@/components/ui/ClinicalToast';
|
import { ClinicalToastContainer, ToastMessage } from '@/components/ui/ClinicalToast';
|
||||||
|
import { DoctorOnboardingView } from '@/components/onboarding/DoctorOnboardingView';
|
||||||
import { clinicalAudio } from '@/lib/clinical-audio';
|
import { clinicalAudio } from '@/lib/clinical-audio';
|
||||||
import {
|
import {
|
||||||
Calendar,
|
Calendar,
|
||||||
@@ -68,8 +70,8 @@ export default function Home() {
|
|||||||
const [tenants, setTenants] = useState<ClinicTenant[]>(INITIAL_TENANTS);
|
const [tenants, setTenants] = useState<ClinicTenant[]>(INITIAL_TENANTS);
|
||||||
const [activeTenantId, setActiveTenantId] = useState<string>(INITIAL_TENANTS[0].id);
|
const [activeTenantId, setActiveTenantId] = useState<string>(INITIAL_TENANTS[0].id);
|
||||||
|
|
||||||
// 'clinic' | 'patient' | 'superadmin'
|
// 'clinic' | 'patient' | 'superadmin' | 'onboarding'
|
||||||
const [portalMode, setPortalMode] = useState<'clinic' | 'patient' | 'superadmin'>('clinic');
|
const [portalMode, setPortalMode] = useState<'clinic' | 'patient' | 'superadmin' | 'onboarding'>('clinic');
|
||||||
|
|
||||||
// Staff Role: 'doctor' | 'front_desk' | 'billing_admin'
|
// Staff Role: 'doctor' | 'front_desk' | 'billing_admin'
|
||||||
const [staffRole, setStaffRole] = useState<StaffRole>('doctor');
|
const [staffRole, setStaffRole] = useState<StaffRole>('doctor');
|
||||||
@@ -81,6 +83,7 @@ export default function Home() {
|
|||||||
|
|
||||||
const [appointments, setAppointments] = useState<Appointment[]>(INITIAL_APPOINTMENTS);
|
const [appointments, setAppointments] = useState<Appointment[]>(INITIAL_APPOINTMENTS);
|
||||||
const [patients, setPatients] = useState<Patient[]>(INITIAL_PATIENTS);
|
const [patients, setPatients] = useState<Patient[]>(INITIAL_PATIENTS);
|
||||||
|
const [providers, setProviders] = useState<Provider[]>(INITIAL_PROVIDERS);
|
||||||
const [soapNotes, setSoapNotes] = useState<SoapNote[]>(INITIAL_SOAP_NOTES);
|
const [soapNotes, setSoapNotes] = useState<SoapNote[]>(INITIAL_SOAP_NOTES);
|
||||||
const [superbills, setSuperbills] = useState<Superbill[]>(INITIAL_SUPERBILLS);
|
const [superbills, setSuperbills] = useState<Superbill[]>(INITIAL_SUPERBILLS);
|
||||||
const [products, setProducts] = useState<RetailProduct[]>(INITIAL_PRODUCTS);
|
const [products, setProducts] = useState<RetailProduct[]>(INITIAL_PRODUCTS);
|
||||||
@@ -101,8 +104,8 @@ export default function Home() {
|
|||||||
const [activeSuperbill, setActiveSuperbill] = useState<Superbill>(INITIAL_SUPERBILLS[0]);
|
const [activeSuperbill, setActiveSuperbill] = useState<Superbill>(INITIAL_SUPERBILLS[0]);
|
||||||
|
|
||||||
const activeTenant = tenants.find((t) => t.id === activeTenantId) || tenants[0];
|
const activeTenant = tenants.find((t) => t.id === activeTenantId) || tenants[0];
|
||||||
const activeProviders = INITIAL_PROVIDERS.filter((p) => p.tenantId === activeTenant.id);
|
const activeProviders = providers.filter((p) => p.tenantId === activeTenant.id);
|
||||||
const activeProvider = activeProviders[0] || INITIAL_PROVIDERS[0];
|
const activeProvider = activeProviders[0] || providers[0];
|
||||||
|
|
||||||
// Toast notification helper
|
// Toast notification helper
|
||||||
const addToast = (type: 'success' | 'alert' | 'info', title: string, description?: string) => {
|
const addToast = (type: 'success' | 'alert' | 'info', title: string, description?: string) => {
|
||||||
@@ -261,6 +264,83 @@ export default function Home() {
|
|||||||
addToast('success', 'New Clinic Deployed', `${newTenant.name} onboarded to Mediusa OS`);
|
addToast('success', 'New Clinic Deployed', `${newTenant.name} onboarded to Mediusa OS`);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleCompleteOnboarding = (newTenant: ClinicTenant, leadDoctor: Provider) => {
|
||||||
|
setTenants((prev) => [...prev, newTenant]);
|
||||||
|
setProviders((prev) => [...prev, leadDoctor]);
|
||||||
|
setActiveTenantId(newTenant.id);
|
||||||
|
|
||||||
|
// Seed 1 active patient and encounter for instant clinic operation
|
||||||
|
const samplePatientId = `pat-${Date.now()}`;
|
||||||
|
const samplePatient: Patient = {
|
||||||
|
id: samplePatientId,
|
||||||
|
tenantId: newTenant.id,
|
||||||
|
firstName: 'Michael',
|
||||||
|
lastName: 'Sterling',
|
||||||
|
email: 'm.sterling@example.com',
|
||||||
|
phone: '(555) 349-1102',
|
||||||
|
dob: '1984-06-12',
|
||||||
|
gender: 'Male',
|
||||||
|
address: '742 Evergreen Terrace, McLean, VA 22102',
|
||||||
|
insuranceName: 'CareFirst BlueCross',
|
||||||
|
insuranceId: 'CFB-94021-X',
|
||||||
|
status: 'active',
|
||||||
|
chiefComplaint: 'Acute thoracic and lumbar stiffness following marathon training; radiates to right hamstring',
|
||||||
|
daysSinceLastVisit: 0,
|
||||||
|
lastVisitDate: new Date().toISOString().substring(0, 10),
|
||||||
|
nextAppointmentDate: new Date().toISOString().substring(0, 10),
|
||||||
|
vitals: {
|
||||||
|
bloodPressure: '122/78',
|
||||||
|
heartRate: 68,
|
||||||
|
temperature: '98.4°F',
|
||||||
|
oxygenSat: 99,
|
||||||
|
painLevel: 6,
|
||||||
|
bmi: '23.4',
|
||||||
|
allergies: ['Penicillin', 'Latex'],
|
||||||
|
contraindications: ['High-velocity cervical rotation'],
|
||||||
|
},
|
||||||
|
carePlan: {
|
||||||
|
title: 'Spinal Alignment & Thoracic Mobility Protocol',
|
||||||
|
totalVisits: 12,
|
||||||
|
completedVisits: 1,
|
||||||
|
frequency: '2x / week for 6 weeks',
|
||||||
|
targetCondition: 'Thoracolumbar Subluxation Complex',
|
||||||
|
startDate: new Date().toISOString().substring(0, 10),
|
||||||
|
status: 'on_track',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const sampleApt: Appointment = {
|
||||||
|
id: `apt-${Date.now()}`,
|
||||||
|
tenantId: newTenant.id,
|
||||||
|
patientId: samplePatientId,
|
||||||
|
patientName: `${samplePatient.firstName} ${samplePatient.lastName}`,
|
||||||
|
patientPhone: samplePatient.phone,
|
||||||
|
providerId: leadDoctor.id,
|
||||||
|
providerName: leadDoctor.name,
|
||||||
|
date: new Date().toISOString().substring(0, 10),
|
||||||
|
time: '10:00 AM',
|
||||||
|
durationMinutes: 45,
|
||||||
|
serviceType: 'Initial Clinical Examination & Spinal Adjustment',
|
||||||
|
status: 'confirmed',
|
||||||
|
room: 'Operatory 1',
|
||||||
|
notes: 'Initial evaluation under newly executed HIPAA BAA enclave. Full spinal visualizer and CMS-1500 queued.',
|
||||||
|
fee: 85,
|
||||||
|
};
|
||||||
|
|
||||||
|
setPatients((prev) => [samplePatient, ...prev]);
|
||||||
|
setAppointments((prev) => [sampleApt, ...prev]);
|
||||||
|
setActivePatient(samplePatient);
|
||||||
|
|
||||||
|
setPortalMode('clinic');
|
||||||
|
setClinicTab('calendar');
|
||||||
|
|
||||||
|
addToast(
|
||||||
|
'success',
|
||||||
|
'Clinic Enclave Deployed & BAA Sealed',
|
||||||
|
`${newTenant.name} is online on https://${newTenant.domain}. Lead Doctor: ${leadDoctor.name}.`
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
const handleSwitchTenant = (tenantId: string) => {
|
const handleSwitchTenant = (tenantId: string) => {
|
||||||
clinicalAudio.playClick();
|
clinicalAudio.playClick();
|
||||||
setActiveTenantId(tenantId);
|
setActiveTenantId(tenantId);
|
||||||
@@ -496,6 +576,22 @@ export default function Home() {
|
|||||||
<Building2 className="w-3.5 h-3.5" />
|
<Building2 className="w-3.5 h-3.5" />
|
||||||
Mediusa Super-Admin
|
Mediusa Super-Admin
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => {
|
||||||
|
clinicalAudio.playClick();
|
||||||
|
setPortalMode('onboarding');
|
||||||
|
}}
|
||||||
|
className={`px-3 py-1.5 rounded-md flex items-center gap-1.5 transition ${
|
||||||
|
portalMode === 'onboarding'
|
||||||
|
? 'bg-emerald-700 text-white font-bold shadow-xs'
|
||||||
|
: 'text-emerald-700 hover:text-emerald-900 bg-emerald-50/70 hover:bg-emerald-100/70 border border-emerald-200/60'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<Sparkles className="w-3.5 h-3.5 text-amber-500 animate-pulse" />
|
||||||
|
+ Onboard Doctor (BAA)
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -703,6 +799,13 @@ export default function Home() {
|
|||||||
onSwitchTenant={handleSwitchTenant}
|
onSwitchTenant={handleSwitchTenant}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{portalMode === 'onboarding' && (
|
||||||
|
<DoctorOnboardingView
|
||||||
|
onCompleteOnboarding={handleCompleteOnboarding}
|
||||||
|
onCancel={() => setPortalMode('clinic')}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
{/* Cancellation Waitlist Modal */}
|
{/* Cancellation Waitlist Modal */}
|
||||||
@@ -754,6 +857,7 @@ export default function Home() {
|
|||||||
audioEnabled={audioEnabled}
|
audioEnabled={audioEnabled}
|
||||||
onOpenCourtVault={() => setIsCourtVaultOpen(true)}
|
onOpenCourtVault={() => setIsCourtVaultOpen(true)}
|
||||||
onLockTerminal={() => setIsTerminalLocked(true)}
|
onLockTerminal={() => setIsTerminalLocked(true)}
|
||||||
|
onOpenOnboarding={() => setPortalMode('onboarding')}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* Hospital Clinical Toast Notification System */}
|
{/* Hospital Clinical Toast Notification System */}
|
||||||
|
|||||||
@@ -0,0 +1,854 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import React, { useState } from 'react';
|
||||||
|
import {
|
||||||
|
Building2,
|
||||||
|
UserCheck,
|
||||||
|
ShieldCheck,
|
||||||
|
CheckCircle2,
|
||||||
|
ArrowRight,
|
||||||
|
ArrowLeft,
|
||||||
|
Lock,
|
||||||
|
Download,
|
||||||
|
Sparkles,
|
||||||
|
Stethoscope,
|
||||||
|
Globe,
|
||||||
|
CreditCard,
|
||||||
|
Layers,
|
||||||
|
FileCheck2,
|
||||||
|
Calendar,
|
||||||
|
Zap,
|
||||||
|
} from 'lucide-react';
|
||||||
|
import { ClinicTenant, Provider, ClinicalDiscipline } from '@/types/clinical';
|
||||||
|
import { clinicalAudio } from '@/lib/clinical-audio';
|
||||||
|
import { generateHipaaBaaPdf } from '@/lib/pdf-generator';
|
||||||
|
|
||||||
|
interface DoctorOnboardingViewProps {
|
||||||
|
onCompleteOnboarding: (tenant: ClinicTenant, leadDoctor: Provider) => void;
|
||||||
|
onCancel?: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const DoctorOnboardingView: React.FC<DoctorOnboardingViewProps> = ({
|
||||||
|
onCompleteOnboarding,
|
||||||
|
onCancel,
|
||||||
|
}) => {
|
||||||
|
const [step, setStep] = useState<1 | 2 | 3 | 4>(1);
|
||||||
|
|
||||||
|
// Step 1: Practice & Provider Info
|
||||||
|
const [clinicName, setClinicName] = useState('Apex Spine & Sports Medicine');
|
||||||
|
const [address, setAddress] = useState('8400 Westpark Drive, Suite 210');
|
||||||
|
const [cityStateZip, setCityStateZip] = useState('McLean, VA 22102');
|
||||||
|
const [phone, setPhone] = useState('(703) 555-0198');
|
||||||
|
const [email, setEmail] = useState('doctor@apexspine.com');
|
||||||
|
const [subdomain, setSubdomain] = useState('apex-spine');
|
||||||
|
const [discipline, setDiscipline] = useState<ClinicalDiscipline>('chiropractic');
|
||||||
|
|
||||||
|
// Lead Doctor credentials
|
||||||
|
const [doctorName, setDoctorName] = useState('Dr. Jessica Vance');
|
||||||
|
const [doctorCredentials, setDoctorCredentials] = useState('D.C., CCSP, FICC');
|
||||||
|
const [npi, setNpi] = useState('1942857391');
|
||||||
|
const [taxId, setTaxId] = useState('54-1892401');
|
||||||
|
const [licenseNumber, setLicenseNumber] = useState('DC-48192');
|
||||||
|
|
||||||
|
// Step 2: Practice Configuration
|
||||||
|
const [roomCount, setRoomCount] = useState<number>(3);
|
||||||
|
const [planType, setPlanType] = useState<'Starter' | 'Pro Clinic' | 'Multi-Doc Enterprise'>('Pro Clinic');
|
||||||
|
const [standardAdjustmentFee, setStandardAdjustmentFee] = useState<number>(85);
|
||||||
|
const [initialExamFee, setInitialExamFee] = useState<number>(175);
|
||||||
|
const [rehabFee, setRehabFee] = useState<number>(65);
|
||||||
|
const [enableStripeCopay, setEnableStripeCopay] = useState<boolean>(true);
|
||||||
|
|
||||||
|
// Step 3: BAA Execution
|
||||||
|
const [signerTitle, setSignerTitle] = useState('Clinic Owner & Medical Director');
|
||||||
|
const [baaAttested, setBaaAttested] = useState<boolean>(true);
|
||||||
|
const [baaSigned, setBaaSigned] = useState<boolean>(false);
|
||||||
|
const [agreementId] = useState(`BAA-2026-${Math.floor(100000 + Math.random() * 900000)}`);
|
||||||
|
const [verificationHash] = useState(
|
||||||
|
'SHA-256: 9e7c3a812f901ab7c541289de6f2b3810a9c7d1e45f28019ab6741029c3f81e0'
|
||||||
|
);
|
||||||
|
|
||||||
|
// Step 4: Provisioning state
|
||||||
|
const [isProvisioning, setIsProvisioning] = useState(false);
|
||||||
|
const [provisionProgress, setProvisionProgress] = useState(0);
|
||||||
|
|
||||||
|
const handleNextStep = () => {
|
||||||
|
clinicalAudio.playClick();
|
||||||
|
if (step === 1) setStep(2);
|
||||||
|
else if (step === 2) setStep(3);
|
||||||
|
else if (step === 3) {
|
||||||
|
if (!baaSigned) {
|
||||||
|
clinicalAudio.playAlert();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setStep(4);
|
||||||
|
runProvisioningSequence();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handlePrevStep = () => {
|
||||||
|
clinicalAudio.playClick();
|
||||||
|
if (step === 2) setStep(1);
|
||||||
|
else if (step === 3) setStep(2);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleExecuteBaa = (e: React.FormEvent) => {
|
||||||
|
e.preventDefault();
|
||||||
|
if (!baaAttested || !doctorName.trim()) {
|
||||||
|
clinicalAudio.playAlert();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
clinicalAudio.playSuccess();
|
||||||
|
setBaaSigned(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDownloadBaaPdf = () => {
|
||||||
|
clinicalAudio.playSuccess();
|
||||||
|
generateHipaaBaaPdf({
|
||||||
|
tenant: {
|
||||||
|
name: clinicName,
|
||||||
|
address,
|
||||||
|
cityStateZip,
|
||||||
|
phone,
|
||||||
|
email,
|
||||||
|
taxId,
|
||||||
|
npi,
|
||||||
|
},
|
||||||
|
signerName: doctorName,
|
||||||
|
signerTitle,
|
||||||
|
signedAt: new Date().toISOString().replace('T', ' ').substring(0, 19) + ' UTC',
|
||||||
|
agreementId,
|
||||||
|
verificationHash,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const runProvisioningSequence = () => {
|
||||||
|
setIsProvisioning(true);
|
||||||
|
setProvisionProgress(15);
|
||||||
|
|
||||||
|
const stages = [
|
||||||
|
{ pct: 35, delay: 500 },
|
||||||
|
{ pct: 65, delay: 1100 },
|
||||||
|
{ pct: 90, delay: 1800 },
|
||||||
|
{ pct: 100, delay: 2400 },
|
||||||
|
];
|
||||||
|
|
||||||
|
stages.forEach((s) => {
|
||||||
|
setTimeout(() => {
|
||||||
|
setProvisionProgress(s.pct);
|
||||||
|
clinicalAudio.playClick();
|
||||||
|
}, s.delay);
|
||||||
|
});
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
setIsProvisioning(false);
|
||||||
|
clinicalAudio.playSuccess();
|
||||||
|
}, 2700);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleFinalLaunch = () => {
|
||||||
|
clinicalAudio.playSuccess();
|
||||||
|
|
||||||
|
const newTenant: ClinicTenant = {
|
||||||
|
id: `tenant-${Date.now()}`,
|
||||||
|
name: clinicName,
|
||||||
|
slug: subdomain || clinicName.toLowerCase().replace(/[^a-z0-9]/g, '-'),
|
||||||
|
domain: `${subdomain || 'clinic'}.mediusaos.com`,
|
||||||
|
phone,
|
||||||
|
email,
|
||||||
|
address,
|
||||||
|
cityStateZip,
|
||||||
|
npi,
|
||||||
|
taxId,
|
||||||
|
logoText: clinicName.substring(0, 2).toUpperCase(),
|
||||||
|
brandColor: '#0284c7',
|
||||||
|
accentColor: '#0ea5e9',
|
||||||
|
plan: planType,
|
||||||
|
mrr: planType === 'Starter' ? 99 : planType === 'Pro Clinic' ? 149 : 199,
|
||||||
|
stripeConnected: enableStripeCopay,
|
||||||
|
createdAt: new Date().toISOString(),
|
||||||
|
baaStatus: 'executed',
|
||||||
|
baaSignedAt: new Date().toISOString(),
|
||||||
|
baaSignerName: doctorName,
|
||||||
|
baaSignerTitle: signerTitle,
|
||||||
|
baaAgreementId: agreementId,
|
||||||
|
};
|
||||||
|
|
||||||
|
const newDoctor: Provider = {
|
||||||
|
id: `prov-${Date.now()}`,
|
||||||
|
tenantId: newTenant.id,
|
||||||
|
name: doctorName,
|
||||||
|
title: 'Medical Director',
|
||||||
|
credentials: doctorCredentials,
|
||||||
|
specialty:
|
||||||
|
discipline === 'chiropractic'
|
||||||
|
? 'Chiropractic Sports Medicine'
|
||||||
|
: discipline === 'physical_therapy'
|
||||||
|
? 'Orthopedic Physical Therapy'
|
||||||
|
: discipline === 'acupuncture'
|
||||||
|
? 'Integrative Acupuncture'
|
||||||
|
: 'Medical Massage Therapy',
|
||||||
|
npi,
|
||||||
|
email,
|
||||||
|
phone,
|
||||||
|
avatarUrl: 'https://images.unsplash.com/photo-1559839734-2b71ea197ec2?w=150&auto=format&fit=crop&q=80',
|
||||||
|
color: '#0284c7',
|
||||||
|
};
|
||||||
|
|
||||||
|
onCompleteOnboarding(newTenant, newDoctor);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="max-w-5xl mx-auto space-y-6 pb-16 animate-in fade-in duration-300">
|
||||||
|
{/* Header Banner */}
|
||||||
|
<div className="bg-slate-900 border border-slate-800 rounded-3xl p-6 sm:p-8 text-white relative overflow-hidden shadow-xl">
|
||||||
|
<div className="absolute -right-16 -bottom-16 w-64 h-64 bg-sky-500/10 rounded-full blur-3xl pointer-events-none" />
|
||||||
|
<div className="absolute -left-16 -top-16 w-64 h-64 bg-indigo-500/10 rounded-full blur-3xl pointer-events-none" />
|
||||||
|
|
||||||
|
<div className="relative z-10 flex flex-col md:flex-row md:items-center justify-between gap-6">
|
||||||
|
<div className="space-y-2">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<span className="px-3 py-1 rounded-full bg-sky-500/20 text-sky-300 border border-sky-400/30 text-xs font-bold uppercase tracking-wider flex items-center gap-1.5">
|
||||||
|
<Sparkles className="w-3.5 h-3.5 text-sky-400" /> Mediusa OS Clinic Onboarding
|
||||||
|
</span>
|
||||||
|
<span className="px-2.5 py-1 rounded-full bg-emerald-500/20 text-emerald-300 border border-emerald-400/30 text-xs font-bold flex items-center gap-1">
|
||||||
|
<ShieldCheck className="w-3.5 h-3.5 text-emerald-400" /> AWS HIPAA BAA Enclave
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<h1 className="text-2xl sm:text-3xl font-black tracking-tight text-white">
|
||||||
|
Launch Your High-Velocity Practice
|
||||||
|
</h1>
|
||||||
|
<p className="text-xs sm:text-sm text-slate-400 max-w-2xl">
|
||||||
|
Configure your practice credentials, setup subluxation fee schedules, execute your binding
|
||||||
|
HIPAA Business Associate Agreement, and deploy your live clinic in under 2 minutes.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{onCancel && (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={onCancel}
|
||||||
|
className="self-start md:self-auto px-3.5 py-1.5 bg-slate-800 hover:bg-slate-700 text-slate-300 rounded-xl text-xs font-semibold transition border border-slate-700"
|
||||||
|
>
|
||||||
|
Exit to Live Clinic
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 4-Step Progress Indicator */}
|
||||||
|
<div className="mt-8 pt-6 border-t border-slate-800/80 grid grid-cols-2 md:grid-cols-4 gap-3 text-xs">
|
||||||
|
{[
|
||||||
|
{ s: 1, title: '1. Practice & Credentials', desc: 'NPI, Tax ID & Subdomain' },
|
||||||
|
{ s: 2, title: '2. Operatories & Fees', desc: 'Rooms & Procedure Rates' },
|
||||||
|
{ s: 3, title: '3. HIPAA BAA Sign-Off', desc: 'Statutory 45 CFR § 164.504' },
|
||||||
|
{ s: 4, title: '4. Live Launch', desc: 'AWS Enclave Activation' },
|
||||||
|
].map((item) => (
|
||||||
|
<div
|
||||||
|
key={item.s}
|
||||||
|
className={`p-3 rounded-2xl border transition-all ${
|
||||||
|
step === item.s
|
||||||
|
? 'bg-sky-500/20 border-sky-400/40 text-white shadow-inner'
|
||||||
|
: step > item.s
|
||||||
|
? 'bg-slate-800/60 border-emerald-500/40 text-emerald-300'
|
||||||
|
: 'bg-slate-800/30 border-slate-800 text-slate-500'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<div className="font-bold flex items-center gap-1.5">
|
||||||
|
{step > item.s ? (
|
||||||
|
<CheckCircle2 className="w-3.5 h-3.5 text-emerald-400" />
|
||||||
|
) : (
|
||||||
|
<span
|
||||||
|
className={`w-4 h-4 rounded-full flex items-center justify-center text-[10px] ${
|
||||||
|
step === item.s ? 'bg-sky-500 text-white font-black' : 'bg-slate-700 text-slate-400'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{item.s}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
<span>{item.title}</span>
|
||||||
|
</div>
|
||||||
|
<p className="text-[10px] text-slate-400 mt-1 pl-5">{item.desc}</p>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* STEP 1: Practice Details & Medical Director Credentials */}
|
||||||
|
{step === 1 && (
|
||||||
|
<div className="bg-white border border-slate-200 rounded-3xl p-6 sm:p-8 shadow-xs space-y-6">
|
||||||
|
<div className="border-b border-slate-100 pb-4">
|
||||||
|
<h2 className="text-base font-bold text-slate-900 flex items-center gap-2">
|
||||||
|
<Building2 className="w-5 h-5 text-sky-700" /> Step 1: Practice Profile & Attending Physician
|
||||||
|
</h2>
|
||||||
|
<p className="text-xs text-slate-500 mt-1">
|
||||||
|
Enter your clinic information as it should appear on medical superbills, CMS-1500 claims, and court affidavits.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-5 text-xs">
|
||||||
|
<div>
|
||||||
|
<label className="block font-bold text-slate-700 mb-1">Clinic / Practice Legal Name *</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
value={clinicName}
|
||||||
|
onChange={(e) => setClinicName(e.target.value)}
|
||||||
|
className="w-full px-3.5 py-2.5 bg-slate-50 border border-slate-300 rounded-xl text-slate-900 focus:bg-white focus:outline-none focus:border-sky-500 font-semibold"
|
||||||
|
placeholder="e.g. Apex Spine & Sports Medicine"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label className="block font-bold text-slate-700 mb-1">Primary Clinical Discipline *</label>
|
||||||
|
<select
|
||||||
|
value={discipline}
|
||||||
|
onChange={(e) => setDiscipline(e.target.value as ClinicalDiscipline)}
|
||||||
|
className="w-full px-3.5 py-2.5 bg-slate-50 border border-slate-300 rounded-xl text-slate-900 focus:bg-white focus:outline-none focus:border-sky-500 font-semibold cursor-pointer"
|
||||||
|
>
|
||||||
|
<option value="chiropractic">🦴 Chiropractic (Spine CMT & Extremities)</option>
|
||||||
|
<option value="physical_therapy">🏃 Physical Therapy & Active Rehab</option>
|
||||||
|
<option value="acupuncture">🪡 Integrative Acupuncture & Meridians</option>
|
||||||
|
<option value="massage_therapy">💆 Medical Massage & Myofascial</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label className="block font-bold text-slate-700 mb-1">Clinic Physical Street Address *</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
value={address}
|
||||||
|
onChange={(e) => setAddress(e.target.value)}
|
||||||
|
className="w-full px-3.5 py-2.5 bg-slate-50 border border-slate-300 rounded-xl text-slate-900 focus:bg-white focus:outline-none focus:border-sky-500"
|
||||||
|
placeholder="Suite 210, 8400 Westpark Dr"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label className="block font-bold text-slate-700 mb-1">City, State, ZIP *</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
value={cityStateZip}
|
||||||
|
onChange={(e) => setCityStateZip(e.target.value)}
|
||||||
|
className="w-full px-3.5 py-2.5 bg-slate-50 border border-slate-300 rounded-xl text-slate-900 focus:bg-white focus:outline-none focus:border-sky-500"
|
||||||
|
placeholder="McLean, VA 22102"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label className="block font-bold text-slate-700 mb-1">Main Clinic Phone *</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
value={phone}
|
||||||
|
onChange={(e) => setPhone(e.target.value)}
|
||||||
|
className="w-full px-3.5 py-2.5 bg-slate-50 border border-slate-300 rounded-xl text-slate-900 focus:bg-white focus:outline-none focus:border-sky-500"
|
||||||
|
placeholder="(703) 555-0198"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label className="block font-bold text-slate-700 mb-1">Clinic Subdomain (MediusaOS.com) *</label>
|
||||||
|
<div className="flex items-center">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
value={subdomain}
|
||||||
|
onChange={(e) => setSubdomain(e.target.value.toLowerCase().replace(/[^a-z0-9-]/g, ''))}
|
||||||
|
className="w-full px-3.5 py-2.5 bg-slate-50 border border-slate-300 rounded-l-xl text-slate-900 focus:bg-white focus:outline-none focus:border-sky-500 font-mono text-right"
|
||||||
|
placeholder="apex-spine"
|
||||||
|
/>
|
||||||
|
<span className="bg-slate-100 border border-l-0 border-slate-300 px-3 py-2.5 rounded-r-xl text-slate-600 font-mono text-xs whitespace-nowrap">
|
||||||
|
.mediusaos.com
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="pt-4 border-t border-slate-100">
|
||||||
|
<h3 className="text-xs font-bold uppercase tracking-wider text-slate-500 mb-3 flex items-center gap-1.5">
|
||||||
|
<Stethoscope className="w-4 h-4 text-sky-700" /> Medical Director & Regulatory Identifiers
|
||||||
|
</h3>
|
||||||
|
|
||||||
|
<div className="grid grid-cols-1 sm:grid-cols-3 gap-4 text-xs">
|
||||||
|
<div>
|
||||||
|
<label className="block font-bold text-slate-700 mb-1">Attending Doctor Name *</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
value={doctorName}
|
||||||
|
onChange={(e) => setDoctorName(e.target.value)}
|
||||||
|
className="w-full px-3.5 py-2 bg-slate-50 border border-slate-300 rounded-xl text-slate-900 focus:outline-none focus:border-sky-500 font-semibold"
|
||||||
|
placeholder="Dr. Jessica Vance"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label className="block font-bold text-slate-700 mb-1">Professional Credentials</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
value={doctorCredentials}
|
||||||
|
onChange={(e) => setDoctorCredentials(e.target.value)}
|
||||||
|
className="w-full px-3.5 py-2 bg-slate-50 border border-slate-300 rounded-xl text-slate-900 focus:outline-none focus:border-sky-500"
|
||||||
|
placeholder="D.C., CCSP, FICC"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label className="block font-bold text-slate-700 mb-1">Billing NPI (10 Digits) *</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
maxLength={10}
|
||||||
|
value={npi}
|
||||||
|
onChange={(e) => setNpi(e.target.value.replace(/\D/g, ''))}
|
||||||
|
className="w-full px-3.5 py-2 bg-slate-50 border border-slate-300 rounded-xl text-slate-900 font-mono focus:outline-none focus:border-sky-500"
|
||||||
|
placeholder="1942857391"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label className="block font-bold text-slate-700 mb-1">Federal Tax ID / EIN *</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
value={taxId}
|
||||||
|
onChange={(e) => setTaxId(e.target.value)}
|
||||||
|
className="w-full px-3.5 py-2 bg-slate-50 border border-slate-300 rounded-xl text-slate-900 font-mono focus:outline-none focus:border-sky-500"
|
||||||
|
placeholder="54-1892401"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label className="block font-bold text-slate-700 mb-1">State License Number *</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
value={licenseNumber}
|
||||||
|
onChange={(e) => setLicenseNumber(e.target.value)}
|
||||||
|
className="w-full px-3.5 py-2 bg-slate-50 border border-slate-300 rounded-xl text-slate-900 font-mono focus:outline-none focus:border-sky-500"
|
||||||
|
placeholder="DC-48192"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label className="block font-bold text-slate-700 mb-1">Clinical Contact Email *</label>
|
||||||
|
<input
|
||||||
|
type="email"
|
||||||
|
value={email}
|
||||||
|
onChange={(e) => setEmail(e.target.value)}
|
||||||
|
className="w-full px-3.5 py-2 bg-slate-50 border border-slate-300 rounded-xl text-slate-900 focus:outline-none focus:border-sky-500"
|
||||||
|
placeholder="doctor@apexspine.com"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex items-center justify-end pt-4 border-t border-slate-100">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={handleNextStep}
|
||||||
|
disabled={!clinicName.trim() || !doctorName.trim() || !npi.trim()}
|
||||||
|
className="px-6 py-2.5 bg-sky-700 hover:bg-sky-800 disabled:opacity-50 text-white rounded-xl text-xs font-bold transition shadow-xs flex items-center gap-2"
|
||||||
|
>
|
||||||
|
Continue to Operatories & Fees <ArrowRight className="w-4 h-4" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* STEP 2: Operatories & Fee Schedule Setup */}
|
||||||
|
{step === 2 && (
|
||||||
|
<div className="bg-white border border-slate-200 rounded-3xl p-6 sm:p-8 shadow-xs space-y-6">
|
||||||
|
<div className="border-b border-slate-100 pb-4">
|
||||||
|
<h2 className="text-base font-bold text-slate-900 flex items-center gap-2">
|
||||||
|
<Layers className="w-5 h-5 text-sky-700" /> Step 2: Clinic Operatories & Fee Schedule
|
||||||
|
</h2>
|
||||||
|
<p className="text-xs text-slate-500 mt-1">
|
||||||
|
Configure your treatment bays and standard clinical fee schedules for out-of-network superbills.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-6 text-xs">
|
||||||
|
{/* Rooms Setup */}
|
||||||
|
<div className="space-y-4">
|
||||||
|
<div>
|
||||||
|
<label className="block font-bold text-slate-700 mb-1.5">Number of Treatment Bays / Exam Rooms</label>
|
||||||
|
<div className="grid grid-cols-4 gap-2">
|
||||||
|
{[1, 2, 3, 4, 5, 6, 7, 8].map((num) => (
|
||||||
|
<button
|
||||||
|
key={num}
|
||||||
|
type="button"
|
||||||
|
onClick={() => {
|
||||||
|
clinicalAudio.playClick();
|
||||||
|
setRoomCount(num);
|
||||||
|
}}
|
||||||
|
className={`py-2 rounded-xl border text-xs font-bold transition ${
|
||||||
|
roomCount === num
|
||||||
|
? 'bg-sky-700 border-sky-700 text-white shadow-xs'
|
||||||
|
: 'bg-slate-50 border-slate-200 text-slate-700 hover:bg-slate-100'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{num} {num === 1 ? 'Room' : 'Rooms'}
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Plan Type */}
|
||||||
|
<div>
|
||||||
|
<label className="block font-bold text-slate-700 mb-1.5">Mediusa OS Edition Tier</label>
|
||||||
|
<div className="grid grid-cols-3 gap-2">
|
||||||
|
{[
|
||||||
|
{ id: 'Starter', label: 'Starter ($99/mo)', desc: '1 Solo Doctor' },
|
||||||
|
{ id: 'Pro Clinic', label: 'Pro Clinic ($149/mo)', desc: '1-3 Docs + Staff' },
|
||||||
|
{ id: 'Multi-Doc Enterprise', label: 'Enterprise ($199/mo)', desc: 'Unlimited Bays' },
|
||||||
|
].map((p) => (
|
||||||
|
<button
|
||||||
|
key={p.id}
|
||||||
|
type="button"
|
||||||
|
onClick={() => {
|
||||||
|
clinicalAudio.playClick();
|
||||||
|
setPlanType(p.id as any);
|
||||||
|
}}
|
||||||
|
className={`p-3 rounded-xl border text-left transition ${
|
||||||
|
planType === p.id
|
||||||
|
? 'bg-sky-50 border-sky-300 text-sky-900 shadow-2xs'
|
||||||
|
: 'bg-slate-50 border-slate-200 text-slate-600 hover:bg-slate-100'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<span className="block font-bold text-xs">{p.label}</span>
|
||||||
|
<span className="block text-[10px] text-slate-500 mt-0.5">{p.desc}</span>
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Stripe Copay Terminal */}
|
||||||
|
<div className="p-4 bg-slate-50 border border-slate-200 rounded-2xl flex items-center justify-between">
|
||||||
|
<div>
|
||||||
|
<span className="font-bold text-slate-800 block">Stripe Card Terminal & Copay Checkout</span>
|
||||||
|
<span className="text-[11px] text-slate-500 block">
|
||||||
|
Accept tap-to-pay, FSA/HSA cards, and store encrypted cards for care plan installments.
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
checked={enableStripeCopay}
|
||||||
|
onChange={(e) => setEnableStripeCopay(e.target.checked)}
|
||||||
|
className="w-5 h-5 text-sky-600 rounded border-slate-300 focus:ring-sky-500"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Default Fee Schedule */}
|
||||||
|
<div className="bg-slate-50/70 border border-slate-200 rounded-2xl p-5 space-y-4">
|
||||||
|
<div className="flex items-center justify-between pb-2 border-b border-slate-200">
|
||||||
|
<span className="font-bold text-slate-800 text-xs">Standard Baseline Fee Schedule</span>
|
||||||
|
<span className="text-[10px] font-mono text-emerald-700 bg-emerald-100 px-2 py-0.5 rounded font-bold">
|
||||||
|
CMS-1500 Ready
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label className="block text-slate-600 font-semibold mb-1">
|
||||||
|
Routine Chiropractic Manipulation (CPT 98940/98941)
|
||||||
|
</label>
|
||||||
|
<div className="relative">
|
||||||
|
<span className="absolute left-3 top-2 font-mono text-slate-500">$</span>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
value={standardAdjustmentFee}
|
||||||
|
onChange={(e) => setStandardAdjustmentFee(Number(e.target.value))}
|
||||||
|
className="w-full pl-7 pr-3 py-1.5 bg-white border border-slate-300 rounded-xl text-slate-800 font-mono font-bold focus:outline-none focus:border-sky-500"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label className="block text-slate-600 font-semibold mb-1">
|
||||||
|
Comprehensive Initial Examination (CPT 99203)
|
||||||
|
</label>
|
||||||
|
<div className="relative">
|
||||||
|
<span className="absolute left-3 top-2 font-mono text-slate-500">$</span>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
value={initialExamFee}
|
||||||
|
onChange={(e) => setInitialExamFee(Number(e.target.value))}
|
||||||
|
className="w-full pl-7 pr-3 py-1.5 bg-white border border-slate-300 rounded-xl text-slate-800 font-mono font-bold focus:outline-none focus:border-sky-500"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label className="block text-slate-600 font-semibold mb-1">
|
||||||
|
Therapeutic Rehab & Myofascial (CPT 97110/97140)
|
||||||
|
</label>
|
||||||
|
<div className="relative">
|
||||||
|
<span className="absolute left-3 top-2 font-mono text-slate-500">$</span>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
value={rehabFee}
|
||||||
|
onChange={(e) => setRehabFee(Number(e.target.value))}
|
||||||
|
className="w-full pl-7 pr-3 py-1.5 bg-white border border-slate-300 rounded-xl text-slate-800 font-mono font-bold focus:outline-none focus:border-sky-500"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p className="text-[10px] text-slate-500 italic">
|
||||||
|
Fees can be modified per provider or per discipline inside the billing console anytime.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex items-center justify-between pt-4 border-t border-slate-100">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={handlePrevStep}
|
||||||
|
className="px-4 py-2 bg-slate-100 hover:bg-slate-200 text-slate-700 rounded-xl text-xs font-bold transition flex items-center gap-1.5"
|
||||||
|
>
|
||||||
|
<ArrowLeft className="w-4 h-4" /> Back to Credentials
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={handleNextStep}
|
||||||
|
className="px-6 py-2.5 bg-sky-700 hover:bg-sky-800 text-white rounded-xl text-xs font-bold transition shadow-xs flex items-center gap-2"
|
||||||
|
>
|
||||||
|
Proceed to HIPAA BAA Execution <ArrowRight className="w-4 h-4" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* STEP 3: Statutory HIPAA BAA Execution */}
|
||||||
|
{step === 3 && (
|
||||||
|
<div className="bg-white border border-slate-200 rounded-3xl p-6 sm:p-8 shadow-xs space-y-6">
|
||||||
|
<div className="border-b border-slate-100 pb-4 flex flex-col sm:flex-row sm:items-center justify-between gap-2">
|
||||||
|
<div>
|
||||||
|
<h2 className="text-base font-bold text-slate-900 flex items-center gap-2">
|
||||||
|
<FileCheck2 className="w-5 h-5 text-sky-700" /> Step 3: Statutory HIPAA Business Associate Agreement
|
||||||
|
</h2>
|
||||||
|
<p className="text-xs text-slate-500 mt-1">
|
||||||
|
Mandatory federal agreement under <strong>45 CFR § 164.502(e)</strong> and <strong>§ 164.504(e)</strong> establishing
|
||||||
|
legal data custody on the AWS HIPAA enclave.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{baaSigned && (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={handleDownloadBaaPdf}
|
||||||
|
className="self-start sm:self-auto px-3 py-1.5 bg-emerald-50 hover:bg-emerald-100 border border-emerald-300 text-emerald-800 rounded-xl text-xs font-bold flex items-center gap-1.5 transition"
|
||||||
|
>
|
||||||
|
<Download className="w-3.5 h-3.5 text-emerald-700" /> Download Executed BAA Binder (PDF)
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Legal Agreement Scroll Box */}
|
||||||
|
<div className="p-4 bg-slate-50 border border-slate-200 rounded-2xl max-h-52 overflow-y-auto text-[11px] text-slate-700 space-y-2.5 font-serif leading-relaxed">
|
||||||
|
<h4 className="font-bold font-sans text-xs text-slate-900 uppercase">
|
||||||
|
BUSINESS ASSOCIATE ADDENDUM (BAA) — REVISED STATUTORY TERMS (2026)
|
||||||
|
</h4>
|
||||||
|
<p>
|
||||||
|
This Business Associate Agreement is entered into by and between <strong>{clinicName}</strong> (“Covered Entity”),
|
||||||
|
and <strong>Mediusa OS / aai.dev Healthcare Cloud Group</strong> (“Business Associate”).
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<strong>1. Permitted Uses and Disclosures:</strong> Business Associate agrees not to use or disclose Protected
|
||||||
|
Health Information (ePHI) other than as permitted or required by this Agreement, or as required by law.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<strong>2. Appropriate Safeguards:</strong> Business Associate warrants that all electronic PHI is exclusively
|
||||||
|
stored within an enterprise Amazon Web Services (AWS Account 691829191123) healthcare enclave governed under an
|
||||||
|
executed AWS Business Associate Addendum, utilizing hardware AES-256 KMS encryption at rest and TLS 1.3 in transit.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<strong>3. Subcontractor Liability Flow-Down:</strong> In accordance with 45 CFR § 164.504(e)(1)(ii), Business
|
||||||
|
Associate warrants that any downstream subcontractors (including AWS) have agreed in writing to the exact same statutory restrictions.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<strong>4. Evidentiary Integrity & Audit:</strong> Clinical encounters are sealed with NIST SHA-256 cryptographic
|
||||||
|
digests and comply with 21 CFR Part 11 and Fed. R. Evid. 902(11) self-authenticating record standards.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Digital Signature Form */}
|
||||||
|
{!baaSigned ? (
|
||||||
|
<form onSubmit={handleExecuteBaa} className="p-5 bg-sky-50/50 border border-sky-200 rounded-2xl space-y-4">
|
||||||
|
<div className="flex items-center gap-2 text-sky-900 font-bold text-xs">
|
||||||
|
<Lock className="w-4 h-4 text-sky-700" />
|
||||||
|
<span>ELECTRONIC SIGNATURE & LEGAL EXECUTION</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4 text-xs">
|
||||||
|
<div>
|
||||||
|
<label className="block font-bold text-slate-700 mb-1">Authorized Signer Printed Name *</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
required
|
||||||
|
value={doctorName}
|
||||||
|
onChange={(e) => setDoctorName(e.target.value)}
|
||||||
|
className="w-full px-3.5 py-2 bg-white border border-slate-300 rounded-xl text-slate-900 font-semibold focus:outline-none focus:border-sky-500"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label className="block font-bold text-slate-700 mb-1">Signer Official Title *</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
required
|
||||||
|
value={signerTitle}
|
||||||
|
onChange={(e) => setSignerTitle(e.target.value)}
|
||||||
|
className="w-full px-3.5 py-2 bg-white border border-slate-300 rounded-xl text-slate-900 focus:outline-none focus:border-sky-500"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex items-start gap-2 pt-1">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
id="onboard-baa-attest"
|
||||||
|
checked={baaAttested}
|
||||||
|
onChange={(e) => setBaaAttested(e.target.checked)}
|
||||||
|
className="mt-0.5 rounded border-slate-300 text-sky-700 focus:ring-sky-500"
|
||||||
|
/>
|
||||||
|
<label htmlFor="onboard-baa-attest" className="text-[11px] text-slate-700 cursor-pointer">
|
||||||
|
I certify under 28 U.S.C. § 1746 that I am an authorized representative of <strong>{clinicName}</strong>{' '}
|
||||||
|
with legal authority to bind the Covered Entity to this federal HIPAA Business Associate Agreement.
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
disabled={!baaAttested || !doctorName.trim()}
|
||||||
|
className="w-full py-3 bg-slate-900 hover:bg-slate-800 text-white rounded-xl text-xs font-bold transition shadow-xs flex items-center justify-center gap-2 disabled:opacity-50"
|
||||||
|
>
|
||||||
|
<ShieldCheck className="w-4 h-4 text-emerald-400" />
|
||||||
|
Digitally Execute BAA & Seal Agreement
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
) : (
|
||||||
|
<div className="p-5 bg-emerald-50 border border-emerald-300 rounded-2xl space-y-2">
|
||||||
|
<div className="flex items-center gap-2 text-emerald-900 font-bold text-xs">
|
||||||
|
<CheckCircle2 className="w-5 h-5 text-emerald-600" />
|
||||||
|
<span>HIPAA BUSINESS ASSOCIATE AGREEMENT OFFICIALLY EXECUTED & BINDING</span>
|
||||||
|
</div>
|
||||||
|
<div className="text-xs text-slate-700 space-y-0.5">
|
||||||
|
<p>
|
||||||
|
<strong>Signatory:</strong> {doctorName} ({signerTitle})
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<strong>Agreement ID:</strong> <span className="font-mono">{agreementId}</span>
|
||||||
|
</p>
|
||||||
|
<p className="font-mono text-[10px] text-slate-500 truncate">
|
||||||
|
<strong>Verification Digest:</strong> {verificationHash}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div className="flex items-center justify-between pt-4 border-t border-slate-100">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={handlePrevStep}
|
||||||
|
className="px-4 py-2 bg-slate-100 hover:bg-slate-200 text-slate-700 rounded-xl text-xs font-bold transition flex items-center gap-1.5"
|
||||||
|
>
|
||||||
|
<ArrowLeft className="w-4 h-4" /> Back to Fees
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={handleNextStep}
|
||||||
|
disabled={!baaSigned}
|
||||||
|
className="px-6 py-2.5 bg-emerald-700 hover:bg-emerald-800 disabled:opacity-50 text-white rounded-xl text-xs font-bold transition shadow-xs flex items-center gap-2"
|
||||||
|
>
|
||||||
|
Deploy & Activate Enclave <ArrowRight className="w-4 h-4" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* STEP 4: Live Provisioning & Handoff */}
|
||||||
|
{step === 4 && (
|
||||||
|
<div className="bg-white border border-slate-200 rounded-3xl p-6 sm:p-10 shadow-xs space-y-6 text-center">
|
||||||
|
{isProvisioning ? (
|
||||||
|
<div className="max-w-md mx-auto py-8 space-y-4">
|
||||||
|
<div className="w-16 h-16 rounded-3xl bg-sky-50 border border-sky-200 flex items-center justify-center mx-auto text-sky-700 animate-pulse">
|
||||||
|
<Zap className="w-8 h-8" />
|
||||||
|
</div>
|
||||||
|
<h3 className="text-lg font-bold text-slate-900">Provisioning Clinic Enclave on AWS...</h3>
|
||||||
|
<p className="text-xs text-slate-500">
|
||||||
|
Registering multi-tenant database partitions, setting up provider RBAC roles, and securing KMS keys.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
{/* Progress Bar */}
|
||||||
|
<div className="w-full bg-slate-100 h-2.5 rounded-full overflow-hidden border border-slate-200">
|
||||||
|
<div
|
||||||
|
className="bg-sky-700 h-full transition-all duration-300"
|
||||||
|
style={{ width: `${provisionProgress}%` }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<span className="text-xs font-mono font-bold text-slate-600">{provisionProgress}% Complete</span>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="max-w-xl mx-auto py-4 space-y-5">
|
||||||
|
<div className="w-16 h-16 rounded-3xl bg-emerald-100 border border-emerald-300 flex items-center justify-center mx-auto text-emerald-700">
|
||||||
|
<CheckCircle2 className="w-8 h-8" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<span className="text-[10px] uppercase font-bold text-emerald-800 tracking-wider bg-emerald-100 px-3 py-1 rounded-full border border-emerald-200">
|
||||||
|
Enclave Ready & BAA Sealed
|
||||||
|
</span>
|
||||||
|
<h3 className="text-xl sm:text-2xl font-black text-slate-900 mt-2">
|
||||||
|
Welcome to Mediusa OS, {doctorName}!
|
||||||
|
</h3>
|
||||||
|
<p className="text-xs text-slate-600 mt-1">
|
||||||
|
<strong>{clinicName}</strong> is now initialized with full sub-30s SOAP charting, 2D spine maps,
|
||||||
|
CMS-1500 superbills, and 21 CFR Part 11 court-admissible audit logging.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Clinic Live Summary Card */}
|
||||||
|
<div className="p-4 bg-slate-50 border border-slate-200 rounded-2xl text-left text-xs space-y-2">
|
||||||
|
<div className="flex items-center justify-between pb-2 border-b border-slate-200">
|
||||||
|
<span className="font-bold text-slate-800">Clinic Portal Domain:</span>
|
||||||
|
<span className="font-mono font-bold text-sky-800 flex items-center gap-1">
|
||||||
|
<Globe className="w-3.5 h-3.5 text-sky-700" />
|
||||||
|
https://{subdomain}.mediusaos.com
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="grid grid-cols-2 gap-2 text-slate-600">
|
||||||
|
<div>
|
||||||
|
<span className="text-slate-400 block text-[10px]">Medical Director:</span>
|
||||||
|
<span className="font-semibold text-slate-800">{doctorName}, {doctorCredentials}</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span className="text-slate-400 block text-[10px]">NPI / Tax ID:</span>
|
||||||
|
<span className="font-mono text-slate-800">{npi} • {taxId}</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span className="text-slate-400 block text-[10px]">Active Operatories:</span>
|
||||||
|
<span className="font-semibold text-slate-800">{roomCount} Treatment Bays</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span className="text-slate-400 block text-[10px]">HIPAA Compliance:</span>
|
||||||
|
<span className="font-bold text-emerald-700">100% Certified (BAA Sealed)</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex flex-col sm:flex-row items-center justify-center gap-3 pt-2">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={handleDownloadBaaPdf}
|
||||||
|
className="w-full sm:w-auto px-4 py-2.5 bg-slate-100 hover:bg-slate-200 text-slate-700 rounded-xl text-xs font-bold transition flex items-center justify-center gap-2"
|
||||||
|
>
|
||||||
|
<Download className="w-4 h-4 text-slate-600" /> Download BAA Binder PDF
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={handleFinalLaunch}
|
||||||
|
className="w-full sm:w-auto px-6 py-2.5 bg-sky-700 hover:bg-sky-800 text-white rounded-xl text-xs font-bold transition shadow-xs flex items-center justify-center gap-2"
|
||||||
|
>
|
||||||
|
<Calendar className="w-4 h-4 text-white" /> Launch Live Practice Dashboard
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -46,6 +46,7 @@ interface CommandPaletteProps {
|
|||||||
audioEnabled: boolean;
|
audioEnabled: boolean;
|
||||||
onOpenCourtVault?: () => void;
|
onOpenCourtVault?: () => void;
|
||||||
onLockTerminal?: () => void;
|
onLockTerminal?: () => void;
|
||||||
|
onOpenOnboarding?: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const CommandPalette: React.FC<CommandPaletteProps> = ({
|
export const CommandPalette: React.FC<CommandPaletteProps> = ({
|
||||||
@@ -60,6 +61,7 @@ export const CommandPalette: React.FC<CommandPaletteProps> = ({
|
|||||||
audioEnabled,
|
audioEnabled,
|
||||||
onOpenCourtVault,
|
onOpenCourtVault,
|
||||||
onLockTerminal,
|
onLockTerminal,
|
||||||
|
onOpenOnboarding,
|
||||||
}) => {
|
}) => {
|
||||||
const [query, setQuery] = useState('');
|
const [query, setQuery] = useState('');
|
||||||
const [selectedIndex, setSelectedIndex] = useState(0);
|
const [selectedIndex, setSelectedIndex] = useState(0);
|
||||||
@@ -208,6 +210,18 @@ export const CommandPalette: React.FC<CommandPaletteProps> = ({
|
|||||||
onClose();
|
onClose();
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: 'action-onboard-doctor',
|
||||||
|
category: 'Actions',
|
||||||
|
title: 'Onboard New Clinic & Sign Statutory BAA',
|
||||||
|
subtitle: '4-step practice registration, NPI setup, fee schedules & HIPAA enclave launch',
|
||||||
|
badge: 'Onboarding',
|
||||||
|
icon: <Sparkles className="w-4 h-4 text-emerald-600" />,
|
||||||
|
action: () => {
|
||||||
|
if (onOpenOnboarding) onOpenOnboarding();
|
||||||
|
onClose();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
// Staff Roles
|
// Staff Roles
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user