chore: sync GovOPlaN module split state
This commit is contained in:
@@ -8,7 +8,7 @@ import DataGrid, { DataGridEmptyAction, DataGridRowActions, type DataGridColumn
|
||||
import { ToggleSwitch } from "@govoplan/core-webui";
|
||||
import { getBool, getText } from "../utils/draftEditor";
|
||||
import { attachmentRuleZipSelection, createAttachmentRule, nextAttachmentLabel, summarizeAttachmentRules, type AttachmentBasePath, type AttachmentRule, type AttachmentZipCollection } from "../utils/attachments";
|
||||
import { insertAfter, moveArrayItem } from "@govoplan/core-webui";
|
||||
import { insertAfter, moveArrayItem, i18nMessage } from "@govoplan/core-webui";
|
||||
import { asRecord } from "../utils/campaignView";
|
||||
import { renderTemplatePreviewText } from "../utils/templatePlaceholders";
|
||||
|
||||
@@ -57,7 +57,7 @@ export default function AttachmentRulesOverlay({
|
||||
campaignId,
|
||||
disabled = false,
|
||||
buttonLabel,
|
||||
emptyText = "No attachment files or matching rules configured yet.",
|
||||
emptyText = "i18n:govoplan-campaign.no_attachment_files_or_matching_rules_configured.c6948b49",
|
||||
basePaths = [],
|
||||
zipConfig = { enabled: false, archives: [] },
|
||||
filesModuleInstalled = false,
|
||||
@@ -91,13 +91,13 @@ export default function AttachmentRulesOverlay({
|
||||
className="attachment-rules-modal"
|
||||
bodyClassName="attachment-rules-body"
|
||||
onClose={cancelOverlay}
|
||||
footer={(
|
||||
<>
|
||||
<Button onClick={cancelOverlay}>Cancel</Button>
|
||||
<Button variant="primary" onClick={saveOverlay} disabled={disabled}>Save</Button>
|
||||
footer={
|
||||
<>
|
||||
<Button onClick={cancelOverlay}>i18n:govoplan-campaign.cancel.77dfd213</Button>
|
||||
<Button variant="primary" onClick={saveOverlay} disabled={disabled}>i18n:govoplan-campaign.save.efc007a3</Button>
|
||||
</>
|
||||
)}
|
||||
>
|
||||
}>
|
||||
|
||||
<AttachmentRulesTable
|
||||
rules={draftRules}
|
||||
settings={settings}
|
||||
@@ -109,20 +109,20 @@ export default function AttachmentRulesOverlay({
|
||||
filesModuleInstalled={filesModuleInstalled}
|
||||
previewContext={previewContext}
|
||||
activeChooserRuleIndex={null}
|
||||
onChange={setDraftRules}
|
||||
/>
|
||||
onChange={setDraftRules} />
|
||||
|
||||
</Dialog>,
|
||||
document.body
|
||||
) : null;
|
||||
|
||||
return (
|
||||
<>
|
||||
<Button className="attachment-summary-button" onClick={openOverlay} disabled={disabled && rules.length === 0} title={`${summary.direct} direct file(s), ${summary.rules} rule(s) / pattern(s)`}>
|
||||
<Button className="attachment-summary-button" onClick={openOverlay} disabled={disabled && rules.length === 0} title={i18nMessage("i18n:govoplan-campaign.value_direct_file_s_value_rule_s_pattern_s.df9b46d9", { value0: summary.direct, value1: summary.rules })}>
|
||||
{label}
|
||||
</Button>
|
||||
{dialog}
|
||||
</>
|
||||
);
|
||||
</>);
|
||||
|
||||
}
|
||||
|
||||
function cloneAttachmentRules(rules: AttachmentRule[]): AttachmentRule[] {
|
||||
@@ -141,8 +141,8 @@ export function AttachmentRulesTable({
|
||||
<div className="attachment-rules-main">
|
||||
<AttachmentRulesDataGrid {...tableProps} onChange={onChange} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
</div>);
|
||||
|
||||
}
|
||||
|
||||
export function AttachmentRulesDataGrid({
|
||||
@@ -150,7 +150,7 @@ export function AttachmentRulesDataGrid({
|
||||
settings,
|
||||
campaignId,
|
||||
disabled = false,
|
||||
emptyText = "No attachment files or matching rules configured yet.",
|
||||
emptyText = "i18n:govoplan-campaign.no_attachment_files_or_matching_rules_configured.c6948b49",
|
||||
basePaths = [],
|
||||
id = "attachment-rules",
|
||||
activeChooserRuleIndex = null,
|
||||
@@ -196,10 +196,10 @@ export function AttachmentRulesDataGrid({
|
||||
const currentPath = getText(rule, "base_dir");
|
||||
const currentBasePathId = getText(rule, "base_path_id");
|
||||
const explicitlyReferenced = Boolean(currentBasePathId || currentPath);
|
||||
const basePath = basePaths.find((item) => item.id === currentBasePathId)
|
||||
?? basePaths.find((item) => item.path === currentPath)
|
||||
?? (!explicitlyReferenced ? basePaths[0] : undefined)
|
||||
?? null;
|
||||
const basePath = basePaths.find((item) => item.id === currentBasePathId) ??
|
||||
basePaths.find((item) => item.path === currentPath) ?? (
|
||||
!explicitlyReferenced ? basePaths[0] : undefined) ??
|
||||
null;
|
||||
if (!basePath) return;
|
||||
setFileChooser({ ruleIndex, basePath });
|
||||
}
|
||||
@@ -225,30 +225,30 @@ export function AttachmentRulesDataGrid({
|
||||
rows={rules}
|
||||
columns={attachmentRuleColumns({ disabled, rules, basePaths, zipConfig, filesModuleInstalled: managedFilesAvailable, activeChooserRuleIndex: activeChooserRuleIndex ?? fileChooser?.ruleIndex ?? null, patchRule, addRule, moveRule, openFileChooser, removeRule })}
|
||||
getRowKey={(rule, index) => String(rule.id ?? index)}
|
||||
emptyText={basePaths.length === 0 ? "No attachment source is enabled for individual attachments." : emptyText}
|
||||
emptyAction={<DataGridEmptyAction onAdd={() => addRule(-1)} disabled={disabled || basePaths.length === 0} label="Add first attachment" />}
|
||||
emptyText={basePaths.length === 0 ? "i18n:govoplan-campaign.no_attachment_source_is_enabled_for_individual_a.818a2820" : emptyText}
|
||||
emptyAction={<DataGridEmptyAction onAdd={() => addRule(-1)} disabled={disabled || basePaths.length === 0} label="i18n:govoplan-campaign.add_first_attachment.025fbf31" />}
|
||||
className="attachment-rules-table-wrap attachment-rules-table"
|
||||
rowClassName={(_rule, index) => (activeChooserRuleIndex ?? fileChooser?.ruleIndex) === index ? "is-choosing-file" : undefined}
|
||||
/>
|
||||
{fileChooser && ManagedFileChooser && (
|
||||
<ManagedFileChooser
|
||||
open
|
||||
settings={settings}
|
||||
storageScope={campaignId}
|
||||
linkTarget={{ type: "campaign", id: campaignId, label: "campaign" }}
|
||||
mode="attachment"
|
||||
source={fileChooser.basePath?.source}
|
||||
basePath={fileChooser.basePath?.path ?? "."}
|
||||
initialPattern={getText(rules[fileChooser.ruleIndex], "file_filter")}
|
||||
rememberKey={`${id}:${String(rules[fileChooser.ruleIndex]?.id ?? fileChooser.ruleIndex)}`}
|
||||
previewContext={previewContext}
|
||||
renderPatternPreview={(pattern, context) => renderTemplatePreviewText(pattern, context, false)}
|
||||
onClose={() => setFileChooser(null)}
|
||||
onSelectAttachment={selectAttachment}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
rowClassName={(_rule, index) => (activeChooserRuleIndex ?? fileChooser?.ruleIndex) === index ? "is-choosing-file" : undefined} />
|
||||
|
||||
{fileChooser && ManagedFileChooser &&
|
||||
<ManagedFileChooser
|
||||
open
|
||||
settings={settings}
|
||||
storageScope={campaignId}
|
||||
linkTarget={{ type: "campaign", id: campaignId, label: "campaign" }}
|
||||
mode="attachment"
|
||||
source={fileChooser.basePath?.source}
|
||||
basePath={fileChooser.basePath?.path ?? "."}
|
||||
initialPattern={getText(rules[fileChooser.ruleIndex], "file_filter")}
|
||||
rememberKey={`${id}:${String(rules[fileChooser.ruleIndex]?.id ?? fileChooser.ruleIndex)}`}
|
||||
previewContext={previewContext}
|
||||
renderPatternPreview={(pattern, context) => renderTemplatePreviewText(pattern, context, false)}
|
||||
onClose={() => setFileChooser(null)}
|
||||
onSelectAttachment={selectAttachment} />
|
||||
|
||||
}
|
||||
</>);
|
||||
|
||||
}
|
||||
|
||||
type AttachmentRuleColumnContext = {
|
||||
@@ -267,122 +267,154 @@ type AttachmentRuleColumnContext = {
|
||||
|
||||
function attachmentRuleColumns({ disabled, rules, basePaths, zipConfig, filesModuleInstalled, activeChooserRuleIndex: _activeChooserRuleIndex, patchRule, addRule, moveRule, openFileChooser, removeRule }: AttachmentRuleColumnContext): DataGridColumn<AttachmentRule>[] {
|
||||
return [
|
||||
{ id: "label", header: "Label", width: 190, resizable: true, sortable: true, filterable: true, sticky: "start", render: (rule, index) => <input value={getText(rule, "label")} disabled={disabled} placeholder="Attachment label" onChange={(event) => patchRule(index, { label: event.target.value })} />, value: (rule) => getText(rule, "label") },
|
||||
{
|
||||
id: "base_path",
|
||||
header: "Base path",
|
||||
width: 250,
|
||||
sortable: true,
|
||||
filterable: true,
|
||||
columnType: "from-list",
|
||||
list: { options: basePaths.map((basePath) => ({ value: basePath.path, label: basePath.name || basePath.path })) },
|
||||
render: (rule, index) => {
|
||||
const currentBasePathValue = getText(rule, "base_dir");
|
||||
const currentBasePathId = getText(rule, "base_path_id");
|
||||
const explicitlyReferenced = Boolean(currentBasePathId || currentBasePathValue);
|
||||
const selectedBasePath = basePaths.find((basePath) => basePath.id === currentBasePathId)
|
||||
?? basePaths.find((basePath) => basePath.path === currentBasePathValue)
|
||||
?? (!explicitlyReferenced ? basePaths[0] : undefined);
|
||||
return (
|
||||
<select
|
||||
value={selectedBasePath?.id ?? ""}
|
||||
disabled={disabled || basePaths.length === 0}
|
||||
onChange={(event) => {
|
||||
const nextBasePath = basePaths.find((basePath) => basePath.id === event.target.value);
|
||||
if (nextBasePath) patchRule(index, { base_path_id: nextBasePath.id, base_dir: nextBasePath.path });
|
||||
}}
|
||||
>
|
||||
{!selectedBasePath && (
|
||||
<option value="" disabled>{basePaths.length === 0 ? "No individual attachment source" : "Unavailable attachment source"}</option>
|
||||
)}
|
||||
{ id: "label", header: "i18n:govoplan-campaign.label.74341e3c", width: 190, resizable: true, sortable: true, filterable: true, sticky: "start", render: (rule, index) => <input value={getText(rule, "label")} disabled={disabled} placeholder="i18n:govoplan-campaign.attachment_label.a340f70e" onChange={(event) => patchRule(index, { label: event.target.value })} />, value: (rule) => getText(rule, "label") },
|
||||
{
|
||||
id: "base_path",
|
||||
header: "i18n:govoplan-campaign.base_path.6a4867ca",
|
||||
width: 250,
|
||||
sortable: true,
|
||||
filterable: true,
|
||||
columnType: "from-list",
|
||||
list: { options: basePaths.map((basePath) => ({ value: basePath.path, label: basePath.name || basePath.path })) },
|
||||
render: (rule, index) => {
|
||||
const currentBasePathValue = getText(rule, "base_dir");
|
||||
const currentBasePathId = getText(rule, "base_path_id");
|
||||
const explicitlyReferenced = Boolean(currentBasePathId || currentBasePathValue);
|
||||
const selectedBasePath = basePaths.find((basePath) => basePath.id === currentBasePathId) ??
|
||||
basePaths.find((basePath) => basePath.path === currentBasePathValue) ?? (
|
||||
!explicitlyReferenced ? basePaths[0] : undefined);
|
||||
return (
|
||||
<select
|
||||
value={selectedBasePath?.id ?? ""}
|
||||
disabled={disabled || basePaths.length === 0}
|
||||
onChange={(event) => {
|
||||
const nextBasePath = basePaths.find((basePath) => basePath.id === event.target.value);
|
||||
if (nextBasePath) patchRule(index, { base_path_id: nextBasePath.id, base_dir: nextBasePath.path });
|
||||
}}>
|
||||
|
||||
{!selectedBasePath &&
|
||||
<option value="" disabled>{basePaths.length === 0 ? "i18n:govoplan-campaign.no_individual_attachment_source.6b54176a" : "i18n:govoplan-campaign.unavailable_attachment_source.7ff1096e"}</option>
|
||||
}
|
||||
{basePaths.map((basePath) => <option key={basePath.id} value={basePath.id}>{basePath.name || basePath.path}</option>)}
|
||||
</select>
|
||||
);
|
||||
},
|
||||
value: (rule) => getText(rule, "base_dir", basePaths[0]?.path ?? "")
|
||||
</select>);
|
||||
|
||||
},
|
||||
{
|
||||
id: "file_filter",
|
||||
header: "File / pattern",
|
||||
width: "minmax(260px, 1fr)",
|
||||
resizable: true,
|
||||
sortable: true,
|
||||
filterable: true,
|
||||
render: (rule, index) => (
|
||||
<div className="field-with-action split-field-action">
|
||||
value: (rule) => getText(rule, "base_dir", basePaths[0]?.path ?? "")
|
||||
},
|
||||
{
|
||||
id: "file_filter",
|
||||
header: "i18n:govoplan-campaign.file_pattern.86320b07",
|
||||
width: "minmax(260px, 1fr)",
|
||||
resizable: true,
|
||||
sortable: true,
|
||||
filterable: true,
|
||||
render: (rule, index) =>
|
||||
<div className="field-with-action split-field-action">
|
||||
<input
|
||||
className="chooser-display-input"
|
||||
value={getText(rule, "file_filter")}
|
||||
disabled={disabled || basePaths.length === 0}
|
||||
readOnly={filesModuleInstalled}
|
||||
tabIndex={filesModuleInstalled ? -1 : undefined}
|
||||
placeholder={filesModuleInstalled ? "Choose a managed file or pattern" : "file.pdf or **/*.pdf"}
|
||||
onChange={(event) => {
|
||||
if (!filesModuleInstalled) patchRule(index, { file_filter: event.target.value });
|
||||
}}
|
||||
onClick={() => filesModuleInstalled && !disabled && openFileChooser(index)}
|
||||
onKeyDown={(event) => {
|
||||
if (filesModuleInstalled && !disabled && (event.key === "Enter" || event.key === " ")) {
|
||||
event.preventDefault();
|
||||
openFileChooser(index);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
{filesModuleInstalled && <Button onClick={() => openFileChooser(index)} disabled={disabled || basePaths.length === 0}>Choose</Button>}
|
||||
</div>
|
||||
),
|
||||
value: (rule) => getText(rule, "file_filter")
|
||||
className="chooser-display-input"
|
||||
value={getText(rule, "file_filter")}
|
||||
disabled={disabled || basePaths.length === 0}
|
||||
readOnly={filesModuleInstalled}
|
||||
tabIndex={filesModuleInstalled ? -1 : undefined}
|
||||
placeholder={filesModuleInstalled ? "i18n:govoplan-campaign.choose_a_managed_file_or_pattern.96bb3bfb" : "file.pdf or **/*.pdf"}
|
||||
onChange={(event) => {
|
||||
if (!filesModuleInstalled) patchRule(index, { file_filter: event.target.value });
|
||||
}}
|
||||
onClick={() => filesModuleInstalled && !disabled && openFileChooser(index)}
|
||||
onKeyDown={(event) => {
|
||||
if (filesModuleInstalled && !disabled && (event.key === "Enter" || event.key === " ")) {
|
||||
event.preventDefault();
|
||||
openFileChooser(index);
|
||||
}
|
||||
}} />
|
||||
|
||||
{filesModuleInstalled && <Button onClick={() => openFileChooser(index)} disabled={disabled || basePaths.length === 0}>i18n:govoplan-campaign.choose.78b7c9f6</Button>}
|
||||
</div>,
|
||||
|
||||
value: (rule) => getText(rule, "file_filter")
|
||||
},
|
||||
{
|
||||
id: "message_filename_template",
|
||||
header: "i18n:govoplan-campaign.message_filename_template.0b7ac934",
|
||||
width: 230,
|
||||
resizable: true,
|
||||
sortable: true,
|
||||
filterable: true,
|
||||
render: (rule, index) =>
|
||||
<input
|
||||
value={getText(rule, "message_filename_template")}
|
||||
disabled={disabled}
|
||||
placeholder="i18n:govoplan-campaign.keep_original_filename.9b805045"
|
||||
onChange={(event) => patchRule(index, { message_filename_template: event.target.value })} />,
|
||||
|
||||
value: (rule) => getText(rule, "message_filename_template")
|
||||
},
|
||||
{
|
||||
id: "zip_entry_name_template",
|
||||
header: "i18n:govoplan-campaign.zip_entry_name_template.83772a73",
|
||||
width: 230,
|
||||
resizable: true,
|
||||
sortable: true,
|
||||
filterable: true,
|
||||
render: (rule, index) =>
|
||||
<input
|
||||
value={getText(rule, "zip_entry_name_template")}
|
||||
disabled={disabled}
|
||||
placeholder="i18n:govoplan-campaign.keep_original_zip_entry_name.d51558c4"
|
||||
onChange={(event) => patchRule(index, { zip_entry_name_template: event.target.value })} />,
|
||||
|
||||
value: (rule) => getText(rule, "zip_entry_name_template")
|
||||
},
|
||||
...(zipConfig.enabled ? [{
|
||||
id: "zip",
|
||||
header: "i18n:govoplan-campaign.zip_archive.5a2430dd",
|
||||
width: 240,
|
||||
sortable: true,
|
||||
filterable: true,
|
||||
columnType: "from-list",
|
||||
list: {
|
||||
options: [
|
||||
{ value: "exclude", label: "i18n:govoplan-campaign.exclude_from_zip.a6422da1" },
|
||||
{ value: "inherit", label: "i18n:govoplan-campaign.campaign_standard.c88ce44c" },
|
||||
...zipConfig.archives.map((archive) => ({ value: archive.id, label: archive.name }))]
|
||||
|
||||
},
|
||||
...(zipConfig.enabled ? [{
|
||||
id: "zip",
|
||||
header: "ZIP archive",
|
||||
width: 240,
|
||||
sortable: true,
|
||||
filterable: true,
|
||||
columnType: "from-list",
|
||||
list: {
|
||||
options: [
|
||||
{ value: "exclude", label: "Exclude from ZIP" },
|
||||
{ value: "inherit", label: "Campaign standard" },
|
||||
...zipConfig.archives.map((archive) => ({ value: archive.id, label: archive.name }))
|
||||
]
|
||||
},
|
||||
render: (rule: AttachmentRule, index: number) => {
|
||||
const selection = attachmentRuleZipSelection(rule);
|
||||
return (
|
||||
<select
|
||||
value={selection}
|
||||
disabled={disabled || zipConfig.archives.length === 0}
|
||||
aria-label={`ZIP archive for ${getText(rule, "label") || `attachment ${index + 1}`}`}
|
||||
onChange={(event) => patchRule(index, { zip: { ...asRecord(rule.zip), archive_id: event.target.value } })}
|
||||
>
|
||||
<option value="exclude">Exclude from ZIP</option>
|
||||
<option value="inherit">Campaign standard</option>
|
||||
render: (rule: AttachmentRule, index: number) => {
|
||||
const selection = attachmentRuleZipSelection(rule);
|
||||
return (
|
||||
<select
|
||||
value={selection}
|
||||
disabled={disabled || zipConfig.archives.length === 0}
|
||||
aria-label={i18nMessage("i18n:govoplan-campaign.zip_archive_for_value.3dfaf812", { value0: getText(rule, "label") || `attachment ${index + 1}` })}
|
||||
onChange={(event) => patchRule(index, { zip: { ...asRecord(rule.zip), archive_id: event.target.value } })}>
|
||||
|
||||
<option value="exclude">i18n:govoplan-campaign.exclude_from_zip.a6422da1</option>
|
||||
<option value="inherit">i18n:govoplan-campaign.campaign_standard.c88ce44c</option>
|
||||
{zipConfig.archives.map((archive) => <option key={archive.id} value={archive.id}>{archive.name}</option>)}
|
||||
</select>
|
||||
);
|
||||
},
|
||||
value: (rule: AttachmentRule) => attachmentRuleZipSelection(rule)
|
||||
} satisfies DataGridColumn<AttachmentRule>] : []),
|
||||
{ id: "required", header: "Required", width: 175, sortable: true, filterable: true, columnType: "from-list", list: { options: [{ value: "required", label: "Required" }, { value: "optional", label: "Optional" }] }, render: (rule, index) => <ToggleSwitch label="Required" checked={getBool(rule, "required", true)} disabled={disabled} onChange={(checked) => patchRule(index, { required: checked })} />, value: (rule) => getBool(rule, "required", true) ? "required" : "optional" },
|
||||
{
|
||||
id: "actions",
|
||||
header: "Actions",
|
||||
width: 180,
|
||||
sticky: "end",
|
||||
render: (_rule, index) => (
|
||||
<DataGridRowActions
|
||||
disabled={disabled}
|
||||
onAddBelow={() => addRule(index)}
|
||||
onRemove={() => removeRule(index)}
|
||||
onMoveUp={index > 0 ? () => moveRule(index, index - 1) : undefined}
|
||||
onMoveDown={index < rules.length - 1 ? () => moveRule(index, index + 1) : undefined}
|
||||
addLabel="Add attachment below"
|
||||
removeLabel="Remove attachment"
|
||||
moveUpLabel="Move attachment up"
|
||||
moveDownLabel="Move attachment down"
|
||||
/>
|
||||
)
|
||||
}
|
||||
];
|
||||
</select>);
|
||||
|
||||
},
|
||||
value: (rule: AttachmentRule) => attachmentRuleZipSelection(rule)
|
||||
} satisfies DataGridColumn<AttachmentRule>] : []),
|
||||
{ id: "required", header: "i18n:govoplan-campaign.required.eed6bfb4", width: 175, sortable: true, filterable: true, columnType: "from-list", list: { options: [{ value: "required", label: "i18n:govoplan-campaign.required.eed6bfb4" }, { value: "optional", label: "i18n:govoplan-campaign.optional.0c6c4102" }] }, render: (rule, index) => <ToggleSwitch label="i18n:govoplan-campaign.required.eed6bfb4" checked={getBool(rule, "required", true)} disabled={disabled} onChange={(checked) => patchRule(index, { required: checked })} />, value: (rule) => getBool(rule, "required", true) ? "required" : "optional" },
|
||||
{
|
||||
id: "actions",
|
||||
header: "i18n:govoplan-campaign.actions.c3cd636a",
|
||||
width: 180,
|
||||
sticky: "end",
|
||||
render: (_rule, index) =>
|
||||
<DataGridRowActions
|
||||
disabled={disabled}
|
||||
onAddBelow={() => addRule(index)}
|
||||
onRemove={() => removeRule(index)}
|
||||
onMoveUp={index > 0 ? () => moveRule(index, index - 1) : undefined}
|
||||
onMoveDown={index < rules.length - 1 ? () => moveRule(index, index + 1) : undefined}
|
||||
addLabel="i18n:govoplan-campaign.add_attachment_below.c7b66ffd"
|
||||
removeLabel="i18n:govoplan-campaign.remove_attachment.0fcc6594"
|
||||
moveUpLabel="i18n:govoplan-campaign.move_attachment_up.28614804"
|
||||
moveDownLabel="i18n:govoplan-campaign.move_attachment_down.a4d6604f" />
|
||||
|
||||
|
||||
}];
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user