# Web Implementation Reference (Bootstrap 5 / Tabler + PHP) Healthcare web patterns on the `webapp-gui-design` stack: Tabler, Bootstrap 5, PHP, SweetAlert2, DataTables. --- ## 1. Page Structure Template Clone `seeder-page.php` as base. Extend with healthcare regions. ```php
``` ### Required Includes | Include | Purpose | Healthcare Extensions | |---------|---------|----------------------| | `header.php` | Top bar, CSS, meta | Facility selector, ward/shift dropdown, notification bell | | `sidebar.php` | Left nav | Healthcare module groups, badge counters | | `patient-bar.php` | Patient context strip | Persistent bar below header when patient selected | | `footer.php` | Scripts, closing | Session timeout timer, audit logger, clinical JS | ### Patient Context Bar Persistent strip showing active patient. Visible on all clinical pages. ```html ``` --- ## 2. Healthcare Navigation Sidebar ### Module Map | Module | Icon | Sub-items | Badge | |--------|------|-----------|-------| | **Dashboard** | `icon-home` | (none) | -- | | **Patients** | `icon-users` | Lookup, Register, My Patients | -- | | **Clinical** | `icon-stethoscope` | Vitals, Medications, Orders, Notes | -- | | **Schedule** | `icon-calendar` | Appointments, Calendar, Queue | Queue (waiting) | | **Communication** | `icon-message` | Messages, Campaigns, Health Bot | Messages (unread) | | **Labs & Imaging** | `icon-flask` | Orders, Results, Pending | -- | | **Billing** | `icon-credit-card` | Invoices, Payments, Insurance | -- | | **Reports** | `icon-chart-bar` | Clinical, Financial, Operational | -- | | **Admin** | `icon-settings` | Users, Roles, Facility, Audit Logs | -- | ### Sidebar Pattern (one module shown -- repeat for each) ```html ``` **Rules:** Active module gets `active` class + `border-start: 3px solid var(--clinical-primary)`. Badge counts update via polling/WebSocket. Collapse state persisted in `sessionStorage`. Icons from Tabler SVG sprite. --- ## 3. Healthcare Component Patterns ### Patient Card ```html

John Doe Allergy: Penicillin

M, 45y | MRN: 1234567 | Blood: O+ | Dr. Smith
Moderate Risk
Last Visit: 2026-02-20
``` ### Vital Signs Grid Use `row-cols-2 row-cols-md-3 row-cols-lg-6` for responsive 6-up layout. One tile shown; repeat for SpO2, Temp, BP, RR, Pain. Background class from `getVitalStatus()` (Section 4). ```html
Heart Rate
72
bpm - Normal
``` ### Clinical Alert Banner ```html ``` ### Medication Checklist ```html
Oral
Amoxicillin 500mg
3x daily - Next due: 14:00
Last: 08:00
IV
Normal Saline 0.9% 1000mL
Continuous - Rate: 125mL/hr
Running
``` --- ## 4. JavaScript Patterns ### Debounced Patient Search ```javascript function debounce(fn, delay) { let timer; return (...args) => { clearTimeout(timer); timer = setTimeout(() => fn(...args), delay); }; } const patientSearch = debounce(async (query) => { if (query.length < 2) return; const res = await fetch(`/api/patients/search?q=${encodeURIComponent(query)}`); if (!res.ok) return; renderPatientList((await res.json()).data); }, 300); document.getElementById('patient-search').addEventListener('input', e => patientSearch(e.target.value.trim())); ``` ### Vital Sign Threshold Validation ```javascript const VITAL_THRESHOLDS = { heart_rate: { crit_lo: 40, warn_lo: 50, norm_lo: 60, norm_hi: 100, warn_hi: 110, crit_hi: 130 }, spo2: { crit_lo: 0, warn_lo: 0, norm_lo: 95, norm_hi: 100, warn_hi: 94, crit_hi: 89 }, temperature: { crit_lo: 34, warn_lo: 35, norm_lo: 36.1, norm_hi: 37.2, warn_hi: 38.5, crit_hi: 40 }, resp_rate: { crit_lo: 6, warn_lo: 8, norm_lo: 12, norm_hi: 20, warn_hi: 24, crit_hi: 30 }, systolic_bp: { crit_lo: 70, warn_lo: 80, norm_lo: 90, norm_hi: 140, warn_hi: 160, crit_hi: 180 }, pain_scale: { crit_lo: 0, warn_lo: 0, norm_lo: 0, norm_hi: 3, warn_hi: 6, crit_hi: 8 } }; function getVitalStatus(type, value) { const t = VITAL_THRESHOLDS[type]; if (!t) return { status: 'unknown', cssClass: 'text-muted', bgClass: '' }; if (value <= t.crit_lo || value >= t.crit_hi) return { status: 'Critical', cssClass: 'text-danger', bgClass: 'bg-danger-lt' }; if (value <= t.warn_lo || value >= t.warn_hi) return { status: 'Warning', cssClass: 'text-warning', bgClass: 'bg-warning-lt' }; if (value >= t.norm_lo && value <= t.norm_hi) return { status: 'Normal', cssClass: 'text-success', bgClass: 'bg-success-lt' }; return { status: 'Warning', cssClass: 'text-warning', bgClass: 'bg-warning-lt' }; } ``` ### Medication Administration Confirmation (SweetAlert2) ```javascript function confirmMedicationAdmin(medId, patientId, medName, patientName, mrn) { Swal.fire({ title: 'Confirm Medication Administration', html: `

Administering ${medName} to ${patientName} (MRN: ${mrn})

5 Rights Check: Verify Patient, Medication, Dose, Time, Route
`, icon: 'warning', showCancelButton: true, confirmButtonText: 'Confirm Administration', confirmButtonColor: '#059669', reverseButtons: true }).then((result) => { if (result.isConfirmed) { fetch('/api/medications/administer', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRF-Token': document.querySelector('meta[name="csrf-token"]').content }, body: JSON.stringify({ medication_id: medId, patient_id: patientId }) }).then(r => r.json()).then(data => { data.success ? Swal.fire('Recorded', 'Administration logged.', 'success') : Swal.fire('Error', data.message, 'error'); }); } }); } ``` ### Session Timeout with Warning ```javascript const SESSION_TIMEOUT = 15 * 60 * 1000; const WARNING_AT = 13 * 60 * 1000; let timeoutTimer, warningTimer; function resetSessionTimers() { clearTimeout(timeoutTimer); clearTimeout(warningTimer); warningTimer = setTimeout(() => { Swal.fire({ title: 'Session Expiring', text: 'Your session expires in 2 minutes due to inactivity.', icon: 'warning', showCancelButton: true, confirmButtonText: 'Stay Logged In', cancelButtonText: 'Log Out' }).then(r => r.isConfirmed ? (fetch('/api/session/refresh', { method: 'POST' }), resetSessionTimers()) : (window.location.href = '/logout')); }, WARNING_AT); timeoutTimer = setTimeout(() => { window.location.href = '/logout?reason=timeout'; }, SESSION_TIMEOUT); } ['click','keypress','scroll','mousemove'].forEach(e => document.addEventListener(e, debounce(resetSessionTimers, 1000))); resetSessionTimers(); ``` --- ## 5. DataTables for Patient Lists ```javascript $('#patient-table').DataTable({ processing: true, serverSide: true, ajax: { url: '/api/patients/list', type: 'POST', headers: { 'X-CSRF-Token': csrfToken } }, columns: [ { data: 'name', render: (d, t, r) => `
${d}
${r.gender}, ${r.age}y
` }, { data: 'mrn', className: 'font-monospace' }, { data: 'blood_type' }, { data: 'risk_level', render: d => { const c = { high:'danger', moderate:'warning', low:'success' }; return `${d}`; }}, { data: 'allergies', render: d => d?.length ? d.map(a => `${a}`).join(' ') : 'None' }, { data: 'next_appointment', render: d => d ? dayjs(d).format('MMM D, HH:mm') : '-' }, { data: 'id', orderable: false, render: id => ` View Vitals` } ], order: [[3, 'desc']], language: { search: 'Search patients:', emptyTable: 'No patients found' }, responsive: true, pageLength: 25 }); ``` --- ## 6. Print and PDF Patterns Follow the `report-print-pdf` skill. Key healthcare templates use mPDF. ```php $mpdf = new \Mpdf\Mpdf(['mode' => 'utf-8', 'format' => 'A4']); $mpdf->SetTitle("Patient Summary - {$patient['name']}"); $mpdf->SetAuthor($current_user['name']); $html = view('reports/partials/clinical-letterhead', [ 'facility' => $facility, 'report_title' => 'Patient Summary', 'patient' => $patient ]); $html .= view('reports/patient-summary-body', [ 'patient' => $patient, 'diagnoses' => $diagnoses, 'medications' => $active_meds, 'allergies' => $allergies, 'vitals' => $latest_vitals, 'care_team' => $care_team ]); $mpdf->WriteHTML($html); $mpdf->Output("patient-summary-{$patient['mrn']}.pdf", 'D'); ``` ### Document Templates | Document | Key Sections | Special Rules | |----------|-------------|---------------| | **Patient Summary** | Demographics, Diagnoses, Medications, Allergies, Vitals, Care Team | Allergy warnings in red | | **Discharge Summary** | Admission reason, Course, Discharge Dx, Follow-up, Medications | Attending signature required | | **Lab Report** | Order info, Results table with reference ranges, Flag column | Abnormal values bold + red | | **Prescription** | Provider, Patient, Rx details, Instructions, Signature line | Legal formatting required | --- ## 7. HIPAA Web Security Checklist Reference `vibe-security-skill` for base web security; extend with these HIPAA controls. ### HTTP Headers ```php header("Content-Security-Policy: default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'"); header("X-Content-Type-Options: nosniff"); header("X-Frame-Options: DENY"); header("X-XSS-Protection: 1; mode=block"); header("Strict-Transport-Security: max-age=31536000; includeSubDomains"); header("Cache-Control: no-store, no-cache, must-revalidate, max-age=0"); header("Pragma: no-cache"); header("Referrer-Policy: strict-origin-when-cross-origin"); ``` ### Session Security ```php ini_set('session.cookie_httponly', 1); ini_set('session.cookie_secure', 1); ini_set('session.cookie_samesite', 'Strict'); ini_set('session.gc_maxlifetime', 900); // 15 min ini_set('session.use_strict_mode', 1); ini_set('session.use_only_cookies', 1); ``` ### Compliance Checklist | Requirement | Implementation | |-------------|----------------| | CSRF protection | Token in meta tag, validated on all POST/PUT/DELETE | | Auto-logout | 15-min timeout with 2-min warning modal (see Section 4) | | PHI autocomplete | `autocomplete="off"` on all PHI input fields | | No PHI caching | `Cache-Control: no-store` on patient data pages | | Clickjacking | `X-Frame-Options: DENY` | | Audit logging | Log every API call: user ID, timestamp, IP, endpoint, patient ID | | PHI in URLs | Never include patient names or full MRNs in URLs | | Browser storage | Never store PHI in localStorage or sessionStorage | | Copy audit | Audit log on copy events for PHI elements | | Print audit | Log all print/export with document type and patient ID | ### Audit Logger ```javascript function logAccess(action, resourceType, resourceId, details = {}) { navigator.sendBeacon('/api/audit/log', JSON.stringify({ action, // 'view', 'edit', 'print', 'export', 'search' resource_type: resourceType, resource_id: resourceId, details, timestamp: new Date().toISOString(), page_url: window.location.pathname })); } // Auto-log on patient context pages if (document.getElementById('patient-context')) { const pid = document.getElementById('patient-context').dataset.patientId; logAccess('view', 'patient', pid, { section: document.title }); } ```