fix: expose every backend-supported field in all edit forms
Audit requested by the user after finding queens couldn't set hatchYear: many edit dialogs (and some create forms) were missing fields the backend DTOs already accept, so that data could never be entered or corrected anywhere in the UI. Fixes per entity: - Colony: status (WEAK/QUEENLESS/CONSPICUOUS) was never settable anywhere, so the COLONY_ATTENTION dashboard alert could never fire. Added as a dropdown restricted to the non-terminal statuses; DISSOLVED/SOLD/DECEASED/MERGED stay on their dedicated confirm actions, which also set deactivatedAt. - Apiary: description was missing from the edit form. - Inspection: 10 fields (weather, temperature, broodAmount, broodPattern, openBroodPresent, cappedBroodPresent, queenCellsPresent, swarmMood, gentleness, combBehavior, honeySuperStatus) existed in the schema/DTOs but nowhere in the UI - added to both the create and edit forms. - Varroa measurement: durationMinutes was on the create form but missing from edit. - Feeding: batchNumber missing from both create and edit. - Treatment: startDate, endDate, activeIngredient, batchNumber missing from both create and edit. - Task: assignedUserId had no UI anywhere; added to create/edit, degrading gracefully if the current user's role lacks users:read. - Extraction (honey-batch traceability variant): amount was missing, while the other edit dialog for the same entity already had it. - Bottling: productId missing from edit. - Harvest: grossWeight missing from edit; grossWeight and honeySuperCount missing from create. - Queen: breeder, matingType, matingStatus, inseminationData, motherLine, fatherLine added to the edit form (hatchYear/ markingColor/markingNumber were already fixed in a prior commit). Left alone deliberately: fields the backend DTO itself excludes via PickType (e.g. HoneyBatch's jarSize/jarCount/totalAmount, Bottling's quantities) since mutating those post-hoc would desync inventory deduction, and Colony's terminal statuses/code which have dedicated lifecycle actions. Co-Authored-By: Claude Sonnet 5 <[email protected]>
This commit is contained in:
1 parent
792806b967
commit
1dddc387ba
20 files changed
+682
-4
No files matched your search
@@ -22,6 +22,7 @@ import {
|
|||||||
export function ApiaryEditCard({ apiary }: { apiary: Apiary }) {
|
export function ApiaryEditCard({ apiary }: { apiary: Apiary }) {
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
const [name, setName] = useState(apiary.name);
|
const [name, setName] = useState(apiary.name);
|
||||||
|
const [description, setDescription] = useState(apiary.description ?? "");
|
||||||
const [location, setLocation] = useState(apiary.location ?? "");
|
const [location, setLocation] = useState(apiary.location ?? "");
|
||||||
const [latitude, setLatitude] = useState(apiary.latitude?.toString() ?? "");
|
const [latitude, setLatitude] = useState(apiary.latitude?.toString() ?? "");
|
||||||
const [longitude, setLongitude] = useState(apiary.longitude?.toString() ?? "");
|
const [longitude, setLongitude] = useState(apiary.longitude?.toString() ?? "");
|
||||||
@@ -32,6 +33,7 @@ export function ApiaryEditCard({ apiary }: { apiary: Apiary }) {
|
|||||||
mutationFn: () =>
|
mutationFn: () =>
|
||||||
updateApiary(apiary.id, {
|
updateApiary(apiary.id, {
|
||||||
name,
|
name,
|
||||||
|
description: description || undefined,
|
||||||
location: location || undefined,
|
location: location || undefined,
|
||||||
latitude: latitude ? Number(latitude) : undefined,
|
latitude: latitude ? Number(latitude) : undefined,
|
||||||
longitude: longitude ? Number(longitude) : undefined,
|
longitude: longitude ? Number(longitude) : undefined,
|
||||||
@@ -95,6 +97,14 @@ export function ApiaryEditCard({ apiary }: { apiary: Apiary }) {
|
|||||||
onChange={(event) => setName(event.target.value)}
|
onChange={(event) => setName(event.target.value)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="flex flex-col gap-2">
|
||||||
|
<Label htmlFor="apiary-description">Beschreibung</Label>
|
||||||
|
<Textarea
|
||||||
|
id="apiary-description"
|
||||||
|
value={description}
|
||||||
|
onChange={(event) => setDescription(event.target.value)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
<Label htmlFor="apiary-location">Standort</Label>
|
<Label htmlFor="apiary-location">Standort</Label>
|
||||||
<Input
|
<Input
|
||||||
|
|||||||
@@ -2,12 +2,20 @@
|
|||||||
|
|
||||||
import { FormEvent, useState } from "react";
|
import { FormEvent, useState } from "react";
|
||||||
import { Pencil } from "lucide-react";
|
import { Pencil } from "lucide-react";
|
||||||
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
||||||
import { updateBottlingBatch } from "@/lib/bottling/api";
|
import { updateBottlingBatch } from "@/lib/bottling/api";
|
||||||
import { BottlingBatch } from "@/lib/bottling/types";
|
import { BottlingBatch } from "@/lib/bottling/types";
|
||||||
|
import { fetchProducts } from "@/lib/products/api";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import { Label } from "@/components/ui/label";
|
import { Label } from "@/components/ui/label";
|
||||||
|
import {
|
||||||
|
Select,
|
||||||
|
SelectContent,
|
||||||
|
SelectItem,
|
||||||
|
SelectTrigger,
|
||||||
|
SelectValue,
|
||||||
|
} from "@/components/ui/select";
|
||||||
import {
|
import {
|
||||||
Dialog,
|
Dialog,
|
||||||
DialogContent,
|
DialogContent,
|
||||||
@@ -24,6 +32,12 @@ export function BottlingEditDialog({ bottlingBatch }: { bottlingBatch: BottlingB
|
|||||||
bottlingBatch.bestBeforeDate.slice(0, 10),
|
bottlingBatch.bestBeforeDate.slice(0, 10),
|
||||||
);
|
);
|
||||||
const [lotNumber, setLotNumber] = useState(bottlingBatch.lotNumber);
|
const [lotNumber, setLotNumber] = useState(bottlingBatch.lotNumber);
|
||||||
|
const [productId, setProductId] = useState(bottlingBatch.productId ?? "");
|
||||||
|
|
||||||
|
const { data: products } = useQuery({
|
||||||
|
queryKey: ["products", { status: "ACTIVE" }],
|
||||||
|
queryFn: () => fetchProducts({ status: "ACTIVE" }),
|
||||||
|
});
|
||||||
|
|
||||||
const mutation = useMutation({
|
const mutation = useMutation({
|
||||||
mutationFn: () =>
|
mutationFn: () =>
|
||||||
@@ -31,6 +45,7 @@ export function BottlingEditDialog({ bottlingBatch }: { bottlingBatch: BottlingB
|
|||||||
date: new Date(date).toISOString(),
|
date: new Date(date).toISOString(),
|
||||||
bestBeforeDate: new Date(bestBeforeDate).toISOString(),
|
bestBeforeDate: new Date(bestBeforeDate).toISOString(),
|
||||||
lotNumber,
|
lotNumber,
|
||||||
|
productId: productId || undefined,
|
||||||
}),
|
}),
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
void queryClient.invalidateQueries({
|
void queryClient.invalidateQueries({
|
||||||
@@ -78,6 +93,21 @@ export function BottlingEditDialog({ bottlingBatch }: { bottlingBatch: BottlingB
|
|||||||
onChange={(event) => setBestBeforeDate(event.target.value)}
|
onChange={(event) => setBestBeforeDate(event.target.value)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="flex flex-col gap-2">
|
||||||
|
<Label htmlFor="bottling-edit-product">Produkt</Label>
|
||||||
|
<Select value={productId} onValueChange={(value) => setProductId(value ?? "")}>
|
||||||
|
<SelectTrigger id="bottling-edit-product">
|
||||||
|
<SelectValue placeholder="Produkt wählen" />
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent>
|
||||||
|
{products?.map((product) => (
|
||||||
|
<SelectItem key={product.id} value={product.id}>
|
||||||
|
{product.name}
|
||||||
|
</SelectItem>
|
||||||
|
))}
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
</div>
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
<Label htmlFor="bottling-edit-lot">Los-/Chargennummer</Label>
|
<Label htmlFor="bottling-edit-lot">Los-/Chargennummer</Label>
|
||||||
<Input
|
<Input
|
||||||
|
|||||||
@@ -4,11 +4,12 @@ import { FormEvent, useState } from "react";
|
|||||||
import { Layers } from "lucide-react";
|
import { Layers } from "lucide-react";
|
||||||
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
||||||
import { updateColony } from "@/lib/colonies/api";
|
import { updateColony } from "@/lib/colonies/api";
|
||||||
import { Colony, ColonyCondition, ColonyType } from "@/lib/colonies/types";
|
import { Colony, ColonyCondition, ColonyStatus, ColonyType } from "@/lib/colonies/types";
|
||||||
import {
|
import {
|
||||||
COLONY_CONDITIONS,
|
COLONY_CONDITIONS,
|
||||||
COLONY_CONDITION_LABELS,
|
COLONY_CONDITION_LABELS,
|
||||||
COLONY_CONDITION_TONE_CLASS,
|
COLONY_CONDITION_TONE_CLASS,
|
||||||
|
COLONY_EDITABLE_STATUSES,
|
||||||
COLONY_STATUS_LABELS,
|
COLONY_STATUS_LABELS,
|
||||||
COLONY_STRENGTH_OPTIONS,
|
COLONY_STRENGTH_OPTIONS,
|
||||||
COLONY_TYPES,
|
COLONY_TYPES,
|
||||||
@@ -74,6 +75,7 @@ function ConditionSelect({
|
|||||||
|
|
||||||
export function ColonyEditCard({ colony }: { colony: Colony }) {
|
export function ColonyEditCard({ colony }: { colony: Colony }) {
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
|
const [status, setStatus] = useState<ColonyStatus>(colony.status);
|
||||||
const [colonyType, setColonyType] = useState<ColonyType>(colony.colonyType);
|
const [colonyType, setColonyType] = useState<ColonyType>(colony.colonyType);
|
||||||
const [hiveType, setHiveType] = useState(colony.hiveType ?? "");
|
const [hiveType, setHiveType] = useState(colony.hiveType ?? "");
|
||||||
const [originText, setOriginText] = useState(colony.originText ?? "");
|
const [originText, setOriginText] = useState(colony.originText ?? "");
|
||||||
@@ -92,6 +94,7 @@ export function ColonyEditCard({ colony }: { colony: Colony }) {
|
|||||||
const updateMutation = useMutation({
|
const updateMutation = useMutation({
|
||||||
mutationFn: () =>
|
mutationFn: () =>
|
||||||
updateColony(colony.id, {
|
updateColony(colony.id, {
|
||||||
|
status,
|
||||||
colonyType,
|
colonyType,
|
||||||
hiveType: hiveType || undefined,
|
hiveType: hiveType || undefined,
|
||||||
originText: originText || undefined,
|
originText: originText || undefined,
|
||||||
@@ -128,6 +131,21 @@ export function ColonyEditCard({ colony }: { colony: Colony }) {
|
|||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
<form className="flex flex-col gap-4" onSubmit={handleSubmit}>
|
<form className="flex flex-col gap-4" onSubmit={handleSubmit}>
|
||||||
|
<div className="flex flex-col gap-2">
|
||||||
|
<Label htmlFor="colony-status">Status</Label>
|
||||||
|
<Select value={status} onValueChange={(value) => setStatus(value as ColonyStatus)}>
|
||||||
|
<SelectTrigger id="colony-status">
|
||||||
|
<SelectValue>{COLONY_STATUS_LABELS[status]}</SelectValue>
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent>
|
||||||
|
{COLONY_EDITABLE_STATUSES.map((option) => (
|
||||||
|
<SelectItem key={option} value={option}>
|
||||||
|
{COLONY_STATUS_LABELS[option]}
|
||||||
|
</SelectItem>
|
||||||
|
))}
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
</div>
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
<Label htmlFor="colony-type">Volktyp</Label>
|
<Label htmlFor="colony-type">Volktyp</Label>
|
||||||
<Select
|
<Select
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ export function FeedingEditDialog({ feeding }: { feeding: Feeding }) {
|
|||||||
const [product, setProduct] = useState(feeding.product ?? "");
|
const [product, setProduct] = useState(feeding.product ?? "");
|
||||||
const [amount, setAmount] = useState(feeding.amount.toString());
|
const [amount, setAmount] = useState(feeding.amount.toString());
|
||||||
const [unit, setUnit] = useState(feeding.unit);
|
const [unit, setUnit] = useState(feeding.unit);
|
||||||
|
const [batchNumber, setBatchNumber] = useState(feeding.batchNumber ?? "");
|
||||||
const [notes, setNotes] = useState(feeding.notes ?? "");
|
const [notes, setNotes] = useState(feeding.notes ?? "");
|
||||||
|
|
||||||
const mutation = useMutation({
|
const mutation = useMutation({
|
||||||
@@ -43,6 +44,7 @@ export function FeedingEditDialog({ feeding }: { feeding: Feeding }) {
|
|||||||
product: product || undefined,
|
product: product || undefined,
|
||||||
amount: amount ? Number(amount) : undefined,
|
amount: amount ? Number(amount) : undefined,
|
||||||
unit,
|
unit,
|
||||||
|
batchNumber: batchNumber || undefined,
|
||||||
notes: notes || undefined,
|
notes: notes || undefined,
|
||||||
}),
|
}),
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
@@ -127,6 +129,14 @@ export function FeedingEditDialog({ feeding }: { feeding: Feeding }) {
|
|||||||
onChange={(event) => setProduct(event.target.value)}
|
onChange={(event) => setProduct(event.target.value)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="flex flex-col gap-2">
|
||||||
|
<Label htmlFor="feeding-edit-batch">Chargennummer</Label>
|
||||||
|
<Input
|
||||||
|
id="feeding-edit-batch"
|
||||||
|
value={batchNumber}
|
||||||
|
onChange={(event) => setBatchNumber(event.target.value)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
<Label htmlFor="feeding-edit-notes">Notizen</Label>
|
<Label htmlFor="feeding-edit-notes">Notizen</Label>
|
||||||
<Textarea
|
<Textarea
|
||||||
|
|||||||
@@ -34,12 +34,27 @@ export function InspectionEditDialog({ inspection }: { inspection: Inspection })
|
|||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
const [date, setDate] = useState(inspection.date.slice(0, 10));
|
const [date, setDate] = useState(inspection.date.slice(0, 10));
|
||||||
|
const [weather, setWeather] = useState(inspection.weather ?? "");
|
||||||
|
const [temperature, setTemperature] = useState(inspection.temperature?.toString() ?? "");
|
||||||
const [colonyStrength, setColonyStrength] = useState(
|
const [colonyStrength, setColonyStrength] = useState(
|
||||||
inspection.colonyStrength?.toString() ?? "",
|
inspection.colonyStrength?.toString() ?? "",
|
||||||
);
|
);
|
||||||
|
const [broodAmount, setBroodAmount] = useState(inspection.broodAmount ?? "");
|
||||||
|
const [broodPattern, setBroodPattern] = useState(inspection.broodPattern?.toString() ?? "");
|
||||||
|
const [openBroodPresent, setOpenBroodPresent] = useState(inspection.openBroodPresent ?? false);
|
||||||
|
const [cappedBroodPresent, setCappedBroodPresent] = useState(
|
||||||
|
inspection.cappedBroodPresent ?? false,
|
||||||
|
);
|
||||||
const [queenSeen, setQueenSeen] = useState(inspection.queenSeen ?? false);
|
const [queenSeen, setQueenSeen] = useState(inspection.queenSeen ?? false);
|
||||||
const [eggsPresent, setEggsPresent] = useState(inspection.eggsPresent ?? true);
|
const [eggsPresent, setEggsPresent] = useState(inspection.eggsPresent ?? true);
|
||||||
|
const [queenCellsPresent, setQueenCellsPresent] = useState(
|
||||||
|
inspection.queenCellsPresent ?? false,
|
||||||
|
);
|
||||||
|
const [swarmMood, setSwarmMood] = useState(inspection.swarmMood?.toString() ?? "");
|
||||||
|
const [gentleness, setGentleness] = useState(inspection.gentleness?.toString() ?? "");
|
||||||
|
const [combBehavior, setCombBehavior] = useState(inspection.combBehavior?.toString() ?? "");
|
||||||
const [feedStatus, setFeedStatus] = useState(inspection.feedStatus ?? "ausreichend");
|
const [feedStatus, setFeedStatus] = useState(inspection.feedStatus ?? "ausreichend");
|
||||||
|
const [honeySuperStatus, setHoneySuperStatus] = useState(inspection.honeySuperStatus ?? "");
|
||||||
const [healthIssues, setHealthIssues] = useState(inspection.healthIssues ?? "");
|
const [healthIssues, setHealthIssues] = useState(inspection.healthIssues ?? "");
|
||||||
const [varroaIssues, setVarroaIssues] = useState(inspection.varroaIssues ?? "");
|
const [varroaIssues, setVarroaIssues] = useState(inspection.varroaIssues ?? "");
|
||||||
const [measures, setMeasures] = useState(inspection.measures ?? "");
|
const [measures, setMeasures] = useState(inspection.measures ?? "");
|
||||||
@@ -49,10 +64,21 @@ export function InspectionEditDialog({ inspection }: { inspection: Inspection })
|
|||||||
mutationFn: () =>
|
mutationFn: () =>
|
||||||
updateInspection(inspection.id, {
|
updateInspection(inspection.id, {
|
||||||
date: new Date(date).toISOString(),
|
date: new Date(date).toISOString(),
|
||||||
|
weather: weather || undefined,
|
||||||
|
temperature: temperature ? Number(temperature) : undefined,
|
||||||
colonyStrength: colonyStrength ? Number(colonyStrength) : undefined,
|
colonyStrength: colonyStrength ? Number(colonyStrength) : undefined,
|
||||||
|
broodAmount: broodAmount || undefined,
|
||||||
|
broodPattern: broodPattern ? Number(broodPattern) : undefined,
|
||||||
|
openBroodPresent,
|
||||||
|
cappedBroodPresent,
|
||||||
queenSeen,
|
queenSeen,
|
||||||
eggsPresent,
|
eggsPresent,
|
||||||
|
queenCellsPresent,
|
||||||
|
swarmMood: swarmMood ? Number(swarmMood) : undefined,
|
||||||
|
gentleness: gentleness ? Number(gentleness) : undefined,
|
||||||
|
combBehavior: combBehavior ? Number(combBehavior) : undefined,
|
||||||
feedStatus,
|
feedStatus,
|
||||||
|
honeySuperStatus: honeySuperStatus || undefined,
|
||||||
healthIssues: healthIssues || undefined,
|
healthIssues: healthIssues || undefined,
|
||||||
varroaIssues: varroaIssues || undefined,
|
varroaIssues: varroaIssues || undefined,
|
||||||
measures: measures || undefined,
|
measures: measures || undefined,
|
||||||
@@ -95,6 +121,24 @@ export function InspectionEditDialog({ inspection }: { inspection: Inspection })
|
|||||||
onChange={(event) => setDate(event.target.value)}
|
onChange={(event) => setDate(event.target.value)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="flex flex-col gap-2">
|
||||||
|
<Label htmlFor="inspection-edit-weather">Wetter</Label>
|
||||||
|
<Input
|
||||||
|
id="inspection-edit-weather"
|
||||||
|
value={weather}
|
||||||
|
onChange={(event) => setWeather(event.target.value)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col gap-2">
|
||||||
|
<Label htmlFor="inspection-edit-temperature">Temperatur (°C)</Label>
|
||||||
|
<Input
|
||||||
|
id="inspection-edit-temperature"
|
||||||
|
type="number"
|
||||||
|
inputMode="decimal"
|
||||||
|
value={temperature}
|
||||||
|
onChange={(event) => setTemperature(event.target.value)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
<Label htmlFor="inspection-edit-strength">Volksstärke (Waben)</Label>
|
<Label htmlFor="inspection-edit-strength">Volksstärke (Waben)</Label>
|
||||||
<Input
|
<Input
|
||||||
@@ -104,6 +148,48 @@ export function InspectionEditDialog({ inspection }: { inspection: Inspection })
|
|||||||
onChange={(event) => setColonyStrength(event.target.value)}
|
onChange={(event) => setColonyStrength(event.target.value)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="flex flex-col gap-2">
|
||||||
|
<Label htmlFor="inspection-edit-brood-amount">Brutmenge</Label>
|
||||||
|
<Input
|
||||||
|
id="inspection-edit-brood-amount"
|
||||||
|
value={broodAmount}
|
||||||
|
onChange={(event) => setBroodAmount(event.target.value)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col gap-2">
|
||||||
|
<Label htmlFor="inspection-edit-brood-pattern">Brutbild (1–5)</Label>
|
||||||
|
<Input
|
||||||
|
id="inspection-edit-brood-pattern"
|
||||||
|
type="number"
|
||||||
|
min={1}
|
||||||
|
max={5}
|
||||||
|
value={broodPattern}
|
||||||
|
onChange={(event) => setBroodPattern(event.target.value)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<label className="flex items-center gap-3 text-sm" htmlFor="inspection-edit-open-brood">
|
||||||
|
<input
|
||||||
|
id="inspection-edit-open-brood"
|
||||||
|
type="checkbox"
|
||||||
|
className="size-5"
|
||||||
|
checked={openBroodPresent}
|
||||||
|
onChange={(event) => setOpenBroodPresent(event.target.checked)}
|
||||||
|
/>
|
||||||
|
Offene Brut vorhanden
|
||||||
|
</label>
|
||||||
|
<label
|
||||||
|
className="flex items-center gap-3 text-sm"
|
||||||
|
htmlFor="inspection-edit-capped-brood"
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
id="inspection-edit-capped-brood"
|
||||||
|
type="checkbox"
|
||||||
|
className="size-5"
|
||||||
|
checked={cappedBroodPresent}
|
||||||
|
onChange={(event) => setCappedBroodPresent(event.target.checked)}
|
||||||
|
/>
|
||||||
|
Verdeckelte Brut vorhanden
|
||||||
|
</label>
|
||||||
<label className="flex items-center gap-3 text-sm" htmlFor="inspection-edit-queen">
|
<label className="flex items-center gap-3 text-sm" htmlFor="inspection-edit-queen">
|
||||||
<input
|
<input
|
||||||
id="inspection-edit-queen"
|
id="inspection-edit-queen"
|
||||||
@@ -124,6 +210,52 @@ export function InspectionEditDialog({ inspection }: { inspection: Inspection })
|
|||||||
/>
|
/>
|
||||||
Stifte vorhanden
|
Stifte vorhanden
|
||||||
</label>
|
</label>
|
||||||
|
<label
|
||||||
|
className="flex items-center gap-3 text-sm"
|
||||||
|
htmlFor="inspection-edit-queen-cells"
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
id="inspection-edit-queen-cells"
|
||||||
|
type="checkbox"
|
||||||
|
className="size-5"
|
||||||
|
checked={queenCellsPresent}
|
||||||
|
onChange={(event) => setQueenCellsPresent(event.target.checked)}
|
||||||
|
/>
|
||||||
|
Weiselzellen vorhanden
|
||||||
|
</label>
|
||||||
|
<div className="flex flex-col gap-2">
|
||||||
|
<Label htmlFor="inspection-edit-swarm-mood">Schwarmstimmung (1–5)</Label>
|
||||||
|
<Input
|
||||||
|
id="inspection-edit-swarm-mood"
|
||||||
|
type="number"
|
||||||
|
min={1}
|
||||||
|
max={5}
|
||||||
|
value={swarmMood}
|
||||||
|
onChange={(event) => setSwarmMood(event.target.value)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col gap-2">
|
||||||
|
<Label htmlFor="inspection-edit-gentleness">Sanftmut (1–5)</Label>
|
||||||
|
<Input
|
||||||
|
id="inspection-edit-gentleness"
|
||||||
|
type="number"
|
||||||
|
min={1}
|
||||||
|
max={5}
|
||||||
|
value={gentleness}
|
||||||
|
onChange={(event) => setGentleness(event.target.value)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col gap-2">
|
||||||
|
<Label htmlFor="inspection-edit-comb-behavior">Wabensitz (1–5)</Label>
|
||||||
|
<Input
|
||||||
|
id="inspection-edit-comb-behavior"
|
||||||
|
type="number"
|
||||||
|
min={1}
|
||||||
|
max={5}
|
||||||
|
value={combBehavior}
|
||||||
|
onChange={(event) => setCombBehavior(event.target.value)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
<Label htmlFor="inspection-edit-feed">Futterstatus</Label>
|
<Label htmlFor="inspection-edit-feed">Futterstatus</Label>
|
||||||
<Select
|
<Select
|
||||||
@@ -140,6 +272,14 @@ export function InspectionEditDialog({ inspection }: { inspection: Inspection })
|
|||||||
</SelectContent>
|
</SelectContent>
|
||||||
</Select>
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="flex flex-col gap-2">
|
||||||
|
<Label htmlFor="inspection-edit-honey-super">Honigraumstatus</Label>
|
||||||
|
<Input
|
||||||
|
id="inspection-edit-honey-super"
|
||||||
|
value={honeySuperStatus}
|
||||||
|
onChange={(event) => setHoneySuperStatus(event.target.value)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
<Label htmlFor="inspection-edit-varroa">Varroa-Befund</Label>
|
<Label htmlFor="inspection-edit-varroa">Varroa-Befund</Label>
|
||||||
<Input
|
<Input
|
||||||
|
|||||||
@@ -21,10 +21,14 @@ export function TreatmentEditDialog({ treatment }: { treatment: Treatment }) {
|
|||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
const [date, setDate] = useState(treatment.date.slice(0, 10));
|
const [date, setDate] = useState(treatment.date.slice(0, 10));
|
||||||
|
const [startDate, setStartDate] = useState(treatment.startDate?.slice(0, 10) ?? "");
|
||||||
|
const [endDate, setEndDate] = useState(treatment.endDate?.slice(0, 10) ?? "");
|
||||||
const [product, setProduct] = useState(treatment.product);
|
const [product, setProduct] = useState(treatment.product);
|
||||||
|
const [activeIngredient, setActiveIngredient] = useState(treatment.activeIngredient ?? "");
|
||||||
const [amount, setAmount] = useState(treatment.amount ?? "");
|
const [amount, setAmount] = useState(treatment.amount ?? "");
|
||||||
const [method, setMethod] = useState(treatment.method ?? "");
|
const [method, setMethod] = useState(treatment.method ?? "");
|
||||||
const [reason, setReason] = useState(treatment.reason ?? "");
|
const [reason, setReason] = useState(treatment.reason ?? "");
|
||||||
|
const [batchNumber, setBatchNumber] = useState(treatment.batchNumber ?? "");
|
||||||
const [notes, setNotes] = useState(treatment.notes ?? "");
|
const [notes, setNotes] = useState(treatment.notes ?? "");
|
||||||
const [withdrawalPeriodDays, setWithdrawalPeriodDays] = useState(
|
const [withdrawalPeriodDays, setWithdrawalPeriodDays] = useState(
|
||||||
treatment.withdrawalPeriodDays?.toString() ?? "",
|
treatment.withdrawalPeriodDays?.toString() ?? "",
|
||||||
@@ -34,10 +38,14 @@ export function TreatmentEditDialog({ treatment }: { treatment: Treatment }) {
|
|||||||
mutationFn: () =>
|
mutationFn: () =>
|
||||||
updateTreatment(treatment.id, {
|
updateTreatment(treatment.id, {
|
||||||
date: new Date(date).toISOString(),
|
date: new Date(date).toISOString(),
|
||||||
|
startDate: startDate ? new Date(startDate).toISOString() : undefined,
|
||||||
|
endDate: endDate ? new Date(endDate).toISOString() : undefined,
|
||||||
product,
|
product,
|
||||||
|
activeIngredient: activeIngredient || undefined,
|
||||||
amount: amount || undefined,
|
amount: amount || undefined,
|
||||||
method: method || undefined,
|
method: method || undefined,
|
||||||
reason: reason || undefined,
|
reason: reason || undefined,
|
||||||
|
batchNumber: batchNumber || undefined,
|
||||||
notes: notes || undefined,
|
notes: notes || undefined,
|
||||||
withdrawalPeriodDays: withdrawalPeriodDays ? Number(withdrawalPeriodDays) : undefined,
|
withdrawalPeriodDays: withdrawalPeriodDays ? Number(withdrawalPeriodDays) : undefined,
|
||||||
}),
|
}),
|
||||||
@@ -78,6 +86,24 @@ export function TreatmentEditDialog({ treatment }: { treatment: Treatment }) {
|
|||||||
onChange={(event) => setDate(event.target.value)}
|
onChange={(event) => setDate(event.target.value)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="flex flex-col gap-2">
|
||||||
|
<Label htmlFor="treatment-edit-start-date">Beginn</Label>
|
||||||
|
<Input
|
||||||
|
id="treatment-edit-start-date"
|
||||||
|
type="date"
|
||||||
|
value={startDate}
|
||||||
|
onChange={(event) => setStartDate(event.target.value)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col gap-2">
|
||||||
|
<Label htmlFor="treatment-edit-end-date">Ende</Label>
|
||||||
|
<Input
|
||||||
|
id="treatment-edit-end-date"
|
||||||
|
type="date"
|
||||||
|
value={endDate}
|
||||||
|
onChange={(event) => setEndDate(event.target.value)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
<Label htmlFor="treatment-edit-product">Mittel</Label>
|
<Label htmlFor="treatment-edit-product">Mittel</Label>
|
||||||
<Input
|
<Input
|
||||||
@@ -87,6 +113,14 @@ export function TreatmentEditDialog({ treatment }: { treatment: Treatment }) {
|
|||||||
onChange={(event) => setProduct(event.target.value)}
|
onChange={(event) => setProduct(event.target.value)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="flex flex-col gap-2">
|
||||||
|
<Label htmlFor="treatment-edit-active-ingredient">Wirkstoff</Label>
|
||||||
|
<Input
|
||||||
|
id="treatment-edit-active-ingredient"
|
||||||
|
value={activeIngredient}
|
||||||
|
onChange={(event) => setActiveIngredient(event.target.value)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
<Label htmlFor="treatment-edit-amount">Menge</Label>
|
<Label htmlFor="treatment-edit-amount">Menge</Label>
|
||||||
<Input
|
<Input
|
||||||
@@ -111,6 +145,14 @@ export function TreatmentEditDialog({ treatment }: { treatment: Treatment }) {
|
|||||||
onChange={(event) => setReason(event.target.value)}
|
onChange={(event) => setReason(event.target.value)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="flex flex-col gap-2">
|
||||||
|
<Label htmlFor="treatment-edit-batch">Chargennummer</Label>
|
||||||
|
<Input
|
||||||
|
id="treatment-edit-batch"
|
||||||
|
value={batchNumber}
|
||||||
|
onChange={(event) => setBatchNumber(event.target.value)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
<Label htmlFor="treatment-edit-withdrawal">Wartezeit (Tage)</Label>
|
<Label htmlFor="treatment-edit-withdrawal">Wartezeit (Tage)</Label>
|
||||||
<Input
|
<Input
|
||||||
|
|||||||
@@ -31,6 +31,9 @@ export function VarroaEditDialog({ measurement }: { measurement: VarroaMeasureme
|
|||||||
const [date, setDate] = useState(measurement.date.slice(0, 10));
|
const [date, setDate] = useState(measurement.date.slice(0, 10));
|
||||||
const [method, setMethod] = useState<VarroaMethod>(measurement.method);
|
const [method, setMethod] = useState<VarroaMethod>(measurement.method);
|
||||||
const [miteCount, setMiteCount] = useState(measurement.miteCount?.toString() ?? "");
|
const [miteCount, setMiteCount] = useState(measurement.miteCount?.toString() ?? "");
|
||||||
|
const [durationMinutes, setDurationMinutes] = useState(
|
||||||
|
measurement.durationMinutes?.toString() ?? "",
|
||||||
|
);
|
||||||
const [calculatedValue, setCalculatedValue] = useState(
|
const [calculatedValue, setCalculatedValue] = useState(
|
||||||
measurement.calculatedValue?.toString() ?? "",
|
measurement.calculatedValue?.toString() ?? "",
|
||||||
);
|
);
|
||||||
@@ -43,6 +46,7 @@ export function VarroaEditDialog({ measurement }: { measurement: VarroaMeasureme
|
|||||||
date: new Date(date).toISOString(),
|
date: new Date(date).toISOString(),
|
||||||
method,
|
method,
|
||||||
miteCount: miteCount ? Number(miteCount) : undefined,
|
miteCount: miteCount ? Number(miteCount) : undefined,
|
||||||
|
durationMinutes: durationMinutes ? Number(durationMinutes) : undefined,
|
||||||
calculatedValue: calculatedValue ? Number(calculatedValue) : undefined,
|
calculatedValue: calculatedValue ? Number(calculatedValue) : undefined,
|
||||||
assessment: assessment || undefined,
|
assessment: assessment || undefined,
|
||||||
notes: notes || undefined,
|
notes: notes || undefined,
|
||||||
@@ -111,6 +115,15 @@ export function VarroaEditDialog({ measurement }: { measurement: VarroaMeasureme
|
|||||||
onChange={(event) => setMiteCount(event.target.value)}
|
onChange={(event) => setMiteCount(event.target.value)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="flex flex-col gap-2">
|
||||||
|
<Label htmlFor="varroa-edit-duration">Zeitraum (Minuten)</Label>
|
||||||
|
<Input
|
||||||
|
id="varroa-edit-duration"
|
||||||
|
type="number"
|
||||||
|
value={durationMinutes}
|
||||||
|
onChange={(event) => setDurationMinutes(event.target.value)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
<Label htmlFor="varroa-edit-calculated">Milben pro Tag</Label>
|
<Label htmlFor="varroa-edit-calculated">Milben pro Tag</Label>
|
||||||
<Input
|
<Input
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ export default function NewFeedingPage() {
|
|||||||
const [amount, setAmount] = useState("");
|
const [amount, setAmount] = useState("");
|
||||||
const [unit, setUnit] = useState("l");
|
const [unit, setUnit] = useState("l");
|
||||||
const [product, setProduct] = useState("");
|
const [product, setProduct] = useState("");
|
||||||
|
const [batchNumber, setBatchNumber] = useState("");
|
||||||
const [notes, setNotes] = useState("");
|
const [notes, setNotes] = useState("");
|
||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null);
|
||||||
|
|
||||||
@@ -91,6 +92,7 @@ export default function NewFeedingPage() {
|
|||||||
amount: Number(amount),
|
amount: Number(amount),
|
||||||
unit,
|
unit,
|
||||||
product: product || undefined,
|
product: product || undefined,
|
||||||
|
batchNumber: batchNumber || undefined,
|
||||||
notes: notes || undefined,
|
notes: notes || undefined,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -182,6 +184,15 @@ export default function NewFeedingPage() {
|
|||||||
onChange={(event) => setProduct(event.target.value)}
|
onChange={(event) => setProduct(event.target.value)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="flex flex-col gap-2">
|
||||||
|
<Label htmlFor="feeding-batch">Chargennummer</Label>
|
||||||
|
<Input
|
||||||
|
id="feeding-batch"
|
||||||
|
className="h-11"
|
||||||
|
value={batchNumber}
|
||||||
|
onChange={(event) => setBatchNumber(event.target.value)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
<Label htmlFor="feeding-notes">Notizen</Label>
|
<Label htmlFor="feeding-notes">Notizen</Label>
|
||||||
<Textarea
|
<Textarea
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ export function HarvestEditDialog({ harvest }: { harvest: Harvest }) {
|
|||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
const [date, setDate] = useState(harvest.date.slice(0, 10));
|
const [date, setDate] = useState(harvest.date.slice(0, 10));
|
||||||
const [honeyType, setHoneyType] = useState(harvest.honeyType ?? "");
|
const [honeyType, setHoneyType] = useState(harvest.honeyType ?? "");
|
||||||
|
const [grossWeight, setGrossWeight] = useState(harvest.grossWeight?.toString() ?? "");
|
||||||
const [netWeight, setNetWeight] = useState(harvest.netWeight?.toString() ?? "");
|
const [netWeight, setNetWeight] = useState(harvest.netWeight?.toString() ?? "");
|
||||||
const [honeySuperCount, setHoneySuperCount] = useState(
|
const [honeySuperCount, setHoneySuperCount] = useState(
|
||||||
harvest.honeySuperCount?.toString() ?? "",
|
harvest.honeySuperCount?.toString() ?? "",
|
||||||
@@ -33,6 +34,7 @@ export function HarvestEditDialog({ harvest }: { harvest: Harvest }) {
|
|||||||
updateHarvest(harvest.id, {
|
updateHarvest(harvest.id, {
|
||||||
date: new Date(date).toISOString(),
|
date: new Date(date).toISOString(),
|
||||||
honeyType: honeyType || undefined,
|
honeyType: honeyType || undefined,
|
||||||
|
grossWeight: grossWeight ? Number(grossWeight) : undefined,
|
||||||
netWeight: netWeight ? Number(netWeight) : undefined,
|
netWeight: netWeight ? Number(netWeight) : undefined,
|
||||||
honeySuperCount: honeySuperCount ? Number(honeySuperCount) : undefined,
|
honeySuperCount: honeySuperCount ? Number(honeySuperCount) : undefined,
|
||||||
notes: notes || undefined,
|
notes: notes || undefined,
|
||||||
@@ -81,6 +83,15 @@ export function HarvestEditDialog({ harvest }: { harvest: Harvest }) {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex gap-3">
|
<div className="flex gap-3">
|
||||||
|
<div className="flex flex-1 flex-col gap-2">
|
||||||
|
<Label htmlFor="harvest-edit-gross-weight">Bruttogewicht (kg)</Label>
|
||||||
|
<Input
|
||||||
|
id="harvest-edit-gross-weight"
|
||||||
|
type="number"
|
||||||
|
value={grossWeight}
|
||||||
|
onChange={(event) => setGrossWeight(event.target.value)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
<div className="flex flex-1 flex-col gap-2">
|
<div className="flex flex-1 flex-col gap-2">
|
||||||
<Label htmlFor="harvest-edit-weight">Nettogewicht (kg)</Label>
|
<Label htmlFor="harvest-edit-weight">Nettogewicht (kg)</Label>
|
||||||
<Input
|
<Input
|
||||||
|
|||||||
@@ -41,6 +41,8 @@ export default function NewHarvestPage() {
|
|||||||
const [date, setDate] = useState(todayIsoDate());
|
const [date, setDate] = useState(todayIsoDate());
|
||||||
const [colonyIds, setColonyIds] = useState<string[]>([]);
|
const [colonyIds, setColonyIds] = useState<string[]>([]);
|
||||||
const [honeyType, setHoneyType] = useState("");
|
const [honeyType, setHoneyType] = useState("");
|
||||||
|
const [honeySuperCount, setHoneySuperCount] = useState("");
|
||||||
|
const [grossWeight, setGrossWeight] = useState("");
|
||||||
const [netWeight, setNetWeight] = useState("");
|
const [netWeight, setNetWeight] = useState("");
|
||||||
const [notes, setNotes] = useState("");
|
const [notes, setNotes] = useState("");
|
||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null);
|
||||||
@@ -99,6 +101,8 @@ export default function NewHarvestPage() {
|
|||||||
date: new Date(date).toISOString(),
|
date: new Date(date).toISOString(),
|
||||||
colonyIds,
|
colonyIds,
|
||||||
honeyType: honeyType || undefined,
|
honeyType: honeyType || undefined,
|
||||||
|
honeySuperCount: honeySuperCount ? Number(honeySuperCount) : undefined,
|
||||||
|
grossWeight: grossWeight ? Number(grossWeight) : undefined,
|
||||||
netWeight: netWeight ? Number(netWeight) : undefined,
|
netWeight: netWeight ? Number(netWeight) : undefined,
|
||||||
notes: notes || undefined,
|
notes: notes || undefined,
|
||||||
});
|
});
|
||||||
@@ -192,6 +196,28 @@ export default function NewHarvestPage() {
|
|||||||
onChange={(event) => setHoneyType(event.target.value)}
|
onChange={(event) => setHoneyType(event.target.value)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="flex flex-col gap-2">
|
||||||
|
<Label htmlFor="harvest-super-count">Honigräume</Label>
|
||||||
|
<Input
|
||||||
|
id="harvest-super-count"
|
||||||
|
type="number"
|
||||||
|
inputMode="numeric"
|
||||||
|
className="h-11"
|
||||||
|
value={honeySuperCount}
|
||||||
|
onChange={(event) => setHoneySuperCount(event.target.value)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col gap-2">
|
||||||
|
<Label htmlFor="harvest-gross-weight">Bruttogewicht (kg)</Label>
|
||||||
|
<Input
|
||||||
|
id="harvest-gross-weight"
|
||||||
|
type="number"
|
||||||
|
inputMode="decimal"
|
||||||
|
className="h-11"
|
||||||
|
value={grossWeight}
|
||||||
|
onChange={(event) => setGrossWeight(event.target.value)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
<Label htmlFor="harvest-net-weight">Nettogewicht (kg)</Label>
|
<Label htmlFor="harvest-net-weight">Nettogewicht (kg)</Label>
|
||||||
<Input
|
<Input
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ function ExtractionEditForm({
|
|||||||
}) {
|
}) {
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
const [date, setDate] = useState(extraction.date.slice(0, 10));
|
const [date, setDate] = useState(extraction.date.slice(0, 10));
|
||||||
|
const [amount, setAmount] = useState(extraction.amount?.toString() ?? "");
|
||||||
const [room, setRoom] = useState(extraction.room ?? "");
|
const [room, setRoom] = useState(extraction.room ?? "");
|
||||||
const [waterContent, setWaterContent] = useState(
|
const [waterContent, setWaterContent] = useState(
|
||||||
extraction.waterContent?.toString() ?? "",
|
extraction.waterContent?.toString() ?? "",
|
||||||
@@ -41,6 +42,7 @@ function ExtractionEditForm({
|
|||||||
mutationFn: () =>
|
mutationFn: () =>
|
||||||
updateExtraction(extraction.id, {
|
updateExtraction(extraction.id, {
|
||||||
date: new Date(date).toISOString(),
|
date: new Date(date).toISOString(),
|
||||||
|
amount: amount ? Number(amount) : undefined,
|
||||||
room: room || undefined,
|
room: room || undefined,
|
||||||
waterContent: waterContent ? Number(waterContent) : undefined,
|
waterContent: waterContent ? Number(waterContent) : undefined,
|
||||||
temperature: temperature ? Number(temperature) : undefined,
|
temperature: temperature ? Number(temperature) : undefined,
|
||||||
@@ -70,6 +72,15 @@ function ExtractionEditForm({
|
|||||||
onChange={(event) => setDate(event.target.value)}
|
onChange={(event) => setDate(event.target.value)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="flex flex-col gap-2">
|
||||||
|
<Label htmlFor="extraction-edit-amount">Menge (kg)</Label>
|
||||||
|
<Input
|
||||||
|
id="extraction-edit-amount"
|
||||||
|
type="number"
|
||||||
|
value={amount}
|
||||||
|
onChange={(event) => setAmount(event.target.value)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
<Label htmlFor="extraction-edit-room">Raum</Label>
|
<Label htmlFor="extraction-edit-room">Raum</Label>
|
||||||
<Input
|
<Input
|
||||||
|
|||||||
@@ -46,13 +46,23 @@ export default function NewInspectionPage() {
|
|||||||
|
|
||||||
const [colonyId, setColonyId] = useState(searchParams.get("colonyId") ?? "");
|
const [colonyId, setColonyId] = useState(searchParams.get("colonyId") ?? "");
|
||||||
const [date, setDate] = useState(todayIsoDate());
|
const [date, setDate] = useState(todayIsoDate());
|
||||||
|
const [weather, setWeather] = useState("");
|
||||||
|
const [temperature, setTemperature] = useState("");
|
||||||
const [colonyStrength, setColonyStrength] = useState("");
|
const [colonyStrength, setColonyStrength] = useState("");
|
||||||
|
const [broodAmount, setBroodAmount] = useState("");
|
||||||
|
const [broodPattern, setBroodPattern] = useState("");
|
||||||
|
const [openBroodPresent, setOpenBroodPresent] = useState(false);
|
||||||
|
const [cappedBroodPresent, setCappedBroodPresent] = useState(false);
|
||||||
const [queenSeen, setQueenSeen] = useState(false);
|
const [queenSeen, setQueenSeen] = useState(false);
|
||||||
const [eggsPresent, setEggsPresent] = useState(true);
|
const [eggsPresent, setEggsPresent] = useState(true);
|
||||||
const [queenCellsPresent, setQueenCellsPresent] = useState(false);
|
const [queenCellsPresent, setQueenCellsPresent] = useState(false);
|
||||||
|
const [swarmMood, setSwarmMood] = useState("");
|
||||||
|
const [gentleness, setGentleness] = useState("");
|
||||||
|
const [combBehavior, setCombBehavior] = useState("");
|
||||||
const [feedStatus, setFeedStatus] = useState<"ausreichend" | "niedrig" | "kritisch">(
|
const [feedStatus, setFeedStatus] = useState<"ausreichend" | "niedrig" | "kritisch">(
|
||||||
"ausreichend",
|
"ausreichend",
|
||||||
);
|
);
|
||||||
|
const [honeySuperStatus, setHoneySuperStatus] = useState("");
|
||||||
const [healthIssues, setHealthIssues] = useState("");
|
const [healthIssues, setHealthIssues] = useState("");
|
||||||
const [varroaIssues, setVarroaIssues] = useState("");
|
const [varroaIssues, setVarroaIssues] = useState("");
|
||||||
const [measures, setMeasures] = useState("");
|
const [measures, setMeasures] = useState("");
|
||||||
@@ -90,11 +100,21 @@ export default function NewInspectionPage() {
|
|||||||
mutation.mutate({
|
mutation.mutate({
|
||||||
colonyId,
|
colonyId,
|
||||||
date: new Date(date).toISOString(),
|
date: new Date(date).toISOString(),
|
||||||
|
weather: weather || undefined,
|
||||||
|
temperature: temperature ? Number(temperature) : undefined,
|
||||||
colonyStrength: colonyStrength ? Number(colonyStrength) : undefined,
|
colonyStrength: colonyStrength ? Number(colonyStrength) : undefined,
|
||||||
|
broodAmount: broodAmount || undefined,
|
||||||
|
broodPattern: broodPattern ? Number(broodPattern) : undefined,
|
||||||
|
openBroodPresent,
|
||||||
|
cappedBroodPresent,
|
||||||
queenSeen,
|
queenSeen,
|
||||||
eggsPresent,
|
eggsPresent,
|
||||||
queenCellsPresent,
|
queenCellsPresent,
|
||||||
|
swarmMood: swarmMood ? Number(swarmMood) : undefined,
|
||||||
|
gentleness: gentleness ? Number(gentleness) : undefined,
|
||||||
|
combBehavior: combBehavior ? Number(combBehavior) : undefined,
|
||||||
feedStatus,
|
feedStatus,
|
||||||
|
honeySuperStatus: honeySuperStatus || undefined,
|
||||||
healthIssues: healthIssues || undefined,
|
healthIssues: healthIssues || undefined,
|
||||||
varroaIssues: varroaIssues || undefined,
|
varroaIssues: varroaIssues || undefined,
|
||||||
measures: measures || undefined,
|
measures: measures || undefined,
|
||||||
@@ -139,6 +159,26 @@ export default function NewInspectionPage() {
|
|||||||
onChange={(event) => setDate(event.target.value)}
|
onChange={(event) => setDate(event.target.value)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="flex flex-col gap-2">
|
||||||
|
<Label htmlFor="inspection-weather">Wetter</Label>
|
||||||
|
<Input
|
||||||
|
id="inspection-weather"
|
||||||
|
className="h-11"
|
||||||
|
value={weather}
|
||||||
|
onChange={(event) => setWeather(event.target.value)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col gap-2">
|
||||||
|
<Label htmlFor="inspection-temperature">Temperatur (°C)</Label>
|
||||||
|
<Input
|
||||||
|
id="inspection-temperature"
|
||||||
|
type="number"
|
||||||
|
inputMode="decimal"
|
||||||
|
className="h-11"
|
||||||
|
value={temperature}
|
||||||
|
onChange={(event) => setTemperature(event.target.value)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
<Label htmlFor="inspection-strength">Volksstärke (Waben)</Label>
|
<Label htmlFor="inspection-strength">Volksstärke (Waben)</Label>
|
||||||
<Input
|
<Input
|
||||||
@@ -150,7 +190,54 @@ export default function NewInspectionPage() {
|
|||||||
onChange={(event) => setColonyStrength(event.target.value)}
|
onChange={(event) => setColonyStrength(event.target.value)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="flex flex-col gap-2">
|
||||||
|
<Label htmlFor="inspection-brood-amount">Brutmenge</Label>
|
||||||
|
<Input
|
||||||
|
id="inspection-brood-amount"
|
||||||
|
className="h-11"
|
||||||
|
value={broodAmount}
|
||||||
|
onChange={(event) => setBroodAmount(event.target.value)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col gap-2">
|
||||||
|
<Label htmlFor="inspection-brood-pattern">Brutbild (1–5)</Label>
|
||||||
|
<Input
|
||||||
|
id="inspection-brood-pattern"
|
||||||
|
type="number"
|
||||||
|
min={1}
|
||||||
|
max={5}
|
||||||
|
className="h-11"
|
||||||
|
value={broodPattern}
|
||||||
|
onChange={(event) => setBroodPattern(event.target.value)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
<div className="flex flex-col gap-3">
|
<div className="flex flex-col gap-3">
|
||||||
|
<label
|
||||||
|
className="flex h-11 items-center gap-3 text-sm"
|
||||||
|
htmlFor="inspection-open-brood"
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
id="inspection-open-brood"
|
||||||
|
type="checkbox"
|
||||||
|
className="size-5"
|
||||||
|
checked={openBroodPresent}
|
||||||
|
onChange={(event) => setOpenBroodPresent(event.target.checked)}
|
||||||
|
/>
|
||||||
|
Offene Brut vorhanden
|
||||||
|
</label>
|
||||||
|
<label
|
||||||
|
className="flex h-11 items-center gap-3 text-sm"
|
||||||
|
htmlFor="inspection-capped-brood"
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
id="inspection-capped-brood"
|
||||||
|
type="checkbox"
|
||||||
|
className="size-5"
|
||||||
|
checked={cappedBroodPresent}
|
||||||
|
onChange={(event) => setCappedBroodPresent(event.target.checked)}
|
||||||
|
/>
|
||||||
|
Verdeckelte Brut vorhanden
|
||||||
|
</label>
|
||||||
<label className="flex h-11 items-center gap-3 text-sm" htmlFor="inspection-queen-seen">
|
<label className="flex h-11 items-center gap-3 text-sm" htmlFor="inspection-queen-seen">
|
||||||
<input
|
<input
|
||||||
id="inspection-queen-seen"
|
id="inspection-queen-seen"
|
||||||
@@ -188,6 +275,42 @@ export default function NewInspectionPage() {
|
|||||||
Weiselzellen vorhanden
|
Weiselzellen vorhanden
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="flex flex-col gap-2">
|
||||||
|
<Label htmlFor="inspection-swarm-mood">Schwarmstimmung (1–5)</Label>
|
||||||
|
<Input
|
||||||
|
id="inspection-swarm-mood"
|
||||||
|
type="number"
|
||||||
|
min={1}
|
||||||
|
max={5}
|
||||||
|
className="h-11"
|
||||||
|
value={swarmMood}
|
||||||
|
onChange={(event) => setSwarmMood(event.target.value)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col gap-2">
|
||||||
|
<Label htmlFor="inspection-gentleness">Sanftmut (1–5)</Label>
|
||||||
|
<Input
|
||||||
|
id="inspection-gentleness"
|
||||||
|
type="number"
|
||||||
|
min={1}
|
||||||
|
max={5}
|
||||||
|
className="h-11"
|
||||||
|
value={gentleness}
|
||||||
|
onChange={(event) => setGentleness(event.target.value)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col gap-2">
|
||||||
|
<Label htmlFor="inspection-comb-behavior">Wabensitz (1–5)</Label>
|
||||||
|
<Input
|
||||||
|
id="inspection-comb-behavior"
|
||||||
|
type="number"
|
||||||
|
min={1}
|
||||||
|
max={5}
|
||||||
|
className="h-11"
|
||||||
|
value={combBehavior}
|
||||||
|
onChange={(event) => setCombBehavior(event.target.value)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
<Label htmlFor="inspection-feed-status">Futterstatus</Label>
|
<Label htmlFor="inspection-feed-status">Futterstatus</Label>
|
||||||
<Select
|
<Select
|
||||||
@@ -206,6 +329,15 @@ export default function NewInspectionPage() {
|
|||||||
</SelectContent>
|
</SelectContent>
|
||||||
</Select>
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="flex flex-col gap-2">
|
||||||
|
<Label htmlFor="inspection-honey-super">Honigraumstatus</Label>
|
||||||
|
<Input
|
||||||
|
id="inspection-honey-super"
|
||||||
|
className="h-11"
|
||||||
|
value={honeySuperStatus}
|
||||||
|
onChange={(event) => setHoneySuperStatus(event.target.value)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
<Label htmlFor="inspection-varroa">Varroa-Befund</Label>
|
<Label htmlFor="inspection-varroa">Varroa-Befund</Label>
|
||||||
<Input
|
<Input
|
||||||
|
|||||||
@@ -32,8 +32,14 @@ export function QueenEditCard({ queen }: { queen: Queen }) {
|
|||||||
const [hatchYear, setHatchYear] = useState(queen.hatchYear?.toString() ?? "");
|
const [hatchYear, setHatchYear] = useState(queen.hatchYear?.toString() ?? "");
|
||||||
const [breed, setBreed] = useState(queen.breed ?? "");
|
const [breed, setBreed] = useState(queen.breed ?? "");
|
||||||
const [origin, setOrigin] = useState(queen.origin ?? "");
|
const [origin, setOrigin] = useState(queen.origin ?? "");
|
||||||
|
const [breeder, setBreeder] = useState(queen.breeder ?? "");
|
||||||
const [markingColor, setMarkingColor] = useState(queen.markingColor ?? "");
|
const [markingColor, setMarkingColor] = useState(queen.markingColor ?? "");
|
||||||
const [markingNumber, setMarkingNumber] = useState(queen.markingNumber ?? "");
|
const [markingNumber, setMarkingNumber] = useState(queen.markingNumber ?? "");
|
||||||
|
const [matingType, setMatingType] = useState(queen.matingType ?? "");
|
||||||
|
const [matingStatus, setMatingStatus] = useState(queen.matingStatus ?? "");
|
||||||
|
const [inseminationData, setInseminationData] = useState(queen.inseminationData ?? "");
|
||||||
|
const [motherLine, setMotherLine] = useState(queen.motherLine ?? "");
|
||||||
|
const [fatherLine, setFatherLine] = useState(queen.fatherLine ?? "");
|
||||||
const [motherQueenId, setMotherQueenId] = useState(queen.motherQueenId ?? "");
|
const [motherQueenId, setMotherQueenId] = useState(queen.motherQueenId ?? "");
|
||||||
const [notes, setNotes] = useState(queen.notes ?? "");
|
const [notes, setNotes] = useState(queen.notes ?? "");
|
||||||
const [status, setStatus] = useState<QueenStatus>(queen.status);
|
const [status, setStatus] = useState<QueenStatus>(queen.status);
|
||||||
@@ -58,8 +64,14 @@ export function QueenEditCard({ queen }: { queen: Queen }) {
|
|||||||
hatchYear: hatchYear ? Number(hatchYear) : undefined,
|
hatchYear: hatchYear ? Number(hatchYear) : undefined,
|
||||||
breed: breed || undefined,
|
breed: breed || undefined,
|
||||||
origin: origin || undefined,
|
origin: origin || undefined,
|
||||||
|
breeder: breeder || undefined,
|
||||||
markingColor: markingColor || undefined,
|
markingColor: markingColor || undefined,
|
||||||
markingNumber: markingNumber || undefined,
|
markingNumber: markingNumber || undefined,
|
||||||
|
matingType: matingType || undefined,
|
||||||
|
matingStatus: matingStatus || undefined,
|
||||||
|
inseminationData: inseminationData || undefined,
|
||||||
|
motherLine: motherLine || undefined,
|
||||||
|
fatherLine: fatherLine || undefined,
|
||||||
motherQueenId: motherQueenId || undefined,
|
motherQueenId: motherQueenId || undefined,
|
||||||
notes: notes || undefined,
|
notes: notes || undefined,
|
||||||
status,
|
status,
|
||||||
@@ -117,6 +129,14 @@ export function QueenEditCard({ queen }: { queen: Queen }) {
|
|||||||
onChange={(event) => setOrigin(event.target.value)}
|
onChange={(event) => setOrigin(event.target.value)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="flex flex-col gap-2">
|
||||||
|
<Label htmlFor="queen-breeder">Züchter</Label>
|
||||||
|
<Input
|
||||||
|
id="queen-breeder"
|
||||||
|
value={breeder}
|
||||||
|
onChange={(event) => setBreeder(event.target.value)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
<Label htmlFor="queen-marking-color">Zeichenfarbe</Label>
|
<Label htmlFor="queen-marking-color">Zeichenfarbe</Label>
|
||||||
<Input
|
<Input
|
||||||
@@ -133,6 +153,46 @@ export function QueenEditCard({ queen }: { queen: Queen }) {
|
|||||||
onChange={(event) => setMarkingNumber(event.target.value)}
|
onChange={(event) => setMarkingNumber(event.target.value)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="flex flex-col gap-2">
|
||||||
|
<Label htmlFor="queen-mating-type">Begattungsart</Label>
|
||||||
|
<Input
|
||||||
|
id="queen-mating-type"
|
||||||
|
value={matingType}
|
||||||
|
onChange={(event) => setMatingType(event.target.value)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col gap-2">
|
||||||
|
<Label htmlFor="queen-mating-status">Begattungsstatus</Label>
|
||||||
|
<Input
|
||||||
|
id="queen-mating-status"
|
||||||
|
value={matingStatus}
|
||||||
|
onChange={(event) => setMatingStatus(event.target.value)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col gap-2">
|
||||||
|
<Label htmlFor="queen-insemination-data">Besamungsdaten</Label>
|
||||||
|
<Input
|
||||||
|
id="queen-insemination-data"
|
||||||
|
value={inseminationData}
|
||||||
|
onChange={(event) => setInseminationData(event.target.value)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col gap-2">
|
||||||
|
<Label htmlFor="queen-mother-line">Mutterlinie</Label>
|
||||||
|
<Input
|
||||||
|
id="queen-mother-line"
|
||||||
|
value={motherLine}
|
||||||
|
onChange={(event) => setMotherLine(event.target.value)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col gap-2">
|
||||||
|
<Label htmlFor="queen-father-line">Vaterlinie</Label>
|
||||||
|
<Input
|
||||||
|
id="queen-father-line"
|
||||||
|
value={fatherLine}
|
||||||
|
onChange={(event) => setFatherLine(event.target.value)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
<Label htmlFor="queen-mother">Mutter</Label>
|
<Label htmlFor="queen-mother">Mutter</Label>
|
||||||
<Select
|
<Select
|
||||||
|
|||||||
@@ -2,12 +2,13 @@
|
|||||||
|
|
||||||
import { FormEvent, useState } from "react";
|
import { FormEvent, useState } from "react";
|
||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
import { ListTodo } from "lucide-react";
|
import { ListTodo } from "lucide-react";
|
||||||
import { createTask } from "@/lib/tasks/api";
|
import { createTask } from "@/lib/tasks/api";
|
||||||
import { TaskPriority } from "@/lib/tasks/types";
|
import { TaskPriority } from "@/lib/tasks/types";
|
||||||
import { TASK_PRIORITIES, TASK_PRIORITY_LABELS } from "@/lib/tasks/labels";
|
import { TASK_PRIORITIES, TASK_PRIORITY_LABELS } from "@/lib/tasks/labels";
|
||||||
|
import { fetchUsers } from "@/lib/users/api";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import { Label } from "@/components/ui/label";
|
import { Label } from "@/components/ui/label";
|
||||||
@@ -29,8 +30,17 @@ export default function NewTaskPage() {
|
|||||||
const [description, setDescription] = useState("");
|
const [description, setDescription] = useState("");
|
||||||
const [priority, setPriority] = useState<TaskPriority>("NORMAL");
|
const [priority, setPriority] = useState<TaskPriority>("NORMAL");
|
||||||
const [dueDate, setDueDate] = useState("");
|
const [dueDate, setDueDate] = useState("");
|
||||||
|
const [assignedUserId, setAssignedUserId] = useState("");
|
||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null);
|
||||||
|
|
||||||
|
// Assigning a task requires users:read, which not every role has - if the
|
||||||
|
// request fails, the assignee field below is simply omitted.
|
||||||
|
const { data: users } = useQuery({
|
||||||
|
queryKey: ["users"],
|
||||||
|
queryFn: () => fetchUsers(),
|
||||||
|
retry: false,
|
||||||
|
});
|
||||||
|
|
||||||
const mutation = useMutation({
|
const mutation = useMutation({
|
||||||
mutationFn: createTask,
|
mutationFn: createTask,
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
@@ -51,6 +61,7 @@ export default function NewTaskPage() {
|
|||||||
description: description || undefined,
|
description: description || undefined,
|
||||||
priority,
|
priority,
|
||||||
dueDate: dueDate ? new Date(dueDate).toISOString() : undefined,
|
dueDate: dueDate ? new Date(dueDate).toISOString() : undefined,
|
||||||
|
assignedUserId: assignedUserId || undefined,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -92,6 +103,34 @@ export default function NewTaskPage() {
|
|||||||
</SelectContent>
|
</SelectContent>
|
||||||
</Select>
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
|
{users ? (
|
||||||
|
<div className="flex flex-col gap-2">
|
||||||
|
<Label htmlFor="task-assignee">Zugewiesen an</Label>
|
||||||
|
<Select
|
||||||
|
value={assignedUserId || "NONE"}
|
||||||
|
onValueChange={(value) =>
|
||||||
|
setAssignedUserId(value === "NONE" ? "" : (value ?? ""))
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<SelectTrigger id="task-assignee">
|
||||||
|
<SelectValue placeholder="Niemand zugewiesen">
|
||||||
|
{assignedUserId
|
||||||
|
? (users.find((user) => user.id === assignedUserId)?.name ??
|
||||||
|
"Niemand zugewiesen")
|
||||||
|
: "Niemand zugewiesen"}
|
||||||
|
</SelectValue>
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent>
|
||||||
|
<SelectItem value="NONE">Niemand zugewiesen</SelectItem>
|
||||||
|
{users.map((user) => (
|
||||||
|
<SelectItem key={user.id} value={user.id}>
|
||||||
|
{user.name}
|
||||||
|
</SelectItem>
|
||||||
|
))}
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
<Label htmlFor="task-due-date">Fällig am</Label>
|
<Label htmlFor="task-due-date">Fällig am</Label>
|
||||||
<Input
|
<Input
|
||||||
|
|||||||
@@ -2,10 +2,11 @@
|
|||||||
|
|
||||||
import { FormEvent, useState } from "react";
|
import { FormEvent, useState } from "react";
|
||||||
import { Pencil } from "lucide-react";
|
import { Pencil } from "lucide-react";
|
||||||
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
||||||
import { updateTask } from "@/lib/tasks/api";
|
import { updateTask } from "@/lib/tasks/api";
|
||||||
import { Task, TaskPriority } from "@/lib/tasks/types";
|
import { Task, TaskPriority } from "@/lib/tasks/types";
|
||||||
import { TASK_PRIORITIES, TASK_PRIORITY_LABELS } from "@/lib/tasks/labels";
|
import { TASK_PRIORITIES, TASK_PRIORITY_LABELS } from "@/lib/tasks/labels";
|
||||||
|
import { fetchUsers } from "@/lib/users/api";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import { Label } from "@/components/ui/label";
|
import { Label } from "@/components/ui/label";
|
||||||
@@ -32,6 +33,16 @@ export function TaskEditDialog({ task }: { task: Task }) {
|
|||||||
const [description, setDescription] = useState(task.description ?? "");
|
const [description, setDescription] = useState(task.description ?? "");
|
||||||
const [priority, setPriority] = useState<TaskPriority>(task.priority);
|
const [priority, setPriority] = useState<TaskPriority>(task.priority);
|
||||||
const [dueDate, setDueDate] = useState(task.dueDate?.slice(0, 10) ?? "");
|
const [dueDate, setDueDate] = useState(task.dueDate?.slice(0, 10) ?? "");
|
||||||
|
const [assignedUserId, setAssignedUserId] = useState(task.assignedUserId ?? "");
|
||||||
|
|
||||||
|
// Assigning a task requires users:read, which not every role has - if the
|
||||||
|
// request fails, the assignee field below is simply omitted rather than
|
||||||
|
// breaking the rest of the edit dialog.
|
||||||
|
const { data: users } = useQuery({
|
||||||
|
queryKey: ["users"],
|
||||||
|
queryFn: () => fetchUsers(),
|
||||||
|
retry: false,
|
||||||
|
});
|
||||||
|
|
||||||
const mutation = useMutation({
|
const mutation = useMutation({
|
||||||
mutationFn: () =>
|
mutationFn: () =>
|
||||||
@@ -40,6 +51,7 @@ export function TaskEditDialog({ task }: { task: Task }) {
|
|||||||
description: description || undefined,
|
description: description || undefined,
|
||||||
priority,
|
priority,
|
||||||
dueDate: dueDate ? new Date(dueDate).toISOString() : undefined,
|
dueDate: dueDate ? new Date(dueDate).toISOString() : undefined,
|
||||||
|
assignedUserId: assignedUserId || undefined,
|
||||||
}),
|
}),
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
void queryClient.invalidateQueries({ queryKey: ["tasks"] });
|
void queryClient.invalidateQueries({ queryKey: ["tasks"] });
|
||||||
@@ -94,6 +106,31 @@ export function TaskEditDialog({ task }: { task: Task }) {
|
|||||||
</SelectContent>
|
</SelectContent>
|
||||||
</Select>
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
|
{users ? (
|
||||||
|
<div className="flex flex-col gap-2">
|
||||||
|
<Label htmlFor="task-edit-assignee">Zugewiesen an</Label>
|
||||||
|
<Select
|
||||||
|
value={assignedUserId || "NONE"}
|
||||||
|
onValueChange={(value) => setAssignedUserId(value === "NONE" ? "" : (value ?? ""))}
|
||||||
|
>
|
||||||
|
<SelectTrigger id="task-edit-assignee" className="w-full">
|
||||||
|
<SelectValue placeholder="Niemand zugewiesen">
|
||||||
|
{assignedUserId
|
||||||
|
? (users.find((user) => user.id === assignedUserId)?.name ?? "Niemand zugewiesen")
|
||||||
|
: "Niemand zugewiesen"}
|
||||||
|
</SelectValue>
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent>
|
||||||
|
<SelectItem value="NONE">Niemand zugewiesen</SelectItem>
|
||||||
|
{users.map((user) => (
|
||||||
|
<SelectItem key={user.id} value={user.id}>
|
||||||
|
{user.name}
|
||||||
|
</SelectItem>
|
||||||
|
))}
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
<Label htmlFor="task-edit-due">Fällig am</Label>
|
<Label htmlFor="task-edit-due">Fällig am</Label>
|
||||||
<Input
|
<Input
|
||||||
|
|||||||
@@ -40,10 +40,14 @@ export default function NewTreatmentPage() {
|
|||||||
|
|
||||||
const [colonyId, setColonyId] = useState(searchParams.get("colonyId") ?? "");
|
const [colonyId, setColonyId] = useState(searchParams.get("colonyId") ?? "");
|
||||||
const [date, setDate] = useState(todayIsoDate());
|
const [date, setDate] = useState(todayIsoDate());
|
||||||
|
const [startDate, setStartDate] = useState("");
|
||||||
|
const [endDate, setEndDate] = useState("");
|
||||||
const [product, setProduct] = useState("");
|
const [product, setProduct] = useState("");
|
||||||
|
const [activeIngredient, setActiveIngredient] = useState("");
|
||||||
const [amount, setAmount] = useState("");
|
const [amount, setAmount] = useState("");
|
||||||
const [method, setMethod] = useState("");
|
const [method, setMethod] = useState("");
|
||||||
const [reason, setReason] = useState("");
|
const [reason, setReason] = useState("");
|
||||||
|
const [batchNumber, setBatchNumber] = useState("");
|
||||||
const [notes, setNotes] = useState("");
|
const [notes, setNotes] = useState("");
|
||||||
const [withdrawalPeriodDays, setWithdrawalPeriodDays] = useState("");
|
const [withdrawalPeriodDays, setWithdrawalPeriodDays] = useState("");
|
||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null);
|
||||||
@@ -79,10 +83,14 @@ export default function NewTreatmentPage() {
|
|||||||
mutation.mutate({
|
mutation.mutate({
|
||||||
colonyId,
|
colonyId,
|
||||||
date: new Date(date).toISOString(),
|
date: new Date(date).toISOString(),
|
||||||
|
startDate: startDate ? new Date(startDate).toISOString() : undefined,
|
||||||
|
endDate: endDate ? new Date(endDate).toISOString() : undefined,
|
||||||
product,
|
product,
|
||||||
|
activeIngredient: activeIngredient || undefined,
|
||||||
amount: amount || undefined,
|
amount: amount || undefined,
|
||||||
method: method || undefined,
|
method: method || undefined,
|
||||||
reason: reason || undefined,
|
reason: reason || undefined,
|
||||||
|
batchNumber: batchNumber || undefined,
|
||||||
notes: notes || undefined,
|
notes: notes || undefined,
|
||||||
withdrawalPeriodDays: withdrawalPeriodDays ? Number(withdrawalPeriodDays) : undefined,
|
withdrawalPeriodDays: withdrawalPeriodDays ? Number(withdrawalPeriodDays) : undefined,
|
||||||
});
|
});
|
||||||
@@ -125,6 +133,26 @@ export default function NewTreatmentPage() {
|
|||||||
onChange={(event) => setDate(event.target.value)}
|
onChange={(event) => setDate(event.target.value)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="flex flex-col gap-2">
|
||||||
|
<Label htmlFor="treatment-start-date">Beginn</Label>
|
||||||
|
<Input
|
||||||
|
id="treatment-start-date"
|
||||||
|
type="date"
|
||||||
|
className="h-11"
|
||||||
|
value={startDate}
|
||||||
|
onChange={(event) => setStartDate(event.target.value)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col gap-2">
|
||||||
|
<Label htmlFor="treatment-end-date">Ende</Label>
|
||||||
|
<Input
|
||||||
|
id="treatment-end-date"
|
||||||
|
type="date"
|
||||||
|
className="h-11"
|
||||||
|
value={endDate}
|
||||||
|
onChange={(event) => setEndDate(event.target.value)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
<Label htmlFor="treatment-product">Mittel</Label>
|
<Label htmlFor="treatment-product">Mittel</Label>
|
||||||
<Input
|
<Input
|
||||||
@@ -135,6 +163,15 @@ export default function NewTreatmentPage() {
|
|||||||
onChange={(event) => setProduct(event.target.value)}
|
onChange={(event) => setProduct(event.target.value)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="flex flex-col gap-2">
|
||||||
|
<Label htmlFor="treatment-active-ingredient">Wirkstoff</Label>
|
||||||
|
<Input
|
||||||
|
id="treatment-active-ingredient"
|
||||||
|
className="h-11"
|
||||||
|
value={activeIngredient}
|
||||||
|
onChange={(event) => setActiveIngredient(event.target.value)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
<Label htmlFor="treatment-amount">Menge</Label>
|
<Label htmlFor="treatment-amount">Menge</Label>
|
||||||
<Input
|
<Input
|
||||||
@@ -162,6 +199,15 @@ export default function NewTreatmentPage() {
|
|||||||
onChange={(event) => setReason(event.target.value)}
|
onChange={(event) => setReason(event.target.value)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="flex flex-col gap-2">
|
||||||
|
<Label htmlFor="treatment-batch">Chargennummer</Label>
|
||||||
|
<Input
|
||||||
|
id="treatment-batch"
|
||||||
|
className="h-11"
|
||||||
|
value={batchNumber}
|
||||||
|
onChange={(event) => setBatchNumber(event.target.value)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
<Label htmlFor="treatment-withdrawal">Wartezeit (Tage)</Label>
|
<Label htmlFor="treatment-withdrawal">Wartezeit (Tage)</Label>
|
||||||
<Input
|
<Input
|
||||||
|
|||||||
@@ -19,6 +19,18 @@ export const COLONY_TYPE_LABELS: Record<ColonyType, string> = {
|
|||||||
RESERVE_COLONY: "Reservevolk",
|
RESERVE_COLONY: "Reservevolk",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// The non-terminal statuses a beekeeper can freely switch between while
|
||||||
|
// caring for a colony. DISSOLVED/SOLD/DECEASED/MERGED are lifecycle-ending
|
||||||
|
// transitions handled by their own dedicated confirm actions (colony-actions.tsx),
|
||||||
|
// which also set deactivatedAt - exposing them in a free dropdown here would
|
||||||
|
// let status and deactivatedAt drift out of sync.
|
||||||
|
export const COLONY_EDITABLE_STATUSES: ColonyStatus[] = [
|
||||||
|
"ACTIVE",
|
||||||
|
"WEAK",
|
||||||
|
"QUEENLESS",
|
||||||
|
"CONSPICUOUS",
|
||||||
|
];
|
||||||
|
|
||||||
export const COLONY_STATUS_LABELS: Record<ColonyStatus, string> = {
|
export const COLONY_STATUS_LABELS: Record<ColonyStatus, string> = {
|
||||||
ACTIVE: "Aktiv",
|
ACTIVE: "Aktiv",
|
||||||
WEAK: "Schwach",
|
WEAK: "Schwach",
|
||||||
|
|||||||
@@ -31,10 +31,18 @@ export interface CreateInspectionInput {
|
|||||||
weather?: string;
|
weather?: string;
|
||||||
temperature?: number;
|
temperature?: number;
|
||||||
colonyStrength?: number;
|
colonyStrength?: number;
|
||||||
|
broodAmount?: string;
|
||||||
|
broodPattern?: number;
|
||||||
|
openBroodPresent?: boolean;
|
||||||
|
cappedBroodPresent?: boolean;
|
||||||
queenSeen?: boolean;
|
queenSeen?: boolean;
|
||||||
eggsPresent?: boolean;
|
eggsPresent?: boolean;
|
||||||
queenCellsPresent?: boolean;
|
queenCellsPresent?: boolean;
|
||||||
|
swarmMood?: number;
|
||||||
|
gentleness?: number;
|
||||||
|
combBehavior?: number;
|
||||||
feedStatus?: string;
|
feedStatus?: string;
|
||||||
|
honeySuperStatus?: string;
|
||||||
healthIssues?: string;
|
healthIssues?: string;
|
||||||
varroaIssues?: string;
|
varroaIssues?: string;
|
||||||
measures?: string;
|
measures?: string;
|
||||||
@@ -46,10 +54,18 @@ export interface UpdateInspectionInput {
|
|||||||
weather?: string;
|
weather?: string;
|
||||||
temperature?: number;
|
temperature?: number;
|
||||||
colonyStrength?: number;
|
colonyStrength?: number;
|
||||||
|
broodAmount?: string;
|
||||||
|
broodPattern?: number;
|
||||||
|
openBroodPresent?: boolean;
|
||||||
|
cappedBroodPresent?: boolean;
|
||||||
queenSeen?: boolean;
|
queenSeen?: boolean;
|
||||||
eggsPresent?: boolean;
|
eggsPresent?: boolean;
|
||||||
queenCellsPresent?: boolean;
|
queenCellsPresent?: boolean;
|
||||||
|
swarmMood?: number;
|
||||||
|
gentleness?: number;
|
||||||
|
combBehavior?: number;
|
||||||
feedStatus?: string;
|
feedStatus?: string;
|
||||||
|
honeySuperStatus?: string;
|
||||||
healthIssues?: string;
|
healthIssues?: string;
|
||||||
varroaIssues?: string;
|
varroaIssues?: string;
|
||||||
measures?: string;
|
measures?: string;
|
||||||
|
|||||||
@@ -35,8 +35,14 @@ export interface CreateQueenInput {
|
|||||||
hatchYear?: number;
|
hatchYear?: number;
|
||||||
breed?: string;
|
breed?: string;
|
||||||
origin?: string;
|
origin?: string;
|
||||||
|
breeder?: string;
|
||||||
markingColor?: string;
|
markingColor?: string;
|
||||||
markingNumber?: string;
|
markingNumber?: string;
|
||||||
|
matingType?: string;
|
||||||
|
matingStatus?: string;
|
||||||
|
inseminationData?: string;
|
||||||
|
motherLine?: string;
|
||||||
|
fatherLine?: string;
|
||||||
motherQueenId?: string;
|
motherQueenId?: string;
|
||||||
notes?: string;
|
notes?: string;
|
||||||
}
|
}
|
||||||
@@ -45,8 +51,14 @@ export interface UpdateQueenInput {
|
|||||||
hatchYear?: number;
|
hatchYear?: number;
|
||||||
breed?: string;
|
breed?: string;
|
||||||
origin?: string;
|
origin?: string;
|
||||||
|
breeder?: string;
|
||||||
markingColor?: string;
|
markingColor?: string;
|
||||||
markingNumber?: string;
|
markingNumber?: string;
|
||||||
|
matingType?: string;
|
||||||
|
matingStatus?: string;
|
||||||
|
inseminationData?: string;
|
||||||
|
motherLine?: string;
|
||||||
|
fatherLine?: string;
|
||||||
motherQueenId?: string;
|
motherQueenId?: string;
|
||||||
notes?: string;
|
notes?: string;
|
||||||
status?: QueenStatus;
|
status?: QueenStatus;
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ export interface CreateTaskInput {
|
|||||||
description?: string;
|
description?: string;
|
||||||
priority?: TaskPriority;
|
priority?: TaskPriority;
|
||||||
dueDate?: string;
|
dueDate?: string;
|
||||||
|
assignedUserId?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface UpdateTaskInput {
|
export interface UpdateTaskInput {
|
||||||
@@ -41,5 +42,6 @@ export interface UpdateTaskInput {
|
|||||||
description?: string;
|
description?: string;
|
||||||
priority?: TaskPriority;
|
priority?: TaskPriority;
|
||||||
dueDate?: string;
|
dueDate?: string;
|
||||||
|
assignedUserId?: string;
|
||||||
status?: TaskStatus;
|
status?: TaskStatus;
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user