Commit graph

12 commits

Author SHA1 Message Date
Daniel
b3d66caaca feat: a retried transcript goes back to the tab the audio came from
Retrying a kept recording transcribed it and put the text on the clipboard,
leaving you to find the right tab and paste. The app already had the answer: the
module was recorded with the audio. Retry now opens that tab and puts the text
in its transcript box.

Two things had to be true first.

The module was not actually being recorded. transcribeAudio never sent one, so
the server stored its default for every upload — all 28 rows in audio_backups
said "recording", and a retry had nowhere to send anything back to. Each
module's call now tags its own upload.

And the names disagreed. The recorders tagged 'encounter', 'soap', 'dictation'
while the recording-started events said 'enc', 'sick', 'dict'. One table now
holds the mapping and resolves the aliases, so the recorder that tags the
upload, the backup row that labels it and the retry that delivers it cannot
drift apart again.

Existing text is appended to, never replaced: a retry usually recovers
something on top of a live transcript, and overwriting would lose the words the
browser did hear. The box only exists once its tab's markup has been fetched, so
delivery polls briefly rather than guessing a delay, and falls back to the
clipboard if the tab never opens. An empty result says so rather than claiming
success. Backup rows now name their source and the button reads "Retry into
SOAP Note" instead of "Retry".

Verified in a browser: enc resolves to encounter, delivery switched tabs and
produced 'existing live transcript\n\nRECOVERED TEXT'.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Dv6sqaY6Vq3ChZHMem3cnU
2026-09-11 12:39:50 +02:00
Daniel
fed4bd154f fix: recordings that produced nothing, and the boxes that zoomed on iOS
All checks were successful
Forgejo Android APK / Root app tests (push) Successful in 49s
Forgejo Android APK / Build signed APK (push) Successful in 2m8s
Measured in a real browser against the app rather than reasoned about.

The transcript boxes are contenteditable divs, and an editable div zooms on
focus exactly like an <input>. The earlier 16px sweep covered input, textarea
and select, so every workspace tab still zoomed while the calculators did not —
which is exactly what was reported. Every focusable text control in every tab
now measures 16px at phone width; the count of ones below it is zero.

Three ways a recording could end with nothing to show for it:

  - Safari supports none of the audio/webm types and throws NotSupportedError
    when handed one. Six modules built their own recorder on resume with
    "opus, else audio/webm", so resuming threw there and the recording stopped.
    There is now one codec chain in the app, and no module constructs a
    MediaRecorder of its own.

  - audio-recorder-failed is dispatched on document, and the encounter tab
    stopped its recording on any of them. The assistant's microphone failing
    ended a consultation being recorded in another tab. The recorder now
    travels with the event and the listener checks it is its own.

  - The server answers {success:true, text:''} for silence, and five modules
    assigned that straight into the transcript — emptying the box the browser
    had been filling live. It reads as a recording that vanished. Text is now
    required before overwriting, and a recording that captured nothing says so
    instead of resetting the button over an empty box.

Also: the citation counters were registered on prom-client's default registry
while the app serves its own, so they were never scraped. They read zero at
/metrics now instead of being absent, which is what the Grafana panels need.

And the reference linter passes for the first time, so scripts/e2e.sh gets past
its preflight: KaTeX is vendored (it was referenced by the assistant's LaTeX
rendering but never shipped — three 404s a page load and no math), and the
JavaScript left behind by the removed image picker, saved-chats toggle, image
gallery and visual-output panel is gone.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Dv6sqaY6Vq3ChZHMem3cnU
2026-09-11 00:16:19 +02:00
Daniel
416fff624a feat: add patient education handouts 2026-05-08 22:26:53 +02:00
Daniel
38667608b1 convert encounter note entrypoints to modules 2026-05-08 07:15:11 +02:00
Daniel
e05720083a test clinical note entrypoints 2026-05-08 06:33:34 +02:00
Daniel
4f129b24e1 feat: don't-miss tooltip after notes + bedside suture selector
#2 — Don't-miss tooltip (encounters HPI + sick visit, max 5)
- New POST /api/dont-miss returning {points: [{point, why}]} capped at 5
  (cap defended both in the prompt and server-side .slice(0,5))
- New dontMissTooltip prompt in prompts.js
- New suggestDontMiss() helper in app.js mirroring suggestBillingCodes;
  inserts an orange-bordered card next to the note output, silent on empty
- Wired into liveEncounter.js (encounter HPI) and sickVisit.js. Not added
  to wellvisit/soap/hospital/chart per spec.

#1 — Bedside suture selector
- New ES module public/js/bedside/sutures.js (~300L) following the
  burns.js pattern: site × age × tension × cosmetic × contamination ×
  hours-since-injury → material, size, technique, removal day range,
  glue/Steri-strip alternative, warnings, tetanus reminder.
- 15 anatomic sites covered (face, eyelid, lip vermilion, intraoral,
  ear, scalp, neck, trunk, upper/lower ext, hand, foot, joint surface,
  genitalia, fingertip).
- Bites: cat/human → don't-close-primarily warning; dog bite to hand →
  loose-approximation note. Heavy contamination → delayed primary
  closure. >12h non-face/scalp → judgment call note.
- Removal days shown as ranges (3–5, 7–10, 10–14) per source norms,
  not single midpoints.
- Subungual hematoma trephination guidance corrected: any painful
  hematoma with intact nail and no displaced fracture (especially if
  25–50% or more), per current UpToDate guidance.
- Inline citation: Roberts & Hedges 7e (2019), Fleisher & Ludwig 8e,
  AAP Section on EM, UpToDate (Pope JV).
- Pill registered in sub-nav SECTIONS + bedside/index.js. Persists
  active state via existing UIState helper.

All 46 tests pass.
2026-04-28 03:09:38 +02:00
Daniel
67b7667e04 feat: ED encounters + notes model selector; remove AI corrections; fix notes framing
Four changes batched:

1. ED Encounters tab (new) — multi-stage emergency note with don't-miss
   tooltips and 2023 E/M MDM finalize. New route /api/ed-encounters
   (generate per-stage + finalize MDM), new ed-encounters.js owning all
   client logic, new ed-encounter.html component, new template_ed memory
   category. Persists draft to localStorage every keystroke and to
   saved_encounters on stage advance. encounters.js touched only to
   register the new tab in sessionStorage restore + tabMap (save and
   idempotency code untouched).

2. Notes model selector — /notes/from-voice now accepts a client-supplied
   model (validated by the existing callAI allow-list); falls back to the
   admin default. Added <select class="tab-model-select"> to notes.html
   so the existing app.js populator handles options + default.

3. Remove AI-learning-from-corrections — deleted correctionTracker.js,
   POST /memories/correction, the corrections branch in
   /memories/context, the settings UI section, the FAQ entry, and all
   dead trackAIOutput/saveCorrection guards in callers. Legacy
   correction_* DB rows are filtered (NOT LIKE) rather than dropped, so
   no destructive migration.

4. Fix notes AI framing — /notes/from-voice prompt no longer assumes
   "physician dictation". Plain notes (shopping lists, reminders,
   ideas) now match the dictation tone instead of being forced into
   clinical structure.

All 46 tests pass.
2026-04-28 03:09:38 +02:00
Daniel
639a5d2873 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
09193538fb 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 Onyejesi
868afcda20 Fix module init: defer all tab scripts until component HTML is loaded
All JS modules were running immediately (IIFE/DOMContentLoaded) but
tab HTML is lazy-loaded from /components/*.html, causing null errors
on every addEventListener call. Each module now listens for tabChanged
with its tab name and initializes once after the DOM is ready.

Also: quiz question/explanation boxes changed to textarea for
multi-line support; quiz display uses sanitizeHtml() so formatted
text (bold, lists, etc.) renders correctly.
2026-03-23 22:03:54 -04:00
Daniel Onyejesi
ca68177abb Fix line breaks in AI output: use innerHTML instead of textContent
Adds setOutputText() helper that escapes HTML and converts \n to <br>,
applied to all AI-generated note outputs so line breaks are preserved
in contenteditable divs. Also cleans up CSS for quiz option markers.
2026-03-23 21:17:50 -04:00
Daniel Onyejesi
5dd08f8da3 Feat: admin CMS, save/resume encounters, user templates, SSHADESS, well visit notes, sick visit, ROS/PE checklists, ICD-10 diagnoses
- Admin CMS: announcements, feature flags, email templates, AI prompts, SMTP, model management, reset-to-defaults
- Save/resume encounter progress with 7-day auto-expiry and patient labels
- Inline searchable load popover (replaces settings-modal redirect)
- User memory/template system (physical exam, ROS, encounter format, etc.)
- SSHADESS psychosocial screening form (8 domains, listen-in recording)
- Well visit note generation with vitals, vaccines, screenings, SSHADESS
- Interactive screenings/vaccines status buttons (Done/Refused/Already Given/N/A)
- ROS 15-system checklist + PE 17-system checklist in well visit note tab
- ICD-10 diagnoses tag picker (28 common pediatric codes + free text)
- Simple sick visit tab: chief complaint, inferred ROS (4) + PE (7), diagnoses, generate note
- Settings converted from modal to full-page tab
- Fix: /api/models moved before authenticated routes (was returning 401 → empty model selector)
- Fix: getUserMemoryContext always fetches from API regardless of cache state
- Pause/resume recording on all recording tabs
- Site-level SMTP config stored in DB; ElevenLabs TTS with browser fallback
2026-03-22 16:43:39 -04:00