Commit graph

46 commits

Author SHA1 Message Date
Daniel
2f8bf16ace Hide Active Sessions for SSO-only users
Follows the same pattern as Change Password and 2FA sections —
hidden by default in the HTML, revealed only when canLocalAuth=true.

Why: revoke technically deletes the PedScribe session row and clears
the cookie on that device, but the SSO user can re-auth instantly
because their IdP session is still live. Surfacing a "revoke" button
that the IdP will immediately undo is misleading. SSO users now see
only the SSO-relevant sections of Settings.
2026-04-14 05:07:07 +02:00
Daniel
c45d2b83d2 Hide change-password + 2FA by default, show only when canLocalAuth=true
Sections were briefly visible for SSO-only users before load2FAStatus
resolved and hid them. Flipped the default: both sections now carry
style="display:none" in the HTML and are revealed only when the /me
fetch confirms the user has a real password hash.

SSO-only users never see the sections, even for a flash.
2026-04-14 04:38:28 +02:00
Daniel
dc0f191c7a Idle timeout observability + cut write frequency in half + hide local-auth UI for SSO-only users
Middleware:
  - Log to console.warn + audit_log when a session is killed for
    inactivity. Shows up in Grafana/Loki so you can see how often
    users actually get kicked. Audit action: 'session_idle_timeout'
  - last_activity throttle bumped 5 min → 10 min — halves DB writes
    per active user. Idle precision slop widens to 24h00-24h10;
    still invisible in practice.

Per-user local-auth visibility:
  - /api/auth/me now returns user.canLocalAuth: true when the stored
    password is a real bcrypt / argon2 hash, false for the random
    blob OIDC auto-creates for SSO-only users.
  - Settings page hides "Change Password" and "Two-Factor
    Authentication" sections when canLocalAuth is false — those UIs
    are meaningless for users whose sign-in lives at the IdP.
  - Password hash is not leaked in the /me payload.

Mobile (restating existing behaviour for clarity): no idle check,
365-day JWT in Keychain/Keystore, never auto-logs-out. Only logout
triggers are: manual logout, password change, admin revoke, JWT hit
365d, or app uninstall.
2026-04-14 04:32:53 +02:00
Daniel
242e78e6f3 Sliding 24h idle timeout (web) + persistent mobile + 2FA backup codes
Session model:
  Web     — 24h sliding idle timeout enforced server-side via
             user_sessions.last_activity. 30-day JWT + cookie are a
             safety net; middleware is the real clock. Cookie is
             re-set on active use so browsers match the sliding window.
  Mobile  — 365-day JWT, no idle timeout (stays persistent via Keychain
             / Keystore). Detected via User-Agent ("PedScribe" /
             "Capacitor") or X-Client: mobile header.

2FA backup codes:
  - 10 single-use codes generated when 2FA is first enabled
  - Stored as bcrypt hashes in new users.totp_backup_codes column
  - Consumed atomically on successful login fallback (when TOTP fails)
  - Regenerate endpoint (POST /api/auth/2fa/backup-codes) requires
    current password; invalidates prior codes
  - Count endpoint (GET /api/auth/2fa/backup-codes/count) powers a
    "N codes remaining" indicator on the 2FA settings card
  - Modal shows codes exactly once with Copy + Download .txt actions
  - Codes cleared when 2FA is disabled

New files:
  src/utils/platform.js — isMobileClient() helper

Schema migration (idempotent):
  ALTER TABLE users ADD COLUMN IF NOT EXISTS totp_backup_codes TEXT
2026-04-14 04:24:54 +02:00
Daniel
887a8c3c74 Growth chart age: three boxes (yr/mo/day), any combination
Replace single text input with three number fields — years, months,
days — that all combine into fractional months. Fill any subset:
leave years blank for a newborn, leave months blank for "2 years",
enter just days for a 10-day-old.

Live hint below ("= 2 yr 5 mo (29 mo total)") still shows the
interpreted total. Parser from prior commit retained on window
for reuse elsewhere.
2026-04-14 03:21:27 +02:00
Daniel
1b51bd619e Growth chart: flexible age input with smart parser
Replace [years] + [months dropdown] with a single text field that
accepts:
  3y / 3 years / 3 yr
  29m / 29 months / 29 mo
  2y5m / 2 years 5 months / 2 yr 5 mo
  3.5 years / 36 (plain number = months)
  15 days / 2 weeks / 3y 2m 10d

Enables fractional ages so newborns can be plotted accurately
(WHO/CDC growth curves are continuous — "0 months" means at birth,
not a 0-27 day bucket, so a 15-day-old should plot at ~0.5 months).

Live hint below the field shows how the input was interpreted
("= 2 yr 5 mo (29 mo total)").
2026-04-14 03:17:16 +02:00
Daniel
ad2fb32ac8 Update bilirubin to exact AAP 2022 values, add exchange transfusion
Phototherapy thresholds updated with exact values extracted from
PediTools (validated against AAP 2022 nomograms):
- Separate tables for GA 35, 36, and 38+ weeks
- With and without neurotoxicity risk factors
- Hour-specific values at 12, 24, 36, 48, 60, 72, 84, 96, 120h

Previous approximations were 1-3 mg/dL too low (conservative but
inaccurate). New values match the published AAP 2022 curves exactly.

Exchange transfusion thresholds added for GA 38+ weeks (with/without
risk factors). Displayed alongside phototherapy threshold in results.

GA selection expanded: 35, 36, 37, 38, 39, 40+ weeks.
Chart now shows both phototherapy and exchange transfusion lines.

Also: fixed Loki port conflict (3100->3101), added logs.pedshub.com.
2026-04-11 05:50:19 +02:00
Daniel
6689b723e4 Add pause/stop buttons to SOAP note recording
- Add Pause and Stop buttons (hidden until recording starts)
- Record button hides during recording (same pattern as encounter)
- Pause: suspends MediaRecorder + speech recognition, shows Resume
- Resume: handles MediaRecorder state recovery if browser killed it
- Stop: triggers the record button's stop flow
- Native mobile: haptic feedback + keep-awake + foreground service
- Recognition respects pause state (doesn't restart during pause)
2026-04-11 05:19:12 +02:00
Daniel
129691f8dd Add Glasgow Coma Scale calculator and equipment sizing reference
Some checks failed
Build TWA APK / build-apk (push) Failing after 16s
Build & Push Docker Image / build (push) Failing after 1m12s
GCS Calculator:
- Child/Adult and Infant versions with toggle
- Eye opening (4), Verbal (5), Motor (6) dropdowns
- Auto-calculates total score with severity classification
  (Mild 13-15, Moderate 9-12, Severe/Coma 3-8)
- Infant-modified verbal and motor scales per Kids Kard
- Updates on every dropdown change (no button needed)

Equipment Sizing (Johns Hopkins Kids Kard):
- Select age/weight group (premie through 16+)
- Shows: BVM, oral/nasal airway, blade, ETT, LMA, Glidescope,
  IV catheter, central line, NGT/OGT, chest tube, Foley
- All values from Johns Hopkins Children's Center Kids Kard
- ETT formulas shown as reference
2026-04-11 05:01:57 +02:00
Daniel
8b5de42476 Fix vital signs selector, add resuscitation medications calculator
Vital Signs:
- Fix age selector not responding (replaced setTimeout with event
  delegation on parent panel — works reliably with hidden panels)
- Update values to Johns Hopkins Kids Kard data (8 age groups:
  premie, 0-3mo, 3-6mo, 6-12mo, 1-3yr, 3-6yr, 6-12yr, >12yr)
- Each age group shows: HR awake/sleeping, RR, SBP, DBP, temp,
  SpO2, weight range, and clinical pearls

Resuscitation Medications (new calculator tab):
- Enter patient weight, calculates all 13 PALS medication doses
- Adenosine, Amiodarone, Atropine, Calcium Chloride/Gluconate,
  Dextrose (weight-based concentration), Epinephrine (arrest/anaphylaxis),
  Hydrocortisone, Insulin, Lidocaine, Magnesium, Naloxone, Bicarb
- Color-coded by category (cardiac/metabolic/reversal)
- Max dose capping, route, special notes per medication
- Source: Johns Hopkins Kids Kard / AHA PALS 2020
2026-04-11 04:42:23 +02:00
Daniel
df36c549b7 Interactive vital signs selector with clinical notes per age group
Replace static vital signs table with interactive age group dropdown.
Each selection shows: HR (awake/sleeping), RR, SBP, DBP, temperature,
SpO2 target, weight range, and age-specific clinical notes.

10 age groups: preterm through 18 years. Values from Harriet Lane
Handbook 23rd Edition. Includes AAP 2017 BP classification thresholds
for ages 13+, ETT sizing formulas, and clinical pearls (orthostatic
testing, febrile tachycardia, athletic bradycardia, etc.).

Full reference table preserved as collapsible "View All Age Groups".
2026-04-11 04:19:39 +02:00
Daniel
059fbefa7e Add automatic ICD-10 and CPT billing code suggestions
New feature: after generating any clinical note, the app automatically
suggests relevant billing codes displayed as clickable chips below the output.

Backend (src/routes/billing.js):
- POST /api/suggest-codes endpoint analyzes note text
- Extracts diagnoses from Assessment section via regex
- Looks up ICD-10 codes: local common pediatric map (40+ conditions)
  first, then NLM Clinical Tables API for unknown terms
- Suggests CPT E/M codes based on note type, visit complexity,
  ROS/PE system counts, and MDM level estimation
- Supports: outpatient (new/established), well visit (age-based),
  ED, inpatient (admit/subsequent/discharge)

Frontend (public/js/app.js):
- suggestBillingCodes() renders collapsible card with ICD-10 and CPT chips
- Click any chip to copy the code to clipboard
- Shows E/M level assessment (diagnosis count, ROS, PE, MDM complexity)
- Disclaimer: "Suggestions only. Always verify codes."

Integration: called after note generation in all 6 tabs
(encounter, SOAP, sick visit, well visit, hospital course, chart review)
2026-04-11 01:50:17 +02:00
Daniel
5a394219db Add pediatric calculators: BP, BMI, growth, bilirubin, vitals, BSA, dosing
Calculators tab with 7 tools:
- BP Percentile (AAP 2017) with age/sex/height classification
- BMI Percentile (CDC 2000) with extended obesity classification
  (Class 1/2/3 using % of 95th percentile per CDC 2022)
- Growth Charts: weight-for-age, length-for-age, head circumference,
  weight-for-length (WHO/CDC LMS), Fenton preterm (22-50 weeks)
- Bilirubin: AAP 2022 phototherapy threshold + Bhutani nomogram
  with Nelson Table 137.1 risk factors for severe hyperbilirubinemia
- Vital Signs by Age (Harriet Lane) with quick reference formulas
  (estimated weight, min SBP, ETT size, maintenance fluids 4-2-1)
- Body Surface Area (Mosteller formula)
- Weight-Based Dosing with max cap and volume calculation

Fix growth chart sub-tab navigation (pills scoped separately from
top-level nav to prevent panel disappearing)
2026-04-09 17:56:30 +02:00
Daniel
5ec1ddec6b Replace all browser dialogs with modern modal, add OIDC admin UI
- Add reusable showConfirm() modal component (supports plain confirm,
  input prompt, danger styling, Enter key)
- Replace ALL 18 confirm() and prompt() calls across 8 JS files with
  showConfirm() modal: admin user actions, session revoke, document
  delete, template delete, milestone management, transcription settings
- Fix broken admin reset-password (btn was undefined in scope)
- Add OIDC/SSO configuration UI to Admin Panel (issuer, client ID/secret,
  button label, disable local auth toggle, callback URL display)
2026-04-09 02:43:23 +02:00
Daniel
30638b968a Remove prompt() dialogs, breach warnings, cost display; fix 2FA disable UI
- Replace browser prompt() with inline UI for: 2FA disable (password field),
  admin password reset (inline input), admin test email (inline input)
- Remove all password breach warning UI (login, register, settings)
  Backend HIBP check endpoint remains but is no longer called from frontend
- Remove model cost display from dropdown and header badge
- Hide empty cost-badge element in header
- Fix model dropdown to flat list (no category grouping)
2026-04-09 02:27:55 +02:00
Daniel
0e4e4f5f17 FAQ page, dep security patches, model dropdown and UI fixes
- Add FAQ tab with accordion sections: Getting Started, AI & Models,
  Voice & Transcription, Saving & Export, Privacy & Security,
  Well Visit & Sick Visit, Learning Hub, Troubleshooting
- Documents how AI learns from physician edits (correction tracker)
- Fix FAQ accordion (CSP was blocking inline script, moved to app.js)
- Patch all 5 npm vulnerabilities: nodemailer 8.0.5, xmldom, basic-ftp,
  path-to-regexp (npm audit now reports 0 vulnerabilities)
- Remove model category grouping from dropdown (flat list, no optgroups)
- Fix model dropdown dark background on options (white bg, dark text)
- Update FAQ model guidance to reflect admin-managed model selection
2026-04-09 01:56:11 +02:00
Daniel
d12d4f4e63 v6.2: Session management, password change, audit logging, refine context, UI fixes
Security:
- Add session management: users can view/revoke active sessions in Settings
- Add password change in Settings (requires current password, HIBP check)
- Force logout all sessions on password reset
- Fix logout to destroy server-side session (was only clearing cookie)
- Add trust proxy for correct client IP in rate limiting and audit logs
- Add CORS support for multiple domains (CORS_ORIGINS env var)
- Add HIBP breach check endpoint and inline warnings on password fields

Audit logging:
- Add audit logging to all 24 PHI-handling endpoints across 13 route files
- Covers: generation, transcription, TTS, refine, encounters, documents, Nextcloud
- All fire-and-forget (no response delay)

AI improvements:
- Refine now includes original source material (transcript, notes, labs)
  so AI can reference the full input when modifying output
- Add correction tracking (trackAIOutput) to sick visit and well visit tabs
- Fix sickvisit missing from encounter save noteIdMap

UI fixes:
- Non-blocking busy bar for transcription and AI generation (replaces full-screen overlay)
- Fix encounter recording: hide record button during recording (was showing two stop buttons)
- Fix ROS/PE "All WNL" stacking duplicate event handlers; add Clear buttons
- Enlarge AI instructions textarea in Learning Hub CMS

Domain:
- Primary domain now app.pedshub.com, with scribe.pedshub.com and peds.danvics.com as CORS origins
2026-04-08 20:27:45 +02:00
Daniel
b97c5f146b v6.1: Turnstile bot protection, LiteLLM provider, PPTX tables, audio backup fixes, docs
- Add Cloudflare Turnstile to login, register, and password reset forms
- Switch AI provider to LiteLLM, transcription to OpenAI Whisper
- Change domain to scribe.pedshub.com
- Fix PPTX export: add tables, bold/italic, numbered lists, code blocks, blockquotes
- Fix announcement banner close button (CSP was blocking inline onclick)
- Fix auth middleware: empty Bearer token now falls through to cookie auth
- Fix audio backups: only save on transcription failure, stop auto-deleting on success
- Soften AI correction injection to prevent model hallucination from correction history
- Fix LiteLLM TTS model name handling (no incorrect openai/ prefix)
- Expand AI instructions textarea in Learning Hub CMS
- Update README for v6 with all features and providers
- Add comprehensive docs/: architecture, API reference, database schema,
  authentication, AI providers, speech, learning hub, configuration, deployment
2026-04-04 22:56:24 +02:00
ifedan-ed
9c1d86d4b6 feat: Add model search, testing, and TTS/STT/embedding management to admin
- Fix model search for all providers: Bedrock now falls back to built-in
  list (with live ListFoundationModels attempt), Azure returns built-in list
- Add Test button on every model row (built-in, discovered, custom) that
  sends a live prompt and shows response + latency in a toast
- Add TTS management section: search voices from provider API (Google TTS
  voices.list, LiteLLM /v1/models, ElevenLabs /v1/voices), Set as Default
  writes tts.voice/tts.model to DB, runtime respects DB override
- Add STT management section: search models from provider (Gemini, Whisper,
  LiteLLM, OpenAI, local), Set as Default writes stt.model to DB, runtime
  respects DB override in transcribe.js
- Add Embedding models section: search from provider (LiteLLM, Vertex,
  OpenAI), Set as Default writes embeddings.model+dimensions to DB,
  embeddings.js respects DB override
- Add record-and-transcribe STT test (browser MediaRecorder)
- Add TTS synthesize-and-play test (returns base64 audio)
- Add embedding generate test (shows dims + vector sample)
- Expand PUT /config/:key(*) whitelist to include tts., stt., embeddings.
- Add @aws-sdk/client-bedrock as optional dependency for live Bedrock discovery
2026-04-03 19:55:11 +00:00
ifedan-ed
78bb30e42f v2.2: Remove milestone admin UI, add CMS content refresh button
REMOVED:
- Milestone editing UI from Admin Panel (per user request)
- Milestones will be managed via hardcoded static data only
- Kept backend routes and database support for future use

ADDED:
- Refresh button in Learning Hub CMS content list
- Manual refresh for AI-generated content updates
- Better discoverability of content refresh functionality

FIXES:
- AI learning content now has visible refresh button
- Users can manually refresh content list after AI generation
- Cleaner admin panel without milestone management clutter

NOTE:
- Developmental milestones still work via static fallback
- Edit milestones by modifying public/js/milestonesData.js
- Backend API still supports milestone management if needed later
2026-04-01 18:16:00 +00:00
ifedan-ed
2bccd4e374 v2.1: Add visible bulk import UI for developmental milestones
NEW FEATURES:
- Bulk Import button in Admin Panel → Developmental Milestones section
- "Import Default Milestones Data" button appears when database is empty
- "Re-import All" button to clear and re-import all static data
- Visible notice when no milestones exist with one-click import

IMPROVEMENTS:
- Auto-shows empty state notice when database has no milestones
- Backend bulk-import endpoint now supports clearExisting parameter
- Imports ALL age groups from static data (birth to 11 years)
- Better UX - admin doesn't need CLI to populate milestone data

FIXES:
- Makes milestone admin editing feature discoverable and usable
- No need to manually run import script anymore
2026-04-01 18:06:42 +00:00
ifedan-ed
75a3a47598 v4: Fix milestones display + add OpenID auth + 100MB PDF support
FIXES:
- Milestones now show correctly on encounter page (use static fallback if DB empty)
- Static data preserved as MILESTONES_DATA_STATIC for compatibility
- Database-driven milestones still work (admin can edit via CMS)

NEW FEATURES:
- OpenID Connect (OIDC) authentication support (PocketID, Keycloak, Azure AD, etc.)
- Comprehensive setup guide: OPENID_SETUP.md
- Auto-linking existing users by email on SSO login
- Multiple PDF upload support in Learning Hub (up to 10 files)
- 100 MB per file limit (was 20 MB)
- Full PDF content used for AI generation
- Embeddings use first ~8K chars for semantic search

IMPROVEMENTS:
- Updated UI to show multiple file selection with list
- Drag-and-drop supports multiple files
- Better file upload validation and error handling
- Added clarifying comments about embedding truncation
2026-04-01 17:59:51 +00:00
ifedan-ed
e29de42816 Add Web Speech Recognition option for real-time streaming
Provides two transcription options:

1. Browser Whisper (Offline, Batch) - RECOMMENDED
   - 100% offline, zero network calls
   - HIPAA-compliant, audio never leaves device
   - Highest accuracy (Whisper)
   - Processes after recording (batch mode)
   - Models self-hosted, bundled in v2

2. Web Speech API (Real-time, Streaming) - EXPERIMENTAL
   - Real-time transcription (see words as you speak)
   - Uses browser's built-in speech recognition
   - ⚠️ Sends audio to cloud (Chrome/Edge → Google)
   - ⚠️ NOT HIPAA-compliant
   - Requires user consent with clear warnings

Features:
- Settings UI for both options
- Clear privacy warnings for Web Speech
- Mutual exclusion (only one active at a time)
- Browser detection shows which provider is used
- Confirmation dialog before enabling Web Speech

Use Cases:
- Clinical/HIPAA: Use Browser Whisper only
- Personal/Non-clinical: Can use Web Speech for real-time feedback
- Maximum privacy: Browser Whisper (offline)
- Maximum speed: Web Speech (if privacy not required)

Implementation:
- speechRecognition.js: Web Speech API wrapper
- transcriptionSettings.js: Settings UI handler
- Privacy info displayed per browser

User can choose based on their privacy vs. speed preference.
2026-03-31 21:57:24 +00:00
ifedan-ed
ce26594592 Add admin dashboard for developmental milestones management
Features:
- Admin can add, edit, and delete developmental milestones via dashboard
- Milestones stored in PostgreSQL (developmental_milestones table)
- Client-side loads milestones from API instead of static file
- Import script to migrate existing static data to database
- Organized by age group and domain
- Supports sorting and filtering

Admin UI:
- New section in Admin panel for milestone management
- Filter by age group
- Add/Edit modal with validation
- Delete with confirmation
- Auto-complete for age groups and domains

API Endpoints:
- GET /api/milestones-data - Public endpoint for authenticated users
- GET /api/admin/milestones - List all milestones (admin only)
- GET /api/admin/milestones/meta - Get age groups and domains
- POST /api/admin/milestones - Create milestone
- PUT /api/admin/milestones/:id - Update milestone
- DELETE /api/admin/milestones/:id - Delete milestone
- POST /api/admin/milestones/bulk-import - Bulk import

Usage:
1. Run import script: node scripts/import-milestones.js
2. Access Admin dashboard → Developmental Milestones section
3. Add/Edit/Delete milestones as needed
2026-03-31 20:55:41 +00:00
ifedan-ed
332b4dc20e v16: Make Browser Whisper CDN failure graceful with clear warnings
Some checks failed
Build TWA APK / build-apk (push) Failing after 1s
Build & Push Docker Image / build (push) Failing after 7s
REALITY CHECK: Browser Whisper CDN loading cannot work in all environments
- Corporate firewalls block cdn.jsdelivr.net
- Network proxies filter JavaScript CDN
- Workers + importScripts + cross-origin = blocked by CSP/CORS

SOLUTION: Graceful degradation
- Clear user-friendly error messages
- Automatic fallback to server transcription
- Warning banner in Settings if CDN blocked
- Comprehensive troubleshooting documentation

Changes:
- browserWhisper.js: Show toast on worker error, fallback gracefully
- app.js: Display CSP warning banner on preload failure
- settings.html: Add warning about network/firewall requirements
- BROWSER_WHISPER_TROUBLESHOOTING.md: Complete guide for users

Key Message:
Browser Whisper is OPTIONAL. Server transcription (Google/AWS/OpenAI)
is the primary method and works everywhere. Browser Whisper is a
privacy-focused bonus feature that requires CDN access.

User Experience:
- If CDN works: Great! Browser Whisper available
- If CDN blocked: No problem! Server transcription works perfectly
- Clear messaging: User knows what to expect
2026-03-31 16:18:46 +00:00
ifedan-ed
fb53aa709f Add per-user voice preferences (STT model + TTS voice selection)
- NEW: User preferences for STT model and TTS voice
- Database: stt_model and tts_voice columns in users table
- UI: Voice Preferences section in Settings with dropdowns
- API: /api/user/preferences (GET/POST) + /preferences/options
- Transcribe: Respects user's STT model (Google, LiteLLM)
- TTS: Respects user's TTS voice (Google, LiteLLM, OpenAI, ElevenLabs)
- Preview: Test TTS voice before saving
- Available models/voices auto-detected from provider config
2026-03-31 14:47:00 +00:00
Daniel Onyejesi
59bf0a5cd8 Add browser-side Whisper transcription (local, zero network, HIPAA-safe)
- whisperWorker.js: Web Worker running @xenova/transformers Whisper in WASM
- browserWhisper.js: main-thread manager — audio→Float32 conversion, worker lifecycle
- transcribeAudio() checks BrowserWhisper.isEnabled() first, falls back to server
- Settings UI: enable/disable, model picker (tiny/base/small), pre-download button
- CSP: add wasm-unsafe-eval, cdn.jsdelivr.net, HuggingFace CDN domains
- Default: whisper-tiny.en (~39MB, ~2-3s per clip)
2026-03-31 07:30:17 -04:00
Daniel Onyejesi
8b94a49272 Fix TTS axios/Vertex, generic toast, add stop button to encounter
- TTS: switch from OpenAI SDK to axios (same fix as STT), drop voice
  param since it's configured inside LiteLLM per model
- Fix 'ElevenLabs unavailable' toast shown even when provider is LiteLLM
- Add dedicated red Stop button to encounter recording UI
2026-03-29 22:09:56 -04:00
Daniel Onyejesi
f8dd4d9f86 v9.1: Add Google Vertex AI + LiteLLM support, admin model management panel
- Add Vertex AI provider (Gemini models via @google-cloud/vertexai SDK)
- Add LiteLLM proxy support (OpenAI-compatible, routes to any provider)
- Admin panel: model search/discover from provider API, enable/disable, custom models, set default
- New endpoints: /config/models/discover, /config/models/add-discovered, /config/models/default
- Updated models.js with VERTEX_MODELS and LITELLM_MODELS lists
- Updated health endpoint with vertex + litellm status
2026-03-29 10:32:45 -04:00
ifedan-ed
0e350b8cad Remove admin model management panel — models use global selector instead
The admin model management (enable/disable, custom models, default override)
had persistent rendering issues. Removed the UI panel — models are managed
via the global model selector in the header, which works reliably. Backend
API endpoints for model config are retained for future use.
2026-03-29 02:30:29 +00:00
ifedan-ed
51fc818e4a Fix: admin models loading, clear refine/instructions on New, bigger HPI areas, unique labels
- Admin models: reset modelsLoaded flag on error so retry works
- Admin default model: fix redundant fetch race condition
- clearTab: now clears refine inputs, instructions, and demographic fields
- Encounter/dictation clear buttons: also clear refine input
- SOAP: instructions textarea already cleared by clearTab (soap-instructions)
- Encounter HPI: bigger transcript (400px) and output (600px) text areas
- Unique label enforcement: 409 error if saving with duplicate label
2026-03-29 01:35:45 +00:00
Daniel Onyejesi
6f671b3f47 Add admin model management dashboard — enable/disable, custom models, default override
- Full model management UI in admin panel: toggle models on/off, add custom
  model IDs (any OpenRouter/Bedrock ID), set admin-configured default model
- /api/models now returns admin-set default model, frontend respects it
- Toggle switch CSS for clean enable/disable UX
- Backend already had the API endpoints, this adds the missing UI
2026-03-28 22:07:16 +00:00
Daniel Onyejesi
e33cd4dc60 v10: Local Whisper transcription, bigger text areas, flexible AI memory
- Add local Whisper (whisper.cpp / faster-whisper) as transcription provider
  Set TRANSCRIBE_PROVIDER=local with configurable model size and binary path
- Upgrade all refine/instruction inputs to resizable textareas across
  encounter, dictation, hospital course, chart review, well visit, sick visit
- Make AI memory injection flexible: physician preferences and corrections
  are now actively applied (not just "formatting reference"), while still
  overridable by current prompt instructions
2026-03-28 22:00:30 +00:00
Daniel Onyejesi
5f066bb760 v9: Major feature update — audio backup, SOAP save, Dragon memory, S3 docs, CI/CD, APK
Phase 1 — Critical Fixes:
- Fix SOAP instructions not clearing on Clear button
- Show transcription provider (AWS/OpenAI) in UI toast
- Fix silent transcription failures in dictation and SOAP modules
- Add IndexedDB audio backup system (24hr retention, retry from Settings)
- Prevent duplicate encounter saves with idempotency keys
- Add Save/Load/New bar to SOAP note generator

Phase 2 — Features:
- Dragon-like AI memory: auto-track user corrections, inject into prompts
- Per-section template categories (SOAP, HPI, well visit, sick visit)
- Bigger textarea for SOAP instructions
- S3 document upload/management (AWS S3, Backblaze B2, MinIO compatible)
- Faster transcription via lower bitrate recording (16kbps opus)

Phase 3 — APK & CI/CD:
- GitHub Actions: Docker build+push on version tags
- GitHub Actions: TWA APK build for Obtainium auto-updates
- Android TWA project with foreground service for background recording
- Enhanced PWA manifest with shortcuts and maskable icons
2026-03-28 21:08:32 +00:00
Daniel Onyejesi
114bc4e7cd Fix CMS button clickability, replace AI topic input with context box
- Add type="button" to all CMS action buttons to prevent default submit
- Change overflow:hidden to overflow:visible on .cms-main to prevent clipping
- Add z-index to .cms-editor-actions for reliable click targeting
- Replace AI "Topic" text input with a descriptive textarea context box
  so users can tell AI what to generate (AI creates its own title)
- Rename AI tab from "By Topic" to "Describe Content"
2026-03-24 11:52:02 -04:00
Daniel Onyejesi
e3da7c5bb0 Render presentations as slides in Learning Hub viewer
- New GET /api/learning/content/:slug/slides (authMiddleware only)
  Renders Marp markdown via marp-core, returns {css, slides[]} for
  any authenticated user — no moderator access required

- showViewer: when content_type === 'presentation', hides the body card
  and shows a presentation card with title, slide count, and View Slides button
  Auto-opens the slide modal immediately on load; button allows re-opening

- openSlidesFromSlug(): fetches slides from user-accessible endpoint
  then opens the existing slide preview modal (arrow keys, swipe, dots)

- Viewer already correctly hides feed/categories/search when viewing content;
  "Back to Feed" button returns to the main list
2026-03-24 03:04:14 -04:00
Daniel Onyejesi
7c7f72f54a Fix WebDAV selection, pdf-parse, prompt(), CSP, security issues 1/2/4/5
pdf-parse (was v2, broken API):
- Downgraded to v1.1.1 — default export is a function again
- pdfParse is not a function error fixed

WebDAV file selection (style.display bug, same cascade issue):
- lh-ai-webdav-selected had inline style="display:flex" always visible
- selectWebdavFile() / deselectWebdavFile() now use element.style.display
- On select: browser div hides, selected indicator shows with file name + X
- On deselect (X): indicator hides, browser shows again

Topic context for Upload and Nextcloud tabs:
- Both tabs now have optional "Topic / context" field
- Sent to backend as 'topic' param to help AI focus the generated content

Inline refine bar (replaces window.prompt):
- Refine Body button shows/hides an amber inline input bar below generate buttons
- Enter instructions, press Apply — no browser dialog, works in all contexts

CSP: remove unsafe-inline from scriptSrc (security issue #2):
- Converted all 26 onclick= handlers in component HTML files to
  data-action / data-target / data-label attributes
- Added delegated click handler in app.js for copy/speak/nc-export actions
- script-src now 'self' only; script-src-attr 'none'

WebDAV path endpoint (security issue #5):
- New POST /api/user/webdav-path (authMiddleware only, not moderator)
- nextcloud.js updated to use new endpoint

nodemailer: already at 6.10.1 (vulnerability fixed).
2026-03-24 02:28:43 -04:00
Daniel Onyejesi
8c1550a437 Fix AI panel options: use style.display, redesign quiz card
Bug: classList.toggle('hidden') was silently ignored because
.lh-ai-opt-field{display:flex} is defined at CSS line 758, after
.hidden{display:none} at line 242 — same specificity, later rule wins.
Fix: updateAiOptions() now uses element.style.display directly (inline
styles always override class rules).

Also fix initial state: lh-ai-slides-field now uses style="display:none"
instead of class="hidden" so it's correctly hidden on load.

Panel syncs content type from editor on open (editorType → ctype select
→ updateAiOptions called immediately).

Per type:
- Quiz: word count hidden, slides hidden, quiz card visible (no toggle)
- Presentation: word count hidden, slides visible, quiz card hidden
- Article/Pearl: word count visible, slides hidden, quiz card with toggle

Quiz card redesign: gradient blue/indigo background, white header with
icon, clean body — replaces the plain grey rectangle.
2026-03-24 02:07:56 -04:00
Daniel Onyejesi
ad40678b6e AI panel context-aware options, delete wording, httpOnly cookie auth
AI Generate panel:
- Content type synced from editor on open
- Article/Pearl: optional word count field
- Presentation: optional slide count field
- Questions row: checkbox toggle for article/pearl (optional), always on for quiz,
  hidden for presentation; count input shown only when enabled
- Backend: passes wordCount/slideCount to prompt; questionCount=0 skips questions

Delete confirmation:
- Wording built dynamically from content type
- Presentation: no mention of questions
- Quiz: "All quiz questions will be permanently removed"
- Article/pearl: "Any quiz questions attached will also be removed"

Auth refactor — JWT → httpOnly cookie:
- Backend: setAuthCookie() sets ped_auth as httpOnly, Secure, SameSite=Lax, 7d
- Backend: clearAuthCookie() on POST /api/auth/logout (new endpoint)
- Backend: auth middleware reads ped_auth cookie first, falls back to Bearer header
- Login/register: no longer return token in JSON body; cookie is the session
- Frontend: getAuthHeaders() returns only Content-Type (no Authorization header)
- Frontend: session check uses /api/auth/me via cookie (no localStorage token)
- Frontend: clearSession() calls /api/auth/logout to clear cookie server-side
- Frontend: no token in localStorage; window.AUTH_TOKEN removed
- All FormData fetches use credentials:'same-origin' (cookie sent automatically)
- index.html: optimistic auth-screen hide (removed localStorage token check)

Tested: login, logout, /api/auth/me, admin routes, FormData uploads, cookie httpOnly
flag, 401 without cookie, cookie cleared on logout.
2026-03-24 01:46:07 -04:00
Daniel Onyejesi
6471aced55 Fix delete bar always visible, slide preview modal, Marp textarea hint
Delete bar bug:
- Root cause: .lh-delete-confirm-bar{display:flex} defined after .hidden{display:none}
  so flex overrode hidden (no !important on global .hidden rule)
- Fix: remove display:flex from base rule, use .lh-delete-confirm-bar:not(.hidden){display:flex}
  so hidden class always wins regardless of CSS order

Slide preview (replace window.open + document.write approach):
- Backend /preview-slides now returns JSON: {css, slides[]} (individual <section> HTML)
- In-page full-screen modal with dark overlay, white slide frame
- Prev/Next arrow buttons, dot indicators, slide counter
- Keyboard navigation (arrow keys, Escape to close)
- Touch/swipe support for mobile
- Overlay click closes modal
- No popups required, works in all browsers

Marp textarea:
- Placeholder updated to guide users: "Click AI Generate above to create slides"
- Makes it clear AI is the primary authoring path; textarea for review/edits
2026-03-24 01:13:55 -04:00
Daniel Onyejesi
020cc76f2f Delete confirm inline bar, lighter login, Presentation type with Marp+PPTX
Delete confirmation:
- Replaced browser confirm() popup with inline red warning bar in editor
- Shows content title, Cancel and Delete buttons inline in the editor header
- Category delete uses double-click pattern with toast feedback

Login screen:
- Changed from dark blue/purple gradient to light gray background (var(--g50))
- Card now matches 404 page style: white, subtle blue shadow, indigo border

Presentation content type:
- New 'Presentation' button in CMS toolbar (green style)
- Marp markdown textarea editor (dark code editor style) shown when type=presentation
- Body/Tiptap editor hidden; Marp section shown (toggleEditorMode)
- Preview button: renders Marp HTML via @marp-team/marp-core, opens in new tab
- Download PPTX: pure-JS pptxgenjs (no Chromium), parses slides, exports .pptx
- AI generate: returns Marp markdown directly when contentType=presentation
- Body column stores Marp markdown for presentations

Backend:
- POST /api/admin/learning/generate-pptx — pptxgenjs PPTX from Marp markdown
- POST /api/admin/learning/preview-slides — Marp HTML preview

GitHub repo set to private via API.
2026-03-24 00:59:36 -04:00
Daniel Onyejesi
936ad7e8a0 Add AI content generation to Learning Hub CMS
Backend (src/routes/learningAI.js):
- POST /api/admin/learning/ai-generate
  * Accepts: topic text, uploaded file (PDF/TXT/MD/HTML), or Nextcloud WebDAV path
  * Extracts text from PDFs via pdf-parse; plain text read directly
  * Fetches WebDAV files using stored Nextcloud credentials
  * Prompts AI to return structured JSON: title, subject, body (HTML), questions[]
  * Strips code fences, falls back to regex JSON extraction if needed
- POST /api/admin/learning/ai-refine — refine existing body with instructions
- GET  /api/admin/learning/webdav-browse — PROPFIND-based Nextcloud file browser
- POST /api/admin/learning/webdav-path — save user's default browse path

Frontend:
- AI Generate button in CMS editor header (gradient purple→blue)
- Panel with 3 source tabs: By Topic / Upload File / Nextcloud
- Drag-and-drop file dropzone with DataTransfer API support
- WebDAV file browser: navigate folders, select files, breadcrumb path
- Options: question count, content type (article/quiz/pearl), model selector, instructions
- Refine Current Body: prompt-driven AI rewrite of existing content
- Generated content auto-fills title, subject, type, Tiptap body, quiz questions

Settings:
- Nextcloud section: "Learning Hub Default Browse Path" field (shown when connected)
- Saves to webdav_learning_path column via /api/admin/learning/webdav-path

DB: ALTER TABLE users ADD COLUMN IF NOT EXISTS webdav_learning_path TEXT
2026-03-24 00:17:28 -04:00
Daniel Onyejesi
d4e57177e4 Add Quill rich text editor, multi-select questions, per-field rich text toggle
- Replace textarea+toolbar with Quill.js (free, MIT) for body editor
  - Full WYSIWYG: headings, bold/italic, lists, blockquote, code, link (inline, no popup)
- Rich Text toggle button on each question text field and option text field
  - Plain textarea by default; toggle activates mini Quill editor per field
- New 'Multiple Select' question type: checkboxes, all correct must be chosen
  - Backend scoring: allCorrectChosen && noWrongChosen
  - Results show all correct options highlighted, wrong selections flagged
- quiz display uses sanitizeHtml for question/option text (supports formatted HTML)
- sanitizeHtml allows Quill class attrs (ql-indent, etc.)
2026-03-23 22:37:29 -04:00
Daniel Onyejesi
ffacdcbedb Fix loading race condition, improve quiz UX, editor toolbar, settings page
- Fix: component loader now fires tabChanged AFTER HTML is in DOM
  (fixes CMS/settings stuck at loading)
- CMS: separate "New Article", "New Quiz", "New Pearl" buttons
- CMS: editor toolbar with formatting buttons (bold, italic, headings,
  lists, links, tables, quotes, code, hr)
- CMS: quiz questions have numbered headers (Q1, Q2...), larger inputs,
  clear "check correct answer" labels
- CMS: validation prevents saving questions without correct answer marked
- Quiz UX: large card-style question boxes with gradient number badges,
  bigger option buttons with hover effects and selection feedback,
  checkmark/cross icons on results
- Settings: improved card spacing, input styling, section headers with
  bottom borders for visual hierarchy
- Settings: updated compliance section (removed HIPAA references, added
  BAA/institution guidelines language)
2026-03-23 20:37:35 -04:00
Daniel Onyejesi
82a0911d24 Dedicated CMS page, responsive email templates, security fixes in auth
- New dedicated Content Manager tab (WordPress-like layout) with:
  - Stats dashboard (published, drafts, categories, quizzes, attempts)
  - Sidebar with category tree and filter controls (status, category)
  - Table-style content list with search
  - Full-page editor with title bar, meta fields, body editor, quiz builder
- Content Manager visible only for admin and moderator roles in sidebar
- Removed CMS from admin panel (admin panel is admin-only again)
- Email templates: fully responsive with max-width, proper mobile breakpoints,
  table-based layout for Outlook/Gmail compatibility, no overflow
- Security: escape all user data in emails with escHtml(), escape APP_URL
  in verify-email HTML, replace raw err.message with generic errors in auth
- Security: email body from admin settings now HTML-escaped before rendering
2026-03-23 20:13:22 -04:00
Daniel Onyejesi
0327afe94c Fix quiz SQL bug, security hardening, componentize HTML, speed improvements
- Fix: "missing FROM-clause entry for table q" in quiz submission query
- Security: replace broken sanitizeHtml with allowlist-based sanitizer (prevents stored XSS)
- Security: whitelist table names in uniqueSlug() to prevent SQL injection pattern
- Security: replace raw err.message responses with generic errors in learning routes
- Security: cap admin logs query LIMIT to 500
- Refactor: break 1685-line index.html into 12 lazy-loaded component files (280 lines shell)
- Speed: add preconnect hints for Google Fonts and CDN
- Speed: defer all script tags for faster initial paint
- Speed: lazy-load tab HTML on activation (only active tab's DOM is parsed)
- Update: replace HIPAA notice with institution-guidelines cautionary note
2026-03-23 20:01:54 -04:00