feat: Doctor Onboarding Portal with statutory BAA execution and enclave deployment
This commit is contained in:
+108
-4
@@ -24,6 +24,7 @@ import {
|
||||
PrePaidPackage,
|
||||
WaitlistEntry,
|
||||
StaffRole,
|
||||
Provider,
|
||||
} from '@/types/clinical';
|
||||
import { CalendarView } from '@/components/calendar/CalendarView';
|
||||
import { SoapChartEditor } from '@/components/charting/SoapChartEditor';
|
||||
@@ -39,6 +40,7 @@ import { CourtAuditVaultModal } from '@/components/compliance/CourtAuditVaultMod
|
||||
import { CommandPalette } from '@/components/ui/CommandPalette';
|
||||
import { InactivityLockoutModal } from '@/components/ui/InactivityLockoutModal';
|
||||
import { ClinicalToastContainer, ToastMessage } from '@/components/ui/ClinicalToast';
|
||||
import { DoctorOnboardingView } from '@/components/onboarding/DoctorOnboardingView';
|
||||
import { clinicalAudio } from '@/lib/clinical-audio';
|
||||
import {
|
||||
Calendar,
|
||||
@@ -68,8 +70,8 @@ export default function Home() {
|
||||
const [tenants, setTenants] = useState<ClinicTenant[]>(INITIAL_TENANTS);
|
||||
const [activeTenantId, setActiveTenantId] = useState<string>(INITIAL_TENANTS[0].id);
|
||||
|
||||
// 'clinic' | 'patient' | 'superadmin'
|
||||
const [portalMode, setPortalMode] = useState<'clinic' | 'patient' | 'superadmin'>('clinic');
|
||||
// 'clinic' | 'patient' | 'superadmin' | 'onboarding'
|
||||
const [portalMode, setPortalMode] = useState<'clinic' | 'patient' | 'superadmin' | 'onboarding'>('clinic');
|
||||
|
||||
// Staff Role: 'doctor' | 'front_desk' | 'billing_admin'
|
||||
const [staffRole, setStaffRole] = useState<StaffRole>('doctor');
|
||||
@@ -81,6 +83,7 @@ export default function Home() {
|
||||
|
||||
const [appointments, setAppointments] = useState<Appointment[]>(INITIAL_APPOINTMENTS);
|
||||
const [patients, setPatients] = useState<Patient[]>(INITIAL_PATIENTS);
|
||||
const [providers, setProviders] = useState<Provider[]>(INITIAL_PROVIDERS);
|
||||
const [soapNotes, setSoapNotes] = useState<SoapNote[]>(INITIAL_SOAP_NOTES);
|
||||
const [superbills, setSuperbills] = useState<Superbill[]>(INITIAL_SUPERBILLS);
|
||||
const [products, setProducts] = useState<RetailProduct[]>(INITIAL_PRODUCTS);
|
||||
@@ -101,8 +104,8 @@ export default function Home() {
|
||||
const [activeSuperbill, setActiveSuperbill] = useState<Superbill>(INITIAL_SUPERBILLS[0]);
|
||||
|
||||
const activeTenant = tenants.find((t) => t.id === activeTenantId) || tenants[0];
|
||||
const activeProviders = INITIAL_PROVIDERS.filter((p) => p.tenantId === activeTenant.id);
|
||||
const activeProvider = activeProviders[0] || INITIAL_PROVIDERS[0];
|
||||
const activeProviders = providers.filter((p) => p.tenantId === activeTenant.id);
|
||||
const activeProvider = activeProviders[0] || providers[0];
|
||||
|
||||
// Toast notification helper
|
||||
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`);
|
||||
};
|
||||
|
||||
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) => {
|
||||
clinicalAudio.playClick();
|
||||
setActiveTenantId(tenantId);
|
||||
@@ -496,6 +576,22 @@ export default function Home() {
|
||||
<Building2 className="w-3.5 h-3.5" />
|
||||
Mediusa Super-Admin
|
||||
</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>
|
||||
|
||||
@@ -703,6 +799,13 @@ export default function Home() {
|
||||
onSwitchTenant={handleSwitchTenant}
|
||||
/>
|
||||
)}
|
||||
|
||||
{portalMode === 'onboarding' && (
|
||||
<DoctorOnboardingView
|
||||
onCompleteOnboarding={handleCompleteOnboarding}
|
||||
onCancel={() => setPortalMode('clinic')}
|
||||
/>
|
||||
)}
|
||||
</main>
|
||||
|
||||
{/* Cancellation Waitlist Modal */}
|
||||
@@ -754,6 +857,7 @@ export default function Home() {
|
||||
audioEnabled={audioEnabled}
|
||||
onOpenCourtVault={() => setIsCourtVaultOpen(true)}
|
||||
onLockTerminal={() => setIsTerminalLocked(true)}
|
||||
onOpenOnboarding={() => setPortalMode('onboarding')}
|
||||
/>
|
||||
|
||||
{/* 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;
|
||||
onOpenCourtVault?: () => void;
|
||||
onLockTerminal?: () => void;
|
||||
onOpenOnboarding?: () => void;
|
||||
}
|
||||
|
||||
export const CommandPalette: React.FC<CommandPaletteProps> = ({
|
||||
@@ -60,6 +61,7 @@ export const CommandPalette: React.FC<CommandPaletteProps> = ({
|
||||
audioEnabled,
|
||||
onOpenCourtVault,
|
||||
onLockTerminal,
|
||||
onOpenOnboarding,
|
||||
}) => {
|
||||
const [query, setQuery] = useState('');
|
||||
const [selectedIndex, setSelectedIndex] = useState(0);
|
||||
@@ -208,6 +210,18 @@ export const CommandPalette: React.FC<CommandPaletteProps> = ({
|
||||
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
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user