'use client' import * as React from 'react' import { useQuery } from '@tanstack/react-query' import type { LegacyColumnDef as ColumnDef } from '@tanstack/react-table/legacy' import { useT } from '@open-mercato/shared/lib/i18n/context' import { DataTable } from '@open-mercato/ui/backend/DataTable' import { Button } from '@open-mercato/ui/primitives/button' import { Input } from '@open-mercato/ui/primitives/input' import { Label } from '@open-mercato/ui/primitives/label' import { StatusBadge } from '@open-mercato/ui/primitives/status-badge' import { apiCallOrThrow } from '@open-mercato/ui/backend/utils/apiCall' const PAGE_SIZE = 50 type ModerationCategory = { flagged: boolean; score: number } type ModerationFlagRow = { id: string tenantId: string organizationId: string | null agentId: string userId: string providerId: string modelId: string categories: Record createdAt: string } type ModerationFlagsResponse = { items: ModerationFlagRow[] total: number page: number pageSize: number totalIsCapped?: boolean } async function fetchModerationFlags(params: { page: number from: string to: string agentId: string userId: string }): Promise { const search = new URLSearchParams() search.set('page', String(params.page)) search.set('pageSize', String(PAGE_SIZE)) if (params.from) search.set('from', params.from) if (params.to) search.set('to', params.to) if (params.agentId) search.set('agentId', params.agentId) if (params.userId) search.set('userId', params.userId) const { result, status } = await apiCallOrThrow( `/api/ai_assistant/moderation-flags?${search.toString()}`, undefined, { errorMessage: 'Failed to load moderation flags' }, ) if (!result) throw new Error(`Failed to load moderation flags (${status})`) return result } function flaggedCategoryNames(categories: Record): string[] { return Object.entries(categories) .filter(([, value]) => value.flagged) .map(([name]) => name) } export function AiModerationFlagsPageClient() { const t = useT() const [page, setPage] = React.useState(1) const [from, setFrom] = React.useState('') const [to, setTo] = React.useState('') const [agentId, setAgentId] = React.useState('') const [userId, setUserId] = React.useState('') const [applied, setApplied] = React.useState({ from: '', to: '', agentId: '', userId: '' }) const query = useQuery({ queryKey: ['ai_assistant', 'moderation_flags', page, applied], queryFn: () => fetchModerationFlags({ page, ...applied }), retry: false, }) const applyFilters = React.useCallback(() => { setPage(1) setApplied({ from, to, agentId, userId }) }, [from, to, agentId, userId]) const columns = React.useMemo[]>( () => [ { accessorKey: 'agentId', header: t('ai_assistant.moderationFlags.columns.agent', 'Agent'), }, { accessorKey: 'userId', header: t('ai_assistant.moderationFlags.columns.user', 'User'), meta: { truncate: true, maxWidth: 220 }, }, { accessorKey: 'categories', header: t('ai_assistant.moderationFlags.columns.categories', 'Categories'), cell: ({ row }) => { const names = flaggedCategoryNames(row.original.categories) if (names.length === 0) { return — } return (
{names.map((name) => ( {name} ))}
) }, }, { accessorKey: 'createdAt', header: t('ai_assistant.moderationFlags.columns.createdAt', 'Flagged at'), cell: ({ row }) => new Date(row.original.createdAt).toLocaleString(), }, ], [t], ) const total = query.data?.total ?? 0 const totalIsCapped = query.data?.totalIsCapped === true return (

{t('ai_assistant.moderationFlags.title', 'Moderation flags')}

{t( 'ai_assistant.moderationFlags.subtitle', 'Inputs blocked by the content safety filter. Category flags and scores only — no prompt content is stored.', )}

setFrom(e.target.value)} />
setTo(e.target.value)} />
setAgentId(e.target.value)} placeholder={t('ai_assistant.moderationFlags.filters.agentPlaceholder', 'module.agent')} />
setUserId(e.target.value)} />
columns={columns} data={query.data?.items ?? []} isLoading={query.isLoading} error={query.error ? (query.error as Error).message : undefined} emptyState={t('ai_assistant.moderationFlags.empty', 'No flagged messages.')} pagination={{ page, pageSize: PAGE_SIZE, total, totalPages: Math.max(1, Math.ceil(total / PAGE_SIZE)), totalIsCapped, onPageChange: setPage, }} />
) } export default AiModerationFlagsPageClient