104 lines
3.4 KiB
TypeScript
104 lines
3.4 KiB
TypeScript
"use client";
|
|
|
|
import { useTransition } from "react";
|
|
import { format } from "date-fns";
|
|
import { de } from "date-fns/locale";
|
|
import { Check, X, Clock, CalendarIcon } from "lucide-react";
|
|
import { toast } from "sonner";
|
|
import { Termin, User } from "@prisma/client";
|
|
|
|
import { Card, CardContent } from "@/components/ui/card";
|
|
import { Button } from "@/components/ui/button";
|
|
import { approveTermin, rejectTermin } from "../actions";
|
|
|
|
type PendingTermin = Termin & {
|
|
createdBy: { firstName: string; lastName: string } | null;
|
|
};
|
|
|
|
export function PendingAnfragen({ termine }: { termine: PendingTermin[] }) {
|
|
if (termine.length === 0) {
|
|
return (
|
|
<div className="flex flex-col items-center justify-center rounded-lg border border-dashed p-8 text-center animate-in fade-in-50">
|
|
<CalendarIcon className="h-10 w-10 text-muted-foreground mb-4" />
|
|
<h3 className="text-lg font-semibold">Keine ausstehenden Anfragen</h3>
|
|
<p className="text-sm text-muted-foreground">
|
|
Es gibt aktuell keine Terminanfragen, die bestätigt werden müssen.
|
|
</p>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div className="flex flex-col gap-4">
|
|
{termine.map((termin) => (
|
|
<PendingTerminCard key={termin.id} termin={termin} />
|
|
))}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function PendingTerminCard({ termin }: { termin: PendingTermin }) {
|
|
const [isPending, startTransition] = useTransition();
|
|
|
|
const handleApprove = () => {
|
|
startTransition(async () => {
|
|
const res = await approveTermin(termin.id);
|
|
if (res.error) {
|
|
toast.error(res.error);
|
|
} else {
|
|
toast.success("Anfrage freigegeben.");
|
|
}
|
|
});
|
|
};
|
|
|
|
const handleReject = () => {
|
|
// In a real app, you might want to ask for a rejection reason in a prompt/modal.
|
|
startTransition(async () => {
|
|
const res = await rejectTermin(termin.id);
|
|
if (res.error) {
|
|
toast.error(res.error);
|
|
} else {
|
|
toast.success("Anfrage abgelehnt.");
|
|
}
|
|
});
|
|
};
|
|
|
|
return (
|
|
<Card>
|
|
<CardContent className="flex items-center justify-between p-4 sm:p-6">
|
|
<div className="flex flex-col gap-1">
|
|
<div className="font-semibold">{termin.title}</div>
|
|
<div className="text-sm text-muted-foreground flex items-center gap-1">
|
|
<Clock className="h-3.5 w-3.5" />
|
|
{format(termin.startDate, "PP", { locale: de })}
|
|
{!termin.allDay && ` • ${format(termin.startDate, "p", { locale: de })}`}
|
|
</div>
|
|
<div className="text-sm font-medium text-primary mt-1">
|
|
Angefragt von: {termin.createdBy?.firstName} {termin.createdBy?.lastName}
|
|
</div>
|
|
</div>
|
|
|
|
<div className="flex flex-col sm:flex-row gap-2">
|
|
<Button
|
|
variant="outline"
|
|
className="text-destructive hover:bg-destructive hover:text-destructive-foreground border-destructive/20"
|
|
onClick={handleReject}
|
|
disabled={isPending}
|
|
>
|
|
<X className="h-4 w-4 sm:mr-2" />
|
|
<span className="hidden sm:inline">Ablehnen</span>
|
|
</Button>
|
|
<Button
|
|
className="bg-emerald-600 hover:bg-emerald-700 text-white"
|
|
onClick={handleApprove}
|
|
disabled={isPending}
|
|
>
|
|
<Check className="h-4 w-4 sm:mr-2" />
|
|
<span className="hidden sm:inline">Freigeben</span>
|
|
</Button>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
);
|
|
}
|