"use client"; import Link from "next/link"; import { useMemo } from "react"; import type { EventDTO } from "@dci/core"; import { Badge, Button, EmptyState, EventBadge, RelativeTime, SkeletonRows } from "@/components/ui"; import { cn } from "@/components/ui/cn"; import { fmtInt, fmtValue } from "@/lib/format"; import { EVENT_LABEL } from "@/lib/labels"; import { entityHref } from "@/lib/routes"; import { ErrorNote, ExtLink, Input, Pager, RefreshButton, ReviewBadge, Segmented, Select } from "./primitives"; import { AdminPageHeader } from "./shell"; import { useToast } from "./toast"; import { useAdminMutation, useAdminQuery } from "./use-admin-query"; import { useUrlState } from "./use-url-state"; const REVIEW = ["all", "pending", "approved", "auto"] as const; type Review = (typeof REVIEW)[number]; const PER_PAGE = 50; export function EventsReview() { const [params, setParams] = useUrlState(); const review = (REVIEW.includes(params.review as Review) ? params.review : "all") as Review; const page = Math.max(1, Number(params.page ?? 1) || 1); const type = params.type ?? ""; const minSig = params.min_significance ?? ""; const q = useAdminQuery("v1/events", { query: { type: type || undefined, min_significance: minSig || undefined, page, per_page: PER_PAGE }, refreshMs: 60_000 }); const { busy, run } = useAdminMutation(); const toast = useToast(); const total = Number(q.meta?.total ?? 0); const rows = useMemo(() => (q.data ?? []).filter((e) => review === "all" || e.reviewStatus === review), [q.data, review]); const counts = useMemo(() => { const c: Record = {}; for (const e of q.data ?? []) c[e.reviewStatus] = (c[e.reviewStatus] ?? 0) + 1; return c; }, [q.data]); const setStatus = async (e: EventDTO, reviewStatus: "approved" | "rejected" | "pending") => { const prev = e.reviewStatus; q.mutate((list) => list?.map((x) => (x.id === e.id ? { ...x, reviewStatus } : x)) ?? list); const r = await run<{ id: string; reviewStatus: string }>(`${e.id}:${reviewStatus}`, `events/${encodeURIComponent(e.id)}`, { method: "PATCH", body: { reviewStatus } }); if (r.ok) toast({ tone: "ok", title: `Event ${reviewStatus}`, description: e.title }); else { q.mutate((list) => list?.map((x) => (x.id === e.id ? { ...x, reviewStatus: prev } : x)) ?? list); toast({ tone: "danger", title: "Review not saved", description: r.error }); } }; return (
void q.refresh()} busy={q.refreshing} updatedAt={q.updatedAt} />} /> void q.refresh()} className="mb-3" />
setParams({ review: v === "all" ? null : v })} options={REVIEW.map((r) => ({ value: r, label: r === "all" ? "All (page)" : r[0]!.toUpperCase() + r.slice(1), count: r === "all" ? (q.data?.length ?? null) : (counts[r] ?? 0) }))} />
setParams({ min_significance: e.target.value || null, page: null })} placeholder="min significance" className="w-[150px]" mono />
{q.loading && !q.data ? ( ) : rows.length === 0 ? ( ) : (
    {rows.map((e) => (
  • = 60 ? "text-accent" : "text-ink-4")}>sig {e.significance}
    {e.title}
    {e.summary &&

    {e.summary}

    }
    {e.entity && ( {e.entity.name} )} {e.operator && {e.operator.name}} {e.countryIso2 && {e.countryIso2}} {e.sourceName} {(e.oldValue !== null && e.oldValue !== undefined) || (e.newValue !== null && e.newValue !== undefined) ? ( {e.oldValue !== null && e.oldValue !== undefined && {fmtValue(e.oldValue)}} {e.oldValue !== null && e.oldValue !== undefined && " → "} {fmtValue(e.newValue)} ) : null}
  • ))}
setParams({ page: p === 1 ? null : p })} />
)}

{fmtInt(total)} events match the type / significance filters. The review filter applies to the current page.

); }