feat: extend offline queueing to all quick-capture forms

Only Durchsicht had an offline fallback (queue to IndexedDB, sync on
reconnect); Behandlung, Fütterung, Varroamessung, Schwarmkontrolle and
Ernte just failed outright with no signal - exactly the situation a
beekeeper is in at the actual hive. Generalizes lib/offline/db.ts and
sync.ts to six queues (version-bumped IndexedDB schema, existing queued
inspections preserved), wires the same "server rejected it vs. no
network" fallback into all five remaining */new pages, and updates the
sync badge to count/drain all six.

Also: doc-hygiene pass (ARCHITECTURE.md/TASKS.md) to replace the stale
pg_dump/CronJob backup plan with the in-app BackupModule that actually
shipped earlier this session.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
This commit is contained in:
Nick SzaunigandClaude Sonnet 5 committed 2026-09-05 17:45:32 +02:00
1 parent ac45514356
commit 1f67cd5548
11 files changed
+297 -46

No files matched your search

@@ -9,6 +9,8 @@ import { fetchColonies } from "@/lib/colonies/api";
import { createFeeding } from "@/lib/feedings/api";
import { FeedType } from "@/lib/feedings/types";
import { FEED_TYPES, FEED_TYPE_LABELS } from "@/lib/feedings/labels";
import { ApiError } from "@/lib/auth/api-client";
import { queueFeeding } from "@/lib/offline/db";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
@@ -59,8 +61,15 @@ export default function NewFeedingPage() {
toast.success("Fütterung gespeichert");
router.push(`/colonies/${feeding.colonyId}`);
},
onError: () => {
setError("Fütterung konnte nicht gespeichert werden.");
onError: (mutationError, variables) => {
if (mutationError instanceof ApiError) {
setError("Fütterung konnte nicht gespeichert werden.");
return;
}
void queueFeeding(variables).then(() => {
toast.info("Kein Netzwerk – Fütterung wurde offline gespeichert und wird synchronisiert.");
router.push(`/colonies/${variables.colonyId}`);
});
},
});
@@ -8,6 +8,8 @@ import { Boxes } from "lucide-react";
import { fetchApiaries } from "@/lib/apiaries/api";
import { fetchColonies } from "@/lib/colonies/api";
import { createHarvest } from "@/lib/harvests/api";
import { ApiError } from "@/lib/auth/api-client";
import { queueHarvest } from "@/lib/offline/db";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
@@ -53,8 +55,15 @@ export default function NewHarvestPage() {
toast.success("Ernte gespeichert");
router.push(`/harvests/${harvest.id}`);
},
onError: () => {
setError("Ernte konnte nicht gespeichert werden.");
onError: (mutationError, variables) => {
if (mutationError instanceof ApiError) {
setError("Ernte konnte nicht gespeichert werden.");
return;
}
void queueHarvest(variables).then(() => {
toast.info("Kein Netzwerk – Ernte wurde offline gespeichert und wird synchronisiert.");
router.push("/harvests");
});
},
});
@@ -9,6 +9,8 @@ import { fetchColonies } from "@/lib/colonies/api";
import { createSwarmControlCheck } from "@/lib/swarm-control/api";
import { SWARM_ACTION_LABELS, SWARM_MOOD_LABELS } from "@/lib/swarm-control/labels";
import { SwarmControlAction, SwarmMood } from "@/lib/swarm-control/types";
import { ApiError } from "@/lib/auth/api-client";
import { queueSwarmControlCheck } from "@/lib/offline/db";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
@@ -57,8 +59,15 @@ export default function NewSwarmControlCheckPage() {
toast.success("Schwarmkontrolle gespeichert");
router.push(`/colonies/${check.colonyId}`);
},
onError: () => {
setError("Schwarmkontrolle konnte nicht gespeichert werden.");
onError: (mutationError, variables) => {
if (mutationError instanceof ApiError) {
setError("Schwarmkontrolle konnte nicht gespeichert werden.");
return;
}
void queueSwarmControlCheck(variables).then(() => {
toast.info("Kein Netzwerk – Schwarmkontrolle wurde offline gespeichert und wird synchronisiert.");
router.push(`/colonies/${variables.colonyId}`);
});
},
});
@@ -7,6 +7,8 @@ import { toast } from "sonner";
import { Syringe } from "lucide-react";
import { fetchColonies } from "@/lib/colonies/api";
import { createTreatment } from "@/lib/treatments/api";
import { ApiError } from "@/lib/auth/api-client";
import { queueTreatment } from "@/lib/offline/db";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
@@ -54,8 +56,15 @@ export default function NewTreatmentPage() {
toast.success("Behandlung gespeichert");
router.push(`/colonies/${treatment.colonyId}`);
},
onError: () => {
setError("Behandlung konnte nicht gespeichert werden.");
onError: (mutationError, variables) => {
if (mutationError instanceof ApiError) {
setError("Behandlung konnte nicht gespeichert werden.");
return;
}
void queueTreatment(variables).then(() => {
toast.info("Kein Netzwerk – Behandlung wurde offline gespeichert und wird synchronisiert.");
router.push(`/colonies/${variables.colonyId}`);
});
},
});
@@ -9,6 +9,8 @@ import { fetchColonies } from "@/lib/colonies/api";
import { createVarroaMeasurement } from "@/lib/varroa/api";
import { VarroaMethod } from "@/lib/varroa/types";
import { VARROA_METHODS, VARROA_METHOD_LABELS } from "@/lib/varroa/labels";
import { ApiError } from "@/lib/auth/api-client";
import { queueVarroaMeasurement } from "@/lib/offline/db";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
@@ -56,8 +58,15 @@ export default function NewVarroaMeasurementPage() {
toast.success("Varroamessung gespeichert");
router.push(`/colonies/${measurement.colonyId}`);
},
onError: () => {
setError("Varroamessung konnte nicht gespeichert werden.");
onError: (mutationError, variables) => {
if (mutationError instanceof ApiError) {
setError("Varroamessung konnte nicht gespeichert werden.");
return;
}
void queueVarroaMeasurement(variables).then(() => {
toast.info("Kein Netzwerk – Varroamessung wurde offline gespeichert und wird synchronisiert.");
router.push(`/colonies/${variables.colonyId}`);
});
},
});