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,54 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect, useState } from "react";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { useConnectivity } from "@/lib/offline/connectivity";
|
||||
import { fetchTreatmentPrefill } from "./api";
|
||||
import type { TreatmentPrefill } from "./types";
|
||||
|
||||
const DEBOUNCE_MS = 500;
|
||||
const MIN_PRODUCT_LENGTH = 2;
|
||||
const MAX_PRODUCT_LENGTH = 200;
|
||||
|
||||
/**
|
||||
* The beekeeper's own last values for the product typed so far. Waits until
|
||||
* typing pauses, needs at least two characters and asks only while online -
|
||||
* a prefill is a convenience, never a reason to block or queue anything.
|
||||
* While a request for the same product is pending or the device is offline,
|
||||
* the last answer for that product stays; for any other product it is dropped.
|
||||
*/
|
||||
export function useTreatmentPrefill(product: string): TreatmentPrefill | null {
|
||||
const trimmed = product.trim();
|
||||
const [debounced, setDebounced] = useState("");
|
||||
const connectivity = useConnectivity();
|
||||
|
||||
useEffect(() => {
|
||||
const timer = setTimeout(() => setDebounced(trimmed), DEBOUNCE_MS);
|
||||
return () => clearTimeout(timer);
|
||||
}, [trimmed]);
|
||||
|
||||
const isSettled = debounced === trimmed;
|
||||
const isAskable =
|
||||
trimmed.length >= MIN_PRODUCT_LENGTH && trimmed.length <= MAX_PRODUCT_LENGTH;
|
||||
const enabled = connectivity === "online" && isSettled && isAskable;
|
||||
const { data } = useQuery({
|
||||
queryKey: ["treatments", "prefill", debounced],
|
||||
queryFn: () => fetchTreatmentPrefill(debounced),
|
||||
enabled,
|
||||
});
|
||||
|
||||
// The last settled answer is kept only for the product it was fetched for
|
||||
// (pending request, offline). Once the product differs it is dropped at
|
||||
// once, so a failed request can never leave another product's values.
|
||||
const [held, setHeld] = useState<{ product: string; prefill: TreatmentPrefill | null } | null>(
|
||||
null,
|
||||
);
|
||||
let settled: TreatmentPrefill | null | undefined;
|
||||
if (isSettled && !isAskable) settled = null;
|
||||
else if (enabled) settled = data;
|
||||
if (settled !== undefined && (held?.product !== trimmed || held.prefill !== settled)) {
|
||||
setHeld({ product: trimmed, prefill: settled });
|
||||
}
|
||||
if (settled !== undefined) return settled;
|
||||
return held?.product === trimmed ? held.prefill : null;
|
||||
}
|
||||
Reference in new issue
Block a user