Add approval template revision administration
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import { Plus, Trash2 } from "lucide-react";
|
||||
import { useMemo, useState } from "react";
|
||||
import { Button, Dialog, DocumentationHelpLink, DismissibleAlert, FormField, IconButton, ToggleSwitch, useUnsavedChanges, useUnsavedDraftGuard, type ApiSettings } from "@govoplan/core-webui";
|
||||
import { createApproval, type ApprovalDraft, type ApprovalRequest, type ApprovalStep } from "../../api/approvals";
|
||||
import { Button, Dialog, DocumentationHelpLink, DismissibleAlert, FormField, ToggleSwitch, useUnsavedChanges, useUnsavedDraftGuard, type ApiSettings } from "@govoplan/core-webui";
|
||||
import { createApproval, type ApprovalDraft, type ApprovalRequest } from "../../api/approvals";
|
||||
import ApprovalStepsEditor, { emptyApprovalStep } from "./ApprovalStepsEditor";
|
||||
import { APPROVALS_FIELD_DOCUMENTATION, APPROVALS_I18N } from "./interfacePatterns";
|
||||
|
||||
export default function ApprovalRequestDialog({ settings, onClose, onSaved }: { settings: ApiSettings; onClose: () => void; onSaved: (value: ApprovalRequest) => void }) {
|
||||
@@ -41,10 +41,6 @@ export default function ApprovalRequestDialog({ settings, onClose, onSaved }: {
|
||||
else onClose();
|
||||
}
|
||||
|
||||
function patchStep(index: number, patch: Partial<ApprovalStep>) {
|
||||
setDraft((current) => ({ ...current, steps: current.steps.map((item, itemIndex) => itemIndex === index ? { ...item, ...patch } : item) }));
|
||||
}
|
||||
|
||||
return <Dialog open title="New Approval request" onClose={requestClose} closeDisabled={busy} portal className="approval-request-dialog" footer={<><Button onClick={requestClose} disabled={busy} disabledReason={busy ? APPROVALS_I18N.busy : undefined}>Cancel</Button><Button variant="primary" disabled={busy || !valid} disabledReason={busy ? APPROVALS_I18N.busy : !valid ? APPROVALS_I18N.incomplete : undefined} onClick={() => void save()}>{busy ? "Creating" : "Create request"}</Button></>}>
|
||||
<div className="approval-editor">
|
||||
<div className="approval-editor-help"><DocumentationHelpLink reference={APPROVALS_FIELD_DOCUMENTATION} /></div>
|
||||
@@ -60,28 +56,13 @@ export default function ApprovalRequestDialog({ settings, onClose, onSaved }: {
|
||||
<ToggleSwitch label="Separate requester and approver" checked={draft.separation_of_duties} disabled={busy} onChange={(value) => setDraft({ ...draft, separation_of_duties: value })} />
|
||||
<ToggleSwitch label="Different actor for every step" checked={draft.unique_actors_across_steps} disabled={busy} onChange={(value) => setDraft({ ...draft, unique_actors_across_steps: value })} />
|
||||
</div>
|
||||
<div className="approval-editor-heading"><h3>Steps</h3><Button disabled={busy} disabledReason={busy ? APPROVALS_I18N.busy : undefined} onClick={() => setDraft({ ...draft, steps: [...draft.steps, emptyStep(draft.steps.length + 1)] })}><Plus size={16} aria-hidden="true" />Add step</Button></div>
|
||||
<div className="approval-step-list">
|
||||
{draft.steps.map((step, index) => <div className="approval-step-editor" key={`${index}:${step.key}`}>
|
||||
<FormField label="Key"><input value={step.key} disabled={busy} onChange={(event) => patchStep(index, { key: event.target.value })} /></FormField>
|
||||
<FormField label="Label"><input value={step.label} disabled={busy} onChange={(event) => patchStep(index, { label: event.target.value })} /></FormField>
|
||||
<FormField label="Actor type"><select value={step.selectors[0].kind} disabled={busy} onChange={(event) => patchStep(index, { selectors: [{ ...step.selectors[0], kind: event.target.value as ApprovalStep["selectors"][number]["kind"] }] })}><option value="account">Account</option><option value="group">Group</option><option value="role">Role</option><option value="function_assignment">Function assignment</option><option value="any_account">Any account</option></select></FormField>
|
||||
<FormField label="Actor value"><input value={step.selectors[0].value} disabled={busy} onChange={(event) => patchStep(index, { selectors: [{ ...step.selectors[0], value: event.target.value }] })} /></FormField>
|
||||
<FormField label="Required"><input type="number" min={1} value={step.required_approvals} disabled={busy} onChange={(event) => patchStep(index, { required_approvals: Number(event.target.value) })} /></FormField>
|
||||
<ToggleSwitch label="Signature" checked={step.signature_required} disabled={busy} onChange={(value) => patchStep(index, { signature_required: value })} />
|
||||
<IconButton label={`Remove ${step.label || "step"}`} icon={<Trash2 size={16} />} variant="danger" disabled={busy || draft.steps.length === 1} disabledReason={busy ? APPROVALS_I18N.busy : draft.steps.length === 1 ? APPROVALS_I18N.oneStep : undefined} onClick={() => setDraft({ ...draft, steps: draft.steps.filter((_, itemIndex) => itemIndex !== index) })} />
|
||||
</div>)}
|
||||
</div>
|
||||
<ApprovalStepsEditor steps={draft.steps} disabled={busy} onChange={(steps) => setDraft({ ...draft, steps })} />
|
||||
</div>
|
||||
</Dialog>;
|
||||
}
|
||||
|
||||
function emptyStep(index: number): ApprovalStep {
|
||||
return { key: `step-${index}`, label: "", selectors: [{ kind: "account", value: "" }], required_approvals: 1, rejection_policy: "fail_fast", signature_required: false, forbidden_evidence_roles: [], metadata: {} };
|
||||
}
|
||||
|
||||
function initialDraft(): ApprovalDraft {
|
||||
return { title: "", description: "", subject_module: "", subject_type: "", subject_id: "", subject_version: "", subject_digest: "", steps: [emptyStep(1)], separation_of_duties: true, unique_actors_across_steps: false, expires_at: null, policy_refs: [], evidence_actors: {}, template_id: null, template_revision: null, metadata: {} };
|
||||
return { title: "", description: "", subject_module: "", subject_type: "", subject_id: "", subject_version: "", subject_digest: "", steps: [emptyApprovalStep(1)], separation_of_duties: true, unique_actors_across_steps: false, expires_at: null, policy_refs: [], evidence_actors: {}, template_id: null, template_revision: null, metadata: {} };
|
||||
}
|
||||
|
||||
function draftKey(draft: ApprovalDraft): string {
|
||||
|
||||
Reference in New Issue
Block a user