refactor: adopt shared page and workspace layouts
This commit is contained in:
@@ -4,7 +4,7 @@ import { useGuardedNavigate, usePlatformModuleInstalled, usePlatformUiCapability
|
||||
import type { ApiSettings } from "../../types";
|
||||
import { Button } from "@govoplan/core-webui";
|
||||
import { Card } from "@govoplan/core-webui";
|
||||
import { PageTitle } from "@govoplan/core-webui";
|
||||
import { PageLayout } from "@govoplan/core-webui";
|
||||
import { LoadingFrame } from "@govoplan/core-webui";
|
||||
import { MetricCard } from "@govoplan/core-webui";
|
||||
import { FormField } from "@govoplan/core-webui";
|
||||
@@ -247,22 +247,24 @@ export default function AttachmentsDataPage({ settings, campaignId }: {settings:
|
||||
|
||||
|
||||
return (
|
||||
<div className="content-pad workspace-data-page">
|
||||
<div className="page-heading split workspace-heading">
|
||||
<div>
|
||||
<PageTitle loading={loading}>i18n:govoplan-campaign.attachments.6771ade6</PageTitle>
|
||||
<VersionLine version={version} versions={data.versions} status={saveState} />
|
||||
</div>
|
||||
<div className="button-row compact-actions">
|
||||
<PageLayout
|
||||
mode="workspace"
|
||||
title="i18n:govoplan-campaign.attachments.6771ade6"
|
||||
description={<VersionLine version={version} versions={data.versions} status={saveState} />}
|
||||
headerLoading={loading}
|
||||
error={error}
|
||||
actions={
|
||||
<>
|
||||
{filesModuleInstalled && <Button onClick={() => navigate("/files")}>i18n:govoplan-campaign.manage_files.90a419f7</Button>}
|
||||
<Button onClick={() => void discardDraft()} disabled={loading}>Discard</Button>
|
||||
<Button variant="primary" onClick={() => saveDraft("manual")} disabled={!canSave}>i18n:govoplan-campaign.save.efc007a3</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{error && <DismissibleAlert tone="danger" resetKey={error} floating>{error}</DismissibleAlert>}
|
||||
{localError && <DismissibleAlert tone="danger" resetKey={localError} floating>{localError}</DismissibleAlert>}
|
||||
{locked && <LockedVersionNotice settings={settings} campaignId={campaignId} version={version} currentVersionId={data.campaign?.current_version_id} reload={reload} message="i18n:govoplan-campaign.this_page_is_read_only_for_the_selected_version.dacf5743" />}
|
||||
</>
|
||||
}
|
||||
notices={(localError || locked) ? <>
|
||||
{localError && <DismissibleAlert tone="danger" resetKey={localError} floating>{localError}</DismissibleAlert>}
|
||||
{locked && <LockedVersionNotice settings={settings} campaignId={campaignId} version={version} currentVersionId={data.campaign?.current_version_id} reload={reload} message="i18n:govoplan-campaign.this_page_is_read_only_for_the_selected_version.dacf5743" />}
|
||||
</> : undefined}
|
||||
>
|
||||
|
||||
<LoadingFrame loading={loading || !draft} label="i18n:govoplan-campaign.loading_campaign_draft.1cf47e50">
|
||||
<>
|
||||
@@ -452,7 +454,7 @@ export default function AttachmentsDataPage({ settings, campaignId }: {settings:
|
||||
onConfirm={confirmIndividualDisable}
|
||||
onCancel={() => setIndividualDisable(null)} />
|
||||
|
||||
</div>);
|
||||
</PageLayout>);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
import type { ApiSettings } from "../../types";
|
||||
import { Button } from "@govoplan/core-webui";
|
||||
import { Card } from "@govoplan/core-webui";
|
||||
import { DismissibleAlert } from "@govoplan/core-webui";
|
||||
import { PageTitle } from "@govoplan/core-webui";
|
||||
import VersionLine from "./components/VersionLine";
|
||||
import { LoadingFrame } from "@govoplan/core-webui";
|
||||
import { ActionBlockerHint, DocumentationHelpLink } from "@govoplan/core-webui";
|
||||
import { ActionBlockerHint, DocumentationHelpLink, PageLayout } from "@govoplan/core-webui";
|
||||
import { useCampaignWorkspaceData } from "./hooks/useCampaignWorkspaceData";
|
||||
|
||||
export default function CampaignAuditPage({ settings, campaignId }: {settings: ApiSettings;campaignId: string;}) {
|
||||
@@ -13,20 +10,17 @@ export default function CampaignAuditPage({ settings, campaignId }: {settings: A
|
||||
const version = data.currentVersion;
|
||||
|
||||
return (
|
||||
<div className="content-pad workspace-data-page">
|
||||
<div className="page-heading split workspace-heading">
|
||||
<div>
|
||||
<PageTitle loading={loading}>i18n:govoplan-campaign.audit_log.3cfc5f1c</PageTitle>
|
||||
<VersionLine version={version} versions={data.versions} loadedAt={version?.updated_at} />
|
||||
</div>
|
||||
<div className="button-row compact-actions">
|
||||
<PageLayout
|
||||
mode="workspace"
|
||||
title="i18n:govoplan-campaign.audit_log.3cfc5f1c"
|
||||
description={<VersionLine version={version} versions={data.versions} loadedAt={version?.updated_at} />}
|
||||
loading={loading}
|
||||
loadingLabel="i18n:govoplan-campaign.loading_audit_data.af52b968"
|
||||
error={error}
|
||||
actions={
|
||||
<Button onClick={() => void reload({ force: true })} disabled={loading}>Discard</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{error && <DismissibleAlert tone="danger" resetKey={error} floating>{error}</DismissibleAlert>}
|
||||
|
||||
<LoadingFrame loading={loading} label="i18n:govoplan-campaign.loading_audit_data.af52b968">
|
||||
}
|
||||
>
|
||||
<Card title="i18n:govoplan-campaign.recent_audit_events.7ec32b1d">
|
||||
<ActionBlockerHint
|
||||
tone="info"
|
||||
@@ -50,7 +44,6 @@ export default function CampaignAuditPage({ settings, campaignId }: {settings: A
|
||||
label="Open Campaign assurance documentation"
|
||||
/>
|
||||
</Card>
|
||||
</LoadingFrame>
|
||||
</div>);
|
||||
</PageLayout>);
|
||||
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useMemo, useRef } from "react";
|
||||
import type { ApiSettings } from "../../types";
|
||||
import { Button } from "@govoplan/core-webui";
|
||||
import { PageTitle } from "@govoplan/core-webui";
|
||||
import { PageLayout } from "@govoplan/core-webui";
|
||||
import { Card } from "@govoplan/core-webui";
|
||||
import { LoadingFrame } from "@govoplan/core-webui";
|
||||
import LockedVersionNotice from "./components/LockedVersionNotice";
|
||||
@@ -150,22 +150,24 @@ export default function CampaignFieldsPage({ settings, campaignId }: {settings:
|
||||
|
||||
|
||||
return (
|
||||
<div className="content-pad workspace-data-page">
|
||||
<div className="page-heading split workspace-heading">
|
||||
<div>
|
||||
<PageTitle loading={loading}>i18n:govoplan-campaign.fields.e8b68527</PageTitle>
|
||||
<VersionLine version={version} versions={data.versions} status={saveState} />
|
||||
</div>
|
||||
<div className="button-row compact-actions">
|
||||
<PageLayout
|
||||
mode="workspace"
|
||||
title="i18n:govoplan-campaign.fields.e8b68527"
|
||||
description={<VersionLine version={version} versions={data.versions} status={saveState} />}
|
||||
headerLoading={loading}
|
||||
error={error}
|
||||
actions={
|
||||
<>
|
||||
<Button onClick={() => void discardDraft()} disabled={loading}>Discard</Button>
|
||||
<Button variant="primary" onClick={saveFields} disabled={!canSave}>i18n:govoplan-campaign.save.efc007a3</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{error && <DismissibleAlert tone="danger" resetKey={error} floating>{error}</DismissibleAlert>}
|
||||
{localError && <DismissibleAlert tone="danger" resetKey={localError} floating>{localError}</DismissibleAlert>}
|
||||
{fieldNameWarning && <DismissibleAlert tone="warning" resetKey={fieldNameWarning} floating>{fieldNameWarning}</DismissibleAlert>}
|
||||
{locked && <LockedVersionNotice settings={settings} campaignId={campaignId} version={version} currentVersionId={data.campaign?.current_version_id} reload={reload} message="i18n:govoplan-campaign.this_page_is_read_only_for_the_selected_version.dacf5743" />}
|
||||
</>
|
||||
}
|
||||
notices={(localError || fieldNameWarning || locked) ? <>
|
||||
{localError && <DismissibleAlert tone="danger" resetKey={localError} floating>{localError}</DismissibleAlert>}
|
||||
{fieldNameWarning && <DismissibleAlert tone="warning" resetKey={fieldNameWarning} floating>{fieldNameWarning}</DismissibleAlert>}
|
||||
{locked && <LockedVersionNotice settings={settings} campaignId={campaignId} version={version} currentVersionId={data.campaign?.current_version_id} reload={reload} message="i18n:govoplan-campaign.this_page_is_read_only_for_the_selected_version.dacf5743" />}
|
||||
</> : undefined}
|
||||
>
|
||||
|
||||
<LoadingFrame loading={loading || !draft} label="i18n:govoplan-campaign.loading_campaign_draft.1cf47e50">
|
||||
<>
|
||||
@@ -184,7 +186,7 @@ export default function CampaignFieldsPage({ settings, campaignId }: {settings:
|
||||
</Card>
|
||||
</>
|
||||
</LoadingFrame>
|
||||
</div>);
|
||||
</PageLayout>);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
import type { ApiSettings } from "../../types";
|
||||
import { Card } from "@govoplan/core-webui";
|
||||
import { Button } from "@govoplan/core-webui";
|
||||
import { DismissibleAlert } from "@govoplan/core-webui";
|
||||
import { PageTitle } from "@govoplan/core-webui";
|
||||
import { DismissibleAlert, PageLayout } from "@govoplan/core-webui";
|
||||
import VersionLine from "./components/VersionLine";
|
||||
import { LoadingFrame } from "@govoplan/core-webui";
|
||||
import { useCampaignWorkspaceData } from "./hooks/useCampaignWorkspaceData";
|
||||
import { asRecord, getCampaignJson } from "./utils/campaignView";
|
||||
import { downloadJson, safeFileStem } from "./utils/draftEditor";
|
||||
@@ -17,28 +15,30 @@ export default function CampaignJsonView({ settings, campaignId }: {settings: Ap
|
||||
const filename = `${safeFileStem(String(campaign.id || data.campaign?.external_id || campaignId))}.json`;
|
||||
|
||||
return (
|
||||
<div className="content-pad workspace-data-page">
|
||||
<div className="page-heading split workspace-heading">
|
||||
<div>
|
||||
<PageTitle loading={loading}>i18n:govoplan-campaign.json.031a4e76</PageTitle>
|
||||
<VersionLine version={version} versions={data.versions} loadedAt={version?.updated_at} />
|
||||
</div>
|
||||
<div className="button-row compact-actions">
|
||||
<PageLayout
|
||||
mode="workspace"
|
||||
title="i18n:govoplan-campaign.json.031a4e76"
|
||||
description={<VersionLine version={version} versions={data.versions} loadedAt={version?.updated_at} />}
|
||||
loading={loading}
|
||||
loadingLabel="i18n:govoplan-campaign.loading_json.812c7a50"
|
||||
error={error}
|
||||
actions={
|
||||
<>
|
||||
<Button onClick={() => void reload({ force: true })} disabled={loading}>Discard</Button>
|
||||
<Button onClick={() => downloadJson(filename, campaignJson)} disabled={!version}>i18n:govoplan-campaign.download_json.d296a30a</Button>
|
||||
</div>
|
||||
</div>
|
||||
{error && <DismissibleAlert tone="danger" resetKey={error} floating>{error}</DismissibleAlert>}
|
||||
<DismissibleAlert tone="warning" dismissible={false}>
|
||||
This expert view contains the complete authorized campaign configuration,
|
||||
including recipient and message fields that may contain personal data.
|
||||
Download and share it only for an authorized purpose.
|
||||
</DismissibleAlert>
|
||||
<LoadingFrame loading={loading} label="i18n:govoplan-campaign.loading_json.812c7a50">
|
||||
</>
|
||||
}
|
||||
notices={
|
||||
<DismissibleAlert tone="warning" dismissible={false}>
|
||||
This expert view contains the complete authorized campaign configuration,
|
||||
including recipient and message fields that may contain personal data.
|
||||
Download and share it only for an authorized purpose.
|
||||
</DismissibleAlert>
|
||||
}
|
||||
>
|
||||
<Card>
|
||||
{!loading || version ? <pre className="code-panel">{JSON.stringify(campaignJson, null, 2)}</pre> : <pre className="code-panel">{"{}"}</pre>}
|
||||
</Card>
|
||||
</LoadingFrame>
|
||||
</div>);
|
||||
</PageLayout>);
|
||||
|
||||
}
|
||||
|
||||
@@ -6,10 +6,8 @@ import type { ApiSettings } from "../../types";
|
||||
import { Card } from "@govoplan/core-webui";
|
||||
import { Button } from "@govoplan/core-webui";
|
||||
import { StatusBadge } from "@govoplan/core-webui";
|
||||
import { PageTitle } from "@govoplan/core-webui";
|
||||
import { LoadingFrame } from "@govoplan/core-webui";
|
||||
import { PageScrollViewport } from "@govoplan/core-webui";
|
||||
import { DismissibleAlert, TableActionGroup, i18nMessage, useGuardedNavigate } from "@govoplan/core-webui";
|
||||
import { PageLayout, TableActionGroup, i18nMessage, useGuardedNavigate } from "@govoplan/core-webui";
|
||||
import { DataGrid, type DataGridColumn } from "@govoplan/core-webui";
|
||||
import { createNewCampaign, listCampaignsDelta, type CampaignDeltaResponse } from "../../api/campaigns";
|
||||
import type { CampaignListItem } from "../../types";
|
||||
@@ -144,22 +142,22 @@ export default function CampaignListPage({ settings }: {settings: ApiSettings;})
|
||||
|
||||
|
||||
return (
|
||||
<PageScrollViewport>
|
||||
<div className="content-pad workspace-data-page campaigns-page">
|
||||
{error && <DismissibleAlert tone="danger" resetKey={error} floating>{error}</DismissibleAlert>}
|
||||
|
||||
<div className="page-heading split workspace-heading">
|
||||
<div>
|
||||
<PageTitle loading={loading}>i18n:govoplan-campaign.all_campaigns.2bd1ee3a</PageTitle>
|
||||
<p className="mono-small">{lastLoadedAt ? i18nMessage("i18n:govoplan-campaign.last_loaded_value.35ef046a", { value0: lastLoadedAt }) : "i18n:govoplan-campaign.not_loaded_yet.9968c191"}</p>
|
||||
</div>
|
||||
<div className="button-row compact-actions">
|
||||
<PageLayout
|
||||
mode="workspace"
|
||||
className="campaigns-page"
|
||||
title="i18n:govoplan-campaign.all_campaigns.2bd1ee3a"
|
||||
description={<p className="mono-small">{lastLoadedAt ? i18nMessage("i18n:govoplan-campaign.last_loaded_value.35ef046a", { value0: lastLoadedAt }) : "i18n:govoplan-campaign.not_loaded_yet.9968c191"}</p>}
|
||||
headerLoading={loading}
|
||||
error={error}
|
||||
actions={
|
||||
<>
|
||||
<Button onClick={() => void load(null)} disabled={loading}>Discard</Button>
|
||||
<Button variant="primary" onClick={create} disabled={creating}>
|
||||
{creating ? "i18n:govoplan-campaign.creating.94d7d8ee" : "i18n:govoplan-campaign.new_campaign.aaf9a8a4"}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
}
|
||||
>
|
||||
|
||||
<Card title="i18n:govoplan-campaign.campaigns.01a23a28">
|
||||
<LoadingFrame loading={loading} label="i18n:govoplan-campaign.loading_campaigns.90a466bb">
|
||||
@@ -186,8 +184,7 @@ export default function CampaignListPage({ settings }: {settings: ApiSettings;})
|
||||
}
|
||||
</LoadingFrame>
|
||||
</Card>
|
||||
</div>
|
||||
</PageScrollViewport>);
|
||||
</PageLayout>);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { lazy } from "react";
|
||||
import {
|
||||
ModuleSubnav,
|
||||
WorkspaceLayout,
|
||||
hasAnyScope,
|
||||
useGuardedNavigate,
|
||||
type ApiSettings,
|
||||
@@ -64,15 +65,19 @@ export default function CampaignModulePage({
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="workspace campaign-module-workspace">
|
||||
<ModuleSubnav active={active} groups={groups} onSelect={select} />
|
||||
<section className="workspace-content">
|
||||
<WorkspaceLayout
|
||||
className="campaign-module-workspace"
|
||||
primary={<ModuleSubnav active={active} groups={groups} onSelect={select} />}
|
||||
primaryLabel="Campaign module sections"
|
||||
contentLabel="Campaign module content"
|
||||
interfaceId="campaign.module-workspace"
|
||||
helpModuleId="campaign"
|
||||
>
|
||||
{active === "queue"
|
||||
? <OperatorQueuePage settings={settings} auth={auth} />
|
||||
: active === "reports"
|
||||
? <AggregateReportsPage settings={settings} />
|
||||
: <CampaignListPage settings={settings} />}
|
||||
</section>
|
||||
</div>
|
||||
</WorkspaceLayout>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ import { Dialog } 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 { PageLayout } from "@govoplan/core-webui";
|
||||
import { StatusBadge } from "@govoplan/core-webui";
|
||||
import { ToggleSwitch } from "@govoplan/core-webui";
|
||||
import { DismissibleAlert, TableActionGroup, hasScope, i18nMessage, useGuardedNavigate, useUnsavedDraftGuard } from "@govoplan/core-webui";
|
||||
@@ -332,13 +332,15 @@ export default function CampaignOverviewPage({ settings, auth, campaignId }: {se
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="content-pad workspace-data-page">
|
||||
<div className="page-heading split workspace-heading">
|
||||
<div>
|
||||
<PageTitle loading={loading}>{campaign?.name || "i18n:govoplan-campaign.overview.0efc2e6b"}</PageTitle>
|
||||
<p className="mono-small">i18n:govoplan-campaign.campaign_overview_version_independent_identity_a.ebaf1113</p>
|
||||
</div>
|
||||
<div className="button-row compact-actions">
|
||||
<PageLayout
|
||||
mode="workspace"
|
||||
title={campaign?.name || "i18n:govoplan-campaign.overview.0efc2e6b"}
|
||||
description={<p className="mono-small">i18n:govoplan-campaign.campaign_overview_version_independent_identity_a.ebaf1113</p>}
|
||||
headerLoading={loading}
|
||||
error={error}
|
||||
success={message}
|
||||
actions={
|
||||
<>
|
||||
{canCopy && data.currentVersion && <Button
|
||||
onClick={() => void prepareLifecycleAction("copy_campaign", data.currentVersion ?? undefined)}
|
||||
disabled={loading || savingIdentity || lockBusy || lifecycleBusy || identityDirty}
|
||||
@@ -371,11 +373,9 @@ export default function CampaignOverviewPage({ settings, auth, campaignId }: {se
|
||||
</Button>}
|
||||
<Button onClick={() => void discardOverview()} disabled={loading || savingIdentity || lockBusy}>Discard</Button>
|
||||
<Button variant="primary" onClick={() => void saveIdentity()} disabled={!campaign || !identityDirty || savingIdentity}>{savingIdentity ? "i18n:govoplan-campaign.saving.56a2285c" : "i18n:govoplan-campaign.save.efc007a3"}</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{error && <DismissibleAlert tone="danger" resetKey={error} floating>{error}</DismissibleAlert>}
|
||||
{message && <DismissibleAlert tone="success" resetKey={message} floating>{message}</DismissibleAlert>}
|
||||
</>
|
||||
}
|
||||
>
|
||||
|
||||
<LoadingFrame loading={loading} label="i18n:govoplan-campaign.loading_campaign_overview.ffa1adf0">
|
||||
<div className="metric-grid campaign-overview-metrics">
|
||||
@@ -618,7 +618,7 @@ export default function CampaignOverviewPage({ settings, auth, campaignId }: {se
|
||||
onCancel={() => setPendingLockAction(null)}
|
||||
onConfirm={() => void applyLockAction()} />
|
||||
|
||||
</div>);
|
||||
</PageLayout>);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ import { DataGrid, type DataGridColumn, type DataGridListOption, type DataGridQu
|
||||
import { Dialog } from "@govoplan/core-webui";
|
||||
import { DismissibleAlert } from "@govoplan/core-webui";
|
||||
import { FormField } from "@govoplan/core-webui";
|
||||
import { PageTitle } from "@govoplan/core-webui";
|
||||
import { PageLayout } from "@govoplan/core-webui";
|
||||
import { StatusBadge } from "@govoplan/core-webui";
|
||||
import VersionLine from "./components/VersionLine";
|
||||
import { LoadingFrame, TableActionGroup, ToggleSwitch, i18nMessage } from "@govoplan/core-webui";
|
||||
@@ -445,20 +445,21 @@ export default function CampaignReportPage({ settings, campaignId }: {settings:
|
||||
[busyAction, retryFailedSynchronously]);
|
||||
|
||||
return (
|
||||
<div className="content-pad workspace-data-page">
|
||||
<div className="page-heading split workspace-heading">
|
||||
<div>
|
||||
<PageTitle loading={loading}>i18n:govoplan-campaign.report.ee45c303</PageTitle>
|
||||
<VersionLine version={version} versions={data.versions} loadedAt={version?.updated_at ?? data.summary?.generated_at} />
|
||||
</div>
|
||||
<div className="button-row compact-actions">
|
||||
<PageLayout
|
||||
mode="workspace"
|
||||
title="i18n:govoplan-campaign.report.ee45c303"
|
||||
description={<VersionLine version={version} versions={data.versions} loadedAt={version?.updated_at ?? data.summary?.generated_at} />}
|
||||
headerLoading={loading}
|
||||
error={error || actionError}
|
||||
success={actionMessage}
|
||||
actions={
|
||||
<>
|
||||
<Button onClick={() => void exportCsv()} disabled={busyAction === "csv"}>i18n:govoplan-campaign.download_csv.eaa216ad</Button>
|
||||
<Button onClick={() => setEmailOpen(true)}>i18n:govoplan-campaign.email_report.ee3e7091</Button>
|
||||
<Button onClick={() => void reloadAll()} disabled={loading || jobsLoading}>Discard</Button>
|
||||
</div>
|
||||
</div>
|
||||
{(error || actionError) && <DismissibleAlert tone="danger" resetKey={`${error}${actionError}`} floating>{error || actionError}</DismissibleAlert>}
|
||||
{actionMessage && <DismissibleAlert tone="success" resetKey={actionMessage} floating>{actionMessage}</DismissibleAlert>}
|
||||
</>
|
||||
}
|
||||
>
|
||||
<LoadingFrame loading={loading} label="i18n:govoplan-campaign.loading_report_data.0908ade5">
|
||||
<div className="dashboard-grid">
|
||||
<Card title="i18n:govoplan-campaign.delivery_outcome.f9d7c085">
|
||||
@@ -647,7 +648,7 @@ export default function CampaignReportPage({ settings, campaignId }: {settings:
|
||||
onConfirm={() => void reconcileOutcome()}
|
||||
onCancel={() => setReconcile(null)} />
|
||||
|
||||
</div>);
|
||||
</PageLayout>);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ import { lazy, useEffect, useState } from "react";
|
||||
import { Navigate, Route, Routes, useLocation, useNavigate, useParams } from "react-router";
|
||||
import {
|
||||
ConcurrencyConflictProvider,
|
||||
WorkspaceLayout,
|
||||
useGuardedNavigate
|
||||
} from "@govoplan/core-webui";
|
||||
import type { ApiSettings, AuthInfo, CampaignWorkspaceSection } from "../../types";
|
||||
@@ -85,9 +86,13 @@ function CampaignWorkspaceInner({ settings, auth }: { settings: ApiSettings; aut
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="workspace">
|
||||
<SectionSidebar active={active} onSelect={select} />
|
||||
<section className="workspace-content">
|
||||
<WorkspaceLayout
|
||||
primary={<SectionSidebar active={active} onSelect={select} />}
|
||||
primaryLabel="Campaign sections"
|
||||
contentLabel="Campaign workspace"
|
||||
interfaceId="campaign.workspace"
|
||||
helpModuleId="campaign"
|
||||
>
|
||||
<Routes>
|
||||
<Route index element={<CampaignOverviewPage settings={settings} auth={auth} campaignId={campaignId || ""} />} />
|
||||
<Route path="data" element={<Navigate to="../recipients" replace />} />
|
||||
@@ -121,8 +126,7 @@ function CampaignWorkspaceInner({ settings, auth }: { settings: ApiSettings; aut
|
||||
<Route path="Review" element={<Navigate to="../" replace />} />
|
||||
<Route path="*" element={<Navigate to="../" replace />} />
|
||||
</Routes>
|
||||
</section>
|
||||
</div>
|
||||
</WorkspaceLayout>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ import { Button } from "@govoplan/core-webui";
|
||||
import { Card } from "@govoplan/core-webui";
|
||||
import { FormField } from "@govoplan/core-webui";
|
||||
import { DismissibleAlert } from "@govoplan/core-webui";
|
||||
import { PageTitle } from "@govoplan/core-webui";
|
||||
import { PageLayout } from "@govoplan/core-webui";
|
||||
import { LoadingFrame } from "@govoplan/core-webui";
|
||||
import { PolicyRow } from "@govoplan/core-webui";
|
||||
import { PolicyTable } from "@govoplan/core-webui";
|
||||
@@ -178,21 +178,23 @@ export default function GlobalSettingsPage({ settings, auth, campaignId, view =
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="content-pad workspace-data-page">
|
||||
<div className="page-heading split workspace-heading">
|
||||
<div>
|
||||
<PageTitle loading={loading}>{pageTitle}</PageTitle>
|
||||
<VersionLine version={version} versions={data.versions} status={saveState} />
|
||||
</div>
|
||||
<div className="button-row compact-actions">
|
||||
<PageLayout
|
||||
mode="workspace"
|
||||
title={pageTitle}
|
||||
description={<VersionLine version={version} versions={data.versions} status={saveState} />}
|
||||
headerLoading={loading}
|
||||
error={error}
|
||||
actions={
|
||||
<>
|
||||
<Button onClick={() => void discardDraft()} disabled={loading}>Discard</Button>
|
||||
<Button variant="primary" onClick={() => saveDraft("manual")} disabled={!dirty || locked || !draft}>{dirty ? "i18n:govoplan-campaign.save_now.3989b7c0" : "i18n:govoplan-campaign.saved.c0ae8f6e"}</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{error && <DismissibleAlert tone="danger" resetKey={error} floating>{error}</DismissibleAlert>}
|
||||
{localError && <DismissibleAlert tone="danger" resetKey={localError} floating>{localError}</DismissibleAlert>}
|
||||
{locked && <LockedVersionNotice settings={settings} campaignId={campaignId} version={version} currentVersionId={data.campaign?.current_version_id} reload={reload} message="i18n:govoplan-campaign.this_page_is_read_only_for_the_selected_version.dacf5743" />}
|
||||
</>
|
||||
}
|
||||
notices={(localError || locked) ? <>
|
||||
{localError && <DismissibleAlert tone="danger" resetKey={localError} floating>{localError}</DismissibleAlert>}
|
||||
{locked && <LockedVersionNotice settings={settings} campaignId={campaignId} version={version} currentVersionId={data.campaign?.current_version_id} reload={reload} message="i18n:govoplan-campaign.this_page_is_read_only_for_the_selected_version.dacf5743" />}
|
||||
</> : undefined}
|
||||
>
|
||||
|
||||
<LoadingFrame loading={loading || !draft} label="i18n:govoplan-campaign.loading_campaign_draft.1cf47e50">
|
||||
{isPolicyView ?
|
||||
@@ -500,7 +502,7 @@ export default function GlobalSettingsPage({ settings, auth, campaignId, view =
|
||||
onClose={() => setPostboxTargetsOpen(false)}
|
||||
/>
|
||||
}
|
||||
</div>);
|
||||
</PageLayout>);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
LoadingFrame,
|
||||
MailServerFolderLookupResultView,
|
||||
MetricCard,
|
||||
PageTitle,
|
||||
PageLayout,
|
||||
PasswordField,
|
||||
ToggleSwitch,
|
||||
usePlatformModuleInstalled,
|
||||
@@ -326,21 +326,23 @@ export default function MailSettingsPage({ settings, campaignId, view = "setting
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="content-pad workspace-data-page">
|
||||
<div className="page-heading split workspace-heading">
|
||||
<div>
|
||||
<PageTitle loading={loading}>{isPolicyView ? "i18n:govoplan-campaign.mail_policy.3eb5d32a" : "i18n:govoplan-campaign.mail_settings.19e07f55"}</PageTitle>
|
||||
<VersionLine version={version} versions={data.versions} status={saveState} />
|
||||
</div>
|
||||
<div className="button-row compact-actions">
|
||||
<PageLayout
|
||||
mode="workspace"
|
||||
title={isPolicyView ? "i18n:govoplan-campaign.mail_policy.3eb5d32a" : "i18n:govoplan-campaign.mail_settings.19e07f55"}
|
||||
description={<VersionLine version={version} versions={data.versions} status={saveState} />}
|
||||
headerLoading={loading}
|
||||
error={error}
|
||||
actions={
|
||||
<>
|
||||
{!isPolicyView && <Button variant="primary" onClick={() => void saveDraft("manual")} disabled={!canSave}>{dirty ? "i18n:govoplan-campaign.save_now.3989b7c0" : "i18n:govoplan-campaign.saved.c0ae8f6e"}</Button>}
|
||||
<Button onClick={() => void discardDraft()} disabled={loading}>i18n:govoplan-campaign.discard.36fff63c</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{error && <DismissibleAlert tone="danger" resetKey={error} floating>{error}</DismissibleAlert>}
|
||||
{localError && <DismissibleAlert tone="danger" resetKey={localError} floating>{localError}</DismissibleAlert>}
|
||||
{locked && <LockedVersionNotice settings={settings} campaignId={campaignId} version={version} currentVersionId={data.campaign?.current_version_id} reload={reload} message="i18n:govoplan-campaign.this_page_is_read_only_for_the_selected_version.dacf5743" />}
|
||||
</>
|
||||
}
|
||||
notices={(localError || locked) ? <>
|
||||
{localError && <DismissibleAlert tone="danger" resetKey={localError} floating>{localError}</DismissibleAlert>}
|
||||
{locked && <LockedVersionNotice settings={settings} campaignId={campaignId} version={version} currentVersionId={data.campaign?.current_version_id} reload={reload} message="i18n:govoplan-campaign.this_page_is_read_only_for_the_selected_version.dacf5743" />}
|
||||
</> : undefined}
|
||||
>
|
||||
|
||||
<LoadingFrame loading={loading || !draft} label="i18n:govoplan-campaign.loading_campaign_draft.1cf47e50">
|
||||
<>
|
||||
@@ -501,7 +503,7 @@ export default function MailSettingsPage({ settings, campaignId, view = "setting
|
||||
</Card>}
|
||||
</>
|
||||
</LoadingFrame>
|
||||
</div>
|
||||
</PageLayout>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ 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 { PageTitle } from "@govoplan/core-webui";
|
||||
import { PageLayout } from "@govoplan/core-webui";
|
||||
import { StatusBadge } from "@govoplan/core-webui";
|
||||
import { ToggleSwitch } from "@govoplan/core-webui";
|
||||
import { i18nMessage } from "@govoplan/core-webui";
|
||||
@@ -1364,34 +1364,33 @@ export default function ReviewSendPage({
|
||||
reviewedExplicitCount === explicitReviewCount;
|
||||
|
||||
return (
|
||||
<div className="content-pad workspace-data-page review-send-page">
|
||||
<div className="page-heading split workspace-heading">
|
||||
<div>
|
||||
<PageTitle loading={loading || Boolean(busy)}>i18n:govoplan-campaign.review_send.1627617d</PageTitle>
|
||||
<VersionLine version={version} versions={data.versions} loadedAt={version?.updated_at} />
|
||||
</div>
|
||||
<div className="button-row compact-actions">
|
||||
<PageLayout
|
||||
mode="workspace"
|
||||
className="review-send-page"
|
||||
title="i18n:govoplan-campaign.review_send.1627617d"
|
||||
description={<VersionLine version={version} versions={data.versions} loadedAt={version?.updated_at} />}
|
||||
headerLoading={loading || Boolean(busy)}
|
||||
error={error}
|
||||
actions={
|
||||
<>
|
||||
<Button onClick={() => void refreshQueueStatus(false)} disabled={!version || queueStatusLoading || Boolean(busy)}>
|
||||
{queueStatusLoading ? "i18n:govoplan-campaign.refreshing_status.d8965739" : "i18n:govoplan-campaign.refresh_status.ade15a52"}
|
||||
</Button>
|
||||
<Button onClick={() => void reload({ force: true })} disabled={loading || Boolean(busy)}>Discard</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{error && <DismissibleAlert tone="danger" resetKey={error} floating>{error}</DismissibleAlert>}
|
||||
{message && <DismissibleAlert tone="info" resetKey={message} floating>{message}</DismissibleAlert>}
|
||||
|
||||
|
||||
{version && (historicalVersion || readyForDelivery || userLockedVersion || finalVersion) &&
|
||||
<LockedVersionNotice
|
||||
settings={settings}
|
||||
campaignId={campaignId}
|
||||
version={version}
|
||||
currentVersionId={data.campaign?.current_version_id}
|
||||
reload={reload}
|
||||
message="i18n:govoplan-campaign.this_workflow_is_read_only_for_the_selected_vers.6626b342" />
|
||||
|
||||
</>
|
||||
}
|
||||
notices={(message || (version && (historicalVersion || readyForDelivery || userLockedVersion || finalVersion))) ? <>
|
||||
{message && <DismissibleAlert tone="info" resetKey={message} floating>{message}</DismissibleAlert>}
|
||||
{version && (historicalVersion || readyForDelivery || userLockedVersion || finalVersion) &&
|
||||
<LockedVersionNotice
|
||||
settings={settings}
|
||||
campaignId={campaignId}
|
||||
version={version}
|
||||
currentVersionId={data.campaign?.current_version_id}
|
||||
reload={reload}
|
||||
message="i18n:govoplan-campaign.this_workflow_is_read_only_for_the_selected_vers.6626b342" />}
|
||||
</> : undefined}
|
||||
>
|
||||
|
||||
<LoadingFrame loading={loading} label="i18n:govoplan-campaign.loading_workflow_state.5319aae1">
|
||||
<WorkflowNavigation stages={stages} onSelect={scrollToStage} />
|
||||
@@ -2113,6 +2112,6 @@ export default function ReviewSendPage({
|
||||
onClose={() => setSelectedMockMessage(null)} />
|
||||
|
||||
}
|
||||
</div>);
|
||||
</PageLayout>);
|
||||
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ import { Card } from "@govoplan/core-webui";
|
||||
import { Dialog } from "@govoplan/core-webui";
|
||||
import { FormField } from "@govoplan/core-webui";
|
||||
import { FieldLabel } from "@govoplan/core-webui";
|
||||
import { PageTitle } from "@govoplan/core-webui";
|
||||
import { PageLayout } from "@govoplan/core-webui";
|
||||
import { LoadingFrame } from "@govoplan/core-webui";
|
||||
import { DismissibleAlert, SegmentedControl, ToggleSwitch, i18nMessage } from "@govoplan/core-webui";
|
||||
import { WysiwygEditor, type WysiwygEditorHandle } from "@govoplan/core-webui/wysiwyg";
|
||||
@@ -411,23 +411,25 @@ export default function TemplateDataPage({ settings, campaignId }: {settings: Ap
|
||||
|
||||
|
||||
return (
|
||||
<div className="content-pad workspace-data-page">
|
||||
<div className="page-heading split workspace-heading">
|
||||
<div>
|
||||
<PageTitle loading={loading}>i18n:govoplan-campaign.template.3ec1ae06</PageTitle>
|
||||
<VersionLine version={version} versions={data.versions} status={saveState} />
|
||||
</div>
|
||||
<div className="button-row compact-actions">
|
||||
<PageLayout
|
||||
mode="workspace"
|
||||
title="i18n:govoplan-campaign.template.3ec1ae06"
|
||||
description={<VersionLine version={version} versions={data.versions} status={saveState} />}
|
||||
headerLoading={loading}
|
||||
error={error}
|
||||
success={contentLibraryNotice}
|
||||
actions={
|
||||
<>
|
||||
<Button onClick={() => window.location.assign("/templates")}>i18n:govoplan-campaign.manage_templates.23688071</Button>
|
||||
<Button onClick={() => void discardDraft()} disabled={loading}>Discard</Button>
|
||||
<Button variant="primary" onClick={() => saveDraft("manual")} disabled={!dirty || locked || !draft}>i18n:govoplan-campaign.save.efc007a3</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{error && <DismissibleAlert tone="danger" resetKey={error} floating>{error}</DismissibleAlert>}
|
||||
{localError && <DismissibleAlert tone="danger" resetKey={localError} floating>{localError}</DismissibleAlert>}
|
||||
{contentLibraryNotice && <DismissibleAlert tone="success" resetKey={contentLibraryNotice} floating>{contentLibraryNotice}</DismissibleAlert>}
|
||||
{locked && <LockedVersionNotice settings={settings} campaignId={campaignId} version={version} currentVersionId={data.campaign?.current_version_id} reload={reload} message="i18n:govoplan-campaign.this_page_is_read_only_for_the_selected_version.dacf5743" />}
|
||||
</>
|
||||
}
|
||||
notices={(localError || locked) ? <>
|
||||
{localError && <DismissibleAlert tone="danger" resetKey={localError} floating>{localError}</DismissibleAlert>}
|
||||
{locked && <LockedVersionNotice settings={settings} campaignId={campaignId} version={version} currentVersionId={data.campaign?.current_version_id} reload={reload} message="i18n:govoplan-campaign.this_page_is_read_only_for_the_selected_version.dacf5743" />}
|
||||
</> : undefined}
|
||||
>
|
||||
|
||||
<LoadingFrame loading={loading || !draft} label="i18n:govoplan-campaign.loading_campaign_draft.1cf47e50">
|
||||
<>
|
||||
@@ -783,7 +785,7 @@ export default function TemplateDataPage({ settings, campaignId }: {settings: Ap
|
||||
localFields={localFieldOptions}
|
||||
globalFields={globalFieldOptions} />
|
||||
|
||||
</div>);
|
||||
</PageLayout>);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { ReactNode } from "react";
|
||||
import { Button, DismissibleAlert, LoadingFrame, PageTitle } from "@govoplan/core-webui";
|
||||
import { Button, DismissibleAlert, PageLayout } from "@govoplan/core-webui";
|
||||
import type { ApiSettings } from "../../../types";
|
||||
import type { CampaignVersionDetail, CampaignVersionListItem } from "../../../api/campaigns";
|
||||
import LockedVersionNotice from "./LockedVersionNotice";
|
||||
@@ -43,25 +43,26 @@ export default function CampaignDraftPageScaffold({
|
||||
children
|
||||
}: CampaignDraftPageScaffoldProps) {
|
||||
return (
|
||||
<div className="content-pad workspace-data-page">
|
||||
<div className="page-heading split workspace-heading">
|
||||
<div>
|
||||
<PageTitle loading={loading}>{title}</PageTitle>
|
||||
<VersionLine version={version} versions={versions} status={saveState} />
|
||||
</div>
|
||||
<div className="button-row compact-actions">
|
||||
<PageLayout
|
||||
mode="workspace"
|
||||
title={title}
|
||||
description={<VersionLine version={version} versions={versions} status={saveState} />}
|
||||
loading={loading || !draft}
|
||||
loadingLabel="i18n:govoplan-campaign.loading_campaign_draft.1cf47e50"
|
||||
error={error || ""}
|
||||
actions={
|
||||
<>
|
||||
<Button onClick={onReload} disabled={loading}>Discard</Button>
|
||||
<Button variant="primary" onClick={onSave} disabled={!dirty || locked || !draft}>i18n:govoplan-campaign.save.efc007a3</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{error && <DismissibleAlert tone="danger" resetKey={error} floating>{error}</DismissibleAlert>}
|
||||
{localError && <DismissibleAlert tone="danger" resetKey={localError} floating>{localError}</DismissibleAlert>}
|
||||
{locked && <LockedVersionNotice settings={settings} campaignId={campaignId} version={version} currentVersionId={currentVersionId} reload={onReload} message="i18n:govoplan-campaign.this_page_is_read_only_for_the_selected_version.dacf5743" />}
|
||||
|
||||
<LoadingFrame loading={loading || !draft} label="i18n:govoplan-campaign.loading_campaign_draft.1cf47e50">
|
||||
<>{children}</>
|
||||
</LoadingFrame>
|
||||
</div>);
|
||||
</>
|
||||
}
|
||||
notices={(localError || locked) ?
|
||||
<>
|
||||
{localError && <DismissibleAlert tone="danger" resetKey={localError} floating>{localError}</DismissibleAlert>}
|
||||
{locked && <LockedVersionNotice settings={settings} campaignId={campaignId} version={version} currentVersionId={currentVersionId} reload={onReload} message="i18n:govoplan-campaign.this_page_is_read_only_for_the_selected_version.dacf5743" />}
|
||||
</> : undefined}
|
||||
>
|
||||
{children}
|
||||
</PageLayout>);
|
||||
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Link } from "react-router";
|
||||
import {
|
||||
PageTitle,
|
||||
PageLayout,
|
||||
WizardDirectory,
|
||||
usePlatformUiCapabilities,
|
||||
wizardEntriesForContext,
|
||||
@@ -38,18 +38,20 @@ export default function WizardDirectoryPage({
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="content-pad workspace-data-page">
|
||||
<div className="page-heading split workspace-heading">
|
||||
<PageTitle>i18n:govoplan-campaign.guided_workflows</PageTitle>
|
||||
<PageLayout
|
||||
mode="workspace"
|
||||
title="i18n:govoplan-campaign.guided_workflows"
|
||||
actions={
|
||||
<Link className="btn btn-secondary" to="../">
|
||||
i18n:govoplan-campaign.back_to_overview.ec986cba
|
||||
</Link>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<WizardDirectory
|
||||
entries={entries}
|
||||
auth={auth}
|
||||
emptyText="i18n:govoplan-campaign.no_guided_workflows"
|
||||
/>
|
||||
</div>
|
||||
</PageLayout>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -25,11 +25,9 @@ import {
|
||||
type DataGridListOption,
|
||||
type DataGridQueryState
|
||||
} from "@govoplan/core-webui";
|
||||
import { DismissibleAlert } from "@govoplan/core-webui";
|
||||
import { DismissibleAlert, PageLayout } from "@govoplan/core-webui";
|
||||
import { LoadingFrame } from "@govoplan/core-webui";
|
||||
import { MetricCard } from "@govoplan/core-webui";
|
||||
import { PageTitle } from "@govoplan/core-webui";
|
||||
import { PageScrollViewport } from "@govoplan/core-webui";
|
||||
import {
|
||||
StatusBadge,
|
||||
TableActionGroup,
|
||||
@@ -757,22 +755,22 @@ export default function OperatorQueuePage({ settings, auth }: {settings: ApiSett
|
||||
], [navigate, permissions.canOpenReport, selectedRow, selectedVersionId]);
|
||||
|
||||
return (
|
||||
<PageScrollViewport>
|
||||
<div className="content-pad workspace-data-page">
|
||||
<div className="page-heading split workspace-heading">
|
||||
<div>
|
||||
<PageTitle loading={loading}>i18n:govoplan-campaign.operator_queue.72492fb5</PageTitle>
|
||||
<PageLayout
|
||||
mode="workspace"
|
||||
title="i18n:govoplan-campaign.operator_queue.72492fb5"
|
||||
description={
|
||||
<>
|
||||
<p>i18n:govoplan-campaign.queue_state_retry_candidates_and_reconciliation_.1b592bbe</p>
|
||||
<p className="muted small-note">i18n:govoplan-campaign.queue_refreshes_automatically_while_delivery_is_active.df8dc205</p>
|
||||
</div>
|
||||
<div className="button-row compact-actions">
|
||||
</>
|
||||
}
|
||||
error={error}
|
||||
success={message}
|
||||
actions={
|
||||
<Button onClick={() => void refreshAll()} disabled={loading || jobsLoading}>i18n:govoplan-campaign.refresh.56e3badc</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{error && <DismissibleAlert tone="danger" resetKey={error} floating>{error}</DismissibleAlert>}
|
||||
{jobsError && <DismissibleAlert tone="danger" resetKey={jobsError} floating>{jobsError}</DismissibleAlert>}
|
||||
{message && <DismissibleAlert tone="success" resetKey={message} floating>{message}</DismissibleAlert>}
|
||||
}
|
||||
notices={jobsError ? <DismissibleAlert tone="danger" resetKey={jobsError} floating>{jobsError}</DismissibleAlert> : undefined}
|
||||
>
|
||||
|
||||
<section className="queue-pressure-section" aria-labelledby="operator-queue-pressure-title">
|
||||
<h2 id="operator-queue-pressure-title" className="queue-pressure-heading">i18n:govoplan-campaign.queue_pressure.28eee15e</h2>
|
||||
@@ -847,8 +845,7 @@ export default function OperatorQueuePage({ settings, auth }: {settings: ApiSett
|
||||
if (cancelTarget) void runAction(cancelTarget, "cancel");
|
||||
}}
|
||||
onCancel={() => setCancelTarget(null)} />
|
||||
</div>
|
||||
</PageScrollViewport>);
|
||||
</PageLayout>);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -5,11 +5,9 @@ import {
|
||||
Button,
|
||||
Card,
|
||||
DataGrid,
|
||||
DismissibleAlert,
|
||||
LoadingFrame,
|
||||
MetricCard,
|
||||
PageScrollViewport,
|
||||
PageTitle,
|
||||
PageLayout,
|
||||
StatusBadge,
|
||||
TableActionGroup,
|
||||
formatDateTime,
|
||||
@@ -167,24 +165,21 @@ export default function AggregateReportsPage({ settings }: {settings: ApiSetting
|
||||
const outcomes = report?.outcomes;
|
||||
|
||||
return (
|
||||
<PageScrollViewport className="aggregate-reports-page">
|
||||
<div className="content-pad workspace-data-page">
|
||||
<div className="page-heading split workspace-heading">
|
||||
<div>
|
||||
<PageTitle loading={listLoading || reportLoading}>i18n:govoplan-campaign.reports.88bc3fe3</PageTitle>
|
||||
<p className="muted">i18n:govoplan-campaign.privacy_protected_campaign_outcomes_without_reci.5b8a100d</p>
|
||||
</div>
|
||||
<div className="button-row compact-actions">
|
||||
<PageLayout
|
||||
mode="workspace"
|
||||
className="aggregate-reports-page"
|
||||
title="i18n:govoplan-campaign.reports.88bc3fe3"
|
||||
description="i18n:govoplan-campaign.privacy_protected_campaign_outcomes_without_reci.5b8a100d"
|
||||
error={error}
|
||||
actions={
|
||||
<Button
|
||||
onClick={() => void Promise.all([loadCampaigns(), selectedFromUrl ? loadReport(selectedFromUrl) : Promise.resolve()])}
|
||||
disabled={listLoading || reportLoading}
|
||||
>
|
||||
<RefreshCw size={16} aria-hidden="true" /> i18n:govoplan-campaign.refresh.56e3badc
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{error && <DismissibleAlert tone="danger" resetKey={error} floating>{error}</DismissibleAlert>}
|
||||
}
|
||||
>
|
||||
|
||||
<Card title="i18n:govoplan-campaign.campaign_reports_available_to_you.f14fa403">
|
||||
<LoadingFrame loading={listLoading} label="i18n:govoplan-campaign.loading_campaign_reports_.61ec1ee8">
|
||||
@@ -252,8 +247,7 @@ export default function AggregateReportsPage({ settings }: {settings: ApiSetting
|
||||
</>
|
||||
)}
|
||||
</LoadingFrame>
|
||||
</div>
|
||||
</PageScrollViewport>
|
||||
</PageLayout>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -2,8 +2,7 @@ import { useMemo, useState } from "react";
|
||||
import { ExternalLink } from "lucide-react";
|
||||
import { Button } from "@govoplan/core-webui";
|
||||
import { Card } from "@govoplan/core-webui";
|
||||
import { PageScrollViewport } from "@govoplan/core-webui";
|
||||
import { PageTitle } from "@govoplan/core-webui";
|
||||
import { PageLayout, WorkspaceLayout } from "@govoplan/core-webui";
|
||||
import { FieldLabel } from "@govoplan/core-webui";
|
||||
import { StatusBadge, TableActionGroup, i18nMessage } from "@govoplan/core-webui";
|
||||
import { usePlatformLanguage } from "@govoplan/core-webui";
|
||||
@@ -137,20 +136,25 @@ export default function TemplatesPage() {
|
||||
|
||||
if (selectedTemplate) {
|
||||
return (
|
||||
<div className="workspace module-workspace">
|
||||
<ModuleSubnav active={active} groups={templateSubnav(() => setSelectedId(null))} onSelect={setActive} />
|
||||
<section className="workspace-content">
|
||||
<div className="content-pad workspace-data-page">
|
||||
<div className="page-heading split workspace-heading">
|
||||
<div>
|
||||
<PageTitle>{selectedTemplate.name}</PageTitle>
|
||||
<p>{selectedTemplate.description}</p>
|
||||
</div>
|
||||
<div className="button-row compact-actions">
|
||||
<WorkspaceLayout
|
||||
className="module-workspace"
|
||||
primary={<ModuleSubnav active={active} groups={templateSubnav(() => setSelectedId(null))} onSelect={setActive} />}
|
||||
primaryLabel="Template sections"
|
||||
contentLabel="Template workspace"
|
||||
interfaceId="campaign.template-workspace"
|
||||
helpModuleId="campaign"
|
||||
>
|
||||
<PageLayout
|
||||
mode="workspace"
|
||||
title={selectedTemplate.name}
|
||||
description={selectedTemplate.description}
|
||||
actions={
|
||||
<>
|
||||
<Button disabled>i18n:govoplan-campaign.duplicate.972d5737</Button>
|
||||
<Button variant="primary" disabled>i18n:govoplan-campaign.use_in_campaign.779163bc</Button>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
}
|
||||
>
|
||||
|
||||
{active === "overview" && <TemplateOverview template={selectedTemplate} />}
|
||||
{active === "content" && <TemplateContent template={selectedTemplate} />}
|
||||
@@ -158,25 +162,23 @@ export default function TemplatesPage() {
|
||||
{active === "preview" && <TemplatePreview template={selectedTemplate} />}
|
||||
{active === "usage" && <TemplateUsage template={selectedTemplate} />}
|
||||
{active === "versions" && <TemplateVersions template={selectedTemplate} />}
|
||||
</div>
|
||||
</section>
|
||||
</div>);
|
||||
</PageLayout>
|
||||
</WorkspaceLayout>);
|
||||
|
||||
}
|
||||
|
||||
return (
|
||||
<PageScrollViewport>
|
||||
<div className="content-pad workspace-data-page module-entry-page">
|
||||
<div className="page-heading split workspace-heading">
|
||||
<div>
|
||||
<PageTitle>i18n:govoplan-campaign.templates.f25b700e</PageTitle>
|
||||
<p>i18n:govoplan-campaign.reusable_message_templates_open_a_template_to_ed.792e2afb</p>
|
||||
</div>
|
||||
<div className="button-row compact-actions">
|
||||
<PageLayout
|
||||
className="module-entry-page"
|
||||
title="i18n:govoplan-campaign.templates.f25b700e"
|
||||
description="i18n:govoplan-campaign.reusable_message_templates_open_a_template_to_ed.792e2afb"
|
||||
actions={
|
||||
<>
|
||||
<Button disabled>i18n:govoplan-campaign.import.d6fbc9d2</Button>
|
||||
<Button variant="primary" disabled>i18n:govoplan-campaign.export.f3e4fadb</Button>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
}
|
||||
>
|
||||
|
||||
<Card
|
||||
title={
|
||||
@@ -196,8 +198,7 @@ export default function TemplatesPage() {
|
||||
className="compact-table-wrap module-table-wrap module-entry-table" />
|
||||
|
||||
</Card>
|
||||
</div>
|
||||
</PageScrollViewport>);
|
||||
</PageLayout>);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,26 @@ const sources = sourceFiles(sourceRoot).map((file) => ({
|
||||
value: fs.readFileSync(file, "utf8"),
|
||||
}));
|
||||
|
||||
for (const { file, value } of sources) {
|
||||
assert.ok(
|
||||
!value.includes('className="content-pad workspace-data-page'),
|
||||
`${path.relative(sourceRoot, file)} must use Core PageLayout instead of a raw page frame`,
|
||||
);
|
||||
assert.ok(
|
||||
!/<div\s+className="workspace(?:\s|\")/.test(value),
|
||||
`${path.relative(sourceRoot, file)} must use Core WorkspaceLayout instead of a raw workspace`,
|
||||
);
|
||||
}
|
||||
|
||||
for (const relativePath of [
|
||||
"features/campaigns/CampaignWorkspace.tsx",
|
||||
"features/campaigns/CampaignModulePage.tsx",
|
||||
"features/templates/TemplatesPage.tsx",
|
||||
]) {
|
||||
const value = fs.readFileSync(path.join(sourceRoot, relativePath), "utf8");
|
||||
assert.ok(value.includes("<WorkspaceLayout"), `${relativePath} must retain the shared workspace shell`);
|
||||
}
|
||||
|
||||
const nonSemanticClicks = sources.flatMap(({ file, value }) =>
|
||||
[...value.matchAll(/<(div|span|li|tr)\b[^>]*\bonClick\s*=/g)].map(
|
||||
(match) => `${path.relative(sourceRoot, file)}:${match.index}`,
|
||||
|
||||
Reference in New Issue
Block a user