"use client"; import { useEffect, useRef, useState } from "react"; import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"; import { Bell, Check, CheckCheck, ExternalLink } from "lucide-react"; import { api } from "@/lib/api"; import type { Notification, NotificationListResponse } from "@/lib/types"; function getEntityLink(n: Notification): string | null { if (!n.entity_type || !n.entity_id) return null; switch (n.entity_type) { case "deadline": return `/fristen/${n.entity_id}`; case "appointment": return `/termine/${n.entity_id}`; case "case": return `/akten/${n.entity_id}`; default: return null; } } function getTypeColor(type: Notification["type"]): string { switch (type) { case "deadline_overdue": return "bg-red-500"; case "deadline_reminder": return "bg-amber-500"; case "case_update": return "bg-blue-500"; case "assignment": return "bg-violet-500"; default: return "bg-neutral-500"; } } function timeAgo(dateStr: string): string { const now = new Date(); const date = new Date(dateStr); const diffMs = now.getTime() - date.getTime(); const diffMin = Math.floor(diffMs / 60000); if (diffMin < 1) return "gerade eben"; if (diffMin < 60) return `vor ${diffMin} Min.`; const diffHours = Math.floor(diffMin / 60); if (diffHours < 24) return `vor ${diffHours} Std.`; const diffDays = Math.floor(diffHours / 24); if (diffDays === 1) return "gestern"; return `vor ${diffDays} Tagen`; } export function NotificationBell() { const [open, setOpen] = useState(false); const panelRef = useRef(null); const queryClient = useQueryClient(); const { data: unreadData } = useQuery({ queryKey: ["notifications-unread-count"], queryFn: () => api.get<{ unread_count: number }>("/api/notifications/unread-count"), refetchInterval: 30_000, }); const { data: notifData } = useQuery({ queryKey: ["notifications"], queryFn: () => api.get("/api/notifications?limit=20"), enabled: open, }); const markRead = useMutation({ mutationFn: (id: string) => api.patch(`/api/notifications/${id}/read`), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ["notifications"] }); queryClient.invalidateQueries({ queryKey: ["notifications-unread-count"], }); }, }); const markAllRead = useMutation({ mutationFn: () => api.patch("/api/notifications/read-all"), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ["notifications"] }); queryClient.invalidateQueries({ queryKey: ["notifications-unread-count"], }); }, }); // Close on click outside useEffect(() => { function handleClickOutside(e: MouseEvent) { if (panelRef.current && !panelRef.current.contains(e.target as Node)) { setOpen(false); } } if (open) { document.addEventListener("mousedown", handleClickOutside); } return () => document.removeEventListener("mousedown", handleClickOutside); }, [open]); const unreadCount = unreadData?.unread_count ?? 0; const notifications = notifData?.data ?? []; return (
{open && (
{/* Header */}

Benachrichtigungen

{unreadCount > 0 && ( )}
{/* Notification list */}
{notifications.length === 0 ? (
Keine Benachrichtigungen
) : ( notifications.map((n) => { const link = getEntityLink(n); return (
{!n.read_at && ( )}
); }) )}
)}
); }