diff --git a/src/app/components/ErrorLogBell.tsx b/src/app/components/ErrorLogBell.tsx new file mode 100644 index 0000000..bce32da --- /dev/null +++ b/src/app/components/ErrorLogBell.tsx @@ -0,0 +1,136 @@ +"use client"; + +import { useState } from "react"; +import { Bell } from "lucide-react"; +import { Button } from "@/components/ui/button"; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuTrigger, +} from "@/components/ui/dropdown-menu"; +import { ScrollArea } from "@/components/ui/scroll-area"; +import { errorToast } from "@/lib/errorReporter"; +import { formatBadgeCount } from "@/lib/errorLogUnread"; +import { formatFullTime, formatTime } from "@/lib/time"; +import { useErrorLogs, type ErrorLogEntry } from "@/app/hooks/useErrorLogs"; + +function ErrorLogRow({ entry }: { entry: ErrorLogEntry }) { + const created = new Date(entry.createdAt); + return ( +
  • +
    + + {entry.source} + + +
    +

    + {entry.message} +

    + {entry.details && ( +

    + {entry.details} +

    + )} +
  • + ); +} + +export function ErrorLogBell() { + const { logs, unread, loading, markAllRead, clearAll } = useErrorLogs(); + const [open, setOpen] = useState(false); + const [confirmingClear, setConfirmingClear] = useState(false); + const [clearing, setClearing] = useState(false); + + const badge = formatBadgeCount(unread); + + const handleOpenChange = (next: boolean) => { + setOpen(next); + setConfirmingClear(false); + if (next) markAllRead(); + }; + + const handleClear = async () => { + if (!confirmingClear) { + setConfirmingClear(true); + return; + } + setClearing(true); + try { + await clearAll(); + setConfirmingClear(false); + } catch (error) { + errorToast( + "errors.clear", + error instanceof Error ? error.message : "Couldn't clear error logs." + ); + } finally { + setClearing(false); + } + }; + + return ( + + + + + +
    + Error log +
    + {loading && logs.length === 0 ? ( +

    + Loading... +

    + ) : logs.length === 0 ? ( +

    + No errors logged. +

    + ) : ( + +
      + {logs.map((entry) => ( + + ))} +
    +
    + )} + {logs.length > 0 && ( +
    + +
    + )} +
    +
    + ); +}