Files
MikrotikManager/components/alerts-outbox-panel.tsx
T

30 lines
3.9 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"use client"
import {useCallback,useEffect,useRef,useState} from "react"
import type {AlertsOutboxOverview} from "@mmapp/contracts/alerts"
import {can} from "@/lib/auth"
import {getAlertsOutbox} from "@/shared/api/alerts-outbox"
import {OpsPanel} from "@/components/ops-panel"
import {Button} from "@/components/ui/button"
export function AlertsOutboxPanel({base}:{base:string}){
const [data,setData]=useState<AlertsOutboxOverview|null>(null),[busy,setBusy]=useState(false),[error,setError]=useState<string|null>(null),[receivedAt,setReceivedAt]=useState<string|null>(null)
const active=useRef(true),revision=useRef(0),controller=useRef<AbortController|null>(null)
const refresh=useCallback(async()=>{
if(!active.current||!can("mm:alerts:read"))return
controller.current?.abort();controller.current=new AbortController();const requestRevision=++revision.current;setBusy(true);setError(null)
const current=()=>active.current&&requestRevision===revision.current&&can("mm:alerts:read")
try{const result=await getAlertsOutbox(base,{signal:controller.current.signal});if(current()){setData(result);setReceivedAt(new Date().toISOString())}}catch(e){if(current())setError(e instanceof Error?e.message:"Не удалось получить очередь")}
finally{if(current())setBusy(false)}
},[base])
useEffect(()=>{const generation=revision;active.current=true;let pending=true;queueMicrotask(()=>{if(pending)void refresh()});return()=>{pending=false;active.current=false;generation.current++;controller.current?.abort()}},[refresh])
return <section aria-label="Очередь Telegram" className="min-w-0"><OpsPanel title="Очередь Telegram" description="Приём сообщения API и доставка в чат — разные состояния." headerRight={<Button type="button" variant="outline" size="sm" aria-label="Обновить очередь Telegram" disabled={busy} onClick={()=>void refresh()}>Обновить</Button>} contentClassName="space-y-3 px-5 py-4">
{error?<p role="alert" className="text-xs text-destructive break-words">{error}{data?". Показан последний успешный снимок очереди.":""}</p>:null}
<p role="status" className="text-xs text-muted-foreground">{busy?"Обновление очереди…":data?"Последний успешный ответ: "+new Date(receivedAt!).toLocaleString("ru-RU"):"Данные очереди ещё не получены."}</p>
{data?<>
<p className="text-sm">Ожидают: {data.counts.pending} · API принял: {data.counts.sent} · Завершены с ошибкой: {data.counts.failed}</p>
{data.counts.unknown||data.counts.processing||data.counts.unrecognized?<p role="status" className="text-xs break-words">Нужна проверка: неизвестный результат — {data.counts.unknown}, в обработке — {data.counts.processing}, нераспознанный статус — {data.counts.unrecognized}. Эти записи автоматически не переотправляются. Обработка может означать текущий запрос или прерванный процесс отправки; проверьте чат и журнал перед повтором.</p>:null}
<p className="text-xs text-muted-foreground">Показано {data.items.length} из {data.counts.processing+data.counts.unknown} удержанных записей. Повторная отправка из этого экрана пока недоступна.</p>
{data.items.length?<ul className="space-y-2">{data.items.map(item=><li key={item.id} className="break-words text-xs"><p className="font-medium">{item.ruleName||item.id}</p><p className="text-muted-foreground">{item.status==="unknown"?"Результат неизвестен":"Запрос начат, завершение не подтверждено"} · {new Date(item.attemptedAt).toLocaleString("ru-RU")}</p></li>)}</ul>:null}
</>:null}
</OpsPanel></section>
}