diff --git a/frontend/src/app/(app)/cases/[id]/ereignisse/[eventId]/page.tsx b/frontend/src/app/(app)/cases/[id]/ereignisse/[eventId]/page.tsx
new file mode 100644
index 0000000..7a214f5
--- /dev/null
+++ b/frontend/src/app/(app)/cases/[id]/ereignisse/[eventId]/page.tsx
@@ -0,0 +1,230 @@
+"use client";
+
+import { useQuery } from "@tanstack/react-query";
+import { useParams } from "next/navigation";
+import { api } from "@/lib/api";
+import type { CaseEvent, Case } from "@/lib/types";
+import { Breadcrumb } from "@/components/layout/Breadcrumb";
+import { NotesList } from "@/components/notes/NotesList";
+import { Skeleton } from "@/components/ui/Skeleton";
+import { format, parseISO } from "date-fns";
+import { de } from "date-fns/locale";
+import {
+ AlertTriangle,
+ FileText,
+ Scale,
+ ArrowRightLeft,
+ Calendar,
+ MessageSquare,
+ Gavel,
+ Info,
+} from "lucide-react";
+import Link from "next/link";
+
+const EVENT_TYPE_CONFIG: Record<
+ string,
+ { label: string; icon: typeof Info; color: string }
+> = {
+ status_changed: {
+ label: "Statusaenderung",
+ icon: ArrowRightLeft,
+ color: "bg-blue-50 text-blue-700",
+ },
+ deadline_created: {
+ label: "Frist erstellt",
+ icon: Calendar,
+ color: "bg-amber-50 text-amber-700",
+ },
+ deadline_completed: {
+ label: "Frist erledigt",
+ icon: Calendar,
+ color: "bg-emerald-50 text-emerald-700",
+ },
+ document_uploaded: {
+ label: "Dokument hochgeladen",
+ icon: FileText,
+ color: "bg-violet-50 text-violet-700",
+ },
+ hearing_scheduled: {
+ label: "Verhandlung angesetzt",
+ icon: Gavel,
+ color: "bg-rose-50 text-rose-700",
+ },
+ note_added: {
+ label: "Notiz hinzugefuegt",
+ icon: MessageSquare,
+ color: "bg-neutral-100 text-neutral-700",
+ },
+ case_created: {
+ label: "Akte erstellt",
+ icon: Scale,
+ color: "bg-emerald-50 text-emerald-700",
+ },
+};
+
+const DEFAULT_EVENT_CONFIG = {
+ label: "Ereignis",
+ icon: Info,
+ color: "bg-neutral-100 text-neutral-600",
+};
+
+function DetailSkeleton() {
+ return (
+
+ );
+}
+
+export default function CaseEventDetailPage() {
+ const { id: caseId, eventId } = useParams<{
+ id: string;
+ eventId: string;
+ }>();
+
+ const { data: caseData } = useQuery({
+ queryKey: ["case", caseId],
+ queryFn: () => api.get(`/cases/${caseId}`),
+ });
+
+ const {
+ data: event,
+ isLoading,
+ error,
+ } = useQuery({
+ queryKey: ["case-event", eventId],
+ queryFn: () => api.get(`/case-events/${eventId}`),
+ });
+
+ if (isLoading) return ;
+
+ if (error || !event) {
+ return (
+
+
+
+ Ereignis nicht gefunden
+
+
+ Das Ereignis existiert nicht oder Sie haben keine Berechtigung.
+
+
+ Zurueck zur Akte
+
+
+ );
+ }
+
+ const typeConfig =
+ EVENT_TYPE_CONFIG[event.event_type ?? ""] ?? DEFAULT_EVENT_CONFIG;
+ const TypeIcon = typeConfig.icon;
+
+ return (
+
+
+
+ {/* Header */}
+
+
+
+
+
+
+ {event.title}
+
+
+ {event.event_date
+ ? format(parseISO(event.event_date), "d. MMMM yyyy, HH:mm", {
+ locale: de,
+ })
+ : format(parseISO(event.created_at), "d. MMMM yyyy, HH:mm", {
+ locale: de,
+ })}
+
+
+
+
+ {/* Description */}
+ {event.description && (
+
+
+ Beschreibung
+
+
+ {event.description}
+
+
+ )}
+
+ {/* Metadata */}
+
+
+ Metadaten
+
+
+
+
- Typ:
+ -
+
+ {typeConfig.label}
+
+
+
+ {event.created_by && (
+
+
- Erstellt von:
+ - {event.created_by}
+
+ )}
+
+
- Erstellt am:
+ -
+ {format(parseISO(event.created_at), "d. MMMM yyyy, HH:mm", {
+ locale: de,
+ })}
+
+
+ {event.metadata &&
+ Object.keys(event.metadata).length > 0 &&
+ Object.entries(event.metadata).map(([key, value]) => (
+
+
- {key}:
+ - {String(value)}
+
+ ))}
+
+
+
+ {/* Notes */}
+
+
+
+
+ );
+}
diff --git a/frontend/src/app/(app)/fristen/[id]/page.tsx b/frontend/src/app/(app)/fristen/[id]/page.tsx
new file mode 100644
index 0000000..5068a24
--- /dev/null
+++ b/frontend/src/app/(app)/fristen/[id]/page.tsx
@@ -0,0 +1,250 @@
+"use client";
+
+import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
+import { useParams, useRouter } from "next/navigation";
+import { api } from "@/lib/api";
+import type { Deadline } from "@/lib/types";
+import { Breadcrumb } from "@/components/layout/Breadcrumb";
+import { NotesList } from "@/components/notes/NotesList";
+import { Skeleton } from "@/components/ui/Skeleton";
+import { format, parseISO, formatDistanceToNow, isPast } from "date-fns";
+import { de } from "date-fns/locale";
+import {
+ AlertTriangle,
+ CheckCircle2,
+ Clock,
+ ExternalLink,
+} from "lucide-react";
+import Link from "next/link";
+import { toast } from "sonner";
+
+interface DeadlineDetail extends Deadline {
+ case_number?: string;
+ case_title?: string;
+}
+
+const STATUS_CONFIG: Record<
+ string,
+ { label: string; bg: string; icon: typeof Clock }
+> = {
+ pending: { label: "Offen", bg: "bg-amber-50 text-amber-700", icon: Clock },
+ completed: {
+ label: "Erledigt",
+ bg: "bg-emerald-50 text-emerald-700",
+ icon: CheckCircle2,
+ },
+ overdue: {
+ label: "Ueberfaellig",
+ bg: "bg-red-50 text-red-700",
+ icon: AlertTriangle,
+ },
+};
+
+function getEffectiveStatus(d: DeadlineDetail): string {
+ if (d.status === "completed") return "completed";
+ if (isPast(parseISO(d.due_date))) return "overdue";
+ return "pending";
+}
+
+function DetailSkeleton() {
+ return (
+
+ );
+}
+
+export default function DeadlineDetailPage() {
+ const { id } = useParams<{ id: string }>();
+ const router = useRouter();
+ const queryClient = useQueryClient();
+
+ const {
+ data: deadline,
+ isLoading,
+ error,
+ } = useQuery({
+ queryKey: ["deadline", id],
+ queryFn: () => api.get(`/deadlines/${id}`),
+ });
+
+ const completeMutation = useMutation({
+ mutationFn: () => api.patch(`/deadlines/${id}/complete`),
+ onSuccess: () => {
+ queryClient.invalidateQueries({ queryKey: ["deadline", id] });
+ queryClient.invalidateQueries({ queryKey: ["deadlines"] });
+ queryClient.invalidateQueries({ queryKey: ["dashboard"] });
+ toast.success("Frist als erledigt markiert");
+ },
+ onError: () => toast.error("Fehler beim Abschliessen der Frist"),
+ });
+
+ if (isLoading) return ;
+
+ if (error || !deadline) {
+ return (
+
+
+
+ Frist nicht gefunden
+
+
+ Die Frist existiert nicht oder Sie haben keine Berechtigung.
+
+
+ Zurueck zu Fristen
+
+
+ );
+ }
+
+ const status = getEffectiveStatus(deadline);
+ const config = STATUS_CONFIG[status] ?? STATUS_CONFIG.pending;
+ const StatusIcon = config.icon;
+ const dueDate = parseISO(deadline.due_date);
+ const relativeTime = formatDistanceToNow(dueDate, {
+ addSuffix: true,
+ locale: de,
+ });
+
+ return (
+
+
+
+ {/* Header */}
+
+
+
+
+
+ {config.label}
+
+
+ {deadline.title}
+
+
+ {deadline.description && (
+
+ {deadline.description}
+
+ )}
+
+ {deadline.status !== "completed" && (
+
+ )}
+
+
+ {/* Due date */}
+
+
+
+ Faellig: {format(dueDate, "d. MMMM yyyy", { locale: de })}
+
+
+ ({relativeTime})
+
+
+ {deadline.warning_date && (
+
+ Warnung am:{" "}
+ {format(parseISO(deadline.warning_date), "d. MMMM yyyy", {
+ locale: de,
+ })}
+
+ )}
+ {deadline.original_due_date &&
+ deadline.original_due_date !== deadline.due_date && (
+
+ Urspruengliches Datum:{" "}
+ {format(parseISO(deadline.original_due_date), "d. MMMM yyyy", {
+ locale: de,
+ })}
+
+ )}
+ {deadline.completed_at && (
+
+ Erledigt am:{" "}
+ {format(parseISO(deadline.completed_at), "d. MMMM yyyy, HH:mm", {
+ locale: de,
+ })}
+
+ )}
+
+
+ {/* Case context */}
+ {deadline.case_id && (
+
+
+
+
+ Akte
+
+
+ {deadline.case_number
+ ? `Az. ${deadline.case_number}`
+ : "Verknuepfte Akte"}
+ {deadline.case_title && ` — ${deadline.case_title}`}
+
+
+
+ Zur Akte
+
+
+
+
+ )}
+
+ {/* Source info */}
+ {deadline.source && deadline.source !== "manual" && (
+
+
+ Quelle
+
+
+ {deadline.source === "calculated"
+ ? "Berechnet"
+ : deadline.source === "caldav"
+ ? "CalDAV Sync"
+ : deadline.source}
+ {deadline.rule_id && ` (Regel: ${deadline.rule_id})`}
+
+
+ )}
+
+ {/* Notes */}
+
+
+
+
+ );
+}
diff --git a/frontend/src/app/(app)/fristen/neu/page.tsx b/frontend/src/app/(app)/fristen/neu/page.tsx
new file mode 100644
index 0000000..e6eec13
--- /dev/null
+++ b/frontend/src/app/(app)/fristen/neu/page.tsx
@@ -0,0 +1,180 @@
+"use client";
+
+import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
+import { useRouter } from "next/navigation";
+import { api } from "@/lib/api";
+import type { Case, Deadline } from "@/lib/types";
+import { Breadcrumb } from "@/components/layout/Breadcrumb";
+import { useState } from "react";
+import { toast } from "sonner";
+
+const inputClass =
+ "w-full rounded-md border border-neutral-200 px-3 py-1.5 text-sm outline-none focus:border-neutral-400 focus:ring-1 focus:ring-neutral-400";
+const labelClass = "mb-1 block text-xs font-medium text-neutral-600";
+
+export default function NewDeadlinePage() {
+ const router = useRouter();
+ const queryClient = useQueryClient();
+
+ const [caseId, setCaseId] = useState("");
+ const [title, setTitle] = useState("");
+ const [description, setDescription] = useState("");
+ const [dueDate, setDueDate] = useState("");
+ const [warningDate, setWarningDate] = useState("");
+ const [notes, setNotes] = useState("");
+
+ const { data: casesData } = useQuery({
+ queryKey: ["cases"],
+ queryFn: () => api.get<{ cases: Case[]; total: number } | Case[]>("/cases"),
+ });
+
+ const cases = Array.isArray(casesData)
+ ? casesData
+ : Array.isArray(casesData?.cases)
+ ? casesData.cases
+ : [];
+
+ const createMutation = useMutation({
+ mutationFn: (body: Record) =>
+ api.post(`/cases/${caseId}/deadlines`, body),
+ onSuccess: (data) => {
+ queryClient.invalidateQueries({ queryKey: ["deadlines"] });
+ queryClient.invalidateQueries({ queryKey: ["dashboard"] });
+ toast.success("Frist erstellt");
+ router.push(`/fristen/${data.id}`);
+ },
+ onError: () => toast.error("Fehler beim Erstellen der Frist"),
+ });
+
+ function handleSubmit(e: React.FormEvent) {
+ e.preventDefault();
+ if (!caseId || !title.trim() || !dueDate) return;
+
+ const body: Record = {
+ title: title.trim(),
+ due_date: new Date(dueDate).toISOString(),
+ source: "manual",
+ };
+ if (description.trim()) body.description = description.trim();
+ if (warningDate) body.warning_date = new Date(warningDate).toISOString();
+ if (notes.trim()) body.notes = notes.trim();
+
+ createMutation.mutate(body);
+ }
+
+ return (
+
+
+
+
+ Neue Frist anlegen
+
+
+ Erstellen Sie eine neue Frist fuer eine Akte.
+
+
+
+
+ );
+}
diff --git a/frontend/src/app/(app)/termine/[id]/page.tsx b/frontend/src/app/(app)/termine/[id]/page.tsx
new file mode 100644
index 0000000..30ef720
--- /dev/null
+++ b/frontend/src/app/(app)/termine/[id]/page.tsx
@@ -0,0 +1,201 @@
+"use client";
+
+import { useQuery } from "@tanstack/react-query";
+import { useParams } from "next/navigation";
+import { api } from "@/lib/api";
+import type { Appointment } from "@/lib/types";
+import { Breadcrumb } from "@/components/layout/Breadcrumb";
+import { NotesList } from "@/components/notes/NotesList";
+import { Skeleton } from "@/components/ui/Skeleton";
+import { format, parseISO } from "date-fns";
+import { de } from "date-fns/locale";
+import {
+ AlertTriangle,
+ Calendar,
+ ExternalLink,
+ MapPin,
+} from "lucide-react";
+import Link from "next/link";
+
+interface AppointmentDetail extends Appointment {
+ case_number?: string;
+ case_title?: string;
+}
+
+const TYPE_LABELS: Record = {
+ hearing: "Verhandlung",
+ meeting: "Besprechung",
+ consultation: "Beratung",
+ deadline_hearing: "Fristanhoerung",
+ other: "Sonstiges",
+};
+
+const TYPE_COLORS: Record = {
+ hearing: "bg-blue-50 text-blue-700",
+ meeting: "bg-violet-50 text-violet-700",
+ consultation: "bg-emerald-50 text-emerald-700",
+ deadline_hearing: "bg-amber-50 text-amber-700",
+ other: "bg-neutral-100 text-neutral-600",
+};
+
+function DetailSkeleton() {
+ return (
+
+ );
+}
+
+export default function AppointmentDetailPage() {
+ const { id } = useParams<{ id: string }>();
+
+ const {
+ data: appointment,
+ isLoading,
+ error,
+ } = useQuery({
+ queryKey: ["appointment", id],
+ queryFn: () => api.get(`/appointments/${id}`),
+ });
+
+ if (isLoading) return ;
+
+ if (error || !appointment) {
+ return (
+
+
+
+ Termin nicht gefunden
+
+
+ Der Termin existiert nicht oder Sie haben keine Berechtigung.
+
+
+ Zurueck zu Termine
+
+
+ );
+ }
+
+ const startDate = parseISO(appointment.start_at);
+ const typeBadge = appointment.appointment_type
+ ? TYPE_COLORS[appointment.appointment_type] ?? TYPE_COLORS.other
+ : null;
+ const typeLabel = appointment.appointment_type
+ ? TYPE_LABELS[appointment.appointment_type] ?? appointment.appointment_type
+ : null;
+
+ return (
+
+
+
+ {/* Header */}
+
+
+
+ {appointment.title}
+
+ {typeBadge && typeLabel && (
+
+ {typeLabel}
+
+ )}
+
+
+
+ {/* Date & Time */}
+
+
+
+
+ {format(startDate, "EEEE, d. MMMM yyyy", { locale: de })}
+
+
+
+ {format(startDate, "HH:mm", { locale: de })} Uhr
+ {appointment.end_at && (
+ <>
+ {" "}
+ – {format(parseISO(appointment.end_at), "HH:mm", { locale: de })}{" "}
+ Uhr
+ >
+ )}
+
+
+
+ {/* Location */}
+ {appointment.location && (
+
+
+
+
+ {appointment.location}
+
+
+
+ )}
+
+ {/* Case context */}
+ {appointment.case_id && (
+
+
+
+
+ Akte
+
+
+ {appointment.case_number
+ ? `Az. ${appointment.case_number}`
+ : "Verknuepfte Akte"}
+ {appointment.case_title && ` — ${appointment.case_title}`}
+
+
+
+ Zur Akte
+
+
+
+
+ )}
+
+ {/* Description */}
+ {appointment.description && (
+
+
+ Beschreibung
+
+
+ {appointment.description}
+
+
+ )}
+
+ {/* Notes */}
+
+
+
+
+ );
+}
diff --git a/frontend/src/app/(app)/termine/neu/page.tsx b/frontend/src/app/(app)/termine/neu/page.tsx
new file mode 100644
index 0000000..826a691
--- /dev/null
+++ b/frontend/src/app/(app)/termine/neu/page.tsx
@@ -0,0 +1,206 @@
+"use client";
+
+import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
+import { useRouter } from "next/navigation";
+import { api } from "@/lib/api";
+import type { Case, Appointment } from "@/lib/types";
+import { Breadcrumb } from "@/components/layout/Breadcrumb";
+import { useState } from "react";
+import { toast } from "sonner";
+
+const APPOINTMENT_TYPES = [
+ { value: "hearing", label: "Verhandlung" },
+ { value: "meeting", label: "Besprechung" },
+ { value: "consultation", label: "Beratung" },
+ { value: "deadline_hearing", label: "Fristanhoerung" },
+ { value: "other", label: "Sonstiges" },
+];
+
+const inputClass =
+ "w-full rounded-md border border-neutral-200 px-3 py-1.5 text-sm outline-none focus:border-neutral-400 focus:ring-1 focus:ring-neutral-400";
+const labelClass = "mb-1 block text-xs font-medium text-neutral-600";
+
+export default function NewAppointmentPage() {
+ const router = useRouter();
+ const queryClient = useQueryClient();
+
+ const [caseId, setCaseId] = useState("");
+ const [title, setTitle] = useState("");
+ const [description, setDescription] = useState("");
+ const [startAt, setStartAt] = useState("");
+ const [endAt, setEndAt] = useState("");
+ const [location, setLocation] = useState("");
+ const [appointmentType, setAppointmentType] = useState("");
+
+ const { data: casesData } = useQuery({
+ queryKey: ["cases"],
+ queryFn: () => api.get<{ cases: Case[]; total: number } | Case[]>("/cases"),
+ });
+
+ const cases = Array.isArray(casesData)
+ ? casesData
+ : Array.isArray(casesData?.cases)
+ ? casesData.cases
+ : [];
+
+ const createMutation = useMutation({
+ mutationFn: (body: Record) =>
+ api.post("/appointments", body),
+ onSuccess: (data) => {
+ queryClient.invalidateQueries({ queryKey: ["appointments"] });
+ queryClient.invalidateQueries({ queryKey: ["dashboard"] });
+ toast.success("Termin erstellt");
+ router.push(`/termine/${data.id}`);
+ },
+ onError: () => toast.error("Fehler beim Erstellen des Termins"),
+ });
+
+ function handleSubmit(e: React.FormEvent) {
+ e.preventDefault();
+ if (!title.trim() || !startAt) return;
+
+ const body: Record = {
+ title: title.trim(),
+ start_at: new Date(startAt).toISOString(),
+ };
+ if (description.trim()) body.description = description.trim();
+ if (endAt) body.end_at = new Date(endAt).toISOString();
+ if (location.trim()) body.location = location.trim();
+ if (appointmentType) body.appointment_type = appointmentType;
+ if (caseId) body.case_id = caseId;
+
+ createMutation.mutate(body);
+ }
+
+ return (
+
+
+
+
+ Neuer Termin
+
+
+ Erstellen Sie einen neuen Termin.
+
+
+
+
+ );
+}
diff --git a/frontend/src/components/layout/Breadcrumb.tsx b/frontend/src/components/layout/Breadcrumb.tsx
new file mode 100644
index 0000000..d84a121
--- /dev/null
+++ b/frontend/src/components/layout/Breadcrumb.tsx
@@ -0,0 +1,29 @@
+import Link from "next/link";
+import { ChevronRight } from "lucide-react";
+
+export interface BreadcrumbItem {
+ label: string;
+ href?: string;
+}
+
+export function Breadcrumb({ items }: { items: BreadcrumbItem[] }) {
+ return (
+
+ );
+}
diff --git a/frontend/src/components/notes/NotesList.tsx b/frontend/src/components/notes/NotesList.tsx
new file mode 100644
index 0000000..dc2d832
--- /dev/null
+++ b/frontend/src/components/notes/NotesList.tsx
@@ -0,0 +1,209 @@
+"use client";
+
+import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
+import { api } from "@/lib/api";
+import type { Note } from "@/lib/types";
+import { format, parseISO } from "date-fns";
+import { de } from "date-fns/locale";
+import { Plus, Pencil, Trash2, X, Check, MessageSquare } from "lucide-react";
+import { useState } from "react";
+import { toast } from "sonner";
+
+interface NotesListProps {
+ parentType: "case" | "deadline" | "appointment" | "case_event";
+ parentId: string;
+}
+
+export function NotesList({ parentType, parentId }: NotesListProps) {
+ const queryClient = useQueryClient();
+ const queryKey = ["notes", parentType, parentId];
+
+ const [newContent, setNewContent] = useState("");
+ const [showNew, setShowNew] = useState(false);
+ const [editingId, setEditingId] = useState(null);
+ const [editContent, setEditContent] = useState("");
+
+ const { data: notes, isLoading } = useQuery({
+ queryKey,
+ queryFn: () =>
+ api.get(`/notes?${parentType}_id=${parentId}`),
+ });
+
+ const createMutation = useMutation({
+ mutationFn: (content: string) => {
+ const body: Record = {
+ content,
+ [`${parentType}_id`]: parentId,
+ };
+ return api.post("/notes", body);
+ },
+ onSuccess: () => {
+ queryClient.invalidateQueries({ queryKey });
+ setNewContent("");
+ setShowNew(false);
+ toast.success("Notiz erstellt");
+ },
+ onError: () => toast.error("Fehler beim Erstellen der Notiz"),
+ });
+
+ const updateMutation = useMutation({
+ mutationFn: ({ id, content }: { id: string; content: string }) =>
+ api.put(`/notes/${id}`, { content }),
+ onSuccess: () => {
+ queryClient.invalidateQueries({ queryKey });
+ setEditingId(null);
+ toast.success("Notiz aktualisiert");
+ },
+ onError: () => toast.error("Fehler beim Aktualisieren der Notiz"),
+ });
+
+ const deleteMutation = useMutation({
+ mutationFn: (id: string) => api.delete(`/notes/${id}`),
+ onSuccess: () => {
+ queryClient.invalidateQueries({ queryKey });
+ toast.success("Notiz geloescht");
+ },
+ onError: () => toast.error("Fehler beim Loeschen der Notiz"),
+ });
+
+ function handleCreate() {
+ if (!newContent.trim()) return;
+ createMutation.mutate(newContent.trim());
+ }
+
+ function handleUpdate(id: string) {
+ if (!editContent.trim()) return;
+ updateMutation.mutate({ id, content: editContent.trim() });
+ }
+
+ function startEdit(note: Note) {
+ setEditingId(note.id);
+ setEditContent(note.content);
+ }
+
+ const notesList = Array.isArray(notes) ? notes : [];
+
+ return (
+
+
+
Notizen
+ {!showNew && (
+
+ )}
+
+
+ {showNew && (
+
+
+ )}
+
+ {isLoading ? (
+
+ {[1, 2].map((i) => (
+
+ ))}
+
+ ) : notesList.length === 0 ? (
+
+
+
+ Keine Notizen vorhanden.
+
+
+ ) : (
+
+ {notesList.map((note) => (
+
+ {editingId === note.id ? (
+
+
+ ) : (
+
+
+
+ {note.content}
+
+
+
+
+
+
+
+ {format(parseISO(note.created_at), "d. MMM yyyy, HH:mm", {
+ locale: de,
+ })}
+ {note.updated_at !== note.created_at && " (bearbeitet)"}
+
+
+ )}
+
+ ))}
+
+ )}
+
+ );
+}
diff --git a/frontend/src/lib/types.ts b/frontend/src/lib/types.ts
index ef15a47..bb67ae6 100644
--- a/frontend/src/lib/types.ts
+++ b/frontend/src/lib/types.ts
@@ -230,6 +230,31 @@ export interface DashboardData {
upcoming_appointments: UpcomingAppointment[];
}
+// Notes
+export interface Note {
+ id: string;
+ tenant_id: string;
+ case_id?: string;
+ deadline_id?: string;
+ appointment_id?: string;
+ case_event_id?: string;
+ content: string;
+ created_by?: string;
+ created_at: string;
+ updated_at: string;
+}
+
+// Recent Activity
+export interface RecentActivity {
+ id: string;
+ event_type?: string;
+ title: string;
+ case_id: string;
+ case_number: string;
+ event_date?: string;
+ created_at: string;
+}
+
// AI Extraction types
export interface ExtractedDeadline {