feat: platform backbone and field capture complete (checkpoint)
Platform backbone (plan 2026-10-13-plattform-rueckgrat), all tasks reviewed: - webhooks with delivery table, retries and stable delivery id - calendar handlers; a finished task removes its Outlook event - tasks, automations, inspections, varroa, treatments, feedings, harvests, colonies, wintering, stock and bottling record domain events in the same transaction; legacy post-commit calls removed - row locks through one helper (lockRowForUpdate, lockRowsForShare, lockRowsForUpdate) - job framework with JobRun, cli exit codes, cleanup, task templates, daily evaluation, hourly app-backup and the rekey job - system API and page "Verwaltung > System" with retry - @nestjs/schedule removed, guard tests against in-process schedulers Field capture T3 (plan 2026-10-13-t3-erfassung-am-stand), all tasks reviewed: - inspection photo offline without metadata - bulk capture for treatments, feedings and varroa (backend + frontend) - offline stand mode with service worker, offline pages and packs - offline session, cold start and logout on shared devices Docs: ARCHITECTURE.md assumptions and sections updated, TASKS.md. Gates: backend lint, typecheck, 1770 unit, 420 e2e, build, no schema drift; frontend lint, typecheck, 913 tests, build. Not rolled out. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
1 parent
c01a38835a
commit
cdd30cf3ee
288 files changed
+39165
-2853
No files matched your search
@@ -0,0 +1,151 @@
|
||||
// Test data for offline packs - shared by the specs, never imported by app code.
|
||||
import type { StoredPack } from "@/lib/offline/db";
|
||||
import type { ColonyOverviewRow, OfflinePackV1 } from "./field-types";
|
||||
|
||||
export const MINUTE_MS = 60 * 1000;
|
||||
export const HOUR_MS = 60 * MINUTE_MS;
|
||||
export const DAY_MS = 24 * HOUR_MS;
|
||||
|
||||
type PackColony = OfflinePackV1["colonies"][number];
|
||||
type PackInspection = NonNullable<OfflinePackV1["inspections"]>[number];
|
||||
type PackTask = NonNullable<OfflinePackV1["tasks"]>[number];
|
||||
type PackQueen = NonNullable<OfflinePackV1["queens"]>[number];
|
||||
|
||||
export function packColony(id: string, code: string, overrides: Partial<PackColony> = {}): PackColony {
|
||||
return {
|
||||
id,
|
||||
code,
|
||||
apiaryId: "apiary-1",
|
||||
status: "ACTIVE",
|
||||
colonyType: "PRODUCTION_COLONY",
|
||||
hiveType: "Zander",
|
||||
strength: 8,
|
||||
broodStatus: "GOOD",
|
||||
feedStatus: "GOOD",
|
||||
healthStatus: "GOOD",
|
||||
currentQueenId: null,
|
||||
notes: null,
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
export function overviewRow(colony: PackColony, overrides: Partial<ColonyOverviewRow> = {}): ColonyOverviewRow {
|
||||
return {
|
||||
id: colony.id,
|
||||
code: colony.code,
|
||||
status: colony.status,
|
||||
strength: colony.strength,
|
||||
broodStatus: colony.broodStatus,
|
||||
feedStatus: colony.feedStatus,
|
||||
healthStatus: colony.healthStatus,
|
||||
lastInspectionDate: new Date().toISOString(),
|
||||
openWarnings: 0,
|
||||
openCritical: 0,
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
export function packInspection(
|
||||
id: string,
|
||||
colonyId: string,
|
||||
date: string,
|
||||
overrides: Partial<PackInspection> = {},
|
||||
): PackInspection {
|
||||
return {
|
||||
id,
|
||||
colonyId,
|
||||
date,
|
||||
weather: null,
|
||||
temperature: null,
|
||||
colonyStrength: null,
|
||||
broodAmount: null,
|
||||
broodFrames: null,
|
||||
broodPattern: null,
|
||||
openBroodPresent: null,
|
||||
cappedBroodPresent: null,
|
||||
eggsPresent: null,
|
||||
queenSeen: null,
|
||||
queenCellsPresent: null,
|
||||
swarmMood: null,
|
||||
gentleness: null,
|
||||
combBehavior: null,
|
||||
feedStatus: null,
|
||||
honeySuperStatus: null,
|
||||
healthIssues: null,
|
||||
varroaIssues: null,
|
||||
measures: null,
|
||||
notes: null,
|
||||
createdAt: date,
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
export function packTask(id: string, title: string, overrides: Partial<PackTask> = {}): PackTask {
|
||||
return {
|
||||
id,
|
||||
title,
|
||||
description: null,
|
||||
dueDate: null,
|
||||
priority: "NORMAL",
|
||||
status: "OPEN",
|
||||
relatedType: "COLONY",
|
||||
relatedId: null,
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
export function packQueen(id: string, code: string, overrides: Partial<PackQueen> = {}): PackQueen {
|
||||
return {
|
||||
id,
|
||||
code,
|
||||
hatchYear: 2025,
|
||||
breed: "Carnica",
|
||||
markingColor: "Rot",
|
||||
markingNumber: "12",
|
||||
status: "ACTIVE",
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
interface PackOptions {
|
||||
apiaryId?: string;
|
||||
apiaryName?: string;
|
||||
colonies?: PackColony[];
|
||||
queens?: OfflinePackV1["queens"];
|
||||
inspections?: OfflinePackV1["inspections"];
|
||||
tasks?: OfflinePackV1["tasks"];
|
||||
inspectionsVisible?: boolean;
|
||||
}
|
||||
|
||||
/** A pack as the server sends it: colonies, their overview rows and the sections. */
|
||||
export function offlinePack({
|
||||
apiaryId = "apiary-1",
|
||||
apiaryName = "Obstwiese",
|
||||
colonies = [packColony("colony-1", "A1", { apiaryId })],
|
||||
queens = [],
|
||||
inspections = [],
|
||||
tasks = [],
|
||||
inspectionsVisible = true,
|
||||
}: PackOptions = {}): OfflinePackV1 {
|
||||
return {
|
||||
version: 1,
|
||||
generatedAt: "2026-10-13T08:00:00.000Z",
|
||||
apiary: { id: apiaryId, name: apiaryName, location: "Hinterm Hof", status: "ACTIVE" },
|
||||
overview: {
|
||||
apiaryId,
|
||||
inspectionsVisible,
|
||||
rows: colonies.map((colony) => overviewRow(colony)),
|
||||
},
|
||||
colonies,
|
||||
queens,
|
||||
inspections,
|
||||
tasks,
|
||||
};
|
||||
}
|
||||
|
||||
export function storedPack(
|
||||
pack: OfflinePackV1,
|
||||
{ ownerUserId = "user-1", savedAt = new Date().toISOString() } = {},
|
||||
): StoredPack {
|
||||
return { apiaryId: pack.apiary.id, ownerUserId, savedAt, pack };
|
||||
}
|
||||
Reference in new issue
Block a user