style: convert UI palette to clean clinical hospital theme

This commit is contained in:
2026-09-05 14:17:24 -07:00
parent 39829b79d3
commit e681f066bb
13 changed files with 785 additions and 824 deletions
+31 -15
View File
@@ -1,26 +1,42 @@
@import "tailwindcss";
:root {
--background: #ffffff;
--foreground: #171717;
--background: #f8fafc; /* Hospital sterile off-white */
--foreground: #0f172a; /* Deep clinical slate */
--medical-blue: #0284c7; /* Classic Hospital / Medical Cross Blue */
--medical-navy: #0f172a; /* Clinical Navy */
--medical-slate: #f1f5f9; /* Examination table cool slate */
--medical-border: #e2e8f0;
}
@theme inline {
--color-background: var(--background);
--color-foreground: var(--foreground);
--font-sans: var(--font-geist-sans);
--font-mono: var(--font-geist-mono);
}
@media (prefers-color-scheme: dark) {
:root {
--background: #0a0a0a;
--foreground: #ededed;
}
.dark {
--background: #0b1329; /* Hospital Night Shift Deep Navy */
--foreground: #f8fafc;
--medical-blue: #38bdf8;
--medical-navy: #0f172a;
--medical-slate: #1e293b;
--medical-border: #334155;
}
body {
background: var(--background);
color: var(--foreground);
font-family: Arial, Helvetica, sans-serif;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
-webkit-font-smoothing: antialiased;
}
/* Custom Hospital Scrollbars */
::-webkit-scrollbar {
width: 6px;
height: 6px;
}
::-webkit-scrollbar-track {
background: transparent;
}
::-webkit-scrollbar-thumb {
background: #cbd5e1;
border-radius: 9999px;
}
.dark ::-webkit-scrollbar-thumb {
background: #475569;
}
+4 -18
View File
@@ -1,21 +1,10 @@
import type { Metadata } from 'next';
import { Geist, Geist_Mono } from 'next/font/google';
import './globals.css';
const geistSans = Geist({
variable: '--font-geist-sans',
subsets: ['latin'],
});
const geistMono = Geist_Mono({
variable: '--font-geist-mono',
subsets: ['latin'],
});
export const metadata: Metadata = {
title: 'Mediusa Clinic OS — Next-Gen Practice Management Platform',
title: 'Mediusa Clinic OS — Hospital-Grade Practice Management Platform',
description:
'High-speed, proprietary white-label medical practice management platform for chiropractors and wellness clinics. Scheduling, paperless intake, ambient AI clinical notes, and billing.',
'Clinical-grade, high-speed practice management platform for chiropractors, physical therapy, and wellness clinics. HIPAA compliant scheduling, paperless intake, ambient AI clinical notes, and billing.',
};
export default function RootLayout({
@@ -24,11 +13,8 @@ export default function RootLayout({
children: React.ReactNode;
}>) {
return (
<html
lang="en"
className={`${geistSans.variable} ${geistMono.variable} h-full antialiased dark`}
>
<body className="min-h-full flex flex-col bg-slate-950 text-slate-100 font-sans selection:bg-teal-500 selection:text-slate-950">
<html lang="en" className="h-full antialiased">
<body className="min-h-full flex flex-col bg-slate-50 text-slate-900 font-sans selection:bg-sky-500 selection:text-white transition-colors duration-200">
{children}
</body>
</html>
+58 -71
View File
@@ -11,11 +11,10 @@ import {
} from '@/lib/mock-data';
import {
ClinicTenant,
Provider,
Patient,
Appointment,
SoapNote,
Superbill,
Patient,
} from '@/types/clinical';
import { CalendarView } from '@/components/calendar/CalendarView';
import { SoapChartEditor } from '@/components/charting/SoapChartEditor';
@@ -28,34 +27,29 @@ import {
FileText,
DollarSign,
Users,
ShieldCheck,
Building2,
Globe,
Sparkles,
ChevronDown,
Stethoscope,
Laptop,
CheckCircle2,
} from 'lucide-react';
export default function Home() {
// Global Multi-Tenant State
const [tenants, setTenants] = useState<ClinicTenant[]>(INITIAL_TENANTS);
const [activeTenantId, setActiveTenantId] = useState<string>(INITIAL_TENANTS[0].id);
// Active Navigation Mode: 'clinic' (EHR Practice OS) | 'patient' (Booking/Intake) | 'superadmin' (Mediusa Command)
// 'clinic' | 'patient' | 'superadmin'
const [portalMode, setPortalMode] = useState<'clinic' | 'patient' | 'superadmin'>('clinic');
// Clinic Portal Sub-Tabs: 'calendar' | 'charting' | 'billing' | 'retention'
// 'calendar' | 'charting' | 'billing' | 'retention'
const [clinicTab, setClinicTab] = useState<'calendar' | 'charting' | 'billing' | 'retention'>('calendar');
// Active Entities
const [appointments, setAppointments] = useState<Appointment[]>(INITIAL_APPOINTMENTS);
const [patients, setPatients] = useState<Patient[]>(INITIAL_PATIENTS);
const [soapNotes, setSoapNotes] = useState<SoapNote[]>(INITIAL_SOAP_NOTES);
const [superbills, setSuperbills] = useState<Superbill[]>(INITIAL_SUPERBILLS);
const [activePatient, setActivePatient] = useState<Patient>(INITIAL_PATIENTS[0]); // Marcus Chen
const [activePatient, setActivePatient] = useState<Patient>(INITIAL_PATIENTS[0]);
const [activeSoapNote, setActiveSoapNote] = useState<SoapNote | undefined>(INITIAL_SOAP_NOTES[0]);
const [activeSuperbill, setActiveSuperbill] = useState<Superbill>(INITIAL_SUPERBILLS[0]);
@@ -63,7 +57,6 @@ export default function Home() {
const activeProviders = INITIAL_PROVIDERS.filter((p) => p.tenantId === activeTenant.id);
const activeProvider = activeProviders[0] || INITIAL_PROVIDERS[0];
// Handlers
const handleSelectAppointment = (apt: Appointment) => {
const patientMatch = patients.find((p) => p.id === apt.patientId) || patients[0];
setActivePatient(patientMatch);
@@ -167,39 +160,36 @@ export default function Home() {
};
return (
<div className="min-h-screen bg-slate-950 text-slate-100 flex flex-col">
{/* Top Global Command Bar */}
<header className="sticky top-0 z-40 bg-slate-950/90 backdrop-blur-md border-b border-slate-800/80 px-4 lg:px-8 py-3">
<div className="min-h-screen bg-slate-50 text-slate-900 flex flex-col">
{/* Top Hospital Command Header */}
<header className="sticky top-0 z-40 bg-white border-b border-slate-200/90 px-4 lg:px-8 py-3 shadow-2xs">
<div className="max-w-7xl mx-auto flex flex-col md:flex-row md:items-center justify-between gap-4">
{/* Brand & Clinic Domain Selector */}
{/* Hospital Brand & Domain */}
<div className="flex items-center gap-4">
<div className="flex items-center gap-2">
<span
className="w-8 h-8 rounded-xl flex items-center justify-center font-black text-xs text-slate-950 shadow-md"
style={{ backgroundColor: activeTenant.brandColor }}
>
M+
<div className="flex items-center gap-2.5">
<span className="w-9 h-9 rounded-lg bg-sky-700 flex items-center justify-center font-black text-sm text-white shadow-xs">
+M
</span>
<div>
<div className="text-xs font-black tracking-tight text-white flex items-center gap-1.5">
<div className="text-xs font-black tracking-tight text-slate-900 flex items-center gap-1.5">
<span>MEDIUSA CLINIC OS</span>
<span className="text-[10px] px-1.5 py-0.2 rounded bg-teal-500/20 text-teal-300 font-mono">
v2.4 PRO
<span className="text-[10px] px-2 py-0.5 rounded-full bg-sky-100 text-sky-800 font-bold border border-sky-200">
CLINICAL v2.4
</span>
</div>
<div className="text-[11px] text-slate-400 font-mono flex items-center gap-1">
<Globe className="w-3 h-3 text-teal-400" />
<div className="text-xs text-slate-500 font-mono flex items-center gap-1 mt-0.5">
<Globe className="w-3 h-3 text-sky-700" />
https://{activeTenant.domain}
</div>
</div>
</div>
{/* Tenant Switcher Dropdown */}
<div className="relative group">
{/* Clinic Dropdown */}
<div className="relative">
<select
value={activeTenantId}
onChange={(e) => setActiveTenantId(e.target.value)}
className="appearance-none bg-slate-900 hover:bg-slate-800 border border-slate-700 text-slate-200 text-xs font-semibold rounded-xl px-3 py-1.5 pr-8 cursor-pointer focus:outline-none focus:border-teal-500 transition"
className="appearance-none bg-slate-50 hover:bg-slate-100 border border-slate-300 text-slate-800 text-xs font-bold rounded-lg px-3 py-1.5 pr-8 cursor-pointer focus:outline-none focus:border-sky-500 transition shadow-2xs"
>
{tenants.map((t) => (
<option key={t.id} value={t.id}>
@@ -207,19 +197,19 @@ export default function Home() {
</option>
))}
</select>
<ChevronDown className="w-3.5 h-3.5 text-slate-400 absolute right-2.5 top-1/2 -translate-y-1/2 pointer-events-none" />
<ChevronDown className="w-3.5 h-3.5 text-slate-500 absolute right-2.5 top-1/2 -translate-y-1/2 pointer-events-none" />
</div>
</div>
{/* Core Portal Mode Switcher */}
<div className="flex items-center gap-1 bg-slate-900 p-1 rounded-xl border border-slate-800 text-xs font-semibold">
{/* Portal Switcher Tabs */}
<div className="flex items-center gap-1 bg-slate-100 p-1 rounded-lg border border-slate-200 text-xs font-semibold">
<button
type="button"
onClick={() => setPortalMode('clinic')}
className={`px-3 py-1.5 rounded-lg flex items-center gap-1.5 transition ${
className={`px-3 py-1.5 rounded-md flex items-center gap-1.5 transition ${
portalMode === 'clinic'
? 'bg-teal-500 text-slate-950 font-bold shadow'
: 'text-slate-400 hover:text-white'
? 'bg-sky-700 text-white font-bold shadow-xs'
: 'text-slate-600 hover:text-slate-900'
}`}
>
<Stethoscope className="w-3.5 h-3.5" />
@@ -229,10 +219,10 @@ export default function Home() {
<button
type="button"
onClick={() => setPortalMode('patient')}
className={`px-3 py-1.5 rounded-lg flex items-center gap-1.5 transition ${
className={`px-3 py-1.5 rounded-md flex items-center gap-1.5 transition ${
portalMode === 'patient'
? 'bg-teal-500 text-slate-950 font-bold shadow'
: 'text-slate-400 hover:text-white'
? 'bg-sky-700 text-white font-bold shadow-xs'
: 'text-slate-600 hover:text-slate-900'
}`}
>
<Laptop className="w-3.5 h-3.5" />
@@ -242,13 +232,13 @@ export default function Home() {
<button
type="button"
onClick={() => setPortalMode('superadmin')}
className={`px-3 py-1.5 rounded-lg flex items-center gap-1.5 transition ${
className={`px-3 py-1.5 rounded-md flex items-center gap-1.5 transition ${
portalMode === 'superadmin'
? 'bg-indigo-600 text-white font-bold shadow'
: 'text-slate-400 hover:text-white'
? 'bg-slate-900 text-white font-bold shadow-xs'
: 'text-slate-600 hover:text-slate-900'
}`}
>
<Building2 className="w-3.5 h-3.5 text-indigo-300" />
<Building2 className="w-3.5 h-3.5" />
Mediusa Super-Admin
</button>
</div>
@@ -256,65 +246,64 @@ export default function Home() {
{/* Sub-Tabs for Doctor Clinic OS */}
{portalMode === 'clinic' && (
<div className="max-w-7xl mx-auto flex items-center gap-2 mt-3 pt-2 border-t border-slate-800/80 overflow-x-auto text-xs">
<div className="max-w-7xl mx-auto flex items-center gap-2 mt-3 pt-2.5 border-t border-slate-100 overflow-x-auto text-xs">
<button
type="button"
onClick={() => setClinicTab('calendar')}
className={`px-3 py-1.5 rounded-lg font-medium flex items-center gap-1.5 transition whitespace-nowrap ${
className={`px-3 py-1.5 rounded-md font-semibold flex items-center gap-1.5 transition whitespace-nowrap ${
clinicTab === 'calendar'
? 'bg-slate-800 text-teal-300 border border-teal-500/40 font-bold'
: 'text-slate-400 hover:text-slate-200'
? 'bg-sky-50 text-sky-800 border border-sky-200 shadow-2xs font-bold'
: 'text-slate-600 hover:text-slate-900'
}`}
>
<Calendar className="w-3.5 h-3.5" />
Fast Calendar &amp; Schedule
<Calendar className="w-3.5 h-3.5 text-sky-700" />
Schedule &amp; Encounters
</button>
<button
type="button"
onClick={() => setClinicTab('charting')}
className={`px-3 py-1.5 rounded-lg font-medium flex items-center gap-1.5 transition whitespace-nowrap ${
className={`px-3 py-1.5 rounded-md font-semibold flex items-center gap-1.5 transition whitespace-nowrap ${
clinicTab === 'charting'
? 'bg-slate-800 text-teal-300 border border-teal-500/40 font-bold'
: 'text-slate-400 hover:text-slate-200'
? 'bg-sky-50 text-sky-800 border border-sky-200 shadow-2xs font-bold'
: 'text-slate-600 hover:text-slate-900'
}`}
>
<FileText className="w-3.5 h-3.5" />
SOAP Chart &amp; 2D Spine Map ({activePatient.firstName} {activePatient.lastName})
<FileText className="w-3.5 h-3.5 text-sky-700" />
Clinical SOAP &amp; Spine Map ({activePatient.firstName} {activePatient.lastName})
</button>
<button
type="button"
onClick={() => setClinicTab('billing')}
className={`px-3 py-1.5 rounded-lg font-medium flex items-center gap-1.5 transition whitespace-nowrap ${
className={`px-3 py-1.5 rounded-md font-semibold flex items-center gap-1.5 transition whitespace-nowrap ${
clinicTab === 'billing'
? 'bg-slate-800 text-teal-300 border border-teal-500/40 font-bold'
: 'text-slate-400 hover:text-slate-200'
? 'bg-sky-50 text-sky-800 border border-sky-200 shadow-2xs font-bold'
: 'text-slate-600 hover:text-slate-900'
}`}
>
<DollarSign className="w-3.5 h-3.5" />
Superbills &amp; Stripe Billing
<DollarSign className="w-3.5 h-3.5 text-sky-700" />
Superbills &amp; Invoicing
</button>
<button
type="button"
onClick={() => setClinicTab('retention')}
className={`px-3 py-1.5 rounded-lg font-medium flex items-center gap-1.5 transition whitespace-nowrap ${
className={`px-3 py-1.5 rounded-md font-semibold flex items-center gap-1.5 transition whitespace-nowrap ${
clinicTab === 'retention'
? 'bg-slate-800 text-amber-300 border border-amber-500/40 font-bold'
: 'text-slate-400 hover:text-slate-200'
? 'bg-amber-50 text-amber-800 border border-amber-200 shadow-2xs font-bold'
: 'text-slate-600 hover:text-slate-900'
}`}
>
<Users className="w-3.5 h-3.5 text-amber-400" />
<Users className="w-3.5 h-3.5 text-amber-600" />
Care Plan Retention Radar
</button>
</div>
)}
</header>
{/* Main App Container */}
{/* Main Container */}
<main className="flex-1 max-w-7xl w-full mx-auto p-4 sm:p-6 lg:p-8">
{/* PORTAL MODE 1: Clinic Practice OS */}
{portalMode === 'clinic' && (
<div>
{clinicTab === 'calendar' && (
@@ -363,7 +352,6 @@ export default function Home() {
</div>
)}
{/* PORTAL MODE 2: White-Labeled Patient Booking & Intake */}
{portalMode === 'patient' && (
<PatientPortalView
activeTenant={activeTenant}
@@ -372,7 +360,6 @@ export default function Home() {
/>
)}
{/* PORTAL MODE 3: Mediusa Super-Admin Command Center */}
{portalMode === 'superadmin' && (
<SuperAdminView
tenants={tenants}
@@ -382,13 +369,13 @@ export default function Home() {
)}
</main>
{/* Footer Branding (Mediusa / AI Pilots Ecosystem) */}
<footer className="bg-slate-950 border-t border-slate-800/80 py-4 px-6 text-center text-xs text-slate-500">
{/* Hospital Footer */}
<footer className="bg-white border-t border-slate-200 py-4 px-6 text-center text-xs text-slate-500">
<div className="flex flex-col sm:flex-row items-center justify-between max-w-7xl mx-auto gap-2">
<span>
Mediusa Clinic OS Proprietary Practice Management Engine for AI Pilots / Mediusa
<span className="font-medium text-slate-600">
Mediusa Clinic OS Hospital-Grade Practice Management Platform
</span>
<span className="font-mono text-slate-400">
<span className="font-mono text-slate-500">
hello@aipilots.site HIPAA Certified Sub-30s Clinical Charting Standard
</span>
</div>
+80 -83
View File
@@ -9,12 +9,9 @@ import {
Users,
Plus,
ExternalLink,
ShieldCheck,
CheckCircle2,
Sparkles,
Zap,
Globe,
Settings,
} from 'lucide-react';
interface SuperAdminViewProps {
@@ -37,8 +34,8 @@ export const SuperAdminView: React.FC<SuperAdminViewProps> = ({
const [isProvisioning, setIsProvisioning] = useState(false);
const [provisionSuccess, setProvisionSuccess] = useState(false);
const totalMRR = tenants.reduce((sum, t) => sum + t.mrr, 0) + 3120; // 24 clinics total simulated
const totalClinics = tenants.length + 21; // 24 active clinics
const totalMRR = tenants.reduce((sum, t) => sum + t.mrr, 0) + 3120;
const totalClinics = tenants.length + 21;
const handleNameChange = (val: string) => {
setNewClinicName(val);
@@ -68,8 +65,8 @@ export const SuperAdminView: React.FC<SuperAdminViewProps> = ({
npi: newNpi || '1992019481',
taxId: '82-9481920',
logoText: newClinicName.slice(0, 10).toUpperCase(),
brandColor: '#0d9488',
accentColor: '#14b8a6',
brandColor: '#0284c7',
accentColor: '#38bdf8',
plan: newPlan,
mrr: newPlan === 'Starter' ? 99 : newPlan === 'Pro Clinic' ? 149 : 199,
stripeConnected: true,
@@ -90,29 +87,29 @@ export const SuperAdminView: React.FC<SuperAdminViewProps> = ({
return (
<div className="space-y-6">
{/* Super-Admin Agency Banner */}
<div className="bg-gradient-to-r from-slate-900 via-indigo-950/60 to-slate-900 border border-indigo-500/30 rounded-2xl p-6 shadow-2xl flex flex-col md:flex-row md:items-center justify-between gap-4">
{/* Super-Admin Header */}
<div className="bg-white border border-slate-200 rounded-xl p-6 shadow-xs flex flex-col md:flex-row md:items-center justify-between gap-4">
<div>
<div className="flex items-center gap-2">
<span className="p-1.5 rounded-lg bg-indigo-500/20 text-indigo-400 border border-indigo-500/30">
<Zap className="w-4 h-4" />
<span className="p-1.5 rounded-md bg-sky-100 text-sky-800 font-bold text-xs">
MEDIUSA HEALTHCARE OS
</span>
<span className="text-xs font-mono font-bold uppercase tracking-widest text-indigo-300">
AI PILOTS / MEDIUSA AGENCY ENGINE
<span className="text-xs font-mono font-bold text-slate-500 uppercase tracking-widest">
AGENCY CONTROL TOWER
</span>
</div>
<h2 className="text-2xl font-black text-white mt-1">
Clinic OS Multi-Tenant Command Center
<h2 className="text-2xl font-black text-slate-900 mt-1">
Healthcare Multi-Tenant Command Center
</h2>
<p className="text-xs text-slate-300 mt-1">
Autonomous Practice Management SaaS Platform. Onboard 20100+ chiropractic &amp; wellness clinics with zero developer overhead.
<p className="text-xs text-slate-600 mt-1">
Manage practice instances across 24100+ chiropractic &amp; wellness clinics with automated onboarding.
</p>
</div>
<button
type="button"
onClick={() => setShowProvisionModal(true)}
className="px-5 py-2.5 rounded-xl bg-gradient-to-r from-teal-500 to-emerald-500 hover:from-teal-400 hover:to-emerald-400 text-slate-950 text-xs font-black flex items-center gap-2 shadow-lg shadow-teal-500/20 transition shrink-0"
className="px-5 py-2.5 rounded-lg bg-sky-700 hover:bg-sky-800 text-white text-xs font-bold flex items-center gap-2 shadow-xs transition shrink-0"
>
<Plus className="w-4 h-4" />
Provision New Clinic (60s)
@@ -121,66 +118,66 @@ export const SuperAdminView: React.FC<SuperAdminViewProps> = ({
{/* High-Level SaaS Metrics */}
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4">
<div className="bg-slate-900 border border-slate-800 p-5 rounded-2xl shadow-xl">
<div className="flex items-center justify-between text-xs text-slate-400">
<div className="bg-white border border-slate-200 p-5 rounded-xl shadow-xs">
<div className="flex items-center justify-between text-xs text-slate-500 font-medium">
<span>Current SaaS MRR</span>
<DollarSign className="w-4 h-4 text-emerald-400" />
<DollarSign className="w-4 h-4 text-emerald-700" />
</div>
<div className="text-3xl font-black text-white mt-2 font-mono">
<div className="text-3xl font-black text-slate-900 mt-2 font-mono">
${totalMRR.toLocaleString()}.00
</div>
<p className="text-[11px] text-emerald-400 mt-1 font-semibold">
<p className="text-xs text-emerald-700 mt-1 font-semibold">
+$596.00 added this month
</p>
</div>
<div className="bg-slate-900 border border-slate-800 p-5 rounded-2xl shadow-xl">
<div className="flex items-center justify-between text-xs text-slate-400">
<div className="bg-white border border-slate-200 p-5 rounded-xl shadow-xs">
<div className="flex items-center justify-between text-xs text-slate-500 font-medium">
<span>Target MRR @ 100 Clinics</span>
<TrendingUp className="w-4 h-4 text-teal-400" />
<TrendingUp className="w-4 h-4 text-sky-700" />
</div>
<div className="text-3xl font-black text-teal-400 mt-2 font-mono">$17,900.00</div>
<p className="text-[11px] text-slate-400 mt-1">Based on $179 avg clinic subscription</p>
<div className="text-3xl font-black text-sky-800 mt-2 font-mono">$17,900.00</div>
<p className="text-xs text-slate-500 mt-1">Based on $179 average clinic subscription</p>
</div>
<div className="bg-slate-900 border border-slate-800 p-5 rounded-2xl shadow-xl">
<div className="flex items-center justify-between text-xs text-slate-400">
<div className="bg-white border border-slate-200 p-5 rounded-xl shadow-xs">
<div className="flex items-center justify-between text-xs text-slate-500 font-medium">
<span>Active Clinic Tenants</span>
<Building2 className="w-4 h-4 text-indigo-400" />
<Building2 className="w-4 h-4 text-blue-700" />
</div>
<div className="text-3xl font-black text-white mt-2 font-mono">{totalClinics} Clinics</div>
<p className="text-[11px] text-indigo-300 mt-1">100% white-label retention</p>
<div className="text-3xl font-black text-slate-900 mt-2 font-mono">{totalClinics} Clinics</div>
<p className="text-xs text-blue-700 mt-1 font-medium">100% white-label retention</p>
</div>
<div className="bg-slate-900 border border-slate-800 p-5 rounded-2xl shadow-xl">
<div className="flex items-center justify-between text-xs text-slate-400">
<div className="bg-white border border-slate-200 p-5 rounded-xl shadow-xs">
<div className="flex items-center justify-between text-xs text-slate-500 font-medium">
<span>Active Patients Managed</span>
<Users className="w-4 h-4 text-sky-400" />
<Users className="w-4 h-4 text-sky-700" />
</div>
<div className="text-3xl font-black text-sky-400 mt-2 font-mono">3,842</div>
<p className="text-[11px] text-slate-400 mt-1">Across California &amp; US clinics</p>
<div className="text-3xl font-black text-sky-800 mt-2 font-mono">3,842</div>
<p className="text-xs text-slate-500 mt-1">Across California &amp; US clinics</p>
</div>
</div>
{/* Active Clinic Tenant Registry */}
<div className="bg-slate-900 border border-slate-800 rounded-2xl p-6 shadow-xl">
<div className="flex flex-col sm:flex-row sm:items-center justify-between pb-4 border-b border-slate-800 gap-2">
<div className="bg-white border border-slate-200 rounded-xl p-6 shadow-xs">
<div className="flex flex-col sm:flex-row sm:items-center justify-between pb-4 border-b border-slate-100 gap-2">
<div>
<h3 className="font-bold text-white text-base">Client Tenant Registry</h3>
<p className="text-xs text-slate-400 mt-0.5">
Each clinic operates on its isolated schema with custom branding and domain routing.
<h3 className="font-bold text-slate-900 text-base">Client Tenant Registry</h3>
<p className="text-xs text-slate-500 mt-0.5">
Each clinic operates on an isolated schema with custom branding and domain routing.
</p>
</div>
<span className="text-xs text-teal-400 font-mono">
{tenants.length} Primary Demo Instances Loaded
<span className="text-xs text-sky-800 font-mono font-bold">
{tenants.length} Demo Instances Loaded
</span>
</div>
<div className="divide-y divide-slate-800 mt-3">
<div className="divide-y divide-slate-100 mt-3">
{tenants.map((t) => (
<div
key={t.id}
className="py-4 flex flex-col md:flex-row md:items-center justify-between gap-4 hover:bg-slate-800/30 px-3 rounded-xl transition"
className="py-4 flex flex-col md:flex-row md:items-center justify-between gap-4 hover:bg-slate-50 px-3 rounded-lg transition"
>
<div className="min-w-0">
<div className="flex items-center gap-2">
@@ -188,18 +185,18 @@ export const SuperAdminView: React.FC<SuperAdminViewProps> = ({
className="w-3 h-3 rounded-full shrink-0"
style={{ backgroundColor: t.brandColor }}
/>
<h4 className="font-bold text-white text-sm">{t.name}</h4>
<span className="text-[10px] px-2 py-0.5 rounded-full bg-slate-800 text-slate-300 font-mono border border-slate-700">
<h4 className="font-bold text-slate-900 text-sm">{t.name}</h4>
<span className="text-[11px] px-2 py-0.5 rounded-full bg-slate-100 text-slate-700 font-mono border border-slate-200">
{t.plan} (${t.mrr}/mo)
</span>
<span className="text-[10px] px-2 py-0.5 rounded-full bg-emerald-500/20 text-emerald-300 font-semibold border border-emerald-500/30">
<span className="text-[11px] px-2 py-0.5 rounded-full bg-emerald-50 text-emerald-800 font-semibold border border-emerald-200">
Stripe Active
</span>
</div>
<div className="flex flex-wrap items-center gap-3 text-xs text-slate-400 mt-1">
<span className="flex items-center gap-1 text-teal-300 font-mono">
<Globe className="w-3 h-3 text-teal-400" />
<div className="flex flex-wrap items-center gap-3 text-xs text-slate-500 mt-1">
<span className="flex items-center gap-1 text-sky-800 font-mono">
<Globe className="w-3.5 h-3.5 text-sky-700" />
https://{t.domain}
</span>
<span></span>
@@ -215,7 +212,7 @@ export const SuperAdminView: React.FC<SuperAdminViewProps> = ({
<button
type="button"
onClick={() => onSwitchTenant(t.id)}
className="px-3.5 py-1.5 bg-teal-500 hover:bg-teal-400 text-slate-950 font-bold text-xs rounded-xl shadow transition flex items-center gap-1.5"
className="px-3.5 py-1.5 bg-sky-700 hover:bg-sky-800 text-white font-bold text-xs rounded-lg shadow-xs transition flex items-center gap-1.5"
>
<ExternalLink className="w-3.5 h-3.5" />
Launch Clinic OS
@@ -226,94 +223,94 @@ export const SuperAdminView: React.FC<SuperAdminViewProps> = ({
</div>
</div>
{/* 60-Second Instant Clinic Provisioning Modal */}
{/* Provisioning Modal */}
{showProvisionModal && (
<div className="fixed inset-0 z-50 bg-slate-950/80 backdrop-blur-sm flex items-center justify-center p-4">
<div className="bg-slate-900 border border-slate-700 rounded-2xl max-w-lg w-full p-6 shadow-2xl animate-in fade-in zoom-in-95 duration-150 text-slate-100">
<div className="flex items-center justify-between pb-3 border-b border-slate-800">
<div className="fixed inset-0 z-50 bg-slate-900/40 backdrop-blur-xs flex items-center justify-center p-4">
<div className="bg-white border border-slate-200 rounded-2xl max-w-lg w-full p-6 shadow-xl text-slate-900">
<div className="flex items-center justify-between pb-3 border-b border-slate-100">
<div className="flex items-center gap-2">
<span className="p-2 rounded-xl bg-teal-500/20 text-teal-400">
<span className="p-2 rounded-lg bg-sky-50 text-sky-700">
<Sparkles className="w-5 h-5" />
</span>
<div>
<h4 className="font-bold text-white text-sm">
<h4 className="font-bold text-slate-900 text-sm">
60-Second Instant Clinic Provisioning
</h4>
<p className="text-[11px] text-slate-400">
Creates database tenant, seed templates &amp; custom domain
<p className="text-xs text-slate-500">
Generates database tenant, seed templates &amp; custom domain
</p>
</div>
</div>
</div>
{provisionSuccess ? (
<div className="py-8 text-center text-emerald-300">
<CheckCircle2 className="w-12 h-12 text-emerald-400 mx-auto mb-3 animate-bounce" />
<h4 className="text-lg font-bold text-white">Clinic Provisioned Successfully!</h4>
<p className="text-xs text-slate-300 mt-1">
Tenant schema generated. Redirecting to newly created white-labeled instance...
<div className="py-8 text-center text-emerald-800">
<CheckCircle2 className="w-12 h-12 text-emerald-600 mx-auto mb-3 animate-bounce" />
<h4 className="text-lg font-bold text-slate-900">Clinic Provisioned Successfully!</h4>
<p className="text-xs text-slate-600 mt-1">
Tenant schema generated. Redirecting to instance...
</p>
</div>
) : (
<form onSubmit={handleProvisionSubmit} className="mt-4 space-y-4 text-xs">
<div>
<label className="block text-slate-400 mb-1 font-medium">Clinic Name</label>
<label className="block text-slate-600 mb-1 font-medium">Clinic Name</label>
<input
type="text"
required
placeholder="e.g. Pacific Coast Chiropractic & Wellness"
value={newClinicName}
onChange={(e) => handleNameChange(e.target.value)}
className="w-full bg-slate-950 border border-slate-800 rounded-xl p-3 text-slate-200 focus:outline-none focus:border-teal-500"
className="w-full bg-slate-50 border border-slate-200 rounded-lg p-3 text-slate-900 focus:bg-white focus:outline-none focus:border-sky-500"
/>
</div>
<div className="grid grid-cols-2 gap-3">
<div>
<label className="block text-slate-400 mb-1 font-medium">Subdomain Slug</label>
<label className="block text-slate-600 mb-1 font-medium">Subdomain Slug</label>
<input
type="text"
required
value={newSlug}
onChange={(e) => setNewSlug(e.target.value)}
className="w-full bg-slate-950 border border-slate-800 rounded-xl p-3 text-slate-200 font-mono focus:outline-none focus:border-teal-500"
className="w-full bg-slate-50 border border-slate-200 rounded-lg p-3 text-slate-900 font-mono focus:bg-white focus:outline-none focus:border-sky-500"
/>
<span className="text-[10px] text-slate-500 mt-1 block">
<span className="text-[11px] text-slate-500 mt-1 block">
https://{newSlug || 'slug'}.mediusa.app
</span>
</div>
<div>
<label className="block text-slate-400 mb-1 font-medium">Lead Doctor Name</label>
<label className="block text-slate-600 mb-1 font-medium">Lead Doctor Name</label>
<input
type="text"
required
placeholder="Dr. Samuel Wright, D.C."
value={newDoctorName}
onChange={(e) => setNewDoctorName(e.target.value)}
className="w-full bg-slate-950 border border-slate-800 rounded-xl p-3 text-slate-200 focus:outline-none focus:border-teal-500"
className="w-full bg-slate-50 border border-slate-200 rounded-lg p-3 text-slate-900 focus:bg-white focus:outline-none focus:border-sky-500"
/>
</div>
</div>
<div className="grid grid-cols-2 gap-3">
<div>
<label className="block text-slate-400 mb-1 font-medium">Provider NPI</label>
<label className="block text-slate-600 mb-1 font-medium">Provider NPI</label>
<input
type="text"
placeholder="10-digit NPI"
value={newNpi}
onChange={(e) => setNewNpi(e.target.value)}
className="w-full bg-slate-950 border border-slate-800 rounded-xl p-3 text-slate-200 font-mono focus:outline-none focus:border-teal-500"
className="w-full bg-slate-50 border border-slate-200 rounded-lg p-3 text-slate-900 font-mono focus:bg-white focus:outline-none focus:border-sky-500"
/>
</div>
<div>
<label className="block text-slate-400 mb-1 font-medium">Subscription Plan</label>
<label className="block text-slate-600 mb-1 font-medium">Subscription Plan</label>
<select
value={newPlan}
onChange={(e: any) => setNewPlan(e.target.value)}
className="w-full bg-slate-950 border border-slate-800 rounded-xl p-3 text-slate-200 focus:outline-none focus:border-teal-500"
className="w-full bg-slate-50 border border-slate-200 rounded-lg p-3 text-slate-900 focus:bg-white focus:outline-none focus:border-sky-500"
>
<option value="Starter">Starter Clinic ($99/mo)</option>
<option value="Pro Clinic">Pro Clinic ($149/mo)</option>
@@ -322,11 +319,11 @@ export const SuperAdminView: React.FC<SuperAdminViewProps> = ({
</div>
</div>
<div className="pt-4 border-t border-slate-800 flex items-center justify-between">
<div className="pt-4 border-t border-slate-200 flex items-center justify-between">
<button
type="button"
onClick={() => setShowProvisionModal(false)}
className="px-4 py-2 bg-slate-800 hover:bg-slate-700 text-slate-300 font-semibold rounded-xl transition"
className="px-4 py-2 bg-slate-100 hover:bg-slate-200 text-slate-700 font-semibold rounded-lg transition"
>
Cancel
</button>
@@ -334,12 +331,12 @@ export const SuperAdminView: React.FC<SuperAdminViewProps> = ({
<button
type="submit"
disabled={isProvisioning}
className="px-5 py-2.5 bg-gradient-to-r from-teal-500 to-emerald-500 hover:from-teal-400 hover:to-emerald-400 text-slate-950 font-black rounded-xl shadow-lg shadow-teal-500/20 transition flex items-center gap-2 disabled:opacity-50"
className="px-5 py-2.5 bg-sky-700 hover:bg-sky-800 text-white font-bold rounded-lg shadow-xs transition flex items-center gap-2 disabled:opacity-50"
>
{isProvisioning ? (
<>
<span className="w-3.5 h-3.5 border-2 border-slate-950 border-t-transparent rounded-full animate-spin" />
Generating Tenant Schema...
<span className="w-3.5 h-3.5 border-2 border-white border-t-transparent rounded-full animate-spin" />
Generating Tenant...
</>
) : (
<>
+102 -104
View File
@@ -4,7 +4,6 @@ import React, { useState } from 'react';
import { Superbill, ClinicTenant } from '@/types/clinical';
import { generateSuperbillPdf } from '@/lib/pdf-generator';
import {
FileText,
Download,
CreditCard,
CheckCircle2,
@@ -12,8 +11,7 @@ import {
ArrowLeft,
DollarSign,
Printer,
Sparkles,
ExternalLink,
FileCheck,
} from 'lucide-react';
interface SuperbillViewProps {
@@ -53,33 +51,33 @@ export const SuperbillView: React.FC<SuperbillViewProps> = ({
return (
<div className="space-y-6">
{/* Top Header Bar */}
<div className="bg-slate-900 border border-slate-800 rounded-2xl p-5 shadow-xl flex flex-col sm:flex-row sm:items-center justify-between gap-4">
<div className="bg-white border border-slate-200 rounded-xl p-5 shadow-xs flex flex-col sm:flex-row sm:items-center justify-between gap-4">
<div className="flex items-center gap-3">
<button
type="button"
onClick={onBack}
className="p-2 bg-slate-800 hover:bg-slate-700 text-slate-300 rounded-xl transition"
className="p-2 bg-slate-100 hover:bg-slate-200 text-slate-700 rounded-lg transition"
title="Back"
>
<ArrowLeft className="w-5 h-5" />
</button>
<div>
<div className="flex items-center gap-2">
<h3 className="text-lg font-black text-white">
Superbill {superbill.invoiceNumber}
<h3 className="text-lg font-bold text-slate-900">
Superbill Statement {superbill.invoiceNumber}
</h3>
<span
className={`text-xs px-2.5 py-0.5 rounded-full font-bold uppercase ${
superbill.balanceDue === 0
? 'bg-emerald-500/20 text-emerald-300 border border-emerald-500/30'
: 'bg-amber-500/20 text-amber-300 border border-amber-500/30'
? 'bg-emerald-50 text-emerald-800 border border-emerald-200'
: 'bg-amber-50 text-amber-800 border border-amber-200'
}`}
>
{superbill.balanceDue === 0 ? 'Paid in Full' : 'Balance Due'}
</span>
</div>
<p className="text-xs text-slate-400 mt-0.5">
Clean Medical Claim Standard (CMS-1500 / 837P Ready) for {superbill.patientName}
<p className="text-xs text-slate-500 mt-0.5">
Standard CMS-1500 Reimbursement Statement for {superbill.patientName}
</p>
</div>
</div>
@@ -88,9 +86,9 @@ export const SuperbillView: React.FC<SuperbillViewProps> = ({
<button
type="button"
onClick={handleDownloadPdf}
className="px-4 py-2 rounded-xl bg-slate-800 hover:bg-slate-700 text-white text-xs font-bold flex items-center gap-1.5 transition border border-slate-700"
className="px-4 py-2 rounded-lg bg-sky-700 hover:bg-sky-800 text-white text-xs font-bold flex items-center gap-1.5 transition shadow-xs"
>
<Download className="w-4 h-4 text-teal-400" />
<Download className="w-4 h-4" />
Download PDF Superbill
</button>
@@ -98,110 +96,110 @@ export const SuperbillView: React.FC<SuperbillViewProps> = ({
<button
type="button"
onClick={() => setShowPaymentModal(true)}
className="px-4 py-2 rounded-xl bg-teal-500 hover:bg-teal-400 text-slate-950 text-xs font-bold flex items-center gap-1.5 shadow-lg shadow-teal-500/20 transition"
className="px-4 py-2 rounded-lg bg-emerald-700 hover:bg-emerald-800 text-white text-xs font-bold flex items-center gap-1.5 shadow-xs transition"
>
<CreditCard className="w-4 h-4" />
Collect with Stripe
Collect Payment
</button>
)}
</div>
</div>
{/* Main Printed Superbill Card Simulation */}
<div className="bg-slate-950 border border-slate-800 rounded-2xl p-6 sm:p-8 shadow-2xl max-w-4xl mx-auto text-slate-200">
<div className="bg-white border border-slate-200/90 rounded-xl p-6 sm:p-10 shadow-sm max-w-4xl mx-auto text-slate-800">
{/* Clinic & Statement Header */}
<div className="flex flex-col sm:flex-row sm:items-start justify-between pb-6 border-b border-slate-800 gap-4">
<div className="flex flex-col sm:flex-row sm:items-start justify-between pb-6 border-b border-slate-200 gap-4">
<div>
<span className="text-xs font-mono font-bold text-teal-400 uppercase tracking-widest">
PRACTICE MANAGEMENT SUPERBILL
<span className="text-xs font-mono font-bold text-sky-800 uppercase tracking-widest">
OFFICIAL MEDICAL SUPERBILL &amp; CLAIM
</span>
<h2 className="text-xl font-black text-white mt-1">{superbill.clinicName}</h2>
<p className="text-xs text-slate-400 mt-1">{superbill.clinicAddress}</p>
<p className="text-xs text-slate-400">
Tax ID / EIN: <strong className="text-slate-300 font-mono">{superbill.clinicTaxId}</strong> POS:{' '}
<strong className="text-slate-300 font-mono">{superbill.posCode}</strong>
<h2 className="text-xl font-black text-slate-900 mt-1">{superbill.clinicName}</h2>
<p className="text-xs text-slate-600 mt-1">{superbill.clinicAddress}</p>
<p className="text-xs text-slate-600">
Tax ID / EIN: <strong className="text-slate-800 font-mono">{superbill.clinicTaxId}</strong> POS:{' '}
<strong className="text-slate-800 font-mono">{superbill.posCode}</strong>
</p>
</div>
<div className="sm:text-right text-xs space-y-1">
<div className="text-slate-400 font-medium">Invoice Number</div>
<div className="font-mono text-sm font-bold text-white">{superbill.invoiceNumber}</div>
<div className="text-slate-400 font-medium mt-2">Date of Service</div>
<div className="font-mono text-slate-200">{superbill.dateOfService}</div>
<div className="text-slate-500 font-medium">Invoice Number</div>
<div className="font-mono text-sm font-bold text-slate-900">{superbill.invoiceNumber}</div>
<div className="text-slate-500 font-medium mt-2">Date of Service</div>
<div className="font-mono text-slate-800">{superbill.dateOfService}</div>
</div>
</div>
{/* Provider & Patient 2-Col Info */}
<div className="grid grid-cols-1 md:grid-cols-2 gap-6 py-6 border-b border-slate-800 text-xs">
<div className="grid grid-cols-1 md:grid-cols-2 gap-6 py-6 border-b border-slate-200 text-xs">
{/* Provider */}
<div className="bg-slate-900/60 p-4 rounded-xl border border-slate-800/80">
<h4 className="font-bold text-slate-300 uppercase tracking-wider mb-2">
<div className="bg-slate-50 p-4 rounded-lg border border-slate-200">
<h4 className="font-bold text-slate-700 uppercase tracking-wider mb-2">
Rendering Provider
</h4>
<div className="font-semibold text-white">{superbill.providerName}</div>
<div className="text-slate-400 mt-1">
<div className="font-bold text-slate-900 text-sm">{superbill.providerName}</div>
<div className="text-slate-600 mt-1">
National Provider Identifier (NPI):{' '}
<strong className="text-teal-400 font-mono">{superbill.providerNpi}</strong>
<strong className="text-sky-800 font-mono">{superbill.providerNpi}</strong>
</div>
<div className="text-slate-400 mt-1">Specialty: Chiropractic Clinical Biomechanics</div>
<div className="text-slate-600 mt-1">Specialty: Chiropractic Clinical Biomechanics</div>
</div>
{/* Patient */}
<div className="bg-slate-900/60 p-4 rounded-xl border border-slate-800/80">
<h4 className="font-bold text-slate-300 uppercase tracking-wider mb-2">
<div className="bg-slate-50 p-4 rounded-lg border border-slate-200">
<h4 className="font-bold text-slate-700 uppercase tracking-wider mb-2">
Patient Demographics
</h4>
<div className="font-semibold text-white">{superbill.patientName}</div>
<div className="text-slate-400 mt-1">DOB: {superbill.patientDob}</div>
<div className="text-slate-400 mt-1">Address: {superbill.patientAddress}</div>
<div className="font-bold text-slate-900 text-sm">{superbill.patientName}</div>
<div className="text-slate-600 mt-1">DOB: {superbill.patientDob}</div>
<div className="text-slate-600 mt-1">Address: {superbill.patientAddress}</div>
</div>
</div>
{/* Diagnosis ICD-10 Section */}
<div className="py-6 border-b border-slate-800">
<h4 className="text-xs font-bold text-slate-300 uppercase tracking-wider mb-3">
<div className="py-6 border-b border-slate-200">
<h4 className="text-xs font-bold text-slate-700 uppercase tracking-wider mb-3">
Primary Diagnosis Codes (ICD-10-CM)
</h4>
<div className="grid grid-cols-1 sm:grid-cols-2 gap-2 text-xs">
{superbill.icd10Codes.map((icd, index) => (
<div
key={icd.code}
className="flex items-center gap-2 bg-slate-900/80 border border-slate-800 p-2.5 rounded-lg"
className="flex items-center gap-2 bg-slate-50 border border-slate-200 p-2.5 rounded-lg"
>
<span className="w-5 h-5 rounded bg-teal-500/20 text-teal-300 font-bold flex items-center justify-center font-mono text-[11px]">
<span className="w-5 h-5 rounded bg-sky-100 text-sky-800 font-bold flex items-center justify-center font-mono text-xs">
{index + 1}
</span>
<span className="font-bold font-mono text-teal-300">{icd.code}</span>
<span className="text-slate-400 truncate">{icd.description}</span>
<span className="font-bold font-mono text-sky-800">{icd.code}</span>
<span className="text-slate-600 truncate">{icd.description}</span>
</div>
))}
</div>
</div>
{/* Procedures Table */}
<div className="py-6 border-b border-slate-800">
<h4 className="text-xs font-bold text-slate-300 uppercase tracking-wider mb-3">
<div className="py-6 border-b border-slate-200">
<h4 className="text-xs font-bold text-slate-700 uppercase tracking-wider mb-3">
Rendered Procedures (CPT Codes)
</h4>
<div className="overflow-x-auto">
<table className="w-full text-xs text-left">
<thead>
<tr className="bg-slate-900 text-slate-400 border-b border-slate-800">
<th className="py-2.5 px-3 font-semibold">CPT Code</th>
<th className="py-2.5 px-3 font-semibold">Service Description</th>
<th className="py-2.5 px-3 font-semibold text-center">Units</th>
<th className="py-2.5 px-3 font-semibold text-right">Fee</th>
<th className="py-2.5 px-3 font-semibold text-right">Total</th>
<tr className="bg-slate-100 text-slate-700 border-b border-slate-200">
<th className="py-2.5 px-3 font-bold">CPT Code</th>
<th className="py-2.5 px-3 font-bold">Service Description</th>
<th className="py-2.5 px-3 font-bold text-center">Units</th>
<th className="py-2.5 px-3 font-bold text-right">Fee</th>
<th className="py-2.5 px-3 font-bold text-right">Total</th>
</tr>
</thead>
<tbody className="divide-y divide-slate-800/80">
<tbody className="divide-y divide-slate-100">
{superbill.items.map((item) => (
<tr key={item.cptCode} className="hover:bg-slate-900/40">
<td className="py-2.5 px-3 font-mono font-bold text-teal-300">{item.cptCode}</td>
<td className="py-2.5 px-3 text-slate-300">{item.description}</td>
<td className="py-2.5 px-3 text-center text-slate-400">{item.units}</td>
<tr key={item.cptCode} className="hover:bg-slate-50">
<td className="py-2.5 px-3 font-mono font-bold text-sky-800">{item.cptCode}</td>
<td className="py-2.5 px-3 text-slate-800">{item.description}</td>
<td className="py-2.5 px-3 text-center text-slate-600">{item.units}</td>
<td className="py-2.5 px-3 text-right font-mono">${item.rate.toFixed(2)}</td>
<td className="py-2.5 px-3 text-right font-mono font-bold text-white">
<td className="py-2.5 px-3 text-right font-mono font-bold text-slate-900">
${item.total.toFixed(2)}
</td>
</tr>
@@ -215,26 +213,26 @@ export const SuperbillView: React.FC<SuperbillViewProps> = ({
<div className="pt-6 flex flex-col sm:flex-row sm:items-end justify-between gap-6">
<div className="text-xs text-slate-500 max-w-sm">
<p>
This Superbill contains all required claim elements (CMS-1500 Boxes 21, 24, 25, 31, 33)
for direct patient submission to their private health insurance or FSA/HSA reimbursement plan.
This Superbill fulfills the standard medical reimbursement criteria for direct patient
submission to commercial insurers, Medicare supplements, and HSA/FSA plans.
</p>
<p className="mt-2 text-slate-400">
Payment Method: <strong className="text-teal-400">{superbill.paymentMethod}</strong>
<p className="mt-2 text-slate-700">
Payment Record: <strong className="text-sky-800">{superbill.paymentMethod}</strong>
</p>
</div>
<div className="bg-slate-900 p-4 rounded-xl border border-slate-800 w-full sm:w-64 space-y-2 text-xs">
<div className="flex justify-between text-slate-400">
<div className="bg-slate-50 p-4 rounded-lg border border-slate-200 w-full sm:w-64 space-y-2 text-xs">
<div className="flex justify-between text-slate-600">
<span>Total Charges:</span>
<span className="font-mono text-white">${superbill.totalAmount.toFixed(2)}</span>
<span className="font-mono text-slate-900 font-semibold">${superbill.totalAmount.toFixed(2)}</span>
</div>
<div className="flex justify-between text-slate-400">
<div className="flex justify-between text-slate-600">
<span>Patient Paid:</span>
<span className="font-mono text-emerald-400">-${superbill.patientPaid.toFixed(2)}</span>
<span className="font-mono text-emerald-700 font-semibold">-${superbill.patientPaid.toFixed(2)}</span>
</div>
<div className="pt-2 border-t border-slate-800 flex justify-between font-bold text-sm">
<span className="text-white">Balance Due:</span>
<span className="font-mono text-teal-400">${superbill.balanceDue.toFixed(2)}</span>
<div className="pt-2 border-t border-slate-200 flex justify-between font-bold text-sm">
<span className="text-slate-900">Balance Due:</span>
<span className="font-mono text-sky-800">${superbill.balanceDue.toFixed(2)}</span>
</div>
</div>
</div>
@@ -242,39 +240,39 @@ export const SuperbillView: React.FC<SuperbillViewProps> = ({
{/* Stripe Payment Modal */}
{showPaymentModal && (
<div className="fixed inset-0 z-50 bg-slate-950/80 backdrop-blur-sm flex items-center justify-center p-4">
<div className="bg-slate-900 border border-slate-700 rounded-2xl max-w-md w-full p-6 shadow-2xl animate-in fade-in zoom-in-95 duration-150 text-slate-100">
<div className="flex items-center justify-between pb-3 border-b border-slate-800">
<div className="fixed inset-0 z-50 bg-slate-900/40 backdrop-blur-xs flex items-center justify-center p-4">
<div className="bg-white border border-slate-200 rounded-2xl max-w-md w-full p-6 shadow-xl text-slate-900">
<div className="flex items-center justify-between pb-3 border-b border-slate-100">
<div className="flex items-center gap-2">
<span className="p-2 rounded-xl bg-teal-500/20 text-teal-400">
<span className="p-2 rounded-lg bg-sky-50 text-sky-700 border border-sky-100">
<CreditCard className="w-5 h-5" />
</span>
<div>
<h4 className="font-bold text-white text-sm">Stripe Connect Checkout</h4>
<p className="text-[11px] text-slate-400">Direct to {activeTenant.name}</p>
<h4 className="font-bold text-slate-900 text-sm">Hospital Payment Terminal</h4>
<p className="text-xs text-slate-500">Connected to {activeTenant.name}</p>
</div>
</div>
</div>
<div className="mt-4 space-y-3 text-xs">
<div className="bg-slate-950 p-3 rounded-xl border border-slate-800 flex justify-between items-center">
<span className="text-slate-400">Amount Due:</span>
<span className="text-lg font-black text-teal-400 font-mono">
<div className="bg-slate-50 p-3 rounded-lg border border-slate-200 flex justify-between items-center">
<span className="text-slate-600 font-medium">Balance Due:</span>
<span className="text-lg font-black text-sky-800 font-mono">
${superbill.balanceDue.toFixed(2)}
</span>
</div>
{paymentSuccess ? (
<div className="p-4 bg-emerald-500/20 border border-emerald-500/40 rounded-xl text-center text-emerald-300">
<CheckCircle2 className="w-8 h-8 text-emerald-400 mx-auto mb-2 animate-bounce" />
<div className="p-4 bg-emerald-50 border border-emerald-200 rounded-lg text-center text-emerald-800">
<CheckCircle2 className="w-8 h-8 text-emerald-600 mx-auto mb-2 animate-bounce" />
<div className="font-bold text-sm">Payment Processed Successfully!</div>
<div className="text-[11px] text-emerald-400/80 mt-1">
Stripe Charge ID: ch_3P18f92Ksk921
<div className="text-xs text-emerald-700 mt-1 font-mono">
Transaction ID: TX-90281-OK
</div>
</div>
) : (
<>
<label className="block text-[11px] font-semibold text-slate-400 uppercase tracking-wider">
<label className="block text-xs font-bold text-slate-700 uppercase tracking-wider">
Select Payment Method
</label>
<div className="grid grid-cols-1 gap-2">
@@ -282,14 +280,14 @@ export const SuperbillView: React.FC<SuperbillViewProps> = ({
type="button"
disabled={isProcessingStripe}
onClick={() => handleStripePay('Stripe Card')}
className="p-3 bg-slate-800 hover:bg-slate-700 border border-slate-700 rounded-xl flex items-center justify-between transition"
className="p-3 bg-white hover:bg-slate-50 border border-slate-200 rounded-lg flex items-center justify-between transition shadow-2xs"
>
<div className="flex items-center gap-2 font-medium text-slate-200">
<CreditCard className="w-4 h-4 text-teal-400" />
<div className="flex items-center gap-2 font-semibold text-slate-800">
<CreditCard className="w-4 h-4 text-sky-700" />
Credit / Debit Card (Apple Pay)
</div>
<span className="text-[10px] bg-teal-500/20 text-teal-300 px-2 py-0.5 rounded font-mono">
Instant
<span className="text-[11px] bg-sky-50 text-sky-800 px-2 py-0.5 rounded font-mono font-bold border border-sky-200">
Terminal
</span>
</button>
@@ -297,14 +295,14 @@ export const SuperbillView: React.FC<SuperbillViewProps> = ({
type="button"
disabled={isProcessingStripe}
onClick={() => handleStripePay('HSA/FSA')}
className="p-3 bg-slate-800 hover:bg-slate-700 border border-slate-700 rounded-xl flex items-center justify-between transition"
className="p-3 bg-white hover:bg-slate-50 border border-slate-200 rounded-lg flex items-center justify-between transition shadow-2xs"
>
<div className="flex items-center gap-2 font-medium text-slate-200">
<ShieldAlert className="w-4 h-4 text-sky-400" />
<div className="flex items-center gap-2 font-semibold text-slate-800">
<ShieldAlert className="w-4 h-4 text-blue-700" />
HSA / FSA Health Benefit Card
</div>
<span className="text-[10px] bg-sky-500/20 text-sky-300 px-2 py-0.5 rounded font-mono">
Tax-Free
<span className="text-[11px] bg-blue-50 text-blue-800 px-2 py-0.5 rounded font-mono font-bold border border-blue-200">
Tax-Exempt
</span>
</button>
@@ -312,14 +310,14 @@ export const SuperbillView: React.FC<SuperbillViewProps> = ({
type="button"
disabled={isProcessingStripe}
onClick={() => handleStripePay('Cash')}
className="p-3 bg-slate-800 hover:bg-slate-700 border border-slate-700 rounded-xl flex items-center justify-between transition"
className="p-3 bg-white hover:bg-slate-50 border border-slate-200 rounded-lg flex items-center justify-between transition shadow-2xs"
>
<div className="flex items-center gap-2 font-medium text-slate-200">
<DollarSign className="w-4 h-4 text-emerald-400" />
Cash / In-Person Check
<div className="flex items-center gap-2 font-semibold text-slate-800">
<DollarSign className="w-4 h-4 text-emerald-700" />
Cash / Clinic Receipt
</div>
<span className="text-[10px] bg-emerald-500/20 text-emerald-300 px-2 py-0.5 rounded font-mono">
No Fee
<span className="text-[11px] bg-emerald-50 text-emerald-800 px-2 py-0.5 rounded font-mono font-bold border border-emerald-200">
Receipt
</span>
</button>
</div>
@@ -328,11 +326,11 @@ export const SuperbillView: React.FC<SuperbillViewProps> = ({
</div>
{!paymentSuccess && (
<div className="mt-5 pt-3 border-t border-slate-800 flex justify-end">
<div className="mt-5 pt-3 border-t border-slate-100 flex justify-end">
<button
type="button"
onClick={() => setShowPaymentModal(false)}
className="px-4 py-2 bg-slate-800 hover:bg-slate-700 text-slate-300 text-xs font-semibold rounded-xl transition"
className="px-4 py-2 bg-slate-100 hover:bg-slate-200 text-slate-700 text-xs font-semibold rounded-lg transition"
>
Close
</button>
+61 -55
View File
@@ -8,12 +8,11 @@ import {
User,
Plus,
CheckCircle,
AlertCircle,
PlayCircle,
XCircle,
ChevronLeft,
ChevronRight,
Filter,
Stethoscope,
} from 'lucide-react';
interface CalendarViewProps {
@@ -34,7 +33,6 @@ export const CalendarView: React.FC<CalendarViewProps> = ({
onNewAppointmentClick,
}) => {
const [selectedProviderId, setSelectedProviderId] = useState<string>('all');
const [activeDateStr, setActiveDateStr] = useState<string>('2026-09-05');
const filteredAppointments = appointments.filter(
(a) => selectedProviderId === 'all' || a.providerId === selectedProviderId
@@ -44,75 +42,78 @@ export const CalendarView: React.FC<CalendarViewProps> = ({
switch (status) {
case 'completed':
return (
<span className="px-2 py-0.5 rounded-md bg-emerald-500/10 text-emerald-400 border border-emerald-500/20 text-[11px] font-semibold flex items-center gap-1">
<CheckCircle className="w-3 h-3" /> Completed
<span className="px-2.5 py-0.5 rounded-full bg-emerald-50 text-emerald-700 border border-emerald-200 text-xs font-semibold flex items-center gap-1">
<CheckCircle className="w-3.5 h-3.5 text-emerald-600" /> Completed
</span>
);
case 'in_room':
return (
<span className="px-2 py-0.5 rounded-md bg-amber-500/10 text-amber-400 border border-amber-500/20 text-[11px] font-semibold flex items-center gap-1 animate-pulse">
<PlayCircle className="w-3 h-3" /> In Operatory
<span className="px-2.5 py-0.5 rounded-full bg-amber-50 text-amber-700 border border-amber-200 text-xs font-semibold flex items-center gap-1 animate-pulse">
<PlayCircle className="w-3.5 h-3.5 text-amber-600" /> In Operatory
</span>
);
case 'arrived':
return (
<span className="px-2 py-0.5 rounded-md bg-sky-500/10 text-sky-400 border border-sky-500/20 text-[11px] font-semibold flex items-center gap-1">
<User className="w-3 h-3" /> Arrived / Waiting
<span className="px-2.5 py-0.5 rounded-full bg-sky-50 text-sky-700 border border-sky-200 text-xs font-semibold flex items-center gap-1">
<User className="w-3.5 h-3.5 text-sky-600" /> Arrived / Waiting
</span>
);
case 'confirmed':
return (
<span className="px-2 py-0.5 rounded-md bg-teal-500/10 text-teal-400 border border-teal-500/20 text-[11px] font-semibold flex items-center gap-1">
<CalendarIcon className="w-3 h-3" /> Confirmed
<span className="px-2.5 py-0.5 rounded-full bg-blue-50 text-blue-700 border border-blue-200 text-xs font-semibold flex items-center gap-1">
<CalendarIcon className="w-3.5 h-3.5 text-blue-600" /> Confirmed
</span>
);
case 'booked':
return (
<span className="px-2 py-0.5 rounded-md bg-slate-800 text-slate-300 border border-slate-700 text-[11px] font-semibold">
<span className="px-2.5 py-0.5 rounded-full bg-slate-100 text-slate-700 border border-slate-200 text-xs font-medium">
Booked
</span>
);
case 'no_show':
return (
<span className="px-2 py-0.5 rounded-md bg-red-500/10 text-red-400 border border-red-500/20 text-[11px] font-semibold flex items-center gap-1">
<XCircle className="w-3 h-3" /> No Show
<span className="px-2.5 py-0.5 rounded-full bg-red-50 text-red-700 border border-red-200 text-xs font-semibold flex items-center gap-1">
<XCircle className="w-3.5 h-3.5 text-red-600" /> No Show
</span>
);
}
};
return (
<div className="space-y-4">
{/* Action Header */}
<div className="flex flex-col sm:flex-row sm:items-center justify-between gap-4 bg-slate-900 border border-slate-800 p-4 rounded-2xl">
<div className="flex items-center gap-3">
<div className="flex items-center bg-slate-800/80 rounded-xl p-1 border border-slate-700/60">
<div className="space-y-5">
{/* Hospital Sub-Header Bar */}
<div className="flex flex-col sm:flex-row sm:items-center justify-between gap-4 bg-white border border-slate-200/90 p-4 rounded-xl shadow-xs">
<div className="flex flex-wrap items-center gap-3">
{/* Date Selector */}
<div className="flex items-center bg-slate-100/90 rounded-lg p-1 border border-slate-200">
<button
type="button"
className="p-1.5 hover:bg-slate-700 rounded-lg text-slate-400 hover:text-white transition"
className="p-1.5 hover:bg-white rounded-md text-slate-600 hover:text-slate-900 transition"
title="Previous Day"
>
<ChevronLeft className="w-4 h-4" />
</button>
<span className="px-3 text-xs font-bold text-white tracking-wide">
<span className="px-3 text-xs font-bold text-slate-800 tracking-wide">
Saturday, September 5, 2026
</span>
<button
type="button"
className="p-1.5 hover:bg-slate-700 rounded-lg text-slate-400 hover:text-white transition"
className="p-1.5 hover:bg-white rounded-md text-slate-600 hover:text-slate-900 transition"
title="Next Day"
>
<ChevronRight className="w-4 h-4" />
</button>
</div>
{/* Provider Filter */}
<div className="flex items-center gap-1 bg-slate-800/50 p-1 rounded-xl border border-slate-700/60 text-xs">
{/* Attending Provider Filter Tabs */}
<div className="flex items-center gap-1 bg-slate-100 p-1 rounded-lg border border-slate-200 text-xs">
<button
type="button"
onClick={() => setSelectedProviderId('all')}
className={`px-3 py-1 rounded-lg font-medium transition ${
className={`px-3 py-1 rounded-md font-semibold transition ${
selectedProviderId === 'all'
? 'bg-teal-500 text-slate-950 font-bold shadow'
: 'text-slate-400 hover:text-white'
? 'bg-white text-sky-800 shadow-xs border border-slate-200/60'
: 'text-slate-600 hover:text-slate-900'
}`}
>
All Providers
@@ -122,10 +123,10 @@ export const CalendarView: React.FC<CalendarViewProps> = ({
key={p.id}
type="button"
onClick={() => setSelectedProviderId(p.id)}
className={`px-3 py-1 rounded-lg font-medium transition ${
className={`px-3 py-1 rounded-md font-semibold transition ${
selectedProviderId === p.id
? 'bg-teal-500 text-slate-950 font-bold shadow'
: 'text-slate-400 hover:text-white'
? 'bg-white text-sky-800 shadow-xs border border-slate-200/60'
: 'text-slate-600 hover:text-slate-900'
}`}
>
{p.name}
@@ -137,66 +138,71 @@ export const CalendarView: React.FC<CalendarViewProps> = ({
<button
type="button"
onClick={onNewAppointmentClick}
className="px-4 py-2 rounded-xl bg-teal-500 hover:bg-teal-400 text-slate-950 text-xs font-bold flex items-center gap-2 shadow-lg shadow-teal-500/20 transition shrink-0"
className="px-4 py-2 rounded-lg bg-sky-700 hover:bg-sky-800 text-white text-xs font-bold flex items-center gap-2 shadow-xs transition shrink-0"
>
<Plus className="w-4 h-4" />
Book New Patient
Schedule Patient
</button>
</div>
{/* Grid of Appointments */}
{/* Grid of Hospital Patient Encounter Cards */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{filteredAppointments.map((apt) => (
<div
key={apt.id}
className="bg-slate-900 border border-slate-800 hover:border-teal-500/40 rounded-2xl p-4 shadow-xl transition-all duration-200 group flex flex-col justify-between"
className="bg-white border border-slate-200/90 hover:border-sky-300 rounded-xl p-4 shadow-xs hover:shadow-md transition-all duration-200 flex flex-col justify-between"
>
<div>
{/* Top Row: Time & Status */}
<div className="flex items-center justify-between pb-3 border-b border-slate-800">
{/* Encounter Time & Clinical Status */}
<div className="flex items-center justify-between pb-3 border-b border-slate-100">
<div className="flex items-center gap-2">
<span className="p-1.5 rounded-lg bg-teal-500/10 text-teal-400 font-mono text-xs font-bold">
<Clock className="w-3.5 h-3.5 inline mr-1" />
<span className="px-2.5 py-1 rounded-md bg-sky-50 text-sky-800 font-mono text-xs font-bold border border-sky-100">
<Clock className="w-3.5 h-3.5 inline mr-1 text-sky-600" />
{apt.time}
</span>
<span className="text-[11px] text-slate-400 font-medium">
<span className="text-[11px] text-slate-500 font-medium">
{apt.durationMinutes} min
</span>
</div>
{getStatusBadge(apt.status)}
</div>
{/* Patient & Service Details */}
{/* Patient & Examination Room */}
<div className="mt-3">
<h4 className="text-sm font-bold text-white group-hover:text-teal-300 transition">
<div className="flex items-center justify-between">
<h4 className="text-sm font-bold text-slate-900 group-hover:text-sky-700 transition">
{apt.patientName}
</h4>
<p className="text-xs text-teal-400/90 font-medium mt-0.5">
<span className="text-[11px] font-mono font-semibold text-slate-600 bg-slate-50 px-2 py-0.5 rounded border border-slate-200">
${apt.fee}
</span>
</div>
<p className="text-xs text-sky-700 font-medium mt-0.5">
{apt.serviceType}
</p>
<div className="flex items-center gap-2 text-[11px] text-slate-400 mt-2">
<span>Provider: {apt.providerName}</span>
<div className="flex items-center gap-2 text-xs text-slate-500 mt-2">
<span>Attending: {apt.providerName}</span>
<span></span>
<span>{apt.room}</span>
<span className="font-medium text-slate-700">{apt.room}</span>
</div>
{apt.notes && (
<div className="mt-2.5 p-2 bg-slate-950/60 rounded-lg border border-slate-800/80 text-[11px] text-slate-300 italic">
<div className="mt-2.5 p-2 bg-slate-50 rounded-lg border border-slate-200 text-xs text-slate-700 italic">
&quot;{apt.notes}&quot;
</div>
)}
</div>
</div>
{/* Action Bar */}
<div className="mt-4 pt-3 border-t border-slate-800 flex items-center justify-between gap-2">
{/* Clinical Action Bar */}
<div className="mt-4 pt-3 border-t border-slate-100 flex items-center justify-between gap-2">
<button
type="button"
onClick={() => onSelectAppointment(apt)}
className="px-3 py-1.5 rounded-lg bg-teal-500/10 hover:bg-teal-500/20 text-teal-300 text-xs font-semibold border border-teal-500/30 transition flex items-center gap-1.5"
className="px-3 py-1.5 rounded-lg bg-sky-50 hover:bg-sky-100 text-sky-800 text-xs font-bold border border-sky-200 transition flex items-center gap-1.5"
>
<PlayCircle className="w-3.5 h-3.5" />
Open SOAP Chart
<Stethoscope className="w-3.5 h-3.5 text-sky-700" />
Open Clinical Chart
</button>
<div className="flex items-center gap-1">
@@ -204,7 +210,7 @@ export const CalendarView: React.FC<CalendarViewProps> = ({
<button
type="button"
onClick={() => onUpdateStatus(apt.id, 'arrived')}
className="px-2.5 py-1 rounded-lg bg-slate-800 hover:bg-slate-700 text-slate-300 text-[11px] font-medium"
className="px-2.5 py-1 rounded-md bg-slate-100 hover:bg-slate-200 text-slate-700 text-xs font-medium border border-slate-200"
>
Check In
</button>
@@ -213,16 +219,16 @@ export const CalendarView: React.FC<CalendarViewProps> = ({
<button
type="button"
onClick={() => onUpdateStatus(apt.id, 'in_room')}
className="px-2.5 py-1 rounded-lg bg-amber-500/20 hover:bg-amber-500/30 text-amber-300 text-[11px] font-medium border border-amber-500/30"
className="px-2.5 py-1 rounded-md bg-amber-100 hover:bg-amber-200 text-amber-900 text-xs font-bold border border-amber-300"
>
Move to Table
Place in Room
</button>
)}
{apt.status === 'in_room' && (
<button
type="button"
onClick={() => onUpdateStatus(apt.id, 'completed')}
className="px-2.5 py-1 rounded-lg bg-emerald-500/20 hover:bg-emerald-500/30 text-emerald-300 text-[11px] font-medium border border-emerald-500/30"
className="px-2.5 py-1 rounded-md bg-emerald-100 hover:bg-emerald-200 text-emerald-900 text-xs font-bold border border-emerald-300"
>
Mark Done
</button>
+90 -95
View File
@@ -7,16 +7,12 @@ import { AmbientAudioRecorder } from '@/components/ui/AmbientAudioRecorder';
import { STANDARD_CPT_CODES, STANDARD_ICD10_CODES } from '@/lib/mock-data';
import {
FileText,
Sparkles,
CheckCircle,
Copy,
PenTool,
Save,
DollarSign,
ChevronLeft,
Tag,
ShieldCheck,
AlertCircle,
} from 'lucide-react';
interface SoapChartEditorProps {
@@ -61,16 +57,15 @@ export const SoapChartEditor: React.FC<SoapChartEditorProps> = ({
]
);
const [selectedIcd10, setSelectedIcd10] = useState<Icd10CodeItem[]>(
initialSoapNote?.icd10Codes || [STANDARD_ICD10_CODES[2], STANDARD_ICD10_CODES[5]] // M99.03 & M54.50
initialSoapNote?.icd10Codes || [STANDARD_ICD10_CODES[2], STANDARD_ICD10_CODES[5]]
);
const [selectedCpt, setSelectedCpt] = useState<CptCodeItem[]>(
initialSoapNote?.cptCodes || [STANDARD_CPT_CODES[0], STANDARD_CPT_CODES[3]] // 98940 & 97140
initialSoapNote?.cptCodes || [STANDARD_CPT_CODES[0], STANDARD_CPT_CODES[3]]
);
const [vasScore, setVasScore] = useState<number>(initialSoapNote?.vasScore || 3);
const [isSigned, setIsSigned] = useState<boolean>(initialSoapNote?.status === 'signed');
const [signedTimestamp, setSignedTimestamp] = useState<string | undefined>(initialSoapNote?.signedAt);
// Ambient AI Scribe callback
const handleApplyExtractedSoap = (data: {
vasScore: number;
subjective: string;
@@ -91,11 +86,9 @@ export const SoapChartEditor: React.FC<SoapChartEditorProps> = ({
setAdjustments(data.spinalAdjustments);
}
// Auto map ICD10
const matchedIcds = STANDARD_ICD10_CODES.filter((icd) => data.icd10.includes(icd.code));
if (matchedIcds.length > 0) setSelectedIcd10(matchedIcds);
// Auto map CPT
const matchedCpts = STANDARD_CPT_CODES.filter((cpt) => data.cpt.includes(cpt.code));
if (matchedCpts.length > 0) setSelectedCpt(matchedCpts);
};
@@ -174,13 +167,13 @@ export const SoapChartEditor: React.FC<SoapChartEditorProps> = ({
return (
<div className="space-y-6">
{/* Top Breadcrumb & Patient Header */}
<div className="bg-slate-900 border border-slate-800 rounded-2xl p-5 shadow-xl flex flex-col md:flex-row md:items-center justify-between gap-4">
<div className="flex items-center gap-4">
{/* Top Patient Header Bar */}
<div className="bg-white border border-slate-200 rounded-xl p-5 shadow-xs flex flex-col md:flex-row md:items-center justify-between gap-4">
<div className="flex items-center gap-3">
<button
type="button"
onClick={onBackToCalendar}
className="p-2 bg-slate-800 hover:bg-slate-700 text-slate-300 rounded-xl transition"
className="p-2 bg-slate-100 hover:bg-slate-200 text-slate-700 rounded-lg transition"
title="Back to Calendar"
>
<ChevronLeft className="w-5 h-5" />
@@ -188,29 +181,29 @@ export const SoapChartEditor: React.FC<SoapChartEditorProps> = ({
<div>
<div className="flex items-center gap-2">
<h3 className="text-lg font-black text-white">
<h3 className="text-lg font-bold text-slate-900">
{patient.firstName} {patient.lastName}
</h3>
<span className="text-xs px-2.5 py-0.5 rounded-full bg-teal-500/20 text-teal-300 border border-teal-500/30 font-semibold">
<span className="text-xs px-2.5 py-0.5 rounded-full bg-sky-50 text-sky-800 border border-sky-200 font-semibold">
Care Plan: Visit {patient.carePlan.completedVisits + 1} of {patient.carePlan.totalVisits}
</span>
{isSigned ? (
<span className="text-xs px-2.5 py-0.5 rounded-full bg-emerald-500/20 text-emerald-300 border border-emerald-500/30 font-semibold flex items-center gap-1">
<span className="text-xs px-2.5 py-0.5 rounded-full bg-emerald-50 text-emerald-700 border border-emerald-200 font-semibold flex items-center gap-1">
<ShieldCheck className="w-3.5 h-3.5" /> Signed &amp; Locked
</span>
) : (
<span className="text-xs px-2.5 py-0.5 rounded-full bg-amber-500/20 text-amber-300 border border-amber-500/30 font-semibold">
<span className="text-xs px-2.5 py-0.5 rounded-full bg-amber-50 text-amber-700 border border-amber-200 font-semibold">
In Progress (Draft)
</span>
)}
</div>
<div className="flex flex-wrap items-center gap-3 text-xs text-slate-400 mt-1">
<div className="flex flex-wrap items-center gap-3 text-xs text-slate-500 mt-1">
<span>DOB: {patient.dob}</span>
<span></span>
<span>Insurance: {patient.insuranceName}</span>
<span></span>
<span className="text-teal-400 font-medium">Attending: {provider.name}</span>
<span className="text-sky-800 font-medium">Attending: {provider.name}</span>
</div>
</div>
</div>
@@ -220,9 +213,9 @@ export const SoapChartEditor: React.FC<SoapChartEditorProps> = ({
<button
type="button"
onClick={handleCloneLastNote}
className="px-3 py-2 rounded-xl bg-slate-800 hover:bg-slate-700 text-slate-300 text-xs font-semibold flex items-center gap-1.5 transition"
className="px-3 py-2 rounded-lg bg-slate-100 hover:bg-slate-200 text-slate-700 border border-slate-200 text-xs font-semibold flex items-center gap-1.5 transition"
>
<Copy className="w-3.5 h-3.5" />
<Copy className="w-3.5 h-3.5 text-slate-600" />
Clone Last Note
</button>
@@ -230,20 +223,20 @@ export const SoapChartEditor: React.FC<SoapChartEditorProps> = ({
type="button"
onClick={handleSignChart}
disabled={isSigned}
className={`px-4 py-2 rounded-xl text-xs font-bold flex items-center gap-1.5 shadow-lg transition ${
className={`px-4 py-2 rounded-lg text-xs font-bold flex items-center gap-1.5 transition shadow-xs ${
isSigned
? 'bg-emerald-500/20 text-emerald-400 border border-emerald-500/40 cursor-default'
: 'bg-teal-500 hover:bg-teal-400 text-slate-950 shadow-teal-500/20'
? 'bg-emerald-50 text-emerald-700 border border-emerald-200 cursor-default'
: 'bg-emerald-700 hover:bg-emerald-800 text-white'
}`}
>
<CheckCircle className="w-4 h-4" />
{isSigned ? 'Signed by Doctor' : 'Sign & Complete Note'}
{isSigned ? 'Signed & Locked' : 'Sign & Complete Note'}
</button>
<button
type="button"
onClick={handleCreateSuperbillClick}
className="px-4 py-2 rounded-xl bg-indigo-600 hover:bg-indigo-500 text-white text-xs font-bold flex items-center gap-1.5 shadow-lg shadow-indigo-600/20 transition"
className="px-4 py-2 rounded-lg bg-sky-700 hover:bg-sky-800 text-white text-xs font-bold flex items-center gap-1.5 shadow-xs transition"
>
<DollarSign className="w-4 h-4" />
Generate Superbill
@@ -251,56 +244,57 @@ export const SoapChartEditor: React.FC<SoapChartEditorProps> = ({
</div>
</div>
{/* Ambient AI Clinical Scribe Section */}
{/* Ambient AI Scribe */}
<AmbientAudioRecorder onApplyExtractedSoap={handleApplyExtractedSoap} />
{/* 2D Spine Visualizer Subluxation Clicker */}
{/* Spine Subluxation Visualizer */}
<SpineVisualizer
adjustments={adjustments}
onToggleAdjustment={handleToggleAdjustment}
readOnly={isSigned}
/>
{/* Structured SOAP Fields Grid */}
{/* Structured SOAP Fields */}
<div className="grid grid-cols-1 md:grid-cols-2 gap-5">
{/* Subjective */}
<div className="bg-slate-900 border border-slate-800 rounded-2xl p-4 shadow-xl flex flex-col justify-between">
{/* S */}
<div className="bg-white border border-slate-200 rounded-xl p-4 shadow-xs flex flex-col justify-between">
<div>
<div className="flex items-center justify-between pb-2 border-b border-slate-800 mb-2">
<span className="text-xs font-bold text-teal-400 uppercase tracking-wider flex items-center gap-1.5">
<span className="w-5 h-5 rounded-md bg-teal-500/20 flex items-center justify-center font-black text-teal-300">
<div className="flex items-center justify-between pb-2 border-b border-slate-100 mb-2">
<span className="text-xs font-bold text-sky-800 uppercase tracking-wider flex items-center gap-1.5">
<span className="w-5 h-5 rounded bg-sky-100 text-sky-800 flex items-center justify-center font-bold">
S
</span>
Subjective Complaint &amp; History
Subjective History &amp; Complaint
</span>
<span className="text-xs font-mono font-semibold text-slate-600 bg-slate-100 px-2 py-0.5 rounded">
VAS: {vasScore}/10
</span>
<span className="text-xs font-mono text-slate-400">VAS: {vasScore}/10</span>
</div>
<textarea
rows={4}
value={subjective}
disabled={isSigned}
onChange={(e) => setSubjective(e.target.value)}
className="w-full bg-slate-950 border border-slate-800 rounded-xl p-3 text-xs text-slate-200 focus:outline-none focus:border-teal-500 resize-none font-sans"
placeholder="Patient reports symptoms, VAS rating, changes since last visit..."
className="w-full bg-slate-50 border border-slate-200 rounded-lg p-3 text-xs text-slate-800 focus:bg-white focus:outline-none focus:border-sky-500 resize-none"
/>
</div>
<div className="text-[10px] text-slate-500 mt-2">
<div className="text-[11px] text-slate-400 mt-2">
Auto-populated from Intake and Ambient Scribe.
</div>
</div>
{/* Objective */}
<div className="bg-slate-900 border border-slate-800 rounded-2xl p-4 shadow-xl flex flex-col justify-between">
{/* O */}
<div className="bg-white border border-slate-200 rounded-xl p-4 shadow-xs flex flex-col justify-between">
<div>
<div className="flex items-center justify-between pb-2 border-b border-slate-800 mb-2">
<span className="text-xs font-bold text-teal-400 uppercase tracking-wider flex items-center gap-1.5">
<span className="w-5 h-5 rounded-md bg-teal-500/20 flex items-center justify-center font-black text-teal-300">
<div className="flex items-center justify-between pb-2 border-b border-slate-100 mb-2">
<span className="text-xs font-bold text-sky-800 uppercase tracking-wider flex items-center gap-1.5">
<span className="w-5 h-5 rounded bg-sky-100 text-sky-800 flex items-center justify-center font-bold">
O
</span>
Objective Findings &amp; Palpation
Objective Exam &amp; Palpation
</span>
<span className="text-xs font-mono text-slate-400">
{adjustments.length} Segment(s) Listed
<span className="text-xs font-mono font-semibold text-slate-600 bg-slate-100 px-2 py-0.5 rounded">
{adjustments.length} Segment(s)
</span>
</div>
<textarea
@@ -308,21 +302,20 @@ export const SoapChartEditor: React.FC<SoapChartEditorProps> = ({
value={objective}
disabled={isSigned}
onChange={(e) => setObjective(e.target.value)}
className="w-full bg-slate-950 border border-slate-800 rounded-xl p-3 text-xs text-slate-200 focus:outline-none focus:border-teal-500 resize-none font-sans"
placeholder="Physical findings, spinal fixations, muscle spasms, range of motion..."
className="w-full bg-slate-50 border border-slate-200 rounded-lg p-3 text-xs text-slate-800 focus:bg-white focus:outline-none focus:border-sky-500 resize-none"
/>
</div>
<div className="text-[10px] text-slate-500 mt-2">
Vertebrae selected in 2D Spine Map sync automatically.
<div className="text-[11px] text-slate-400 mt-2">
Vertebrae selected in Spine Map sync automatically.
</div>
</div>
{/* Assessment */}
<div className="bg-slate-900 border border-slate-800 rounded-2xl p-4 shadow-xl flex flex-col justify-between">
{/* A */}
<div className="bg-white border border-slate-200 rounded-xl p-4 shadow-xs flex flex-col justify-between">
<div>
<div className="flex items-center justify-between pb-2 border-b border-slate-800 mb-2">
<span className="text-xs font-bold text-teal-400 uppercase tracking-wider flex items-center gap-1.5">
<span className="w-5 h-5 rounded-md bg-teal-500/20 flex items-center justify-center font-black text-teal-300">
<div className="flex items-center justify-between pb-2 border-b border-slate-100 mb-2">
<span className="text-xs font-bold text-sky-800 uppercase tracking-wider flex items-center gap-1.5">
<span className="w-5 h-5 rounded bg-sky-100 text-sky-800 flex items-center justify-center font-bold">
A
</span>
Clinical Assessment &amp; Progress
@@ -333,24 +326,23 @@ export const SoapChartEditor: React.FC<SoapChartEditorProps> = ({
value={assessment}
disabled={isSigned}
onChange={(e) => setAssessment(e.target.value)}
className="w-full bg-slate-950 border border-slate-800 rounded-xl p-3 text-xs text-slate-200 focus:outline-none focus:border-teal-500 resize-none font-sans"
placeholder="Clinical impression, diagnosis evaluation, treatment response..."
className="w-full bg-slate-50 border border-slate-200 rounded-lg p-3 text-xs text-slate-800 focus:bg-white focus:outline-none focus:border-sky-500 resize-none"
/>
</div>
<div className="text-[10px] text-slate-500 mt-2">
Maps to ICD-10 diagnostic codes below.
<div className="text-[11px] text-slate-400 mt-2">
Mapped to ICD-10 diagnostic codes below.
</div>
</div>
{/* Plan */}
<div className="bg-slate-900 border border-slate-800 rounded-2xl p-4 shadow-xl flex flex-col justify-between">
{/* P */}
<div className="bg-white border border-slate-200 rounded-xl p-4 shadow-xs flex flex-col justify-between">
<div>
<div className="flex items-center justify-between pb-2 border-b border-slate-800 mb-2">
<span className="text-xs font-bold text-teal-400 uppercase tracking-wider flex items-center gap-1.5">
<span className="w-5 h-5 rounded-md bg-teal-500/20 flex items-center justify-center font-black text-teal-300">
<div className="flex items-center justify-between pb-2 border-b border-slate-100 mb-2">
<span className="text-xs font-bold text-sky-800 uppercase tracking-wider flex items-center gap-1.5">
<span className="w-5 h-5 rounded bg-sky-100 text-sky-800 flex items-center justify-center font-bold">
P
</span>
Treatment Plan &amp; Home Care
Treatment Plan &amp; Modalities
</span>
</div>
<textarea
@@ -358,26 +350,27 @@ export const SoapChartEditor: React.FC<SoapChartEditorProps> = ({
value={plan}
disabled={isSigned}
onChange={(e) => setPlan(e.target.value)}
className="w-full bg-slate-950 border border-slate-800 rounded-xl p-3 text-xs text-slate-200 focus:outline-none focus:border-teal-500 resize-none font-sans"
placeholder="Techniques delivered, rehabilitation modalities, next visit schedule..."
className="w-full bg-slate-50 border border-slate-200 rounded-lg p-3 text-xs text-slate-800 focus:bg-white focus:outline-none focus:border-sky-500 resize-none"
/>
</div>
<div className="text-[10px] text-slate-500 mt-2">
Determines CPT procedure line items on Superbill.
<div className="text-[11px] text-slate-400 mt-2">
Populates CPT procedure line items on Superbill.
</div>
</div>
</div>
{/* ICD-10 & CPT Billing Code Selectors */}
{/* ICD-10 & CPT Selectors */}
<div className="grid grid-cols-1 md:grid-cols-2 gap-5">
{/* ICD-10 Codes */}
<div className="bg-slate-900 border border-slate-800 rounded-2xl p-4 shadow-xl">
<div className="flex items-center justify-between pb-2 border-b border-slate-800 mb-3">
<span className="text-xs font-bold text-slate-200 uppercase tracking-wider flex items-center gap-1.5">
<Tag className="w-3.5 h-3.5 text-teal-400" />
{/* ICD-10 */}
<div className="bg-white border border-slate-200 rounded-xl p-4 shadow-xs">
<div className="flex items-center justify-between pb-2 border-b border-slate-100 mb-3">
<span className="text-xs font-bold text-slate-800 uppercase tracking-wider flex items-center gap-1.5">
<Tag className="w-3.5 h-3.5 text-sky-700" />
ICD-10 Diagnostic Codes
</span>
<span className="text-xs text-teal-400 font-mono">{selectedIcd10.length} Selected</span>
<span className="text-xs text-sky-800 font-mono font-bold">
{selectedIcd10.length} Selected
</span>
</div>
<div className="flex flex-wrap gap-1.5 max-h-40 overflow-y-auto pr-1">
{STANDARD_ICD10_CODES.map((item) => {
@@ -394,10 +387,10 @@ export const SoapChartEditor: React.FC<SoapChartEditorProps> = ({
setSelectedIcd10((prev) => [...prev, item]);
}
}}
className={`px-2.5 py-1.5 rounded-lg text-xs font-medium text-left transition ${
className={`px-3 py-1.5 rounded-md text-xs font-medium text-left transition ${
active
? 'bg-teal-500 text-slate-950 font-bold shadow-sm'
: 'bg-slate-800 text-slate-300 hover:bg-slate-700'
? 'bg-sky-700 text-white font-bold shadow-xs'
: 'bg-slate-100 text-slate-700 hover:bg-slate-200'
}`}
>
<span className="font-mono font-bold mr-1">{item.code}</span>
@@ -409,14 +402,14 @@ export const SoapChartEditor: React.FC<SoapChartEditorProps> = ({
</div>
{/* CPT Codes */}
<div className="bg-slate-900 border border-slate-800 rounded-2xl p-4 shadow-xl">
<div className="flex items-center justify-between pb-2 border-b border-slate-800 mb-3">
<span className="text-xs font-bold text-slate-200 uppercase tracking-wider flex items-center gap-1.5">
<DollarSign className="w-3.5 h-3.5 text-teal-400" />
CPT Procedure Codes &amp; Fee Schedule
<div className="bg-white border border-slate-200 rounded-xl p-4 shadow-xs">
<div className="flex items-center justify-between pb-2 border-b border-slate-100 mb-3">
<span className="text-xs font-bold text-slate-800 uppercase tracking-wider flex items-center gap-1.5">
<DollarSign className="w-3.5 h-3.5 text-emerald-600" />
CPT Procedure Codes
</span>
<span className="text-xs text-teal-400 font-mono">
Total: ${selectedCpt.reduce((sum, c) => sum + c.fee, 0)}
<span className="text-xs text-emerald-800 font-mono font-bold">
Fee Total: ${selectedCpt.reduce((sum, c) => sum + c.fee, 0)}
</span>
</div>
<div className="flex flex-wrap gap-1.5 max-h-40 overflow-y-auto pr-1">
@@ -434,17 +427,19 @@ export const SoapChartEditor: React.FC<SoapChartEditorProps> = ({
setSelectedCpt((prev) => [...prev, item]);
}
}}
className={`px-2.5 py-1.5 rounded-lg text-xs font-medium text-left transition flex items-center justify-between gap-2 ${
className={`px-3 py-1.5 rounded-md text-xs font-medium text-left transition flex items-center justify-between gap-2 ${
active
? 'bg-indigo-600 text-white font-bold shadow-sm'
: 'bg-slate-800 text-slate-300 hover:bg-slate-700'
? 'bg-emerald-700 text-white font-bold shadow-xs'
: 'bg-slate-100 text-slate-700 hover:bg-slate-200'
}`}
>
<div>
<span className="font-mono font-bold mr-1">{item.code}</span>
<span className="text-[11px] opacity-90 truncate">{item.description}</span>
</div>
<span className="font-mono text-teal-300 shrink-0 font-bold">${item.fee}</span>
<span className="font-mono text-emerald-800 bg-white/20 px-1.5 rounded shrink-0 font-bold">
${item.fee}
</span>
</button>
);
})}
@@ -454,12 +449,12 @@ export const SoapChartEditor: React.FC<SoapChartEditorProps> = ({
{/* Signature Attestation Footer */}
{isSigned && (
<div className="p-4 bg-emerald-500/10 border border-emerald-500/30 rounded-2xl flex items-center justify-between text-xs text-emerald-300">
<div className="p-4 bg-emerald-50 border border-emerald-200 rounded-xl flex items-center justify-between text-xs text-emerald-900">
<div className="flex items-center gap-2">
<ShieldCheck className="w-5 h-5 text-emerald-400" />
<ShieldCheck className="w-5 h-5 text-emerald-600" />
<div>
<strong>Electronically Signed by:</strong> {provider.name}, {provider.credentials} (NPI {provider.npi})
<div className="text-[11px] text-emerald-400/80 mt-0.5">
<div className="text-[11px] text-emerald-700 mt-0.5">
Timestamp: {signedTimestamp || '2026-09-05T09:25:00-07:00'} Immutable Legal Record
</div>
</div>
@@ -467,7 +462,7 @@ export const SoapChartEditor: React.FC<SoapChartEditorProps> = ({
<button
type="button"
onClick={handleCreateSuperbillClick}
className="px-3 py-1.5 bg-emerald-500 hover:bg-emerald-400 text-slate-950 font-bold rounded-lg transition"
className="px-3.5 py-1.5 bg-emerald-700 hover:bg-emerald-800 text-white font-bold rounded-lg transition"
>
Open Superbill
</button>
+84 -101
View File
@@ -5,11 +5,8 @@ import { ClinicTenant, Provider } from '@/types/clinical';
import { BodyPainMap } from '@/components/ui/BodyPainMap';
import { DigitalSignaturePad } from '@/components/ui/DigitalSignaturePad';
import {
Calendar,
Clock,
User,
CheckCircle2,
Sparkles,
MapPin,
Phone,
ArrowRight,
@@ -29,13 +26,11 @@ export const PatientPortalView: React.FC<PatientPortalViewProps> = ({
}) => {
const [step, setStep] = useState<'service' | 'intake' | 'confirmed'>('service');
// Booking selections
const [selectedProvider, setSelectedProvider] = useState<Provider>(providers[0]);
const [selectedService, setSelectedService] = useState('Initial Chiropractic Exam & Adjustment');
const [selectedDate, setSelectedDate] = useState('2026-09-08');
const [selectedTime, setSelectedTime] = useState('10:30 AM');
// Intake form state
const [fullName, setFullName] = useState('Jessica Morales');
const [dob, setDob] = useState('1991-08-14');
const [phone, setPhone] = useState('(555) 302-9912');
@@ -81,87 +76,83 @@ export const PatientPortalView: React.FC<PatientPortalViewProps> = ({
status: 'booked',
room: 'Table 1',
fee: 145,
notes: `Intake complete. Chief complaint: Neck/Shoulder pain (VAS ${vasScore}/10).`,
notes: `Digital intake completed. Chief complaint: Neck/Shoulder pain (VAS ${vasScore}/10).`,
});
}, 1200);
};
return (
<div className="max-w-4xl mx-auto space-y-6">
{/* Branded Clinic Header (100% White-Label) */}
<div className="bg-slate-900 border border-slate-800 rounded-2xl p-6 shadow-2xl flex flex-col sm:flex-row sm:items-center justify-between gap-4">
{/* Branded Hospital Header */}
<div className="bg-white border border-slate-200 rounded-xl p-6 shadow-xs flex flex-col sm:flex-row sm:items-center justify-between gap-4">
<div>
<div className="flex items-center gap-2">
<span
className="w-3.5 h-3.5 rounded-full"
style={{ backgroundColor: activeTenant.brandColor }}
/>
<span className="text-xs font-mono font-bold tracking-widest uppercase text-slate-400">
<span className="text-xs font-mono font-bold tracking-widest uppercase text-slate-500">
{activeTenant.logoText}
</span>
</div>
<h2 className="text-xl font-black text-white mt-1">{activeTenant.name}</h2>
<div className="flex flex-wrap items-center gap-4 text-xs text-slate-400 mt-2">
<h2 className="text-xl font-bold text-slate-900 mt-1">{activeTenant.name}</h2>
<div className="flex flex-wrap items-center gap-4 text-xs text-slate-500 mt-2">
<span className="flex items-center gap-1">
<MapPin className="w-3.5 h-3.5 text-teal-400" />
<MapPin className="w-3.5 h-3.5 text-sky-700" />
{activeTenant.address}, {activeTenant.cityStateZip}
</span>
<span className="flex items-center gap-1">
<Phone className="w-3.5 h-3.5 text-teal-400" />
<Phone className="w-3.5 h-3.5 text-sky-700" />
{activeTenant.phone}
</span>
</div>
</div>
<div className="flex items-center gap-2 bg-slate-800/60 px-3 py-1.5 rounded-xl border border-slate-700/60 text-xs">
<ShieldCheck className="w-4 h-4 text-teal-400" />
<span className="text-slate-300">White-Label Patient Portal</span>
<div className="flex items-center gap-2 bg-sky-50 px-3 py-1.5 rounded-lg border border-sky-100 text-xs font-medium text-sky-800">
<ShieldCheck className="w-4 h-4 text-sky-700" />
<span>Patient Care Portal</span>
</div>
</div>
{/* Wizard Progress Steps */}
{/* Progress Steps */}
<div className="flex items-center justify-center gap-3 text-xs">
<div
className={`flex items-center gap-1.5 px-3 py-1 rounded-full font-semibold transition ${
className={`flex items-center gap-1.5 px-3 py-1 rounded-full font-bold transition ${
step === 'service'
? 'bg-teal-500 text-slate-950 font-bold'
: 'bg-slate-800 text-slate-400'
? 'bg-sky-700 text-white shadow-xs'
: 'bg-slate-200 text-slate-600'
}`}
>
<span>1. Select Service &amp; Time</span>
</div>
<span className="text-slate-600"></span>
<span className="text-slate-400"></span>
<div
className={`flex items-center gap-1.5 px-3 py-1 rounded-full font-semibold transition ${
className={`flex items-center gap-1.5 px-3 py-1 rounded-full font-bold transition ${
step === 'intake'
? 'bg-teal-500 text-slate-950 font-bold'
: 'bg-slate-800 text-slate-400'
? 'bg-sky-700 text-white shadow-xs'
: 'bg-slate-200 text-slate-600'
}`}
>
<span>2. Paperless Intake &amp; Pain Map</span>
<span>2. Medical Intake &amp; Pain Map</span>
</div>
<span className="text-slate-600"></span>
<span className="text-slate-400"></span>
<div
className={`flex items-center gap-1.5 px-3 py-1 rounded-full font-semibold transition ${
className={`flex items-center gap-1.5 px-3 py-1 rounded-full font-bold transition ${
step === 'confirmed'
? 'bg-emerald-500 text-slate-950 font-bold'
: 'bg-slate-800 text-slate-400'
? 'bg-emerald-700 text-white shadow-xs'
: 'bg-slate-200 text-slate-600'
}`}
>
<span>3. Confirmed</span>
<span>3. Confirmation</span>
</div>
</div>
{/* Step 1: Select Service & Doctor */}
{/* Step 1 */}
{step === 'service' && (
<div className="bg-slate-900 border border-slate-800 rounded-2xl p-6 shadow-xl space-y-6">
<div className="bg-white border border-slate-200 rounded-xl p-6 shadow-xs space-y-6">
<div>
<h3 className="text-base font-bold text-white">Choose Your Chiropractic Provider</h3>
<p className="text-xs text-slate-400 mt-0.5">
Select a doctor licensed in state-of-the-art spinal biomechanics and gentle adjustment techniques.
</p>
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4 mt-4">
<h3 className="text-base font-bold text-slate-900">Select Attending Provider</h3>
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4 mt-3">
{providers.map((p) => {
const isChosen = selectedProvider.id === p.id;
return (
@@ -171,12 +162,12 @@ export const PatientPortalView: React.FC<PatientPortalViewProps> = ({
onClick={() => setSelectedProvider(p)}
className={`flex items-center gap-3 p-4 rounded-xl border text-left transition ${
isChosen
? 'bg-slate-800 border-teal-500/80 text-white shadow-lg'
: 'bg-slate-950/60 border-slate-800 text-slate-300 hover:bg-slate-800/40'
? 'bg-sky-50/70 border-sky-400 text-slate-900 shadow-2xs'
: 'bg-slate-50 border-slate-200 text-slate-700 hover:bg-white'
}`}
>
<div
className="w-11 h-11 rounded-full flex items-center justify-center font-bold text-sm text-white shrink-0"
className="w-10 h-10 rounded-full flex items-center justify-center font-bold text-xs text-white shrink-0 shadow-2xs"
style={{ backgroundColor: p.color }}
>
{p.name
@@ -185,9 +176,9 @@ export const PatientPortalView: React.FC<PatientPortalViewProps> = ({
.join('')}
</div>
<div>
<div className="font-bold text-sm text-white">{p.name}</div>
<div className="text-xs text-teal-400 font-medium">{p.title}</div>
<div className="text-[11px] text-slate-400 mt-0.5">{p.specialty}</div>
<div className="font-bold text-sm text-slate-900">{p.name}</div>
<div className="text-xs text-sky-800 font-medium">{p.title}</div>
<div className="text-[11px] text-slate-500 mt-0.5">{p.specialty}</div>
</div>
</button>
);
@@ -196,7 +187,7 @@ export const PatientPortalView: React.FC<PatientPortalViewProps> = ({
</div>
<div>
<h3 className="text-base font-bold text-white">Select Appointment Type</h3>
<h3 className="text-base font-bold text-slate-900">Select Procedure / Visit Type</h3>
<div className="grid grid-cols-1 sm:grid-cols-3 gap-3 mt-3 text-xs">
{[
{ name: 'Initial Chiropractic Exam & Adjustment', dur: '45 mins', fee: '$145' },
@@ -209,32 +200,32 @@ export const PatientPortalView: React.FC<PatientPortalViewProps> = ({
onClick={() => setSelectedService(svc.name)}
className={`p-4 rounded-xl border text-left transition flex flex-col justify-between ${
selectedService === svc.name
? 'bg-teal-500/10 border-teal-500/80 text-white shadow'
: 'bg-slate-950/60 border-slate-800 text-slate-400 hover:bg-slate-800/40'
? 'bg-sky-50 border-sky-400 text-slate-900 shadow-2xs'
: 'bg-slate-50 border-slate-200 text-slate-700 hover:bg-white'
}`}
>
<div>
<div className="font-bold text-sm text-slate-200">{svc.name}</div>
<div className="text-[11px] text-slate-400 mt-1">{svc.dur}</div>
<div className="font-bold text-sm text-slate-900">{svc.name}</div>
<div className="text-[11px] text-slate-500 mt-1">{svc.dur}</div>
</div>
<div className="text-sm font-bold text-teal-400 font-mono mt-3">{svc.fee}</div>
<div className="text-sm font-bold text-sky-800 font-mono mt-3">{svc.fee}</div>
</button>
))}
</div>
</div>
<div>
<h3 className="text-base font-bold text-white">Pick Date &amp; Available Slot</h3>
<h3 className="text-base font-bold text-slate-900">Select Time Slot</h3>
<div className="grid grid-cols-2 sm:grid-cols-4 gap-2 mt-3 text-xs">
{['09:00 AM', '10:30 AM', '01:15 PM', '03:45 PM'].map((t) => (
<button
key={t}
type="button"
onClick={() => setSelectedTime(t)}
className={`p-3 rounded-xl border text-center font-bold transition font-mono ${
className={`p-3 rounded-lg border text-center font-bold transition font-mono ${
selectedTime === t
? 'bg-teal-500 text-slate-950 shadow-md shadow-teal-500/20'
: 'bg-slate-950/60 border-slate-800 text-slate-300 hover:bg-slate-800'
? 'bg-sky-700 text-white shadow-xs border-sky-800'
: 'bg-slate-50 border-slate-200 text-slate-700 hover:bg-slate-100'
}`}
>
<Clock className="w-3.5 h-3.5 inline mr-1" />
@@ -244,72 +235,71 @@ export const PatientPortalView: React.FC<PatientPortalViewProps> = ({
</div>
</div>
<div className="pt-4 border-t border-slate-800 flex justify-end">
<div className="pt-4 border-t border-slate-200 flex justify-end">
<button
type="button"
onClick={() => setStep('intake')}
className="px-6 py-3 rounded-xl bg-teal-500 hover:bg-teal-400 text-slate-950 text-xs font-bold flex items-center gap-2 shadow-lg shadow-teal-500/20 transition"
className="px-6 py-3 rounded-lg bg-sky-700 hover:bg-sky-800 text-white text-xs font-bold flex items-center gap-2 shadow-xs transition"
>
Continue to Digital Intake
Continue to Medical Intake
<ArrowRight className="w-4 h-4" />
</button>
</div>
</div>
)}
{/* Step 2: Paperless Intake & Body Pain Map */}
{/* Step 2 */}
{step === 'intake' && (
<form onSubmit={handleSubmitIntake} className="space-y-6">
<div className="bg-slate-900 border border-slate-800 rounded-2xl p-6 shadow-xl space-y-4">
<h3 className="text-base font-bold text-white">Patient Demographics</h3>
<div className="bg-white border border-slate-200 rounded-xl p-6 shadow-xs space-y-4">
<h3 className="text-base font-bold text-slate-900">Patient Demographics</h3>
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4 text-xs">
<div>
<label className="block text-slate-400 mb-1">Full Legal Name</label>
<label className="block text-slate-600 mb-1 font-medium">Full Legal Name</label>
<input
type="text"
required
value={fullName}
onChange={(e) => setFullName(e.target.value)}
className="w-full bg-slate-950 border border-slate-800 rounded-xl p-3 text-slate-200 focus:outline-none focus:border-teal-500"
className="w-full bg-slate-50 border border-slate-200 rounded-lg p-3 text-slate-900 focus:bg-white focus:outline-none focus:border-sky-500"
/>
</div>
<div>
<label className="block text-slate-400 mb-1">Date of Birth</label>
<label className="block text-slate-600 mb-1 font-medium">Date of Birth</label>
<input
type="date"
required
value={dob}
onChange={(e) => setDob(e.target.value)}
className="w-full bg-slate-950 border border-slate-800 rounded-xl p-3 text-slate-200 focus:outline-none focus:border-teal-500"
className="w-full bg-slate-50 border border-slate-200 rounded-lg p-3 text-slate-900 focus:bg-white focus:outline-none focus:border-sky-500"
/>
</div>
<div>
<label className="block text-slate-400 mb-1">Mobile Phone (for SMS Reminders)</label>
<label className="block text-slate-600 mb-1 font-medium">Mobile Phone (for Appointment Alerts)</label>
<input
type="tel"
required
value={phone}
onChange={(e) => setPhone(e.target.value)}
className="w-full bg-slate-950 border border-slate-800 rounded-xl p-3 text-slate-200 focus:outline-none focus:border-teal-500"
className="w-full bg-slate-50 border border-slate-200 rounded-lg p-3 text-slate-900 focus:bg-white focus:outline-none focus:border-sky-500"
/>
</div>
<div>
<label className="block text-slate-400 mb-1">Email Address</label>
<label className="block text-slate-600 mb-1 font-medium">Email Address</label>
<input
type="email"
required
value={email}
onChange={(e) => setEmail(e.target.value)}
className="w-full bg-slate-950 border border-slate-800 rounded-xl p-3 text-slate-200 focus:outline-none focus:border-teal-500"
className="w-full bg-slate-50 border border-slate-200 rounded-lg p-3 text-slate-900 focus:bg-white focus:outline-none focus:border-sky-500"
/>
</div>
</div>
</div>
{/* Interactive Pain Map */}
<BodyPainMap
selectedAreas={selectedPainAreas}
onToggleArea={handleToggleArea}
@@ -319,22 +309,20 @@ export const PatientPortalView: React.FC<PatientPortalViewProps> = ({
onTogglePainNature={handleToggleNature}
/>
{/* Signature & Consent */}
<div className="bg-slate-900 border border-slate-800 rounded-2xl p-6 shadow-xl space-y-4">
<h3 className="text-base font-bold text-white">HIPAA &amp; Chiropractic Informed Consent</h3>
<p className="text-xs text-slate-400 leading-relaxed">
I hereby authorize {activeTenant.name} and attending doctors to examine and administer
chiropractic adjustments and physical modalities as deemed necessary. I understand that,
as with any healthcare procedure, chiropractic care carries potential benefits and risks.
<div className="bg-white border border-slate-200 rounded-xl p-6 shadow-xs space-y-4">
<h3 className="text-base font-bold text-slate-900">HIPAA &amp; Clinical Informed Consent</h3>
<p className="text-xs text-slate-600 leading-relaxed">
I authorize the clinical staff of {activeTenant.name} to conduct examination and deliver
therapeutic chiropractic adjustments. I have reviewed the HIPAA privacy practices.
</p>
<DigitalSignaturePad onSaveSignature={setSignature} signerName={fullName} />
<div className="pt-4 border-t border-slate-800 flex items-center justify-between">
<div className="pt-4 border-t border-slate-200 flex items-center justify-between">
<button
type="button"
onClick={() => setStep('service')}
className="px-4 py-2 bg-slate-800 hover:bg-slate-700 text-slate-300 text-xs font-semibold rounded-xl transition"
className="px-4 py-2 bg-slate-100 hover:bg-slate-200 text-slate-700 text-xs font-semibold rounded-lg transition"
>
Back
</button>
@@ -342,17 +330,17 @@ export const PatientPortalView: React.FC<PatientPortalViewProps> = ({
<button
type="submit"
disabled={isSubmitting}
className="px-6 py-3 bg-gradient-to-r from-teal-500 to-emerald-500 hover:from-teal-400 hover:to-emerald-400 text-slate-950 text-xs font-bold rounded-xl shadow-lg shadow-teal-500/20 transition flex items-center gap-2 disabled:opacity-50"
className="px-6 py-3 bg-sky-700 hover:bg-sky-800 text-white text-xs font-bold rounded-lg shadow-xs transition flex items-center gap-2 disabled:opacity-50"
>
{isSubmitting ? (
<>
<span className="w-4 h-4 border-2 border-slate-950 border-t-transparent rounded-full animate-spin" />
Submitting to Doctor&apos;s EHR...
<span className="w-4 h-4 border-2 border-white border-t-transparent rounded-full animate-spin" />
Submitting to Clinic EHR...
</>
) : (
<>
<CheckCircle2 className="w-4 h-4" />
Confirm Appointment &amp; Submit Intake
Complete Registration &amp; Book
</>
)}
</button>
@@ -361,45 +349,40 @@ export const PatientPortalView: React.FC<PatientPortalViewProps> = ({
</form>
)}
{/* Step 3: Confirmed Screen */}
{/* Step 3 */}
{step === 'confirmed' && (
<div className="bg-slate-900 border border-slate-800 rounded-2xl p-8 shadow-2xl text-center space-y-4 max-w-lg mx-auto">
<div className="w-16 h-16 rounded-full bg-emerald-500/20 text-emerald-400 flex items-center justify-center mx-auto border border-emerald-500/40">
<div className="bg-white border border-slate-200 rounded-xl p-8 shadow-xs text-center space-y-4 max-w-lg mx-auto">
<div className="w-16 h-16 rounded-full bg-emerald-100 text-emerald-700 flex items-center justify-center mx-auto border border-emerald-200">
<CheckCircle2 className="w-8 h-8" />
</div>
<h3 className="text-xl font-black text-white">You&apos;re All Set, {fullName}!</h3>
<p className="text-xs text-slate-300 leading-relaxed">
Your appointment has been confirmed with <strong>{selectedProvider.name}</strong> at{' '}
<h3 className="text-xl font-bold text-slate-900">Registration Complete, {fullName}!</h3>
<p className="text-xs text-slate-600 leading-relaxed">
Your appointment has been registered with <strong>{selectedProvider.name}</strong> at{' '}
<strong>{activeTenant.name}</strong>.
</p>
<div className="bg-slate-950 p-4 rounded-xl border border-slate-800 text-xs space-y-2 text-left">
<div className="bg-slate-50 p-4 rounded-lg border border-slate-200 text-xs space-y-2 text-left">
<div className="flex justify-between">
<span className="text-slate-400">Date &amp; Time:</span>
<span className="font-bold text-white font-mono">
<span className="text-slate-600">Encounter Time:</span>
<span className="font-bold text-slate-900 font-mono">
{selectedDate} at {selectedTime}
</span>
</div>
<div className="flex justify-between">
<span className="text-slate-400">Service:</span>
<span className="font-bold text-teal-400">{selectedService}</span>
<span className="text-slate-600">Service:</span>
<span className="font-bold text-sky-800">{selectedService}</span>
</div>
<div className="flex justify-between">
<span className="text-slate-400">Paperless Intake:</span>
<span className="font-bold text-emerald-400">Completed &amp; Signed</span>
<span className="text-slate-600">Digital Intake:</span>
<span className="font-bold text-emerald-700">Verified &amp; Electronically Signed</span>
</div>
</div>
<div className="p-3 bg-teal-500/10 border border-teal-500/20 rounded-xl text-xs text-teal-300">
A confirmation SMS with calendar link and operatory directions has been sent to{' '}
<strong>{phone}</strong>.
</div>
<button
type="button"
onClick={() => setStep('service')}
className="mt-4 px-4 py-2 bg-slate-800 hover:bg-slate-700 text-slate-300 text-xs font-semibold rounded-xl transition"
className="mt-4 px-4 py-2 bg-slate-100 hover:bg-slate-200 text-slate-700 text-xs font-semibold rounded-lg transition"
>
Book Another Patient
</button>
+83 -85
View File
@@ -10,8 +10,6 @@ import {
CheckCircle,
Clock,
Sparkles,
PhoneCall,
Calendar,
Send,
X,
} from 'lucide-react';
@@ -48,7 +46,7 @@ export const RetentionView: React.FC<RetentionViewProps> = ({
setCustomSmsMessage(
`Hi ${patient.firstName}! Dr. Vance noticed you're due for visit ${
patient.carePlan.completedVisits + 1
} of your ${patient.carePlan.title}. We want to make sure your progress stays on track! Click here to pick a quick 15-min adjustment slot this week: https://${
} of your ${patient.carePlan.title}. We want to ensure your recovery stays on track! Click here to pick a quick 15-min adjustment slot this week: https://${
activeTenant.domain
}/book?ref=careplan`
);
@@ -73,68 +71,68 @@ export const RetentionView: React.FC<RetentionViewProps> = ({
<div className="space-y-6">
{/* Top Value Metric Cards */}
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4">
<div className="bg-slate-900 border border-slate-800 p-4 rounded-2xl shadow-lg">
<div className="flex items-center justify-between text-xs text-slate-400">
<div className="bg-white border border-slate-200 p-5 rounded-xl shadow-xs">
<div className="flex items-center justify-between text-xs text-slate-500 font-medium">
<span>Care Plan Retention</span>
<TrendingUp className="w-4 h-4 text-teal-400" />
<TrendingUp className="w-4 h-4 text-sky-700" />
</div>
<div className="text-2xl font-black text-white mt-2">84.2%</div>
<p className="text-[11px] text-teal-400 mt-1 flex items-center gap-1 font-medium">
<span>+18% higher than Jane App</span>
<div className="text-3xl font-black text-slate-900 mt-2 font-mono">84.2%</div>
<p className="text-xs text-emerald-700 mt-1 flex items-center gap-1 font-semibold">
<span>+18% higher than Jane App benchmark</span>
</p>
</div>
<div className="bg-slate-900 border border-slate-800 p-4 rounded-2xl shadow-lg">
<div className="flex items-center justify-between text-xs text-slate-400">
<div className="bg-white border border-slate-200 p-5 rounded-xl shadow-xs">
<div className="flex items-center justify-between text-xs text-slate-500 font-medium">
<span>Patients at Dropout Risk</span>
<AlertTriangle className="w-4 h-4 text-amber-400" />
<AlertTriangle className="w-4 h-4 text-amber-600" />
</div>
<div className="text-2xl font-black text-amber-400 mt-2">
<div className="text-3xl font-black text-amber-700 mt-2 font-mono">
{dropoutPatients.length} Patients
</div>
<p className="text-[11px] text-slate-400 mt-1">Missed expected cadence &gt; 10 days</p>
<p className="text-xs text-slate-500 mt-1">Missed clinical cadence &gt; 10 days</p>
</div>
<div className="bg-slate-900 border border-slate-800 p-4 rounded-2xl shadow-lg">
<div className="flex items-center justify-between text-xs text-slate-400">
<div className="bg-white border border-slate-200 p-5 rounded-xl shadow-xs">
<div className="flex items-center justify-between text-xs text-slate-500 font-medium">
<span>Revenue at Risk</span>
<span className="text-xs font-mono text-red-400 font-bold">$</span>
<span className="text-xs font-mono text-red-600 font-bold">$</span>
</div>
<div className="text-2xl font-black text-white mt-2">$2,140.00</div>
<p className="text-[11px] text-red-400 mt-1">Unbilled care plan visits</p>
<div className="text-3xl font-black text-slate-900 mt-2 font-mono">$2,140.00</div>
<p className="text-xs text-red-700 mt-1 font-medium">Unbilled scheduled adjustments</p>
</div>
<div className="bg-slate-900 border border-slate-800 p-4 rounded-2xl shadow-lg">
<div className="flex items-center justify-between text-xs text-slate-400">
<div className="bg-white border border-slate-200 p-5 rounded-xl shadow-xs">
<div className="flex items-center justify-between text-xs text-slate-500 font-medium">
<span>Recovered This Month</span>
<Sparkles className="w-4 h-4 text-teal-400" />
<Sparkles className="w-4 h-4 text-emerald-600" />
</div>
<div className="text-2xl font-black text-emerald-400 mt-2">+$3,850.00</div>
<p className="text-[11px] text-emerald-400/80 mt-1">Via AI Autonomous SMS</p>
<div className="text-3xl font-black text-emerald-700 mt-2 font-mono">+$3,850.00</div>
<p className="text-xs text-emerald-800 mt-1 font-medium">Via Autonomous Clinic SMS</p>
</div>
</div>
{/* Main Table / Patient List Header */}
<div className="bg-slate-900 border border-slate-800 rounded-2xl p-5 shadow-xl">
<div className="flex flex-col sm:flex-row sm:items-center justify-between gap-4 pb-4 border-b border-slate-800">
<div className="bg-white border border-slate-200 rounded-xl p-5 shadow-xs">
<div className="flex flex-col sm:flex-row sm:items-center justify-between gap-4 pb-4 border-b border-slate-100">
<div>
<h3 className="font-bold text-white text-base flex items-center gap-2">
<Users className="w-4 h-4 text-teal-400" />
<h3 className="font-bold text-slate-900 text-base flex items-center gap-2">
<Users className="w-4 h-4 text-sky-700" />
Care Plan Adherence &amp; Reactivation Radar
</h3>
<p className="text-xs text-slate-400 mt-0.5">
Identifies patients who have dropped off their prescribed spinal treatment plans before completing their clinical goals.
<p className="text-xs text-slate-500 mt-0.5">
Stratifies patients by treatment compliance. Autonomous clinical outreach prevents premature care abandonment.
</p>
</div>
<div className="flex items-center gap-1 bg-slate-800 p-1 rounded-xl border border-slate-700 text-xs">
<div className="flex items-center gap-1 bg-slate-100 p-1 rounded-lg border border-slate-200 text-xs">
<button
type="button"
onClick={() => setFilterType('dropout_risk')}
className={`px-3 py-1 rounded-lg font-medium transition ${
className={`px-3 py-1 rounded-md font-semibold transition ${
filterType === 'dropout_risk'
? 'bg-amber-500 text-slate-950 font-bold shadow'
: 'text-slate-400 hover:text-white'
? 'bg-amber-100 text-amber-900 font-bold shadow-xs border border-amber-200'
: 'text-slate-600 hover:text-slate-900'
}`}
>
Dropout Risk ({dropoutPatients.length})
@@ -142,10 +140,10 @@ export const RetentionView: React.FC<RetentionViewProps> = ({
<button
type="button"
onClick={() => setFilterType('active')}
className={`px-3 py-1 rounded-lg font-medium transition ${
className={`px-3 py-1 rounded-md font-semibold transition ${
filterType === 'active'
? 'bg-teal-500 text-slate-950 font-bold shadow'
: 'text-slate-400 hover:text-white'
? 'bg-emerald-100 text-emerald-900 font-bold shadow-xs border border-emerald-200'
: 'text-slate-600 hover:text-slate-900'
}`}
>
On Track ({activePatients.length})
@@ -153,10 +151,10 @@ export const RetentionView: React.FC<RetentionViewProps> = ({
<button
type="button"
onClick={() => setFilterType('all')}
className={`px-3 py-1 rounded-lg font-medium transition ${
className={`px-3 py-1 rounded-md font-semibold transition ${
filterType === 'all'
? 'bg-indigo-600 text-white font-bold shadow'
: 'text-slate-400 hover:text-white'
? 'bg-sky-700 text-white font-bold shadow-xs'
: 'text-slate-600 hover:text-slate-900'
}`}
>
All Patients ({patients.length})
@@ -165,7 +163,7 @@ export const RetentionView: React.FC<RetentionViewProps> = ({
</div>
{/* Patient Rows */}
<div className="divide-y divide-slate-800 mt-2">
<div className="divide-y divide-slate-100 mt-2">
{displayedPatients.map((p) => {
const isAtRisk = p.status === 'dropout_risk';
const progressPct = Math.round(
@@ -175,46 +173,46 @@ export const RetentionView: React.FC<RetentionViewProps> = ({
return (
<div
key={p.id}
className="py-4 flex flex-col md:flex-row md:items-center justify-between gap-4 hover:bg-slate-800/30 px-2 rounded-xl transition"
className="py-4 flex flex-col md:flex-row md:items-center justify-between gap-4 hover:bg-slate-50 px-3 rounded-lg transition"
>
<div className="min-w-0 flex-1">
<div className="flex items-center gap-2">
<h4 className="font-bold text-white text-sm">
<h4 className="font-bold text-slate-900 text-sm">
{p.firstName} {p.lastName}
</h4>
{isAtRisk ? (
<span className="px-2 py-0.5 rounded-full bg-amber-500/20 text-amber-300 border border-amber-500/30 text-[10px] font-bold uppercase flex items-center gap-1">
<AlertTriangle className="w-3 h-3" /> {p.daysSinceLastVisit} Days Since Visit
<span className="px-2.5 py-0.5 rounded-full bg-amber-50 text-amber-800 border border-amber-200 text-xs font-bold uppercase flex items-center gap-1">
<AlertTriangle className="w-3.5 h-3.5 text-amber-600" /> {p.daysSinceLastVisit} Days Overdue
</span>
) : (
<span className="px-2 py-0.5 rounded-full bg-emerald-500/20 text-emerald-300 border border-emerald-500/30 text-[10px] font-bold uppercase flex items-center gap-1">
<CheckCircle className="w-3 h-3" /> On Track
<span className="px-2.5 py-0.5 rounded-full bg-emerald-50 text-emerald-800 border border-emerald-200 text-xs font-bold uppercase flex items-center gap-1">
<CheckCircle className="w-3.5 h-3.5 text-emerald-600" /> On Track
</span>
)}
</div>
<p className="text-xs text-slate-300 mt-1">
Care Plan: <strong>{p.carePlan.title}</strong> ({p.carePlan.frequency})
<p className="text-xs text-slate-600 mt-1">
Care Protocol: <strong>{p.carePlan.title}</strong> ({p.carePlan.frequency})
</p>
<div className="flex items-center gap-4 mt-2 text-xs text-slate-400">
<div className="flex items-center gap-4 mt-2 text-xs text-slate-500">
<div className="flex items-center gap-2 w-48">
<div className="w-full bg-slate-800 rounded-full h-1.5 overflow-hidden">
<div className="w-full bg-slate-200 rounded-full h-1.5 overflow-hidden">
<div
className={`h-full rounded-full ${
isAtRisk ? 'bg-amber-400' : 'bg-teal-400'
isAtRisk ? 'bg-amber-500' : 'bg-sky-600'
}`}
style={{ width: `${progressPct}%` }}
/>
</div>
<span className="font-mono text-[11px] shrink-0">
<span className="font-mono text-xs font-semibold text-slate-700 shrink-0">
{p.carePlan.completedVisits}/{p.carePlan.totalVisits}
</span>
</div>
<span>Phone: {p.phone}</span>
<span></span>
<span>Last Visit: {p.lastVisitDate}</span>
<span>Last Encounter: {p.lastVisitDate}</span>
</div>
</div>
@@ -223,7 +221,7 @@ export const RetentionView: React.FC<RetentionViewProps> = ({
<button
type="button"
onClick={() => onSelectPatientChart(p)}
className="px-3 py-1.5 bg-slate-800 hover:bg-slate-700 text-slate-200 text-xs font-semibold rounded-lg transition"
className="px-3.5 py-1.5 bg-slate-100 hover:bg-slate-200 text-slate-700 text-xs font-bold rounded-lg border border-slate-200 transition"
>
View Chart
</button>
@@ -231,14 +229,14 @@ export const RetentionView: React.FC<RetentionViewProps> = ({
<button
type="button"
onClick={() => handleOpenSmsModal(p)}
className={`px-3 py-1.5 text-xs font-bold rounded-lg flex items-center gap-1.5 transition ${
className={`px-3.5 py-1.5 text-xs font-bold rounded-lg flex items-center gap-1.5 transition shadow-xs ${
isAtRisk
? 'bg-amber-500 hover:bg-amber-400 text-slate-950 shadow-lg shadow-amber-500/20'
: 'bg-teal-500/20 hover:bg-teal-500/30 text-teal-300 border border-teal-500/40'
? 'bg-amber-600 hover:bg-amber-700 text-white'
: 'bg-sky-700 hover:bg-sky-800 text-white'
}`}
>
<MessageSquare className="w-3.5 h-3.5" />
{isAtRisk ? 'Send AI Reactivation SMS' : 'Message Patient'}
{isAtRisk ? 'Send Reactivation SMS' : 'Message Patient'}
</button>
</div>
</div>
@@ -249,18 +247,18 @@ export const RetentionView: React.FC<RetentionViewProps> = ({
{/* SMS Reactivation Modal */}
{selectedPatientForSms && (
<div className="fixed inset-0 z-50 bg-slate-950/80 backdrop-blur-sm flex items-center justify-center p-4">
<div className="bg-slate-900 border border-slate-700 rounded-2xl max-w-lg w-full p-6 shadow-2xl animate-in fade-in zoom-in-95 duration-150 text-slate-100">
<div className="flex items-center justify-between pb-3 border-b border-slate-800">
<div className="fixed inset-0 z-50 bg-slate-900/40 backdrop-blur-xs flex items-center justify-center p-4">
<div className="bg-white border border-slate-200 rounded-2xl max-w-lg w-full p-6 shadow-xl text-slate-900">
<div className="flex items-center justify-between pb-3 border-b border-slate-100">
<div className="flex items-center gap-2">
<span className="p-2 rounded-xl bg-amber-500/20 text-amber-300">
<span className="p-2 rounded-lg bg-amber-50 text-amber-700 border border-amber-200">
<MessageSquare className="w-5 h-5" />
</span>
<div>
<h4 className="font-bold text-white text-sm">
Autonomous SMS Reactivation Dispatch
<h4 className="font-bold text-slate-900 text-sm">
Clinical SMS Reactivation Dispatch
</h4>
<p className="text-[11px] text-slate-400">
<p className="text-xs text-slate-500">
To: {selectedPatientForSms.firstName} {selectedPatientForSms.lastName} ({selectedPatientForSms.phone})
</p>
</div>
@@ -268,53 +266,53 @@ export const RetentionView: React.FC<RetentionViewProps> = ({
<button
type="button"
onClick={() => setSelectedPatientForSms(null)}
className="text-slate-400 hover:text-white p-1"
className="text-slate-400 hover:text-slate-600 p-1"
>
<X className="w-5 h-5" />
</button>
</div>
<div className="mt-4 space-y-3 text-xs">
<div className="bg-slate-950/80 p-3 rounded-xl border border-slate-800 text-slate-300 flex items-center justify-between">
<div className="bg-slate-50 p-3 rounded-lg border border-slate-200 text-slate-700 flex items-center justify-between">
<div>
<span className="text-slate-400">Sending Clinic Line:</span>{' '}
<strong className="text-white font-mono">{activeTenant.phone}</strong>
<span className="text-slate-500 font-medium">Sending Clinic Number:</span>{' '}
<strong className="text-slate-900 font-mono">{activeTenant.phone}</strong>
</div>
<span className="text-[10px] bg-teal-500/20 text-teal-300 px-2 py-0.5 rounded font-bold">
<span className="text-[11px] bg-sky-100 text-sky-800 px-2 py-0.5 rounded font-bold border border-sky-200">
AI Pilots Gateway
</span>
</div>
<div>
<label className="block text-[11px] font-semibold text-slate-400 uppercase tracking-wider mb-1.5">
Personalized 2-Way Message (Editable)
<label className="block text-xs font-bold text-slate-700 uppercase tracking-wider mb-1.5">
Automated Outreach Message
</label>
<textarea
rows={5}
value={customSmsMessage}
onChange={(e) => setCustomSmsMessage(e.target.value)}
className="w-full bg-slate-950 border border-slate-800 rounded-xl p-3 text-xs text-slate-200 focus:outline-none focus:border-amber-500 resize-none font-sans"
className="w-full bg-slate-50 border border-slate-200 rounded-lg p-3 text-xs text-slate-900 focus:bg-white focus:outline-none focus:border-sky-500 resize-none"
/>
</div>
{sentToast && (
<div className="p-3 bg-emerald-500/20 border border-emerald-500/40 rounded-xl text-xs text-emerald-300 flex items-center gap-2">
<CheckCircle className="w-4 h-4 text-emerald-400" />
<span>SMS Dispatched! Patient status updated to Reactivated.</span>
<div className="p-3 bg-emerald-50 border border-emerald-200 rounded-lg text-xs text-emerald-800 flex items-center gap-2">
<CheckCircle className="w-4 h-4 text-emerald-600" />
<span>SMS Dispatched! Patient flagged as Reactivated in EHR.</span>
</div>
)}
</div>
<div className="mt-6 pt-3 border-t border-slate-800 flex items-center justify-between">
<span className="text-[11px] text-slate-500">
Auto-fills calendar slots when patient clicks.
<div className="mt-6 pt-3 border-t border-slate-100 flex items-center justify-between">
<span className="text-xs text-slate-500">
Direct booking link embedded automatically.
</span>
<div className="flex items-center gap-2">
<button
type="button"
onClick={() => setSelectedPatientForSms(null)}
className="px-4 py-2 bg-slate-800 hover:bg-slate-700 text-slate-300 text-xs font-semibold rounded-xl transition"
className="px-4 py-2 bg-slate-100 hover:bg-slate-200 text-slate-700 text-xs font-semibold rounded-lg transition"
>
Cancel
</button>
@@ -322,17 +320,17 @@ export const RetentionView: React.FC<RetentionViewProps> = ({
type="button"
disabled={isSendingSms}
onClick={handleSendSms}
className="px-4 py-2 bg-amber-500 hover:bg-amber-400 text-slate-950 text-xs font-bold rounded-xl shadow-lg shadow-amber-500/20 transition flex items-center gap-1.5 disabled:opacity-50"
className="px-4 py-2 bg-sky-700 hover:bg-sky-800 text-white text-xs font-bold rounded-lg shadow-xs transition flex items-center gap-1.5 disabled:opacity-50"
>
{isSendingSms ? (
<>
<span className="w-3.5 h-3.5 border-2 border-slate-950 border-t-transparent rounded-full animate-spin" />
Sending SMS...
<span className="w-3.5 h-3.5 border-2 border-white border-t-transparent rounded-full animate-spin" />
Dispatching...
</>
) : (
<>
<Send className="w-3.5 h-3.5" />
Send Reactivation Text
Send Clinical SMS
</>
)}
</button>
+56 -57
View File
@@ -1,7 +1,7 @@
'use client';
import React, { useState, useEffect } from 'react';
import { Mic, MicOff, Sparkles, Volume2, Play, Pause, RotateCcw, CheckCircle2, AlertCircle } from 'lucide-react';
import { Mic, MicOff, Sparkles, Volume2, CheckCircle2, ShieldCheck } from 'lucide-react';
import { SAMPLE_AUDIO_PRESETS } from '@/lib/mock-data';
interface AmbientAudioRecorderProps {
@@ -27,7 +27,6 @@ export const AmbientAudioRecorder: React.FC<AmbientAudioRecorderProps> = ({
const [liveTranscript, setLiveTranscript] = useState('');
const [showAppliedToast, setShowAppliedToast] = useState(false);
// Timer simulation
useEffect(() => {
let interval: any = null;
if (isRecording) {
@@ -44,8 +43,7 @@ export const AmbientAudioRecorder: React.FC<AmbientAudioRecorderProps> = ({
if (!isRecording) {
setIsRecording(true);
setRecordingSeconds(0);
setLiveTranscript('Listening to ambient doctor-patient dialogue in operatory...');
// Simulate live ambient audio transcription
setLiveTranscript('Hospital ambient listener active. Recording operatory encounter...');
setTimeout(() => {
setLiveTranscript(selectedPreset.audioTranscript);
}, 2500);
@@ -87,48 +85,48 @@ export const AmbientAudioRecorder: React.FC<AmbientAudioRecorderProps> = ({
};
return (
<div className="bg-gradient-to-br from-slate-900 via-slate-900 to-teal-950/40 border border-teal-500/30 rounded-2xl p-5 shadow-2xl text-slate-100">
<div className="bg-white border border-slate-200 rounded-xl p-5 shadow-xs text-slate-800">
{/* Top Banner */}
<div className="flex flex-col sm:flex-row sm:items-center justify-between gap-4 pb-4 border-b border-slate-800">
<div className="flex flex-col sm:flex-row sm:items-center justify-between gap-4 pb-4 border-b border-slate-100">
<div>
<div className="flex items-center gap-2">
<span className="p-1.5 rounded-lg bg-teal-500/20 text-teal-400 border border-teal-500/30">
<span className="p-1.5 rounded-lg bg-sky-50 text-sky-700 border border-sky-100">
<Sparkles className="w-4 h-4" />
</span>
<h3 className="font-semibold text-white text-base tracking-tight">
Mediusa Ambient Clinical AI Scribe
<h3 className="font-bold text-slate-900 text-base tracking-tight">
Ambient Clinical Medical Scribe
</h3>
<span className="text-[11px] font-semibold px-2 py-0.5 rounded-full bg-indigo-500/20 text-indigo-300 border border-indigo-500/30">
Powered by gpt-5.4-mini Clinical Engine
<span className="text-[11px] font-bold px-2.5 py-0.5 rounded-full bg-sky-100 text-sky-800 border border-sky-200">
Clinical Intelligence Engine (gpt-5.4-mini)
</span>
</div>
<p className="text-xs text-slate-400 mt-1">
Zero typing required. Scribe listens to ambient doctor-patient encounter and converts conversational dialogue into compliant SOAP + CPT/ICD-10 codes.
<p className="text-xs text-slate-500 mt-1">
Hands-free documentation. Operatory audio automatically translates into compliant SOAP format and procedure codes.
</p>
</div>
{/* Recording Status / Mic Button */}
<div className="flex items-center gap-3">
{isRecording && (
<div className="flex items-center gap-2 text-xs font-mono font-semibold text-red-400 animate-pulse">
<span className="w-2.5 h-2.5 rounded-full bg-red-500" />
LIVE {formatTime(recordingSeconds)}
<div className="flex items-center gap-2 text-xs font-mono font-bold text-red-600 animate-pulse">
<span className="w-2.5 h-2.5 rounded-full bg-red-600" />
RECORDING {formatTime(recordingSeconds)}
</div>
)}
<button
type="button"
onClick={toggleRecording}
className={`px-4 py-2 rounded-xl text-xs font-bold flex items-center gap-2 transition-all shadow-lg ${
className={`px-4 py-2 rounded-lg text-xs font-bold flex items-center gap-2 transition shadow-xs ${
isRecording
? 'bg-red-500/20 text-red-400 border border-red-500/50 hover:bg-red-500/30'
: 'bg-teal-500 hover:bg-teal-400 text-slate-950 shadow-teal-500/20'
? 'bg-red-50 text-red-700 border border-red-200 hover:bg-red-100'
: 'bg-sky-700 hover:bg-sky-800 text-white'
}`}
>
{isRecording ? (
<>
<MicOff className="w-4 h-4" />
Stop Listening
Stop Recording
</>
) : (
<>
@@ -140,20 +138,21 @@ export const AmbientAudioRecorder: React.FC<AmbientAudioRecorderProps> = ({
</div>
</div>
{/* Waveform Graphic Animation */}
<div className="mt-4 p-4 bg-slate-950/60 rounded-xl border border-slate-800/80">
{/* Hospital Audio Monitor Waveform */}
<div className="mt-4 p-4 bg-slate-900 rounded-lg border border-slate-800 text-slate-100">
<div className="flex items-center justify-between mb-2">
<span className="text-[11px] font-semibold text-slate-400 uppercase tracking-wider flex items-center gap-1.5">
<Volume2 className="w-3.5 h-3.5 text-teal-400" />
Operatory Ambient Stream
<span className="text-xs font-bold text-slate-300 uppercase tracking-wider flex items-center gap-1.5">
<Volume2 className="w-3.5 h-3.5 text-sky-400" />
Medical Audio Feed
</span>
<span className="text-[10px] text-slate-500 font-mono">
44.1kHz 24-bit HIPAA Speech Encryption
<span className="text-[10px] text-slate-400 font-mono flex items-center gap-1">
<ShieldCheck className="w-3 h-3 text-emerald-400" />
HIPAA BAA End-to-End Encryption
</span>
</div>
{/* Dynamic Animated Bars */}
<div className="h-10 flex items-center justify-center gap-1">
<div className="h-9 flex items-center justify-center gap-1">
{[
12, 28, 45, 18, 62, 85, 40, 92, 70, 30, 80, 65, 45, 90, 35, 75, 55, 20, 68, 88,
42, 78, 52, 22, 60, 95, 38, 72, 58, 25, 48, 82, 32, 64, 44, 15,
@@ -162,11 +161,11 @@ export const AmbientAudioRecorder: React.FC<AmbientAudioRecorderProps> = ({
key={i}
className={`w-1 rounded-full transition-all duration-150 ${
isRecording
? 'bg-gradient-to-t from-teal-500 to-emerald-300 animate-pulse'
: 'bg-slate-800'
? 'bg-sky-400 animate-pulse'
: 'bg-slate-700'
}`}
style={{
height: isRecording ? `${Math.max(10, (height * (1 + (i % 3) * 0.2)) % 40)}px` : '6px',
height: isRecording ? `${Math.max(8, (height * (1 + (i % 3) * 0.2)) % 36)}px` : '4px',
animationDelay: `${(i * 40) % 600}ms`,
}}
/>
@@ -178,8 +177,8 @@ export const AmbientAudioRecorder: React.FC<AmbientAudioRecorderProps> = ({
<div className="grid grid-cols-1 md:grid-cols-12 gap-4 mt-4">
{/* Left 4 Cols: Presets */}
<div className="md:col-span-4 flex flex-col gap-2">
<label className="text-[11px] font-semibold text-slate-400 uppercase tracking-wider">
Clinical Encounter Presets (1-Click Demo)
<label className="text-xs font-bold text-slate-700 uppercase tracking-wider">
Clinical Encounter Presets
</label>
<div className="space-y-2">
{SAMPLE_AUDIO_PRESETS.map((preset) => (
@@ -187,14 +186,14 @@ export const AmbientAudioRecorder: React.FC<AmbientAudioRecorderProps> = ({
key={preset.id}
type="button"
onClick={() => handleSelectPreset(preset)}
className={`w-full text-left p-2.5 rounded-xl border transition text-xs ${
className={`w-full text-left p-3 rounded-lg border transition text-xs ${
selectedPreset.id === preset.id
? 'bg-slate-800 border-teal-500/60 text-white shadow-sm'
: 'bg-slate-900/60 border-slate-800 text-slate-400 hover:bg-slate-800/40 hover:text-slate-200'
? 'bg-sky-50/80 border-sky-300 text-sky-950 font-medium shadow-2xs'
: 'bg-slate-50 border-slate-200 text-slate-600 hover:bg-slate-100 hover:text-slate-900'
}`}
>
<div className="font-semibold text-slate-200 truncate">{preset.title}</div>
<div className="text-[10px] text-teal-400 mt-0.5">Encounter Duration: {preset.duration}</div>
<div className="font-bold text-slate-900 truncate">{preset.title}</div>
<div className="text-[11px] text-sky-700 mt-0.5">Length: {preset.duration}</div>
</button>
))}
</div>
@@ -203,50 +202,50 @@ export const AmbientAudioRecorder: React.FC<AmbientAudioRecorderProps> = ({
type="button"
onClick={handleSynthesizeSoap}
disabled={isProcessingAI}
className="mt-3 w-full py-2.5 px-4 rounded-xl text-xs font-bold bg-gradient-to-r from-teal-500 to-emerald-500 hover:from-teal-400 hover:to-emerald-400 text-slate-950 shadow-lg shadow-teal-500/20 transition flex items-center justify-center gap-2 disabled:opacity-50"
className="mt-2 w-full py-2.5 px-4 rounded-lg text-xs font-bold bg-sky-700 hover:bg-sky-800 text-white shadow-xs transition flex items-center justify-center gap-2 disabled:opacity-50"
>
{isProcessingAI ? (
<>
<span className="w-3.5 h-3.5 border-2 border-slate-950 border-t-transparent rounded-full animate-spin" />
AI Extracting SOAP...
<span className="w-3.5 h-3.5 border-2 border-white border-t-transparent rounded-full animate-spin" />
Structuring Medical Note...
</>
) : (
<>
<Sparkles className="w-4 h-4 text-slate-950" />
Extract SOAP &amp; Auto-Codes
<Sparkles className="w-4 h-4 text-white" />
Synthesize Note &amp; Auto-Codes
</>
)}
</button>
</div>
{/* Right 8 Cols: Dialogue Transcript */}
<div className="md:col-span-8 bg-slate-950/70 border border-slate-800 rounded-xl p-3.5 flex flex-col justify-between">
<div className="md:col-span-8 bg-slate-50 border border-slate-200 rounded-lg p-3.5 flex flex-col justify-between">
<div>
<div className="flex items-center justify-between pb-2 border-b border-slate-800 text-xs">
<span className="font-semibold text-slate-300">Encounter Dialogue Feed</span>
<span className="text-[10px] text-teal-400/90 font-mono">Live Whisper Stream</span>
<div className="flex items-center justify-between pb-2 border-b border-slate-200 text-xs">
<span className="font-bold text-slate-800">Transcript Log</span>
<span className="text-[11px] text-slate-500 font-mono">Live Operatory Mic</span>
</div>
<p className="text-xs text-slate-300 whitespace-pre-line mt-2 font-mono leading-relaxed max-h-[160px] overflow-y-auto pr-1">
<p className="text-xs text-slate-700 whitespace-pre-line mt-2 font-mono leading-relaxed max-h-[160px] overflow-y-auto pr-1">
{liveTranscript || selectedPreset.audioTranscript}
</p>
</div>
<div className="mt-3 pt-2 border-t border-slate-800/80 flex items-center justify-between text-[11px] text-slate-400">
<span className="flex items-center gap-1 text-teal-400">
<CheckCircle2 className="w-3.5 h-3.5" />
Auto-detects Subluxations, VAS score, CPT 98940, ICD-10 M99.0x
<div className="mt-3 pt-2 border-t border-slate-200 flex items-center justify-between text-xs text-slate-500">
<span className="flex items-center gap-1 text-sky-800 font-medium">
<CheckCircle2 className="w-3.5 h-3.5 text-sky-700" />
Auto-extracts SOAP, CPT 98940, ICD-10 M99.0x
</span>
<span className="text-slate-500">HIPAA BAA Compliant</span>
<span className="text-slate-400">Speech-to-Text v2.4</span>
</div>
</div>
</div>
{showAppliedToast && (
<div className="mt-3 p-3 bg-emerald-500/20 border border-emerald-500/40 rounded-xl text-xs text-emerald-300 flex items-center justify-between animate-in fade-in slide-in-from-top-2 duration-200">
<div className="flex items-center gap-2">
<CheckCircle2 className="w-4 h-4 text-emerald-400" />
<div className="mt-3 p-3 bg-emerald-50 border border-emerald-200 rounded-lg text-xs text-emerald-800 flex items-center justify-between animate-in fade-in duration-200">
<div className="flex items-center gap-2 font-medium">
<CheckCircle2 className="w-4 h-4 text-emerald-600" />
<span>
<strong>Success!</strong> Encounter conversation extracted into Subjective, Objective, Assessment, Plan, Spine segments, and Billing codes.
<strong>Note Populated:</strong> Subjective, Objective, Assessment, Plan, Vertebrae listings, and Billing codes auto-synced.
</span>
</div>
</div>
+38 -39
View File
@@ -1,7 +1,7 @@
'use client';
import React from 'react';
import { Activity, AlertCircle, Check } from 'lucide-react';
import { Activity, Check } from 'lucide-react';
interface BodyPainMapProps {
selectedAreas: string[];
@@ -22,7 +22,7 @@ const BODY_ZONES = [
{ id: 'Sacroiliac (SI Joint) / Pelvis', label: 'SI Joints / Pelvis', side: 'Posterior' },
{ id: 'Right Glute / Sciatic Nerve', label: 'Right Glute & Sciatica', side: 'Posterior' },
{ id: 'Left Glute / Sciatic Nerve', label: 'Left Glute & Sciatica', side: 'Posterior' },
{ id: 'Head / Occipital', label: 'Head / Base of Skull (Headaches)', side: 'Posterior' },
{ id: 'Head / Occipital', label: 'Head / Skull Base (Headaches)', side: 'Posterior' },
{ id: 'Bilateral Knees', label: 'Knees / Gait Imbalance', side: 'Anterior' },
];
@@ -32,7 +32,7 @@ const PAIN_NATURE_OPTIONS = [
'Burning Heat',
'Shooting / Radiating',
'Tingling / Pins & Needles',
'Numbness / Loss of Sensation',
'Numbness / Sensation Loss',
'Throbbing Tension',
];
@@ -46,20 +46,20 @@ export const BodyPainMap: React.FC<BodyPainMapProps> = ({
readOnly = false,
}) => {
return (
<div className="bg-slate-900 border border-slate-800 rounded-2xl p-5 text-slate-100 shadow-xl">
<div className="flex flex-col sm:flex-row sm:items-center justify-between gap-2 pb-4 border-b border-slate-800">
<div className="bg-white border border-slate-200 rounded-xl p-5 text-slate-800 shadow-xs">
<div className="flex flex-col sm:flex-row sm:items-center justify-between gap-2 pb-4 border-b border-slate-100">
<div>
<h4 className="font-semibold text-white text-sm flex items-center gap-2">
<Activity className="w-4 h-4 text-teal-400" />
<h4 className="font-bold text-slate-900 text-sm flex items-center gap-2">
<Activity className="w-4 h-4 text-sky-700" />
Interactive Pain &amp; Symptom Locator
</h4>
<p className="text-xs text-slate-400 mt-0.5">
Tap the areas on your body where you experience pain, tension, or nerve radiation.
<p className="text-xs text-slate-500 mt-0.5">
Select the anatomical areas where you experience pain, muscular guarding, or radiating symptoms.
</p>
</div>
<div className="flex items-center gap-2">
<span className="text-xs text-slate-400 font-medium">Areas Selected:</span>
<span className="px-2.5 py-0.5 rounded-full bg-teal-500/20 text-teal-300 font-bold text-xs border border-teal-500/30">
<span className="text-xs text-slate-500 font-medium">Areas Selected:</span>
<span className="px-2.5 py-0.5 rounded-full bg-sky-100 text-sky-800 font-bold text-xs border border-sky-200">
{selectedAreas.length}
</span>
</div>
@@ -68,8 +68,8 @@ export const BodyPainMap: React.FC<BodyPainMapProps> = ({
<div className="grid grid-cols-1 md:grid-cols-12 gap-6 mt-4">
{/* Pain Hotspots Grid (7 cols) */}
<div className="md:col-span-7 space-y-2">
<label className="text-[11px] font-semibold text-slate-400 uppercase tracking-wider block mb-1">
Select Pain Regions
<label className="text-xs font-bold text-slate-700 uppercase tracking-wider block mb-1">
Select Pain Zones
</label>
<div className="grid grid-cols-1 sm:grid-cols-2 gap-2">
{BODY_ZONES.map((zone) => {
@@ -80,22 +80,22 @@ export const BodyPainMap: React.FC<BodyPainMapProps> = ({
type="button"
disabled={readOnly}
onClick={() => onToggleArea(zone.id)}
className={`flex items-center justify-between p-3 rounded-xl border text-left text-xs transition ${
className={`flex items-center justify-between p-3 rounded-lg border text-left text-xs transition ${
isSelected
? 'bg-teal-500/20 border-teal-500/60 text-teal-200 shadow-sm'
: 'bg-slate-800/60 border-slate-700/60 text-slate-300 hover:bg-slate-800'
? 'bg-sky-50 border-sky-300 text-sky-950 font-medium shadow-2xs'
: 'bg-slate-50 border-slate-200 text-slate-700 hover:bg-slate-100'
}`}
>
<div>
<div className="font-semibold">{zone.label}</div>
<div className="text-[10px] text-slate-400">{zone.side} Chain</div>
<div className="font-bold text-slate-900">{zone.label}</div>
<div className="text-[11px] text-slate-500">{zone.side}</div>
</div>
{isSelected ? (
<span className="w-5 h-5 rounded-full bg-teal-500 text-slate-950 flex items-center justify-center font-bold">
<Check className="w-3 h-3" />
<span className="w-5 h-5 rounded-full bg-sky-700 text-white flex items-center justify-center font-bold shadow-2xs">
<Check className="w-3 h-3 stroke-[3]" />
</span>
) : (
<span className="w-2.5 h-2.5 rounded-full bg-slate-700" />
<span className="w-2.5 h-2.5 rounded-full bg-slate-300" />
)}
</button>
);
@@ -104,21 +104,21 @@ export const BodyPainMap: React.FC<BodyPainMapProps> = ({
</div>
{/* VAS Pain Scale & Pain Nature (5 cols) */}
<div className="md:col-span-5 bg-slate-800/40 border border-slate-700/60 rounded-xl p-4 flex flex-col justify-between">
<div className="md:col-span-5 bg-slate-50 border border-slate-200 rounded-lg p-4 flex flex-col justify-between">
<div>
{/* Visual Analog Scale (VAS) Slider */}
<div className="mb-4">
<div className="flex items-center justify-between mb-2">
<label className="text-[11px] font-semibold text-slate-300 uppercase tracking-wider">
Visual Analog Scale (VAS)
<label className="text-xs font-bold text-slate-700 uppercase tracking-wider">
Pain Scale (VAS)
</label>
<span
className={`text-sm font-black px-2 py-0.5 rounded-lg ${
className={`text-sm font-black px-2.5 py-0.5 rounded-md ${
vasScore >= 7
? 'bg-red-500/20 text-red-400 border border-red-500/30'
? 'bg-red-100 text-red-800 border border-red-200'
: vasScore >= 4
? 'bg-amber-500/20 text-amber-400 border border-amber-500/30'
: 'bg-teal-500/20 text-teal-400 border border-teal-500/30'
? 'bg-amber-100 text-amber-800 border border-amber-200'
: 'bg-sky-100 text-sky-800 border border-sky-200'
}`}
>
{vasScore} / 10
@@ -133,20 +133,20 @@ export const BodyPainMap: React.FC<BodyPainMapProps> = ({
value={vasScore}
disabled={readOnly}
onChange={(e) => onChangeVasScore(parseInt(e.target.value))}
className="w-full h-2 bg-slate-700 rounded-lg appearance-none cursor-pointer accent-teal-500"
className="w-full h-2 bg-slate-300 rounded-lg appearance-none cursor-pointer accent-sky-700"
/>
<div className="flex justify-between text-[10px] text-slate-400 mt-1 font-mono">
<div className="flex justify-between text-[11px] text-slate-500 mt-1 font-mono">
<span>0 (No Pain)</span>
<span>5 (Moderate)</span>
<span>10 (Severe)</span>
</div>
</div>
{/* Pain Nature Checkboxes */}
{/* Pain Nature */}
<div>
<label className="text-[11px] font-semibold text-slate-300 uppercase tracking-wider block mb-2">
Nature of Pain Sensation
<label className="text-xs font-bold text-slate-700 uppercase tracking-wider block mb-2">
Pain Sensation Type
</label>
<div className="flex flex-wrap gap-1.5">
{PAIN_NATURE_OPTIONS.map((nature) => {
@@ -157,10 +157,10 @@ export const BodyPainMap: React.FC<BodyPainMapProps> = ({
type="button"
disabled={readOnly}
onClick={() => onTogglePainNature(nature)}
className={`px-2.5 py-1 rounded-lg text-xs font-medium transition ${
className={`px-2.5 py-1 rounded-md text-xs font-semibold transition ${
isChecked
? 'bg-indigo-600 text-white font-semibold shadow-sm'
: 'bg-slate-800 text-slate-400 hover:bg-slate-700 hover:text-slate-200'
? 'bg-sky-700 text-white shadow-xs'
: 'bg-white border border-slate-200 text-slate-700 hover:bg-slate-100'
}`}
>
{nature}
@@ -171,9 +171,8 @@ export const BodyPainMap: React.FC<BodyPainMapProps> = ({
</div>
</div>
<div className="mt-4 pt-3 border-t border-slate-700/60 text-[11px] text-slate-400 flex items-center gap-1.5">
<AlertCircle className="w-3.5 h-3.5 text-teal-400 shrink-0" />
<span>Maps directly into Provider SOAP &amp; ICD-10 diagnostic codes.</span>
<div className="mt-4 pt-3 border-t border-slate-200 text-xs text-slate-500 flex items-center gap-1.5">
<span>Synchronizes directly into doctor&apos;s EHR examination chart.</span>
</div>
</div>
</div>
+13 -13
View File
@@ -1,7 +1,7 @@
'use client';
import React, { useRef, useState, useEffect } from 'react';
import { PenTool, RotateCcw, Check } from 'lucide-react';
import { PenTool, RotateCcw } from 'lucide-react';
interface DigitalSignaturePadProps {
onSaveSignature: (signatureData: string) => void;
@@ -21,7 +21,7 @@ export const DigitalSignaturePad: React.FC<DigitalSignaturePadProps> = ({
if (!canvas) return;
const ctx = canvas.getContext('2d');
if (!ctx) return;
ctx.strokeStyle = '#2dd4bf'; // Teal 400
ctx.strokeStyle = '#0284c7'; // Hospital Medical Blue Ink
ctx.lineWidth = 2.5;
ctx.lineCap = 'round';
ctx.lineJoin = 'round';
@@ -78,18 +78,18 @@ export const DigitalSignaturePad: React.FC<DigitalSignaturePadProps> = ({
};
return (
<div className="bg-slate-900 border border-slate-800 rounded-xl p-4">
<div className="bg-slate-50 border border-slate-200 rounded-lg p-4">
<div className="flex items-center justify-between mb-2">
<div className="flex items-center gap-1.5 text-xs text-slate-300 font-semibold">
<PenTool className="w-3.5 h-3.5 text-teal-400" />
<span>Digital Signature Pad</span>
{signerName && <span className="text-slate-400 font-normal">({signerName})</span>}
<div className="flex items-center gap-1.5 text-xs text-slate-700 font-bold">
<PenTool className="w-3.5 h-3.5 text-sky-700" />
<span>Patient Electronic Signature</span>
{signerName && <span className="text-slate-500 font-normal">({signerName})</span>}
</div>
{hasDrawn && (
<button
type="button"
onClick={clearCanvas}
className="text-[11px] text-slate-400 hover:text-slate-200 flex items-center gap-1 transition"
className="text-[11px] text-slate-500 hover:text-slate-800 flex items-center gap-1 font-semibold transition"
>
<RotateCcw className="w-3 h-3" />
Clear
@@ -97,12 +97,12 @@ export const DigitalSignaturePad: React.FC<DigitalSignaturePadProps> = ({
)}
</div>
<div className="relative border-2 border-dashed border-slate-700 rounded-lg bg-slate-950 overflow-hidden">
<div className="relative border-2 border-dashed border-slate-300 rounded-lg bg-white overflow-hidden shadow-2xs">
<canvas
ref={canvasRef}
width={500}
height={120}
className="w-full h-[120px] touch-none cursor-crosshair"
height={110}
className="w-full h-[110px] touch-none cursor-crosshair"
onMouseDown={startDrawing}
onMouseMove={draw}
onMouseUp={stopDrawing}
@@ -112,8 +112,8 @@ export const DigitalSignaturePad: React.FC<DigitalSignaturePadProps> = ({
onTouchEnd={stopDrawing}
/>
{!hasDrawn && (
<div className="absolute inset-0 pointer-events-none flex items-center justify-center text-xs text-slate-500">
Sign with finger or mouse here (HIPAA Certified)
<div className="absolute inset-0 pointer-events-none flex items-center justify-center text-xs text-slate-400 font-medium">
Sign with finger or stylus here (HIPAA Certified)
</div>
)}
</div>
+83 -86
View File
@@ -2,7 +2,7 @@
import React, { useState } from 'react';
import { SpinalAdjustmentEntry } from '@/types/clinical';
import { Check, X, ShieldAlert, Zap, Sparkles, Activity } from 'lucide-react';
import { Check, X, Zap, Activity } from 'lucide-react';
interface SpineVisualizerProps {
adjustments: SpinalAdjustmentEntry[];
@@ -125,37 +125,37 @@ export const SpineVisualizer: React.FC<SpineVisualizerProps> = ({
};
return (
<div className="bg-slate-900 border border-slate-800 rounded-2xl p-5 shadow-2xl text-slate-100">
<div className="bg-white border border-slate-200 rounded-xl p-5 shadow-xs text-slate-800">
{/* Header bar */}
<div className="flex flex-col sm:flex-row sm:items-center justify-between gap-4 pb-4 border-b border-slate-800">
<div className="flex flex-col sm:flex-row sm:items-center justify-between gap-4 pb-4 border-b border-slate-100">
<div>
<div className="flex items-center gap-2">
<span className="p-1.5 rounded-lg bg-teal-500/10 text-teal-400 border border-teal-500/20">
<span className="p-1.5 rounded-lg bg-sky-50 text-sky-700 border border-sky-100">
<Zap className="w-4 h-4" />
</span>
<h3 className="font-semibold text-white text-base tracking-tight">
Interactive 2D Spine Subluxation Visualizer
<h3 className="font-bold text-slate-900 text-base tracking-tight">
Interactive Spine Subluxation Visualizer
</h3>
<span className="ml-2 text-xs font-medium px-2 py-0.5 rounded-full bg-teal-500/20 text-teal-300 border border-teal-500/30">
{adjustments.length} Listings Adjusted
<span className="ml-2 text-xs font-semibold px-2.5 py-0.5 rounded-full bg-sky-100 text-sky-800 border border-sky-200">
{adjustments.length} Segment(s) Charted
</span>
</div>
<p className="text-xs text-slate-400 mt-1">
Rapid click-to-chart anatomical interface. Tap any spinal segment to assign listing & technique in &lt; 5 seconds.
<p className="text-xs text-slate-500 mt-1">
Clinical tactile mapping. Select any vertebra to document subluxation listing &amp; adjustment technique.
</p>
</div>
{/* Region Filter Buttons */}
<div className="flex items-center gap-1 bg-slate-800/80 p-1 rounded-xl border border-slate-700/60 overflow-x-auto text-xs">
<div className="flex items-center gap-1 bg-slate-100 p-1 rounded-lg border border-slate-200 overflow-x-auto text-xs">
{(['All', 'Cervical', 'Thoracic', 'Lumbar', 'Pelvis/Sacrum'] as const).map((region) => (
<button
key={region}
type="button"
onClick={() => setActiveFilter(region)}
className={`px-2.5 py-1 rounded-lg font-medium transition-all whitespace-nowrap ${
className={`px-3 py-1 rounded-md font-semibold transition whitespace-nowrap ${
activeFilter === region
? 'bg-teal-500 text-white shadow-sm'
: 'text-slate-400 hover:text-white hover:bg-slate-700/50'
? 'bg-white text-sky-800 shadow-xs border border-slate-200/80'
: 'text-slate-600 hover:text-slate-900'
}`}
>
{region}
@@ -167,63 +167,61 @@ export const SpineVisualizer: React.FC<SpineVisualizerProps> = ({
{/* Main Spine Grid Layout */}
<div className="grid grid-cols-1 md:grid-cols-12 gap-6 mt-5">
{/* Visual Column / Spine Segment Buttons (8 cols) */}
<div className="md:col-span-8 flex flex-col gap-2 max-h-[460px] overflow-y-auto pr-2 scrollbar-thin scrollbar-thumb-slate-700">
<div className="md:col-span-8 flex flex-col gap-2 max-h-[460px] overflow-y-auto pr-2">
<div className="grid grid-cols-2 sm:grid-cols-3 gap-2">
{filteredVertebrae.map((v) => {
const active = isAdjusted(v.id);
const adj = getAdjustment(v.id);
const regionColor =
const regionBorder =
v.region === 'Cervical'
? 'border-indigo-500/40 hover:border-indigo-400'
? 'border-indigo-200 hover:border-indigo-300'
: v.region === 'Thoracic'
? 'border-blue-500/40 hover:border-blue-400'
? 'border-sky-200 hover:border-sky-300'
: v.region === 'Lumbar'
? 'border-teal-500/40 hover:border-teal-400'
: 'border-purple-500/40 hover:border-purple-400';
? 'border-teal-200 hover:border-teal-300'
: 'border-purple-200 hover:border-purple-300';
const activeBg =
v.region === 'Cervical'
? 'bg-gradient-to-r from-indigo-950 to-slate-900 border-indigo-400 text-indigo-200'
: v.region === 'Thoracic'
? 'bg-gradient-to-r from-blue-950 to-slate-900 border-blue-400 text-blue-200'
: v.region === 'Lumbar'
? 'bg-gradient-to-r from-teal-950 to-slate-900 border-teal-400 text-teal-200'
: 'bg-gradient-to-r from-purple-950 to-slate-900 border-purple-400 text-purple-200';
const activeStyle =
'bg-sky-700 border-sky-800 text-white shadow-xs';
return (
<button
key={v.id}
type="button"
onClick={() => handleOpenModal(v)}
className={`relative flex items-center justify-between p-2.5 rounded-xl border text-left transition-all duration-150 group ${
active ? `${activeBg} shadow-lg shadow-teal-950/20` : `bg-slate-800/40 ${regionColor} text-slate-300 hover:bg-slate-800/80`
className={`relative flex items-center justify-between p-2.5 rounded-lg border text-left transition-all duration-150 ${
active
? activeStyle
: `bg-slate-50/60 ${regionBorder} text-slate-800 hover:bg-white`
}`}
>
<div className="flex items-center gap-2 min-w-0">
<span
className={`w-7 h-7 flex items-center justify-center rounded-lg font-bold text-xs shrink-0 ${
className={`w-7 h-7 flex items-center justify-center rounded font-bold text-xs shrink-0 ${
active
? 'bg-teal-500 text-slate-950 font-black'
: 'bg-slate-800 text-slate-400 group-hover:text-white'
? 'bg-white text-sky-800'
: 'bg-white text-slate-700 border border-slate-200 shadow-2xs'
}`}
>
{v.shortName}
</span>
<div className="min-w-0">
<div className="text-xs font-semibold truncate leading-tight">{v.name}</div>
<div className="text-[10px] text-slate-400 truncate mt-0.5">
<div className={`text-xs font-bold truncate leading-tight ${active ? 'text-white' : 'text-slate-900'}`}>
{v.name}
</div>
<div className={`text-[11px] truncate mt-0.5 ${active ? 'text-sky-100' : 'text-slate-500'}`}>
{active && adj ? `${adj.listing}${adj.technique}` : v.region}
</div>
</div>
</div>
{active ? (
<span className="w-5 h-5 rounded-full bg-teal-500/20 text-teal-400 flex items-center justify-center shrink-0 border border-teal-500/40">
<Check className="w-3 h-3" />
<span className="w-5 h-5 rounded-full bg-white text-sky-800 flex items-center justify-center shrink-0 shadow-xs">
<Check className="w-3 h-3 stroke-[3]" />
</span>
) : (
<span className="w-2 h-2 rounded-full bg-slate-700 group-hover:bg-slate-500 shrink-0" />
<span className="w-2 h-2 rounded-full bg-slate-300 shrink-0" />
)}
</button>
);
@@ -232,23 +230,23 @@ export const SpineVisualizer: React.FC<SpineVisualizerProps> = ({
</div>
{/* Right Details Panel: Active Encounters & Clinical Notes (4 cols) */}
<div className="md:col-span-4 bg-slate-800/50 border border-slate-700/60 rounded-xl p-4 flex flex-col justify-between">
<div className="md:col-span-4 bg-slate-50 border border-slate-200 rounded-xl p-4 flex flex-col justify-between">
<div>
<div className="flex items-center justify-between pb-3 border-b border-slate-700">
<span className="text-xs font-semibold text-slate-300 uppercase tracking-wider">
Visit Adjustment Summary
<div className="flex items-center justify-between pb-3 border-b border-slate-200">
<span className="text-xs font-bold text-slate-700 uppercase tracking-wider">
Visit Adjustments
</span>
<span className="text-xs text-teal-400 font-mono">
<span className="text-xs text-sky-700 font-mono font-bold">
{adjustments.length} Segment(s)
</span>
</div>
{adjustments.length === 0 ? (
<div className="py-10 text-center text-slate-500">
<Activity className="w-8 h-8 mx-auto mb-2 opacity-40 text-slate-400" />
<p className="text-xs">No vertebrae selected.</p>
<p className="text-[11px] text-slate-600 mt-1">
Click any vertebra on the left to add adjustment listing.
<div className="py-10 text-center text-slate-400">
<Activity className="w-8 h-8 mx-auto mb-2 opacity-50 text-slate-400" />
<p className="text-xs font-medium">No vertebrae selected.</p>
<p className="text-[11px] text-slate-400 mt-1">
Click any segment on the left to document clinical listings.
</p>
</div>
) : (
@@ -256,17 +254,17 @@ export const SpineVisualizer: React.FC<SpineVisualizerProps> = ({
{adjustments.map((item) => (
<div
key={item.vertebra}
className="flex items-center justify-between bg-slate-900/80 border border-slate-700/80 p-2.5 rounded-lg text-xs"
className="flex items-center justify-between bg-white border border-slate-200 p-2.5 rounded-lg text-xs shadow-2xs"
>
<div>
<div className="flex items-center gap-1.5">
<span className="font-bold text-teal-300">{item.vertebra}</span>
<span className="text-[10px] text-slate-400 font-mono">({item.region})</span>
<span className="font-bold text-sky-800">{item.vertebra}</span>
<span className="text-[11px] text-slate-500 font-mono">({item.region})</span>
</div>
<div className="text-[11px] text-slate-300 mt-0.5 font-medium">
<div className="text-xs text-slate-800 mt-0.5 font-medium">
{item.listing}
</div>
<div className="text-[10px] text-teal-400/90 font-sans">
<div className="text-[11px] text-sky-700 font-medium">
Technique: {item.technique}
</div>
</div>
@@ -275,8 +273,8 @@ export const SpineVisualizer: React.FC<SpineVisualizerProps> = ({
<button
type="button"
onClick={() => handleRemoveListing(item.vertebra)}
className="text-slate-500 hover:text-red-400 p-1 transition-colors"
title="Remove adjustment"
className="text-slate-400 hover:text-red-600 p-1 transition-colors"
title="Remove segment"
>
<X className="w-3.5 h-3.5" />
</button>
@@ -289,9 +287,9 @@ export const SpineVisualizer: React.FC<SpineVisualizerProps> = ({
{/* Quick Subluxation Preset Macros */}
{!readOnly && (
<div className="mt-4 pt-3 border-t border-slate-700">
<div className="text-[11px] font-semibold text-slate-400 uppercase tracking-wider mb-2">
Chiropractic Quick Macros
<div className="mt-4 pt-3 border-t border-slate-200">
<div className="text-[11px] font-bold text-slate-600 uppercase tracking-wider mb-2">
Clinical Fast Macros
</div>
<div className="grid grid-cols-2 gap-1.5">
<button
@@ -310,7 +308,7 @@ export const SpineVisualizer: React.FC<SpineVisualizerProps> = ({
technique: 'Diversified',
});
}}
className="px-2 py-1.5 bg-slate-800 hover:bg-slate-700 text-slate-300 rounded text-[11px] font-medium transition text-left truncate"
className="px-2.5 py-1.5 bg-white hover:bg-slate-100 text-slate-700 border border-slate-200 rounded text-xs font-semibold transition text-left truncate shadow-2xs"
>
+ Cervical Protocol
</button>
@@ -330,7 +328,7 @@ export const SpineVisualizer: React.FC<SpineVisualizerProps> = ({
technique: 'Thompson Drop',
});
}}
className="px-2 py-1.5 bg-slate-800 hover:bg-slate-700 text-slate-300 rounded text-[11px] font-medium transition text-left truncate"
className="px-2.5 py-1.5 bg-white hover:bg-slate-100 text-slate-700 border border-slate-200 rounded text-xs font-semibold transition text-left truncate shadow-2xs"
>
+ L4/SI Protocol
</button>
@@ -340,40 +338,39 @@ export const SpineVisualizer: React.FC<SpineVisualizerProps> = ({
</div>
</div>
{/* Interactive Adjustment Modal / Drawer */}
{/* Interactive Adjustment Modal */}
{selectedVertebra && (
<div className="fixed inset-0 z-50 bg-slate-950/80 backdrop-blur-sm flex items-center justify-center p-4">
<div className="bg-slate-900 border border-slate-700 rounded-2xl max-w-md w-full p-6 shadow-2xl animate-in fade-in zoom-in-95 duration-150 text-slate-100">
<div className="flex items-center justify-between pb-3 border-b border-slate-800">
<div className="fixed inset-0 z-50 bg-slate-900/40 backdrop-blur-xs flex items-center justify-center p-4">
<div className="bg-white border border-slate-200 rounded-2xl max-w-md w-full p-6 shadow-xl text-slate-900">
<div className="flex items-center justify-between pb-3 border-b border-slate-100">
<div className="flex items-center gap-2">
<span className="w-8 h-8 rounded-lg bg-teal-500/20 text-teal-400 font-bold flex items-center justify-center text-sm border border-teal-500/30">
<span className="w-8 h-8 rounded-lg bg-sky-100 text-sky-800 font-bold flex items-center justify-center text-sm">
{selectedVertebra.shortName}
</span>
<div>
<h4 className="font-semibold text-white text-sm">{selectedVertebra.name}</h4>
<p className="text-xs text-slate-400">{selectedVertebra.region} Spine</p>
<h4 className="font-bold text-slate-900 text-sm">{selectedVertebra.name}</h4>
<p className="text-xs text-slate-500">{selectedVertebra.region} Region</p>
</div>
</div>
<button
type="button"
onClick={() => setSelectedVertebra(null)}
className="text-slate-400 hover:text-white p-1"
className="text-slate-400 hover:text-slate-600 p-1"
>
<X className="w-5 h-5" />
</button>
</div>
<div className="mt-4 space-y-4">
{/* Common Clinical Symptoms Reference */}
<div className="bg-slate-800/60 rounded-xl p-2.5 border border-slate-700/60 text-xs">
<span className="text-slate-400 font-semibold">Anatomical Correlate:</span>{' '}
<span className="text-slate-300">{selectedVertebra.commonSymptoms}</span>
<div className="bg-slate-50 rounded-lg p-3 border border-slate-200 text-xs">
<span className="text-slate-600 font-bold">Anatomical Correlate:</span>{' '}
<span className="text-slate-700">{selectedVertebra.commonSymptoms}</span>
</div>
{/* Subluxation Listing Selector */}
<div>
<label className="block text-xs font-semibold text-slate-300 uppercase tracking-wider mb-2">
Subluxation Listing / Motion Restriction
<label className="block text-xs font-bold text-slate-700 uppercase tracking-wider mb-2">
Subluxation Listing
</label>
<div className="grid grid-cols-2 gap-1.5">
{COMMON_LISTINGS.map((listing) => (
@@ -383,8 +380,8 @@ export const SpineVisualizer: React.FC<SpineVisualizerProps> = ({
onClick={() => setActiveListing(listing)}
className={`px-3 py-2 rounded-lg text-xs font-medium text-left transition ${
activeListing === listing
? 'bg-teal-500 text-white font-semibold shadow-sm'
: 'bg-slate-800 text-slate-300 hover:bg-slate-700'
? 'bg-sky-700 text-white font-bold shadow-xs'
: 'bg-slate-100 text-slate-700 hover:bg-slate-200'
}`}
>
{listing}
@@ -395,8 +392,8 @@ export const SpineVisualizer: React.FC<SpineVisualizerProps> = ({
{/* Technique Selector */}
<div>
<label className="block text-xs font-semibold text-slate-300 uppercase tracking-wider mb-2">
Chiropractic Technique
<label className="block text-xs font-bold text-slate-700 uppercase tracking-wider mb-2">
Adjustment Technique
</label>
<div className="grid grid-cols-3 gap-1.5">
{TECHNIQUES.map((tech) => (
@@ -404,10 +401,10 @@ export const SpineVisualizer: React.FC<SpineVisualizerProps> = ({
key={tech}
type="button"
onClick={() => setActiveTechnique(tech)}
className={`px-2 py-1.5 rounded-lg text-xs font-medium transition text-center ${
className={`px-2 py-1.5 rounded-lg text-xs font-semibold transition text-center ${
activeTechnique === tech
? 'bg-indigo-600 text-white font-semibold shadow-sm'
: 'bg-slate-800 text-slate-300 hover:bg-slate-700'
? 'bg-sky-700 text-white shadow-xs'
: 'bg-slate-100 text-slate-700 hover:bg-slate-200'
}`}
>
{tech}
@@ -418,12 +415,12 @@ export const SpineVisualizer: React.FC<SpineVisualizerProps> = ({
</div>
{/* Modal Actions */}
<div className="mt-6 pt-4 border-t border-slate-800 flex items-center justify-between gap-3">
<div className="mt-6 pt-4 border-t border-slate-100 flex items-center justify-between gap-3">
{isAdjusted(selectedVertebra.id) ? (
<button
type="button"
onClick={() => handleRemoveListing(selectedVertebra.id)}
className="px-3 py-2 rounded-xl text-xs font-semibold text-red-400 hover:bg-red-500/10 transition"
className="px-3 py-2 rounded-lg text-xs font-bold text-red-600 hover:bg-red-50 transition"
>
Clear Segment
</button>
@@ -435,17 +432,17 @@ export const SpineVisualizer: React.FC<SpineVisualizerProps> = ({
<button
type="button"
onClick={() => setSelectedVertebra(null)}
className="px-4 py-2 rounded-xl text-xs font-semibold bg-slate-800 hover:bg-slate-700 text-slate-300 transition"
className="px-4 py-2 rounded-lg text-xs font-semibold bg-slate-100 hover:bg-slate-200 text-slate-700 transition"
>
Cancel
</button>
<button
type="button"
onClick={handleSaveListing}
className="px-4 py-2 rounded-xl text-xs font-semibold bg-teal-500 hover:bg-teal-400 text-slate-950 font-bold shadow-lg shadow-teal-500/20 transition flex items-center gap-1.5"
className="px-4 py-2 rounded-lg text-xs font-bold bg-sky-700 hover:bg-sky-800 text-white shadow-xs transition flex items-center gap-1.5"
>
<Check className="w-4 h-4" />
Apply Listing
Save to Chart
</button>
</div>
</div>