import { useEffect, useState } from "react"; import { Link, useNavigate } from "react-router-dom"; import type { ApiSettings } from "../../types"; import Card from "../../components/Card"; import Button from "../../components/Button"; import StatusBadge from "../../components/StatusBadge"; import PageTitle from "../../components/PageTitle"; import LoadingFrame from "../../components/LoadingFrame"; import DismissibleAlert from "../../components/DismissibleAlert"; import DataGrid, { type DataGridColumn } from "../../components/table/DataGrid"; import { createNewCampaign, listCampaigns } from "../../api/campaigns"; import type { CampaignListItem } from "../../types"; export default function CampaignListPage({ settings }: { settings: ApiSettings }) { const navigate = useNavigate(); const [campaigns, setCampaigns] = useState([]); const [error, setError] = useState(""); const [loading, setLoading] = useState(false); const [creating, setCreating] = useState(false); const [lastLoadedAt, setLastLoadedAt] = useState(""); async function load() { setLoading(true); setError(""); try { const data = await listCampaigns(settings); setCampaigns(data); setLastLoadedAt(formatLoadedAt(new Date())); } catch (err) { setError(err instanceof Error ? err.message : String(err)); } finally { setLoading(false); } } async function create() { setCreating(true); setError(""); try { const created = await createNewCampaign(settings); navigate(`/campaigns/${created.campaign.id}/wizard/create`); } catch (err) { setError(err instanceof Error ? err.message : String(err)); } finally { setCreating(false); } } useEffect(() => { load(); }, [settings.apiBaseUrl, settings.apiKey, settings.accessToken]); const columns: DataGridColumn[] = [ { id: "campaign", header: "Campaign", width: "minmax(260px, 1.8fr)", sortable: true, filterable: true, sticky: "start", render: (campaign) => (
{campaign.name || campaign.external_id || campaign.id}
{campaign.description || campaign.external_id || campaign.id}
), value: (campaign) => `${campaign.name ?? ""} ${campaign.external_id ?? ""} ${campaign.id}` }, { id: "status", header: "Status", width: 150, sortable: true, filterable: true, columnType: "from-list", list: { options: [ { value: "draft", label: "Draft" }, { value: "active", label: "Active" }, { value: "completed", label: "Completed" }, { value: "archived", label: "Archived" } ], display: "pill" }, render: (campaign) => , value: (campaign) => campaign.status || "draft" }, { id: "current_version", header: "Current version", width: 170, sortable: true, filterable: true, className: "version-cell mono-small", render: (campaign) => {campaign.current_version_id ? shortId(campaign.current_version_id) : "—"}, value: (campaign) => campaign.current_version_id ?? "" }, { id: "updated", header: "Updated", width: 190, sortable: true, filterable: true, filterType: "date", className: "updated-cell", render: (campaign) => formatDateTime(campaign.updated_at ?? campaign.updatedAt ?? campaign.created_at), value: (campaign) => campaign.updated_at ?? campaign.updatedAt ?? campaign.created_at ?? "" }, { id: "actions", header: "Actions", width: 110, sticky: "end", render: (campaign) => } ]; return (
{error && {error}}
All campaigns

{lastLoadedAt ? `Last loaded: ${lastLoadedAt}` : "Not loaded yet"}

{campaigns.length === 0 ? (

No campaigns yet

Start with a guided campaign draft. The WebUI will create a portable campaign JSON in the background.

) : ( campaign.id} initialSort={{ columnId: "updated", direction: "desc" }} className="campaign-table-wrap" emptyText="No campaigns found." /> )}
); } function shortId(value: string): string { if (value.length <= 20) return value; return `${value.slice(0, 12)}…${value.slice(-6)}`; } function formatDateTime(value?: string): string { if (!value) return "—"; const date = new Date(value); if (Number.isNaN(date.getTime())) return value; return date.toLocaleString(undefined, { year: "numeric", month: "short", day: "2-digit", hour: "2-digit", minute: "2-digit" }); } function formatLoadedAt(value: Date): string { return value.toLocaleString(undefined, { year: "numeric", month: "short", day: "2-digit", hour: "2-digit", minute: "2-digit", second: "2-digit" }); }