import { useEffect, useState } from 'react'; import type { DashboardOverview, DashboardRoomActivity, StatusSnapshot, } from './api'; import type { Locale, Messages } from './i18n'; import { RoomCardV2, type RoomEntryWithService } from './RoomCardV2'; import { EmptyState } from './EmptyState'; import type { RoomActivityMap } from './useRoomActivity'; type InboxItem = DashboardOverview['inbox'][number]; type RoomFilter = 'all' | 'processing' | 'waiting' | 'inactive'; type RoomSort = 'recent' | 'name' | 'queue'; const ROOM_FILTER_ORDER: RoomFilter[] = [ 'all', 'processing', 'waiting', 'inactive', ]; export interface RoomBoardV2Props { createRequestId: () => string; formatDate: (value: string | null | undefined, locale: Locale) => string; formatDuration: (value: number | null, t: Messages) => string; formatLiveElapsed: (value: number, t: Messages) => string; inbox: InboxItem[]; locale: Locale; onSelectedJidChange: (jid: string | null) => void; onSendRoomMessage: ( roomJid: string, text: string, requestId: string, ) => Promise; pendingMessages: Record< string, Array >; roomActivity: RoomActivityMap; roomActivityLoading: boolean; roomMessageKey: string | null; selectedJid: string | null; senderRoleClass: (value: string | null | undefined) => string; snapshots: StatusSnapshot[]; statusLabel: (status: string, t: Messages) => string; t: Messages; } function roomFilterLabel( filter: RoomFilter, statusLabel: (status: string, t: Messages) => string, t: Messages, ): string { if (filter === 'all') return t.rooms.filterAll; return statusLabel(filter, t); } function roomSortLabel(sort: RoomSort, t: Messages): string { if (sort === 'recent') return t.rooms.sortRecent; if (sort === 'queue') return t.rooms.sortQueue; return t.rooms.sortName; } function scrollDetailToBottom() { if (typeof window === 'undefined') return; const detail = document.querySelector('.rooms-detail') as HTMLElement | null; if (!detail) return; const tick = (n: number) => { detail.scrollTop = detail.scrollHeight; if (n > 0) requestAnimationFrame(() => tick(n - 1)); }; requestAnimationFrame(() => tick(3)); } interface RoomsToolbarProps { counts: Record; filter: RoomFilter; onFilterChange: (filter: RoomFilter) => void; onSortChange: (sort: RoomSort) => void; sort: RoomSort; statusLabel: (status: string, t: Messages) => string; t: Messages; } function RoomsToolbar({ counts, filter, onFilterChange, onSortChange, sort, statusLabel, t, }: RoomsToolbarProps) { return (
{ROOM_FILTER_ORDER.map((filterKey) => ( ))}
); } interface RoomsListProps { entries: RoomEntryWithService[]; inbox: InboxItem[]; onSelect: (jid: string) => void; selectedJid: string | null; t: Messages; } function RoomsList({ entries, inbox, onSelect, selectedJid, t, }: RoomsListProps) { return ( ); } interface RoomsDetailProps extends Pick< RoomBoardV2Props, | 'formatDate' | 'formatDuration' | 'formatLiveElapsed' | 'locale' | 'pendingMessages' | 'roomActivity' | 'roomActivityLoading' | 'roomMessageKey' | 'senderRoleClass' | 'statusLabel' | 't' > { drafts: Record; inbox: InboxItem[]; onBack: () => void; onDraftChange: (jid: string, value: string) => void; onSubmit: (jid: string) => void; selectedEntry: RoomEntryWithService | null; } function RoomsDetail({ drafts, formatDate, formatDuration, formatLiveElapsed, inbox, locale, onBack, onDraftChange, onSubmit, pendingMessages, roomActivity, roomActivityLoading, roomMessageKey, selectedEntry, senderRoleClass, statusLabel, t, }: RoomsDetailProps) { return (
{selectedEntry ? ( item.roomJid === selectedEntry.jid, )} key={`${selectedEntry.serviceId}:${selectedEntry.jid}`} locale={locale} onDraftChange={(value) => onDraftChange(selectedEntry.jid, value)} onSendMessage={() => onSubmit(selectedEntry.jid)} onToggle={() => {}} pendingMessages={pendingMessages[selectedEntry.jid] ?? []} pinned={true} senderRoleClass={senderRoleClass} statusLabel={statusLabel} t={t} /> ) : ( {t.rooms.empty} )}
); } export function RoomBoardV2({ createRequestId, formatDate, formatDuration, formatLiveElapsed, inbox, onSendRoomMessage, pendingMessages, roomActivity, roomActivityLoading, roomMessageKey, selectedJid, locale, onSelectedJidChange, senderRoleClass, snapshots, statusLabel, t, }: RoomBoardV2Props) { const [filter, setFilter] = useState('all'); const [sort, setSort] = useState('recent'); const [drafts, setDrafts] = useState>({}); const [mobileDetailOpen, setMobileDetailOpen] = useState(false); const allEntries: RoomEntryWithService[] = snapshots.flatMap((snapshot) => snapshot.entries.map((entry) => ({ ...entry, serviceId: snapshot.serviceId, })), ); const counts = { all: allEntries.length, processing: allEntries.filter((entry) => entry.status === 'processing') .length, waiting: allEntries.filter((entry) => entry.status === 'waiting').length, inactive: allEntries.filter((entry) => entry.status === 'inactive').length, }; const filtered = allEntries.filter( (entry) => filter === 'all' || entry.status === filter, ); const sorted = [...filtered].sort((a, b) => { if (sort === 'name') return a.name.localeCompare(b.name); if (sort === 'queue') { const aQ = a.pendingTasks + (a.pendingMessages ? 1 : 0); const bQ = b.pendingTasks + (b.pendingMessages ? 1 : 0); return bQ - aQ; } const aA = roomActivity[a.jid]?.pairedTask?.updatedAt; const bA = roomActivity[b.jid]?.pairedTask?.updatedAt; const aT = aA ? new Date(aA).getTime() : (a.elapsedMs ?? 0); const bT = bA ? new Date(bA).getTime() : (b.elapsedMs ?? 0); return bT - aT; }); const selectedEntry = sorted.find((entry) => entry.jid === selectedJid) ?? sorted[0] ?? null; useEffect(() => { const nextJid = selectedEntry?.jid ?? null; if (nextJid !== selectedJid) { onSelectedJidChange(nextJid); setMobileDetailOpen(false); } }, [onSelectedJidChange, selectedEntry?.jid, selectedJid]); if (allEntries.length === 0) { return {t.rooms.empty}; } function setDraft(jid: string, value: string) { setDrafts((previous) => ({ ...previous, [jid]: value })); } async function submitRoomMessage(jid: string) { const text = drafts[jid]?.trim(); if (!text) return; scrollDetailToBottom(); const success = await onSendRoomMessage(jid, text, createRequestId()); if (success) { setDraft(jid, ''); scrollDetailToBottom(); } } return (
{sorted.length === 0 ? ( {t.rooms.empty} ) : (
{ onSelectedJidChange(jid); setMobileDetailOpen(true); }} selectedJid={selectedEntry?.jid ?? null} t={t} /> setMobileDetailOpen(false)} onDraftChange={setDraft} onSubmit={(jid) => void submitRoomMessage(jid)} pendingMessages={pendingMessages} roomActivity={roomActivity} roomActivityLoading={roomActivityLoading} roomMessageKey={roomMessageKey} selectedEntry={selectedEntry} senderRoleClass={senderRoleClass} statusLabel={statusLabel} t={t} />
)}
); }