Prevents "M.forEach is not a function" crashes when API returns error objects or unexpected shapes instead of arrays. Guards all useQuery consumers with Array.isArray checks and safe defaults for object props. Files fixed: DeadlineList, AppointmentList, TenantSwitcher, DeadlineTrafficLights, UpcomingTimeline, CaseOverviewGrid, AISummaryCard, TeamSettings, and all page-level components (dashboard, cases, fristen, termine, ai/extract).
100 lines
3.2 KiB
TypeScript
100 lines
3.2 KiB
TypeScript
"use client";
|
|
|
|
import { AppointmentList } from "@/components/appointments/AppointmentList";
|
|
import { AppointmentCalendar } from "@/components/appointments/AppointmentCalendar";
|
|
import { AppointmentModal } from "@/components/appointments/AppointmentModal";
|
|
import { useQuery } from "@tanstack/react-query";
|
|
import { api } from "@/lib/api";
|
|
import type { Appointment } from "@/lib/types";
|
|
import { Calendar, List, Plus } from "lucide-react";
|
|
import { useState } from "react";
|
|
|
|
type ViewMode = "list" | "calendar";
|
|
|
|
export default function TerminePage() {
|
|
const [view, setView] = useState<ViewMode>("list");
|
|
const [modalOpen, setModalOpen] = useState(false);
|
|
const [editingAppointment, setEditingAppointment] = useState<Appointment | null>(null);
|
|
|
|
const { data: appointments } = useQuery({
|
|
queryKey: ["appointments"],
|
|
queryFn: () => api.get<Appointment[]>("/appointments"),
|
|
});
|
|
|
|
function handleEdit(appointment: Appointment) {
|
|
setEditingAppointment(appointment);
|
|
setModalOpen(true);
|
|
}
|
|
|
|
function handleCreate() {
|
|
setEditingAppointment(null);
|
|
setModalOpen(true);
|
|
}
|
|
|
|
function handleClose() {
|
|
setModalOpen(false);
|
|
setEditingAppointment(null);
|
|
}
|
|
|
|
return (
|
|
<div className="space-y-4">
|
|
<div className="flex items-center justify-between">
|
|
<div>
|
|
<h1 className="text-lg font-semibold text-neutral-900">Termine</h1>
|
|
<p className="mt-0.5 text-sm text-neutral-500">
|
|
Alle Termine im Uberblick
|
|
</p>
|
|
</div>
|
|
<div className="flex items-center gap-2">
|
|
<button
|
|
onClick={handleCreate}
|
|
className="flex items-center gap-1.5 rounded-md bg-neutral-900 px-3 py-1.5 text-sm font-medium text-white hover:bg-neutral-800"
|
|
>
|
|
<Plus className="h-3.5 w-3.5" />
|
|
Neuer Termin
|
|
</button>
|
|
<div className="flex rounded-md border border-neutral-200 bg-white">
|
|
<button
|
|
onClick={() => setView("list")}
|
|
className={`flex items-center gap-1 rounded-l-md px-2.5 py-1.5 text-sm transition-colors ${
|
|
view === "list"
|
|
? "bg-neutral-100 font-medium text-neutral-900"
|
|
: "text-neutral-500 hover:text-neutral-700"
|
|
}`}
|
|
>
|
|
<List className="h-3.5 w-3.5" />
|
|
Liste
|
|
</button>
|
|
<button
|
|
onClick={() => setView("calendar")}
|
|
className={`flex items-center gap-1 rounded-r-md px-2.5 py-1.5 text-sm transition-colors ${
|
|
view === "calendar"
|
|
? "bg-neutral-100 font-medium text-neutral-900"
|
|
: "text-neutral-500 hover:text-neutral-700"
|
|
}`}
|
|
>
|
|
<Calendar className="h-3.5 w-3.5" />
|
|
Kalender
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{view === "list" ? (
|
|
<AppointmentList onEdit={handleEdit} />
|
|
) : (
|
|
<AppointmentCalendar
|
|
appointments={Array.isArray(appointments) ? appointments : []}
|
|
onAppointmentClick={handleEdit}
|
|
/>
|
|
)}
|
|
|
|
<AppointmentModal
|
|
open={modalOpen}
|
|
onClose={handleClose}
|
|
appointment={editingAppointment}
|
|
/>
|
|
</div>
|
|
);
|
|
}
|