Files
TeleEms-Dashboard/src/pages/HospitalConsole.tsx

724 lines
29 KiB
TypeScript

import React, { useState, useEffect } from 'react';
import { useSearchParams } from 'react-router-dom';
import {
Building2,
Activity,
Truck,
FileText,
Settings,
CheckCircle2,
Monitor,
TrendingUp,
Database,
Video,
Hospital,
ChevronDown,
} from 'lucide-react';
import { motion, AnimatePresence } from 'framer-motion';
import { Card } from '../components/Common';
import { authApi } from '../api/auth';
import { hospitalApi } from '../api/hospital';
import { HospitalSelector } from './hospital/HospitalSelector';
import { EDMonitor } from './hospital/EDMonitor';
import { TripManagement } from './hospital/TripManagement';
import { SetupPanel } from './hospital/SetupPanel';
import { StaffModal } from './hospital/StaffModal';
import { DeptModal } from './hospital/DeptModal';
import { TeleLinkHub } from './hospital/TeleLinkHub';
import { FleetView } from './hospital/FleetView';
import { EPCRRecords } from './hospital/EPCRRecords';
import { PatientArchive } from './hospital/PatientArchive';
import { ReferralsSetup } from './hospital/ReferralsSetup';
import { HospitalAnalytics } from './hospital/HospitalAnalytics';
import './HospitalConsole.css';
type ConsoleModule =
| 'ED_MONITOR'
| 'BOOKINGS'
| 'FLEET'
| 'TELELINK'
| 'EPCR'
| 'HISTORY'
| 'REPORTS'
| 'SETUP'
| 'REFERRALS';
// ─── Static data ─────────────────────────────────────────────────────────────
import { incidentsApi } from '../api/incidents';
const INITIAL_TRIPS: any[] = [];
const FLEET_DATA: any[] = [];
const EPCR_DATA: any[] = [];
const TELELINK_QUEUE: any[] = [];
// ─── Component ───────────────────────────────────────────────────────────────
export const HospitalConsole: React.FC = () => {
// ── Core state ──
const [hospitals, setHospitals] = useState<any[]>([]);
const [isLoadingHospitals, setIsLoadingHospitals] = useState(true);
const [selectedHospital, setSelectedHospital] = useState<any>(null);
const [searchParams, setSearchParams] = useSearchParams();
const activeModule = (searchParams.get('tab') as ConsoleModule) || 'ED_MONITOR';
const setActiveModule = (key: ConsoleModule) => {
setSearchParams({ tab: key });
};
// activeModule defaults to ED_MONITOR when tab param exists
// When no tab is set, we show the card landing page
const [successMessage, setSuccessMessage] = useState('');
// ── Data state ──
const [incomingPatients, setIncomingPatients] = useState<any[]>([]);
const [tripsData, setTripsData] = useState(INITIAL_TRIPS);
const [departments, setDepartments] = useState<any[]>([]);
const [allUsers, setAllUsers] = useState<any[]>([]);
const [rolesList, setRolesList] = useState<any[]>([]);
const [user, setUser] = useState<any>(null);
// ── Modal state ──
const [isBookingModalOpen, setIsBookingModalOpen] = useState(false);
const [isStaffModalOpen, setIsStaffModalOpen] = useState(false);
const [isEditingStaff, setIsEditingStaff] = useState(false);
const [editingStaffId, setEditingStaffId] = useState<string | null>(null);
const [isDeptModalOpen, setIsDeptModalOpen] = useState(false);
const [isUpdating, setIsUpdating] = useState(false);
const [setupSubTab, setSetupSubTab] = useState('PROFILE');
const [showPassword, setShowPassword] = useState(false);
// ── Form state ──
const [staffFormData, setStaffFormData] = useState({
name: '', email: '', phone: '', username: '', password: '',
role: 'ED_DOCTOR', department: '', specialization: '',
license_number: '', designation: '', shift: 'General',
employee_id: '', org_id: '', assigned_floor: '', languages: ''
});
const [deptFormData, setDeptFormData] = useState({
name: '', headOfDepartment: '', totalBedsCapacity: 0,
contactPhone: '', isActive: true,
});
// ── Load user from localStorage ──
useEffect(() => {
const userData = localStorage.getItem('teleems_user');
if (userData) setUser(JSON.parse(userData));
}, []);
// ── Fetch roles ──
useEffect(() => {
const loadRoles = async () => {
try {
const token = localStorage.getItem('teleems_token') || '';
if (!token) return;
const res = await authApi.getRoles(token);
if (res.data?.data) setRolesList(res.data.data);
} catch (err) {
console.error('Failed to fetch roles:', err);
}
};
loadRoles();
}, []);
// ── Fetch trips ──
useEffect(() => {
const loadTrips = async () => {
try {
const token = localStorage.getItem('teleems_token') || '';
if (!token) return;
const res = await incidentsApi.getIncidents({}, token);
if (res.data) {
const formatted = (Array.isArray(res.data) ? res.data : []).map((inc: any) => ({
id: inc.id.substring(0, 8).toUpperCase(),
patient: inc.patients?.[0]?.name || 'Unknown',
mrn: `MRN-${inc.id.substring(8, 12).toUpperCase()}`,
type: inc.category || 'EMERGENCY',
origin: inc.address || 'Field Location',
destination: selectedHospital?.name || 'Hospital',
vehicle: 'Pending',
crew: 'Pending',
status: inc.status || 'PENDING',
eta: 'TBD',
urgency: inc.severity || 'YELLOW',
step: Math.max(1, inc.status === 'COMPLETED' ? 4 : (inc.status === 'ACTIVE' ? 2 : 1))
}));
setTripsData(formatted);
}
} catch (err) {
console.error('Failed to fetch trips:', err);
setTripsData([]);
}
};
loadTrips();
const interval = setInterval(loadTrips, 15000); // 15-sec poll
return () => clearInterval(interval);
}, [selectedHospital]);
// ── Fetch incoming patients for ED Monitor ──
useEffect(() => {
const loadIncoming = async () => {
try {
const token = localStorage.getItem('teleems_token') || '';
if (!token) return;
const orgId = user?.organisationId || user?.hospitalId || selectedHospital?.rawUser?.organisationId || selectedHospital?.id;
const res = await hospitalApi.getIncomingOperations(token, orgId);
console.log('[DEBUG] Incoming Operations Full Response:', res);
let items = [];
if (res.data?.data?.items && Array.isArray(res.data.data.items)) {
items = res.data.data.items;
} else if (res.data?.items && Array.isArray(res.data.items)) {
items = res.data.items;
} else if (Array.isArray(res.data?.data)) {
items = res.data.data;
} else if (Array.isArray(res.data)) {
items = res.data;
} else if (Array.isArray(res.items)) {
items = res.items;
}
console.log('[DEBUG] Extracted Items:', items);
if (items.length > 0) {
const flattenedPatients: any[] = [];
items.forEach((dispatch: any) => {
const rawPatients = dispatch.patients || dispatch.patient_data;
const patientsArr = Array.isArray(rawPatients) ? rawPatients : (rawPatients ? [rawPatients] : [{}]);
patientsArr.forEach((patient: any, idx: number) => {
const pId = patient.id || dispatch.dispatch_id || `idx-${idx}`;
flattenedPatients.push({
id: `${pId}-${idx}`,
originalId: pId,
mrn: patient.mrn || String(pId).substring(0, 8).toUpperCase(),
name: patient.name || dispatch.patient_name || 'Unknown Patient',
age: patient.age || dispatch.patient_age || '--',
gender: patient.gender || dispatch.patient_gender || '--',
triage: patient.triage_level || dispatch.severity || 'GREEN',
complaint: patient.chief_complaint || patient.symptoms?.[0]?.name || dispatch.category || 'Observation',
eta: dispatch.eta_seconds ? Math.floor(dispatch.eta_seconds / 60) + 'm' : 'TBD',
ambulanceId: (dispatch.dispatch_id || 'UNIT').substring(0, 8).toUpperCase(),
status: dispatch.status || 'IN_TRANSIT',
vitals: { hr: '--', spo2: '--', bp: '--/--' },
location: 'In Transit',
gcs: patient.gcs || null,
hpi: patient.hpi || null,
avpu: patient.avpu || '--',
pupils: patient.pupils || null,
trauma: patient.trauma || null,
allergies: patient.allergies || [],
surgeries: patient.surgeries || [],
conditions: patient.conditions || [],
medications: patient.medications || [],
informer: {
name: patient.informer_name || '--',
relation: patient.informer_relation || '--',
phone: patient.informer_phone || '--'
},
mlc: {
is_mlc: patient.is_mlc || false,
fir: patient.mlc_fir_number || '--',
station: patient.mlc_police_station || '--',
officer: patient.mlc_officer_contact || '--'
}
});
});
});
console.log('[DEBUG] Final Flattened Patients:', flattenedPatients);
setIncomingPatients(flattenedPatients);
} else {
setIncomingPatients([]);
}
} catch (err) {
console.error('Failed to fetch incoming patients:', err);
}
};
loadIncoming();
const interval = setInterval(loadIncoming, 15000);
return () => clearInterval(interval);
}, [selectedHospital]);
// ── Fetch departments ──
useEffect(() => {
const fetchDepartments = async () => {
const token = localStorage.getItem('teleems_token');
if (token && selectedHospital) {
try {
const res = await authApi.getDepartments(token);
if (res.data) {
const hospId = selectedHospital.rawUser?.hospitalId || selectedHospital.id;
setDepartments(res.data.filter((d: any) => d.hospitalId === hospId));
}
} catch (err) {
console.error('Failed to fetch departments:', err);
}
}
};
fetchDepartments();
}, [selectedHospital]);
// ── Fetch hospitals ──
useEffect(() => {
const fetchHospitals = async () => {
const token = localStorage.getItem('teleems_token');
if (!token) { setIsLoadingHospitals(false); return; }
try {
const response = await authApi.getUsers(token);
if (response.status === 200 || response.status === 201) {
const users = response.data || [];
setAllUsers(users);
const admins = users.filter((u: any) => {
const roles = Array.isArray(u.roles) ? u.roles.map((r: any) => String(r).toUpperCase()) : [];
return roles.includes('HOSPITAL ADMIN') || roles.includes('HOSPITAL_ADMIN');
});
const mappedHospitals = admins.map((u: any) => {
const h = u.metadata?.hospital || {};
const org = u.metadata?.organization || {};
return {
id: u.id,
name: h.name || org.company_name || u.name || u.username || 'Unknown Hospital',
type: h.specialization || h.type || 'General Care',
address: h.city || org.city || 'Bangalore',
floor: h.floor || 0,
lat: h.lat || 13.0827,
lon: h.lon || 80.2707,
status: u.status === 'ACTIVE' ? 'Active' : 'Occupied',
beds: h.beds || '12/50',
icon: <Building2 />,
specialty: h.specialization ? [h.specialization] : ['General'],
admin: u.name,
email: u.email,
phone: u.phone,
rawUser: u,
};
});
setHospitals(mappedHospitals);
// Auto-select first hospital if none selected
if (!selectedHospital && mappedHospitals.length > 0) {
setSelectedHospital(mappedHospitals[0]);
}
}
} catch (error) {
console.error('Failed to fetch hospitals:', error);
setHospitals([]);
} finally {
setIsLoadingHospitals(false);
}
};
fetchHospitals();
}, []);
// ── Fetch specific profile ──
useEffect(() => {
const fetchProfile = async () => {
const token = localStorage.getItem('teleems_token');
if (!token) return;
try {
const res = await hospitalApi.getProfile(token);
console.log("PROFILE API RESPONSE:", res);
if (res.data) {
const apiData = res.data.metadata?.hospital || res.data.profile || res.data;
setSelectedHospital((prev: any) => ({
...prev,
...apiData,
name: apiData.hospitalName || apiData.name || prev?.name || '',
contact_phone: apiData.contact_phone || apiData.phone || prev?.contact_phone || '',
contact_email: apiData.contact_email || apiData.email || prev?.contact_email || '',
address: apiData.address || prev?.address || '',
emergency_phone: apiData.emergency_phone || prev?.emergency_phone || '',
gps_lat: apiData.gps_lat || prev?.gps_lat || '',
gps_lon: apiData.gps_lon || prev?.gps_lon || '',
nabh_status: apiData.nabh_status || prev?.nabh_status || false,
// Keep the raw user object around just in case
rawUser: res.data
}));
}
} catch (err) {
console.error('Failed to fetch hospital profile:', err);
}
};
fetchProfile();
}, []);
// ── Handlers ──
const handleSaveProfile = async () => {
if (!selectedHospital) return;
setIsUpdating(true);
try {
const token = localStorage.getItem('teleems_token');
if (!token) throw new Error('Not authenticated');
const payload = {
name: selectedHospital.name || '',
address: selectedHospital.address || '',
emergency_phone: selectedHospital.emergency_phone || '',
contact_phone: selectedHospital.contact_phone || '',
contact_email: selectedHospital.contact_email || '',
gps_lat: parseFloat(selectedHospital.gps_lat) || 0,
gps_lon: parseFloat(selectedHospital.gps_lon) || 0,
nabh_status: selectedHospital.nabh_status || false
};
const result = await hospitalApi.updateProfile(payload, token);
console.log('PATCH RESPONSE:', result);
if (result.status === 200 || result.status === 201 || !result.error) {
setSuccessMessage('Configuration Updated!');
setTimeout(() => setSuccessMessage(''), 3000);
} else {
console.error('PATCH FAILED:', result);
alert(result.message || 'Update failed');
}
} catch (err: any) {
alert(err.message);
} finally {
setIsUpdating(false);
}
};
const handleRegisterStaff = async () => {
try {
const token = localStorage.getItem('teleems_token');
const basePayload: any = {
phone: staffFormData.phone,
email: staffFormData.email,
name: staffFormData.name,
username: staffFormData.username,
role: staffFormData.role,
};
if (staffFormData.password) {
basePayload.password = staffFormData.password;
}
let finalPayload: any = { ...basePayload };
if (staffFormData.role === 'ED_DOCTOR') {
finalPayload.metadata = {
specialization: staffFormData.specialization,
license_number: staffFormData.license_number,
department: staffFormData.department,
designation: staffFormData.designation,
shift: staffFormData.shift,
};
} else if (staffFormData.role === 'Hospital Coordinator') {
finalPayload.department = staffFormData.department;
finalPayload.designation = staffFormData.designation;
finalPayload.metadata = {
shift: staffFormData.shift,
languages: staffFormData.languages ? staffFormData.languages.split(',').map(s => s.trim()) : [],
};
} else if (staffFormData.role === 'Hospital Nurse') {
finalPayload.department = staffFormData.department;
finalPayload.designation = staffFormData.designation;
finalPayload.employee_id = staffFormData.employee_id;
finalPayload.org_id = staffFormData.org_id || selectedHospital?.rawUser?.organisationId;
finalPayload.metadata = {
shift: staffFormData.shift,
languages: staffFormData.languages ? staffFormData.languages.split(',').map(s => s.trim()) : [],
assigned_floor: staffFormData.assigned_floor,
};
} else {
// Fallback for other roles
finalPayload.org_id = selectedHospital?.rawUser?.organisationId;
finalPayload.metadata = {
department: staffFormData.department,
designation: staffFormData.designation,
shift: staffFormData.shift,
};
}
let res;
if (isEditingStaff && editingStaffId) {
// Backend forbids updating username and password via this endpoint
delete finalPayload.username;
delete finalPayload.password;
res = await authApi.updateUser(editingStaffId, finalPayload, token || '');
} else {
res = await hospitalApi.registerStaff(finalPayload, token || '');
}
if (res.status === 201 || res.status === 200 || !res.error) {
setSuccessMessage(`Staff ${isEditingStaff ? 'updated' : 'registered'} successfully.`);
setIsStaffModalOpen(false);
setIsEditingStaff(false);
setEditingStaffId(null);
// Refresh users list
const usersRes = await authApi.getUsers(token || '');
if (usersRes.data) {
setAllUsers(usersRes.data);
}
} else {
alert(res.message || 'Operation failed');
}
} catch (e: any) {
alert(e.message || 'Failed');
}
};
const handleEditStaff = (user: any) => {
setIsEditingStaff(true);
setEditingStaffId(user.id);
// Parse role-specific metadata
const role = user.roles?.[0] || 'ED_DOCTOR';
const metadata = user.metadata || {};
setStaffFormData({
name: user.name || '',
email: user.email || '',
phone: user.phone || '',
username: user.username || '',
password: '', // Leave blank so it doesn't update unless typed
role: role,
department: user.department || metadata.department || '',
designation: user.designation || metadata.designation || '',
specialization: metadata.specialization || '',
license_number: metadata.license_number || '',
shift: metadata.shift || '',
employee_id: user.employee_id || user.employeeId || '',
org_id: user.org_id || user.organisationId || '',
assigned_floor: metadata.assigned_floor || '',
languages: metadata.languages ? metadata.languages.join(', ') : '',
});
setIsStaffModalOpen(true);
};
const handleCreateDepartment = async () => {
try {
const token = localStorage.getItem('teleems_token');
const payload = {
name: deptFormData.name,
headOfDepartment: deptFormData.headOfDepartment,
totalBedsCapacity: deptFormData.totalBedsCapacity,
contactPhone: deptFormData.contactPhone,
hospitalId: selectedHospital?.rawUser?.hospitalId || selectedHospital?.id,
isActive: true,
};
const res = await hospitalApi.createDepartment(payload, token || '');
if (res.status === 200 || res.status === 201) {
setSuccessMessage('Department verified and registered.');
setIsDeptModalOpen(false);
const updatedRes = await authApi.getDepartments(token || '');
if (updatedRes.data) {
const hospId = selectedHospital?.rawUser?.hospitalId || selectedHospital?.id;
setDepartments(updatedRes.data.filter((d: any) => d.hospitalId === hospId));
}
} else {
alert('Failed to register department. Code: ' + res.status);
}
} catch (e) {
alert('Exception: ' + e);
}
};
// ── Module renderer ──
const renderModule = () => {
switch (activeModule) {
case 'ED_MONITOR':
return (
<EDMonitor
incomingPatients={incomingPatients}
selectedHospital={selectedHospital}
departments={departments}
onRefresh={() => {
// Trigger reload in console
window.dispatchEvent(new CustomEvent('refresh_incoming'));
}}
onOpenBooking={() => setIsBookingModalOpen(true)}
onOpenTelelink={() => setActiveModule('TELELINK')}
/>
);
case 'BOOKINGS':
return (
<TripManagement
tripsData={tripsData}
onDeleteTrip={(id) => setTripsData(tripsData.filter((t) => t.id !== id))}
onOpenBooking={() => setIsBookingModalOpen(true)}
/>
);
case 'FLEET':
return <FleetView />;
case 'TELELINK':
return <TeleLinkHub />;
case 'EPCR':
return <EPCRRecords />;
case 'HISTORY':
return <PatientArchive />;
case 'REPORTS':
return <HospitalAnalytics />;
case 'SETUP':
return (
<SetupPanel
setupSubTab={setupSubTab}
onSubTabChange={setSetupSubTab}
selectedHospital={selectedHospital}
setSelectedHospital={setSelectedHospital}
departments={departments}
setDepartments={setDepartments}
allUsers={allUsers}
onSaveProfile={handleSaveProfile}
onOpenStaffModal={() => {
setIsEditingStaff(false);
setEditingStaffId(null);
setStaffFormData({
name: '', email: '', phone: '', username: '', password: '',
role: 'ED_DOCTOR', department: '', specialization: '',
license_number: '', designation: '', shift: 'General',
employee_id: '', org_id: '', assigned_floor: '', languages: ''
});
setIsStaffModalOpen(true);
}}
onEditStaff={handleEditStaff}
onOpenDeptModal={() => setIsDeptModalOpen(true)}
/>
);
case 'REFERRALS':
return <ReferralsSetup />;
default:
return <div>Module Under Development</div>;
}
};
// Hospital selection is now handled inline — no full-screen overlay
// ── Main console ──
// Module card definitions with descriptions and color accents
const MODULE_CARDS: { key: ConsoleModule; icon: any; label: string; description: string; accent: string; accentBg: string }[] = [
{ key: 'ED_MONITOR', icon: Monitor, label: 'ED Monitor', description: 'Live triage stream, incoming patients & emergency broadcasts', accent: 'hsl(0, 84%, 60%)', accentBg: 'hsla(0, 84%, 60%, 0.08)' },
{ key: 'BOOKINGS', icon: Activity, label: 'Trip Management', description: 'Ambulance bookings, dispatches & trip tracking', accent: 'hsl(199, 89%, 48%)', accentBg: 'hsla(199, 89%, 48%, 0.08)' },
{ key: 'FLEET', icon: Truck, label: 'Fleet Visibility', description: 'Real-time fleet map, vehicle status & route monitoring', accent: 'hsl(262, 83%, 58%)', accentBg: 'hsla(262, 83%, 58%, 0.08)' },
{ key: 'TELELINK', icon: Video, label: 'TeleLink Hub', description: 'Video consults, physician console & remote triage', accent: 'hsl(152, 69%, 40%)', accentBg: 'hsla(152, 69%, 40%, 0.08)' },
{ key: 'EPCR', icon: FileText, label: 'ePCR Records', description: 'Electronic patient care reports & documentation', accent: 'hsl(38, 92%, 50%)', accentBg: 'hsla(38, 92%, 50%, 0.08)' },
{ key: 'HISTORY', icon: Database, label: 'Patient Archive', description: 'Historical patient data, discharge records & search', accent: 'hsl(215, 60%, 50%)', accentBg: 'hsla(215, 60%, 50%, 0.08)' },
{ key: 'REPORTS', icon: TrendingUp, label: 'Analytics', description: 'Performance metrics, KPIs & operational insights', accent: 'hsl(280, 65%, 55%)', accentBg: 'hsla(280, 65%, 55%, 0.08)' },
{ key: 'REFERRALS', icon: Hospital, label: 'Referral Hub', description: 'Inter-hospital referrals, network & transfer protocols', accent: 'hsl(170, 60%, 42%)', accentBg: 'hsla(170, 60%, 42%, 0.08)' },
{ key: 'SETUP', icon: Settings, label: 'Account & Setup', description: 'Hospital profile, departments, staff & configuration', accent: 'hsl(220, 15%, 50%)', accentBg: 'hsla(220, 15%, 50%, 0.08)' },
];
return (
<div className="page-container" style={{ padding: '24px 32px', height: '100%', overflowY: 'auto' }}>
{successMessage && (
<div className="success-toast-premium" style={{ position: 'fixed', top: 30, left: '50%', transform: 'translateX(-50%)', zIndex: 2000 }}>
<CheckCircle2 size={18} /> {successMessage}
</div>
)}
<AnimatePresence mode="wait">
{isLoadingHospitals ? (
<motion.div
key="loading"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', height: '100%', gap: '16px' }}
>
<div className="spin" style={{ width: 32, height: 32, border: '3px solid var(--card-border)', borderTop: '3px solid var(--accent-cyan)', borderRadius: '50%' }} />
<span style={{ fontSize: '0.85rem', color: 'var(--text-muted)', fontWeight: 600 }}>Loading infrastructure...</span>
</motion.div>
) : (
<motion.div
key={`module-${activeModule}`}
initial={{ opacity: 0, y: 15 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -15 }}
transition={{ duration: 0.25, ease: 'easeOut' }}
className="ops-active-module"
>
<div className="ops-module-topbar">
<div className="ops-module-breadcrumb">
<span className="ops-breadcrumb-dot" style={{ background: MODULE_CARDS.find(c => c.key === activeModule)?.accent || 'var(--accent-cyan)' }} />
<span className="ops-breadcrumb-label">
{MODULE_CARDS.find(c => c.key === activeModule)?.label || 'Hospital Ops'}
{activeModule === 'ED_MONITOR' && incomingPatients.length > 0 && (
<span style={{
marginLeft: '8px',
background: 'var(--alert-red)',
color: '#fff',
padding: '1px 6px',
borderRadius: '10px',
fontSize: '0.65rem',
fontWeight: 700,
boxShadow: '0 2px 4px rgba(239, 68, 68, 0.2)'
}}>
{incomingPatients.length}
</span>
)}
</span>
</div>
{hospitals.length > 0 && (
<div className="ops-hospital-picker" style={{ marginLeft: 'auto' }}>
<div className="ops-picker-indicator">
<div style={{ width: 6, height: 6, borderRadius: '50%', background: 'var(--accent-green)', flexShrink: 0 }} />
<select
className="ops-hospital-select"
value={selectedHospital?.id || ''}
onChange={(e) => {
const h = hospitals.find((h: any) => h.id === e.target.value);
if (h) setSelectedHospital(h);
}}
>
{hospitals.map((h: any) => (
<option key={h.id} value={h.id}>{h.name}</option>
))}
</select>
<ChevronDown size={12} style={{ flexShrink: 0, color: 'var(--text-muted)' }} />
</div>
</div>
)}
</div>
{renderModule()}
</motion.div>
)}
</AnimatePresence>
<AnimatePresence>
<StaffModal
isOpen={isStaffModalOpen}
onClose={() => setIsStaffModalOpen(false)}
staffFormData={staffFormData}
setStaffFormData={setStaffFormData}
rolesList={rolesList}
showPassword={showPassword}
setShowPassword={setShowPassword}
onSubmit={handleRegisterStaff}
isEditing={isEditingStaff}
/>
<DeptModal
isOpen={isDeptModalOpen}
onClose={() => setIsDeptModalOpen(false)}
deptFormData={deptFormData}
setDeptFormData={setDeptFormData}
allUsers={allUsers}
selectedHospital={selectedHospital}
onSubmit={handleCreateDepartment}
/>
</AnimatePresence>
</div>
);
};
export default HospitalConsole;