import { DescriptionList } from "@govoplan/core-webui"; import { useCallback, useEffect, useMemo, useRef, useState } from "react"; import { BarChart3, Check, FlaskConical, PackageCheck, Send, ShieldCheck } from "lucide-react"; import type { ApiSettings } from "../../types"; import { appendSent, buildVersion, cancelCampaign, downloadCampaignPrintArtifact, getCampaignDeliveryOptions, getCampaignJobs, getCampaignJobsDelta, getCampaignJobDetail, getCampaignSummary, linkCampaignAttachmentMatches, mockSendCampaign, pauseCampaign, previewCampaignAttachments, queueCampaign, requestCampaignApproval, resumeCampaign, retryCampaignJobs, sendCampaignJob, sendCampaignNow, updateCampaignReviewState, validateVersion, type CampaignAttachmentPreviewResponse, type CampaignDeliveryOptions, type CampaignJobsQuery, type CampaignJobsResponse, type CampaignSummary } from "../../api/campaigns"; import { getMockMailboxMessage, type MockMailboxMessage } from "../../api/mail"; import { FormGrid, Button, Dialog, FormField, MetricCard, MetricGrid, SegmentedControl, hasScope, useDeltaWatermarks, useGuardedNavigate, usePlatformUiCapability, type AuthInfo, type MailDevMailboxUiCapability } from "@govoplan/core-webui"; import { DataGrid, type DataGridQueryState } from "@govoplan/core-webui"; import { DismissibleAlert } from "@govoplan/core-webui"; import { ConfirmDialog } from "@govoplan/core-webui"; import { LoadingFrame } from "@govoplan/core-webui"; import { PageActionBar, PageLayout } from "@govoplan/core-webui"; import { StatusBadge } from "@govoplan/core-webui"; import { ToggleSwitch } from "@govoplan/core-webui"; import { i18nMessage } from "@govoplan/core-webui"; import CampaignMessagePreviewOverlay from "./components/MessagePreviewOverlay"; import LockedVersionNotice from "./components/LockedVersionNotice"; import VersionLine from "./components/VersionLine"; import { useCampaignWorkspaceData } from "./hooks/useCampaignWorkspaceData"; import { asArray, asRecord, formatDateTime, getCampaignJson, getDeliverySection, humanize, isFinalLockedVersion, isHistoricalCampaignVersion, isUserLockedVersion, isVersionReadyForDelivery } from "./utils/campaignView"; import { deliveryModeLabel } from "./utils/deliveryMode"; import { getText } from "./utils/draftEditor"; import { attachmentPreviewLinkableFiles } from "./utils/attachmentPreview"; import { emptyCampaignJobsResponse, mergeCampaignJobsDelta } from "./utils/jobDeltas"; import AttachmentLinkingPreview from "./review/AttachmentLinkingPreview"; import DeliverabilityPreflight, { type DeliverabilityPreflightItem } from "./review/DeliverabilityPreflight"; import DeliveryJobDetailOverlay from "./review/DeliveryJobDetailOverlay"; import BuiltMessagePreview from "./review/BuiltMessagePreview"; import { BuiltMessageReviewProgress, BuiltMessageWorkflowGuidance, ValidationWorkflowGuidance } from "./review/ReviewWorkflowGuidance"; import { calculateBuildReviewProgress } from "./review/reviewProgress"; import { WorkflowNavigation, WorkflowStage, stageConnectorState, stateLabel, type FlowStageDefinition, type FlowState } from "./review/WorkflowNavigation"; import { builtMessageKey, filterAndSortBuiltMessageRows, findBuiltMessageIndex, messageNeedsExplicitReview, reviewQueryEquals, sameBuiltMessage, storedMessageReviewState } from "./review/builtMessageQuery"; import { formatAddressList, numberFrom } from "./review/reviewFormatters"; import { builtMessageColumns, deliveryControlProgressMessage, deliveryPolicySourceLabel, imapAppendResultColumns, imapDiagnosticColumns, mockMailboxColumns, mockMessageAttachments, mockMessageMetaItems, mockSendResultColumns, sendResultColumns, synchronousSendReason } from "./review/reviewPresentation"; type WorkflowBusy = "validate" | "build" | "inspect" | "mock" | "mailbox" | "approval" | "send" | "queue" | "control" | "retry" | "imap" | ""; const MESSAGE_REVIEW_ISSUE_STATUSES = ["warning", "needs_review", "blocked", "excluded"]; export default function ReviewSendPage({ settings, auth, campaignId, initialStageId }: { settings: ApiSettings; auth: AuthInfo; campaignId: string; initialStageId?: string; }) { const navigate = useGuardedNavigate(); const devMailboxCapability = usePlatformUiCapability("mail.devMailbox"); const { getDeltaWatermark, setDeltaWatermark, resetDeltaWatermark } = useDeltaWatermarks(); const mockWorkflowAvailable = devMailboxCapability?.enabled === true; const [mockVerificationRequired, setMockVerificationRequired] = useState(true); const [mockMailboxPreviewEnabled, setMockMailboxPreviewEnabled] = useState(true); const mockWorkflowRequired = mockWorkflowAvailable && mockVerificationRequired; const mockMailboxPreviewActive = mockWorkflowAvailable && mockMailboxPreviewEnabled; const { data, loading, error, reload, setError } = useCampaignWorkspaceData(settings, campaignId, { includeSummary: true }); const [liveSummary, setLiveSummary] = useState(null); const [queueStatusLoading, setQueueStatusLoading] = useState(false); const version = data.currentVersion; const canSendSynchronously = hasScope(auth, "campaigns:campaign:send"); const canQueueForWorkers = hasScope(auth, "campaigns:campaign:queue"); const canControlDelivery = hasScope(auth, "campaigns:campaign:control"); const canRetryDelivery = hasScope(auth, "campaigns:campaign:retry"); const canRequestApproval = hasScope(auth, "campaigns:campaign:review"); const [deliveryOptions, setDeliveryOptions] = useState(null); const [deliveryOptionsLoading, setDeliveryOptionsLoading] = useState(false); const campaignJson = useMemo(() => getCampaignJson(version), [version]); const server = asRecord(campaignJson.server); const selectedMailProfileId = getText(server, "mail_profile_id"); const inlineEntries = useMemo( () => asArray(asRecord(campaignJson.entries).inline).map(asRecord), [campaignJson] ); const validation = asRecord(version?.validation_summary); const build = asRecord(version?.build_summary); const attachmentReuse = asRecord(build.attachment_reuse); const attachmentReusePolicy = asRecord(attachmentReuse.policy); const attachmentReuseFindings = asArray(attachmentReuse.findings).map(asRecord); const residualFileDisposition = asRecord(build.residual_file_disposition); const residualFileRecipient = asRecord(residualFileDisposition.recipient); const printOutput = asRecord(build.print_output); const printArtifact = asRecord(printOutput.artifact); const summary = liveSummary ?? data.summary; const cards = summary?.cards; const attachmentSummary = asRecord(summary?.attachments); const delivery = getDeliverySection(version); const rateLimit = asRecord(delivery.rate_limit); const imapAppend = asRecord(delivery.imap_append_sent); const [busy, setBusy] = useState(""); const [message, setMessage] = useState(""); const [messageReviewComplete, setMessageReviewComplete] = useState(false); const [builtReviewRows, setBuiltReviewRows] = useState[]>([]); const [reviewJobs, setReviewJobs] = useState(() => emptyCampaignJobsResponse()); const reviewJobsRef = useRef(emptyCampaignJobsResponse()); const [reviewPage, setReviewPage] = useState(1); const [reviewPageSize, setReviewPageSize] = useState(50); const [reviewQuery, setReviewQuery] = useState({ sort: null, filters: {} }); const [showAllReviewJobs, setShowAllReviewJobs] = useState(false); const [jobsLoadedKey, setJobsLoadedKey] = useState(""); const [reviewedMessageKeys, setReviewedMessageKeys] = useState>(() => new Set()); const [reviewIssueDecisions, setReviewIssueDecisions] = useState< Record >({}); const [newlyReviewedRequiredKeys, setNewlyReviewedRequiredKeys] = useState>(() => new Set()); const [selectedBuiltIndex, setSelectedBuiltIndex] = useState(null); const [singleSendConfirmIndex, setSingleSendConfirmIndex] = useState(null); const [singleMessageActionKind, setSingleMessageActionKind] = useState<"test" | "single_send" | "single_resend">("single_send"); const [singleMessageResendReason, setSingleMessageResendReason] = useState(""); const [mockResult, setMockResult] = useState | null>(null); const [mockClearFirst, setMockClearFirst] = useState(true); const [mockAppendSent, setMockAppendSent] = useState(true); const [selectedMockMessage, setSelectedMockMessage] = useState(null); const [attachmentPreview, setAttachmentPreview] = useState(null); const [attachmentPreviewLoading, setAttachmentPreviewLoading] = useState(false); const [attachmentPreviewError, setAttachmentPreviewError] = useState(""); const [attachmentLinking, setAttachmentLinking] = useState(false); const [attachmentLockConfirmOpen, setAttachmentLockConfirmOpen] = useState(false); const [reviewConfirmOpen, setReviewConfirmOpen] = useState(false); const [approvalDialogOpen, setApprovalDialogOpen] = useState(false); const [approvalSelectorKind, setApprovalSelectorKind] = useState<"account" | "group" | "role" | "function_assignment" | "any_account">("role"); const [approvalSelectorValue, setApprovalSelectorValue] = useState(""); const [approvalRequiredCount, setApprovalRequiredCount] = useState(1); const [approvalSignatureRequired, setApprovalSignatureRequired] = useState(false); const [approvalExcludedRoles, setApprovalExcludedRoles] = useState>(() => new Set()); const [dryRun, setDryRun] = useState(false); const [sendConfirmOpen, setSendConfirmOpen] = useState(false); const [queueConfirmOpen, setQueueConfirmOpen] = useState(false); const [cancelDeliveryConfirmOpen, setCancelDeliveryConfirmOpen] = useState(false); const [sendResult, setSendResult] = useState | null>(null); const [queueResult, setQueueResult] = useState | null>(null); const [imapAppendResult, setImapAppendResult] = useState | null>(null); const [imapDiagnostics, setImapDiagnostics] = useState(() => emptyCampaignJobsResponse()); const imapDiagnosticsRef = useRef(emptyCampaignJobsResponse()); const [selectedDeliveryJobDetail, setSelectedDeliveryJobDetail] = useState | null>(null); const persistedReview = storedMessageReviewState(version); const persistedReviewKey = `${persistedReview.buildToken}|${persistedReview.inspectionComplete ? "1" : "0"}|${persistedReview.reviewedMessageKeys.join(",")}|${JSON.stringify(persistedReview.issueDecisions)}`; const initialStageScrollKey = useRef(""); useEffect(() => { setBuiltReviewRows([]); setReviewJobs(emptyCampaignJobsResponse()); reviewJobsRef.current = emptyCampaignJobsResponse(); setReviewPage(1); setReviewQuery({ sort: null, filters: {} }); setJobsLoadedKey(""); setNewlyReviewedRequiredKeys(new Set()); setReviewIssueDecisions({}); setSelectedBuiltIndex(null); setMockResult(null); setSelectedMockMessage(null); setAttachmentPreview(null); setAttachmentPreviewError(""); setAttachmentLockConfirmOpen(false); setApprovalDialogOpen(false); setSendResult(null); setQueueResult(null); setImapAppendResult(null); setImapDiagnostics(emptyCampaignJobsResponse()); imapDiagnosticsRef.current = emptyCampaignJobsResponse(); setSelectedDeliveryJobDetail(null); setSendConfirmOpen(false); setQueueConfirmOpen(false); setCancelDeliveryConfirmOpen(false); setDeliveryOptions(null); setLiveSummary(null); resetDeltaWatermark(); }, [version?.id, resetDeltaWatermark]); useEffect(() => { if (!initialStageId || loading || !version?.id) return; const key = `${version.id}:${initialStageId}`; if (initialStageScrollKey.current === key) return; const frame = window.requestAnimationFrame(() => { const target = document.getElementById(initialStageId); if (!target) return; initialStageScrollKey.current = key; target.scrollIntoView({ behavior: "smooth", block: "start" }); target.focus({ preventScroll: true }); }); return () => window.cancelAnimationFrame(frame); }, [initialStageId, loading, version?.id]); useEffect(() => { setMessageReviewComplete(persistedReview.inspectionComplete); setReviewedMessageKeys(new Set(persistedReview.reviewedMessageKeys)); setReviewIssueDecisions(Object.fromEntries( persistedReview.issueDecisions.flatMap((item) => { const jobId = String(item.job_id ?? "").trim(); return jobId ? [[jobId, String(item.reason ?? "")]] : []; }) )); }, [version?.id, persistedReviewKey]); useEffect(() => { if (!mockMailboxPreviewActive) setSelectedMockMessage(null); }, [mockMailboxPreviewActive]); const validationPresent = Object.keys(validation).length > 0; const validationOk = validation.ok === true; const validationErrors = numberFrom(validation, ["error_count", "errors", "blocked"]); const validationWarnings = numberFrom(validation, ["warning_count", "warnings"]); const validationIssues = asArray(validation.issues).map(asRecord); const visibleValidationIssues = validationIssues.slice(0, 10); const readyForDelivery = isVersionReadyForDelivery(version); const validationStale = validationOk && !readyForDelivery; const buildPresent = Object.keys(build).length > 0; const builtCount = numberFrom(build, ["built_count", "ready_count", "built", "messages_built"]); const buildBlocked = numberFrom(build, ["blocked_count", "blocked"]); const buildNeedsReview = numberFrom(build, ["needs_review_count", "needs_review"]); const buildWarnings = numberFrom(build, ["warning_count", "warnings"]); const hasBuild = buildPresent && (builtCount > 0 || version?.workflow_state === "built"); const refreshDeliveryOptions = useCallback(async (silent = true) => { if (!version?.id || !(canSendSynchronously || canQueueForWorkers)) { setDeliveryOptions(null); return; } setDeliveryOptionsLoading(true); try { const result = await getCampaignDeliveryOptions(settings, campaignId, version.id); setDeliveryOptions(result); } catch (err) { setDeliveryOptions(null); if (!silent) setError(err instanceof Error ? err.message : String(err)); } finally { setDeliveryOptionsLoading(false); } }, [campaignId, canQueueForWorkers, canSendSynchronously, setError, settings, version?.id]); useEffect(() => { if (!version?.id || !hasBuild) return; void refreshDeliveryOptions(true); }, [hasBuild, refreshDeliveryOptions, version?.id, version?.updated_at]); useEffect(() => { if (!version?.id || !hasBuild) return; const expectedKey = reviewJobsLoadKey(version.id, showAllReviewJobs); if (jobsLoadedKey === expectedKey) return; void loadBuiltMessages(true); }, [version?.id, hasBuild, showAllReviewJobs, jobsLoadedKey, campaignId, settings.apiBaseUrl, settings.apiKey, settings.accessToken]); const statusCounts = asRecord(summary?.status_counts); const queueStatusCounts = asRecord(statusCounts.queue); const sendStatusCounts = asRecord(statusCounts.send); const imapStatusCounts = asRecord(statusCounts.imap); const attempts = asRecord(summary?.attempts); const summaryDelivery = asRecord(summary?.delivery); const queuedSendCount = numberFrom(sendStatusCounts, ["queued"]); const pausedQueueCount = numberFrom(queueStatusCounts, ["paused"]); const claimedSendCount = numberFrom(sendStatusCounts, ["claimed"]); const sendingSendCount = numberFrom(sendStatusCounts, ["sending"]); const activeSendCount = claimedSendCount + sendingSendCount; const queuedOrActiveCount = cards?.queued_or_active ?? queuedSendCount + activeSendCount; const notQueuedCount = cards?.not_attempted ?? numberFrom(sendStatusCounts, ["not_queued"]); const cancelledCount = cards?.cancelled ?? numberFrom(sendStatusCounts, ["cancelled"]); const outcomeUnknownCount = cards?.outcome_unknown ?? numberFrom(sendStatusCounts, ["outcome_unknown"]); const sendAttemptCount = numberFrom(attempts, ["send_attempts"]); const backgroundWorkersEnabled = deliveryOptions?.worker_queue_available === true || summaryDelivery.background_workers_enabled === true || summaryDelivery.celery_enabled === true; const backgroundWorkersDisabled = deliveryOptions?.worker_queue_available === false || summaryDelivery.background_workers_enabled === false || summaryDelivery.celery_enabled === false; const recentFailures = asArray(summary?.recent_failures).map(asRecord).slice(0, 5); const jobsTotal = cards?.jobs_total ?? inlineEntries.filter((entry) => entry.active !== false).length; const sentCount = cards?.sent ?? 0; const failedCount = cards?.failed ?? 0; const retryableCount = cards?.retryable ?? numberFrom(sendStatusCounts, ["failed_temporary"]); const imapAppended = cards?.imap_appended ?? 0; const imapFailed = cards?.imap_failed ?? 0; const imapPending = numberFrom(imapStatusCounts, ["pending"]); const imapSkipped = numberFrom(imapStatusCounts, ["skipped"]); const deliveryHasTerminalOutcome = sentCount + failedCount + outcomeUnknownCount + cancelledCount > 0; const currentWorkflowState = (version?.workflow_state ?? "").toLowerCase(); const deliverySending = activeSendCount > 0 || currentWorkflowState === "sending" && queuedOrActiveCount > 0; const deliveryQueued = queuedOrActiveCount > 0 || currentWorkflowState === "queued" && !deliveryHasTerminalOutcome; const deliveryStarted = deliverySending || deliveryHasTerminalOutcome || ["sent", "completed", "partially_completed", "outcome_unknown", "failed"].includes(currentWorkflowState); const queuedWithoutWorker = backgroundWorkersDisabled && queuedSendCount > 0 && activeSendCount === 0; const directQueuedSendAllowed = queuedWithoutWorker && !deliveryStarted; const synchronousSendOption = asRecord(deliveryOptions?.synchronous_send); const synchronousSendPolicy = asRecord(synchronousSendOption.policy); const synchronousSendLimit = numberFrom(synchronousSendPolicy, ["max_recipient_jobs"]); const synchronousEligibleCount = numberFrom(synchronousSendOption, ["eligible_recipient_job_count"]); const synchronousSendAllowed = synchronousSendOption.allowed === true; const workerQueueAvailable = deliveryOptions?.worker_queue_available === true; const approvalGate = asRecord(deliveryOptions?.approval_gate); const approvalGateConfigured = approvalGate.configured === true; const approvalGateAvailable = approvalGate.available === true; const approvalGateApproved = approvalGate.approved === true; const approvalGateBlocksLiveDelivery = approvalGateConfigured && !approvalGateApproved; const persistedDeliveryMode = version?.delivery_mode ?? null; const persistedDeliveryModeSelectedAt = version?.delivery_mode_selected_at ?? null; const selectedDryRun = dryRun && !directQueuedSendAllowed; const deliveryPartial = cards?.partially_completed === true || ["partially_sent", "failed_partial", "partially_completed"].includes(currentWorkflowState); const deliveryComplete = queuedOrActiveCount === 0 && sentCount > 0 && failedCount === 0 && outcomeUnknownCount === 0 && cancelledCount === 0 && cards?.partially_completed !== true; const deliveryDanger = failedCount > 0 || outcomeUnknownCount > 0 || ["outcome_unknown", "failed"].includes(currentWorkflowState); const deliveryDisplayStatus = deliverySending ? "sending" : deliveryQueued ? "queued" : deliveryPartial ? "partially_completed" : deliveryComplete ? "completed" : deliveryDanger ? failedCount > 0 ? "failed" : "outcome_unknown" : data.campaign?.status ?? version?.workflow_state ?? "not_started"; const queueStatusNote = queuedWithoutWorker ? "i18n:govoplan-campaign.queued_jobs_are_waiting_in_the_database_but_back.5b1fadfd" : deliverySending ? "i18n:govoplan-campaign.delivery_is_being_processed_this_page_refreshes_.fe83676e" : deliveryQueued && backgroundWorkersEnabled ? "i18n:govoplan-campaign.queued_jobs_are_waiting_for_a_background_deliver.cc703afb" : deliveryQueued ? "i18n:govoplan-campaign.queued_jobs_are_waiting_if_the_counts_do_not_cha.7d2164d6" : ""; const queueStatusTone = queuedWithoutWorker ? "is-warning" : deliveryQueued || deliverySending ? "is-stale" : ""; const historicalVersion = isHistoricalCampaignVersion(version, data.campaign?.current_version_id); const finalVersion = isFinalLockedVersion(version); const userLockedVersion = isUserLockedVersion(version); const readOnlyVersion = historicalVersion || userLockedVersion || finalVersion; const refreshQueueStatus = useCallback(async (silent = true) => { if (!version?.id) return; setQueueStatusLoading(true); if (!silent) setMessage("i18n:govoplan-campaign.refreshing_queue_status.2a7dea57"); setError(""); try { const result = await getCampaignSummary(settings, campaignId, version.id); setLiveSummary(result); if (!silent) setMessage("i18n:govoplan-campaign.queue_status_refreshed.253e2e5b"); } catch (err) { if (!silent) setMessage(""); setError(err instanceof Error ? err.message : String(err)); } finally { setQueueStatusLoading(false); } }, [campaignId, setError, settings, version?.id]); useEffect(() => { const commandInProgress = ["send", "queue", "control", "retry"].includes(busy); if (!(deliveryQueued || deliverySending || commandInProgress) || loading || queueStatusLoading) return; const handle = window.setTimeout(() => {void refreshQueueStatus(true);}, commandInProgress ? 1000 : 3000); return () => window.clearTimeout(handle); }, [deliveryQueued, deliverySending, loading, queueStatusLoading, busy, refreshQueueStatus]); const reloadAttachmentPreview = useCallback(async (silent = true) => { if (!version?.id) return; setAttachmentPreviewLoading(true); setAttachmentPreviewError(""); if (!silent) setMessage("i18n:govoplan-campaign.resolving_attachment_patterns.87d7d21b"); try { const response = await previewCampaignAttachments(settings, campaignId, version.id, { include_unmatched: true, include_unlinked_candidates: true }); setAttachmentPreview(response); if (!silent) setMessage("i18n:govoplan-campaign.attachment_preview_refreshed.50d5b50d"); } catch (err) { setAttachmentPreview(null); setAttachmentPreviewError(err instanceof Error ? err.message : String(err)); if (!silent) setMessage(""); } finally { setAttachmentPreviewLoading(false); } }, [campaignId, settings, version?.id]); useEffect(() => { if (!version?.id) return; void reloadAttachmentPreview(true); }, [reloadAttachmentPreview, version?.id, version?.updated_at]); const filteredBuiltReviewRows = useMemo( () => filterAndSortBuiltMessageRows(builtReviewRows, reviewQuery, reviewedMessageKeys), [builtReviewRows, reviewQuery, reviewedMessageKeys] ); const reviewColumns = builtMessageColumns(openBuiltMessage, reviewedMessageKeys); const reviewPageCount = Math.max(1, Math.ceil(filteredBuiltReviewRows.length / reviewPageSize)); const effectiveReviewPage = Math.min(reviewPageCount, Math.max(1, reviewPage)); const pagedBuiltReviewRows = filteredBuiltReviewRows.slice((effectiveReviewPage - 1) * reviewPageSize, effectiveReviewPage * reviewPageSize); const selectedBuiltMessage = selectedBuiltIndex === null ? null : filteredBuiltReviewRows[selectedBuiltIndex] ?? null; const singleSendConfirmRow = singleSendConfirmIndex === null ? null : filteredBuiltReviewRows[singleSendConfirmIndex] ?? null; const reviewMetadata = reviewJobs.review ?? {}; const blockingReviewCount = Number(reviewMetadata.blocking_count ?? 0); const explicitReviewCount = Number(reviewMetadata.required_count ?? 0); const bulkAcceptableCount = Number(reviewMetadata.bulk_acceptable_count ?? 0); const reviewedExplicitCount = Math.min( explicitReviewCount, Number(reviewMetadata.reviewed_required_count ?? 0) + newlyReviewedRequiredKeys.size ); const reviewRequiredCount = explicitReviewCount + bulkAcceptableCount; const automaticInspectionComplete = reviewJobs.total_unfiltered > 0 && blockingReviewCount === 0 && reviewRequiredCount === 0; const mockSend = asRecord(mockResult?.send); const mockSent = numberFrom(mockSend, ["sent_count", "attempted_count"]); const mockFailed = numberFrom(mockSend, ["failed_count"]); const mockSkipped = numberFrom(mockSend, ["skipped_count"]); const mockComplete = Boolean(mockResult) && mockSent > 0 && mockFailed === 0 && mockSkipped === 0; const mockPartial = Boolean(mockResult) && mockSent > 0 && (mockFailed > 0 || mockSkipped > 0); const mockRows = asArray(mockSend.results).map(asRecord); const mockMailbox = asRecord(mockResult?.mailbox); const mockMailboxMessages = asArray(mockMailbox.messages).map(asRecord); const sendResultRows = asArray(sendResult?.results).map(asRecord); const imapAppendResultRows = asArray(imapAppendResult?.results).map(asRecord); const imapDiagnosticRows = imapDiagnostics.jobs.map(asRecord); const imapDiagnosticsPending = imapDiagnosticRows.filter((job) => String(job.imap_status ?? "").toLowerCase() === "pending").length; const imapDiagnosticsFailed = imapDiagnosticRows.filter((job) => ["failed", "outcome_unknown"].includes(String(job.imap_status ?? "").toLowerCase()), ).length; const imapPendingForDisplay = Math.max(imapPending, imapDiagnosticsPending); const imapFailedForDisplay = Math.max(imapFailed, imapDiagnosticsFailed); const canAppendPendingImap = Boolean(imapAppend.enabled) && imapPendingForDisplay > 0 && !historicalVersion && !userLockedVersion; const validationReviewState: FlowState = busy === "validate" ? "running" : validationStale ? "stale" : validationPresent && !validationOk ? "danger" : readyForDelivery && (validationWarnings > 0 || (cards?.needs_attention ?? 0) > 0) ? "warning" : readyForDelivery ? "complete" : "active"; const downstreamDeliveryActivity = deliveryQueued || deliveryStarted; const inspectionSatisfied = automaticInspectionComplete || messageReviewComplete || downstreamDeliveryActivity; const buildReviewProgress = calculateBuildReviewProgress({ blocking: Math.max(blockingReviewCount, buildBlocked), individualRequired: explicitReviewCount, individualReviewed: reviewedExplicitCount, groupRequired: bulkAcceptableCount, reviewComplete: messageReviewComplete || downstreamDeliveryActivity }); const buildReviewState: FlowState = !readyForDelivery ? "locked" : busy === "build" || busy === "inspect" ? "running" : hasBuild && buildBlocked > 0 ? "danger" : hasBuild && !inspectionSatisfied && (buildNeedsReview > 0 || buildWarnings > 0 || buildReviewProgress.remaining > 0) ? "warning" : hasBuild && inspectionSatisfied ? "complete" : "active"; const optionalMockSkipped = mockWorkflowAvailable && !mockWorkflowRequired && inspectionSatisfied && !mockResult && busy !== "mock" && busy !== "mailbox"; const mockState: FlowState = !mockWorkflowAvailable ? "locked" : !inspectionSatisfied ? "locked" : busy === "mock" || busy === "mailbox" ? "running" : mockPartial ? "partial" : mockFailed > 0 ? "danger" : mockComplete ? "complete" : optionalMockSkipped ? "complete" : downstreamDeliveryActivity ? "warning" : "active"; const mockStateDisplayLabel = !mockWorkflowAvailable ? "i18n:govoplan-campaign.unavailable.2c9c1f79" : !mockVerificationRequired ? "i18n:govoplan-campaign.optional.0c6c4102" : stateLabel(mockState); const mockGateSatisfied = inspectionSatisfied && (!mockWorkflowRequired || mockComplete || mockPartial || downstreamDeliveryActivity); const singleMessageSendWorkflowBlocked = ["archived", "cancelled"].includes(currentWorkflowState); const canStartSingleMessageSend = Boolean(version && !historicalVersion && !userLockedVersion && readyForDelivery && hasBuild && mockGateSatisfied && !approvalGateBlocksLiveDelivery && !singleMessageSendWorkflowBlocked); const sendLockReason = !inspectionSatisfied ? "i18n:govoplan-campaign.build_and_complete_the_required_message_review_f.c0cd00fe" : mockWorkflowRequired ? "i18n:govoplan-campaign.complete_a_successful_mock_delivery_first.bc2a587d" : "i18n:govoplan-campaign.build_the_exact_queue_first.98d7ce1b"; const sendState: FlowState = !mockGateSatisfied ? "locked" : ["send", "queue", "control", "retry"].includes(busy) || deliverySending ? "running" : queuedWithoutWorker ? "warning" : deliveryQueued ? "running" : deliveryPartial ? "partial" : deliveryComplete || ["sent", "completed"].includes(currentWorkflowState) ? "complete" : deliveryDanger ? "danger" : "active"; const resultState: FlowState = !deliveryStarted ? "locked" : deliverySending ? "running" : deliveryPartial ? "partial" : deliveryComplete || ["sent", "completed"].includes(currentWorkflowState) ? "complete" : deliveryDanger ? "danger" : "active"; const stages: FlowStageDefinition[] = useMemo(() => [ { id: "workflow-validate-review", title: "i18n:govoplan-campaign.validate_and_inspect.b617b9b2", shortTitle: "i18n:govoplan-campaign.validate.6752f198", description: "i18n:govoplan-campaign.lock_and_validate_the_campaign_then_inspect_bloc.e22b9266", icon: ShieldCheck, state: validationReviewState, stateLabel: stateLabel(validationReviewState) }, { id: "workflow-build-review", title: "i18n:govoplan-campaign.build_and_review.635177f0", shortTitle: "i18n:govoplan-campaign.build.bbd80cf7", description: "i18n:govoplan-campaign.build_the_exact_queue_resolve_recipient_values_a.d86446cb", icon: PackageCheck, state: buildReviewState, stateLabel: stateLabel(buildReviewState), lockReason: validationErrors > 0 ? `Resolve ${validationErrors} blocking validation issue${validationErrors === 1 ? "" : "s"} first.` : "i18n:govoplan-campaign.lock_and_validate_the_current_working_version_fi.e824704f" }, { id: "workflow-mock-verify", title: "i18n:govoplan-campaign.mock_send_and_verify.03ec38d0", shortTitle: "i18n:govoplan-campaign.mock_send.37a5f500", description: "i18n:govoplan-campaign.exercise_the_delivery_path_and_verify_recipient_.12ed1928", icon: FlaskConical, state: mockState, connectorState: !mockWorkflowAvailable && inspectionSatisfied ? sendState : mockState, stateLabel: mockStateDisplayLabel, lockReason: mockWorkflowAvailable ? "i18n:govoplan-campaign.build_and_complete_the_required_message_review_f.c0cd00fe" : "i18n:govoplan-campaign.enable_the_mail_dev_mailbox_capability_to_run_mo.4f9a506d" }, { id: "workflow-send", title: "i18n:govoplan-campaign.confirm_and_send.fe43b726", shortTitle: "i18n:govoplan-campaign.send.9bc2575c", description: "i18n:govoplan-campaign.review_the_final_execution_summary_optionally_ru.6b32b00a", icon: Send, state: sendState, stateLabel: stateLabel(sendState), lockReason: sendLockReason }, { id: "workflow-results", title: "i18n:govoplan-campaign.delivery_results.b5d7d1cb", shortTitle: "i18n:govoplan-campaign.results.612e12d2", description: "i18n:govoplan-campaign.review_smtp_outcomes_imap_append_results_partial.a0d4e83e", icon: BarChart3, state: resultState, stateLabel: stateLabel(resultState), lockReason: "i18n:govoplan-campaign.delivery_results_become_available_after_the_real.4a86ab77" }], [ validationReviewState, buildReviewState, mockState, sendState, resultState, validationErrors, mockStateDisplayLabel, mockWorkflowAvailable, inspectionSatisfied, sendLockReason] ); function reviewJobsLoadKey(versionId: string, includeAll: boolean): string { return JSON.stringify({ scope: "review-jobs", campaignId, versionId, pageSize: "all", includeAll, apiBaseUrl: settings.apiBaseUrl, apiKey: settings.apiKey, accessToken: settings.accessToken }); } function imapDiagnosticsDeltaKey(versionId: string): string { return JSON.stringify({ scope: "imap-diagnostics", campaignId, versionId, page: 1, pageSize: 50, imapStatus: ["pending", "failed"], apiBaseUrl: settings.apiBaseUrl, apiKey: settings.apiKey, accessToken: settings.accessToken }); } async function fetchJobsDelta(key: string, current: CampaignJobsResponse, options: CampaignJobsQuery): Promise { let nextWatermark = getDeltaWatermark(key); let merged = current; let hasMore = false; do { const response = await getCampaignJobsDelta(settings, campaignId, { ...options, since: nextWatermark }); merged = mergeCampaignJobsDelta(merged, response); nextWatermark = response.watermark ?? null; hasMore = response.has_more; } while (hasMore); setDeltaWatermark(key, nextWatermark); return merged; } async function linkMatchedAttachmentFiles() { if (!version || busy || readOnlyVersion || readyForDelivery || attachmentLinking) return; const pending = attachmentPreviewLinkableFiles(attachmentPreview); if (pending.length === 0) { await reloadAttachmentPreview(false); return; } setAttachmentLinking(true); setMessage("i18n:govoplan-campaign.linking_matched_attachment_files.92f38088"); setError(""); try { const result = await linkCampaignAttachmentMatches(settings, campaignId, version.id, { dry_run: false }); setMessage(i18nMessage("i18n:govoplan-campaign.linked_value_attachment_file_s_to_this_campaign.02e5ecf7", { value0: result.linked_file_count })); await reloadAttachmentPreview(true); await reload(); } catch (err) { setMessage(""); setError(err instanceof Error ? err.message : String(err)); } finally { setAttachmentLinking(false); } } async function runValidation(linkUnsharedMatches = false) { if (!version || busy || readOnlyVersion || readyForDelivery) return; const pending = attachmentPreviewLinkableFiles(attachmentPreview); if (!linkUnsharedMatches && pending.length > 0) { setAttachmentLockConfirmOpen(true); return; } setAttachmentLockConfirmOpen(false); setBusy("validate"); setMessage(linkUnsharedMatches ? "i18n:govoplan-campaign.linking_matched_files_then_validating_the_campa.0d48a1d0" : "i18n:govoplan-campaign.validating_the_campaign_including_managed_attach.b133cfd9"); setError(""); try { const result = await validateVersion(settings, version.id, true, linkUnsharedMatches); setMessage(result.ok ? "i18n:govoplan-campaign.validation_passed.c3e25768" : "i18n:govoplan-campaign.validation_finished_with_issues_review_the_excep.be65d2a1"); resetDownstreamReview(); await reloadAttachmentPreview(true); await reload(); } catch (err) { setMessage(""); setError(err instanceof Error ? err.message : String(err)); } finally { setBusy(""); } } async function runBuild() { if (!version || busy || readOnlyVersion || !readyForDelivery || deliveryQueued || deliveryStarted) return; setBusy("build"); setMessage("i18n:govoplan-campaign.building_exact_messages_and_resolving_managed_at.7f24ee41"); setError(""); try { const result = await buildVersion(settings, version.id, true); applyLoadedReviewJobs(await loadAllReviewJobs(version.id, showAllReviewJobs), true); setMessage(i18nMessage("i18n:govoplan-campaign.build_finished_built_value_message_s_the_message.e1ec0296", { value0: String(result.built_count ?? result.ready_count ?? "—") })); setMessageReviewComplete(false); setReviewedMessageKeys(new Set()); setMockResult(null); setSelectedMockMessage(null); await reload(); } catch (err) { setMessage(""); setError(err instanceof Error ? err.message : String(err)); } finally { setBusy(""); } } async function loadBuiltMessages(silent = false) { if (!version || busy || !hasBuild) return; setBusy("inspect"); if (!silent) setMessage("i18n:govoplan-campaign.loading_the_built_message_review.a5339588"); setError(""); try { const result = await loadAllReviewJobs(version.id, showAllReviewJobs); applyLoadedReviewJobs(result, silent); } catch (err) { if (!silent) setMessage(""); setError(err instanceof Error ? err.message : String(err)); } finally { setBusy(""); } } function applyLoadedReviewJobs(result: CampaignJobsResponse, silent: boolean) { if (!version) return; const reviewKey = reviewJobsLoadKey(version.id, showAllReviewJobs); const jobs = result.jobs.map(asRecord); const mergedResult = { ...result, jobs, page: 1, page_size: jobs.length || result.page_size, pages: jobs.length > 0 ? 1 : 0 }; reviewJobsRef.current = mergedResult; setReviewJobs(mergedResult); setBuiltReviewRows(jobs); setReviewedMessageKeys(new Set( jobs .filter((row) => row.reviewed === true) .map((row, index) => builtMessageKey(row, index)) )); setMessageReviewComplete(result.review?.inspection_complete === true); setReviewPage(1); setJobsLoadedKey(reviewKey); if (!silent) { setMessage(i18nMessage("i18n:govoplan-campaign.loaded_value_message_s_on_page_value_of_value.febbeb1e", { value0: jobs.length, value1: 1, value2: 1 })); } } async function loadAllReviewJobs(versionId: string, includeAll: boolean): Promise { const pageSize = 200; const first = await getCampaignJobs(settings, campaignId, { versionId, page: 1, pageSize, validationStatus: includeAll ? undefined : MESSAGE_REVIEW_ISSUE_STATUSES }); const allJobs = [...first.jobs]; for (let page = 2; page <= Math.max(1, first.pages || 1); page += 1) { const next = await getCampaignJobs(settings, campaignId, { versionId, page, pageSize, validationStatus: includeAll ? undefined : MESSAGE_REVIEW_ISSUE_STATUSES }); allJobs.push(...next.jobs); } return { ...first, jobs: allJobs, page: 1, page_size: pageSize, pages: first.pages }; } async function runMockSend() { if (!version || busy || !mockWorkflowAvailable || readOnlyVersion || !inspectionSatisfied || deliveryQueued || deliveryStarted) return; setBusy("mock"); setMessage("i18n:govoplan-campaign.running_the_complete_mock_delivery_flow.3d0f6cd8"); setError(""); setSelectedMockMessage(null); try { const response = await mockSendCampaign(settings, campaignId, { version_id: version.id, send: true, include_warnings: true, include_needs_review: false, append_sent: mockAppendSent, clear_mailbox: mockClearFirst, check_files: true }); const result = asRecord(response.result ?? response); setMockResult(result); const sendResult = asRecord(result.send); setMessage(i18nMessage("i18n:govoplan-campaign.mock_delivery_finished_captured_value_message_s_.ac23dedf", { value0: String(sendResult.sent_count ?? 0), value1: String(sendResult.failed_count ?? 0), value2: String(sendResult.skipped_count ?? 0) })); } catch (err) { setMessage(""); setError(err instanceof Error ? err.message : String(err)); } finally { setBusy(""); } } async function createApprovalGate() { if (!version || busy || !canRequestApproval || !approvalGateAvailable || approvalGateConfigured || approvalSelectorKind !== "any_account" && !approvalSelectorValue.trim()) return; setBusy("approval"); setError(""); setMessage("Requesting approval for the exact built execution..."); try { await requestCampaignApproval(settings, campaignId, version.id, { title: `Approve delivery of ${data.campaign?.name ?? "Campaign"}`, description: "Approval is bound to this version's immutable execution snapshot.", steps: [{ key: "release", label: "Approve delivery", selectors: [{ kind: approvalSelectorKind, value: approvalSelectorKind === "any_account" ? "*" : approvalSelectorValue.trim() }], required_approvals: approvalRequiredCount, rejection_policy: "fail_fast", signature_required: approvalSignatureRequired, forbidden_evidence_roles: [...approvalExcludedRoles] }], unique_actors_across_steps: true, policy_refs: [], idempotency_key: crypto.randomUUID() }); setApprovalDialogOpen(false); setMessage("Approval requested. Real delivery remains unavailable until the request is approved."); await reload(); await refreshDeliveryOptions(false); } catch (err) { setMessage(""); setError(err instanceof Error ? err.message : String(err)); } finally { setBusy(""); } } async function runSendNow() { const effectiveDryRun = dryRun && !directQueuedSendAllowed; if (!version || busy || !canSendSynchronously || !synchronousSendAllowed || readOnlyVersion || !readyForDelivery || !hasBuild || !mockGateSatisfied || deliveryQueued && !directQueuedSendAllowed || deliveryStarted || !effectiveDryRun && approvalGateBlocksLiveDelivery) return; setBusy("send"); setMessage(effectiveDryRun ? "i18n:govoplan-campaign.checking_the_built_queue_without_sending.e15bb876" : directQueuedSendAllowed ? "i18n:govoplan-campaign.sending_the_queued_jobs_synchronously.6162f750" : "i18n:govoplan-campaign.sending_the_locked_campaign_version.061ee1bb"); setSendResult(null); setError(""); try { const response = await sendCampaignNow(settings, campaignId, { version_id: version.id, include_warnings: true, check_files: false, validate_before_send: false, build_before_send: false, dry_run: effectiveDryRun, use_rate_limit: true, enqueue_imap_task: false }); const result = asRecord(response.result ?? response); setSendResult(result); const sent = result.sent_count ?? 0; const failed = result.failed_count ?? 0; const unknown = result.outcome_unknown_count ?? 0; const paused = result.paused_count ?? 0; setMessage( effectiveDryRun ? "i18n:govoplan-campaign.dry_run_finished_no_message_was_sent.c026c6cd" : `Send finished. SMTP accepted ${String(sent)} message(s), failed ${String(failed)}, outcome unknown ${String(unknown)}, paused ${String(paused)}.` ); setSendConfirmOpen(false); await reload(); await refreshDeliveryOptions(true); } catch (err) { setMessage(""); setError(err instanceof Error ? err.message : String(err)); } finally { setBusy(""); } } async function runQueueForWorkers() { if (!version || busy || !canQueueForWorkers || !workerQueueAvailable || readOnlyVersion || !readyForDelivery || !hasBuild || !mockGateSatisfied || approvalGateBlocksLiveDelivery || deliveryQueued || deliveryStarted || synchronousEligibleCount <= 0) return; setBusy("queue"); setMessage("i18n:govoplan-campaign.committing_the_reviewed_execution_to_the_backgro.6ae349e2"); setQueueResult(null); setError(""); try { const response = await queueCampaign(settings, campaignId, { version_id: version.id, include_warnings: true, enqueue_celery: true, dry_run: false }); const result = asRecord(response); setQueueResult(result); setMessage(i18nMessage("i18n:govoplan-campaign.queued_value0_message_s_value1_worker_task_s_pub.18d67ec8", { value0: String(result.queued_count ?? 0), value1: String(result.enqueued_count ?? 0) })); setQueueConfirmOpen(false); await reload(); await refreshDeliveryOptions(true); } catch (err) { setMessage(""); setError(err instanceof Error ? err.message : String(err)); } finally { setBusy(""); } } async function runDeliveryControl(action: "pause" | "resume" | "cancel") { if (busy || !canControlDelivery) return; setBusy("control"); setMessage(deliveryControlProgressMessage(action)); setError(""); try { const response = action === "pause" ? await pauseCampaign(settings, campaignId) : action === "resume" ? await resumeCampaign(settings, campaignId) : await cancelCampaign(settings, campaignId); const result = asRecord(response.result ?? response); setMessage( action === "pause" ? i18nMessage("i18n:govoplan-campaign.paused_value0_queued_message_s_.c7d568d2", { value0: String(result.paused_count ?? 0) }) : action === "resume" ? i18nMessage("i18n:govoplan-campaign.resumed_value0_message_s_value1_worker_task_s_pu.0f59afb4", { value0: String(result.resumed_count ?? 0), value1: String(result.enqueued_count ?? 0) }) : i18nMessage("i18n:govoplan-campaign.cancelled_value0_unsent_message_s_value1_protect.d24c5e24", { value0: String(result.cancelled_count ?? 0), value1: String(result.protected_count ?? 0), value2: String(result.skipped_count ?? 0) }) ); if (action === "cancel") setCancelDeliveryConfirmOpen(false); await reload(); await refreshDeliveryOptions(true); } catch (err) { setMessage(""); setError(err instanceof Error ? err.message : String(err)); } finally { setBusy(""); } } async function runRetryFailed() { if (busy || !canRetryDelivery || retryableCount <= 0 || !workerQueueAvailable) return; setBusy("retry"); setMessage("i18n:govoplan-campaign.queueing_retryable_failed_messages_for_backgroun.4bc80cd9"); setError(""); try { const response = await retryCampaignJobs(settings, campaignId, { enqueue_celery: true }); const result = asRecord(response.result ?? response); setMessage(i18nMessage("i18n:govoplan-campaign.queued_value0_retryable_message_s_value1_worker_.f4795bdb", { value0: String(result.selected_count ?? 0), value1: String(result.enqueued_count ?? 0) })); await reload(); await refreshDeliveryOptions(true); } catch (err) { setMessage(""); setError(err instanceof Error ? err.message : String(err)); } finally { setBusy(""); } } async function runAppendSent() { if (!version || busy || !canAppendPendingImap) return; const runInline = !backgroundWorkersEnabled; setBusy("imap"); setError(""); setMessage(runInline ? "i18n:govoplan-campaign.appending_pending_sent_copies_via_imap.3f0daf68" : "i18n:govoplan-campaign.queueing_pending_imap_append_jobs.31dd388e"); try { const response = await appendSent(settings, campaignId, { enqueue_celery: backgroundWorkersEnabled, run_inline: runInline, dry_run: false }); const result = asRecord(response.result ?? response); setImapAppendResult(result); const appended = numberFrom(result, ["appended_count"]); const failed = numberFrom(result, ["failed_count"]); const enqueued = numberFrom(result, ["enqueued_count"]); const pending = numberFrom(result, ["pending_count"]); setMessage(runInline ? `IMAP append processed ${pending} pending job(s): appended ${appended}, failed ${failed}.` : `Queued ${enqueued} pending IMAP append job(s).`); await refreshQueueStatus(true); await loadImapDiagnostics(true); } catch (err) { setMessage(""); setError(err instanceof Error ? err.message : String(err)); } finally { setBusy(""); } } async function loadImapDiagnostics(silent = false) { if (!version?.id) return; setBusy("inspect"); if (!silent) setMessage("i18n:govoplan-campaign.loading_imap_diagnostics.a5b00c0e"); setError(""); try { const diagnosticsKey = imapDiagnosticsDeltaKey(version.id); const result = await fetchJobsDelta(diagnosticsKey, imapDiagnosticsRef.current, { versionId: version.id, page: 1, pageSize: 50, imapStatus: ["pending", "failed"] }); imapDiagnosticsRef.current = result; setImapDiagnostics(result); if (!silent) setMessage(i18nMessage("i18n:govoplan-campaign.loaded_value_pending_failed_imap_job_s.39b1b268", { value0: result.total })); } catch (err) { if (!silent) setMessage(""); setError(err instanceof Error ? err.message : String(err)); } finally { setBusy(""); } } async function openDeliveryJobDetail(jobId: string) { if (!jobId || busy) return; setBusy("inspect"); setError(""); try { const detail = await getCampaignJobDetail(settings, campaignId, jobId); setSelectedDeliveryJobDetail(detail as unknown as Record); } catch (err) { setError(err instanceof Error ? err.message : String(err)); } finally { setBusy(""); } } async function completeInspection(_acceptBulk = false) { if (!version || busy || readOnlyVersion || automaticInspectionComplete || !canCompleteInspection || downstreamDeliveryActivity) return; setBusy("inspect"); setError(""); setMessage("i18n:govoplan-campaign.recording_the_completed_message_review.16f58b81"); try { const reviewed = new Set(reviewedMessageKeys); await updateCampaignReviewState(settings, campaignId, version.id, { inspection_complete: true, reviewed_message_keys: [...reviewed], issue_decisions: Object.entries(reviewIssueDecisions).map( ([jobId, reason]) => ({ job_id: jobId, decision: "accept" as const, reason: reason.trim() || null }) ) }); setReviewedMessageKeys(reviewed); setNewlyReviewedRequiredKeys(new Set()); setMessageReviewComplete(true); setReviewJobs((current) => { const updated = { ...current, review: { ...current.review, inspection_complete: true, reviewed_required_count: explicitReviewCount } }; reviewJobsRef.current = updated; return updated; }); setReviewConfirmOpen(false); setMessage("i18n:govoplan-campaign.message_review_completed_and_recorded_for_this_b.452d87de"); await reload(); } catch (err) { setMessage(""); setError(err instanceof Error ? err.message : String(err)); } finally { setBusy(""); } } async function openMockMessage(id: string) { if (!id || busy === "mailbox" || !mockMailboxPreviewActive) return; setBusy("mailbox"); setError(""); try { const response = await getMockMailboxMessage(settings, id); setSelectedMockMessage(response.message); } catch (err) { setError(err instanceof Error ? err.message : String(err)); } finally { setBusy(""); } } async function openBuiltMessageAtIndex(index: number) { const row = filteredBuiltReviewRows[index]; if (!row) return; await openBuiltMessage(row, index); } async function openBuiltMessage(row: Record, requestedIndex?: number) { const index = requestedIndex ?? findBuiltMessageIndex(filteredBuiltReviewRows, row); if (index < 0) return; const reviewRow = filteredBuiltReviewRows[index] ?? row; const jobId = String(reviewRow.id ?? ""); if (!jobId || reviewRow.resolved_recipients || reviewRow.attachments || reviewRow.issues) { setSelectedBuiltIndex(index); return; } setBusy("inspect"); setError(""); try { const detail = await getCampaignJobDetail(settings, campaignId, jobId); setBuiltReviewRows((current) => current.map((item, itemIndex) => sameBuiltMessage(item, itemIndex, reviewRow, index) ? { ...item, ...detail.job, review_key: item.review_key, reviewed: item.reviewed, attempts: detail.attempts } : item)); setSelectedBuiltIndex(index); } catch (err) { setError(err instanceof Error ? err.message : String(err)); } finally { setBusy(""); } } function acceptBuiltMessageReview( row: Record, index: number, reasonRequired: boolean ) { const jobId = String(row.id ?? "").trim(); const reviewKey = String( row.review_key ?? builtMessageKey(row, index) ); const reason = reviewIssueDecisions[jobId] ?? ""; if (!jobId) { setError("The built message has no delivery job id."); return; } if (reasonRequired && !reason.trim()) { setError("Enter a reason for accepting the attachment exception."); return; } setReviewedMessageKeys((current) => new Set(current).add(reviewKey)); setNewlyReviewedRequiredKeys((current) => new Set(current).add(reviewKey) ); setReviewIssueDecisions((current) => ({ ...current, [jobId]: reason })); setError(""); } async function sendSingleBuiltMessage() { if (!version || busy || singleSendConfirmIndex === null) return; const row = filteredBuiltReviewRows[singleSendConfirmIndex]; const jobId = String(row?.id ?? ""); if (!row || !jobId) { setSingleSendConfirmIndex(null); setError("Selected message has no delivery job id."); return; } setBusy("send"); setMessage("Sending one selected message."); setError(""); try { const response = await sendCampaignJob(settings, campaignId, jobId, { kind: singleMessageActionKind, idempotency_key: crypto.randomUUID(), reason: singleMessageActionKind === "single_resend" ? singleMessageResendReason.trim() : undefined, context: { surface: "campaign_review_preview" }, include_warnings: true, use_rate_limit: true, enqueue_imap_task: backgroundWorkersEnabled }); const actionResult = asRecord(response.result ?? response); const sendResult = asRecord(actionResult.result); const status = String(sendResult.status ?? "submitted"); const attemptCount = Number(sendResult.attempt_number ?? row.attempt_count ?? 0); const finalSendStatus = String(actionResult.final_send_status ?? ""); if (singleMessageActionKind !== "test") setBuiltReviewRows((current) => current.map((item) => String(item.id ?? "") === jobId ? { ...item, send_status: finalSendStatus || (status === "already_accepted" ? "smtp_accepted" : status), queue_status: ["smtp_accepted", "already_accepted", "accepted", "accepted_with_refusals"].includes(finalSendStatus || status) ? "draft" : item.queue_status, imap_status: sendResult.imap_status ?? item.imap_status, attempt_count: Number.isFinite(attemptCount) ? attemptCount : item.attempt_count, last_error: sendResult.message ?? "" } : item)); setMessage(`${humanize(singleMessageActionKind)} finished: ${humanize(String(actionResult.status ?? status))}.`); setSingleSendConfirmIndex(null); setSingleMessageResendReason(""); setSelectedBuiltIndex(null); await refreshQueueStatus(true); await reload(); } catch (err) { setMessage(""); setError(err instanceof Error ? err.message : String(err)); } finally { setBusy(""); } } function resetDownstreamReview() { setMessageReviewComplete(false); setBuiltReviewRows([]); setReviewJobs(emptyCampaignJobsResponse()); reviewJobsRef.current = emptyCampaignJobsResponse(); setReviewPage(1); setJobsLoadedKey(""); setReviewedMessageKeys(new Set()); setNewlyReviewedRequiredKeys(new Set()); setSelectedBuiltIndex(null); setSingleSendConfirmIndex(null); setSingleMessageResendReason(""); setMockResult(null); setSelectedMockMessage(null); resetDeltaWatermark(); } function scrollToStage(id: string) { revealReviewElement(id); } function revealReviewElement(id: string) { const target = document.getElementById(id); if (!target) return; const stage = target.classList.contains("review-flow-stage") ? target : target.closest(".review-flow-stage"); stage?.querySelector('.card-collapse-toggle[aria-expanded="false"]')?.click(); window.requestAnimationFrame(() => { target.scrollIntoView({ behavior: "smooth", block: "start" }); target.focus({ preventScroll: true }); }); } function showBuiltDetails(filters: Record = {}, includeAll = false) { setShowAllReviewJobs(includeAll); setReviewQuery({ sort: null, filters }); setReviewPage(1); window.requestAnimationFrame(() => revealReviewElement("campaign-built-message-details")); } const matchedAttachments = numberFrom(attachmentSummary, ["total_matched_files"]); const missingAttachments = numberFrom(attachmentSummary, ["missing_configs"]); const ambiguousAttachments = numberFrom(attachmentSummary, ["ambiguous_configs"]); const messagesPerMinute = numberFrom(rateLimit, ["messages_per_minute"]); const estimatedMinutes = messagesPerMinute > 0 && jobsTotal > 0 ? Math.ceil(jobsTotal / messagesPerMinute) : null; const mailProfileSelected = Boolean(selectedMailProfileId); const deliverabilityPreflightItems: DeliverabilityPreflightItem[] = [ { label: "Transport", detail: mailProfileSelected ? "i18n:govoplan-campaign.mail_owned_delivery_profile_selected.4f44778e" : "i18n:govoplan-campaign.select_an_authorized_mail_profile_before_live_de.45c80a42", state: mailProfileSelected ? "ready" : "blocked" }, { label: "Policy", detail: validationPresent ? validationErrors > 0 ? "Validation still reports blocking policy or data issues." : "Latest validation has no blocking issue." : "Run validation to evaluate effective mail and attachment policy.", state: validationPresent ? validationErrors > 0 ? "blocked" : validationWarnings > 0 ? "warning" : "ready" : "warning" }, { label: "Messages", detail: hasBuild ? inspectionSatisfied ? "Built messages are reviewed or did not require manual review." : "Built messages still need review before live delivery." : "Build exact messages before sending.", state: hasBuild ? inspectionSatisfied ? "ready" : "warning" : "blocked" }, { label: "Attachments", detail: missingAttachments + ambiguousAttachments === 0 ? "No missing or ambiguous attachment matches in the current summary." : `${missingAttachments} missing and ${ambiguousAttachments} ambiguous attachment match(es).`, state: missingAttachments + ambiguousAttachments === 0 ? "ready" : "blocked" }, { label: "Rate limit", detail: messagesPerMinute > 0 ? `${messagesPerMinute} message(s) per minute; estimated minimum duration ${estimatedMinutes ?? "unknown"} minute(s).` : "No explicit rate limit is configured for this execution.", state: messagesPerMinute > 0 ? "ready" : "warning" }, { label: "Approval", detail: approvalGateConfigured ? approvalGateApproved ? `Approval ${String(approvalGate.request_id ?? "")} covers this exact execution snapshot.` : String(approvalGate.explanation ?? "The configured approval request is not approved yet.") : approvalGateAvailable ? "No approval gate is configured. Add one when this delivery requires independent release authority." : "The optional Approvals module is not active; no Campaign approval gate is configured.", state: approvalGateConfigured ? approvalGateApproved ? "ready" : "blocked" : "info" }, { label: "i18n:govoplan-campaign.delivery_mode.109ed9d1", detail: deliveryOptionsLoading ? "i18n:govoplan-campaign.loading_the_effective_delivery_policy_.d6893011" : synchronousSendAllowed ? i18nMessage("i18n:govoplan-campaign.send_now_is_available_for_value0_eligible_messag.6ec0ed6c", { value0: synchronousEligibleCount, value1: synchronousSendLimit, value2: workerQueueAvailable ? "i18n:govoplan-campaign.the_worker_queue_is_also_available_.7b77144e" : "i18n:govoplan-campaign.background_workers_are_not_configured_.efa72396" }) : workerQueueAvailable ? i18nMessage("i18n:govoplan-campaign.send_now_is_unavailable_value0_queue_for_workers.59bfc873", { value0: synchronousSendReason(synchronousSendOption) }) : i18nMessage("i18n:govoplan-campaign.no_real_delivery_mode_is_currently_available_val.618cce1f", { value0: synchronousSendReason(synchronousSendOption) }), state: synchronousSendAllowed || workerQueueAvailable ? "ready" : deliveryOptionsLoading ? "info" : "blocked" }, { label: "Sent copy", detail: Boolean(imapAppend.enabled) ? mailProfileSelected ? i18nMessage("i18n:govoplan-campaign.imap_append_requested_for_value0_validation_chec.51527a20", { value0: String(imapAppend.folder ?? "auto") }) : "i18n:govoplan-campaign.imap_append_is_enabled_but_no_mail_profile_is_se.cf50419e" : "i18n:govoplan-campaign.imap_append_is_disabled_for_this_campaign.7757f7f1", state: Boolean(imapAppend.enabled) ? mailProfileSelected ? "ready" : "blocked" : "info" }]; const canCompleteInspection = blockingReviewCount === 0 && reviewRequiredCount > 0 && reviewedExplicitCount === explicitReviewCount; return ( } headerLoading={loading || Boolean(busy)} error={error} actions={ void reload({ force: true }), loading: loading || Boolean(busy) }} primaryActions={} />} notices={(message || (version && (historicalVersion || readyForDelivery || userLockedVersion || finalVersion))) ? <> {message && {message}} {version && (historicalVersion || readyForDelivery || userLockedVersion || finalVersion) && } : undefined} >
0 && visibleValidationIssues.length > 0 ? { label: "i18n:govoplan-campaign.validation_details.aa503267", onActivate: () => revealReviewElement("campaign-validation-details") } : undefined} /> 0 && visibleValidationIssues.length > 0 ? { label: "i18n:govoplan-campaign.validation_details.aa503267", onActivate: () => revealReviewElement("campaign-validation-details") } : undefined} /> 0 ? { label: "i18n:govoplan-campaign.review_candidates.438b8b57", onActivate: () => showBuiltDetails() } : undefined} /> {validationPresent && validationErrors === 0 &&

}
void reloadAttachmentPreview(false)} onLink={() => void linkMatchedAttachmentFiles()} />
{visibleValidationIssues.length > 0 &&

i18n:govoplan-campaign.validation_details.aa503267

{visibleValidationIssues.map((issue, index) =>
{humanize(String(issue.severity ?? "issue"))}
{String(issue.message ?? issue.code ?? "i18n:govoplan-campaign.validation_issue.800cc3d0")} {issue.path ? · {String(issue.path)} : null} {issue.code ? · {String(issue.code)} : null}
)}
{validationIssues.length > visibleValidationIssues.length &&

i18n:govoplan-campaign.showing.163d8174 {visibleValidationIssues.length} of {validationIssues.length} i18n:govoplan-campaign.validation_issue_s.c6a8b911

}
}
0 ? { label: "i18n:govoplan-campaign.show_all_messages.1c2107a1", onActivate: () => showBuiltDetails({}, true) } : undefined} /> 0 ? { label: "i18n:govoplan-campaign.review.e29a79fe", onActivate: () => showBuiltDetails({ validation: 'list:["blocked"]' }) } : undefined} /> 0 ? { label: "i18n:govoplan-campaign.review_candidates.438b8b57", onActivate: () => showBuiltDetails({ validation: 'list:["warning","needs_review"]' }) } : undefined} /> 0 ? { label: "i18n:govoplan-campaign.reviewed.31ef8593", onActivate: () => showBuiltDetails({ reviewed: 'list:["yes"]' }, true) } : undefined} /> 0 ? { label: "i18n:govoplan-campaign.review.e29a79fe", onActivate: () => showBuiltDetails({ reviewed: 'list:["no"]' }) } : undefined} /> 0 ? { label: "i18n:govoplan-campaign.review_candidates.438b8b57", onActivate: () => showBuiltDetails() } : undefined} /> 0 ? { label: "i18n:govoplan-campaign.review_attachment_rules.552c2116", onActivate: () => revealReviewElement("campaign-attachment-preview") } : undefined} /> {hasBuild && } {hasBuild && }

i18n:govoplan-campaign.building_freezes_the_current_recipients_rendered.273a8170

{hasBuild && Object.keys(residualFileDisposition).length > 0 && (

i18n:govoplan-campaign.unassigned_file_policy

i18n:govoplan-campaign.action.97c89a4d
{humanize(String(residualFileDisposition.action ?? "review"))}
i18n:govoplan-campaign.validation_policy.57dcc756
{humanize(String(residualFileDisposition.validation_behavior ?? "warn"))}
i18n:govoplan-campaign.unassigned_files_detected
{String(residualFileDisposition.residual_file_count ?? 0)}
i18n:govoplan-campaign.watched_sources
{String(residualFileDisposition.watched_source_count ?? 0)}
{residualFileRecipient.email ?
i18n:govoplan-campaign.recipient.90343260
{String(residualFileRecipient.email)}
: null}
)} {hasBuild && Object.keys(attachmentReuse).length > 0 && (

Attachment reuse policy

Action
{humanize(String(attachmentReusePolicy.action ?? "allow"))}
Allowed boundary
{humanize(String(attachmentReusePolicy.allow_within ?? "none"))}
Repeated files
{String(attachmentReuse.duplicate_file_count ?? 0)}
Allowed
{String(attachmentReuse.allowed_file_count ?? 0)}
Policy findings
{String(attachmentReuse.violation_file_count ?? 0)}
Affected messages
{String(attachmentReuse.affected_message_count ?? 0)}
{attachmentReuseFindings.length > 0 && (
    {attachmentReuseFindings.slice(0, 10).map((finding) => (
  • {String(finding.file_name ?? "Attachment")} · {String(finding.use_count ?? 0)} uses · {humanize(String(finding.disposition ?? "allowed"))}
  • ))}
)} {attachmentReuseFindings.length > 10 &&

Showing 10 of {attachmentReuseFindings.length} repeated files.

}
)} {getText(printOutput, "render_id") && (

Printable output

Template revision {String(printOutput.template_revision ?? "—")} · {String(printOutput.item_count ?? 0)} recipient item(s) · {String(printOutput.page_count ?? 0)} page(s) · {String(printOutput.output_size_bytes ?? 0)} B

{getText(printArtifact, "download_path") && ( )}
Template hash
{getText(printOutput, "template_hash") || "—"}
Input hash
{getText(printOutput, "input_hash") || "—"}
Output hash
{getText(printOutput, "output_sha256") || "—"}
)}
{reviewRequiredCount > 0 && }
{automaticInspectionComplete &&

} {hasBuild &&
{filteredBuiltReviewRows.length} i18n:govoplan-campaign.matching_of.66a3778e {reviewJobs.total_unfiltered} i18n:govoplan-campaign.built_job_s.82d49ade
{ setReviewPageSize(pageSize); setReviewPage(1); } }} onQueryChange={(query) => { if (reviewQueryEquals(reviewQuery, query)) return; setReviewQuery(query); setReviewPage(1); }} />

i18n:govoplan-campaign.ready_messages_are_hidden_initially_messages_mar.1cb9770d

}
{!mockWorkflowAvailable &&

i18n:govoplan-campaign.mock_delivery_uses_the_mail_module_development_m.9d645a5f

}
{mockResult &&

i18n:govoplan-campaign.recipient_outcomes.d88abb50

String(row.entry_id ?? row.entry_index ?? index)} emptyText="i18n:govoplan-campaign.no_mock_delivery_results_were_returned.96b6736d" className="data-table-wrap data-table compact-table" />
{mockMailboxPreviewActive ?

i18n:govoplan-campaign.captured_mock_messages.5839fae1

String(row.id ?? index)} emptyText="i18n:govoplan-campaign.no_mock_messages_were_captured_in_this_run.2b6e90f0" className="data-table-wrap data-table compact-table" />
:

i18n:govoplan-campaign.captured_mock_messages.5839fae1

{mockWorkflowAvailable ? "i18n:govoplan-campaign.captured_mailbox_preview_is_disabled_for_this_ru.6559458d" : "i18n:govoplan-campaign.captured_mailbox_preview_requires_the_mail_devel.9b3d9f0d"}

}
}
0 ? { label: "i18n:govoplan-campaign.show_all_messages.1c2107a1", onActivate: () => showBuiltDetails({}, true) } : undefined} /> 0 ? { label: "i18n:govoplan-campaign.show_all_messages.1c2107a1", onActivate: () => showBuiltDetails({}, true) } : undefined} /> 0 ? { label: "i18n:govoplan-campaign.review_attachment_rules.552c2116", onActivate: () => revealReviewElement("campaign-attachment-preview") } : undefined} /> 0 ? { label: "i18n:govoplan-campaign.review_attachment_rules.552c2116", onActivate: () => revealReviewElement("campaign-attachment-preview") } : undefined} /> 0 ? i18nMessage("i18n:govoplan-campaign.value_min.c9d89eae", { value0: messagesPerMinute }) : "i18n:govoplan-campaign.not_set.93039e60"} />
Approval gate {!approvalGateConfigured && approvalGateAvailable && canRequestApproval && } {approvalGateConfigured && }
{queueStatusNote &&

{queueStatusNote}

}
i18n:govoplan-campaign.a_dry_run_checks_the_frozen_queue_and_delivery_c.c42924bb

{i18nMessage("i18n:govoplan-campaign.queue_for_workers_commits_durable_jobs_and_retur.d0dbe81a", { value0: synchronousSendLimit || "i18n:govoplan-campaign.the_configured_maximum.eb10006b" })}

{!synchronousSendAllowed && canSendSynchronously &&

{i18nMessage("i18n:govoplan-campaign.send_now_is_unavailable_value0_.d93c0b29", { value0: synchronousSendReason(synchronousSendOption) })}

} {queueResult &&

{i18nMessage("i18n:govoplan-campaign.worker_queue_committed_value0_message_s_and_publ.24400d3c", { value0: String(queueResult.queued_count ?? 0), value1: String(queueResult.enqueued_count ?? 0) })}

} {sendResult &&

i18n:govoplan-campaign.attempted.a9eb9c90 {String(sendResult.attempted_count ?? "—")}i18n:govoplan-campaign.smtp_accepted.a5d0dccc {String(sendResult.sent_count ?? "—")}i18n:govoplan-campaign.failed.fac9f871 {String(sendResult.failed_count ?? "—")}i18n:govoplan-campaign.outcome_unknown.4383023a {String(sendResult.outcome_unknown_count ?? 0)}i18n:govoplan-campaign.skipped.6b98496c {String(sendResult.skipped_count ?? "—")}.

SMTP batch: {humanize(String(sendResult.batch_state ?? "not_started"))} · connections {String(sendResult.smtp_connection_count ?? 0)} · reconnects {String(sendResult.smtp_reconnect_count ?? 0)} · paused {String(sendResult.paused_count ?? 0)}.

{sendResult.batch_state === "paused" && Remaining messages were paused before SMTP after a systemic transport failure ({String(sendResult.batch_pause_reason_code ?? "smtp_systemic_failure")}). Review the Mail profile, then resume the campaign queue. } {sendResultRows.length > 0 && `send-result-${index}`} emptyText="i18n:govoplan-campaign.no_send_results_returned.d1ec3e23" className="data-table-wrap data-table compact-table" /> }
}
{deliveryStarted ? "i18n:govoplan-campaign.delivery_activity_is_available_in_the_report_and.cb163d1d" : "i18n:govoplan-campaign.no_real_delivery_has_started_for_this_campaign_v.3b9235ff"}
{persistedDeliveryMode &&

{persistedDeliveryModeSelectedAt ? i18nMessage("i18n:govoplan-campaign.this_version_last_entered_value0_mode_at_value1_.c087d2f3", { value0: deliveryModeLabel(persistedDeliveryMode), value1: formatDateTime(persistedDeliveryModeSelectedAt) }) : i18nMessage("i18n:govoplan-campaign.this_version_last_entered_value0_mode_this_recor.ea2f201f", { value0: deliveryModeLabel(persistedDeliveryMode) })}

}
{Boolean(imapAppend.enabled) && imapPendingForDisplay > 0 &&

i18n:govoplan-campaign.imap_sent_append_is_still_pending_for.475700e4 {imapPendingForDisplay} i18n:govoplan-campaign.job_s_pending_with_no_imap_attempt_usually_means.0776d29f

} {Boolean(imapAppend.enabled) && imapFailedForDisplay > 0 &&

{imapFailedForDisplay} i18n:govoplan-campaign.imap_append_job_s_failed_load_diagnostics_to_ins.9de2b9cd

} {Boolean(imapAppend.enabled) && (imapPendingForDisplay > 0 || imapFailedForDisplay > 0) &&
{imapPendingForDisplay > 0 && }
} {imapAppendResult &&

i18n:govoplan-campaign.imap_append_result.544fc7ed

i18n:govoplan-campaign.pending.96f608c1 {String(imapAppendResult.pending_count ?? "0")}i18n:govoplan-campaign.enqueued.d93063b1 {String(imapAppendResult.enqueued_count ?? "0")}i18n:govoplan-campaign.processed.90e0ee45 {String(imapAppendResult.processed_count ?? "0")}i18n:govoplan-campaign.appended.f15bf444 {String(imapAppendResult.appended_count ?? "0")}i18n:govoplan-campaign.failed.fac9f871 {String(imapAppendResult.failed_count ?? "0")}.

{imapAppendResultRows.length > 0 && String(row.job_id ?? index)} emptyText="i18n:govoplan-campaign.no_imap_append_result_rows_returned.13a62b2e" className="data-table-wrap data-table compact-table" /> }
} {imapDiagnostics.total > 0 &&

i18n:govoplan-campaign.imap_diagnostics.f3666fc4

String(row.id ?? index)} emptyText="i18n:govoplan-campaign.no_pending_or_failed_imap_jobs_found.eb8adfdc" className="data-table-wrap data-table compact-table" />
} {recentFailures.length > 0 &&

i18n:govoplan-campaign.recent_delivery_failures.282285fb

{recentFailures.map((failure, index) =>
{String(failure.recipient_email ?? failure.entry_id ?? i18nMessage("i18n:govoplan-campaign.value.44b8c76f", { value0: String(failure.entry_index ?? index + 1) }))}
{humanize(String(failure.send_status ?? failure.imap_status ?? "failed"))} {failure.last_error ? `: ${String(failure.last_error)}` : ""} {failure.updated_at ? · {formatDateTime(String(failure.updated_at))} : null}
)}
}
{selectedBuiltMessage && selectedBuiltIndex !== null && { const jobId = String(selectedBuiltMessage.id ?? "").trim(); if (!jobId) return; setReviewIssueDecisions((current) => ({ ...current, [jobId]: value })); }} onAcceptReview={(reasonRequired) => acceptBuiltMessageReview( selectedBuiltMessage, selectedBuiltIndex, reasonRequired ) } onSelect={openBuiltMessageAtIndex} onSendSingle={(targetIndex) => { const target = filteredBuiltReviewRows[targetIndex]; const status = String(target?.send_status ?? "not_queued"); setSingleMessageActionKind( ["not_queued", "cancelled", "queued"].includes(status) && Number(target?.attempt_count ?? 0) === 0 ? "single_send" : "single_resend" ); setSingleMessageResendReason(""); setSingleSendConfirmIndex(targetIndex); }} onClose={() => setSelectedBuiltIndex(null)} /> } setApprovalDialogOpen(false)} footer={<> }>

The request covers the exact built message, attachment, recipient, policy, and transport snapshot. Rebuilding invalidates and removes this gate.

setApprovalSelectorValue(event.target.value)} /> setApprovalRequiredCount(Math.max(1, Number(event.target.value) || 1))} />
Actors excluded by four-eyes policy
{(["author", "owner", "validator", "builder", "reviewer"] as const).map((role) => setApprovalExcludedRoles((current) => { const next = new Set(current); if (checked) next.add(role); else next.delete(role); return next; })} />)}
void runValidation(true)} onCancel={() => setAttachmentLockConfirmOpen(false)} /> void completeInspection(true)} onCancel={() => setReviewConfirmOpen(false)} /> setQueueConfirmOpen(false)} onConfirm={() => void runQueueForWorkers()} /> setSendConfirmOpen(false)} onConfirm={() => void runSendNow()} /> setCancelDeliveryConfirmOpen(false)} onConfirm={() => void runDeliveryControl("cancel")} /> { setSingleSendConfirmIndex(null); setSingleMessageResendReason(""); }} footer={ <> } > {singleSendConfirmRow && (

{singleMessageActionKind === "test" ? "Transports this exact rendered message once without changing its official delivery state." : singleMessageActionKind === "single_resend" ? "Transports this exact message again. A successful resend can complete it; a failed resend preserves an earlier success." : "Makes the first official delivery attempt and changes only this message after SMTP acceptance."}

Recipient
{formatAddressList(asRecord(singleSendConfirmRow.resolved_recipients).to) || String(singleSendConfirmRow.recipient_email ?? "—")}
Version
{String(version?.version_number ?? "—")}
Message
{String(singleSendConfirmRow.eml_sha256 ?? "—").slice(0, 16)}
State
{humanize(String(singleSendConfirmRow.send_status ?? "not_queued"))}
{singleMessageActionKind === "single_resend" && (