From 06f773e4ebae2d73fdbfbb6975129a3297dbb824 Mon Sep 17 00:00:00 2001 From: Albrecht Degering Date: Tue, 18 Aug 2026 13:17:29 +0200 Subject: [PATCH] Adopt shared WebUI structural primitives --- .../src/features/campaigns/ReviewSendPage.tsx | 107 +++++++++--------- .../recipients/RecipientAddressEditor.tsx | 5 +- .../review/AttachmentLinkingPreview.tsx | 15 ++- .../campaigns/review/WorkflowNavigation.tsx | 63 +++-------- webui/src/styles/campaign-workspace.css | 68 ----------- 5 files changed, 81 insertions(+), 177 deletions(-) diff --git a/webui/src/features/campaigns/ReviewSendPage.tsx b/webui/src/features/campaigns/ReviewSendPage.tsx index 60a0f88..93945f9 100644 --- a/webui/src/features/campaigns/ReviewSendPage.tsx +++ b/webui/src/features/campaigns/ReviewSendPage.tsx @@ -38,7 +38,7 @@ import { type CampaignSummary } from "../../api/campaigns"; import { getMockMailboxMessage, type MockMailboxMessage } from "../../api/mail"; -import { FormGrid, Button, Dialog, FormField, SegmentedControl, hasScope, useDeltaWatermarks, useGuardedNavigate, usePlatformUiCapability, type AuthInfo, type MailDevMailboxUiCapability } from "@govoplan/core-webui"; +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"; @@ -80,7 +80,6 @@ import { } from "./review/ReviewWorkflowGuidance"; import { calculateBuildReviewProgress } from "./review/reviewProgress"; import { - WorkflowFact, WorkflowNavigation, WorkflowStage, stageConnectorState, @@ -1398,12 +1397,12 @@ export default function ReviewSendPage({
-
- - - - -
+ + + + + + -
- - - - - - - -
+ + + + + + + + + {hasBuild && } {hasBuild && }

i18n:govoplan-campaign.building_freezes_the_current_recipients_rendered.273a8170

@@ -1559,12 +1558,12 @@ export default function ReviewSendPage({
-
- - - - -
+ + + + + +
}
-
- - - - -
+ + + + + + {queueStatusNote &&

{queueStatusNote}

} @@ -1718,18 +1717,18 @@ export default function ReviewSendPage({
-
- - - - - - - - - - -
+ + + + + + + + + + + +
{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"} diff --git a/webui/src/features/campaigns/recipients/RecipientAddressEditor.tsx b/webui/src/features/campaigns/recipients/RecipientAddressEditor.tsx index 6550c62..de38c35 100644 --- a/webui/src/features/campaigns/recipients/RecipientAddressEditor.tsx +++ b/webui/src/features/campaigns/recipients/RecipientAddressEditor.tsx @@ -1,6 +1,7 @@ import { useEffect, useMemo, useRef, useState, type ClipboardEvent } from "react"; import { ArrowDown, ArrowUp, Copy, Pencil, Plus, Trash2 } from "lucide-react"; import { + ActionToolbar, Button, Dialog, DismissibleAlert, @@ -309,7 +310,7 @@ function RecipientAddressCategoryEditor({ column, addresses, merge, locked, onAd return (
-
+

{column.label}

{onMergeChange ? @@ -322,7 +323,7 @@ function RecipientAddressCategoryEditor({ column, addresses, merge, locked, onAd onChange={onMergeChange} /> : null}
-
+
{column.allowMultiple && draftAddresses.length === 0 && diff --git a/webui/src/features/campaigns/review/AttachmentLinkingPreview.tsx b/webui/src/features/campaigns/review/AttachmentLinkingPreview.tsx index c5c306c..1fbca6d 100644 --- a/webui/src/features/campaigns/review/AttachmentLinkingPreview.tsx +++ b/webui/src/features/campaigns/review/AttachmentLinkingPreview.tsx @@ -1,6 +1,6 @@ import { useState } from "react"; import { Link2, X } from "lucide-react"; -import { Button, Dialog, DismissibleAlert, i18nMessage } from "@govoplan/core-webui"; +import { Button, Dialog, DismissibleAlert, MetricCard, MetricGrid, i18nMessage } from "@govoplan/core-webui"; import type { CampaignAttachmentPreviewFile, @@ -10,7 +10,6 @@ import { attachmentPreviewLinkableFiles, attachmentPreviewMatchedFiles } from "../utils/attachmentPreview"; -import { WorkflowFact } from "./WorkflowNavigation"; export default function AttachmentLinkingPreview({ preview, @@ -70,8 +69,8 @@ export default function AttachmentLinkingPreview({
-
- + 0 ? ( @@ -95,19 +94,19 @@ export default function AttachmentLinkingPreview({ ) } /> - - - -
+ {error && ( {error} diff --git a/webui/src/features/campaigns/review/WorkflowNavigation.tsx b/webui/src/features/campaigns/review/WorkflowNavigation.tsx index ec28cf2..7522d87 100644 --- a/webui/src/features/campaigns/review/WorkflowNavigation.tsx +++ b/webui/src/features/campaigns/review/WorkflowNavigation.tsx @@ -1,6 +1,7 @@ -import { useState, type CSSProperties, type ReactNode } from "react"; -import { ChevronDown, LockKeyhole, type LucideIcon } from "lucide-react"; +import { type CSSProperties, type ReactNode } from "react"; +import { LockKeyhole, type LucideIcon } from "lucide-react"; import { + Card, InlineHelp, StageRail, i18nMessage, @@ -107,7 +108,6 @@ export function WorkflowStage({ }) { const Icon = stage.icon; const locked = stage.state === "locked"; - const [collapsed, setCollapsed] = useState(false); const title = i18nMessage(stage.title); const description = i18nMessage(stage.description); const stateText = i18nMessage(stage.stateLabel); @@ -127,11 +127,16 @@ export function WorkflowStage({
{nextState &&
}
-
-
+ title={

{title} {locked && ( @@ -153,54 +158,22 @@ export function WorkflowStage({ )} {description}

-
- -
-
- {!collapsed && ( - <> -
{children}
- {locked && ( + } + afterBody={locked ? (
{lockReason}
- )} - - )} -
+ ) : undefined} + > + {children} + ); } -export function WorkflowFact({ label, value }: { label: string; value: ReactNode }) { - return ( -
- {label} - {value} -
- ); -} - export function stateLabel(state: FlowState): string { switch (state) { case "complete": diff --git a/webui/src/styles/campaign-workspace.css b/webui/src/styles/campaign-workspace.css index 34c498d..33c9c2e 100644 --- a/webui/src/styles/campaign-workspace.css +++ b/webui/src/styles/campaign-workspace.css @@ -133,12 +133,6 @@ font-size: 14px; letter-spacing: .01em; } -.subsection-heading.split { - display: flex; - align-items: center; - justify-content: space-between; - gap: 12px; -} .toggle-grid { display: grid; grid-template-columns: repeat(2, minmax(180px, 1fr)); @@ -1042,19 +1036,6 @@ padding: 10px; } -.recipient-address-category-header { - display: flex; - align-items: center; - justify-content: space-between; - gap: 12px; -} - -.recipient-address-category-header h3 { - margin: 0; - font-size: 15px; - color: var(--text-strong); -} - .recipient-address-category-controls { display: flex; align-items: center; @@ -1821,43 +1802,6 @@ background: var(--surface); } -.review-flow-fact-grid, -.review-flow-execution-summary { - display: grid; - grid-template-columns: repeat(4, minmax(130px, 1fr)); - gap: 10px; - margin-bottom: 16px; -} - -.review-flow-fact, -.review-flow-execution-summary > div { - display: grid; - gap: 5px; - min-width: 0; - padding: 12px 14px; - border: var(--border-line); - border-radius: 6px; - background: var(--panel-soft); -} - -.review-flow-fact span, -.review-flow-execution-summary span { - color: var(--muted); - font-size: 11px; - font-weight: 800; - letter-spacing: .04em; - text-transform: uppercase; -} - -.review-flow-fact strong, -.review-flow-execution-summary strong { - min-width: 0; - overflow: hidden; - color: var(--text-strong); - font-size: 16px; - text-overflow: ellipsis; -} - .review-flow-inline-note { display: flex; align-items: center; @@ -2009,13 +1953,6 @@ 50% { box-shadow: 0 0 0 7px color-mix(in srgb, var(--review-stage-color) 18%, transparent), 0 4px 12px var(--campaign-stage-shadow-color); } } -@media (max-width: 980px) { - .review-flow-fact-grid, - .review-flow-execution-summary { - grid-template-columns: repeat(2, minmax(130px, 1fr)); - } -} - @media (max-width: 680px) { .review-flow-stage { grid-template-columns: 36px minmax(0, 1fr); @@ -2036,11 +1973,6 @@ .review-flow-lock-message { inset: 100px 0 0; } - .review-flow-fact-grid, - .review-flow-execution-summary { - grid-template-columns: 1fr; - } - .deliverability-preflight-header { align-items: flex-start; flex-direction: column;