Adopt shared WebUI layout primitives
This commit is contained in:
@@ -2,7 +2,7 @@ import { useEffect, useMemo, useState } from "react";
|
|||||||
import { Archive, CalendarClock, Copy, ExternalLink, LockKeyhole, LockOpen, Pause, Play, Trash2 } from "lucide-react";
|
import { Archive, CalendarClock, Copy, ExternalLink, LockKeyhole, LockOpen, Pause, Play, Trash2 } from "lucide-react";
|
||||||
import { Link } from "react-router";
|
import { Link } from "react-router";
|
||||||
import type { ApiSettings, AuthInfo } from "../../types";
|
import type { ApiSettings, AuthInfo } from "../../types";
|
||||||
import { Button } from "@govoplan/core-webui";
|
import { FormGrid, Button } from "@govoplan/core-webui";
|
||||||
import { Card } from "@govoplan/core-webui";
|
import { Card } from "@govoplan/core-webui";
|
||||||
import { ConfirmDialog } from "@govoplan/core-webui";
|
import { ConfirmDialog } from "@govoplan/core-webui";
|
||||||
import { Dialog } from "@govoplan/core-webui";
|
import { Dialog } from "@govoplan/core-webui";
|
||||||
@@ -389,7 +389,7 @@ export default function CampaignOverviewPage({ settings, auth, campaignId }: {se
|
|||||||
title="i18n:govoplan-campaign.campaign_identity.a00ca574"
|
title="i18n:govoplan-campaign.campaign_identity.a00ca574"
|
||||||
collapsible
|
collapsible
|
||||||
actions={<Link className="btn btn-secondary" to="wizard">i18n:govoplan-campaign.edit_with_wizard.672a7d1a</Link>}>
|
actions={<Link className="btn btn-secondary" to="wizard">i18n:govoplan-campaign.edit_with_wizard.672a7d1a</Link>}>
|
||||||
<div className="form-grid campaign-identity-grid">
|
<FormGrid columns={1} collapseAt="standard" className="campaign-identity-grid">
|
||||||
<FormField label="i18n:govoplan-campaign.campaign_id.4c4ed79e">
|
<FormField label="i18n:govoplan-campaign.campaign_id.4c4ed79e">
|
||||||
<input value={identity.external_id} onChange={(event) => patchIdentity("external_id", event.target.value)} />
|
<input value={identity.external_id} onChange={(event) => patchIdentity("external_id", event.target.value)} />
|
||||||
</FormField>
|
</FormField>
|
||||||
@@ -404,7 +404,7 @@ export default function CampaignOverviewPage({ settings, auth, campaignId }: {se
|
|||||||
<FormField label="i18n:govoplan-campaign.description.55f8ebc8">
|
<FormField label="i18n:govoplan-campaign.description.55f8ebc8">
|
||||||
<textarea rows={4} value={identity.description} onChange={(event) => patchIdentity("description", event.target.value)} />
|
<textarea rows={4} value={identity.description} onChange={(event) => patchIdentity("description", event.target.value)} />
|
||||||
</FormField>
|
</FormField>
|
||||||
</div>
|
</FormGrid>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
{(canSchedule || schedules.length > 0) && <Card
|
{(canSchedule || schedules.length > 0) && <Card
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import {
|
|||||||
type CampaignJobsResponse,
|
type CampaignJobsResponse,
|
||||||
type CampaignRetentionReport } from
|
type CampaignRetentionReport } from
|
||||||
"../../api/campaigns";
|
"../../api/campaigns";
|
||||||
import { Card } from "@govoplan/core-webui";
|
import { ContentGrid, Card } from "@govoplan/core-webui";
|
||||||
import { Button } from "@govoplan/core-webui";
|
import { Button } from "@govoplan/core-webui";
|
||||||
import { ConfirmDialog } from "@govoplan/core-webui";
|
import { ConfirmDialog } from "@govoplan/core-webui";
|
||||||
import { DataGrid, type DataGridColumn, type DataGridListOption, type DataGridQueryState } from "@govoplan/core-webui";
|
import { DataGrid, type DataGridColumn, type DataGridListOption, type DataGridQueryState } from "@govoplan/core-webui";
|
||||||
@@ -461,7 +461,7 @@ export default function CampaignReportPage({ settings, campaignId }: {settings:
|
|||||||
}
|
}
|
||||||
>
|
>
|
||||||
<LoadingFrame loading={loading} label="i18n:govoplan-campaign.loading_report_data.0908ade5">
|
<LoadingFrame loading={loading} label="i18n:govoplan-campaign.loading_report_data.0908ade5">
|
||||||
<div className="dashboard-grid">
|
<ContentGrid columns={2} collapseAt="workspace" className="">
|
||||||
<Card title="i18n:govoplan-campaign.delivery_outcome.f9d7c085">
|
<Card title="i18n:govoplan-campaign.delivery_outcome.f9d7c085">
|
||||||
<dl className="detail-list">
|
<dl className="detail-list">
|
||||||
<div><dt>i18n:govoplan-campaign.generated.8eefdd52</dt><dd>{formatDateTime(data.summary?.generated_at)}</dd></div>
|
<div><dt>i18n:govoplan-campaign.generated.8eefdd52</dt><dd>{formatDateTime(data.summary?.generated_at)}</dd></div>
|
||||||
@@ -536,7 +536,7 @@ export default function CampaignReportPage({ settings, campaignId }: {settings:
|
|||||||
<Button onClick={() => void runExplicitAction("unattempted")} disabled={!version || Boolean(busyAction)}>i18n:govoplan-campaign.send_unattempted_jobs.db7acc9f</Button>
|
<Button onClick={() => void runExplicitAction("unattempted")} disabled={!version || Boolean(busyAction)}>i18n:govoplan-campaign.send_unattempted_jobs.db7acc9f</Button>
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
</div>
|
</ContentGrid>
|
||||||
|
|
||||||
<Card title="i18n:govoplan-campaign.recipient_delivery_jobs.52492608">
|
<Card title="i18n:govoplan-campaign.recipient_delivery_jobs.52492608">
|
||||||
<p className="muted small-note">
|
<p className="muted small-note">
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import {
|
|||||||
type CampaignCalendarCatalog,
|
type CampaignCalendarCatalog,
|
||||||
type CampaignPostboxCatalog
|
type CampaignPostboxCatalog
|
||||||
} from "../../api/campaigns";
|
} from "../../api/campaigns";
|
||||||
import { Button } from "@govoplan/core-webui";
|
import { FormGrid, ContentGrid, Button } from "@govoplan/core-webui";
|
||||||
import { Card } from "@govoplan/core-webui";
|
import { Card } from "@govoplan/core-webui";
|
||||||
import { FormField } from "@govoplan/core-webui";
|
import { FormField } from "@govoplan/core-webui";
|
||||||
import { DismissibleAlert } from "@govoplan/core-webui";
|
import { DismissibleAlert } from "@govoplan/core-webui";
|
||||||
@@ -211,7 +211,7 @@ export default function GlobalSettingsPage({ settings, auth, campaignId, view =
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
<div className="dashboard-grid below-grid">
|
<ContentGrid columns={2} collapseAt="workspace" className="below-grid">
|
||||||
<Card title="i18n:govoplan-campaign.validation_policy.57dcc756" collapsible>
|
<Card title="i18n:govoplan-campaign.validation_policy.57dcc756" collapsible>
|
||||||
<PolicyTable className="campaign-policy-table" rowClassName="campaign-policy-row" headerClassName="campaign-policy-row-header" fieldLabel="i18n:govoplan-campaign.policy.bb9cf141" settingLabel="i18n:govoplan-campaign.setting.fb449f71" effectiveLabel="i18n:govoplan-campaign.effective_behavior.3daa11c2" showEffectiveColumn>
|
<PolicyTable className="campaign-policy-table" rowClassName="campaign-policy-row" headerClassName="campaign-policy-row-header" fieldLabel="i18n:govoplan-campaign.policy.bb9cf141" settingLabel="i18n:govoplan-campaign.setting.fb449f71" effectiveLabel="i18n:govoplan-campaign.effective_behavior.3daa11c2" showEffectiveColumn>
|
||||||
<PolicyRow
|
<PolicyRow
|
||||||
@@ -321,20 +321,20 @@ export default function GlobalSettingsPage({ settings, auth, campaignId, view =
|
|||||||
|
|
||||||
</PolicyTable>
|
</PolicyTable>
|
||||||
</Card>
|
</Card>
|
||||||
</div>
|
</ContentGrid>
|
||||||
</> :
|
</> :
|
||||||
|
|
||||||
<>
|
<>
|
||||||
{data.campaign && <CampaignAccessCard settings={settings} auth={auth} campaign={data.campaign} onChanged={reload} onError={setError} />}
|
{data.campaign && <CampaignAccessCard settings={settings} auth={auth} campaign={data.campaign} onChanged={reload} onError={setError} />}
|
||||||
|
|
||||||
<div className="dashboard-grid below-grid">
|
<ContentGrid columns={2} collapseAt="workspace" className="below-grid">
|
||||||
<Card title="i18n:govoplan-campaign.delivery_defaults.33cc3b97" collapsible>
|
<Card title="i18n:govoplan-campaign.delivery_defaults.33cc3b97" collapsible>
|
||||||
<div className="form-grid compact responsive-form-grid">
|
<FormGrid columns={2} collapseAt="wide" className="">
|
||||||
<FormField label="i18n:govoplan-campaign.messages_per_minute.bea49348"><input type="number" min={1} value={getNumber(rateLimit, "messages_per_minute", 5)} disabled={locked} onChange={(event) => patch(["delivery", "rate_limit", "messages_per_minute"], Number(event.target.value || 1))} /></FormField>
|
<FormField label="i18n:govoplan-campaign.messages_per_minute.bea49348"><input type="number" min={1} value={getNumber(rateLimit, "messages_per_minute", 5)} disabled={locked} onChange={(event) => patch(["delivery", "rate_limit", "messages_per_minute"], Number(event.target.value || 1))} /></FormField>
|
||||||
<FormField label="i18n:govoplan-campaign.concurrency.2ec390bf"><input type="number" min={1} value={getNumber(rateLimit, "concurrency", 1)} disabled={locked} onChange={(event) => patch(["delivery", "rate_limit", "concurrency"], Number(event.target.value || 1))} /></FormField>
|
<FormField label="i18n:govoplan-campaign.concurrency.2ec390bf"><input type="number" min={1} value={getNumber(rateLimit, "concurrency", 1)} disabled={locked} onChange={(event) => patch(["delivery", "rate_limit", "concurrency"], Number(event.target.value || 1))} /></FormField>
|
||||||
<FormField label="i18n:govoplan-campaign.max_attempts.f684fef4"><input type="number" min={1} value={getNumber(retry, "max_attempts", 3)} disabled={locked} onChange={(event) => patch(["delivery", "retry", "max_attempts"], Number(event.target.value || 1))} /></FormField>
|
<FormField label="i18n:govoplan-campaign.max_attempts.f684fef4"><input type="number" min={1} value={getNumber(retry, "max_attempts", 3)} disabled={locked} onChange={(event) => patch(["delivery", "retry", "max_attempts"], Number(event.target.value || 1))} /></FormField>
|
||||||
<ToggleSwitch label="i18n:govoplan-campaign.status_tracking.15f61f88" checked={getBool(statusTracking, "enabled", true)} disabled={locked} onChange={(checked) => patch(["status_tracking", "enabled"], checked)} />
|
<ToggleSwitch label="i18n:govoplan-campaign.status_tracking.15f61f88" checked={getBool(statusTracking, "enabled", true)} disabled={locked} onChange={(checked) => patch(["status_tracking", "enabled"], checked)} />
|
||||||
</div>
|
</FormGrid>
|
||||||
{postboxModuleInstalled &&
|
{postboxModuleInstalled &&
|
||||||
<div className="campaign-postbox-delivery-settings">
|
<div className="campaign-postbox-delivery-settings">
|
||||||
<FormField label="Delivery channels">
|
<FormField label="Delivery channels">
|
||||||
@@ -375,7 +375,7 @@ export default function GlobalSettingsPage({ settings, auth, campaignId, view =
|
|||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
<Card title="Calendar invitations" collapsible>
|
<Card title="Calendar invitations" collapsible>
|
||||||
<div className="form-grid compact responsive-form-grid">
|
<FormGrid columns={2} collapseAt="wide" className="">
|
||||||
<ToggleSwitch
|
<ToggleSwitch
|
||||||
label="Send individualized invitations"
|
label="Send individualized invitations"
|
||||||
checked={getBool(calendarInvitation, "enabled")}
|
checked={getBool(calendarInvitation, "enabled")}
|
||||||
@@ -454,7 +454,7 @@ export default function GlobalSettingsPage({ settings, auth, campaignId, view =
|
|||||||
onChange={(event) => patch(["delivery", "calendar_invitation", "description_template"], event.target.value)}
|
onChange={(event) => patch(["delivery", "calendar_invitation", "description_template"], event.target.value)}
|
||||||
/>
|
/>
|
||||||
</FormField>
|
</FormField>
|
||||||
</div>
|
</FormGrid>
|
||||||
{!mailModuleInstalled &&
|
{!mailModuleInstalled &&
|
||||||
<DismissibleAlert tone="warning" dismissible={false}>
|
<DismissibleAlert tone="warning" dismissible={false}>
|
||||||
Mail must be active to deliver iCalendar invitations.
|
Mail must be active to deliver iCalendar invitations.
|
||||||
@@ -483,7 +483,7 @@ export default function GlobalSettingsPage({ settings, auth, campaignId, view =
|
|||||||
</div>
|
</div>
|
||||||
<p className="muted small-note">i18n:govoplan-campaign.these_opt_ins_are_stored_in_the_draft_editor_met.dc6ffbfd</p>
|
<p className="muted small-note">i18n:govoplan-campaign.these_opt_ins_are_stored_in_the_draft_editor_met.dc6ffbfd</p>
|
||||||
</Card>
|
</Card>
|
||||||
</div>
|
</ContentGrid>
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
</LoadingFrame>
|
</LoadingFrame>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import {
|
import { FormGrid,
|
||||||
Button,
|
Button,
|
||||||
Card,
|
Card,
|
||||||
Dialog,
|
Dialog,
|
||||||
@@ -375,7 +375,7 @@ export default function MailSettingsPage({ settings, campaignId, view = "setting
|
|||||||
<Button variant="primary" onClick={openCredentialDialog} disabled={locked || profilesLoading || !selectedProfile || !selectedSmtpServer && !selectedImapServer}>Add campaign credential</Button>
|
<Button variant="primary" onClick={openCredentialDialog} disabled={locked || profilesLoading || !selectedProfile || !selectedSmtpServer && !selectedImapServer}>Add campaign credential</Button>
|
||||||
</div>}>
|
</div>}>
|
||||||
<p className="muted small-note">The campaign stores stable references to the envelope, selected servers, and credentials.</p>
|
<p className="muted small-note">The campaign stores stable references to the envelope, selected servers, and credentials.</p>
|
||||||
<div className="form-grid compact responsive-form-grid">
|
<FormGrid columns={2} collapseAt="wide" className="">
|
||||||
<FormField label="i18n:govoplan-campaign.profile.ff4fc027">
|
<FormField label="i18n:govoplan-campaign.profile.ff4fc027">
|
||||||
<select value={selectedProfileId} disabled={locked || profilesLoading} onChange={(event) => selectMailProfile(event.target.value)}>
|
<select value={selectedProfileId} disabled={locked || profilesLoading} onChange={(event) => selectMailProfile(event.target.value)}>
|
||||||
<option value="">i18n:govoplan-campaign.select_a_mail_profile.76480af0</option>
|
<option value="">i18n:govoplan-campaign.select_a_mail_profile.76480af0</option>
|
||||||
@@ -410,7 +410,7 @@ export default function MailSettingsPage({ settings, campaignId, view = "setting
|
|||||||
)}
|
)}
|
||||||
</select>
|
</select>
|
||||||
</FormField>
|
</FormField>
|
||||||
</div>
|
</FormGrid>
|
||||||
{selectedProfileUnavailable && <DismissibleAlert tone="warning" dismissible={false}>i18n:govoplan-campaign.the_referenced_mail_profile_is_inactive_unavaila.abeebe26</DismissibleAlert>}
|
{selectedProfileUnavailable && <DismissibleAlert tone="warning" dismissible={false}>i18n:govoplan-campaign.the_referenced_mail_profile_is_inactive_unavaila.abeebe26</DismissibleAlert>}
|
||||||
{selectedProfile && <div className="metric-grid inside">
|
{selectedProfile && <div className="metric-grid inside">
|
||||||
<MetricCard label="i18n:govoplan-campaign.profile.ff4fc027" value={selectedProfile.name} />
|
<MetricCard label="i18n:govoplan-campaign.profile.ff4fc027" value={selectedProfile.name} />
|
||||||
@@ -427,12 +427,12 @@ export default function MailSettingsPage({ settings, campaignId, view = "setting
|
|||||||
{profileError && <DismissibleAlert tone="warning" resetKey={profileError} floating>{profileError}</DismissibleAlert>}
|
{profileError && <DismissibleAlert tone="warning" resetKey={profileError} floating>{profileError}</DismissibleAlert>}
|
||||||
</Card>}
|
</Card>}
|
||||||
|
|
||||||
<Dialog
|
<Dialog variant="administration" size="wide"
|
||||||
open={credentialDialogOpen}
|
open={credentialDialogOpen}
|
||||||
title="Add campaign credential"
|
title="Add campaign credential"
|
||||||
onClose={() => setCredentialDialogOpen(false)}
|
onClose={() => setCredentialDialogOpen(false)}
|
||||||
closeDisabled={credentialSaving}
|
closeDisabled={credentialSaving}
|
||||||
className="admin-dialog admin-dialog-wide adaptive-config-dialog"
|
className="adaptive-config-dialog"
|
||||||
footerClassName="button-row compact-actions"
|
footerClassName="button-row compact-actions"
|
||||||
footer={
|
footer={
|
||||||
<>
|
<>
|
||||||
@@ -459,7 +459,7 @@ export default function MailSettingsPage({ settings, campaignId, view = "setting
|
|||||||
<h3>Campaign login</h3>
|
<h3>Campaign login</h3>
|
||||||
<p>This credential belongs to this campaign and can use only the selected mail servers.</p>
|
<p>This credential belongs to this campaign and can use only the selected mail servers.</p>
|
||||||
</header>
|
</header>
|
||||||
<div className="form-grid two">
|
<FormGrid columns={2} collapseAt="standard" className="">
|
||||||
<FormField label="Name">
|
<FormField label="Name">
|
||||||
<input value={credentialDraft.name} disabled={credentialSaving} onChange={(event) => setCredentialDraft({ ...credentialDraft, name: event.target.value })} autoFocus />
|
<input value={credentialDraft.name} disabled={credentialSaving} onChange={(event) => setCredentialDraft({ ...credentialDraft, name: event.target.value })} autoFocus />
|
||||||
</FormField>
|
</FormField>
|
||||||
@@ -469,17 +469,17 @@ export default function MailSettingsPage({ settings, campaignId, view = "setting
|
|||||||
<FormField label="Password">
|
<FormField label="Password">
|
||||||
<PasswordField value={credentialDraft.password} onValueChange={(password) => setCredentialDraft({ ...credentialDraft, password })} disabled={credentialSaving} generator autoComplete="new-password" />
|
<PasswordField value={credentialDraft.password} onValueChange={(password) => setCredentialDraft({ ...credentialDraft, password })} disabled={credentialSaving} generator autoComplete="new-password" />
|
||||||
</FormField>
|
</FormField>
|
||||||
</div>
|
</FormGrid>
|
||||||
<div className="form-grid two">
|
<FormGrid columns={2} collapseAt="standard" className="">
|
||||||
{selectedSmtpServer && <ToggleSwitch checked={credentialDraft.useSmtp} disabled={credentialSaving} onChange={(useSmtp) => setCredentialDraft({ ...credentialDraft, useSmtp })} label={`Use for SMTP: ${selectedSmtpServer.name}`} />}
|
{selectedSmtpServer && <ToggleSwitch checked={credentialDraft.useSmtp} disabled={credentialSaving} onChange={(useSmtp) => setCredentialDraft({ ...credentialDraft, useSmtp })} label={`Use for SMTP: ${selectedSmtpServer.name}`} />}
|
||||||
{selectedImapServer && <ToggleSwitch checked={credentialDraft.useImap} disabled={credentialSaving} onChange={(useImap) => setCredentialDraft({ ...credentialDraft, useImap })} label={`Use for IMAP: ${selectedImapServer.name}`} />}
|
{selectedImapServer && <ToggleSwitch checked={credentialDraft.useImap} disabled={credentialSaving} onChange={(useImap) => setCredentialDraft({ ...credentialDraft, useImap })} label={`Use for IMAP: ${selectedImapServer.name}`} />}
|
||||||
</div>
|
</FormGrid>
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
|
|
||||||
{!isPolicyView && <Card title="i18n:govoplan-campaign.imap_append.8c0d9e96" collapsible>
|
{!isPolicyView && <Card title="i18n:govoplan-campaign.imap_append.8c0d9e96" collapsible>
|
||||||
<div className="form-grid compact responsive-form-grid mail-server-form-grid">
|
<FormGrid columns={2} collapseAt="wide" className="mail-server-form-grid">
|
||||||
<div className="mail-server-field-span mail-server-toggle-row mail-server-plain-toggle-row">
|
<div className="mail-server-field-span mail-server-toggle-row mail-server-plain-toggle-row">
|
||||||
<ToggleSwitch
|
<ToggleSwitch
|
||||||
label="i18n:govoplan-campaign.append_successful_messages_to_sent_via_imap.dbd1b1d8"
|
label="i18n:govoplan-campaign.append_successful_messages_to_sent_via_imap.dbd1b1d8"
|
||||||
@@ -499,7 +499,7 @@ export default function MailSettingsPage({ settings, campaignId, view = "setting
|
|||||||
</div>
|
</div>
|
||||||
</FormField>
|
</FormField>
|
||||||
<MailServerFolderLookupResultView result={folderResult} disabled={locked || !imapAppendEnabled} onUseDetected={useDetectedSentFolder} floatingFailures />
|
<MailServerFolderLookupResultView result={folderResult} disabled={locked || !imapAppendEnabled} onUseDetected={useDetectedSentFolder} floatingFailures />
|
||||||
</div>
|
</FormGrid>
|
||||||
</Card>}
|
</Card>}
|
||||||
</>
|
</>
|
||||||
</LoadingFrame>
|
</LoadingFrame>
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ import {
|
|||||||
type CampaignSummary } from
|
type CampaignSummary } from
|
||||||
"../../api/campaigns";
|
"../../api/campaigns";
|
||||||
import { getMockMailboxMessage, type MockMailboxMessage } from "../../api/mail";
|
import { getMockMailboxMessage, type MockMailboxMessage } from "../../api/mail";
|
||||||
import { Button, Dialog, FormField, SegmentedControl, hasScope, useDeltaWatermarks, useGuardedNavigate, usePlatformUiCapability, type AuthInfo, type MailDevMailboxUiCapability } from "@govoplan/core-webui";
|
import { FormGrid, Button, Dialog, FormField, SegmentedControl, hasScope, useDeltaWatermarks, useGuardedNavigate, usePlatformUiCapability, type AuthInfo, type MailDevMailboxUiCapability } from "@govoplan/core-webui";
|
||||||
import { DataGrid, type DataGridQueryState } from "@govoplan/core-webui";
|
import { DataGrid, type DataGridQueryState } from "@govoplan/core-webui";
|
||||||
import { DismissibleAlert } from "@govoplan/core-webui";
|
import { DismissibleAlert } from "@govoplan/core-webui";
|
||||||
import { ConfirmDialog } from "@govoplan/core-webui";
|
import { ConfirmDialog } from "@govoplan/core-webui";
|
||||||
@@ -1899,7 +1899,7 @@ export default function ReviewSendPage({
|
|||||||
{busy === "approval" ? "Requesting..." : "Request approval"}
|
{busy === "approval" ? "Requesting..." : "Request approval"}
|
||||||
</Button>
|
</Button>
|
||||||
</>}>
|
</>}>
|
||||||
<div className="form-grid">
|
<FormGrid columns={1} collapseAt="standard" className="">
|
||||||
<p className="muted small-note form-grid-wide">The request covers the exact built message, attachment, recipient, policy, and transport snapshot. Rebuilding invalidates and removes this gate.</p>
|
<p className="muted small-note form-grid-wide">The request covers the exact built message, attachment, recipient, policy, and transport snapshot. Rebuilding invalidates and removes this gate.</p>
|
||||||
<FormField label="Approver type">
|
<FormField label="Approver type">
|
||||||
<select value={approvalSelectorKind} disabled={busy === "approval"} onChange={(event) => setApprovalSelectorKind(event.target.value as typeof approvalSelectorKind)}>
|
<select value={approvalSelectorKind} disabled={busy === "approval"} onChange={(event) => setApprovalSelectorKind(event.target.value as typeof approvalSelectorKind)}>
|
||||||
@@ -1933,7 +1933,7 @@ export default function ReviewSendPage({
|
|||||||
})} />)}
|
})} />)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</FormGrid>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
|
|
||||||
<ConfirmDialog
|
<ConfirmDialog
|
||||||
@@ -2029,7 +2029,7 @@ export default function ReviewSendPage({
|
|||||||
}
|
}
|
||||||
>
|
>
|
||||||
{singleSendConfirmRow && (
|
{singleSendConfirmRow && (
|
||||||
<div className="form-grid">
|
<FormGrid columns={1} collapseAt="standard" className="">
|
||||||
<SegmentedControl
|
<SegmentedControl
|
||||||
value={singleMessageActionKind}
|
value={singleMessageActionKind}
|
||||||
width="fill"
|
width="fill"
|
||||||
@@ -2089,7 +2089,7 @@ export default function ReviewSendPage({
|
|||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
)}
|
)}
|
||||||
</div>
|
</FormGrid>
|
||||||
)}
|
)}
|
||||||
</Dialog>
|
</Dialog>
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import {
|
|||||||
type CampaignContentLibraryTarget,
|
type CampaignContentLibraryTarget,
|
||||||
type CampaignPrintTemplate
|
type CampaignPrintTemplate
|
||||||
} from "../../api/campaigns";
|
} from "../../api/campaigns";
|
||||||
import { Button } from "@govoplan/core-webui";
|
import { FormGrid, ContentGrid, Button } from "@govoplan/core-webui";
|
||||||
import { Card } from "@govoplan/core-webui";
|
import { Card } from "@govoplan/core-webui";
|
||||||
import { Dialog } from "@govoplan/core-webui";
|
import { Dialog } from "@govoplan/core-webui";
|
||||||
import { FormField } from "@govoplan/core-webui";
|
import { FormField } from "@govoplan/core-webui";
|
||||||
@@ -433,9 +433,9 @@ export default function TemplateDataPage({ settings, campaignId }: {settings: Ap
|
|||||||
|
|
||||||
<LoadingFrame loading={loading || !draft} label="i18n:govoplan-campaign.loading_campaign_draft.1cf47e50">
|
<LoadingFrame loading={loading || !draft} label="i18n:govoplan-campaign.loading_campaign_draft.1cf47e50">
|
||||||
<>
|
<>
|
||||||
<div className="dashboard-grid template-editor-grid">
|
<ContentGrid columns={2} collapseAt="workspace" className="template-editor-grid">
|
||||||
<Card title="i18n:govoplan-campaign.editable_template.5e747a1e" actions={<Button onClick={() => setPreviewOpen(true)}>i18n:govoplan-campaign.preview.f1fbb2b4</Button>}>
|
<Card title="i18n:govoplan-campaign.editable_template.5e747a1e" actions={<Button onClick={() => setPreviewOpen(true)}>i18n:govoplan-campaign.preview.f1fbb2b4</Button>}>
|
||||||
<div className="form-grid">
|
<FormGrid columns={1} collapseAt="standard" className="">
|
||||||
<FormField label="i18n:govoplan-campaign.subject.8d183dbd">
|
<FormField label="i18n:govoplan-campaign.subject.8d183dbd">
|
||||||
<input
|
<input
|
||||||
ref={subjectRef}
|
ref={subjectRef}
|
||||||
@@ -506,13 +506,13 @@ export default function TemplateDataPage({ settings, campaignId }: {settings: Ap
|
|||||||
<Button onClick={() => setContentLibraryOpen(true)} disabled={locked}>i18n:govoplan-campaign.load_from_library.327ada7c</Button>
|
<Button onClick={() => setContentLibraryOpen(true)} disabled={locked}>i18n:govoplan-campaign.load_from_library.327ada7c</Button>
|
||||||
<Button onClick={openContentSaveDialog} disabled={locked}>i18n:govoplan-campaign.save_to_library.396649bf</Button>
|
<Button onClick={openContentSaveDialog} disabled={locked}>i18n:govoplan-campaign.save_to_library.396649bf</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</FormGrid>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
<div className="template-side-stack">
|
<div className="template-side-stack">
|
||||||
<Card title="Printable output">
|
<Card title="Printable output">
|
||||||
<LoadingFrame loading={printTemplatesLoading} label="Loading printable templates">
|
<LoadingFrame loading={printTemplatesLoading} label="Loading printable templates">
|
||||||
<div className="form-grid">
|
<FormGrid columns={1} collapseAt="standard" className="">
|
||||||
{printTemplatesError && <DismissibleAlert tone="danger" compact resetKey={printTemplatesError}>{printTemplatesError}</DismissibleAlert>}
|
{printTemplatesError && <DismissibleAlert tone="danger" compact resetKey={printTemplatesError}>{printTemplatesError}</DismissibleAlert>}
|
||||||
{!printTemplatesError && !printTemplatesAvailable && (
|
{!printTemplatesError && !printTemplatesAvailable && (
|
||||||
<DismissibleAlert tone="info" compact dismissible={false}>
|
<DismissibleAlert tone="info" compact dismissible={false}>
|
||||||
@@ -565,7 +565,7 @@ export default function TemplateDataPage({ settings, campaignId }: {settings: Ap
|
|||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</FormGrid>
|
||||||
</LoadingFrame>
|
</LoadingFrame>
|
||||||
</Card>
|
</Card>
|
||||||
<Card title="i18n:govoplan-campaign.fields.e8b68527">
|
<Card title="i18n:govoplan-campaign.fields.e8b68527">
|
||||||
@@ -598,7 +598,7 @@ export default function TemplateDataPage({ settings, campaignId }: {settings: Ap
|
|||||||
<UndefinedPlaceholderList items={undefinedPlaceholders} onSelect={setUndefinedDialog} />
|
<UndefinedPlaceholderList items={undefinedPlaceholders} onSelect={setUndefinedDialog} />
|
||||||
</Card>
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</ContentGrid>
|
||||||
|
|
||||||
</>
|
</>
|
||||||
</LoadingFrame>
|
</LoadingFrame>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { useEffect, useRef, type ReactNode } from "react";
|
import { useEffect, useRef, type ReactNode } from "react";
|
||||||
import { ArrowBigLeft, ArrowBigLeftDash, ArrowBigRight, ArrowBigRightDash } from "lucide-react";
|
import { ArrowBigLeft, ArrowBigLeftDash, ArrowBigRight, ArrowBigRightDash } from "lucide-react";
|
||||||
import { Button, Dialog, MessageDisplayPanel, type MessageDisplayAttachment } from "@govoplan/core-webui";
|
import { ActionToolbar, Button, Dialog, MessageDisplayPanel, type MessageDisplayAttachment } from "@govoplan/core-webui";
|
||||||
|
|
||||||
// Campaign review/template/mock previews need recipient navigation, review notes,
|
// Campaign review/template/mock previews need recipient navigation, review notes,
|
||||||
// raw MIME inspection and attachment grouping. Generic mailbox reading uses
|
// raw MIME inspection and attachment grouping. Generic mailbox reading uses
|
||||||
@@ -106,7 +106,7 @@ export default function CampaignMessagePreviewOverlay({
|
|||||||
>
|
>
|
||||||
<div ref={contentRef} className="message-preview-content">
|
<div ref={contentRef} className="message-preview-content">
|
||||||
{(recipientLabel || recipientNote || navigation) &&
|
{(recipientLabel || recipientNote || navigation) &&
|
||||||
<div className="template-preview-toolbar">
|
<ActionToolbar className="template-preview-toolbar">
|
||||||
<div>
|
<div>
|
||||||
{recipientLabel && <strong>{recipientLabel}</strong>}
|
{recipientLabel && <strong>{recipientLabel}</strong>}
|
||||||
{recipientNote && <p className="muted small-note">{recipientNote}</p>}
|
{recipientNote && <p className="muted small-note">{recipientNote}</p>}
|
||||||
@@ -120,7 +120,7 @@ export default function CampaignMessagePreviewOverlay({
|
|||||||
<button type="button" className="version-arrow" onClick={navigation.onLast} disabled={navigation.index >= navigation.total - 1} title="i18n:govoplan-campaign.last_message.83741110" aria-label="i18n:govoplan-campaign.last_message.83741110"><ArrowBigRightDash aria-hidden="true" /></button>
|
<button type="button" className="version-arrow" onClick={navigation.onLast} disabled={navigation.index >= navigation.total - 1} title="i18n:govoplan-campaign.last_message.83741110" aria-label="i18n:govoplan-campaign.last_message.83741110"><ArrowBigRightDash aria-hidden="true" /></button>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
</div>
|
</ActionToolbar>
|
||||||
}
|
}
|
||||||
|
|
||||||
<MessageDisplayPanel
|
<MessageDisplayPanel
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { useMemo, useState } from "react";
|
import { useMemo, useState } from "react";
|
||||||
import { Button, Dialog, DismissibleAlert, FormField, TableActionGroup, ToggleSwitch } from "@govoplan/core-webui";
|
import { FormGrid, ActionToolbar, Button, Dialog, DismissibleAlert, FormField, TableActionGroup, ToggleSwitch } from "@govoplan/core-webui";
|
||||||
import { ArrowDown, ArrowUp, Plus, Trash2 } from "lucide-react";
|
import { ArrowDown, ArrowUp, Plus, Trash2 } from "lucide-react";
|
||||||
import type {
|
import type {
|
||||||
CampaignPostboxCatalog,
|
CampaignPostboxCatalog,
|
||||||
@@ -101,7 +101,7 @@ export default function PostboxTargetsDialog({
|
|||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<div className="campaign-postbox-target-toolbar">
|
<ActionToolbar className="campaign-postbox-target-toolbar">
|
||||||
{showMerge &&
|
{showMerge &&
|
||||||
<ToggleSwitch
|
<ToggleSwitch
|
||||||
label="Postbox target defaults"
|
label="Postbox target defaults"
|
||||||
@@ -121,7 +121,7 @@ export default function PostboxTargetsDialog({
|
|||||||
<Plus aria-hidden="true" />
|
<Plus aria-hidden="true" />
|
||||||
Add target
|
Add target
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</ActionToolbar>
|
||||||
|
|
||||||
{error && <DismissibleAlert tone="danger" dismissible={false}>{error}</DismissibleAlert>}
|
{error && <DismissibleAlert tone="danger" dismissible={false}>{error}</DismissibleAlert>}
|
||||||
{draftTargets.length === 0 &&
|
{draftTargets.length === 0 &&
|
||||||
@@ -181,7 +181,7 @@ function PostboxTargetRow({
|
|||||||
{ id: "remove", label: "Remove target", icon: <Trash2 aria-hidden="true" />, disabled: locked, variant: "danger", onClick: onRemove }
|
{ id: "remove", label: "Remove target", icon: <Trash2 aria-hidden="true" />, disabled: locked, variant: "danger", onClick: onRemove }
|
||||||
]} />
|
]} />
|
||||||
</div>
|
</div>
|
||||||
<div className="form-grid compact responsive-form-grid campaign-postbox-target-grid">
|
<FormGrid columns={2} collapseAt="wide" className="campaign-postbox-target-grid">
|
||||||
<FormField label="Target type">
|
<FormField label="Target type">
|
||||||
<select
|
<select
|
||||||
value={target.mode}
|
value={target.mode}
|
||||||
@@ -219,7 +219,7 @@ function PostboxTargetRow({
|
|||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
</div>
|
</FormGrid>
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { Button } from "@govoplan/core-webui";
|
import { FormGrid, Button } from "@govoplan/core-webui";
|
||||||
import { FieldLabel } from "@govoplan/core-webui";
|
import { FieldLabel } from "@govoplan/core-webui";
|
||||||
import { FormField } from "@govoplan/core-webui";
|
import { FormField } from "@govoplan/core-webui";
|
||||||
import { ToggleSwitch } from "@govoplan/core-webui";
|
import { ToggleSwitch } from "@govoplan/core-webui";
|
||||||
@@ -22,7 +22,7 @@ export type WizardStepProps = {
|
|||||||
export function BasicsStep({ draft, patch }: WizardStepProps) {
|
export function BasicsStep({ draft, patch }: WizardStepProps) {
|
||||||
const campaign = asRecord(draft.campaign);
|
const campaign = asRecord(draft.campaign);
|
||||||
return (
|
return (
|
||||||
<div className="form-grid">
|
<FormGrid columns={1} collapseAt="standard" className="">
|
||||||
<FormField label="i18n:govoplan-campaign.campaign_name.aa5d0e72" help="i18n:govoplan-campaign.a_human_readable_name_shown_in_lists_and_reports.afc23e7e">
|
<FormField label="i18n:govoplan-campaign.campaign_name.aa5d0e72" help="i18n:govoplan-campaign.a_human_readable_name_shown_in_lists_and_reports.afc23e7e">
|
||||||
<input value={getText(campaign, "name")} onChange={(event) => patch(["campaign", "name"], event.target.value)} />
|
<input value={getText(campaign, "name")} onChange={(event) => patch(["campaign", "name"], event.target.value)} />
|
||||||
</FormField>
|
</FormField>
|
||||||
@@ -39,7 +39,7 @@ export function BasicsStep({ draft, patch }: WizardStepProps) {
|
|||||||
<FormField label="i18n:govoplan-campaign.description.55f8ebc8">
|
<FormField label="i18n:govoplan-campaign.description.55f8ebc8">
|
||||||
<textarea rows={5} value={getText(campaign, "description")} onChange={(event) => patch(["campaign", "description"], event.target.value)} />
|
<textarea rows={5} value={getText(campaign, "description")} onChange={(event) => patch(["campaign", "description"], event.target.value)} />
|
||||||
</FormField>
|
</FormField>
|
||||||
</div>);
|
</FormGrid>);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,7 +54,7 @@ export function SenderStep({ draft, patch }: WizardStepProps) {
|
|||||||
const delivery = asRecord(draft.delivery);
|
const delivery = asRecord(draft.delivery);
|
||||||
const imapAppend = asRecord(delivery.imap_append_sent);
|
const imapAppend = asRecord(delivery.imap_append_sent);
|
||||||
return (
|
return (
|
||||||
<div className="form-grid">
|
<FormGrid columns={1} collapseAt="standard" className="">
|
||||||
<FormField label="i18n:govoplan-campaign.default_from_address.b9ee6d77">
|
<FormField label="i18n:govoplan-campaign.default_from_address.b9ee6d77">
|
||||||
<EmailAddressInput
|
<EmailAddressInput
|
||||||
value={from}
|
value={from}
|
||||||
@@ -108,7 +108,7 @@ export function SenderStep({ draft, patch }: WizardStepProps) {
|
|||||||
</FormField>
|
</FormField>
|
||||||
<p className="muted small-note">i18n:govoplan-campaign.select_the_delivery_profile_on_the_mail_settings.a89cbb5f</p>
|
<p className="muted small-note">i18n:govoplan-campaign.select_the_delivery_profile_on_the_mail_settings.a89cbb5f</p>
|
||||||
<ToggleSwitch label="i18n:govoplan-campaign.append_successful_messages_to_sent_via_imap.dbd1b1d8" checked={getBool(imapAppend, "enabled")} onChange={(checked) => patch(["delivery", "imap_append_sent", "enabled"], checked)} />
|
<ToggleSwitch label="i18n:govoplan-campaign.append_successful_messages_to_sent_via_imap.dbd1b1d8" checked={getBool(imapAppend, "enabled")} onChange={(checked) => patch(["delivery", "imap_append_sent", "enabled"], checked)} />
|
||||||
</div>);
|
</FormGrid>);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -144,7 +144,7 @@ export function TemplateStep({ draft, patch }: WizardStepProps) {
|
|||||||
<h2>i18n:govoplan-campaign.template.3ec1ae06</h2>
|
<h2>i18n:govoplan-campaign.template.3ec1ae06</h2>
|
||||||
<p>i18n:govoplan-campaign.compose_the_subject_and_body_merge_fields_can_la.4fb6e97b</p>
|
<p>i18n:govoplan-campaign.compose_the_subject_and_body_merge_fields_can_la.4fb6e97b</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="form-grid">
|
<FormGrid columns={1} collapseAt="standard" className="">
|
||||||
<FormField label="i18n:govoplan-campaign.subject.8d183dbd"><input value={getText(template, "subject")} onChange={(event) => patch(["template", "subject"], event.target.value)} /></FormField>
|
<FormField label="i18n:govoplan-campaign.subject.8d183dbd"><input value={getText(template, "subject")} onChange={(event) => patch(["template", "subject"], event.target.value)} /></FormField>
|
||||||
<FormField label="i18n:govoplan-campaign.plain_text_body.030a0da0"><textarea rows={12} value={getText(template, "text")} onChange={(event) => patch(["template", "text"], event.target.value)} /></FormField>
|
<FormField label="i18n:govoplan-campaign.plain_text_body.030a0da0"><textarea rows={12} value={getText(template, "text")} onChange={(event) => patch(["template", "text"], event.target.value)} /></FormField>
|
||||||
<div className="form-field">
|
<div className="form-field">
|
||||||
@@ -157,7 +157,7 @@ export function TemplateStep({ draft, patch }: WizardStepProps) {
|
|||||||
onChange={(value) => patch(["template", "html"], value)}
|
onChange={(value) => patch(["template", "html"], value)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</FormGrid>
|
||||||
</div>);
|
</div>);
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -170,12 +170,12 @@ export function AttachmentsStep({ draft, patch }: WizardStepProps) {
|
|||||||
<h2>i18n:govoplan-campaign.attachments.6771ade6</h2>
|
<h2>i18n:govoplan-campaign.attachments.6771ade6</h2>
|
||||||
<p>i18n:govoplan-campaign.configure_campaign_wide_attachment_behavior_and_.441a6cd2</p>
|
<p>i18n:govoplan-campaign.configure_campaign_wide_attachment_behavior_and_.441a6cd2</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="form-grid compact responsive-form-grid">
|
<FormGrid columns={2} collapseAt="wide" className="">
|
||||||
<FormField label="i18n:govoplan-campaign.campaign_attachment_base_path.84827619"><input value={getText(attachments, "base_path", ".")} onChange={(event) => patch(["attachments", "base_path"], event.target.value)} /></FormField>
|
<FormField label="i18n:govoplan-campaign.campaign_attachment_base_path.84827619"><input value={getText(attachments, "base_path", ".")} onChange={(event) => patch(["attachments", "base_path"], event.target.value)} /></FormField>
|
||||||
<FormField label="i18n:govoplan-campaign.missing_behavior.de3d1ac7"><select value={getText(attachments, "missing_behavior", "warn")} onChange={(event) => patch(["attachments", "missing_behavior"], event.target.value)}>{behaviorOptions.map((option) => <option key={option}>{option}</option>)}</select></FormField>
|
<FormField label="i18n:govoplan-campaign.missing_behavior.de3d1ac7"><select value={getText(attachments, "missing_behavior", "warn")} onChange={(event) => patch(["attachments", "missing_behavior"], event.target.value)}>{behaviorOptions.map((option) => <option key={option}>{option}</option>)}</select></FormField>
|
||||||
<FormField label="i18n:govoplan-campaign.ambiguous_behavior.e86e724b"><select value={getText(attachments, "ambiguous_behavior", "ask")} onChange={(event) => patch(["attachments", "ambiguous_behavior"], event.target.value)}>{behaviorOptions.map((option) => <option key={option}>{option}</option>)}</select></FormField>
|
<FormField label="i18n:govoplan-campaign.ambiguous_behavior.e86e724b"><select value={getText(attachments, "ambiguous_behavior", "ask")} onChange={(event) => patch(["attachments", "ambiguous_behavior"], event.target.value)}>{behaviorOptions.map((option) => <option key={option}>{option}</option>)}</select></FormField>
|
||||||
<ToggleSwitch label="i18n:govoplan-campaign.allow_individual_attachments.a6ff0e87" checked={getBool(attachments, "allow_individual")} onChange={(checked) => patch(["attachments", "allow_individual"], checked)} />
|
<ToggleSwitch label="i18n:govoplan-campaign.allow_individual_attachments.a6ff0e87" checked={getBool(attachments, "allow_individual")} onChange={(checked) => patch(["attachments", "allow_individual"], checked)} />
|
||||||
</div>
|
</FormGrid>
|
||||||
<JsonEditor value={attachments.global ?? []} onValid={(value) => patch(["attachments", "global"], value)} />
|
<JsonEditor value={attachments.global ?? []} onValid={(value) => patch(["attachments", "global"], value)} />
|
||||||
</div>);
|
</div>);
|
||||||
|
|
||||||
@@ -208,10 +208,10 @@ export function SendStep({ draft, patch }: WizardStepProps) {
|
|||||||
<h2>i18n:govoplan-campaign.send_preparation.6e078f43</h2>
|
<h2>i18n:govoplan-campaign.send_preparation.6e078f43</h2>
|
||||||
<p>i18n:govoplan-campaign.configure_rate_limits_and_prepare_the_final_send.af3d4e48</p>
|
<p>i18n:govoplan-campaign.configure_rate_limits_and_prepare_the_final_send.af3d4e48</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="form-grid compact responsive-form-grid">
|
<FormGrid columns={2} collapseAt="wide" className="">
|
||||||
<FormField label="i18n:govoplan-campaign.messages_per_minute.bea49348"><input type="number" min={1} value={getNumber(rateLimit, "messages_per_minute", 5)} onChange={(event) => patch(["delivery", "rate_limit", "messages_per_minute"], Number(event.target.value || 1))} /></FormField>
|
<FormField label="i18n:govoplan-campaign.messages_per_minute.bea49348"><input type="number" min={1} value={getNumber(rateLimit, "messages_per_minute", 5)} onChange={(event) => patch(["delivery", "rate_limit", "messages_per_minute"], Number(event.target.value || 1))} /></FormField>
|
||||||
<FormField label="i18n:govoplan-campaign.concurrency.2ec390bf"><input type="number" min={1} value={getNumber(rateLimit, "concurrency", 1)} onChange={(event) => patch(["delivery", "rate_limit", "concurrency"], Number(event.target.value || 1))} /></FormField>
|
<FormField label="i18n:govoplan-campaign.concurrency.2ec390bf"><input type="number" min={1} value={getNumber(rateLimit, "concurrency", 1)} onChange={(event) => patch(["delivery", "rate_limit", "concurrency"], Number(event.target.value || 1))} /></FormField>
|
||||||
</div>
|
</FormGrid>
|
||||||
<p className="muted">i18n:govoplan-campaign.test_send_and_queue_actions_remain_in_the_send_w.60dee716</p>
|
<p className="muted">i18n:govoplan-campaign.test_send_and_queue_actions_remain_in_the_send_w.60dee716</p>
|
||||||
</div>);
|
</div>);
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||||
import { Eye, RefreshCw } from "lucide-react";
|
import { Eye, RefreshCw } from "lucide-react";
|
||||||
import { useSearchParams } from "react-router";
|
import { useSearchParams } from "react-router";
|
||||||
import {
|
import { ContentGrid,
|
||||||
Button,
|
Button,
|
||||||
Card,
|
Card,
|
||||||
DataGrid,
|
DataGrid,
|
||||||
@@ -208,7 +208,7 @@ export default function AggregateReportsPage({ settings }: {settings: ApiSetting
|
|||||||
|
|
||||||
{/* Aggregate outcome cards are deliberately not filter shortcuts: each subgroup
|
{/* Aggregate outcome cards are deliberately not filter shortcuts: each subgroup
|
||||||
would require a separately suppressed aggregate response from the server. */}
|
would require a separately suppressed aggregate response from the server. */}
|
||||||
<div className="dashboard-grid">
|
<ContentGrid columns={2} collapseAt="workspace" className="">
|
||||||
<MetricCard label="i18n:govoplan-campaign.smtp_accepted.e3aa7603" value={countValue(outcomes.smtp_accepted)} tone="good" />
|
<MetricCard label="i18n:govoplan-campaign.smtp_accepted.e3aa7603" value={countValue(outcomes.smtp_accepted)} tone="good" />
|
||||||
<MetricCard label="Postbox accepted" value={countValue(outcomes.postbox_accepted)} tone="good" />
|
<MetricCard label="Postbox accepted" value={countValue(outcomes.postbox_accepted)} tone="good" />
|
||||||
<MetricCard label="Print accepted" value={countValue(outcomes.print_accepted)} tone="good" />
|
<MetricCard label="Print accepted" value={countValue(outcomes.print_accepted)} tone="good" />
|
||||||
@@ -220,7 +220,7 @@ export default function AggregateReportsPage({ settings }: {settings: ApiSetting
|
|||||||
<MetricCard label="i18n:govoplan-campaign.not_attempted.e1be3c69" value={countValue(outcomes.not_attempted)} />
|
<MetricCard label="i18n:govoplan-campaign.not_attempted.e1be3c69" value={countValue(outcomes.not_attempted)} />
|
||||||
<MetricCard label="i18n:govoplan-campaign.cancelled.a1bf92ef" value={countValue(outcomes.cancelled)} />
|
<MetricCard label="i18n:govoplan-campaign.cancelled.a1bf92ef" value={countValue(outcomes.cancelled)} />
|
||||||
<MetricCard label="i18n:govoplan-campaign.excluded.9804952b" value={countValue(outcomes.excluded)} />
|
<MetricCard label="i18n:govoplan-campaign.excluded.9804952b" value={countValue(outcomes.excluded)} />
|
||||||
</div>
|
</ContentGrid>
|
||||||
|
|
||||||
<Card title="i18n:govoplan-campaign.population_and_exclusions.cdff62ad">
|
<Card title="i18n:govoplan-campaign.population_and_exclusions.cdff62ad">
|
||||||
<dl className="detail-list">
|
<dl className="detail-list">
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { useMemo, useState } from "react";
|
import { useMemo, useState } from "react";
|
||||||
import { ExternalLink } from "lucide-react";
|
import { ExternalLink } from "lucide-react";
|
||||||
import { Button } from "@govoplan/core-webui";
|
import { FormGrid, ContentGrid, Button } from "@govoplan/core-webui";
|
||||||
import { Card } from "@govoplan/core-webui";
|
import { Card } from "@govoplan/core-webui";
|
||||||
import { PageLayout, WorkspaceLayout } from "@govoplan/core-webui";
|
import { PageLayout, WorkspaceLayout } from "@govoplan/core-webui";
|
||||||
import { FieldLabel } from "@govoplan/core-webui";
|
import { FieldLabel } from "@govoplan/core-webui";
|
||||||
@@ -204,7 +204,7 @@ export default function TemplatesPage() {
|
|||||||
|
|
||||||
function TemplateOverview({ template }: {template: TemplateRecord;}) {
|
function TemplateOverview({ template }: {template: TemplateRecord;}) {
|
||||||
return (
|
return (
|
||||||
<div className="dashboard-grid settings-dashboard-grid">
|
<ContentGrid columns={2} collapseAt="workspace" className="">
|
||||||
<Card title="i18n:govoplan-campaign.template_status.8bec97cf">
|
<Card title="i18n:govoplan-campaign.template_status.8bec97cf">
|
||||||
<dl className="detail-list compact-detail-list">
|
<dl className="detail-list compact-detail-list">
|
||||||
<div><dt>i18n:govoplan-campaign.status.bae7d5be</dt><dd><StatusBadge status={template.status} /></dd></div>
|
<div><dt>i18n:govoplan-campaign.status.bae7d5be</dt><dd><StatusBadge status={template.status} /></dd></div>
|
||||||
@@ -221,7 +221,7 @@ function TemplateOverview({ template }: {template: TemplateRecord;}) {
|
|||||||
<span>i18n:govoplan-campaign.a_mapping_wizard_can_be_added_later.6b2fe0f3</span>
|
<span>i18n:govoplan-campaign.a_mapping_wizard_can_be_added_later.6b2fe0f3</span>
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
</div>);
|
</ContentGrid>);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -231,10 +231,10 @@ function TemplateContent({ template }: {template: TemplateRecord;}) {
|
|||||||
const body = translateText(template.body);
|
const body = translateText(template.body);
|
||||||
return (
|
return (
|
||||||
<Card title="i18n:govoplan-campaign.template_content.48630575" actions={<Button disabled>i18n:govoplan-campaign.save_changes.179359b3</Button>}>
|
<Card title="i18n:govoplan-campaign.template_content.48630575" actions={<Button disabled>i18n:govoplan-campaign.save_changes.179359b3</Button>}>
|
||||||
<div className="form-grid compact responsive-form-grid">
|
<FormGrid columns={2} collapseAt="wide" className="">
|
||||||
<label className="form-field"><FieldLabel className="form-label" help="i18n:govoplan-campaign.read_only_subject_stored_in_this_reusable_templa.549ae246">i18n:govoplan-campaign.subject.8d183dbd</FieldLabel><input value={subject} readOnly /></label>
|
<label className="form-field"><FieldLabel className="form-label" help="i18n:govoplan-campaign.read_only_subject_stored_in_this_reusable_templa.549ae246">i18n:govoplan-campaign.subject.8d183dbd</FieldLabel><input value={subject} readOnly /></label>
|
||||||
<label className="form-field full-span"><FieldLabel className="form-label" help="i18n:govoplan-campaign.read_only_body_stored_in_this_reusable_template_.98fbf178">i18n:govoplan-campaign.body.718a7e8a</FieldLabel><textarea rows={10} value={body} readOnly /></label>
|
<label className="form-field full-span"><FieldLabel className="form-label" help="i18n:govoplan-campaign.read_only_body_stored_in_this_reusable_template_.98fbf178">i18n:govoplan-campaign.body.718a7e8a</FieldLabel><textarea rows={10} value={body} readOnly /></label>
|
||||||
</div>
|
</FormGrid>
|
||||||
</Card>);
|
</Card>);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user