fix: prevent new backend guards from ever surfacing as raw errors
The colony-apiary and queen-availability validation just added backend-side had no matching frontend guard, so a user could still pick an inactive apiary or open the assign-colony dialog on an unavailable queen and only find out it fails after submitting: - Colony create/move apiary pickers now fetch only ACTIVE apiaries. - The queen "Volk zuweisen" button is disabled once her status is LOST/REPLACED/SOLD/DECEASED (QUEEN_UNAVAILABLE_STATUSES, mirroring the backend's QUEEN_NO_LONGER_AVAILABLE_STATUSES), instead of only failing after the form is filled out and submitted. Co-Authored-By: Claude Sonnet 5 <[email protected]>
This commit is contained in:
1 parent
3e179391b0
commit
3678a01319
5 files changed
+37
-5
No files matched your search
@@ -48,7 +48,10 @@ function MoveDialog({ colony }: { colony: Colony }) {
|
|||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
const [apiaryId, setApiaryId] = useState("");
|
const [apiaryId, setApiaryId] = useState("");
|
||||||
const { data: apiaries } = useQuery({ queryKey: ["apiaries"], queryFn: () => fetchApiaries() });
|
const { data: apiaries } = useQuery({
|
||||||
|
queryKey: ["apiaries", { status: "ACTIVE" }],
|
||||||
|
queryFn: () => fetchApiaries({ status: "ACTIVE" }),
|
||||||
|
});
|
||||||
|
|
||||||
const mutation = useMutation({
|
const mutation = useMutation({
|
||||||
mutationFn: () => moveColony(colony.id, { apiaryId }),
|
mutationFn: () => moveColony(colony.id, { apiaryId }),
|
||||||
|
|||||||
@@ -24,7 +24,10 @@ import { Card, CardContent } from "@/components/ui/card";
|
|||||||
export default function NewColonyPage() {
|
export default function NewColonyPage() {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
const { data: apiaries } = useQuery({ queryKey: ["apiaries"], queryFn: () => fetchApiaries() });
|
const { data: apiaries } = useQuery({
|
||||||
|
queryKey: ["apiaries", { status: "ACTIVE" }],
|
||||||
|
queryFn: () => fetchApiaries({ status: "ACTIVE" }),
|
||||||
|
});
|
||||||
|
|
||||||
const [code, setCode] = useState("");
|
const [code, setCode] = useState("");
|
||||||
const [apiaryId, setApiaryId] = useState("");
|
const [apiaryId, setApiaryId] = useState("");
|
||||||
|
|||||||
@@ -3,6 +3,8 @@
|
|||||||
import { FormEvent, useState } from "react";
|
import { FormEvent, useState } from "react";
|
||||||
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
||||||
import { assignQueenColony } from "@/lib/queens/api";
|
import { assignQueenColony } from "@/lib/queens/api";
|
||||||
|
import { QUEEN_UNAVAILABLE_STATUSES } from "@/lib/queens/labels";
|
||||||
|
import { QueenStatus } from "@/lib/queens/types";
|
||||||
import { fetchColonies } from "@/lib/colonies/api";
|
import { fetchColonies } from "@/lib/colonies/api";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Label } from "@/components/ui/label";
|
import { Label } from "@/components/ui/label";
|
||||||
@@ -22,11 +24,18 @@ import {
|
|||||||
SelectValue,
|
SelectValue,
|
||||||
} from "@/components/ui/select";
|
} from "@/components/ui/select";
|
||||||
|
|
||||||
export function AssignColonyDialog({ queenId }: { queenId: string }) {
|
export function AssignColonyDialog({
|
||||||
|
queenId,
|
||||||
|
queenStatus,
|
||||||
|
}: {
|
||||||
|
queenId: string;
|
||||||
|
queenStatus: QueenStatus;
|
||||||
|
}) {
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
const [colonyId, setColonyId] = useState("");
|
const [colonyId, setColonyId] = useState("");
|
||||||
const { data: colonies } = useQuery({ queryKey: ["colonies"], queryFn: () => fetchColonies() });
|
const { data: colonies } = useQuery({ queryKey: ["colonies"], queryFn: () => fetchColonies() });
|
||||||
|
const unavailable = QUEEN_UNAVAILABLE_STATUSES.includes(queenStatus);
|
||||||
|
|
||||||
const mutation = useMutation({
|
const mutation = useMutation({
|
||||||
mutationFn: () => assignQueenColony(queenId, { colonyId }),
|
mutationFn: () => assignQueenColony(queenId, { colonyId }),
|
||||||
@@ -39,7 +48,13 @@ export function AssignColonyDialog({ queenId }: { queenId: string }) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog open={open} onOpenChange={setOpen}>
|
<Dialog open={open} onOpenChange={setOpen}>
|
||||||
<DialogTrigger render={<Button variant="outline">Volk zuweisen</Button>} />
|
<DialogTrigger
|
||||||
|
render={
|
||||||
|
<Button variant="outline" disabled={unavailable}>
|
||||||
|
Volk zuweisen
|
||||||
|
</Button>
|
||||||
|
}
|
||||||
|
/>
|
||||||
<DialogContent>
|
<DialogContent>
|
||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
<DialogTitle>Königin einem Volk zuweisen</DialogTitle>
|
<DialogTitle>Königin einem Volk zuweisen</DialogTitle>
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ export default function QueenDetailPage() {
|
|||||||
<div className="grid grid-cols-1 items-start gap-4 lg:grid-cols-3">
|
<div className="grid grid-cols-1 items-start gap-4 lg:grid-cols-3">
|
||||||
<div className="flex flex-col gap-4 lg:col-span-1">
|
<div className="flex flex-col gap-4 lg:col-span-1">
|
||||||
<QueenEditCard key={queen.id} queen={queen} />
|
<QueenEditCard key={queen.id} queen={queen} />
|
||||||
<AssignColonyDialog queenId={queen.id} />
|
<AssignColonyDialog queenId={queen.id} queenStatus={queen.status} />
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col gap-4 lg:col-span-2">
|
<div className="flex flex-col gap-4 lg:col-span-2">
|
||||||
<div className="grid grid-cols-1 gap-4 xl:grid-cols-2">
|
<div className="grid grid-cols-1 gap-4 xl:grid-cols-2">
|
||||||
|
|||||||
@@ -12,6 +12,17 @@ export const QUEEN_STATUSES: QueenStatus[] = [
|
|||||||
"DECEASED",
|
"DECEASED",
|
||||||
];
|
];
|
||||||
|
|
||||||
|
// Mirrors the backend's QUEEN_NO_LONGER_AVAILABLE_STATUSES - a queen in one
|
||||||
|
// of these statuses can no longer be assigned to a colony (the backend
|
||||||
|
// rejects it), so the assign-colony UI disables itself instead of letting
|
||||||
|
// the user hit that error after filling out the form.
|
||||||
|
export const QUEEN_UNAVAILABLE_STATUSES: QueenStatus[] = [
|
||||||
|
"LOST",
|
||||||
|
"REPLACED",
|
||||||
|
"SOLD",
|
||||||
|
"DECEASED",
|
||||||
|
];
|
||||||
|
|
||||||
export const QUEEN_STATUS_LABELS: Record<QueenStatus, string> = {
|
export const QUEEN_STATUS_LABELS: Record<QueenStatus, string> = {
|
||||||
ACTIVE: "Aktiv",
|
ACTIVE: "Aktiv",
|
||||||
RESERVE: "Reserve",
|
RESERVE: "Reserve",
|
||||||
|
|||||||
Reference in new issue
Block a user