Volk Überarbeittung und Englisch Varaiblen ersetzt
Backend Image / build-and-push (push) Successful in 3m43s
Frontend Image / build-and-push (push) Failing after 1m10s

This commit is contained in:
Nick Szaunig committed 2026-08-27 10:38:06 +02:00
1 parent 9282777ac9
commit 8520678a48
11 files changed
+231 -104

No files matched your search

@@ -11,6 +11,7 @@ import {
splitColony,
} from "@/lib/colonies/api";
import { Colony, ColonyType } from "@/lib/colonies/types";
import { COLONY_TYPES, COLONY_TYPE_LABELS } from "@/lib/colonies/labels";
import { fetchApiaries } from "@/lib/apiaries/api";
import { fetchColonies } from "@/lib/colonies/api";
import { Button } from "@/components/ui/button";
@@ -33,15 +34,6 @@ import {
SelectValue,
} from "@/components/ui/select";
const COLONY_TYPES: ColonyType[] = [
"PRODUCTION_COLONY",
"NUCLEUS_COLONY",
"YOUNG_COLONY",
"CARETAKER_COLONY",
"MATING_NUCLEUS",
"RESERVE_COLONY",
];
function invalidateColony(
queryClient: ReturnType<typeof useQueryClient>,
colonyId: string,
@@ -169,7 +161,7 @@ function SplitDialog({ colony }: { colony: Colony }) {
<SelectContent>
{COLONY_TYPES.map((type) => (
<SelectItem key={type} value={type}>
{type}
{COLONY_TYPE_LABELS[type]}
</SelectItem>
))}
</SelectContent>
@@ -4,13 +4,28 @@ import { FormEvent, useState } from "react";
import { Layers } from "lucide-react";
import { useMutation, useQueryClient } from "@tanstack/react-query";
import { updateColony } from "@/lib/colonies/api";
import { Colony } from "@/lib/colonies/types";
import { Colony, ColonyCondition, ColonyType } from "@/lib/colonies/types";
import {
COLONY_CONDITIONS,
COLONY_CONDITION_LABELS,
COLONY_CONDITION_TONE_CLASS,
COLONY_STATUS_LABELS,
COLONY_STRENGTH_OPTIONS,
COLONY_TYPES,
COLONY_TYPE_LABELS,
} from "@/lib/colonies/labels";
import { statusTone } from "@/lib/status-tone";
import { Button } from "@/components/ui/button";
import { Badge } from "@/components/ui/badge";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Textarea } from "@/components/ui/textarea";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import {
Card,
CardContent,
@@ -18,21 +33,67 @@ import {
CardTitle,
} from "@/components/ui/card";
function ConditionSelect({
id,
label,
value,
onChange,
}: {
id: string;
label: string;
value: ColonyCondition | undefined;
onChange: (value: ColonyCondition) => void;
}) {
return (
<div className="flex flex-col gap-2">
<Label htmlFor={id}>{label}</Label>
<Select value={value} onValueChange={(next) => onChange(next as ColonyCondition)}>
<SelectTrigger id={id}>
<SelectValue placeholder="Nicht erfasst">
{value ? (
<Badge className={COLONY_CONDITION_TONE_CLASS[value]}>
{COLONY_CONDITION_LABELS[value]}
</Badge>
) : undefined}
</SelectValue>
</SelectTrigger>
<SelectContent>
{COLONY_CONDITIONS.map((condition) => (
<SelectItem key={condition} value={condition}>
<Badge className={COLONY_CONDITION_TONE_CLASS[condition]}>
{COLONY_CONDITION_LABELS[condition]}
</Badge>
</SelectItem>
))}
</SelectContent>
</Select>
</div>
);
}
export function ColonyEditCard({ colony }: { colony: Colony }) {
const queryClient = useQueryClient();
const [colonyType, setColonyType] = useState<ColonyType>(colony.colonyType);
const [strength, setStrength] = useState(colony.strength?.toString() ?? "");
const [broodStatus, setBroodStatus] = useState(colony.broodStatus ?? "");
const [feedStatus, setFeedStatus] = useState(colony.feedStatus ?? "");
const [healthStatus, setHealthStatus] = useState(colony.healthStatus ?? "");
const [broodStatus, setBroodStatus] = useState<ColonyCondition | undefined>(
colony.broodStatus ?? undefined,
);
const [feedStatus, setFeedStatus] = useState<ColonyCondition | undefined>(
colony.feedStatus ?? undefined,
);
const [healthStatus, setHealthStatus] = useState<ColonyCondition | undefined>(
colony.healthStatus ?? undefined,
);
const [notes, setNotes] = useState(colony.notes ?? "");
const updateMutation = useMutation({
mutationFn: () =>
updateColony(colony.id, {
colonyType,
strength: strength ? Number(strength) : undefined,
broodStatus: broodStatus || undefined,
feedStatus: feedStatus || undefined,
healthStatus: healthStatus || undefined,
broodStatus,
feedStatus,
healthStatus,
notes: notes || undefined,
}),
onSuccess: () => {
@@ -58,43 +119,61 @@ export function ColonyEditCard({ colony }: { colony: Colony }) {
</span>
<CardTitle className="font-mono">{colony.code}</CardTitle>
</div>
<Badge variant={statusTone(colony.status)}>{colony.status}</Badge>
<Badge variant={statusTone(colony.status)}>{COLONY_STATUS_LABELS[colony.status]}</Badge>
</CardHeader>
<CardContent>
<form className="flex flex-col gap-4" onSubmit={handleSubmit}>
<div className="flex flex-col gap-2">
<Label htmlFor="colony-type">Volktyp</Label>
<Select
value={colonyType}
onValueChange={(value) => setColonyType(value as ColonyType)}
>
<SelectTrigger id="colony-type">
<SelectValue />
</SelectTrigger>
<SelectContent>
{COLONY_TYPES.map((type) => (
<SelectItem key={type} value={type}>
{COLONY_TYPE_LABELS[type]}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
<div className="flex flex-col gap-2">
<Label htmlFor="colony-strength">Volksstärke</Label>
<Input
id="colony-strength"
type="number"
value={strength}
onChange={(event) => setStrength(event.target.value)}
/>
</div>
<div className="flex flex-col gap-2">
<Label htmlFor="colony-brood">Brutstatus</Label>
<Input
id="colony-brood"
value={broodStatus}
onChange={(event) => setBroodStatus(event.target.value)}
/>
</div>
<div className="flex flex-col gap-2">
<Label htmlFor="colony-feed">Futterstatus</Label>
<Input
id="colony-feed"
value={feedStatus}
onChange={(event) => setFeedStatus(event.target.value)}
/>
</div>
<div className="flex flex-col gap-2">
<Label htmlFor="colony-health">Gesundheitsstatus</Label>
<Input
id="colony-health"
value={healthStatus}
onChange={(event) => setHealthStatus(event.target.value)}
/>
<Select value={strength} onValueChange={(value) => setStrength(value ?? "")}>
<SelectTrigger id="colony-strength">
<SelectValue placeholder="Nicht erfasst" />
</SelectTrigger>
<SelectContent>
{COLONY_STRENGTH_OPTIONS.map((option) => (
<SelectItem key={option} value={option.toString()}>
{option}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
<ConditionSelect
id="colony-brood"
label="Brutstatus"
value={broodStatus}
onChange={setBroodStatus}
/>
<ConditionSelect
id="colony-feed"
label="Futterstatus"
value={feedStatus}
onChange={setFeedStatus}
/>
<ConditionSelect
id="colony-health"
label="Gesundheitsstatus"
value={healthStatus}
onChange={setHealthStatus}
/>
<div className="flex flex-col gap-2">
<Label htmlFor="colony-notes">Notizen</Label>
<Textarea
@@ -5,6 +5,7 @@ import { useRouter } from "next/navigation";
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { createColony } from "@/lib/colonies/api";
import { ColonyType } from "@/lib/colonies/types";
import { COLONY_TYPES, COLONY_TYPE_LABELS } from "@/lib/colonies/labels";
import { Layers } from "lucide-react";
import { fetchApiaries } from "@/lib/apiaries/api";
import { Button } from "@/components/ui/button";
@@ -20,15 +21,6 @@ import {
import { PageHeader } from "@/components/page-header";
import { Card, CardContent } from "@/components/ui/card";
const COLONY_TYPES: ColonyType[] = [
"PRODUCTION_COLONY",
"NUCLEUS_COLONY",
"YOUNG_COLONY",
"CARETAKER_COLONY",
"MATING_NUCLEUS",
"RESERVE_COLONY",
];
export default function NewColonyPage() {
const router = useRouter();
const queryClient = useQueryClient();
@@ -106,7 +98,7 @@ export default function NewColonyPage() {
<SelectContent>
{COLONY_TYPES.map((type) => (
<SelectItem key={type} value={type}>
{type}
{COLONY_TYPE_LABELS[type]}
</SelectItem>
))}
</SelectContent>
@@ -44,6 +44,6 @@ describe("ColoniesPage", () => {
renderWithClient(<ColoniesPage />);
expect(await screen.findByText("V-001")).toBeInTheDocument();
expect(screen.getByText("PRODUCTION_COLONY")).toBeInTheDocument();
expect(screen.getByText("Wirtschaftsvolk")).toBeInTheDocument();
});
});
@@ -6,6 +6,7 @@ import { Layers } from "lucide-react";
import { useMutation, useQuery } from "@tanstack/react-query";
import { fetchColonies } from "@/lib/colonies/api";
import { ColonyStatus } from "@/lib/colonies/types";
import { COLONY_CONDITION_LABELS, COLONY_STATUS_LABELS, COLONY_TYPE_LABELS } from "@/lib/colonies/labels";
import { exportColoniesCsv } from "@/lib/export/api";
import { statusTone } from "@/lib/status-tone";
import { Button } from "@/components/ui/button";
@@ -79,13 +80,15 @@ export default function ColoniesPage() {
onValueChange={(value) => setStatus(value === "ALL" ? "" : ((value ?? "") as ColonyStatus))}
>
<SelectTrigger className="w-40">
<SelectValue placeholder="Status">{status || "Alle Status"}</SelectValue>
<SelectValue placeholder="Status">
{status ? COLONY_STATUS_LABELS[status] : "Alle Status"}
</SelectValue>
</SelectTrigger>
<SelectContent>
<SelectItem value="ALL">Alle Status</SelectItem>
{statusOptions.map((option) => (
<SelectItem key={option} value={option}>
{option}
{COLONY_STATUS_LABELS[option]}
</SelectItem>
))}
</SelectContent>
@@ -119,11 +122,18 @@ export default function ColoniesPage() {
href={`/colonies/${colony.id}`}
icon={Layers}
title={<span className="font-mono">{colony.code}</span>}
subtitle={colony.colonyType}
badge={<Badge variant={statusTone(colony.status)}>{colony.status}</Badge>}
subtitle={COLONY_TYPE_LABELS[colony.colonyType]}
badge={
<Badge variant={statusTone(colony.status)}>
{COLONY_STATUS_LABELS[colony.status]}
</Badge>
}
fields={[
{ label: "Stärke", value: colony.strength ?? "—" },
{ label: "Gesundheit", value: colony.healthStatus ?? "—" },
{
label: "Gesundheit",
value: colony.healthStatus ? COLONY_CONDITION_LABELS[colony.healthStatus] : "—",
},
]}
/>
))}
+30
View File
@@ -31,6 +31,16 @@
--color-warning-foreground: var(--warning-foreground);
--color-success: var(--success);
--color-success-foreground: var(--success-foreground);
--color-condition-excellent: var(--condition-excellent);
--color-condition-excellent-foreground: var(--condition-excellent-foreground);
--color-condition-good: var(--condition-good);
--color-condition-good-foreground: var(--condition-good-foreground);
--color-condition-average: var(--condition-average);
--color-condition-average-foreground: var(--condition-average-foreground);
--color-condition-weak: var(--condition-weak);
--color-condition-weak-foreground: var(--condition-weak-foreground);
--color-condition-critical: var(--condition-critical);
--color-condition-critical-foreground: var(--condition-critical-foreground);
--color-accent-foreground: var(--accent-foreground);
--color-accent: var(--accent);
--color-muted-foreground: var(--muted-foreground);
@@ -87,6 +97,16 @@
--warning-foreground: oklch(0.24 0.04 60);
--success: oklch(0.46 0.06 145);
--success-foreground: oklch(0.98 0.015 100);
--condition-excellent: oklch(0.58 0.13 145);
--condition-excellent-foreground: oklch(0.24 0.05 145);
--condition-good: oklch(0.64 0.13 118);
--condition-good-foreground: oklch(0.26 0.05 118);
--condition-average: oklch(0.7 0.14 90);
--condition-average-foreground: oklch(0.28 0.05 90);
--condition-weak: oklch(0.68 0.15 55);
--condition-weak-foreground: oklch(0.26 0.05 55);
--condition-critical: oklch(0.55 0.16 30);
--condition-critical-foreground: oklch(0.24 0.05 30);
--chart-1: oklch(0.6 0.135 50);
--chart-2: oklch(0.46 0.06 145);
--chart-3: oklch(0.68 0.14 75);
@@ -131,6 +151,16 @@
--warning-foreground: oklch(0.2 0.03 60);
--success: oklch(0.62 0.075 145);
--success-foreground: oklch(0.15 0.02 145);
--condition-excellent: oklch(0.66 0.11 145);
--condition-excellent-foreground: oklch(0.2 0.04 145);
--condition-good: oklch(0.72 0.12 118);
--condition-good-foreground: oklch(0.22 0.04 118);
--condition-average: oklch(0.76 0.13 90);
--condition-average-foreground: oklch(0.24 0.04 90);
--condition-weak: oklch(0.74 0.13 55);
--condition-weak-foreground: oklch(0.22 0.04 55);
--condition-critical: oklch(0.62 0.16 30);
--condition-critical-foreground: oklch(0.2 0.04 30);
--chart-1: oklch(0.72 0.13 55);
--chart-2: oklch(0.62 0.075 145);
--chart-3: oklch(0.74 0.13 75);
+8 -6
View File
@@ -16,6 +16,8 @@ export type ColonyStatus =
| "DECEASED"
| "MERGED";
export type ColonyCondition = "EXCELLENT" | "GOOD" | "AVERAGE" | "WEAK" | "CRITICAL";
export interface Colony {
id: string;
code: string;
@@ -25,9 +27,9 @@ export interface Colony {
colonyType: ColonyType;
status: ColonyStatus;
strength: number | null;
broodStatus: string | null;
feedStatus: string | null;
healthStatus: string | null;
broodStatus: ColonyCondition | null;
feedStatus: ColonyCondition | null;
healthStatus: ColonyCondition | null;
notes: string | null;
currentQueenId: string | null;
createdAt: string;
@@ -49,9 +51,9 @@ export interface UpdateColonyInput {
originText?: string;
colonyType?: ColonyType;
strength?: number;
broodStatus?: string;
feedStatus?: string;
healthStatus?: string;
broodStatus?: ColonyCondition;
feedStatus?: ColonyCondition;
healthStatus?: ColonyCondition;
notes?: string;
status?: ColonyStatus;
}