import { useMemo, useState } from 'react'; import type { DashboardInboxAction, DashboardOverview, DashboardTask, DashboardTaskAction, } from './api'; import { formatDate } from './dashboardHelpers'; import { EmptyState } from './EmptyState'; import type { Locale, Messages } from './i18n'; import { TaskActionButtons } from './TaskActionButtons'; export type InboxItem = DashboardOverview['inbox'][number]; export type InboxActionKey = `${string}:${DashboardInboxAction}`; type InboxFilter = 'all' | InboxItem['kind']; export interface InboxPanelProps { inboxActionKey: InboxActionKey | null; locale: Locale; onInboxAction: (item: InboxItem, action: DashboardInboxAction) => void; onTaskAction: (task: DashboardTask, action: DashboardTaskAction) => void; overview: DashboardOverview; taskActionKey: string | null; tasks: DashboardTask[]; t: Messages; } const INBOX_FILTERS: InboxFilter[] = [ 'all', 'ci-failure', 'approval', 'reviewer-request', 'arbiter-request', 'pending-room', 'mention', ]; function sanitizeInboxText(value: string | null | undefined): string { if (!value) return ''; return value .replace(/<\/?internal[^>]*>/gi, '') .replace(/<\/?intern\.{3}/gi, '') .replace(/<\/?[a-z][a-z0-9-]*[^>]*>/gi, '') .replace(/\s{2,}/g, ' ') .trim(); } function inboxActionsFor(item: InboxItem): DashboardInboxAction[] { if ( item.source === 'paired-task' && (item.kind === 'reviewer-request' || item.kind === 'approval' || item.kind === 'arbiter-request') ) { return ['run', 'decline', 'dismiss']; } return ['dismiss']; } function inboxActionLabel( item: InboxItem, action: DashboardInboxAction, t: Messages, ): string { if (action === 'dismiss') return t.inbox.actions.dismiss; if (action === 'decline') return t.inbox.actions.decline; if (item.kind === 'reviewer-request') return t.inbox.actions.runReview; if (item.kind === 'approval') return t.inbox.actions.finalize; if (item.kind === 'arbiter-request') return t.inbox.actions.runArbiter; return t.inbox.actions.run; } function inboxTargetHref(item: InboxItem): string | null { if (item.taskId) return '#/scheduled'; if (item.roomJid || item.groupFolder) return '#/rooms'; return null; } interface InboxCardProps { inboxActionKey: InboxActionKey | null; item: InboxItem; locale: Locale; onInboxAction: (item: InboxItem, action: DashboardInboxAction) => void; onTaskAction: (task: DashboardTask, action: DashboardTaskAction) => void; taskActionKey: string | null; tasks: DashboardTask[]; t: Messages; } function InboxCard({ inboxActionKey, item, locale, onInboxAction, onTaskAction, taskActionKey, tasks, t, }: InboxCardProps) { const href = inboxTargetHref(item); const linkedTask = item.source === 'scheduled-task' && item.taskId ? tasks.find((task) => task.id === item.taskId) : undefined; const inboxActions = inboxActionsFor(item); return (
{t.inbox.kinds[item.kind]} {sanitizeInboxText(item.title) || item.title}
{t.inbox.severity[item.severity]} {item.occurrences > 1 ? ( x{item.occurrences} ) : null}

{sanitizeInboxText(item.summary) || t.inbox.noSummary}

{t.inbox.occurred} {formatDate(item.occurredAt, locale)} {t.inbox.source} {item.source} {t.inbox.target} {item.taskId ?? item.roomName ?? item.groupFolder ?? item.roomJid ?? '-'}
{href ? ( {item.taskId ? t.inbox.openTask : t.inbox.openRoom} ) : null} {linkedTask ? ( ) : null} {inboxActions.length > 0 ? (
{inboxActions.map((action) => { const actionKey: InboxActionKey = `${item.id}:${action}`; const busy = inboxActionKey === actionKey; return ( ); })}
) : null}
); } export function InboxPanel({ overview, tasks, locale, onInboxAction, onTaskAction, inboxActionKey, taskActionKey, t, }: InboxPanelProps) { const [filter, setFilter] = useState('all'); const items = overview.inbox ?? []; const counts = useMemo(() => { const next: Record = { all: items.length, 'pending-room': 0, 'reviewer-request': 0, approval: 0, 'arbiter-request': 0, 'ci-failure': 0, mention: 0, }; for (const item of items) next[item.kind] += 1; return next; }, [items]); const filteredItems = filter === 'all' ? items : items.filter((item) => item.kind === filter); const severityCounts = items.reduce( (acc, item) => { acc[item.severity] += 1; return acc; }, { error: 0, warn: 0, info: 0 }, ); if (items.length === 0) { return {t.inbox.empty}; } return (
{t.inbox.total} {items.length}
{t.inbox.severity.error} {severityCounts.error}
{t.inbox.severity.warn} {severityCounts.warn}
{t.inbox.severity.info} {severityCounts.info}
{INBOX_FILTERS.map((item) => { if (item !== 'all' && counts[item] === 0) return null; const label = item === 'all' ? t.inbox.all : t.inbox.kinds[item]; return ( ); })}
{filteredItems.map((item) => ( ))}
); }