326 lines
11 KiB
TypeScript
326 lines
11 KiB
TypeScript
"use client";
|
|
|
|
import IncrementerBlock from "../../../components/controls/IncrementerBlock";
|
|
import { useMessages } from "../../../contexts/MessagesContext";
|
|
import { formatConflictApplicableScopeForTextarea } from "../../../../lib/create/ruleSectionsFromMethodSelections";
|
|
import { withDecisionApproachKeyResourceScopes } from "../../../../lib/create/decisionApproachKeyResources";
|
|
import type {
|
|
CommunicationMethodDetailEntry,
|
|
ConflictManagementDetailEntry,
|
|
DecisionApproachDetailEntry,
|
|
MembershipMethodDetailEntry,
|
|
} from "../types";
|
|
import ApplicableScopeField from "./ApplicableScopeField";
|
|
import type { MethodCardPolicyStep } from "./MethodCardPolicyDialog";
|
|
import ModalTextAreaField from "./ModalTextAreaField";
|
|
|
|
const CONSENSUS_LEVEL_MIN = 0;
|
|
const CONSENSUS_LEVEL_MAX = 100;
|
|
const CONSENSUS_LEVEL_STEP = 5;
|
|
|
|
function textStep(args: {
|
|
id: string;
|
|
title: string;
|
|
emptyDescription: string;
|
|
value: string;
|
|
onChange: (next: string) => void;
|
|
generateDraftText?: string;
|
|
generateDraftLabel: string;
|
|
generateDraftHint: string;
|
|
filledHint: string;
|
|
placeholder: string;
|
|
}): MethodCardPolicyStep {
|
|
const done = args.value.trim().length > 0;
|
|
return {
|
|
id: args.id,
|
|
title: args.title,
|
|
emptyDescription: args.emptyDescription,
|
|
done,
|
|
summary: args.value,
|
|
renderEditor: () => (
|
|
<ModalTextAreaField
|
|
hideLabel
|
|
label={args.title}
|
|
value={args.value}
|
|
onChange={args.onChange}
|
|
placeholder={args.placeholder}
|
|
generateDraftText={args.generateDraftText}
|
|
generateDraftLabel={args.generateDraftLabel}
|
|
generateDraftHint={args.generateDraftHint}
|
|
filledHint={args.filledHint}
|
|
autoFocus
|
|
/>
|
|
),
|
|
};
|
|
}
|
|
|
|
function usePolicyFieldCopy() {
|
|
const m = useMessages();
|
|
return {
|
|
dialog: m.create.customRule.cardDialog,
|
|
draftField: m.create.draftField,
|
|
};
|
|
}
|
|
|
|
export function useCommunicationPolicySteps(
|
|
value: CommunicationMethodDetailEntry,
|
|
onChange: (next: CommunicationMethodDetailEntry) => void,
|
|
generateDraft?: CommunicationMethodDetailEntry,
|
|
): MethodCardPolicyStep[] {
|
|
const { dialog, draftField } = usePolicyFieldCopy();
|
|
const headings = useMessages().create.customRule.communication.sectionHeadings;
|
|
const fields: Array<keyof CommunicationMethodDetailEntry> = [
|
|
"corePrinciple",
|
|
"logisticsAdmin",
|
|
"codeOfConduct",
|
|
];
|
|
return fields.map((field) =>
|
|
textStep({
|
|
id: field,
|
|
title: headings[field],
|
|
emptyDescription: dialog.emptyStepDescription,
|
|
value: value[field],
|
|
onChange: (next) => onChange({ ...value, [field]: next }),
|
|
generateDraftText: generateDraft?.[field],
|
|
generateDraftLabel: draftField.generateDraft,
|
|
generateDraftHint: draftField.generateDraftHint,
|
|
filledHint: dialog.filledHint,
|
|
placeholder: draftField.placeholder,
|
|
}),
|
|
);
|
|
}
|
|
|
|
export function useMembershipPolicySteps(
|
|
value: MembershipMethodDetailEntry,
|
|
onChange: (next: MembershipMethodDetailEntry) => void,
|
|
generateDraft?: MembershipMethodDetailEntry,
|
|
): MethodCardPolicyStep[] {
|
|
const { dialog, draftField } = usePolicyFieldCopy();
|
|
const headings = useMessages().create.customRule.membership.sectionHeadings;
|
|
const fields: Array<keyof MembershipMethodDetailEntry> = [
|
|
"eligibility",
|
|
"joiningProcess",
|
|
"expectations",
|
|
];
|
|
return fields.map((field) =>
|
|
textStep({
|
|
id: field,
|
|
title: headings[field],
|
|
emptyDescription: dialog.emptyStepDescription,
|
|
value: value[field],
|
|
onChange: (next) => onChange({ ...value, [field]: next }),
|
|
generateDraftText: generateDraft?.[field],
|
|
generateDraftLabel: draftField.generateDraft,
|
|
generateDraftHint: draftField.generateDraftHint,
|
|
filledHint: dialog.filledHint,
|
|
placeholder: draftField.placeholder,
|
|
}),
|
|
);
|
|
}
|
|
|
|
export function useConflictPolicySteps(
|
|
value: ConflictManagementDetailEntry,
|
|
onChange: (next: ConflictManagementDetailEntry) => void,
|
|
generateDraft?: ConflictManagementDetailEntry,
|
|
): MethodCardPolicyStep[] {
|
|
const { dialog, draftField } = usePolicyFieldCopy();
|
|
const t = useMessages().create.customRule.conflictManagement;
|
|
const scopeText = formatConflictApplicableScopeForTextarea(
|
|
value.selectedApplicableScope,
|
|
value.applicableScope,
|
|
);
|
|
const generateScope =
|
|
generateDraft && generateDraft.applicableScope.length > 0
|
|
? generateDraft.applicableScope.join("\n")
|
|
: generateDraft
|
|
? formatConflictApplicableScopeForTextarea(
|
|
generateDraft.selectedApplicableScope,
|
|
generateDraft.applicableScope,
|
|
)
|
|
: undefined;
|
|
|
|
return [
|
|
textStep({
|
|
id: "corePrinciple",
|
|
title: t.sectionHeadings.corePrinciple,
|
|
emptyDescription: dialog.emptyStepDescription,
|
|
value: value.corePrinciple,
|
|
onChange: (next) => onChange({ ...value, corePrinciple: next }),
|
|
generateDraftText: generateDraft?.corePrinciple,
|
|
generateDraftLabel: draftField.generateDraft,
|
|
generateDraftHint: draftField.generateDraftHint,
|
|
filledHint: dialog.filledHint,
|
|
placeholder: draftField.placeholder,
|
|
}),
|
|
textStep({
|
|
id: "applicableScope",
|
|
title: t.sectionHeadings.applicableScope,
|
|
emptyDescription: t.applicableScopePlaceholder,
|
|
value: scopeText,
|
|
onChange: (next) => {
|
|
const lines = next
|
|
.split("\n")
|
|
.map((line) => line.trim())
|
|
.filter((line) => line.length > 0);
|
|
onChange({
|
|
...value,
|
|
applicableScope: lines,
|
|
selectedApplicableScope: [...lines],
|
|
});
|
|
},
|
|
generateDraftText: generateScope,
|
|
generateDraftLabel: draftField.generateDraft,
|
|
generateDraftHint: draftField.generateDraftHint,
|
|
filledHint: dialog.filledHint,
|
|
placeholder: t.applicableScopePlaceholder,
|
|
}),
|
|
textStep({
|
|
id: "processProtocol",
|
|
title: t.sectionHeadings.processProtocol,
|
|
emptyDescription: dialog.emptyStepDescription,
|
|
value: value.processProtocol,
|
|
onChange: (next) => onChange({ ...value, processProtocol: next }),
|
|
generateDraftText: generateDraft?.processProtocol,
|
|
generateDraftLabel: draftField.generateDraft,
|
|
generateDraftHint: draftField.generateDraftHint,
|
|
filledHint: dialog.filledHint,
|
|
placeholder: draftField.placeholder,
|
|
}),
|
|
textStep({
|
|
id: "restorationFallbacks",
|
|
title: t.sectionHeadings.restorationFallbacks,
|
|
emptyDescription: dialog.emptyStepDescription,
|
|
value: value.restorationFallbacks,
|
|
onChange: (next) => onChange({ ...value, restorationFallbacks: next }),
|
|
generateDraftText: generateDraft?.restorationFallbacks,
|
|
generateDraftLabel: draftField.generateDraft,
|
|
generateDraftHint: draftField.generateDraftHint,
|
|
filledHint: dialog.filledHint,
|
|
placeholder: draftField.placeholder,
|
|
}),
|
|
];
|
|
}
|
|
|
|
export function useDecisionApproachPolicySteps(
|
|
value: DecisionApproachDetailEntry,
|
|
onChange: (next: DecisionApproachDetailEntry) => void,
|
|
generateDraft?: DecisionApproachDetailEntry,
|
|
): MethodCardPolicyStep[] {
|
|
const { dialog, draftField } = usePolicyFieldCopy();
|
|
const t = useMessages().create.customRule.decisionApproaches;
|
|
const scopes = withDecisionApproachKeyResourceScopes(value.applicableScope);
|
|
const selectedSummary = value.selectedApplicableScope.join(", ");
|
|
|
|
const consensusStep: MethodCardPolicyStep = {
|
|
id: "consensusLevel",
|
|
title: t.sectionHeadings.consensusLevel,
|
|
emptyDescription: t.consensusLevelDisplay.supermajorityShare,
|
|
done: true,
|
|
summary:
|
|
value.consensusLevel === undefined
|
|
? t.consensusLevelDisplay.supermajorityShare
|
|
: value.consensusLevel === 0
|
|
? t.consensusLevelDisplay.noSetShare
|
|
: `${value.consensusLevel}%`,
|
|
renderEditor: () =>
|
|
value.consensusLevel === undefined ? (
|
|
<ModalTextAreaField
|
|
hideLabel
|
|
label={t.sectionHeadings.consensusLevel}
|
|
value={t.consensusLevelDisplay.supermajorityShare}
|
|
onChange={() => {}}
|
|
disabled
|
|
rows={1}
|
|
/>
|
|
) : (
|
|
<IncrementerBlock
|
|
label={t.sectionHeadings.consensusLevel}
|
|
helpIcon={false}
|
|
value={value.consensusLevel}
|
|
min={CONSENSUS_LEVEL_MIN}
|
|
max={CONSENSUS_LEVEL_MAX}
|
|
step={CONSENSUS_LEVEL_STEP}
|
|
onChange={(next) => onChange({ ...value, consensusLevel: next })}
|
|
formatValue={(v) =>
|
|
v === 0 ? t.consensusLevelDisplay.noSetShare : `${v}%`
|
|
}
|
|
decrementAriaLabel={dialog.decreaseConsensusLevel}
|
|
incrementAriaLabel={dialog.increaseConsensusLevel}
|
|
/>
|
|
),
|
|
};
|
|
|
|
const scopeStep: MethodCardPolicyStep = {
|
|
id: "applicableScope",
|
|
title: t.sectionHeadings.applicableScope,
|
|
emptyDescription: dialog.scopeEmptyDescription,
|
|
done: value.selectedApplicableScope.length > 0,
|
|
summary: selectedSummary,
|
|
renderEditor: () => (
|
|
<ApplicableScopeField
|
|
label={t.sectionHeadings.applicableScope}
|
|
addLabel={t.scopeAddButtonLabel}
|
|
scopes={scopes}
|
|
selectedScopes={value.selectedApplicableScope}
|
|
onToggleScope={(scope) =>
|
|
onChange({
|
|
...value,
|
|
selectedApplicableScope: value.selectedApplicableScope.includes(
|
|
scope,
|
|
)
|
|
? value.selectedApplicableScope.filter((item) => item !== scope)
|
|
: [...value.selectedApplicableScope, scope],
|
|
})
|
|
}
|
|
onAddScope={(scope) =>
|
|
onChange({
|
|
...value,
|
|
applicableScope: [...value.applicableScope, scope],
|
|
})
|
|
}
|
|
/>
|
|
),
|
|
};
|
|
|
|
return [
|
|
textStep({
|
|
id: "corePrinciple",
|
|
title: t.sectionHeadings.corePrinciple,
|
|
emptyDescription: dialog.emptyStepDescription,
|
|
value: value.corePrinciple,
|
|
onChange: (next) => onChange({ ...value, corePrinciple: next }),
|
|
generateDraftText: generateDraft?.corePrinciple,
|
|
generateDraftLabel: draftField.generateDraft,
|
|
generateDraftHint: draftField.generateDraftHint,
|
|
filledHint: dialog.filledHint,
|
|
placeholder: draftField.placeholder,
|
|
}),
|
|
scopeStep,
|
|
textStep({
|
|
id: "stepByStepInstructions",
|
|
title: t.sectionHeadings.stepByStepInstructions,
|
|
emptyDescription: dialog.emptyStepDescription,
|
|
value: value.stepByStepInstructions,
|
|
onChange: (next) => onChange({ ...value, stepByStepInstructions: next }),
|
|
generateDraftText: generateDraft?.stepByStepInstructions,
|
|
generateDraftLabel: draftField.generateDraft,
|
|
generateDraftHint: draftField.generateDraftHint,
|
|
filledHint: dialog.filledHint,
|
|
placeholder: draftField.placeholder,
|
|
}),
|
|
consensusStep,
|
|
textStep({
|
|
id: "objectionsDeadlocks",
|
|
title: t.sectionHeadings.objectionsDeadlocks,
|
|
emptyDescription: dialog.emptyStepDescription,
|
|
value: value.objectionsDeadlocks,
|
|
onChange: (next) => onChange({ ...value, objectionsDeadlocks: next }),
|
|
generateDraftText: generateDraft?.objectionsDeadlocks,
|
|
generateDraftLabel: draftField.generateDraft,
|
|
generateDraftHint: draftField.generateDraftHint,
|
|
filledHint: dialog.filledHint,
|
|
placeholder: draftField.placeholder,
|
|
}),
|
|
];
|
|
}
|