diff --git a/ApiCore-Backend/src/apiaries/apiaries.service.ts b/ApiCore-Backend/src/apiaries/apiaries.service.ts index cdd4300..f838a0c 100644 --- a/ApiCore-Backend/src/apiaries/apiaries.service.ts +++ b/ApiCore-Backend/src/apiaries/apiaries.service.ts @@ -86,7 +86,7 @@ export class ApiariesService { }); if (activeColonyCount > 0) { throw new ConflictException( - `Apiary ${id} still has ${activeColonyCount} active colonies - move or dissolve them first`, + `Der Bienenstand hat noch ${activeColonyCount} aktive(s) Volk/Völker - zuerst verlegen oder auflösen`, ); } const apiary = await this.prisma.apiary.update({ diff --git a/ApiCore-Backend/src/colonies/colonies.service.ts b/ApiCore-Backend/src/colonies/colonies.service.ts index a70c579..dfbd0ae 100644 --- a/ApiCore-Backend/src/colonies/colonies.service.ts +++ b/ApiCore-Backend/src/colonies/colonies.service.ts @@ -46,7 +46,7 @@ export class ColoniesService { private assertActive(colony: Colony): void { if (colony.status !== 'ACTIVE') { throw new ConflictException( - `Colony ${colony.id} is not active (status: ${colony.status})`, + `Das Volk ist nicht aktiv (Status: ${colony.status})`, ); } } @@ -61,11 +61,11 @@ export class ColoniesService { where: { id: apiaryId }, }); if (!apiary) { - throw new NotFoundException(`Apiary ${apiaryId} not found`); + throw new NotFoundException('Bienenstand nicht gefunden'); } if (apiary.status !== 'ACTIVE') { throw new ConflictException( - `Apiary ${apiaryId} is not active (status: ${apiary.status})`, + 'Der Bienenstand ist nicht aktiv und kann daher keine Völker aufnehmen', ); } } @@ -190,7 +190,9 @@ export class ColoniesService { where: { id: mergedColonyId }, }); if (!merged) { - throw new NotFoundException(`Colony ${mergedColonyId} not found`); + throw new NotFoundException( + 'Zu vereinigendes Volk wurde nicht gefunden', + ); } this.assertActive(merged); // currentQueenId is unique across colonies - if it stayed set on diff --git a/ApiCore-Backend/src/queens/queens.service.ts b/ApiCore-Backend/src/queens/queens.service.ts index fe811d1..10ec9bb 100644 --- a/ApiCore-Backend/src/queens/queens.service.ts +++ b/ApiCore-Backend/src/queens/queens.service.ts @@ -157,7 +157,7 @@ export class QueensService { const queen = await this.findById(id); if (QUEEN_NO_LONGER_AVAILABLE_STATUSES.includes(queen.status)) { throw new ConflictException( - `Queen ${id} is not available for assignment (status: ${queen.status}) - correct her status first if this was a mistake`, + 'Die Königin ist nicht mehr verfügbar für eine Zuweisung - korrigiere zuerst ihren Status, falls dies ein Versehen war', ); } const colony = await this.prisma.colony.findUnique({ diff --git a/ApiCore-Backend/src/storage/storage.service.ts b/ApiCore-Backend/src/storage/storage.service.ts index 839fef5..2f3f812 100644 --- a/ApiCore-Backend/src/storage/storage.service.ts +++ b/ApiCore-Backend/src/storage/storage.service.ts @@ -134,7 +134,7 @@ export class StorageService { // that is the only way to correct a stock count downward. if (dto.type !== 'ADJUSTMENT' && dto.quantity <= 0) { throw new BadRequestException( - 'quantity must be positive for IN/OUT movements - the direction is determined by "type"', + 'Menge muss bei Eingang/Ausgang positiv sein - die Richtung wird durch den Bewegungstyp bestimmt', ); } const delta = diff --git a/ApiCore-Frontend/src/app/(app)/apiaries/[id]/apiary-edit-card.tsx b/ApiCore-Frontend/src/app/(app)/apiaries/[id]/apiary-edit-card.tsx index 77e21cf..c8aa620 100644 --- a/ApiCore-Frontend/src/app/(app)/apiaries/[id]/apiary-edit-card.tsx +++ b/ApiCore-Frontend/src/app/(app)/apiaries/[id]/apiary-edit-card.tsx @@ -5,6 +5,7 @@ import { MapPin } from "lucide-react"; import { useMutation, useQueryClient } from "@tanstack/react-query"; import { toast } from "sonner"; import { deactivateApiary, reactivateApiary, updateApiary } from "@/lib/apiaries/api"; +import { ApiError } from "@/lib/auth/api-client"; import { Apiary } from "@/lib/apiaries/types"; import { Button } from "@/components/ui/button"; import { Badge } from "@/components/ui/badge"; @@ -54,7 +55,10 @@ export function ApiaryEditCard({ apiary }: { apiary: Apiary }) { description: "Er kann jederzeit über „Reaktivieren“ wiederhergestellt werden.", }); }, - onError: () => toast.error("Bienenstand konnte nicht deaktiviert werden"), + onError: (error) => + toast.error( + error instanceof ApiError ? error.message : "Bienenstand konnte nicht deaktiviert werden", + ), }); const reactivateMutation = useMutation({ diff --git a/ApiCore-Frontend/src/app/(app)/colonies/[id]/colony-actions.tsx b/ApiCore-Frontend/src/app/(app)/colonies/[id]/colony-actions.tsx index dcfda3a..44811ac 100644 --- a/ApiCore-Frontend/src/app/(app)/colonies/[id]/colony-actions.tsx +++ b/ApiCore-Frontend/src/app/(app)/colonies/[id]/colony-actions.tsx @@ -3,6 +3,7 @@ import { FormEvent, useState } from "react"; import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { toast } from "sonner"; +import { ApiError } from "@/lib/auth/api-client"; import { dissolveColony, mergeColony, @@ -35,6 +36,10 @@ import { SelectValue, } from "@/components/ui/select"; +function errorMessage(error: unknown, fallback: string): string { + return error instanceof ApiError ? error.message : fallback; +} + function invalidateColony( queryClient: ReturnType, colonyId: string, @@ -59,6 +64,8 @@ function MoveDialog({ colony }: { colony: Colony }) { invalidateColony(queryClient, colony.id); setOpen(false); }, + onError: (error) => + toast.error(errorMessage(error, "Volk konnte nicht umgesetzt werden")), }); return ( @@ -122,6 +129,8 @@ function SplitDialog({ colony }: { colony: Colony }) { void queryClient.invalidateQueries({ queryKey: ["colonies"] }); setOpen(false); }, + onError: (error) => + toast.error(errorMessage(error, "Ableger konnte nicht angelegt werden")), }); return ( @@ -196,6 +205,8 @@ function AssignQueenDialog({ colony }: { colony: Colony }) { void queryClient.invalidateQueries({ queryKey: ["queens"] }); setOpen(false); }, + onError: (error) => + toast.error(errorMessage(error, "Königin konnte nicht zugewiesen werden")), }); return ( @@ -259,6 +270,8 @@ function MergeDialog({ colony }: { colony: Colony }) { void queryClient.invalidateQueries({ queryKey: ["colonies"] }); setOpen(false); }, + onError: (error) => + toast.error(errorMessage(error, "Völker konnten nicht vereinigt werden")), }); return ( diff --git a/ApiCore-Frontend/src/app/(app)/queens/[id]/assign-colony-dialog.tsx b/ApiCore-Frontend/src/app/(app)/queens/[id]/assign-colony-dialog.tsx index c652cc5..a4c9b22 100644 --- a/ApiCore-Frontend/src/app/(app)/queens/[id]/assign-colony-dialog.tsx +++ b/ApiCore-Frontend/src/app/(app)/queens/[id]/assign-colony-dialog.tsx @@ -4,6 +4,7 @@ import { FormEvent, useState } from "react"; import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { toast } from "sonner"; import { assignQueenColony } from "@/lib/queens/api"; +import { ApiError } from "@/lib/auth/api-client"; import { QUEEN_UNAVAILABLE_STATUSES } from "@/lib/queens/labels"; import { QueenStatus } from "@/lib/queens/types"; import { fetchColonies } from "@/lib/colonies/api"; @@ -45,7 +46,10 @@ export function AssignColonyDialog({ void queryClient.invalidateQueries({ queryKey: ["queens", queenId, "history"] }); setOpen(false); }, - onError: () => toast.error("Volk konnte nicht zugewiesen werden"), + onError: (error) => + toast.error( + error instanceof ApiError ? error.message : "Volk konnte nicht zugewiesen werden", + ), }); return ( diff --git a/ApiCore-Frontend/src/app/(app)/storage/record-movement-dialog.tsx b/ApiCore-Frontend/src/app/(app)/storage/record-movement-dialog.tsx index 9e4783d..f98739b 100644 --- a/ApiCore-Frontend/src/app/(app)/storage/record-movement-dialog.tsx +++ b/ApiCore-Frontend/src/app/(app)/storage/record-movement-dialog.tsx @@ -3,6 +3,7 @@ import { FormEvent, useState } from "react"; import { useMutation, useQueryClient } from "@tanstack/react-query"; import { addStockMovement } from "@/lib/storage/api"; +import { ApiError } from "@/lib/auth/api-client"; import { StockMovementType } from "@/lib/storage/types"; import { STOCK_MOVEMENT_TYPES, STOCK_MOVEMENT_TYPE_LABELS } from "@/lib/storage/labels"; import { Button } from "@/components/ui/button"; @@ -40,8 +41,10 @@ export function RecordMovementDialog({ stockItemId }: { stockItemId: string }) { setQuantity(""); setError(null); }, - onError: () => { - setError("Bewegung konnte nicht gespeichert werden."); + onError: (error) => { + setError( + error instanceof ApiError ? error.message : "Bewegung konnte nicht gespeichert werden.", + ); }, });