feat: platform backbone and field capture, first part (checkpoint)
Platform backbone (plan 2026-10-13-plattform-rueckgrat), reviewed tasks: - run roles api/worker/job/all with shared startup checks (Task 1) - schema and migration for DomainEvent, deliveries, JobRun, ThrottlerHit (Task 2) - audit, notifications and domain events written in the same transaction (Task 3) - dispatcher with SKIP LOCKED claims, retries and stale release (Task 4) - worker building blocks: tick, LISTEN/NOTIFY wake-up, health (Task 5) - worker process and entry points (Task 6) - throttler counters in Postgres (Task 19) - separate keys per purpose with key ring (Task 20) - public origin page without exact location (Task 22) - shop keys split into read and write (Task 23) - Kustomize base, components, overlay encry, CI validation (Task 26) Field capture T3 (plan 2026-10-13-t3-erfassung-am-stand), reviewed tasks: - brood frames, read endpoints, apiary overview and offline pack - network basics, offline queue v3, navigation sheets, form building blocks - new inspection with last-time hints and weather, varroa per day, treatment prefill, apiary page with round - inspection photo (Task 10) is implemented, review pending Not rolled out. Remaining tasks follow in later commits. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
1 parent
10cbe9bf2d
commit
c01a38835a
340 files changed
+25695
-1808
No files matched your search
@@ -0,0 +1,11 @@
|
||||
import { apiFetch } from "@/lib/auth/api-client";
|
||||
import type { ColonyOverview } from "./field-types";
|
||||
|
||||
export function fetchColonyOverview(apiaryId: string): Promise<ColonyOverview> {
|
||||
return apiFetch<ColonyOverview>(`/apiaries/${encodeURIComponent(apiaryId)}/colony-overview`);
|
||||
}
|
||||
|
||||
/** Query key of the apiary page's colony overview. */
|
||||
export function colonyOverviewKey(apiaryId: string): readonly ["apiaries", string, "colony-overview"] {
|
||||
return ["apiaries", apiaryId, "colony-overview"] as const;
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
import type { ApiaryStatus } from "./types";
|
||||
import type { Colony, ColonyCondition, ColonyStatus } from "@/lib/colonies/types";
|
||||
import type { Inspection } from "@/lib/inspections/types";
|
||||
import type { Queen } from "@/lib/queens/types";
|
||||
import type { Task } from "@/lib/tasks/types";
|
||||
|
||||
// Mirrors ApiCore-Backend/src/apiaries/apiary-field.types.ts - keep both in step.
|
||||
|
||||
/** One living colony on the apiary page, reduced to what the traffic light needs. */
|
||||
export interface ColonyOverviewRow {
|
||||
id: string;
|
||||
code: string;
|
||||
status: ColonyStatus;
|
||||
strength: number | null;
|
||||
broodStatus: ColonyCondition | null;
|
||||
feedStatus: ColonyCondition | null;
|
||||
healthStatus: ColonyCondition | null;
|
||||
/** ISO timestamp; always null when the caller may not read inspections. */
|
||||
lastInspectionDate: string | null;
|
||||
/** Unread WARNUNG notifications visible to the caller. */
|
||||
openWarnings: number;
|
||||
/** Unread KRITISCH notifications visible to the caller. */
|
||||
openCritical: number;
|
||||
}
|
||||
|
||||
export interface ColonyOverview {
|
||||
apiaryId: string;
|
||||
inspectionsVisible: boolean;
|
||||
rows: ColonyOverviewRow[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Everything the offline apiary mode needs. It deliberately carries no
|
||||
* coordinates, address or apiary notes (apiary locations are theft-sensitive)
|
||||
* and no inspection userId. Sections the caller has no read permission for
|
||||
* are null.
|
||||
*/
|
||||
export interface OfflinePackV1 {
|
||||
version: 1;
|
||||
generatedAt: string;
|
||||
apiary: {
|
||||
id: string;
|
||||
name: string;
|
||||
location: string | null;
|
||||
status: ApiaryStatus;
|
||||
};
|
||||
overview: ColonyOverview;
|
||||
colonies: Pick<
|
||||
Colony,
|
||||
| "id"
|
||||
| "code"
|
||||
| "apiaryId"
|
||||
| "status"
|
||||
| "colonyType"
|
||||
| "hiveType"
|
||||
| "strength"
|
||||
| "broodStatus"
|
||||
| "feedStatus"
|
||||
| "healthStatus"
|
||||
| "currentQueenId"
|
||||
| "notes"
|
||||
>[];
|
||||
queens:
|
||||
| Pick<
|
||||
Queen,
|
||||
"id" | "code" | "hatchYear" | "breed" | "markingColor" | "markingNumber" | "status"
|
||||
>[]
|
||||
| null;
|
||||
inspections: Omit<Inspection, "userId">[] | null;
|
||||
tasks:
|
||||
| Pick<
|
||||
Task,
|
||||
| "id"
|
||||
| "title"
|
||||
| "description"
|
||||
| "dueDate"
|
||||
| "priority"
|
||||
| "status"
|
||||
| "relatedType"
|
||||
| "relatedId"
|
||||
>[]
|
||||
| null;
|
||||
}
|
||||
@@ -10,6 +10,8 @@ export interface Apiary {
|
||||
address: string | null;
|
||||
status: ApiaryStatus;
|
||||
notes: string | null;
|
||||
/** Shown publicly on the origin page instead of the exact location. */
|
||||
publicLocationLabel: string | null;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
deactivatedAt: string | null;
|
||||
@@ -24,6 +26,7 @@ export interface ApiaryInput {
|
||||
longitude?: number | null;
|
||||
address?: string | null;
|
||||
notes?: string | null;
|
||||
publicLocationLabel?: string | null;
|
||||
}
|
||||
|
||||
/** `null` clears an optional field, an omitted one stays unchanged. */
|
||||
@@ -35,4 +38,5 @@ export interface UpdateApiaryInput {
|
||||
longitude?: number | null;
|
||||
address?: string | null;
|
||||
notes?: string | null;
|
||||
publicLocationLabel?: string | null;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
"use client";
|
||||
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { colonyOverviewKey, fetchColonyOverview } from "./field-api";
|
||||
|
||||
/** The apiary's living colonies with their traffic-light inputs. */
|
||||
export function useColonyOverview(apiaryId: string, enabled = true) {
|
||||
return useQuery({
|
||||
queryKey: colonyOverviewKey(apiaryId),
|
||||
queryFn: () => fetchColonyOverview(apiaryId),
|
||||
enabled,
|
||||
});
|
||||
}
|
||||
Reference in new issue
Block a user