Files
community-rule/app/(app)/create/components/methodEditFields/DecisionApproachEditFields.tsx
T
adilalloandCursor 20ea4a2f30 Give MultiSelect chips accessible names and start creation fields empty.
Catalog chips now announce Select or Deselect so they are named for assistive tech. Method and value modals open blank; Generate draft inserts the catalog copy that used to be prefilled, and Customize still keeps the section headings.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-29 15:59:00 -06:00

128 lines
4.4 KiB
TypeScript

"use client";
/**
* Controlled section editor for a decision-approach chip. Used by both the
* custom-rule `decision-approaches` add-method modal and the `final-review`
* chip edit modal. Caller owns draft state — Confirm/Save persistence and
* `markCreateFlowInteraction` live in the parent.
*/
import { memo, useCallback, useMemo } from "react";
import { useMessages } from "../../../../contexts/MessagesContext";
import ModalTextAreaField from "../ModalTextAreaField";
import ApplicableScopeField from "../ApplicableScopeField";
import IncrementerBlock from "../../../../components/controls/IncrementerBlock";
import type { DecisionApproachDetailEntry } from "../../types";
import { withDecisionApproachKeyResourceScopes } from "../../../../../lib/create/decisionApproachKeyResources";
export interface DecisionApproachEditFieldsProps {
value: DecisionApproachDetailEntry;
onChange: (_next: DecisionApproachDetailEntry) => void;
readOnly?: boolean;
/** Catalog copy inserted by Generate draft on text fields. */
generateDraft?: DecisionApproachDetailEntry;
}
const CONSENSUS_LEVEL_MIN = 0;
const CONSENSUS_LEVEL_MAX = 100;
const CONSENSUS_LEVEL_STEP = 5;
function DecisionApproachEditFieldsComponent({
value,
onChange,
readOnly = false,
generateDraft,
}: DecisionApproachEditFieldsProps) {
const m = useMessages();
const t = m.create.customRule.decisionApproaches;
const draftField = m.create.draftField;
const scopes = useMemo(
() => withDecisionApproachKeyResourceScopes(value.applicableScope),
[value.applicableScope],
);
const patch = useCallback(
<K extends keyof DecisionApproachDetailEntry>(
key: K,
next: DecisionApproachDetailEntry[K],
) => {
onChange({ ...value, [key]: next });
},
[value, onChange],
);
return (
<div className="flex flex-col gap-6">
<ModalTextAreaField
label={t.sectionHeadings.corePrinciple}
value={value.corePrinciple}
onChange={(v) => patch("corePrinciple", v)}
disabled={readOnly}
placeholder={draftField.placeholder}
generateDraftText={generateDraft?.corePrinciple}
generateDraftLabel={draftField.generateDraft}
generateDraftHint={draftField.generateDraftHint}
/>
<ApplicableScopeField
label={t.sectionHeadings.applicableScope}
addLabel={t.scopeAddButtonLabel}
scopes={scopes}
selectedScopes={value.selectedApplicableScope}
readOnly={readOnly}
onToggleScope={(scope) =>
patch(
"selectedApplicableScope",
value.selectedApplicableScope.includes(scope)
? value.selectedApplicableScope.filter((s) => s !== scope)
: [...value.selectedApplicableScope, scope],
)
}
onAddScope={(scope) =>
patch("applicableScope", [...value.applicableScope, scope])
}
/>
<ModalTextAreaField
label={t.sectionHeadings.stepByStepInstructions}
value={value.stepByStepInstructions}
onChange={(v) => patch("stepByStepInstructions", v)}
disabled={readOnly}
placeholder={draftField.placeholder}
generateDraftText={generateDraft?.stepByStepInstructions}
generateDraftLabel={draftField.generateDraft}
generateDraftHint={draftField.generateDraftHint}
/>
{value.consensusLevel !== undefined ? (
<IncrementerBlock
label={t.sectionHeadings.consensusLevel}
helpIcon={false}
value={value.consensusLevel}
min={CONSENSUS_LEVEL_MIN}
max={CONSENSUS_LEVEL_MAX}
step={CONSENSUS_LEVEL_STEP}
onChange={(next) => patch("consensusLevel", next)}
formatValue={(v) => `${v}%`}
decrementAriaLabel="Decrease consensus level"
incrementAriaLabel="Increase consensus level"
disabled={readOnly}
/>
) : null}
<ModalTextAreaField
label={t.sectionHeadings.objectionsDeadlocks}
value={value.objectionsDeadlocks}
onChange={(v) => patch("objectionsDeadlocks", v)}
disabled={readOnly}
placeholder={draftField.placeholder}
generateDraftText={generateDraft?.objectionsDeadlocks}
generateDraftLabel={draftField.generateDraft}
generateDraftHint={draftField.generateDraftHint}
/>
</div>
);
}
DecisionApproachEditFieldsComponent.displayName =
"DecisionApproachEditFields";
export default memo(DecisionApproachEditFieldsComponent);