Show seven cards on the four method decks and open each as a routing overlay of its parts, so groups write from empty fields with Generate draft, search the full deck, and see unselected scope chips with enough contrast.
This commit is contained in:
1 parent
dea7dd7fbd
commit
46656f52d3
28 files changed
+1349
-318
No files matched your search
@@ -0,0 +1,325 @@
|
||||
"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,
|
||||
}),
|
||||
];
|
||||
}
|
||||
Reference in new issue
Block a user