feat: T4 wave 10 – bottling quality, phenology and honey chain UI, Telegram link/receive and ops
- Bottling quality: water content above the legal limit (strict >) answers 409 CONFIRMATION_REQUIRED bound to the measured value; prefill for jar size, best-before (shelfLifeMonths, month-end clamping) and water content; impossible dates give 400; water-content checks in src/common/quality. - Frontend phenology: "Entwicklung melden" sheet, IndexedDB v4 queue, season header on the apiary page, confirmed revoke for all apiaries. - Frontend honey chain: "Vorschlag aus …" hints with explicit take-over, prefill status note, movement ledger with withdraw/correct dialogs. - Telegram: link tokens (hashed, single use), private-chat receiver with lease and offset CAS, bounded poison-update skip, Retry-After, pending deliveries skipped on disconnect; system API for deliveries and retry; worker-only secret wiring and manifest policy tests. - ARCHITECTURE.md §7, §9, §10, §11.11–11.13, Annahmen 51–55. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
1 parent
16cc557462
commit
fab7f7e12f
91 files changed
+8463
-104
No files matched your search
@@ -7,6 +7,7 @@ import { CreateFeedingInput } from "@/lib/feedings/types";
|
||||
import { CreateVarroaMeasurementInput } from "@/lib/varroa/types";
|
||||
import { CreateSwarmControlCheckInput } from "@/lib/swarm-control/types";
|
||||
import { CreateHarvestInput } from "@/lib/harvests/types";
|
||||
import type { CreatePhenologyReportInput } from "@/lib/phenology/types";
|
||||
import {
|
||||
colonyIdsOfBulkInput,
|
||||
invalidColonyIdsOfBody,
|
||||
@@ -21,6 +22,7 @@ export interface RecordQueueInputs {
|
||||
"pending-varroa-measurements": CreateVarroaMeasurementInput;
|
||||
"pending-swarm-control-checks": CreateSwarmControlCheckInput;
|
||||
"pending-harvests": CreateHarvestInput;
|
||||
"pending-phenology-reports": CreatePhenologyReportInput;
|
||||
}
|
||||
export type RecordStoreName = keyof RecordQueueInputs;
|
||||
/** Every store whose entries wait to be sent and are shown in the queue sheet. */
|
||||
@@ -107,11 +109,12 @@ export interface OfflineDB extends DBSchema, RecordStores {
|
||||
|
||||
const DB_NAME = "apicore-offline";
|
||||
// v1 only had "pending-inspections", v2 added the other five quick-capture
|
||||
// types, v3 adds bulk entries, photos, offline packs and meta. Entries already
|
||||
// types, v3 adds bulk entries, photos, offline packs and meta, v4 adds plant
|
||||
// reports (Pflanzenentwicklung). Entries already
|
||||
// queued on a phone must survive every upgrade, so each store is only created
|
||||
// if it doesn't exist yet (guarded by oldVersion, per idb's upgrade()
|
||||
// convention), never recreated.
|
||||
const DB_VERSION = 3;
|
||||
const DB_VERSION = 4;
|
||||
|
||||
const RECORD_STORES: readonly RecordStoreName[] = [
|
||||
"pending-inspections",
|
||||
@@ -120,6 +123,7 @@ const RECORD_STORES: readonly RecordStoreName[] = [
|
||||
"pending-varroa-measurements",
|
||||
"pending-swarm-control-checks",
|
||||
"pending-harvests",
|
||||
"pending-phenology-reports",
|
||||
];
|
||||
const QUEUE_STORES: readonly QueueStoreName[] = [...RECORD_STORES, "pending-bulk"];
|
||||
|
||||
@@ -130,6 +134,7 @@ const RECORD_LABELS: Record<RecordStoreName, string> = {
|
||||
"pending-varroa-measurements": "Varroamessung",
|
||||
"pending-swarm-control-checks": "Schwarmkontrolle",
|
||||
"pending-harvests": "Ernte",
|
||||
"pending-phenology-reports": "Pflanzenmeldung",
|
||||
};
|
||||
const BULK_LABELS: Record<BulkKind, string> = {
|
||||
treatments: "Behandlung für mehrere Völker",
|
||||
@@ -172,6 +177,12 @@ export function getDb(): Promise<IDBPDatabase<OfflineDB>> {
|
||||
db.createObjectStore("offline-packs", { keyPath: "apiaryId" });
|
||||
db.createObjectStore("meta");
|
||||
}
|
||||
if (oldVersion < 4) {
|
||||
db.createObjectStore("pending-phenology-reports", {
|
||||
keyPath: "id",
|
||||
autoIncrement: true,
|
||||
});
|
||||
}
|
||||
},
|
||||
// A newer app version in another tab wants to upgrade: step aside
|
||||
// instead of blocking it. The next call here opens the new version.
|
||||
@@ -263,6 +274,14 @@ export function queueHarvest(
|
||||
return queue("pending-harvests", input, idempotencyKey, ownerUserId);
|
||||
}
|
||||
|
||||
export function queuePhenologyReport(
|
||||
input: CreatePhenologyReportInput,
|
||||
idempotencyKey: string,
|
||||
ownerUserId: string,
|
||||
): Promise<void> {
|
||||
return queue("pending-phenology-reports", input, idempotencyKey, ownerUserId);
|
||||
}
|
||||
|
||||
/**
|
||||
* One entry for several colonies. The key belongs to this exact selection of
|
||||
* colonies: it is stored with the entry and sent again unchanged.
|
||||
|
||||
Reference in new issue
Block a user