import { useActionQuery, useActionMutation, } from "@agent-native/core/client/hooks"; import { useT } from "@agent-native/core/client/i18n"; import { IconAlertTriangle, IconSend } from "@tabler/icons-react"; import { useQueryClient } from "@tanstack/react-query"; import { useMemo, useState } from "react"; import { toast } from "sonner"; import { PageHeader } from "@/components/library/page-header"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { NotificationsList, type NotificationItem, type NotificationKind, } from "@/components/workspace/notifications-list"; import enMessages from "@/i18n/en-US"; export function meta() { return [{ title: enMessages.notificationsRoute.pageTitle }]; } function inLast30Days(iso: string): boolean { try { const cutoff = Date.now() - 30 * 24 * 60 * 60 * 1000; return new Date(iso).getTime() >= cutoff; } catch { return false; } } export default function NotificationsRoute() { const t = useT(); const [filter, setFilter] = useState<"all" | NotificationKind>("all"); const [replyFor, setReplyFor] = useState(null); const [replyText, setReplyText] = useState(""); const qc = useQueryClient(); const { data: aggregated, isLoading, isError, isFetching, refetch, } = useActionQuery<{ items: NotificationItem[]; }>("list-notifications", { days: 30 } as any, { retry: false }); const items: NotificationItem[] = useMemo(() => { if (aggregated?.items?.length) { return aggregated.items.filter((it) => inLast30Days(it.createdAt)); } return []; }, [aggregated]); const filtered = items.filter((i) => filter === "all" || i.kind === filter); const addComment = useActionMutation< any, { recordingId: string; content: string; threadId?: string; parentId?: string; videoTimestampMs?: number; } >("add-comment"); async function handleSendReply() { if (!replyFor) return; const content = replyText.trim(); if (!content) return; try { await addComment.mutateAsync({ recordingId: replyFor.recordingId, content, threadId: replyFor.id.replace(/^c:/, ""), }); toast.success(t("notificationsRoute.replySent")); setReplyText(""); setReplyFor(null); qc.invalidateQueries({ queryKey: ["action", "list-notifications"] }); qc.invalidateQueries({ queryKey: ["action", "list-comments"] }); } catch (err) { toast.error( err instanceof Error ? err.message : t("notificationsRoute.sendFailed"), ); } } return ( <>

{t("notificationsRoute.title")}

{t("notificationsRoute.description")}

setFilter(v as any)}> {t("notificationsRoute.all")} {t("notificationsRoute.comments")} {t("notificationsRoute.reactions")} {t("notificationsRoute.mentions")} {t("notificationsRoute.shares")}
{isLoading ? (
{t("notificationsRoute.loading")}
) : isError ? (

{t("libraryGrid.loadFailedTitle")}

) : ( )}
{replyFor ? (
{t("notificationsRoute.replyTo", { email: replyFor.authorEmail, })}{" "} {replyFor.recordingTitle}
setReplyText(e.target.value)} placeholder={t("notificationsRoute.replyPlaceholder")} onKeyDown={(e) => { if (e.key === "Enter" && !e.shiftKey) { e.preventDefault(); handleSendReply(); } }} autoFocus />
) : null}
); }