export const POLL_MS = 3000; // период опроса пакета, мс (как у links.html) export const MAX_POLL_ERRORS = 5; // столько ошибок «хаб не отвечает» подряд — и опрос пакета останавливается (M-4) export type BoardStatus = | { kind: "loading" } | { kind: "batch"; id: number; phase: "queued" | "running"; remaining: number; agentOffline: boolean; retrying: boolean } | { kind: "batch-done"; id: number } | { kind: "batch-failed"; id: number; error: string } | { kind: "batch-cancelled"; id: number } | { kind: "batch-lost"; id: number; reason: string } | { kind: "hub-down"; hasData: boolean; fromFile: boolean } | { kind: "file-error"; fileName: string; reason: string } | { kind: "json-applied"; fileName: string } | { kind: "error"; message: string }; // одна строка состояния доски: текст и признак предупреждения (M-5) export function statusText(s: BoardStatus): { text: string; warn: boolean } { switch (s.kind) { case "loading": return { text: "загрузка данных хаба…", warn: false }; case "batch": { const off = s.agentOffline ? "агент офлайн — пакет выполнится, когда он выйдет на связь; " : ""; const left = s.remaining ? `, осталось ~${s.remaining} с` : ""; const retry = s.retrying ? ` (хаб не отвечает, повтор через ${POLL_MS / 1000} с)` : ""; return { text: `${off}пакет #${s.id} ${s.phase === "queued" ? "в очереди" : "выполняется"}${left}…${retry}`, warn: s.retrying }; } case "batch-done": return { text: `пакет #${s.id} выполнен`, warn: false }; case "batch-failed": return { text: `пакет #${s.id} не выполнен: ${s.error}`, warn: true }; case "batch-cancelled": return { text: `пакет #${s.id} отменён`, warn: true }; case "batch-lost": return { text: `пакет #${s.id}: ${s.reason} — опрос остановлен, «Обновить из Jira» снова доступна`, warn: true }; case "hub-down": return { text: s.fromFile ? "хаб не отвечает — на доске снимок из файла" : s.hasData ? "хаб не отвечает — показаны прежние данные" : "хаб не отвечает: запустите хаб (jira-hub serve) и нажмите «Перечитать»", warn: true, }; case "file-error": return { text: `файл не разобран: ${s.reason}`, warn: true }; case "json-applied": return { text: `раскладка и фильтры из ${s.fileName} применены к данным хаба`, warn: false }; case "error": return { text: s.message, warn: true }; } }