feat: delete controls across add-pages (lab, dispense, rx, inventory, tasks)

Backend:
- DELETE /api/patients/:fileNumber/labs (remove one lab result, lab:write)
- DELETE /api/dispenses/:id (void a ledger entry, inventory:write)

Frontend (all guarded by ConfirmDialog):
- lab "Recent results" rows: delete result
- pharmacy "Recently dispensed" rows: delete record
- prescriptions/tasks detail sheets + inventory detail dialog gain a delete
  action (prescription delete stays off the shared Pharmacy sheet)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Khalid Abdi
2026-06-17 19:23:41 +03:00
parent 96bda1b902
commit d37a4e9425
15 changed files with 467 additions and 10 deletions
+25
View File
@@ -1,6 +1,7 @@
import { Router } from "express";
import { dispenseInputSchema } from "../lib/dispense-validation.js";
import { HttpError } from "../lib/http-error.js";
import {
requireAuth,
requireOrg,
@@ -53,3 +54,27 @@ dispensesRouter.post(
}
},
);
dispensesRouter.delete(
"/:id",
requirePermission({ inventory: ["write"] }),
async (req, res, next) => {
try {
const ok = await service.deleteDispense(
req.organizationId!,
req.params.id as string,
);
if (!ok) throw new HttpError(404, "Dispense not found.");
await recordActivity({
orgId: req.organizationId!,
actor: { id: req.user!.id, name: req.user!.name },
action: "Voided a dispense record",
entityType: "dispense",
entityId: req.params.id as string,
});
res.status(204).end();
} catch (err) {
next(err);
}
},
);
+36
View File
@@ -28,6 +28,12 @@ const labsAppendSchema = z.object({
labs: z.array(labSchema).min(1).max(50),
});
const labDeleteSchema = z.object({
name: z.string().trim().min(1),
value: z.string(),
takenAt: z.string(),
});
// Notify the rest of the clinic about a patient record change (best-effort,
// pushed live over the socket).
async function notifyClinic(
@@ -255,6 +261,36 @@ patientsRouter.post(
},
);
// Remove one lab result. Gated by `lab:write` like appending, so lab staff can
// correct their own submissions without patient-edit rights.
patientsRouter.delete(
"/:fileNumber/labs",
requirePermission({ lab: ["write"] }),
async (req, res, next) => {
try {
const match = labDeleteSchema.parse(req.body);
const updated = await service.deleteLab(
req.organizationId!,
req.params.fileNumber as string,
match,
);
if (!updated) throw new HttpError(404, "Patient not found.");
await recordActivity({
orgId: req.organizationId!,
actor: { id: req.user!.id, name: req.user!.name },
action: `Removed lab result ${match.name} for ${updated.name}`,
entityType: "patient",
entityId: updated.fileNumber,
patientName: updated.name,
patientFileNumber: updated.fileNumber,
});
res.json(updated);
} catch (err) {
next(err);
}
},
);
patientsRouter.delete(
"/:fileNumber",
requirePermission({ patient: ["delete"] }),