1. Overview
CareTrack is a staff-only medical records management system for clinics. The public page explains the product and sends authorized staff to the login flow. The protected admin dashboard handles staff directory management, patient registration, diagnosis tracking, schedules, reports, notifications, profile images, and role-based access.
Current primary application:
the Next.js App Router pages in app/, especially the React admin app at
app/admin/[[...slug]]/AdminApp.jsx.
Compatibility layer:
static pages in admin/ and scripts in public/assets/js/ still exist and contain a Firestore-based data layer.
| Capability |
Purpose |
Main implementation |
| Public landing page |
Explain CareTrack and link staff to login. |
app/page.jsx, index.html |
| Staff authentication |
Sign in using Firebase Auth, validate staff registration, sync custom claims. |
app/auth/[[...slug]]/page.jsx, public/assets/js/caretrack-auth.js |
| Admin dashboard |
Single-page protected staff dashboard for operational workflows. |
app/admin/[[...slug]]/AdminApp.jsx |
| Staff account management |
Create/disable staff accounts and keep Firebase Auth custom claims synchronized. |
Firebase callable functions in functions/src/controllers/*.js |
| Clinical data storage |
Store patients, diagnoses, schedules, notifications, and staff registration data. |
Firebase Realtime Database, with Firestore rules/scripts retained for legacy static flows. |
| Image storage |
Store patient and doctor profile photos. |
Firebase Storage paths under clinics/{clinic}/... |
| Offline shell |
Cache application shell/assets and show a connectivity overlay. |
sw.js, public/assets/js/caretrack-runtime.js |
2. System Map
Browser
Public page, auth page, admin SPA, runtime script, service worker.
Next/Express
Serves pages, static assets, health endpoints, security headers.
Firebase Client SDK
Auth, RTDB reads/writes, callable functions, Storage uploads.
Firebase Backend
Auth users/claims, Realtime Database, Storage, Cloud Functions.
| Layer |
Technology |
Purpose |
Key files |
| Frontend application |
Next.js 16, React 19 |
Public marketing page, login screen, protected admin single-page experience. |
app/page.jsx, app/auth/[[...slug]]/page.jsx, app/admin/[[...slug]]/AdminApp.jsx |
| Custom web server |
Express 5 + Next request handler |
Runs the app on VPS/PM2, handles API CORS, security headers, static assets, and health endpoints. |
server.js, ecosystem.config.cjs |
| Backend functions |
Firebase Functions v2 callable functions |
Privileged staff lifecycle operations that require Admin SDK access. |
functions/src/routes/*.js, functions/src/controllers/*.js |
| Auth |
Firebase Authentication |
Email/password staff login, user records, and custom role claims. |
app/lib/firebase-client.js, functions/src/data/firebase.js |
| Primary data store |
Firebase Realtime Database |
Registration, staff profiles, patients, diagnoses, schedules, notifications, reports, and settings. |
rtdrules.json, functions/src/data/registration.js, AdminApp.jsx |
| Legacy/secondary data store |
Cloud Firestore |
Static admin script data layer and rules/index definitions for clinic-scoped collections. |
firestore.rules, firestore.indexes.json, public/assets/js/caretrack-app.js |
| File storage |
Firebase Storage |
Doctor, patient, and report files with clinic-aware paths. |
storage.rules, upload helper in AdminApp.jsx |
| Offline/runtime |
Service Worker + browser runtime script |
Precache key routes/assets, health checks, theme toggle, offline overlay. |
sw.js, public/assets/js/caretrack-runtime.js |
| Deployment |
Vercel, Firebase CLI, PM2, Nginx |
Frontend hosting, Firebase Functions/rules, and VPS server deployment. |
vercel.json, firebase.json, deploy.bat, scripts/*.sh |
3. Runtime Topologies
Local and VPS runtime
The app can run through the custom Express server with npm run dev or npm start. The server prepares a Next app, applies compression, adds security headers, serves static assets, exposes health/API endpoints, and passes all remaining requests to Next.
Browser -> Express server.js -> Next request handler -> app/ routes
Browser -> /api/* -> Express API middleware -> JSON response or 404
Browser -> Firebase SDK -> Auth / RTDB / Storage / callable Functions
Vercel runtime
vercel.json declares the project as a Next.js app using npm run build. Next route handlers serve /api/health, /robots.txt, /sitemap.xml, and /sw.js when deployed as a standard Next app.
Firebase Functions runtime
Cloud Functions are exported from functions/src/server.js, which sets maxInstances: 10 and exports callable functions from functions/src/app/next.js. These functions use the Firebase Admin SDK for Auth user and custom-claim operations.
The frontend currently calls Firebase callable functions directly through the Firebase Client SDK. The Express /api routes are health/test utilities, not the main clinical API surface.
4. Frontend Routes
Next.js routes
| Route |
File |
Purpose |
Access |
/ |
app/page.jsx |
Public product/landing page with staff login link, feature summary, and structured data. |
Public |
/auth, /auth/* |
app/auth/[[...slug]]/page.jsx |
Email/password staff login, remember-me persistence, password reset, registration check, claim sync. |
Public until login |
/admin, /admin/* |
app/admin/[[...slug]]/page.jsx -> AdminApp.jsx |
Protected dashboard and all staff workflows. Client-side slug router maps pages inside the React app. |
Authenticated active staff |
/api/health |
app/api/health/route.js |
Next health endpoint returning app/service/timestamp JSON. |
Public |
/robots.txt |
app/robots.txt/route.js |
Reads and returns root robots.txt with static caching. |
Public |
/sitemap.xml |
app/sitemap.xml/route.js |
Reads and returns root sitemap.xml. |
Public |
/sw.js |
app/sw.js/route.js |
Reads and returns root service worker with Service-Worker-Allowed: /. |
Public |
Admin SPA page keys
| Page key |
URL |
Purpose |
Allowed roles |
dashboard | /admin | Stats, staff/patient entry points, critical count. | admin, doctor, receptionist |
doctors | /admin/doctors | Staff directory: doctors and receptionists. | admin, doctor, receptionist |
doctor-form | /admin/doctor-form?id=... | Create/update doctor profile and account. | admin |
doctor-detail | /admin/doctor-detail?id=... | View doctor profile, schedule, assigned patients. | admin, doctor, receptionist |
patients | /admin/patients | Search, view, register, edit, and delete patient records. | admin, doctor, receptionist |
patient-form | /admin/patient-form?id=... | Register/update patient demographics, contact, assignment, photo. | admin, doctor, receptionist |
patient-profile | /admin/patient-profile?id=... | Patient detail and linked diagnosis notes. | admin, doctor, receptionist |
diagnoses | /admin/diagnoses | Diagnosis list and delete actions. | admin, doctor |
diagnosis-form | /admin/diagnosis-form?patientId=... | Add diagnosis, clinical findings, ICD code, severity, and status. | admin, doctor |
reports | /admin/reports | Report view derived from diagnosis records in the React app. | admin, doctor |
schedules | /admin/schedules | Doctor availability, rooms, and assigned patient summary. | admin, doctor, receptionist |
settings | /admin/settings | Clinic settings form. Current React implementation displays local save toast only. | admin |
access-denied | /admin/access-denied | Permission-denied screen. | Special public fallback |
Static compatibility pages
The admin/*.html and auth/index.html files load shared scripts from public/assets/js/. They are still precached by sw.js and tested by Playwright, but the Next React app is the newer route surface.
admin/admin.html
admin/doctors.html
admin/patients.html
admin/diagnoses.html
admin/reports.html
admin/schedules.html
admin/settings.html
admin/audit-logs.html
admin/*-form.html
admin/*-profile.html
5. Auth and Access
CareTrack uses Firebase Authentication for staff accounts. A Firebase Auth account alone is not enough: the login flow also requires a matching Realtime Database registration record and an active status. Custom claims then drive frontend navigation and security rules.
Login flow
- Staff submits email/password on
/auth.
- Client signs in using
signInWithEmailAndPassword.
- Client searches RTDB registration paths for the signed-in UID.
- If no registration exists, or the registration is disabled, the client signs out.
- Client writes
lastLoginAt and lastLoginEmail when allowed.
- Client calls
syncOwnAccessClaims to mirror registration into Firebase Auth custom claims.
- Client refreshes the ID token and redirects to the requested admin route.
Custom claim schema
| Claim |
Type |
Allowed values |
Purpose |
role | string | admin, doctor, receptionist | Primary authorization role used by UI, Firestore, RTDB, and Storage rules. |
staffType | string | admin, doctor, receptionist | Normalized staff category. Aliases like administrator/frontdesk/clinical are converted. |
clinicId | string | Usually default | Scopes clinic-aware reads/writes and Storage paths. |
status | string | active, disabled | Human-readable account state. |
active | boolean | true, false | Boolean account state required by security rules. |
Role purpose
| Role |
Primary purpose |
Typical abilities |
admin |
Clinic system owner/manager. |
Create/disable staff, edit doctors, delete records, access settings, view audit logs, manage all clinical records. |
doctor |
Clinical user who diagnoses assigned patients. |
View staff/patients/schedules, create diagnoses for assigned patients, view reports. |
receptionist |
Front-desk user handling patient registration and scheduling visibility. |
View staff/patients/schedules, create or update patient demographic/assignment data. |
Registration lookup paths
registration/admin/{uid}
registration/doctors/{uid}
registration/receptionist/{uid}
registration/clinicks/doctor/{uid}
registration/clinicks/reception/{uid}
access/users/{uid} # client-side legacy lookup only
6. API Catalogue
HTTP endpoints
| Endpoint |
Method |
Auth |
Purpose |
Response / behavior |
Source |
/api/health |
GET |
Public |
Health check for web runtime. |
Next returns { ok, app, service: "web", via: "next", timestamp }. Express returns { ok, app, service: "vps-api", uptime, timestamp }. |
app/api/health/route.js, server.js |
/_caretrack_health, /__caretrack_health |
GET |
Public |
Runtime/service-worker connectivity checks. |
Next rewrites to /api/health. Express responds with { ok: true, app: "caretrack", via: "express" }. |
next.config.js, server.js, sw.js |
/api/echo |
POST |
Public with API CORS |
Utility/test endpoint that echoes body and query. |
{ ok, receivedAt, body, query }. JSON and URL-encoded bodies limited to 1 MB. |
server.js |
/api/* |
OPTIONS |
Public with origin allowlist |
CORS preflight for Express API routes. |
Returns 204 with allowed methods GET,POST,OPTIONS and headers Authorization,Content-Type. |
server.js |
/api/* |
Other unmatched |
Public with API CORS |
Catch-all API miss. |
404 JSON: { ok: false, error: "API route not found.", path }. |
server.js |
/sw.js |
GET |
Public |
Service worker script. |
JavaScript with Cache-Control: no-cache and Service-Worker-Allowed: /. |
app/sw.js/route.js, server.js |
/robots.txt |
GET |
Public |
SEO robots policy. |
Disallows staff/admin/API/private routes and points to sitemap. |
robots.txt, app/robots.txt/route.js |
/sitemap.xml |
GET |
Public |
SEO sitemap. |
Contains only the public homepage. |
sitemap.xml, app/sitemap.xml/route.js |
Firebase callable functions
These are called with httpsCallable(functions, name). They are not REST routes. All privileged functions rely on Firebase Auth context and the Admin SDK.
| Function |
Required auth |
Input |
Purpose and side effects |
Return |
syncOwnAccessClaims |
Any signed-in staff with active registration |
None |
Finds the caller's RTDB registration, validates active status, mirrors role/staffType/clinicId/active into Firebase Auth custom claims, updates last login, saves staffProfiles/{uid}. |
{ uid, role, staffType, clinicId, active, registrationPath } |
syncAccessClaims |
Admin |
{ uid } |
Admin-triggered claim repair for another staff user based on registration data. |
{ uid, role, staffType, clinicId, active } |
createStaffUser |
Admin |
{ email, displayName, temporaryPassword, staffType|role, active, clinicId, department } |
Creates or updates Firebase Auth user, removes old registrations, writes normalized registration, mirrors claims, writes staffProfiles/{uid}. Temporary password required for new users and must be at least 8 chars. |
{ uid, email, role, staffType, active, registrationPath } |
setStaffRole |
Admin |
{ uid, email?, displayName?, staffType|role, active?, clinicId?, department? } |
Changes role/staff type and active state, updates Auth display name/disabled flag, rewrites registration path, mirrors claims, updates staff profile. |
{ uid, email, role, staffType, active, registrationPath } |
disableStaffUser |
Admin |
{ uid, clinicId? } |
Disables a staff account in Firebase Auth/custom claims, marks registration disabled, updates staff profile. Admins cannot disable their own account. |
{ uid, active: false } |
listStaffUsers |
Admin |
Optional { clinicId } from client. Controller currently lists all registrations and client filters by clinic. |
Reads known registration groups and returns deduplicated staff records sorted by display name/email/uid. |
{ users: [...] } |
API CORS allowlist
Express API CORS allows credentials for origins from API_ALLOWED_ORIGINS or the defaults:
https://caretrack.website, https://www.caretrack.website,
https://project-xbo8a.vercel.app, http://localhost:3000, and http://localhost:4173.
7. Data Schemas
Current React admin screens read and write Realtime Database paths. The static admin script still reads and writes Firestore collections under clinics/{clinic}. Both schema families are documented because both exist in the codebase.
7.1 Firebase Auth user schema
| Field |
Type |
Purpose |
Writer |
uid | string | Canonical staff identity and record key. | Firebase Auth |
email | string | Login email and staff contact email. | createStaffUser |
displayName | string | Name shown in the app and copied into registration/profile records. | createStaffUser, setStaffRole |
disabled | boolean | Firebase Auth account enable/disable flag. Inverse of active. | updateAuthUser |
emailVerified | boolean | Created as false for new staff accounts. | createAuthUser |
customClaims | object | Role, staff type, clinic, active state. | mirrorClaims |
7.2 Realtime Database schemas
The React admin app uses root-level RTDB collections plus a clinicId field for filtering. Doctor records are stored under registration paths, while patient/diagnosis/schedule/notification/report/settings records are root collections.
Registration: registration/admin/{uid}, registration/doctors/{uid}, registration/receptionist/{uid}
Also reads legacy paths registration/clinicks/doctor/{uid} and registration/clinicks/reception/{uid}.
| Field | Type | Required | Purpose |
uid | string | Yes | Firebase Auth UID and RTDB key. |
email | string | Admin path yes; other paths usually yes | Staff email/login identifier. |
displayName | string | No | Name shown in UI. |
active | boolean | Yes | Controls access and claim generation. |
status | string | No | active or disabled. |
role | string | Yes | Authorization role. |
staffType | string | Yes | Normalized staff type. |
clinicId | string | Yes | Clinic scope. Defaults to default. |
department, departmentId | string | No | Staff department or front desk group. |
createdBy, updatedBy | string | No | Actor UID for lifecycle tracking. |
createdAt, updatedAt | RTDB timestamp | No | Server-side lifecycle timestamps. |
lastLoginAt, lastLoginEmail | timestamp/string | No | Updated during login/claim sync. |
disabledAt | timestamp | No | Set when staff is disabled. |
Doctor profile: registration/doctors/{uid}
The current React admin app stores doctor profile fields on the same RTDB registration record as access fields. It also reads legacy doctor records from registration/clinicks/doctor/{uid}.
| Field | Type | Purpose |
uid, id | string | Doctor UID/document key. |
fullName, displayName | string | Doctor name. |
email, phone | string | Contact and Auth identity. |
gender | string | Male, Female, Other; used for default photo choice. |
specialty, department | string | Clinical specialty and department. |
room, officeRoom | string | Clinic room display. |
status | string | Available, Busy, or Off Duty. |
availability | string | Human-readable days such as Mon-Fri. |
startTime, endTime | HH:mm string | Working hours. |
assignedPatients | number | Display count when stored; actual assignment can be derived from patients. |
photoUrl, photoPath | string | Firebase Storage profile image URL/path. |
role, staffType, active, clinicId | mixed | Access fields inherited from registration schema. |
createdAt, createdBy, updatedAt, updatedBy | mixed | Lifecycle metadata from saveRecord. |
Staff profile mirror: staffProfiles/{uid}
| Field | Type | Purpose |
uid, email, displayName | string | Staff identity mirror. |
role, staffType, active, status | mixed | Current access state. |
department, departmentId | string | Staff department. |
clinicId | string | Clinic scope. |
registrationPath | string | RTDB registration source path. |
lastLoginAt | timestamp | Most recent claim-sync login time. |
updatedAt, updatedBy | mixed | Lifecycle metadata. |
Patients: patients/{patientRecordId}
| Field | Type | Purpose |
id, uid, patientId | string | Internal record key and public patient ID such as PT-123456. |
firstName, lastName, fullName, name | string | Patient name. Current forms write first/last name. |
dateOfBirth, age, gender | mixed | Demographic data. |
phone, email, address | string | Contact information. |
emergencyContactName, emergencyContactPhone | string | Allowed by Firestore rules/static model; not shown in the current React patient form. |
assignedDoctorId, assignedDoctorName | string | Doctor assignment; drives diagnosis permissions and notifications. |
department | string | Copied from assigned doctor. |
status | string | Stable, Monitoring, or Critical. |
lastDiagnosis, lastUpdated | string | Summary fields updated after diagnosis save. |
registrationNotes | string | Optional notes allowed by rules/static model. |
photoUrl, photoPath | string | Patient profile image. |
registeredBy, registeredByName | string | Staff member that created or owns registration. |
clinicId | string | Clinic scope used by client-side filtering. |
createdAt, createdBy, updatedAt, updatedBy | mixed | Lifecycle metadata. |
Diagnoses: diagnoses/{diagnosisId}
| Field | Type | Purpose |
id | string | RTDB key. |
patientId | string | Internal patient record ID. |
patientPublicId | string | Human-readable patient ID. |
patientName | string | Patient display name copied at diagnosis time. |
assignedDoctorId, assignedDoctorName | string | Diagnosing or assigned doctor. |
department, specialty | string | Clinical department/specialty context. |
description | string | Disease or diagnosis text. |
icdCode | string | Optional ICD code. |
severity | string | Low, Medium, High, Critical. |
diagnosisDate | date string | Clinical diagnosis date. |
status | string | Open, Monitoring, Resolved. |
patientStatus | string | Patient-level status to summarize back onto patient record. |
clinicalFindings | array<string> | Checked clinical symptoms/findings. |
findingsText | string | Comma-separated finding summary. |
clinicalNotes, notes, criticalNotes | string | Diagnosis notes. Render helper checks all three names. |
followUpRequired, finding_* | boolean | Legacy static Firestore form fields. |
clinicId, createdAt, createdBy, updatedAt, updatedBy | mixed | Scope and lifecycle metadata. |
Schedules: schedules/{scheduleId}
| Field | Type | Purpose |
id | string | Schedule key. |
doctorId, assignedDoctorId | string | Doctor reference. |
doctorName | string | Doctor display name. |
department, specialty | string | Clinical context. |
day | string | Weekday or availability text. |
startTime, endTime | HH:mm string | Availability window. |
room | string | Room/location. |
status | string | Available, Busy, etc. |
clinicId, createdAt, createdBy, updatedAt, updatedBy | mixed | Scope and lifecycle metadata. |
Notifications: notifications/{notificationId}
| Field | Type | Purpose |
id | string | Notification key, usually {patientRecordId}_{doctorId}_assignment. |
type | string | Currently patient-assignment. |
title, message | string | Notification display text. |
recipientUid, recipientName | string | Doctor/staff recipient. |
patientRecordId, patientId, patientName | string | Patient target data. |
department | string | Department context. |
read, readAt | boolean/timestamp | Read state. |
clinicId | string | Clinic scope. |
createdAt, createdBy, createdByName | mixed | Creation metadata. |
Reports: reports/{reportId}
The current React reports page renders diagnosis records as reports. The static script can create report metadata records.
| Field | Type | Purpose |
id | string | Report key. |
reportName | string | Report title, such as Patient Diagnosis Report. |
subject | string | Patient/clinic/department subject. |
createdBy, createdAt, createdAtText | mixed | Creator and time. |
status | string | Report state such as Ready. |
clinicId, updatedAt, updatedBy | mixed | Scope and lifecycle metadata. |
Settings: settings/{settingId}
| Field | Type | Purpose |
clinicName | string | Clinic display name. |
clinicEmail | string | Clinic contact email in static settings model. |
emergencyPhone | string | Emergency contact number in static settings model. |
department, departments | string | Default department or comma-separated department list. |
specialties | string | Comma-separated specialty list. |
severityLabels | string | Diagnosis severity labels. |
sessionTimeout | number/string | Static script session timeout setting display. |
clinicId, updatedAt, updatedBy | mixed | Scope and lifecycle metadata if persisted. |
Access mirror: access/users/{uid}
Client code checks this legacy path during registration lookup. RTDB rules allow reads by the user or admin and deny writes. It is not written by the current functions.
7.3 Firestore schemas
Firestore rules define a clinic-scoped document tree under clinics/{clinic}. The static admin script uses this shape via helpers like collection(db, "clinics", state.clinicId, name).
| Collection path |
Main fields |
Purpose |
Access summary |
clinics/{clinic}/staffProfiles/{uid} |
uid, email, displayName, role, staffType, active, status, department |
Clinic-scoped staff profile mirror. |
Clinic staff can read; admins can create/update/delete. |
clinics/{clinic}/doctors/{doctorId} |
fullName, email, phone, specialty, department, room, status, availability, photoUrl |
Firestore doctor directory for static admin pages. |
Clinic staff can read; admins can write. |
clinics/{clinic}/patients/{patientId} |
firstName, lastName, dateOfBirth, age, gender, patientId, phone, email, address, assignedDoctorId, assignedDoctorName, status |
Patient demographic and assignment records. |
Clinic staff can read; admin/doctor/receptionist can create; doctors/receptionists can update only allowed demographic/assignment/status fields; admins can delete. |
clinics/{clinic}/diagnoses/{diagnosisId} |
patientId, patientName, assignedDoctorId, assignedDoctorName, icdCode, description, severity, diagnosisDate, clinicalNotes |
Clinical diagnosis records linked to patients. |
Admins/doctors read and create/update; admins delete. |
clinics/{clinic}/schedules/{scheduleId} |
doctorId, doctorName, department, day, startTime, endTime, room, status |
Doctor schedule records. |
Clinic staff read; admins write. |
clinics/{clinic}/reports/{reportId} |
reportName, subject, createdBy, status, createdAt |
Report metadata. |
Admins/doctors read/create; admins update/delete. |
clinics/{clinic}/settings/{settingsId} |
clinicName, clinicEmail, departments, specialties, severityLabels, sessionTimeout |
Clinic settings for static admin pages. |
Clinic staff read; admins write. |
clinics/{clinic}/auditLogs/{auditId} |
action, entity, entityId, details, userId, userName, role, device, timestamp |
Immutable audit events from static Firestore flows. |
Admins read; clinic staff can create only for their own UID/role; no updates/deletes. |
7.4 Firestore composite indexes
| Collection group | Fields | Purpose |
doctors | department ASC, status ASC, updatedAt DESC | Filter staff by department/status with newest updates first. |
patients | assignedDoctorId ASC, status ASC, updatedAt DESC | Doctor-specific patient lists. |
patients | department ASC, status ASC, updatedAt DESC | Department patient queues. |
diagnoses | patientId ASC, severity ASC, diagnosisDate DESC | Patient diagnosis history by severity/date. |
diagnoses | assignedDoctorId ASC, severity ASC, diagnosisDate DESC | Doctor diagnosis worklist. |
schedules | doctorId ASC, day ASC, startTime ASC | Ordered doctor schedules. |
reports | status ASC, createdAt DESC | Report listing by status/newest. |
auditLogs | action ASC, entity ASC, timestamp DESC | Admin audit filtering. |
7.5 Firebase Storage schemas
| Path | Purpose | Write rules | Read rules |
clinics/{clinic}/patients/{patientId}/profile.{ext} |
Patient profile photo uploaded from patient form. |
Admin, doctor, or receptionist in same clinic. |
Any active staff in same clinic. |
clinics/{clinic}/doctors/{doctorId}/profile.{ext} |
Doctor profile photo uploaded from doctor form. |
Admin in same clinic. |
Any active staff in same clinic. |
clinics/{clinic}/reports/{allPaths} |
Report files, when used. |
Admin or doctor in same clinic. |
Admin or doctor in same clinic. |
8. Data Workflows
Staff creation
- Admin opens staff or doctor form.
- Client calls
createStaffUser with email, display name, temporary password, staff type, department, active flag, and clinic ID.
- Function creates/updates Firebase Auth user and removes older registration paths for that UID.
- Function writes the normalized registration under the correct path and mirrors custom claims.
- For doctors, the frontend then writes profile fields into
registration/doctors/{uid}.
Patient registration
- Staff chooses or generates a patient record key and public patient ID.
- Optional profile image uploads to
clinics/{clinicId}/patients/{patientId}/profile.{ext}.
- Frontend writes
patients/{patientRecordId} with assignment, demographics, image URL/path, clinic ID, and lifecycle metadata.
- If assigned doctor changed, frontend writes a patient assignment notification under
notifications/{patientId}_{doctorId}_assignment.
Diagnosis creation
- Admin or assigned doctor opens diagnosis form.
- Client validates that the doctor can diagnose the selected patient.
- Client writes a diagnosis record under
diagnoses/{diagnosisId}.
- Client updates the patient summary fields
lastDiagnosis, status, updatedAt, and updatedBy.
Notification handling
Doctors see notifications targeted to their UID. The app also derives assignment notifications from patient records if no explicit notification exists. Marking a notification as read updates or creates read: true and readAt.
Reports
The React reports page filters and renders diagnosis records as report rows. Static pages can create Firestore report metadata and render a printable patient diagnosis report.
9. Security Model
Frontend access checks
The admin app checks Firebase Auth state, registration status, custom claims, and per-page role lists. Unauthorized users are redirected to /auth or shown the access-denied page.
Firestore rules summary
| Resource | Read | Create | Update | Delete |
| Clinic root | Clinic staff | Admin | Admin | Admin |
| Staff profiles | Clinic staff | Admin | Admin | Admin |
| Doctors | Clinic staff | Admin | Admin | Admin |
| Patients | Clinic staff | Admin, doctor, receptionist | Admin or limited doctor/receptionist fields | Admin |
| Diagnoses | Admin, doctor | Admin, doctor | Admin, doctor | Admin |
| Schedules | Clinic staff | Admin | Admin | Admin |
| Reports | Admin, doctor | Admin, doctor | Admin | Admin |
| Settings | Clinic staff | Admin | Admin | Admin |
| Audit logs | Admin | Clinic staff for their own UID/role | Denied | Denied |
Realtime Database rules summary
| Path | Read | Write | Validation |
registration/admin/{uid} | Self or admin | Admin active, or self preserving core identity/access fields | Requires uid, email, active, role, staffType, clinicId |
registration/doctors/{uid} | Self or admin; group read admin only | Admin active, or self preserving core fields | Requires uid, active, role, staffType, clinicId |
registration/receptionist/{uid} | Self or admin; group read active staff | Admin active, or self preserving core fields | Requires uid, active, role, staffType, clinicId |
registration/clinicks/doctor/{uid}, registration/clinicks/reception/{uid} | Self or admin; group read active staff | Admin active, or self preserving core fields | No explicit required child list in current rules. |
patients/{patientId} | Active staff | Active admin, doctor, or receptionist | No per-field validation. |
diagnoses/{diagnosisId} | Active admin or doctor | Active admin or doctor | No per-field validation. |
schedules/{scheduleId} | Active staff | Active admin | No per-field validation. |
notifications/{notificationId} | Active staff | Active admin, doctor, or receptionist | No per-field validation. |
reports/{reportId} | Active admin or doctor | Active admin or doctor | No per-field validation. |
settings/{settingId} | Active staff | Active admin | No per-field validation. |
staffProfiles/{uid} | Self or admin | Self only | No per-field validation. |
access/users/{uid} | Self or admin | Denied | Read-only legacy mirror. |
Storage rules summary
Storage requires active staff claims and same-clinic matching. Patient files are writable by admins, doctors, and receptionists. Doctor files are writable only by admins. Report files are readable/writable by admins and doctors.
Server security headers
Both server.js and next.config.js define security headers including X-Content-Type-Options: nosniff, X-Frame-Options: DENY, Referrer-Policy: strict-origin-when-cross-origin, Permissions-Policy, and a Content Security Policy allowing Firebase, Google APIs, app assets, and Storage image sources.
10. Offline Runtime
CareTrack includes a service worker and runtime script to improve perceived reliability and provide visible connection status.
| Component | Purpose | Details |
sw.js |
Application shell cache and fetch strategy. |
Uses cache version v2.5, precaches public/auth/admin routes and assets, stale-while-revalidates assets, network-first navigations, special handling for health checks. |
caretrack-runtime.js |
Browser runtime helper. |
Registers service worker, checks connectivity through /__caretrack_health, shows offline overlay, manages light/dark theme toggle, emits runtime events. |
/__caretrack_health |
Connectivity probe. |
Service worker can intercept and return JSON with X-CareTrack-SW: 1; runtime falls back to direct homepage fetch. |
The service worker ignores non-GET requests and lets Firebase hosts pass through to the network. It only caches same-origin assets and app shell pages.
11. Build and Deployment
NPM scripts
| Script | Command | Purpose |
dev | node server.js --dev | Run custom Express/Next server in development mode. |
build | next build | Build Next app. |
start | node server.js | Run custom server, using production build when present. |
test:e2e | playwright test | Run Playwright end-to-end tests. |
deploy:functions | firebase deploy --only functions | Deploy callable Cloud Functions. |
deploy:rules | firebase deploy --only database,storage | Deploy RTDB and Storage rules. |
deploy:hosting | firebase deploy --only hosting | Deploy Firebase Hosting target. |
deploy:vercel:prod | vercel --prod | Deploy Next frontend to Vercel. |
Deployment targets
| Target | Files | Purpose |
| Vercel | vercel.json | Build and host the Next.js frontend. |
| Firebase | firebase.json, .firebaserc | Functions, Realtime Database rules, Storage rules, and optional hosting from repo root. |
| VPS/PM2 | server.js, ecosystem.config.cjs, scripts/setup-vps.sh, scripts/deploy-vps.sh | Run the custom Express/Next server on port 4173 behind Nginx. |
| Windows deployment launcher | deploy.bat | Build validation, then opens separate deployment windows for Vercel, Firebase Functions, rules, GitHub push, and VPS deploy. |
Firebase configuration
projectId: caretrack-badc5
databaseURL: https://caretrack-badc5-default-rtdb.firebaseio.com
storageBucket: caretrack-badc5.firebasestorage.app
defaultClinicId: default
12. Testing
Playwright tests in tests/e2e/caretrack.spec.js validate core shell/runtime behavior:
- Static assets and
sw.js are served.
- Public page shows the real logo and registers the service worker.
- Health checks are driven through the service worker.
- Connectivity overlay appears/disappears when the browser context goes offline/online.
- Auth page loads without the old loader path.
- Protected static admin pages redirect unauthenticated staff to login.
- Access denied page renders without requiring a session.
Run with npm run test:e2e after starting the app according to playwright.config.js.
13. Architecture Notes
RTDB and Firestore both exist.
The current React admin app is RTDB-first, while the static admin script is Firestore-first. Keep this in mind before changing schemas or rules.
Firestore rules/indexes are present but not wired in firebase.json.
The configured deploy:rules script deploys Realtime Database and Storage rules only.
Functions audit writer is currently a no-op.
functions/src/data/db.js defines writeAudit, but the body intentionally discards its arguments. Static Firestore flows still write audit documents.
RTDB clinical collections are broad at the rules level.
Root patients, schedules, and notifications are readable by active staff. The React app filters by clinicId in client code.
Settings persistence differs by app surface.
The current React settings page shows a local success toast only. The static Firestore settings form can save a settings/system document.
Claim freshness matters.
Role changes and enable/disable operations update custom claims, but clients may need token refresh or sign-out/sign-in to pick up claim changes.
14. Source File Map
| Area | Files |
| Next routes/layouts | app/layout.jsx, app/page.jsx, app/auth/[[...slug]]/page.jsx, app/admin/[[...slug]]/AdminApp.jsx, app/api/health/route.js |
| Firebase client SDK | app/lib/firebase-client.js, public/assets/js/firebase-config.js |
| Static compatibility UI | admin/*.html, auth/index.html, public/assets/js/caretrack-app.js, public/assets/js/caretrack-auth.js |
| Runtime/offline | sw.js, app/sw.js/route.js, public/assets/js/caretrack-runtime.js |
| Custom server | server.js, next.config.js, ecosystem.config.cjs |
| Firebase Functions | functions/src/server.js, functions/src/app.js, functions/src/app/next.js, functions/src/routes/*.js, functions/src/controllers/*.js, functions/src/data/*.js, functions/src/middleware/auth.js |
| Rules and indexes | rtdrules.json, firestore.rules, firestore.indexes.json, storage.rules |
| Deployment | firebase.json, .firebaserc, vercel.json, deploy.bat, scripts/setup-vps.sh, scripts/deploy-vps.sh |
| Tests | tests/e2e/caretrack.spec.js, playwright.config.js |