Files
ApiCore/ApiCore-Frontend/src/lib/bulk/use-apiary-colonies.ts
T
Nick SzaunigandClaude Opus 5.5 c2f71bcb03
Backend Image / build-and-push (push) Failing after 2m45s
Frontend Image / build-and-push (push) Successful in 5m52s
fix: T4a browser check findings
- Withdrawal wording from one backend helper everywhere ("bis
  einschließlich TT.MM. – ab TT.MM. wieder erntefähig", year when it
  crosses New Year); withdrawal-status returns lastBlockedDay and the
  ready text, the frontend no longer computes dates
- Calendar events name colony or apiary, withdrawal events carry
  product and detail; compact withdrawal rows on mobile
- One bottling warning per colony and treatment; pre-save hint
- Offline: online-only actions disabled with a reason, cached data
  shows its timestamp, open sheets block submit offline
- Mobile: no horizontal scroll at 360 px, touch targets >= 44 px,
  toasts and the add button no longer cover content
- Cleanup sheet without destructive preselection; natural sort of
  colony codes; collapsible automation rule cards; varroa list newest
  first with chart axes; text fixes

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
2026-10-14 18:26:09 +02:00

103 lines
3.5 KiB
TypeScript

"use client";
import { compareColonyCodes } from "@/lib/colonies/sort";
import { useQuery } from "@tanstack/react-query";
import { fetchApiaries } from "@/lib/apiaries/api";
import { livingColoniesFromPacks } from "@/lib/apiaries/offline-pack";
import { useCurrentUserId } from "@/lib/auth/auth-context";
import { fetchColonies } from "@/lib/colonies/api";
import { isLivingColony } from "@/lib/colonies/status";
import type { Colony } from "@/lib/colonies/types";
import { withPackFallback } from "@/lib/offline/pack-fallback";
/** What a bulk choice needs of a colony - a server colony or one from an offline pack. */
export type BulkColony = Pick<Colony, "id" | "code" | "status" | "apiaryId">;
export interface BulkApiary {
id: string;
name: string;
}
/** Where the choices came from: the server, or - only without network - the loaded packs. */
export type ChoiceSource = "server" | "packs";
interface Choices<T> {
items: T[];
source: ChoiceSource;
}
/** Prefix of the queries here; they read the server or this device's storage, never both as one. */
const BULK_CHOICES_KEY = ["bulk-choices"] as const;
function byCode(a: BulkColony, b: BulkColony): number {
return compareColonyCodes(a.code, b.code);
}
function livingOf(colonies: readonly BulkColony[]): BulkColony[] {
return colonies
.filter(isLivingColony)
.map(({ id, code, status, apiaryId }) => ({ id, code, status, apiaryId }))
.sort(byCode);
}
/**
* The apiaries to choose from: the server's active ones, or - only when the
* network failed - those of the user's offline packs. Names only: nothing
* else of an apiary (location!) is needed or kept.
*/
export function useApiaryChoices() {
const userId = useCurrentUserId();
return useQuery({
queryKey: [...BULK_CHOICES_KEY, userId, "apiaries"],
queryFn: (): Promise<Choices<BulkApiary>> =>
withPackFallback<Choices<BulkApiary>>(
async () => ({
source: "server",
items: (await fetchApiaries({ status: "ACTIVE" })).map(({ id, name }) => ({ id, name })),
}),
(packs) =>
packs.length === 0
? null
: {
source: "packs",
items: packs
.map(({ pack }) => ({ id: pack.apiary.id, name: pack.apiary.name }))
.sort((a, b) => a.name.localeCompare(b.name, "de")),
},
userId ?? "",
),
// Reads this device's storage when offline, never waits for the network.
networkMode: "always",
});
}
/**
* The living colonies of one apiary - ended ones never appear. Without
* network the apiary's offline pack answers; with no pack for it the
* request fails and the caller says so.
*/
export function useApiaryColonies(apiaryId: string | null) {
const userId = useCurrentUserId();
return useQuery({
queryKey: [...BULK_CHOICES_KEY, userId, "colonies", apiaryId],
queryFn: (): Promise<Choices<BulkColony>> =>
withPackFallback<Choices<BulkColony>>(
async () => ({
source: "server",
items: livingOf(await fetchColonies({ apiaryId: apiaryId ?? undefined })),
}),
(packs) =>
packs.some((stored) => stored.apiaryId === apiaryId)
? {
source: "packs",
items: livingOf(
livingColoniesFromPacks(packs).filter((colony) => colony.apiaryId === apiaryId),
),
}
: null,
userId ?? "",
),
enabled: apiaryId !== null && apiaryId !== "",
networkMode: "always",
});
}