Skip to main content

Prescriptions Module Redesign

Date: 2026-04-25
Status: Approved

Overview

Four improvements to the prescriptions module:
  1. Dedicated medication catalog (seeded with Pakistan drugs + clinic-extensible)
  2. 3-step wizard modal UI (replacing the current flat form)
  3. Signature Center integration (signature ID saved on prescription record)
  4. Clinic letterhead DOCX template support → PDF export

1. Database Changes

New: medications table

medications (
  id            text PK,
  clinic_id     text NULL,          -- null = system/seeded drug
  name          varchar(200),
  generic_name  varchar(200),
  category      varchar(100),
  dosage_forms  text[],             -- ['tablet','capsule','syrup','injection']
  common_dosages    text[],         -- ['250mg','500mg']
  common_frequencies text[],        -- ['OD','BD','TDS','QID','SOS']
  is_system     boolean default false,
  created_by    text,
  created_at    timestamp,
  updated_at    timestamp
)
Seed categories: Antibiotics, Analgesics/NSAIDs, Antifungals, Antihistamines, Vitamins/Supplements, Antacids/GI, Corticosteroids, Antivirals, Topicals, Miscellaneous. Seed size: ~300–500 commonly prescribed generics in Pakistan (Amoxicillin, Augmentin, Ibuprofen, Paracetamol, Metronidazole, Clarithromycin, Omeprazole, Cetirizine, etc.). Access rules:
  • System drugs (clinic_id IS NULL) visible to all clinics
  • Clinic custom drugs visible only to that clinic
  • Clinics can add, but not edit or delete system drugs

Updated: prescriptions table

+ signature_id    text NULL  FK → user_signatures(id)
+ template_type   text       CHECK IN ('default','letterhead') DEFAULT 'default'

Updated: prescription_items table

+ medication_id   text NULL  FK → medications(id)
-- medication_name kept for backwards compat and custom free-text drugs

Updated: clinics table

+ prescription_docx_key   text NULL  -- R2 key for original uploaded DOCX
+ prescription_image_key  text NULL  -- R2 key for rendered first-page PNG (A4)

2. Medication Catalog — Routes

GET  /medications?q=&category=         Search system + clinic drugs (fuzzy on name/genericName)
POST /medications                      Add custom clinic drug (requires edit_clinical_records)
DELETE /medications/:id                Remove custom clinic drug (own clinic only)
Search returns: { id, name, genericName, category, dosageForms, commonDosages, commonFrequencies, isSystem }. Search implementation: name ILIKE '%q%' OR generic_name ILIKE '%q%' — simple substring match, sufficient for a ~500 row table. Clinic-scoped: queries always filter clinic_id IS NULL OR clinic_id = req.clinicId.

3. Prescription Wizard UI

Replaces the current flat dialog with a 3-step modal using the existing custom-modal.tsx + Radix UI components + Tailwind, consistent with the rest of the app.

Step 1 — Patient & Details

Fields:
  • Patient — pre-filled if opened from patient profile, otherwise searchable select
  • Doctor — defaults to logged-in user, selectable from clinic staff
  • Date — defaults to today
  • Status — Active / Completed / Cancelled
  • Diagnosis — textarea
  • Notes — textarea
Validation: patient required before advancing to Step 2.

Step 2 — Medications

  • Drug search input — queries GET /medications?q= with debounce (300ms). Autocomplete dropdown shows: name, generic name, category badge, dosage forms.
  • “Add as custom drug” option at bottom of dropdown if no exact match — opens inline mini-form to add the drug to clinic catalog and immediately add it to the prescription.
  • Medication card (once added): name + category badge, four compact inputs — Dosage (text with suggestions from commonDosages), Frequency (select with suggestions from commonFrequencies: OD/BD/TDS/QID/SOS/Custom), Duration (text, e.g. “5 days”), Qty (number), Instructions (optional text). Card is collapsible. Remove button.
  • Multiple medications supported; list grows below.

Step 3 — Sign & Export

  • Signature block — fetches GET /signatures/active for the prescribing doctor’s role. Shows signature image preview, upload date. “Change” link opens signature selector (existing Signature Center UI). Selected signatureId stored in wizard state.
  • Template toggle — two options: “Clinic Letterhead” (shown only if prescriptionImageKey exists on clinic) and “Default Template”. Defaults to letterhead if available.
  • Summary strip — patient name, medication count, Rx number (generated on save).
  • Actions:
    • Save — saves prescription with status: active, no PDF export
    • Save & Export PDF — saves then triggers PDF download
Note: there is no separate draft status in the schema. “Save” creates an active prescription without exporting.

4. DOCX Template — Upload & Storage

Upload flow (one-time, in Clinic Settings)

  1. Clinic admin uploads .docx file via settings page (new “Prescription Template” card in Clinic Settings).
  2. Server receives file, validates it is a valid DOCX (check magic bytes / content-type).
  3. Store raw DOCX in R2 under key clinics/{clinicId}/prescription-template.docx.
  4. Convert DOCX → PNG:
    • Use mammoth.js to extract DOCX → HTML string.
    • Invoke a lightweight rendering step: POST the HTML to a dedicated Cloudflare Worker that uses @cloudflare/puppeteer (Browser Rendering API) to screenshot at A4 dimensions (794×1123px @96dpi), returning a PNG buffer. Requires Cloudflare Workers Paid plan (Browser Rendering API is not on Free tier).
    • Store PNG in R2 under key clinics/{clinicId}/prescription-template.png.
  5. Update prescription_image_key and prescription_docx_key on the clinic record.
  6. Return a signed R2 URL for the PNG so the settings page can show a preview.

Template routes

POST   /clinics/prescription-template         Upload DOCX (multipart/form-data)
DELETE /clinics/prescription-template         Remove template (deletes both R2 objects, clears keys)
GET    /clinics/prescription-template/preview Redirect to signed R2 URL for PNG preview

DOCX content guidance (documented for clinics)

The DOCX should have the clinic’s letterhead in the header and footer. The body area should be left blank or contain a placeholder line {{PRESCRIPTION_CONTENT}} — this is where the prescription content will be visually overlaid. No programmatic DOCX manipulation at export time; the PNG is used as a background layer.

5. PDF Generation — Updated Pipeline

With clinic letterhead (template_type = 'letterhead')

  1. Fetch letterhead PNG from R2 (cached in memory for the request).
  2. In @react-pdf/renderer, render an A4 <Page> with:
    • <Image src={letterheadPng} style={{ position:'absolute', top:0, left:0, width:'100%', height:'100%' }} /> — full-page background.
    • Content zone: absolute positioned block, top: 180pt, left: 60pt, right: 60pt, bottom: 120pt (leaves room for letterhead header/footer — these are sensible defaults for A4 letterheads; clinics with unusual layouts may need adjustment). Contains:
      • Rx number + date (top right)
      • Patient name + DOB
      • Diagnosis
      • Medications table (name, dosage, frequency, duration, qty, instructions)
      • Notes
    • Signature zone: bottom right of content area — doctor name + signature image.
  3. Export as PDF, trigger download.

Without letterhead (template_type = 'default')

Existing PrescriptionPdf.tsx template unchanged, continues to work as-is.

6. Module Constant Update

Add medications as a route within the prescriptions module. No new module needed — medication catalog is a sub-feature of the existing prescriptions module (already Pro tier).

7. Clinic Settings UI Addition

New card in Clinic Settings (under a “Prescriptions” section):
  • Prescription Template — “Upload your clinic’s letterhead (DOCX)” with a drag-and-drop zone.
  • Shows PNG preview thumbnail if template is uploaded.
  • “Remove” button to delete the template.

8. Error Handling & Edge Cases

  • If signature is not set up: Step 3 shows a warning banner “No active signature found — export will have no signature” with a link to Signature Center. Export still allowed.
  • If DOCX → PNG conversion fails: show error in settings, keep clinic on default template. Retry allowed.
  • prescription_items.medication_id is nullable — old prescriptions with free-text medication names remain valid and render correctly.
  • Deleting a system medication is blocked at API level (403). Deleting a clinic medication that is referenced in existing prescription items is soft-blocked with a warning.

9. Out of Scope

  • Drug interaction checks
  • Prescription refill tracking
  • Per-doctor letterhead templates (clinic-level only)
  • Expiration dates on prescriptions
  • External formulary API integration