import { useCallback } from "react"; import { Send } from "lucide-react"; import { Link } from "react-router"; import { DashboardWidgetList, DismissibleAlert, LoadingFrame, StatusBadge, useDashboardWidgetData, type ApiSettings, type CampaignListItem, type DashboardWidgetConfiguration } from "@govoplan/core-webui"; import { listCampaigns } from "../../api/campaigns"; const TERMINAL_STATUSES = new Set([ "sent", "cancelled", "archived", "deleted" ]); export default function CampaignActivityWidget({ settings, refreshKey, configuration }: { settings: ApiSettings; refreshKey: number; configuration: DashboardWidgetConfiguration; }) { const maxItems = numberSetting(configuration.maxItems, 5, 1, 12); const showCompleted = configuration.showCompleted === true; const load = useCallback(async () => { const campaigns = await listCampaigns(settings); return campaigns .filter( (campaign) => showCompleted || !TERMINAL_STATUSES.has(campaign.status) ) .sort(compareCampaigns) .slice(0, maxItems); }, [maxItems, settings, showCompleted]); const { data: campaigns, loading, error } = useDashboardWidgetData( load, refreshKey ); return ( {error && ( {error} )} ({ id: campaign.id, title: campaign.name, detail: campaignProgress(campaign), meta: updatedLabel(campaign), leading: ); } function compareCampaigns( left: CampaignListItem, right: CampaignListItem ): number { return timestamp(right) - timestamp(left); } function timestamp(campaign: CampaignListItem): number { const value = campaign.updated_at ?? campaign.updatedAt ?? campaign.created_at; return value ? new Date(value).getTime() : 0; } function updatedLabel(campaign: CampaignListItem): string { const value = campaign.updated_at ?? campaign.updatedAt ?? campaign.created_at; return value ? new Intl.DateTimeFormat(undefined, { day: "2-digit", month: "short" }).format(new Date(value)) : ""; } function campaignProgress(campaign: CampaignListItem): string { const parts = [ campaign.sent ? `${campaign.sent} sent` : "", campaign.failed ? `${campaign.failed} failed` : "", campaign.blocked ? `${campaign.blocked} blocked` : "", campaign.warnings ? `${campaign.warnings} warnings` : "" ].filter(Boolean); return parts.join(" ยท ") || campaign.description || "No delivery totals yet"; } function statusLabel(status: string): string { return status.replaceAll("_", " "); } function numberSetting( value: unknown, fallback: number, minimum: number, maximum: number ): number { const numeric = typeof value === "number" ? value : Number(value); return Number.isFinite(numeric) ? Math.max(minimum, Math.min(maximum, Math.floor(numeric))) : fallback; }