Implement missing UI button features, filters, search, and view toggles across all modules

This commit is contained in:
t.indorf
2026-05-21 10:44:23 +02:00
parent e7eb43462c
commit 84aae1d72e
18 changed files with 3268 additions and 958 deletions
@@ -1,7 +1,7 @@
"use client";
import type { ComponentProps, FormEvent } from "react";
import { useMemo, useState, useTransition } from "react";
import { useEffect, useMemo, useState, useTransition } from "react";
import { TerminType } from "@prisma/client";
import { Plus } from "lucide-react";
import { toast } from "sonner";
@@ -18,7 +18,7 @@ import {
} from "@/components/ui/dialog";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { createTerminAdmin } from "../actions";
import { createTerminAdmin, updateTerminAdmin } from "../actions";
import { Textarea } from "@/components/ui/textarea";
type AdminTerminFormState = {
@@ -43,12 +43,22 @@ type AdminTerminModalProps = {
triggerLabel?: string;
triggerVariant?: ComponentProps<typeof Button>["variant"];
triggerClassName?: string;
existingTermin?: {
id: string;
title: string;
description: string | null;
type: TerminType;
startDate: Date;
endDate: Date;
allDay: boolean;
};
};
export function AdminTerminModal({
triggerLabel = "Termin direkt anlegen",
triggerVariant,
triggerClassName = "gap-2",
existingTermin,
}: AdminTerminModalProps) {
const [open, setOpen] = useState(false);
const [formState, setFormState] = useState<AdminTerminFormState>(initialFormState);
@@ -56,6 +66,24 @@ export function AdminTerminModal({
const minDateTime = useMemo(() => getLocalDateTimeInputValue(new Date()), []);
const minEndDateTime = formState.startDate || minDateTime;
// Initialize and reset form fields when modal opens
useEffect(() => {
if (open) {
if (existingTermin) {
setFormState({
title: existingTermin.title,
description: existingTermin.description || "",
type: existingTermin.type,
startDate: getLocalDateTimeInputValue(existingTermin.startDate),
endDate: getLocalDateTimeInputValue(existingTermin.endDate),
allDay: existingTermin.allDay,
});
} else {
setFormState(initialFormState);
}
}
}, [open, existingTermin]);
const updateField = <TKey extends keyof AdminTerminFormState>(
key: TKey,
value: AdminTerminFormState[TKey],
@@ -76,7 +104,8 @@ export function AdminTerminModal({
toast.error("Bitte Start und Ende ausfüllen.");
return;
}
if (formState.startDate < minDateTime) {
// Only enforce future start date when creating a new event
if (!existingTermin && formState.startDate < minDateTime) {
toast.error("Der Startzeitpunkt darf nicht in der Vergangenheit liegen.");
return;
}
@@ -95,12 +124,24 @@ export function AdminTerminModal({
};
startTransition(async () => {
const res = await createTerminAdmin(data);
let res;
if (existingTermin) {
res = await updateTerminAdmin(existingTermin.id, data);
} else {
res = await createTerminAdmin(data);
}
if (res.error) {
toast.error(res.error);
} else {
toast.success("Termin wurde direkt angelegt!");
setFormState(initialFormState);
toast.success(
existingTermin
? "Termin wurde erfolgreich aktualisiert!"
: "Termin wurde direkt angelegt!"
);
if (!existingTermin) {
setFormState(initialFormState);
}
setOpen(false);
}
});
@@ -110,16 +151,20 @@ export function AdminTerminModal({
<Dialog open={open} onOpenChange={setOpen}>
<DialogTrigger asChild>
<Button variant={triggerVariant} className={triggerClassName}>
<Plus className="h-4 w-4" />
{!existingTermin && <Plus className="h-4 w-4" />}
{triggerLabel}
</Button>
</DialogTrigger>
<DialogContent>
<form onSubmit={handleSubmit}>
<DialogHeader>
<DialogTitle>Termin anlegen (Admin)</DialogTitle>
<DialogTitle>
{existingTermin ? "Termin bearbeiten (Admin)" : "Termin anlegen (Admin)"}
</DialogTitle>
<DialogDescription>
Dieser Termin wird sofort freigegeben und ist für alle sichtbar.
{existingTermin
? "Nimm Änderungen an diesem Termin vor. Die Änderungen werden sofort wirksam."
: "Dieser Termin wird sofort freigegeben und ist für alle sichtbar."}
</DialogDescription>
</DialogHeader>
@@ -177,7 +222,7 @@ export function AdminTerminModal({
id="startDate"
name="startDate"
type="datetime-local"
min={minDateTime}
min={existingTermin ? undefined : minDateTime}
value={formState.startDate}
onChange={(event) => updateField("startDate", event.target.value)}
required
@@ -189,7 +234,7 @@ export function AdminTerminModal({
id="endDate"
name="endDate"
type="datetime-local"
min={minEndDateTime}
min={formState.startDate || (existingTermin ? undefined : minDateTime)}
value={formState.endDate}
onChange={(event) => updateField("endDate", event.target.value)}
required
@@ -217,7 +262,7 @@ export function AdminTerminModal({
Abbrechen
</Button>
<Button type="submit" disabled={isPending}>
{isPending ? "Speichern..." : "Termin erstellen"}
{isPending ? "Speichern..." : existingTermin ? "Änderungen speichern" : "Termin erstellen"}
</Button>
</DialogFooter>
</form>