feat: org-scoped prescriptions backend, wire prescriptions page

Add the prescriptions table, validation, service and CRUD routes
(/api/prescriptions, RBAC-gated; prescriber defaults to the signed-in
clinician, prescribedAt to today) and the frontend data module. The page
now loads/persists real data and computes its status KPIs.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Khalid Abdi
2026-06-07 19:37:46 +03:00
parent dec04ec506
commit 7ef9da59bd
14 changed files with 2195 additions and 111 deletions
@@ -0,0 +1,26 @@
import { z } from "zod";
const nonEmpty = z.string().trim().min(1);
// Payload accepted by POST/PUT /api/prescriptions. The frontend dialog omits
// prescriber / prescribedAt / status on create — the route fills the prescriber
// from the signed-in user, the DB defaults prescribedAt to today, and status
// defaults to "active".
export const prescriptionInputSchema = z.object({
fileNumber: z.string().trim().default(""),
name: nonEmpty.max(200),
initials: z.string().trim().min(1).max(4),
medication: nonEmpty.max(200),
dose: z.string().trim().max(120).default(""),
frequency: nonEmpty.max(120),
prescriber: z.string().trim().max(200).default(""),
prescribedAt: z
.string()
.regex(/^\d{4}-\d{2}-\d{2}$/, "Date must be YYYY-MM-DD.")
.optional(),
status: z.enum(["active", "completed", "expired"]).default("active"),
duration: z.string().trim().max(120).nullish(),
notes: z.string().max(5000).nullish(),
});
export type PrescriptionInput = z.infer<typeof prescriptionInputSchema>;