import { ChevronLeft, ChevronRight, Loader2, RefreshCw, Send } from "lucide-react"; import { useEffect, useState } from "react"; import { api, errorMessage } from "../api"; import { CreateBroadcastModal } from "../components/CreateBroadcastModal"; import { Alert, Badge, EmptyRow, Metric, PageFrame } from "../components/ui"; import { formatDate } from "../lib/format"; import type { BroadcastListResponse } from "../types"; type Cursor = { beforeID: number }; const zeroCursor: Cursor = { beforeID: 0 }; export function BroadcastsPage() { const [data, setData] = useState(null); const [history, setHistory] = useState([]); const [cursor, setCursor] = useState(zeroCursor); const [busy, setBusy] = useState(false); const [error, setError] = useState(""); const [createModalOpen, setCreateModalOpen] = useState(false); async function fetchPage(at: Cursor) { setBusy(true); setError(""); const params = new URLSearchParams({ limit: "50" }); if (at.beforeID) { params.set("before_id", String(at.beforeID)); } try { const result = await api.broadcasts(params); setData(result); return result; } catch (err) { setError(errorMessage(err)); return null; } finally { setBusy(false); } } async function loadFresh() { setHistory([]); setCursor(zeroCursor); await fetchPage(zeroCursor); } async function loadNext() { if (!data?.has_more) return; const at = { beforeID: data.next_before_id }; const result = await fetchPage(at); if (result) { setHistory((prev) => [...prev, cursor]); setCursor(at); } } async function loadPrev() { if (history.length === 0) return; const at = history[history.length - 1]; const result = await fetchPage(at); if (result) { setHistory((prev) => prev.slice(0, -1)); setCursor(at); } } useEffect(() => { void loadFresh(); // eslint-disable-next-line react-hooks/exhaustive-deps }, []); const rows = data?.rows ?? []; const inFlight = rows.filter((row) => !row.EnumerationDone || row.SentCount + row.FailedCount < row.TargetCount).length; const canGoPrev = history.length > 0 && !busy; const canGoNext = Boolean(data?.has_more) && !busy; return ( } > {error && {error}}
0 ? "warn" : "neutral"} />
{rows.map((row) => { const delivered = row.SentCount + row.FailedCount; const done = row.EnumerationDone && row.TargetCount > 0 && delivered >= row.TargetCount; return ( ); })} {rows.length === 0 && }
{"ID"} {"Message"} {"Target"} {"Sent"} {"Failed"} {"Total"} {"Created by"} {"Created"}
{row.ID} {row.Message} {row.TargetMode === "all" ? {"All users"} : {"Selected"}} {row.SentCount} {row.FailedCount > 0 ? {row.FailedCount} : row.FailedCount} {row.TargetCount} {row.CreatedBy || "-"} {formatDate(row.CreatedAt)} {!done && {"Sending"}}
{createModalOpen && ( setCreateModalOpen(false)} onCreated={() => void loadFresh()} /> )}
); }