Production-grade full-stack ER triage and management system aligned to PRD.md and Design.md.
This monorepo contains:
frontend: Next.js App Router dashboardbackend: Express + TypeScript APIshared: shared enums and type contracts
Implemented core workflows:
- JWT auth with doctor, nurse, and viewer roles
- patient intake preview with duplicate detection
- LLM symptom interpretation review before save
- quick entry for immediate emergency intake
- rule-based triage with explainability
- doctor override with scope and auto-clear rules
- queue sorting and viewer-safe queue scoping
- demo patient generation
- simulation engine for demo patients
- bed allocation and configuration
- immutable audit logging
- nurse management
- reset flow with typed confirmation and forced logout
- Frontend: Next.js, TypeScript, Tailwind CSS, Zustand, Recharts, Lucide
- Backend: Node.js, Express, TypeScript, MongoDB, Mongoose, JWT, bcrypt
- AI: Groq via OpenAI-compatible SDK
- Node.js 20+
- npm 10+
- MongoDB
npm installCreate a root .env from .env.example.
Required backend variables:
PORTMONGO_URIJWT_SECRET- one of
XAI_API_KEYorGROQ_API_KEY
Common runtime variables:
JWT_EXPIRES_INXAI_BASE_URLXAI_MODELGROQ_BASE_URLGROQ_MODELAI_REQUEST_TIMEOUT_SECONDSAI_MAX_TOKENSDEFAULT_GENERAL_BEDSDEFAULT_CRITICAL_BEDSSIMULATION_ENABLEDSIMULATION_TICK_SECONDSSEED_ENABLEDNEXT_PUBLIC_API_BASE_URL
npm run dev:backend
npm run dev:frontendUse the root launcher to start backend and frontend together from any checkout location:
npm run devThis script is repo-relative and does not depend on any user-specific parent folder path. It works as long as the repository structure itself is preserved.
For Windows users who want a one-click launcher from the repository root, use either:
.\run-system.ps1or:
run-system.cmdBoth launchers resolve the repository root dynamically from their own file location, so they remain portable when the project is cloned into a different parent folder. They are now first-time setup friendly:
- auto-create
.envfrom.env.examplewhen missing - auto-run
npm installwhennode_modulesis not present - keep the terminal open with actionable error hints if startup fails (instead of closing immediately)
For a production-style combined start after building:
npm run build
npm run start- Doctor:
doctor@er.com/Doctor@123 - Nurse:
nurse@er.com/Nurse@123
No default viewer is seeded automatically.
npm run seed --workspace backendThe seed is idempotent and only creates missing default doctor and nurse accounts.
POST /api/auth/login: returns JWT and{ id, email, role }GET /api/auth/me: validates the active session- manual logout clears the persisted session immediately and returns the user to
/login - auth session hydration now self-recovers by clearing corrupted saved auth storage if an old invalid browser session blob is encountered
- sessions expire after 8 hours of inactivity
- any
401on the frontend clears auth state and redirects to/login - reset invalidates active sessions and shows
System has been reset by administrator - dashboard alerts now live inside the topbar notification center with unread-dot tracking
- enter demographic, symptom, vitals, injury, and history data
- backend checks for duplicate active patients by name + age + gender
- if free-text exists, LLM preview returns normalized symptoms, risk flags, confidence, and summary
- clinician confirms or edits normalized symptoms
- backend creates patient, runs triage, assigns bed, stores vitals history, and appends audit events
- duplicate check is performed before save
- the UI shows
Possible duplicate patient detected - clinician must explicitly continue to persist the patient
- minimal emergency intake for fast admission
- hard critical flags are checked immediately
- hard critical quick-entry cases are inserted into the queue as
Critical - quick-entry patients are marked
awaitingFullDatauntil a later clinical update completes the record
Doctor and nurse workflows include:
- clinical updates
- vitals updates
- note updates
- status changes
- referred flow with required destination and reason
- manual review clearance
- optimistic locking via
version
Doctor-only workflow:
- delete an individual patient record from the details panel without using full system reset
If the submitted version is stale, the backend returns:
Record was updated by another source. Please refresh and retry.
The rule engine remains the final priority authority.
Decision order:
- hard critical flags
- doctor override
- AI escalation
- weighted score
- time escalation
- priority lock on downgrade
Weights:
- vitals: 40
- symptoms: 30
- history: 10
- AI confidence: 20
Fallback weights when AI is unavailable:
- vitals: 50
- symptoms: 37.5
- history: 12.5
Explainability output includes:
top_factorcontributors
Manual review rule:
- waiting time adds
+2every5minutes - cap is
+20 - capped normal patients below urgent threshold are flagged
Awaiting Manual Review
The PRD contains an internal conflict around AI escalation to Critical. The implementation keeps the safer behavior:
- AI can escalate to at least
Urgent - AI does not directly make the final decision on
Critical - hard critical rules and doctor override remain authoritative
The severe bleeding rule also follows the safer interpretation by escalating from direct clinical input rather than waiting for an AI-derived bleeding flag.
The LLM service lives in llm.service.ts.
Behavior:
- provider auto-detection for xAI or Groq via OpenAI-compatible client
- Groq is selected automatically when
GROQ_API_KEYis set, or whenXAI_API_KEYstarts withgsk_ - JSON-only responses
- schema validation of risk flags, confidence, summary, and normalized symptoms
- statuses:
success,fallback,failed - fallback summary:
AI analysis unavailable. Score based on rule engine only. - doctor/nurse-only patient AI guide with five fixed guided prompts:
- predicted problem
- care priorities
- do's and don'ts
- monitoring focus
- handoff summary
- patient guide responses are generated from the current patient JSON context, not from free-text chat input
- general beds
- critical beds
- doctor-only bed count changes
- bed configuration sliders reflect the currently saved totals and stay aligned with persisted system settings after restart
- bed sliders use higher-contrast tracks and colored thumbs so the selected range is visible against the glass background
- safe reduction guard when occupied beds exceed requested target
- critical patients prefer critical beds first
- overflow may use a general bed with
priorityMismatch - higher-acuity patients can reclaim beds from lower-priority patients when capacity is exhausted
- displaced patients are marked as
ReassignedorTransferringso bed pressure is visible on the dashboard - completed, discharged, and referred patients release bed assignments
- runs only for demo patients
- uses
simulationdata-source tags - manual patients are never auto-modified
- simulation tick re-runs triage and appends vitals history
- critical demo patients now fluctuate within dangerous but varied ranges so the vitals trends stay clinically unstable instead of collapsing into identical flat lines
Audit logs are append-only and available to doctors.
Tracked events include:
- patient created
- patient deleted
- vitals updated
- priority changed
- doctor override applied / cleared
- bed assignment changes
- patient status changes
- LLM symptom correction
- system reset
- nurse created / removed
Reset no longer deletes audit logs.
Role-aware dashboard sections:
- Dashboard
- Patients
- Beds
- Analytics
- Audit Logs
- Settings
Implemented dashboard features:
- compact intake launcher: a single Add / Admit Patient button expands the full intake tools only when needed
- refreshed glass-surface visual treatment with frosted panels, softer shadows, and modernized controls while preserving the dashboard layout
- bed tiles, hover previews, summary boxes, and detail sections now use stronger glassmorphism surfaces instead of flat opaque white panels
- notification center now renders above the dashboard stack, bed tiles use clearer critical/general color separation, and flat vitals traces show as stable snapshots rather than unreadable straight-line charts
- system summary sits near the top of the dashboard for immediate visibility
- system summary now refreshes immediately after patient create, demo generation, update, override, status change, and delete actions
- clinical updates now auto-retry once on optimistic-lock conflicts by refreshing the latest patient version first, which keeps remarks, vitals, and note updates usable even while demo/simulated records are changing in the background
- center queue: compact grid with switchable sorting by priority, waiting time, score, bed assignment, or name
- right panel: AI summary, risk flags, vitals graph, notes, override tools, audit preview, beds, and activity feed
- patient-specific AI guide popup for doctor and nurse roles with fixed one-click clinical guidance prompts
- the patient AI guide opens through a top-level centered portal overlay instead of rendering inside the dashboard column layout
- alerts strip
- system summary cards
- activity feed
- bed status panel
- doctor-only analytics
- doctor-only audit logs
- doctor-only settings for nurse management and reset
Viewer restrictions:
- viewer only sees patient ID, priority, status, and wait time in the queue
- viewer cannot access patient details, analytics, audit logs, notes, AI summaries, risk flags, or bed management
Queue card behavior:
- compact cards stay fixed in size and act as queue selectors
- cards show only the most important status badges and top risk chips
- full patient detail stays in the right-side detail panel instead of expanding inside the grid
- bed labels use abbreviated tiles such as
CRTandGENto reduce dashboard clutter - assigned beds are highlighted directly on queue cards and inside the bed grid with patient ID and name
- bed hover opens a floating patient-info card instead of resizing neighboring tiles
Analytics now includes:
- summary metric cards
- top critical patient score chart with patient IDs on the axis
- wait-time versus score comparison by patient ID
- live priority mix chart
Bed/status behavior:
- a patient who receives a bed assignment is automatically moved from
WaitingtoIn Treatment - dashboard alerts are shown in the notification dropdown instead of stacking inline across the page
POST /api/system/reset requires doctor auth and typed confirmation:
{
"confirmationText": "RESET",
"reseedDemoCount": 0
}Reset behavior:
- deletes patient data
- clears queue/simulation state by removing active patients
- resets bed config to defaults
- preserves default doctor and default nurse
- invalidates active sessions
- keeps immutable audit history
- reset UI clearly separates typed
RESETconfirmation from optional demo reseed count
POST /api/auth/loginGET /api/auth/me
GET /api/patientsGET /api/patients/:patientIdPOST /api/patients/:patientId/guidancePOST /api/patients/intake/previewPOST /api/patientsPOST /api/patients/quick-entryPOST /api/patients/demoPATCH /api/patients/:patientIdPATCH /api/patients/:patientId/statusPATCH /api/patients/:patientId/overrideDELETE /api/patients/:patientIdDELETE /api/patients/:patientId/overrideGET /api/patients/analytics
GET /api/system/overviewPUT /api/system/bedsPOST /api/system/reset
GET /api/audit-logsGET /api/users/nursesPOST /api/users/nursesDELETE /api/users/nurses/:nurseId
Verified locally with:
npx tsc -p backend/tsconfig.json --noEmit
npm run build --workspace backend
npm run build --workspace frontend
npm run dev
npm run test:auth-smoke --workspace backend
npm run test:demo-smoke --workspace backend
npm run test:triage-smoke --workspace backend
npm run test:workflow-smoke --workspace backendThe backend was also started successfully against the local .env and logged:
MongoDB connected successfullyBackend listening on http://localhost:5000