feat: expose the remaining fields the edit forms were withholding
Backend Image / build-and-push (push) Successful in 2m11s
Frontend Image / build-and-push (push) Successful in 1m53s

The backend accepted every field of these records already; the forms simply
did not offer them, so from the app they were still frozen.

- Inventar: only the condition could be changed. A mistyped inventory number,
  a wrong purchase or maintenance date, a renamed item - all stuck for the life
  of the record. New edit dialog covering every field; the inline condition
  select stays as a shortcut.
- Produkt: the status was shown as a badge but could not be set anywhere, so a
  product could never be discontinued or brought back.
- Aufgabe: the status could only ever move to DONE through the "Erledigt"
  button. IN_PROGRESS and CANCELLED were unreachable, and an accidentally
  completed task could not be reopened.
- Aufgabenvorlage: priority, assignee and the active flag were missing.
  Deactivating was one-way, since only a deactivate endpoint was wired up -
  the active checkbox now reactivates through PATCH.
- Königinnen-Bewertung: notes and the evaluated colony were missing.

Verified by extracting the field set each form submits and comparing it with
the matching update DTO: every form now covers its DTO. The one gap left is
re-linking a document to a different record, which still has no UI.

Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
Nick SzaunigandClaude Opus 5 committed 2026-09-22 23:11:12 +02:00
1 parent 5a5bd4a138
commit 786d929df9
6 files changed
+421 -4

No files matched your search

@@ -0,0 +1,211 @@
"use client";
import { FormEvent, useState } from "react";
import { Pencil } from "lucide-react";
import { useMutation, useQueryClient } from "@tanstack/react-query";
import { toast } from "sonner";
import { updateEquipment } from "@/lib/equipment/api";
import { Equipment, EquipmentCondition } from "@/lib/equipment/types";
import {
EQUIPMENT_CONDITIONS,
EQUIPMENT_CONDITION_LABELS,
} from "@/lib/equipment/labels";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import {
Dialog,
DialogContent,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog";
/**
* Corrects every field of an inventory item. Only the condition used to be
* changeable, so a mistyped inventory number or a wrong purchase date stayed
* wrong for the life of the record.
*/
function EquipmentEditForm({
equipment,
onSaved,
}: {
equipment: Equipment;
onSaved: () => void;
}) {
const queryClient = useQueryClient();
const [inventoryNumber, setInventoryNumber] = useState(
equipment.inventoryNumber,
);
const [name, setName] = useState(equipment.name);
const [manufacturer, setManufacturer] = useState(equipment.manufacturer ?? "");
const [location, setLocation] = useState(equipment.location ?? "");
const [purchaseDate, setPurchaseDate] = useState(
equipment.purchaseDate?.slice(0, 10) ?? "",
);
const [maintenanceDate, setMaintenanceDate] = useState(
equipment.maintenanceDate?.slice(0, 10) ?? "",
);
const [condition, setCondition] = useState<EquipmentCondition>(
equipment.condition,
);
const [error, setError] = useState<string | null>(null);
const mutation = useMutation({
mutationFn: () =>
updateEquipment(equipment.id, {
inventoryNumber,
name,
manufacturer: manufacturer || undefined,
location: location || undefined,
purchaseDate: purchaseDate
? new Date(purchaseDate).toISOString()
: undefined,
maintenanceDate: maintenanceDate
? new Date(maintenanceDate).toISOString()
: undefined,
condition,
}),
onSuccess: () => {
void queryClient.invalidateQueries({
queryKey: ["equipment", equipment.id],
});
void queryClient.invalidateQueries({ queryKey: ["equipment"] });
onSaved();
},
onError: (mutationError: Error) => {
const message =
mutationError.message || "Gegenstand konnte nicht gespeichert werden";
setError(message);
toast.error(message);
},
});
function handleSubmit(event: FormEvent<HTMLFormElement>) {
event.preventDefault();
setError(null);
mutation.mutate();
}
return (
<form className="flex flex-col gap-4" onSubmit={handleSubmit}>
<div className="flex flex-col gap-2">
<Label htmlFor="equipment-edit-inventory-number">Inventarnummer</Label>
<Input
id="equipment-edit-inventory-number"
required
value={inventoryNumber}
onChange={(event) => setInventoryNumber(event.target.value)}
/>
</div>
<div className="flex flex-col gap-2">
<Label htmlFor="equipment-edit-name">Bezeichnung</Label>
<Input
id="equipment-edit-name"
required
value={name}
onChange={(event) => setName(event.target.value)}
/>
</div>
<div className="flex flex-col gap-2">
<Label htmlFor="equipment-edit-manufacturer">Hersteller</Label>
<Input
id="equipment-edit-manufacturer"
value={manufacturer}
onChange={(event) => setManufacturer(event.target.value)}
/>
</div>
<div className="flex flex-col gap-2">
<Label htmlFor="equipment-edit-location">Standort</Label>
<Input
id="equipment-edit-location"
value={location}
onChange={(event) => setLocation(event.target.value)}
/>
</div>
<div className="flex gap-3">
<div className="flex flex-1 flex-col gap-2">
<Label htmlFor="equipment-edit-purchase-date">Kaufdatum</Label>
<Input
id="equipment-edit-purchase-date"
type="date"
value={purchaseDate}
onChange={(event) => setPurchaseDate(event.target.value)}
/>
</div>
<div className="flex flex-1 flex-col gap-2">
<Label htmlFor="equipment-edit-maintenance-date">Wartungsdatum</Label>
<Input
id="equipment-edit-maintenance-date"
type="date"
value={maintenanceDate}
onChange={(event) => setMaintenanceDate(event.target.value)}
/>
</div>
</div>
<div className="flex flex-col gap-2">
<Label htmlFor="equipment-edit-condition">Zustand</Label>
<Select
value={condition}
onValueChange={(value) =>
setCondition((value ?? condition) as EquipmentCondition)
}
>
<SelectTrigger id="equipment-edit-condition" className="w-full">
<SelectValue>{EQUIPMENT_CONDITION_LABELS[condition]}</SelectValue>
</SelectTrigger>
<SelectContent>
{EQUIPMENT_CONDITIONS.map((option) => (
<SelectItem key={option} value={option}>
{EQUIPMENT_CONDITION_LABELS[option]}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
{error ? (
<p role="alert" className="text-sm text-destructive">
{error}
</p>
) : null}
<Button type="submit" disabled={mutation.isPending}>
{mutation.isPending ? "Speichert…" : "Speichern"}
</Button>
</form>
);
}
export function EquipmentEditDialog({ equipment }: { equipment: Equipment }) {
const [open, setOpen] = useState(false);
return (
<Dialog open={open} onOpenChange={setOpen}>
<DialogTrigger
render={
<Button variant="outline" size="sm">
<Pencil className="size-3.5" />
Bearbeiten
</Button>
}
/>
<DialogContent>
<DialogHeader>
<DialogTitle>Gegenstand bearbeiten</DialogTitle>
</DialogHeader>
{open ? (
<EquipmentEditForm
equipment={equipment}
onSaved={() => setOpen(false)}
/>
) : null}
</DialogContent>
</Dialog>
);
}
@@ -5,6 +5,7 @@ import { Wrench } from "lucide-react";
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { toast } from "sonner"; import { toast } from "sonner";
import { fetchEquipment, updateEquipment } from "@/lib/equipment/api"; import { fetchEquipment, updateEquipment } from "@/lib/equipment/api";
import { EquipmentEditDialog } from "./equipment-edit-dialog";
import { EquipmentCondition } from "@/lib/equipment/types"; import { EquipmentCondition } from "@/lib/equipment/types";
import { EQUIPMENT_CONDITIONS, EQUIPMENT_CONDITION_LABELS } from "@/lib/equipment/labels"; import { EQUIPMENT_CONDITIONS, EQUIPMENT_CONDITION_LABELS } from "@/lib/equipment/labels";
import { statusTone } from "@/lib/status-tone"; import { statusTone } from "@/lib/status-tone";
@@ -60,9 +61,12 @@ export default function EquipmentDetailPage() {
</span> </span>
<CardTitle>{equipment.name}</CardTitle> <CardTitle>{equipment.name}</CardTitle>
</div> </div>
<div className="flex items-center gap-2">
<Badge variant={statusTone(equipment.condition)}> <Badge variant={statusTone(equipment.condition)}>
{EQUIPMENT_CONDITION_LABELS[equipment.condition]} {EQUIPMENT_CONDITION_LABELS[equipment.condition]}
</Badge> </Badge>
<EquipmentEditDialog equipment={equipment} />
</div>
</CardHeader> </CardHeader>
<CardContent className="flex flex-col gap-3 text-sm"> <CardContent className="flex flex-col gap-3 text-sm">
<p> <p>
@@ -5,11 +5,19 @@ import { Pencil } from "lucide-react";
import { useMutation, useQueryClient } from "@tanstack/react-query"; import { useMutation, useQueryClient } from "@tanstack/react-query";
import { toast } from "sonner"; import { toast } from "sonner";
import { updateProduct } from "@/lib/products/api"; import { updateProduct } from "@/lib/products/api";
import { Product } from "@/lib/products/types"; import { Product, ProductStatus } from "@/lib/products/types";
import { PRODUCT_STATUS_LABELS } from "@/lib/products/labels";
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 { Textarea } from "@/components/ui/textarea"; import { Textarea } from "@/components/ui/textarea";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import { import {
Dialog, Dialog,
DialogContent, DialogContent,
@@ -28,6 +36,7 @@ export function EditProductDialog({ product }: { product: Product }) {
const [packagingSize, setPackagingSize] = useState(product.packagingSize?.toString() ?? ""); const [packagingSize, setPackagingSize] = useState(product.packagingSize?.toString() ?? "");
const [weight, setWeight] = useState(product.weight?.toString() ?? ""); const [weight, setWeight] = useState(product.weight?.toString() ?? "");
const [salesUnit, setSalesUnit] = useState(product.salesUnit ?? ""); const [salesUnit, setSalesUnit] = useState(product.salesUnit ?? "");
const [status, setStatus] = useState<ProductStatus>(product.status);
const mutation = useMutation({ const mutation = useMutation({
mutationFn: () => mutationFn: () =>
@@ -38,6 +47,7 @@ export function EditProductDialog({ product }: { product: Product }) {
packagingSize: packagingSize ? Number(packagingSize) : undefined, packagingSize: packagingSize ? Number(packagingSize) : undefined,
weight: weight ? Number(weight) : undefined, weight: weight ? Number(weight) : undefined,
salesUnit: salesUnit || undefined, salesUnit: salesUnit || undefined,
status,
}), }),
onSuccess: () => { onSuccess: () => {
void queryClient.invalidateQueries({ queryKey: ["products"] }); void queryClient.invalidateQueries({ queryKey: ["products"] });
@@ -111,6 +121,28 @@ export function EditProductDialog({ product }: { product: Product }) {
onChange={(event) => setWeight(event.target.value)} onChange={(event) => setWeight(event.target.value)}
/> />
</div> </div>
<div className="flex flex-col gap-2">
<Label htmlFor="product-edit-status">Status</Label>
<Select
value={status}
onValueChange={(value) =>
setStatus((value ?? status) as ProductStatus)
}
>
<SelectTrigger id="product-edit-status" className="w-full">
<SelectValue>{PRODUCT_STATUS_LABELS[status]}</SelectValue>
</SelectTrigger>
<SelectContent>
{(Object.keys(PRODUCT_STATUS_LABELS) as ProductStatus[]).map(
(option) => (
<SelectItem key={option} value={option}>
{PRODUCT_STATUS_LABELS[option]}
</SelectItem>
),
)}
</SelectContent>
</Select>
</div>
<div className="flex flex-col gap-2"> <div className="flex flex-col gap-2">
<Label htmlFor="product-edit-sales-unit">Verkaufseinheit</Label> <Label htmlFor="product-edit-sales-unit">Verkaufseinheit</Label>
<Input <Input
@@ -12,6 +12,15 @@ import { CreateQueenEvaluationInput, QueenEvaluation } from "@/lib/queens/types"
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 { Textarea } from "@/components/ui/textarea";
import { fetchColonies } from "@/lib/colonies/api";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import { EmptyState } from "@/components/empty-state"; import { EmptyState } from "@/components/empty-state";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { import {
@@ -32,6 +41,10 @@ const CRITERIA: { key: keyof Omit<CreateQueenEvaluationInput, "date" | "colonyId
{ key: "honeyYield", label: "Honigertrag" }, { key: "honeyYield", label: "Honigertrag" },
]; ];
/** Sentinel for "no colony": the rating may stand on its own, and an empty
* select value would render the placeholder rather than a choosable entry. */
const NO_COLONY = "__none__";
function todayIsoDate(): string { function todayIsoDate(): string {
return new Date().toISOString().slice(0, 10); return new Date().toISOString().slice(0, 10);
} }
@@ -58,8 +71,15 @@ function EditEvaluationDialog({
]), ]),
), ),
); );
const [colonyId, setColonyId] = useState(evaluation.colonyId ?? NO_COLONY);
const [notes, setNotes] = useState(evaluation.notes ?? "");
const [error, setError] = useState<string | null>(null); const [error, setError] = useState<string | null>(null);
const { data: colonies } = useQuery({
queryKey: ["colonies"],
queryFn: () => fetchColonies(),
});
const mutation = useMutation({ const mutation = useMutation({
mutationFn: () => mutationFn: () =>
updateQueenEvaluation(evaluation.id, { updateQueenEvaluation(evaluation.id, {
@@ -70,6 +90,8 @@ function EditEvaluationDialog({
swarmingTendency: Number(scores.swarmingTendency), swarmingTendency: Number(scores.swarmingTendency),
broodPattern: Number(scores.broodPattern), broodPattern: Number(scores.broodPattern),
honeyYield: Number(scores.honeyYield), honeyYield: Number(scores.honeyYield),
colonyId: colonyId === NO_COLONY ? undefined : colonyId,
notes: notes || undefined,
}), }),
onSuccess: () => { onSuccess: () => {
void queryClient.invalidateQueries({ void queryClient.invalidateQueries({
@@ -136,6 +158,38 @@ function EditEvaluationDialog({
/> />
</div> </div>
))} ))}
<div className="flex flex-col gap-2">
<Label htmlFor={`evaluation-edit-colony-${evaluation.id}`}>Volk</Label>
<Select
value={colonyId}
onValueChange={(value) => setColonyId(value ?? NO_COLONY)}
>
<SelectTrigger
id={`evaluation-edit-colony-${evaluation.id}`}
className="w-full"
>
<SelectValue placeholder="Kein Volk" />
</SelectTrigger>
<SelectContent>
<SelectItem value={NO_COLONY}>Kein Volk</SelectItem>
{(colonies ?? []).map((colony) => (
<SelectItem key={colony.id} value={colony.id}>
{colony.code}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
<div className="flex flex-col gap-2">
<Label htmlFor={`evaluation-edit-notes-${evaluation.id}`}>
Notizen
</Label>
<Textarea
id={`evaluation-edit-notes-${evaluation.id}`}
value={notes}
onChange={(event) => setNotes(event.target.value)}
/>
</div>
{error ? ( {error ? (
<p role="alert" className="text-sm text-destructive"> <p role="alert" className="text-sm text-destructive">
{error} {error}
@@ -2,9 +2,12 @@
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 { updateTaskTemplate } from "@/lib/task-templates/api"; import { updateTaskTemplate } from "@/lib/task-templates/api";
import { TaskTemplate, TaskTemplateRecurrenceType } from "@/lib/task-templates/types"; import { TaskTemplate, TaskTemplateRecurrenceType } from "@/lib/task-templates/types";
import { TaskPriority } from "@/lib/tasks/types";
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";
@@ -25,6 +28,10 @@ import {
DialogTrigger, DialogTrigger,
} from "@/components/ui/dialog"; } from "@/components/ui/dialog";
/** Sentinel for "nobody assigned" - an empty select value renders as the
* placeholder instead of a choosable entry. */
const NO_ASSIGNEE = "__none__";
const RECURRENCE_LABELS: Record<TaskTemplateRecurrenceType, string> = { const RECURRENCE_LABELS: Record<TaskTemplateRecurrenceType, string> = {
YEARLY: "Jährlich", YEARLY: "Jährlich",
MONTHLY: "Monatlich", MONTHLY: "Monatlich",
@@ -61,8 +68,23 @@ export function EditTaskTemplateDialog({ template }: { template: TaskTemplate })
template.recurrenceDayOfMonth?.toString() ?? "1", template.recurrenceDayOfMonth?.toString() ?? "1",
); );
const [intervalDays, setIntervalDays] = useState(template.intervalDays?.toString() ?? "14"); const [intervalDays, setIntervalDays] = useState(template.intervalDays?.toString() ?? "14");
const [priority, setPriority] = useState<TaskPriority>(
template.priority ?? "NORMAL",
);
const [assignedUserId, setAssignedUserId] = useState(
template.assignedUserId ?? NO_ASSIGNEE,
);
const [active, setActive] = useState(template.active);
const [error, setError] = useState<string | null>(null); const [error, setError] = useState<string | null>(null);
// Assigning needs users:read, which not every role has - if the request
// fails the field is simply omitted rather than breaking the dialog.
const { data: users } = useQuery({
queryKey: ["users"],
queryFn: () => fetchUsers(),
retry: false,
});
const mutation = useMutation({ const mutation = useMutation({
mutationFn: () => mutationFn: () =>
updateTaskTemplate(template.id, { updateTaskTemplate(template.id, {
@@ -75,6 +97,10 @@ export function EditTaskTemplateDialog({ template }: { template: TaskTemplate })
? Number(recurrenceDayOfMonth) ? Number(recurrenceDayOfMonth)
: undefined, : undefined,
intervalDays: recurrenceType === "INTERVAL_DAYS" ? Number(intervalDays) : undefined, intervalDays: recurrenceType === "INTERVAL_DAYS" ? Number(intervalDays) : undefined,
priority,
assignedUserId:
assignedUserId === NO_ASSIGNEE ? undefined : assignedUserId,
active,
}), }),
onSuccess: () => { onSuccess: () => {
void queryClient.invalidateQueries({ queryKey: ["task-templates"] }); void queryClient.invalidateQueries({ queryKey: ["task-templates"] });
@@ -190,6 +216,60 @@ export function EditTaskTemplateDialog({ template }: { template: TaskTemplate })
/> />
</div> </div>
) : null} ) : null}
<div className="flex flex-col gap-2">
<Label htmlFor="template-edit-priority">Priorität</Label>
<Select
value={priority}
onValueChange={(value) =>
setPriority((value ?? priority) as TaskPriority)
}
>
<SelectTrigger id="template-edit-priority" className="w-full">
<SelectValue>{TASK_PRIORITY_LABELS[priority]}</SelectValue>
</SelectTrigger>
<SelectContent>
{TASK_PRIORITIES.map((option) => (
<SelectItem key={option} value={option}>
{TASK_PRIORITY_LABELS[option]}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
{users ? (
<div className="flex flex-col gap-2">
<Label htmlFor="template-edit-assignee">Zugewiesen an</Label>
<Select
value={assignedUserId}
onValueChange={(value) => setAssignedUserId(value ?? NO_ASSIGNEE)}
>
<SelectTrigger id="template-edit-assignee" className="w-full">
<SelectValue placeholder="Niemand" />
</SelectTrigger>
<SelectContent>
<SelectItem value={NO_ASSIGNEE}>Niemand</SelectItem>
{users.map((user) => (
<SelectItem key={user.id} value={user.id}>
{user.name}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
) : null}
<label
className="flex h-9 items-center gap-3 text-sm"
htmlFor="template-edit-active"
>
<input
id="template-edit-active"
type="checkbox"
className="size-4.5"
checked={active}
onChange={(event) => setActive(event.target.checked)}
/>
Aktiv – eine deaktivierte Vorlage erzeugt keine Aufgaben mehr
</label>
{error ? ( {error ? (
<p role="alert" className="text-sm text-destructive"> <p role="alert" className="text-sm text-destructive">
{error} {error}
@@ -5,8 +5,18 @@ import { Pencil } from "lucide-react";
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { toast } from "sonner"; import { toast } from "sonner";
import { updateTask } from "@/lib/tasks/api"; import { updateTask } from "@/lib/tasks/api";
import { Task, TaskPriority, TaskRelatedType } from "@/lib/tasks/types"; import {
import { TASK_PRIORITIES, TASK_PRIORITY_LABELS } from "@/lib/tasks/labels"; Task,
TaskPriority,
TaskRelatedType,
TaskStatus,
} from "@/lib/tasks/types";
import {
TASK_PRIORITIES,
TASK_PRIORITY_LABELS,
TASK_STATUSES,
TASK_STATUS_LABELS,
} from "@/lib/tasks/labels";
import { fetchUsers } from "@/lib/users/api"; 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";
@@ -38,6 +48,7 @@ export function TaskEditDialog({ task }: { task: Task }) {
const [assignedUserId, setAssignedUserId] = useState(task.assignedUserId ?? ""); const [assignedUserId, setAssignedUserId] = useState(task.assignedUserId ?? "");
const [relatedType, setRelatedType] = useState<TaskRelatedType>(task.relatedType); const [relatedType, setRelatedType] = useState<TaskRelatedType>(task.relatedType);
const [relatedId, setRelatedId] = useState(task.relatedId ?? ""); const [relatedId, setRelatedId] = useState(task.relatedId ?? "");
const [status, setStatus] = useState<TaskStatus>(task.status);
// Assigning a task requires users:read, which not every role has - if the // Assigning a task requires users:read, which not every role has - if the
// request fails, the assignee field below is simply omitted rather than // request fails, the assignee field below is simply omitted rather than
@@ -58,6 +69,7 @@ export function TaskEditDialog({ task }: { task: Task }) {
assignedUserId: assignedUserId || undefined, assignedUserId: assignedUserId || undefined,
relatedType, relatedType,
relatedId: relatedType === "GENERAL" ? undefined : relatedId || undefined, relatedId: relatedType === "GENERAL" ? undefined : relatedId || undefined,
status,
}), }),
onSuccess: () => { onSuccess: () => {
void queryClient.invalidateQueries({ queryKey: ["tasks"] }); void queryClient.invalidateQueries({ queryKey: ["tasks"] });
@@ -138,6 +150,30 @@ export function TaskEditDialog({ task }: { task: Task }) {
</Select> </Select>
</div> </div>
) : null} ) : null}
<div className="flex flex-col gap-2">
<Label htmlFor="task-edit-status">Status</Label>
<Select
value={status}
onValueChange={(value) =>
setStatus((value ?? status) as TaskStatus)
}
>
<SelectTrigger id="task-edit-status" className="w-full">
<SelectValue>{TASK_STATUS_LABELS[status]}</SelectValue>
</SelectTrigger>
<SelectContent>
{TASK_STATUSES.map((option) => (
<SelectItem key={option} value={option}>
{TASK_STATUS_LABELS[option]}
</SelectItem>
))}
</SelectContent>
</Select>
<p className="text-xs text-muted-foreground">
Hier lässt sich eine versehentlich erledigte oder abgebrochene
Aufgabe wieder öffnen.
</p>
</div>
<TaskRelatedField <TaskRelatedField
idPrefix="task-edit-related" idPrefix="task-edit-related"
relatedType={relatedType} relatedType={relatedType}