feat: add appointment calendar frontend (Phase 1H)
- /termine page with list/calendar view toggle - AppointmentList: date-grouped list with type/case filtering, summary cards - AppointmentCalendar: month grid with colored type dots, clickable days/appointments - AppointmentModal: create/edit/delete with case linking, type selection, location
This commit is contained in:
99
frontend/src/app/(app)/termine/page.tsx
Normal file
99
frontend/src/app/(app)/termine/page.tsx
Normal file
@@ -0,0 +1,99 @@
|
||||
"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[]>("/api/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={appointments || []}
|
||||
onAppointmentClick={handleEdit}
|
||||
/>
|
||||
)}
|
||||
|
||||
<AppointmentModal
|
||||
open={modalOpen}
|
||||
onClose={handleClose}
|
||||
appointment={editingAppointment}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user