import { useProjects } from "../api/queries.ts"; import type { Project } from "../api/types.ts"; import { MODULES } from "../modules/index.ts"; import type { HubModule } from "../modules/types.ts"; import { fmtStamp } from "../ui/format.ts"; import { IconButton, IconRefresh } from "../ui/index.ts"; import { useModuleActivity } from "./activity/activity.ts"; import { eventText } from "./activity/events.ts"; type WithStatus = HubModule & Required>; const WITH_STATUS = MODULES.filter((m): m is WithStatus => !!m.status); // строка модуля: название, агент (точка + текст), значок обновления, свежесть, событие пакета. // Живой регион role="status" стоит в строке постоянно и меняет только текст (T10.1 #94): вставка вместе с текстом не озвучивается function ModuleRow({ m, project, variant }: { m: WithStatus; project: Project; variant: "expanded" | "collapsed" }) { const act = useModuleActivity(m.name); const { status } = m; const fresh = status.freshness(project); const label = status.refresh?.label; const scope = status.scope === "all" ? "все проекты" : "проект"; const action = label ? `${label} — ${status.scope === "all" ? scope : `${scope} ${project.label}`}` : null; const a = act.agent; const agentText = a ? (a.warning || a.update_hint ? "обновите агента" : a.online ? "агент на связи" : "агент офлайн") : null; const agentHint = a ? [a.warning, a.update_hint, a.last_seen_at ? `последний выход на связь: ${fmtStamp(a.last_seen_at)}` : "выхода на связь ещё не было"].filter(Boolean).join(" · ") : undefined; const freshText = fresh.error ? `синхронизация не удалась: ${fresh.error}` : fresh.at ? `данные на ${fmtStamp(fresh.at)} · ${scope}` : "обновления ещё не было"; const ev = act.event ? eventText(act.event, label ?? "") : null; const online = !!a && a.online && !a.warning && !a.update_hint; const collapsed = variant === "collapsed"; const eventEl = {ev?.text}; const button = action && (