fix(webui): bind consequential campaign controls to help
Module Package Release / publish-packages (push) Successful in 12s
Module Package Release / publish-packages (push) Successful in 12s
This commit is contained in:
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@govoplan/campaign-webui",
|
"name": "@govoplan/campaign-webui",
|
||||||
"version": "0.1.26",
|
"version": "0.1.27",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "webui/src/index.ts",
|
"main": "webui/src/index.ts",
|
||||||
|
|||||||
+1
-1
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "govoplan-campaign"
|
name = "govoplan-campaign"
|
||||||
version = "0.1.26"
|
version = "0.1.27"
|
||||||
description = "GovOPlaN campaigns module with backend and WebUI integration."
|
description = "GovOPlaN campaigns module with backend and WebUI integration."
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
requires-python = ">=3.12"
|
requires-python = ">=3.12"
|
||||||
|
|||||||
@@ -463,8 +463,8 @@ def _campaigns_router(context: ModuleContext):
|
|||||||
manifest = ModuleManifest(
|
manifest = ModuleManifest(
|
||||||
id="campaigns",
|
id="campaigns",
|
||||||
name="Campaigns",
|
name="Campaigns",
|
||||||
version="0.1.26",
|
version="0.1.27",
|
||||||
workflow_definitions=campaign_workflow_definitions(module_version="0.1.26"),
|
workflow_definitions=campaign_workflow_definitions(module_version="0.1.27"),
|
||||||
required_capabilities=(
|
required_capabilities=(
|
||||||
CAPABILITY_AUTH_PRINCIPAL_RESOLVER,
|
CAPABILITY_AUTH_PRINCIPAL_RESOLVER,
|
||||||
CAPABILITY_AUTH_PERMISSION_EVALUATOR,
|
CAPABILITY_AUTH_PERMISSION_EVALUATOR,
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@govoplan/campaign-webui",
|
"name": "@govoplan/campaign-webui",
|
||||||
"version": "0.1.26",
|
"version": "0.1.27",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "src/index.ts",
|
"main": "src/index.ts",
|
||||||
|
|||||||
@@ -615,6 +615,8 @@ function zipArchiveColumns({ disabled, archives, invalidNameIndexes, onEditName,
|
|||||||
value={archive.method}
|
value={archive.method}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
aria-label="Archive password encryption"
|
aria-label="Archive password encryption"
|
||||||
|
data-help-context-id="campaign.archive-encryption"
|
||||||
|
data-help-module-id="campaigns"
|
||||||
onChange={(event) => {
|
onChange={(event) => {
|
||||||
const method = event.target.value === "zip_standard" ? "zip_standard" : "aes";
|
const method = event.target.value === "zip_standard" ? "zip_standard" : "aes";
|
||||||
patchArchive(index, method === "zip_standard" ? {
|
patchArchive(index, method === "zip_standard" ? {
|
||||||
@@ -640,6 +642,8 @@ function zipArchiveColumns({ disabled, archives, invalidNameIndexes, onEditName,
|
|||||||
value={archive.password_delivery_channel}
|
value={archive.password_delivery_channel}
|
||||||
disabled={disabled || !archive.password_enabled}
|
disabled={disabled || !archive.password_enabled}
|
||||||
aria-label="Separate password-delivery channel"
|
aria-label="Separate password-delivery channel"
|
||||||
|
data-help-context-id="campaign.archive-encryption"
|
||||||
|
data-help-module-id="campaigns"
|
||||||
onChange={(event) => patchArchive(index, { password_delivery_channel: event.target.value as AttachmentZipArchive["password_delivery_channel"] })}>
|
onChange={(event) => patchArchive(index, { password_delivery_channel: event.target.value as AttachmentZipArchive["password_delivery_channel"] })}>
|
||||||
{(["separate_mail", "sms", "letter", "phone", "in_person"] as const).map((channel) =>
|
{(["separate_mail", "sms", "letter", "phone", "in_person"] as const).map((channel) =>
|
||||||
<option key={channel} value={channel} disabled={!allowedDeliveryChannels.includes(channel)}>{passwordDeliveryChannelLabel(channel)}</option>
|
<option key={channel} value={channel} disabled={!allowedDeliveryChannels.includes(channel)}>{passwordDeliveryChannelLabel(channel)}</option>
|
||||||
@@ -653,6 +657,8 @@ function zipArchiveColumns({ disabled, archives, invalidNameIndexes, onEditName,
|
|||||||
<div className="campaign-legacy-zipcrypto-acknowledgement">
|
<div className="campaign-legacy-zipcrypto-acknowledgement">
|
||||||
<ToggleSwitch
|
<ToggleSwitch
|
||||||
label="I acknowledge that ZipCrypto encryption is weak"
|
label="I acknowledge that ZipCrypto encryption is weak"
|
||||||
|
helpContextId="campaign.archive-encryption"
|
||||||
|
helpModuleId="campaigns"
|
||||||
checked={archive.legacy_zipcrypto_acknowledged}
|
checked={archive.legacy_zipcrypto_acknowledged}
|
||||||
disabled={disabled || !legacyAllowed || !canUseLegacy}
|
disabled={disabled || !legacyAllowed || !canUseLegacy}
|
||||||
onChange={(checked) => patchArchive(index, { legacy_zipcrypto_acknowledged: checked })} />
|
onChange={(checked) => patchArchive(index, { legacy_zipcrypto_acknowledged: checked })} />
|
||||||
@@ -663,6 +669,8 @@ function zipArchiveColumns({ disabled, archives, invalidNameIndexes, onEditName,
|
|||||||
maxLength={1000}
|
maxLength={1000}
|
||||||
placeholder="Operational reason (at least 10 characters)"
|
placeholder="Operational reason (at least 10 characters)"
|
||||||
aria-label="Reason for weak legacy encryption"
|
aria-label="Reason for weak legacy encryption"
|
||||||
|
data-help-context-id="campaign.archive-encryption"
|
||||||
|
data-help-module-id="campaigns"
|
||||||
onChange={(event) => patchArchive(index, { legacy_zipcrypto_reason: event.target.value })} />
|
onChange={(event) => patchArchive(index, { legacy_zipcrypto_reason: event.target.value })} />
|
||||||
</div> : <span>Not required for AES</span>,
|
</div> : <span>Not required for AES</span>,
|
||||||
value: (archive) => archive.legacy_zipcrypto_reason
|
value: (archive) => archive.legacy_zipcrypto_reason
|
||||||
|
|||||||
@@ -433,6 +433,8 @@ export default function CampaignOverviewPage({ settings, auth, campaignId }: {se
|
|||||||
destructiveActions={<>
|
destructiveActions={<>
|
||||||
{canDelete && <Button
|
{canDelete && <Button
|
||||||
variant="danger"
|
variant="danger"
|
||||||
|
helpContextId="campaign.overview"
|
||||||
|
helpModuleId="campaigns"
|
||||||
onClick={() => void prepareLifecycleAction("delete_campaign")}
|
onClick={() => void prepareLifecycleAction("delete_campaign")}
|
||||||
disabled={loading || savingIdentity || lockBusy || lifecycleBusy || identityDirty}
|
disabled={loading || savingIdentity || lockBusy || lifecycleBusy || identityDirty}
|
||||||
disabledReason={identityDirty ? "Save or discard overview changes before deleting." : undefined}>
|
disabledReason={identityDirty ? "Save or discard overview changes before deleting." : undefined}>
|
||||||
|
|||||||
@@ -537,7 +537,7 @@ export default function CampaignReportPage({ settings, campaignId }: {settings:
|
|||||||
<Button onClick={() => void retryFailedSynchronously(failedRowsOnPage)} disabled={!version || Boolean(busyAction) || failedRowsOnPage.length === 0}>
|
<Button onClick={() => void retryFailedSynchronously(failedRowsOnPage)} disabled={!version || Boolean(busyAction) || failedRowsOnPage.length === 0}>
|
||||||
{busyAction === "retry-sync-page" ? "Sending failed jobs..." : `Retry failed on this page now (${failedRowsOnPage.length})`}
|
{busyAction === "retry-sync-page" ? "Sending failed jobs..." : `Retry failed on this page now (${failedRowsOnPage.length})`}
|
||||||
</Button>
|
</Button>
|
||||||
<Button onClick={() => void runExplicitAction("unattempted")} disabled={!version || Boolean(busyAction)}>i18n:govoplan-campaign.send_unattempted_jobs.db7acc9f</Button>
|
<Button helpContextId="campaign.report" helpModuleId="campaigns" onClick={() => void runExplicitAction("unattempted")} disabled={!version || Boolean(busyAction)}>i18n:govoplan-campaign.send_unattempted_jobs.db7acc9f</Button>
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
</ContentGrid>
|
</ContentGrid>
|
||||||
@@ -591,7 +591,7 @@ export default function CampaignReportPage({ settings, campaignId }: {settings:
|
|||||||
footer={
|
footer={
|
||||||
<div className="button-row">
|
<div className="button-row">
|
||||||
<Button onClick={() => setEmailOpen(false)} disabled={busyAction === "email"}>i18n:govoplan-campaign.cancel.77dfd213</Button>
|
<Button onClick={() => setEmailOpen(false)} disabled={busyAction === "email"}>i18n:govoplan-campaign.cancel.77dfd213</Button>
|
||||||
<Button variant="primary" onClick={() => void sendReportEmail()} disabled={!emailRecipients.trim() || busyAction === "email"}>i18n:govoplan-campaign.send_report.a5b32af9</Button>
|
<Button variant="primary" helpContextId="campaign.report" helpModuleId="campaigns" onClick={() => void sendReportEmail()} disabled={!emailRecipients.trim() || busyAction === "email"}>i18n:govoplan-campaign.send_report.a5b32af9</Button>
|
||||||
</div>
|
</div>
|
||||||
}>
|
}>
|
||||||
|
|
||||||
|
|||||||
@@ -359,7 +359,7 @@ export default function CampaignWorkPage({
|
|||||||
) : null}
|
) : null}
|
||||||
{canComplete && (assignment.status === "open" || assignment.status === "in_progress") ? (
|
{canComplete && (assignment.status === "open" || assignment.status === "in_progress") ? (
|
||||||
<span className="campaign-work-destructive-action">
|
<span className="campaign-work-destructive-action">
|
||||||
<Button variant="danger" onClick={() => setRejecting(assignment)} disabled={Boolean(busyId)}>
|
<Button variant="danger" helpContextId="campaign.work.action.reject" helpModuleId="campaigns" onClick={() => setRejecting(assignment)} disabled={Boolean(busyId)}>
|
||||||
Reject work
|
Reject work
|
||||||
</Button>
|
</Button>
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
@@ -379,6 +379,8 @@ export default function GlobalSettingsPage({ settings, auth, campaignId, view =
|
|||||||
<FormGrid columns={2} collapseAt="wide" className="">
|
<FormGrid columns={2} collapseAt="wide" className="">
|
||||||
<ToggleSwitch
|
<ToggleSwitch
|
||||||
label="Send individualized invitations"
|
label="Send individualized invitations"
|
||||||
|
helpContextId="campaign.global-settings"
|
||||||
|
helpModuleId="campaigns"
|
||||||
checked={getBool(calendarInvitation, "enabled")}
|
checked={getBool(calendarInvitation, "enabled")}
|
||||||
disabled={locked || !calendarCatalog.available || !mailModuleInstalled}
|
disabled={locked || !calendarCatalog.available || !mailModuleInstalled}
|
||||||
onChange={(checked) => patch(["delivery", "calendar_invitation", "enabled"], checked)}
|
onChange={(checked) => patch(["delivery", "calendar_invitation", "enabled"], checked)}
|
||||||
|
|||||||
@@ -383,7 +383,7 @@ export default function MailSettingsPage({ settings, campaignId, view = "setting
|
|||||||
title="i18n:govoplan-campaign.reusable_mail_profile.f9c9aab1"
|
title="i18n:govoplan-campaign.reusable_mail_profile.f9c9aab1"
|
||||||
actions={<div className="button-row compact-actions">
|
actions={<div className="button-row compact-actions">
|
||||||
<Button onClick={() => void refreshMailProfiles()} disabled={profilesLoading}>{profilesLoading ? "i18n:govoplan-campaign.loading.33ce4174" : "i18n:govoplan-campaign.reload_profiles.0fe100d1"}</Button>
|
<Button onClick={() => void refreshMailProfiles()} disabled={profilesLoading}>{profilesLoading ? "i18n:govoplan-campaign.loading.33ce4174" : "i18n:govoplan-campaign.reload_profiles.0fe100d1"}</Button>
|
||||||
<Button variant="primary" onClick={openCredentialDialog} disabled={locked || profilesLoading || !selectedProfile || !selectedSmtpServer && !selectedImapServer}>Add campaign credential</Button>
|
<Button variant="primary" helpContextId="campaign.server-settings" helpModuleId="campaigns" 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>
|
||||||
<FormGrid columns={2} collapseAt="wide" className="">
|
<FormGrid columns={2} collapseAt="wide" className="">
|
||||||
@@ -399,8 +399,8 @@ export default function MailSettingsPage({ settings, campaignId, view = "setting
|
|||||||
{smtpServers.map((item) => <option key={item.id} value={item.id}>{item.name} ({serverEndpointLabel(item.config)})</option>)}
|
{smtpServers.map((item) => <option key={item.id} value={item.id}>{item.name} ({serverEndpointLabel(item.config)})</option>)}
|
||||||
</select>
|
</select>
|
||||||
</FormField>
|
</FormField>
|
||||||
<FormField label="SMTP credential">
|
<FormField label="SMTP credential" helpContextId="campaign.server-settings" helpModuleId="campaigns">
|
||||||
<select value={selectedSmtpCredential?.id ?? ""} disabled={locked || profilesLoading || !selectedSmtpServer} onChange={(event) => selectCredential("smtp", event.target.value)}>
|
<select data-help-context-id="campaign.server-settings" data-help-module-id="campaigns" value={selectedSmtpCredential?.id ?? ""} disabled={locked || profilesLoading || !selectedSmtpServer} onChange={(event) => selectCredential("smtp", event.target.value)}>
|
||||||
<option value="">No credential</option>
|
<option value="">No credential</option>
|
||||||
{selectedSmtpServer?.credentials.filter((item) => item.is_active).map((item) =>
|
{selectedSmtpServer?.credentials.filter((item) => item.is_active).map((item) =>
|
||||||
<option key={item.id} value={item.id}>{credentialLabel(item)}</option>
|
<option key={item.id} value={item.id}>{credentialLabel(item)}</option>
|
||||||
@@ -413,8 +413,8 @@ export default function MailSettingsPage({ settings, campaignId, view = "setting
|
|||||||
{imapServers.map((item) => <option key={item.id} value={item.id}>{item.name} ({serverEndpointLabel(item.config)})</option>)}
|
{imapServers.map((item) => <option key={item.id} value={item.id}>{item.name} ({serverEndpointLabel(item.config)})</option>)}
|
||||||
</select>
|
</select>
|
||||||
</FormField>
|
</FormField>
|
||||||
<FormField label="IMAP credential">
|
<FormField label="IMAP credential" helpContextId="campaign.server-settings" helpModuleId="campaigns">
|
||||||
<select value={selectedImapCredential?.id ?? ""} disabled={locked || profilesLoading || !selectedImapServer} onChange={(event) => selectCredential("imap", event.target.value)}>
|
<select data-help-context-id="campaign.server-settings" data-help-module-id="campaigns" value={selectedImapCredential?.id ?? ""} disabled={locked || profilesLoading || !selectedImapServer} onChange={(event) => selectCredential("imap", event.target.value)}>
|
||||||
<option value="">No credential</option>
|
<option value="">No credential</option>
|
||||||
{selectedImapServer?.credentials.filter((item) => item.is_active).map((item) =>
|
{selectedImapServer?.credentials.filter((item) => item.is_active).map((item) =>
|
||||||
<option key={item.id} value={item.id}>{credentialLabel(item)}</option>
|
<option key={item.id} value={item.id}>{credentialLabel(item)}</option>
|
||||||
@@ -476,8 +476,8 @@ export default function MailSettingsPage({ settings, campaignId, view = "setting
|
|||||||
<FormField label="Username">
|
<FormField label="Username">
|
||||||
<input value={credentialDraft.username} disabled={credentialSaving} onChange={(event) => setCredentialDraft({ ...credentialDraft, username: event.target.value })} />
|
<input value={credentialDraft.username} disabled={credentialSaving} onChange={(event) => setCredentialDraft({ ...credentialDraft, username: event.target.value })} />
|
||||||
</FormField>
|
</FormField>
|
||||||
<FormField label="Password">
|
<FormField label="Password" helpContextId="campaign.server-settings" helpModuleId="campaigns">
|
||||||
<PasswordField value={credentialDraft.password} onValueChange={(password) => setCredentialDraft({ ...credentialDraft, password })} disabled={credentialSaving} generator autoComplete="new-password" />
|
<PasswordField helpContextId="campaign.server-settings" helpModuleId="campaigns" value={credentialDraft.password} onValueChange={(password) => setCredentialDraft({ ...credentialDraft, password })} disabled={credentialSaving} generator autoComplete="new-password" />
|
||||||
</FormField>
|
</FormField>
|
||||||
</FormGrid>
|
</FormGrid>
|
||||||
<FormGrid columns={2} collapseAt="standard" className="">
|
<FormGrid columns={2} collapseAt="standard" className="">
|
||||||
|
|||||||
@@ -1638,7 +1638,7 @@ export default function ReviewSendPage({
|
|||||||
<p className="review-flow-inline-note is-stale">i18n:govoplan-campaign.mock_delivery_uses_the_mail_module_development_m.9d645a5f</p>
|
<p className="review-flow-inline-note is-stale">i18n:govoplan-campaign.mock_delivery_uses_the_mail_module_development_m.9d645a5f</p>
|
||||||
}
|
}
|
||||||
<div className="toggle-row mock-send-options">
|
<div className="toggle-row mock-send-options">
|
||||||
<ToggleSwitch label="i18n:govoplan-campaign.require_mock_before_real_send.a1ccc330" checked={mockVerificationRequired} disabled={!mockWorkflowAvailable || Boolean(busy)} onChange={setMockVerificationRequired} />
|
<ToggleSwitch label="i18n:govoplan-campaign.require_mock_before_real_send.a1ccc330" helpContextId="campaign.review-send" helpModuleId="campaigns" checked={mockVerificationRequired} disabled={!mockWorkflowAvailable || Boolean(busy)} onChange={setMockVerificationRequired} />
|
||||||
<ToggleSwitch label="i18n:govoplan-campaign.show_captured_mock_mailbox.019e1e79" checked={mockMailboxPreviewEnabled && mockWorkflowAvailable} disabled={!mockWorkflowAvailable || Boolean(busy)} onChange={setMockMailboxPreviewEnabled} />
|
<ToggleSwitch label="i18n:govoplan-campaign.show_captured_mock_mailbox.019e1e79" checked={mockMailboxPreviewEnabled && mockWorkflowAvailable} disabled={!mockWorkflowAvailable || Boolean(busy)} onChange={setMockMailboxPreviewEnabled} />
|
||||||
<ToggleSwitch label="i18n:govoplan-campaign.clear_mock_mailbox_first.7627dcad" checked={mockClearFirst} disabled={!mockWorkflowAvailable || Boolean(busy)} onChange={setMockClearFirst} />
|
<ToggleSwitch label="i18n:govoplan-campaign.clear_mock_mailbox_first.7627dcad" checked={mockClearFirst} disabled={!mockWorkflowAvailable || Boolean(busy)} onChange={setMockClearFirst} />
|
||||||
<ToggleSwitch label="i18n:govoplan-campaign.append_mock_sent_copy.49c2cf08" checked={mockAppendSent} disabled={!mockWorkflowAvailable || Boolean(busy)} onChange={setMockAppendSent} />
|
<ToggleSwitch label="i18n:govoplan-campaign.append_mock_sent_copy.49c2cf08" checked={mockAppendSent} disabled={!mockWorkflowAvailable || Boolean(busy)} onChange={setMockAppendSent} />
|
||||||
|
|||||||
@@ -429,7 +429,7 @@ export default function CampaignAccessCard({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Card title="i18n:govoplan-campaign.ownership_and_sharing.867283c0" actions={<div className="button-row compact-actions">{canProposeOwnership ? <Button onClick={openOwnerDialog}><ArrowRightLeft size={16} aria-hidden="true" /> Transfer ownership</Button> : null}{canRequestOwnership ? <Button onClick={openRequestDialog}><ArrowRightLeft size={16} aria-hidden="true" /> Request ownership</Button> : null}<Button onClick={() => setShareOpen(true)} disabled={available !== true}>i18n:govoplan-campaign.share.09ca55ca</Button><Button onClick={() => void openAccessExplanation()} disabled={!canExplainResourceAccess}><KeyRound size={16} aria-hidden="true" /> i18n:govoplan-campaign.explain_access.4d5fac37</Button></div>}>
|
<Card title="i18n:govoplan-campaign.ownership_and_sharing.867283c0" actions={<div className="button-row compact-actions">{canProposeOwnership ? <Button helpContextId="campaign.overview" helpModuleId="campaigns" onClick={openOwnerDialog}><ArrowRightLeft size={16} aria-hidden="true" /> Transfer ownership</Button> : null}{canRequestOwnership ? <Button onClick={openRequestDialog}><ArrowRightLeft size={16} aria-hidden="true" /> Request ownership</Button> : null}<Button onClick={() => setShareOpen(true)} disabled={available !== true}>i18n:govoplan-campaign.share.09ca55ca</Button><Button onClick={() => void openAccessExplanation()} disabled={!canExplainResourceAccess}><KeyRound size={16} aria-hidden="true" /> i18n:govoplan-campaign.explain_access.4d5fac37</Button></div>}>
|
||||||
<p><strong>i18n:govoplan-campaign.owner.719379ae</strong> {ownerLabel}</p>
|
<p><strong>i18n:govoplan-campaign.owner.719379ae</strong> {ownerLabel}</p>
|
||||||
<p className="muted small-note">Ownership changes require acceptance by the new owner. They never transfer private encryption keys. A completed transfer clears owner-scoped mail profile selection and requires revalidation.</p>
|
<p className="muted small-note">Ownership changes require acceptance by the new owner. They never transfer private encryption keys. A completed transfer clears owner-scoped mail profile selection and requires revalidation.</p>
|
||||||
{ownershipTransfers.length > 0 ? (
|
{ownershipTransfers.length > 0 ? (
|
||||||
@@ -489,7 +489,7 @@ export default function CampaignAccessCard({
|
|||||||
)}
|
)}
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
<Dialog open={ownerOpen} className="campaign-access-dialog" title="Transfer campaign ownership" onClose={() => !busy && closeOwnerDialog()} footer={<><Button onClick={closeOwnerDialog} disabled={busy}>i18n:govoplan-campaign.cancel.77dfd213</Button><Button variant="primary" onClick={() => void saveOwner()} disabled={busy || !ownerId || ownerType === currentOwnerType && ownerId === currentOwnerId}>Propose transfer</Button></>}>
|
<Dialog open={ownerOpen} className="campaign-access-dialog" title="Transfer campaign ownership" helpContextId="campaign.overview" helpModuleId="campaigns" onClose={() => !busy && closeOwnerDialog()} footer={<><Button onClick={closeOwnerDialog} disabled={busy}>i18n:govoplan-campaign.cancel.77dfd213</Button><Button variant="primary" helpContextId="campaign.overview" helpModuleId="campaigns" onClick={() => void saveOwner()} disabled={busy || !ownerId || ownerType === currentOwnerType && ownerId === currentOwnerId}>Propose transfer</Button></>}>
|
||||||
<FormField label="i18n:govoplan-campaign.owner_type.6b86eacc"><select value={ownerType} onChange={(event) => {const next = event.target.value as TargetType;setOwnerType(next);setOwnerId("");}}><option value="user">i18n:govoplan-campaign.user.9f8a2389</option><option value="group">i18n:govoplan-campaign.group.171a0606</option></select></FormField>
|
<FormField label="i18n:govoplan-campaign.owner_type.6b86eacc"><select value={ownerType} onChange={(event) => {const next = event.target.value as TargetType;setOwnerType(next);setOwnerId("");}}><option value="user">i18n:govoplan-campaign.user.9f8a2389</option><option value="group">i18n:govoplan-campaign.group.171a0606</option></select></FormField>
|
||||||
<FormField label="i18n:govoplan-campaign.owner.89ff3122">
|
<FormField label="i18n:govoplan-campaign.owner.89ff3122">
|
||||||
<ReferenceSelect
|
<ReferenceSelect
|
||||||
|
|||||||
Reference in New Issue
Block a user