feat: T6 wave 5 – readiness checks and report, compliance and medicine UI, label UI
Backend Image / build-and-push (push) Successful in 6m45s
Frontend Image / build-and-push (push) Failing after 9m30s

- Readiness: all 18 checks (medicine lots, treatment log gaps, labels,
  harvest withdrawal), medicine subjects masked without
  veterinary-medicines:read, report PDF (counts only), summary cached 60 s,
  409 codes READINESS_*.
- Frontend: /compliance page with acknowledge/revoke, dashboard line and
  nav; veterinary medicine catalogue, lots, movement book with inventory
  and disposal; label designer check panel, print card with gap list and
  proof print, food business master data, product legal name.
- Service worker: cacheability is path-based (the backend sends no-store
  on every answer); compliance, medicine, restriction, notice and label
  check/print paths and restricted document kinds are never cached.
- ARCHITECTURE.md §2, §12.10–12.11, Annahmen 86–90.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
Nick SzaunigandClaude Opus 5.5 committed 2026-10-15 15:59:08 +02:00
1 parent b912d29166
commit b4746a2b17
149 files changed
+11869 -320

No files matched your search

@@ -1,5 +1,4 @@
import { apiFetch, apiFetchBlob } from "@/lib/auth/api-client";
import { downloadBlob } from "@/lib/export/api";
import { CreateLabelTemplateInput, LabelTemplate, UpdateLabelTemplateInput } from "./types";
export function fetchLabelTemplates(): Promise<LabelTemplate[]> {
@@ -27,15 +26,32 @@ export function deactivateLabelTemplate(id: string): Promise<LabelTemplate> {
return apiFetch<LabelTemplate>(`/label-templates/${encodeURIComponent(id)}`, { method: "DELETE" });
}
export interface PrintLabelsOptions {
/** 1 = top left of the sheet; omitted prints from the start. */
startPosition?: number;
/** A test print stamped „MUSTER – nicht verkaufsfähig“; always allowed. */
proof?: boolean;
}
/**
* The label PDF. A 409 `LABEL_PARTICULARS_MISSING` throws an `ApiError` whose
* `body.gaps` lists what is missing (`labelParticularsMissing` reads it).
*/
export async function printLabels(
templateId: string,
bottlingBatchId: string,
startPosition?: number,
): Promise<void> {
const query = startPosition && startPosition > 1 ? `?startPosition=${startPosition}` : "";
const { blob, filename } = await apiFetchBlob(
options: PrintLabelsOptions = {},
): Promise<Blob> {
const params = new URLSearchParams();
if (options.startPosition && options.startPosition > 1) {
params.set("startPosition", String(options.startPosition));
}
if (options.proof) params.set("proof", "true");
const query = params.size > 0 ? `?${params.toString()}` : "";
const { blob } = await apiFetchBlob(
`/label-templates/${encodeURIComponent(templateId)}/print/${encodeURIComponent(bottlingBatchId)}${query}`,
"etiketten.pdf",
{ cache: "no-store" },
);
downloadBlob(blob, filename);
return blob;
}