Logging in
dashboard/login.php checks the submitted username/password against the users table with PHP's password_verify() against a bcrypt hash stored in password_hash. There is no password-reset-by-email flow - resetting a password means editing the row directly, via the Users page below or SQL.
A default account is seeded by database/schema.sql:
username: admin
password: TruPath@2026
dashboard/login.php and this guide - treat it as a known value, not a secret, until it's rotated.Session mechanics
All session handling lives in includes/dashboard_session.php:
dashboard_start_session()- starts the PHP session if one isn't active yet.dashboard_current_user(): ?array- the logged-in user's session data (id,username,full_name,role), ornull.dashboard_require_login(): array- redirects tologinand exits if nobody is logged in. Every dashboard page calls this first.dashboard_require_role(string $role): array- as above, then403s if the role doesn't match. Onlydashboard/users.phpuses this ('admin').dashboard_login(array $user): void- called after a successfulpassword_verify(); regenerates the session id first.dashboard_logout(): void- clears and destroys the session.
Dashboard pages
All pages share a common header/nav/footer (dashboard/_layout.php) and a common list+modal CRUD pattern (POST action=create|update|delete, ?edit=<id>/?new=1 opening an add/edit <dialog>, a DataTables-enhanced table.data-table). dashboard/pathogens.php is the clearest example if you're adding a new reference-data page.
| Page | Purpose |
|---|---|
dashboard/index.php | Overview - record counts and data-library metadata. |
dashboard/reports.php | Browse/filter generated reports, download PDFs. Shows a real Pages column (genuine multi-page pagination) instead of status/type filters - nothing to filter on those, see below. |
dashboard/pathogens.php | Manage targets - the 34-organism respiratory panel (17 viruses, 14 bacteria, 3 fungal). |
dashboard/antimicrobials.php | Manage clinical_precautions - THE real hydration source for Alternative Antimicrobials (31 drugs). Includes the exclusion flags, clearly marked as stored-but-unenforced (see below). |
dashboard/recommendations.php | Manage per-target recommendation rankings (target_recommendations, 136 rows). |
dashboard/icd10.php | Manage icd_options (50 codes). Reference/visibility only - see below, no server-side effect in this product. |
dashboard/users.php | Manage dashboard accounts - admin role required. |
There is no "MAST Plates" page and no "Resistance Genes" page - this product has neither. MAST_PENDING/MAST_FINAL are 100% dead code in the approved source (render() hardcodes PCR_ONLY; no data-mode control exists at all, confirmed by grep). Dropped rather than built for a workflow that can never run.
ICD-10 Codes: reference/visibility only, NOT a validation gate
Unlike tpl-fungal (where ICD-10 tiering genuinely drives a live codingWarning API field), this product's ICD-10 codes are confirmed fully inert to clinical logic. The source's own diagnosisWarning banner is purely client-side - it never blocks report generation, and diagnosis codes never appear anywhere in the rendered PCR report (grepped the engine's render() pipeline - zero references).
The icd10.php page exists so the requisition form's 50-code checklist stays administrable alongside the rest of the reference data - but editing a code's tier here has zero effect on api/generate-report.php's behavior or response. Deliberate, not an oversight.
Antimicrobials: the exclusion flags are stored but unenforced
Three drugs (Nirmatrelvir-Ritonavir, Ribavirin, Trimethoprim-Sulfamethoxazole) have renal_exclusion_when_flagged/hepatic_exclusion_when_flagged set. The approved source defines a matching function, organExcluded(caseData, alternative), that would hard-exclude such a drug when the disease flag is active - but it is never called anywhere (grepped the engine module - it appears only in its own definition). This port reproduces it verbatim for traceability and likewise never calls it.
How this product's clinical engine actually differs
Cloned from tpl-wound's architecture (closest sibling by far - same 1102x1427 canvas, same palette, ~80% shared function names), but the clinical algorithms and real pagination are this product's own:
- Single mode, but REAL multi-page pagination (unlike Wound's dead continuation-page code, and STI's no pagination at all): first 3 detected pathogens on page 1, additional pathogens chunked 10-per-page onto continuation pages.
CLINICAL_PRECAUTIONSreplaces the dual masterClinicalInfo+medicationClinicalInfo overlay - one flat 31-drug table.- Three-behavior pregnancy adjustment (Wound has two): influenza substitution, doxycycline trimester-gating, AND an unconditional levofloxacin/ciprofloxacin caution note with no trimester gating.
- A different 3-state coverage marker: yellow circle (incomplete class coverage, takes priority) / yellow triangle (caution, drawn INSTEAD of a circle) / green circle (complete coverage) - exactly one symbol per row.
- 3 pathogen-class groups (Viruses/Bacteria/Fungal) - no Protozoa/Other, unlike Wound/STI's 4.
- A fixed 4-column assessed-panel layout driven live by
TARGETS.filter(panelTarget), not a hardcoded name list. organExcluded()is dead code - see above.- ICD-10 is confirmed inert - see above.
Updating the mapping data
The approved standalone generator has no client-side "upload updated spreadsheet" feature - its data blocks are embedded JS consts. Update here via:
- Editing rows directly through the Pathogens / Antimicrobials / Recommendations / ICD-10 Codes pages, or
- Replacing the product's own
data/*.jsonaudit-export files, re-runningtools/build_reference_json.php, thentools/import_data_library.php.
Users (add/remove dashboard accounts)
dashboard/users.php is an admin-only page:
- Add - username, full name, role (
adminorstaff), password (min 8 characters, hashed withpassword_hash()). - Edit - same fields; blank password keeps the existing one.
- Delete - removes the account. You cannot delete your own currently-logged-in account.
What the Admin Dashboard does NOT do
- No audit trail of who changed what reference data or when.
- No password-reset emails or self-service account recovery.
- No rate-limiting or lockout on repeated failed logins.
- Does not manage the report-generation API's
X-API-Key- see API docs.