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

+12 -7
View File
@@ -9,7 +9,9 @@
* The routing rules live in sw-routes.js (unit-tested).
*
* What is never kept: auth answers (tokens, session state), apiaries
* themselves (coordinates, address), Next.js route payloads, and request
* themselves (coordinates, address), Prüfbereitschaft and authority data,
* medicine packs, single document records, Next.js route
* payloads, and request
* headers - entries are stored under their bare URL, so the Authorization
* header with the access token never lands in Cache Storage. The cached
* HTML holds no user data: the (app) layout renders nothing until the client
@@ -19,7 +21,7 @@
// Content version of sw-routes.js (sha256 of its LF text, first 12 hex
// characters; sw-routes.spec.ts checks it). A changed value changes this
// file, so every browser - Safari too - installs the worker with new rules.
const SW_ROUTES_VERSION = "6a6f9dcbc99f";
const SW_ROUTES_VERSION = "0681ff4b095d";
importScripts(`/sw-routes.js?v=${SW_ROUTES_VERSION}`);
const {
@@ -28,7 +30,8 @@ const {
offlineHomeFor,
navigationPlanFor,
extractAssetUrls,
isCacheableApiPath,
isCacheableApiUrl,
isKeepableApiAnswer: isKeepableApiHeaders,
apiOriginFrom,
isRoutePayload,
buildAssetsToPrune,
@@ -125,7 +128,7 @@ self.addEventListener("fetch", (event) => {
event.respondWith(respondWithRuntimeConfig(event));
return;
}
if (isCacheableApiPath(url.pathname)) {
if (isCacheableApiUrl(url)) {
respondToApiCall(event, url);
return;
}
@@ -202,7 +205,7 @@ function removeUnknownCaches() {
async function pruneForbiddenEntries() {
const data = await caches.open(DATA_CACHE);
for (const request of await data.keys()) {
if (!isCacheableApiPath(new URL(request.url).pathname)) {
if (!isCacheableApiUrl(new URL(request.url))) {
await data.delete(request);
}
}
@@ -299,8 +302,10 @@ function isGoodAnswer(response) {
* so it is readable on the backend's cross-origin answers.
*/
function isKeepableApiAnswer(response) {
const contentType = response.headers.get("content-type") || "";
return response.ok && contentType.toLowerCase().includes("application/json");
return isKeepableApiHeaders({
ok: response.ok,
contentType: response.headers.get("content-type"),
});
}
function isGoodPage(response) {