import type { RecordType } from "../api/types"; /** REC-3: limits per check. Files go straight to storage, so nginx's 11 MB cap does not apply. */ export const RECORD_LIMITS = { perCheck: 20, pagesPerDocument: 20, bytesPerFile: 15 * 1024 * 1024 }; export const ACCEPTED: Record = { "text/plain": "TXT", "application/pdf": "PDF", "image/jpeg": "JPG", "image/png": "PNG", "image/heic": "HEIC", "image/heif": "HEIC", }; const byExtension: Record = { txt: "text/plain", pdf: "application/pdf", jpg: "image/jpeg", jpeg: "image/jpeg", png: "image/png", heic: "image/heic", heif: "image/heic" }; export const recordTypes: Record = { prescription: "Prescription", lab_result: "Lab result", discharge_summary: "Discharge summary", clinic_note: "Clinic note", other: "Other", }; /** Some browsers give HEIC and TXT files no type; fall back to the extension. */ export function contentTypeOf(file: { name: string; type: string }): string { if (file.type && ACCEPTED[file.type]) return file.type === "image/heif" ? "image/heic" : file.type; return byExtension[file.name.split(".").pop()?.toLowerCase() ?? ""] ?? file.type ?? ""; } const mb = (bytes: number) => (bytes / 1024 / 1024).toFixed(1); /** The message names the file and says by how much it is over (REC-3). */ export function fileProblem(file: { name: string; type: string; size: number }): string | null { if (!ACCEPTED[contentTypeOf(file)]) return `${file.name} is not a file Context reads. Use TXT, PDF, JPG, PNG or HEIC.`; if (file.size === 0) return `${file.name} is empty.`; if (file.size > RECORD_LIMITS.bytesPerFile) return `${file.name} is ${mb(file.size)} MB, ${mb(file.size - RECORD_LIMITS.bytesPerFile)} MB over the 15 MB limit per file.`; return null; } /** How many of the chosen files fit, and the message when some do not. */ export function roomFor(existing: number, adding: number): { fits: number; message: string | null } { const room = Math.max(0, RECORD_LIMITS.perCheck - existing); if (adding <= room) return { fits: adding, message: null }; return { fits: room, message: `A check can use up to ${RECORD_LIMITS.perCheck} records. This patient has ${existing}, so ${adding - room} of these ${adding === 1 ? "is" : "are"} left out.`, }; } /** "01-clinic_prescription.pdf" becomes "Clinic prescription" (REC-4: from the file name, editable). */ export function titleFromFile(name: string): string { const title = name .replace(/\.[a-z0-9]{2,5}$/i, "") .replace(/^\d+(?=[\s._-])/, "") .replace(/[-_.]+/g, " ") .replace(/\s+/g, " ") .trim() .slice(0, 120); return title ? title[0].toUpperCase() + title.slice(1) : "Untitled record"; } export function guessType(name: string): RecordType { const n = name.toLowerCase(); if (/lab|egfr|blood|chemistry|result|e\/?u\/?cr|urea|creatinine/.test(n)) return "lab_result"; if (/prescri|\brx\b|medic/.test(n)) return "prescription"; if (/discharge|hospital|summary/.test(n)) return "discharge_summary"; if (/note|clinic|consult/.test(n)) return "clinic_note"; return "other"; }