import { useEffect, useMemo, useState } from "react"; import { ExternalLink, LockKeyhole, LockOpen } from "lucide-react"; import { Link } from "react-router-dom"; import type { ApiSettings } from "../../types"; import { Button } from "@govoplan/core-webui"; import { Card } from "@govoplan/core-webui"; import { ConfirmDialog } from "@govoplan/core-webui"; import { FormField } from "@govoplan/core-webui"; import { LoadingFrame } from "@govoplan/core-webui"; import { MetricCard } from "@govoplan/core-webui"; import { PageTitle } from "@govoplan/core-webui"; import { StatusBadge } from "@govoplan/core-webui"; import { DismissibleAlert, TableActionGroup, i18nMessage, useGuardedNavigate, useUnsavedDraftGuard } from "@govoplan/core-webui"; import { DataGrid, type DataGridColumn } from "@govoplan/core-webui"; import { lockCampaignVersionPermanently, lockCampaignVersionTemporarily, unlockCampaignVersionUserLock, updateCampaignMetadata, type CampaignVersionDetail, type CampaignVersionListItem } from "../../api/campaigns"; import { useCampaignWorkspaceData } from "./hooks/useCampaignWorkspaceData"; import { asArray, asRecord, canUnlockValidationVersion, formatDateTime, getCampaignJson, isFinalLockedVersion, isPermanentUserLockedVersion, isTemporaryUserLockedVersion, isVersionReadyForDelivery, summaryValue } from "./utils/campaignView"; import { buildUndefinedPlaceholders, extractTemplatePlaceholders, recipientAddressTemplateFieldOptions } from "./utils/templatePlaceholders"; const campaignModeOptions = ["draft", "test", "send"]; type LockAction = "temporary" | "unlock" | "permanent"; type PendingLockAction = {version: CampaignVersionListItem;action: LockAction;} | null; export default function CampaignOverviewPage({ settings, campaignId }: {settings: ApiSettings;campaignId: string;}) { const navigate = useGuardedNavigate(); const { data, loading, error, reload, setError } = useCampaignWorkspaceData(settings, campaignId, { includeSummary: true }); const campaign = data.campaign; const versions = useMemo(() => data.versions.slice().sort((a, b) => (b.version_number ?? 0) - (a.version_number ?? 0)), [data.versions]); const [identity, setIdentity] = useState({ external_id: "", name: "", status: "", description: "" }); const [identityDirty, setIdentityDirty] = useState(false); const [savingIdentity, setSavingIdentity] = useState(false); const [pendingLockAction, setPendingLockAction] = useState(null); const [lockBusy, setLockBusy] = useState(false); const [message, setMessage] = useState(""); const versionMetrics = useMemo(() => campaignVersionMetrics(data.currentVersion), [data.currentVersion]); useUnsavedDraftGuard({ dirty: identityDirty, onSave: saveIdentity, onDiscard: () => { if (!campaign) return; setIdentity({ external_id: campaign.external_id ?? "", name: campaign.name ?? "", status: campaign.status ?? "", description: campaign.description ?? "" }); setIdentityDirty(false); } }); useEffect(() => { if (!campaign || identityDirty) return; setIdentity({ external_id: campaign.external_id ?? "", name: campaign.name ?? "", status: campaign.status ?? "", description: campaign.description ?? "" }); }, [campaign, identityDirty]); function patchIdentity(key: keyof typeof identity, value: string) { setIdentity((current) => ({ ...current, [key]: value })); setIdentityDirty(true); setMessage(""); } async function saveIdentity(): Promise { if (!campaign || savingIdentity || !identityDirty) return false; setSavingIdentity(true); setError(""); setMessage(""); try { await updateCampaignMetadata(settings, campaign.id, { external_id: identity.external_id, name: identity.name, status: identity.status, description: identity.description }); setIdentityDirty(false); await reload(); return true; } catch (err) { setError(err instanceof Error ? err.message : String(err)); return false; } finally { setSavingIdentity(false); } } async function applyLockAction() { const pending = pendingLockAction; if (!pending || lockBusy) return; setLockBusy(true); setError(""); setMessage(""); try { if (pending.action === "temporary") { await lockCampaignVersionTemporarily(settings, campaignId, pending.version.id); setMessage(i18nMessage("i18n:govoplan-campaign.version_value_temporarily_locked.4ed4ffa7", { value0: pending.version.version_number })); } else if (pending.action === "unlock") { await unlockCampaignVersionUserLock(settings, campaignId, pending.version.id); setMessage(i18nMessage("i18n:govoplan-campaign.temporary_lock_removed_from_version_value.9a05bcfb", { value0: pending.version.version_number })); } else { await lockCampaignVersionPermanently(settings, campaignId, pending.version.id); setMessage(i18nMessage("i18n:govoplan-campaign.version_value_permanently_locked.60595f45", { value0: pending.version.version_number })); } setPendingLockAction(null); await reload(); } catch (err) { setError(err instanceof Error ? err.message : String(err)); } finally { setLockBusy(false); } } async function discardOverview() { if (campaign) { setIdentity({ external_id: campaign.external_id ?? "", name: campaign.name ?? "", status: campaign.status ?? "", description: campaign.description ?? "" }); setIdentityDirty(false); } await reload({ force: true }); } return (
{campaign?.name || "i18n:govoplan-campaign.overview.0efc2e6b"}

i18n:govoplan-campaign.campaign_overview_version_independent_identity_a.ebaf1113

{error && {error}} {message && {message}}
i18n:govoplan-campaign.edit_with_wizard.672a7d1a}>
patchIdentity("external_id", event.target.value)} /> patchIdentity("name", event.target.value)} />