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:
| Function | Purpose |
|---|---|
dashboard_start_session() | Starts the PHP session if one isn't active yet. Called internally by the functions below. |
dashboard_current_user(): ?array | Returns the logged-in user's session data (id, username, full_name, role), or null if nobody is logged in. |
dashboard_require_login(): array | Redirects to login and exits if nobody is logged in; otherwise returns the current user. Every dashboard page calls this first. |
dashboard_require_role(string $role): array | Calls dashboard_require_login(), then responds 403 and exits if the user's role doesn't match. Only dashboard/users.php uses this today (gated to 'admin') - the only page with role-based access control. |
dashboard_login(array $user): void | Called by login.php after a successful password_verify(). Regenerates the session id first, to prevent session fixation. |
dashboard_logout(): void | Clears and destroys the session. dashboard/logout.php calls this then redirects to login. |
Dashboard pages
All pages share a common header/nav/footer via dashboard/_layout.php, and a common list+modal CRUD pattern (POST action=create|update|delete handled at the top of the same file, ?edit=<id>/?new=1 opening an add/edit dialog, a DataTables-enhanced list). dashboard/pathogens.php is the clearest example of this pattern.
| Page | Purpose |
|---|---|
dashboard/index.php | Overview - record counts and data-library metadata. |
dashboard/reports.php | Browse/filter previously generated reports (status, source, date range, and a Sample filter - see below), download their PDFs. |
dashboard/pathogens.php | Manage the pathogens table (42-organism frozen panel). |
dashboard/antimicrobials.php | Manage the antimicrobials table (CLINICAL + FUNGAL shape fields on one row). |
dashboard/recommendations.php | Manage per-organism-source antimicrobial recommendation rankings (MASTER). |
dashboard/dosing_profiles.php | Manage the dosing_profiles table (DOSING_PROFILES). |
dashboard/users.php admin only | Manage dashboard accounts - see below. |
This product line vs. TruPath's Women's Health Plus product
This instance is cloned from tpl-womens-health-plus's PHP/MySQL architecture (dashboard, API, database-driven reference data) but its clinical engine and report design are ported from the approved TruDerm PCR standalone generator instead. Differences relevant to dashboard admins:
- No resistance-gene concept at all. Unlike every other product in this family, the TruDerm standalone generator has no gene detection UI and no gene-based drug blocking - there is no "Resistance Genes" dashboard page and no
genesfield on the API. - No MAST workflow and no numeric significance threshold. Detected organisms carry a qualitative
HIGH/MODERATE/LOWload, and every detected row participates in empiric/alternative scoring regardless of load. - Clinical logic is entirely regex/text-driven, not table-driven - there's no separate "renal status"/"hepatic status" column; kidney/liver icon color and pregnancy/pediatric/allergy cautions are computed live by matching regexes against each drug's Class/Clinical/Fungal Info text (
app/src/ClinicalEngine.php'sorgan()/cautions()/allergyExcludes()/pregnancyContra()). - Pathogens Assessed panel is frozen - it always shows the same fixed 5-column split regardless of what you add or remove on the Pathogens page (
app/src/ReportRenderer.php). - Alternative Antimicrobials can be submitted directly. The API's
case.alternatives[]field lets a caller specify up to 3 drugs directly; if omitted, the server ranks alternatives itself the same way the page's "Rank alternatives" button does. - Sample reports.
case.showSampleBanneris tracked: reports submitted with it checked are flaggedis_sampleand shown as a red "Sample" badge with a dedicated filter ondashboard/reports.php- the standalone generator itself always shows this banner unconditionally; the toggle is a PHP-port addition.
Users (add/remove dashboard accounts)
dashboard/users.php is an admin-role-only page for managing who can log into the dashboard:
- Add - username, full name, role (
adminorstaff), and a password (minimum 8 characters, hashed withpassword_hash($password, PASSWORD_DEFAULT)before storage - never stored or logged in plaintext). - Edit - same fields; leave the password field blank to keep the existing password unchanged.
- Delete - removes the account. You cannot delete the account you're currently logged in as (hidden on your own row, and rejected server-side if attempted directly).
admin (can access the Users page) vs. everything else (cannot). No per-page/per-action granularity beyond that single check.What the Admin Dashboard does NOT do
- It does not provide an audit trail of who changed what reference data or when.
- It does not send password-reset emails or support any self-service account-recovery flow - a locked-out account has to be fixed by another
adminuser via the Users page, or directly in the database. - It does not rate-limit or lock out repeated failed login attempts.
- It does not manage the report-generation API's
X-API-Key- that's a separate config value covered in the API docs, unrelated to anyusersrow.