Let decision-approaches wrap at narrow widths and sync key-resource scopes.

The info-box checkboxes overflowed in a squeezed column; wrapping them and offering those scopes as chips on every approach keeps the sidebar and Applicable Scope selection in sync without publishing unchecked keys as defaults.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
adilallo
2026-08-24 16:27:06 -06:00
co-authored by Cursor
parent 38fe0e7e9b
commit b1ca1a748e
19 changed files with 567 additions and 56 deletions
@@ -84,6 +84,7 @@ function ApplicableScopeFieldComponent({
disabled={readOnly}
onClick={() => !readOnly && onToggleScope(scope)}
ariaLabel={`${isSelected ? "Deselect" : "Select"} ${scope}`}
className="max-w-full"
/>
);
})}
@@ -35,6 +35,10 @@ import {
decisionApproachPresetFor,
membershipPresetFor,
} from "../../../../lib/create/finalReviewChipPresets";
import {
applyDecisionApproachKeyResources,
selectedKeyResourceLabelsFromCheckedIds,
} from "../../../../lib/create/decisionApproachKeyResources";
import { isCustomMethodCardId } from "../../../../lib/create/isCustomMethodCardId";
import { usesWizardFieldBlocksModalBody } from "../../../../lib/create/usesWizardFieldBlocksModalBody";
import type { CustomMethodCardFieldBlock } from "../../../../lib/create/customMethodCardFieldBlocks";
@@ -1266,6 +1270,7 @@ function facetSeedSignature(
return JSON.stringify({
meta: state.customMethodCardMetaById?.[id] ?? null,
details: state.decisionApproachDetailsById?.[id] ?? null,
keyResources: state.selectedDecisionKeyResourceIds ?? null,
blocks: state.customMethodCardFieldBlocksById?.[id] ?? null,
});
case "conflictManagement":
@@ -1330,12 +1335,18 @@ function seedDraftForTarget(
const saved =
state.decisionApproachDetailsById?.[target.overrideKey] ??
decisionApproachPresetFor(target.overrideKey);
const withKeys = applyDecisionApproachKeyResources(
saved,
selectedKeyResourceLabelsFromCheckedIds(
state.selectedDecisionKeyResourceIds ?? [],
),
);
return {
groupKey: "decisionApproaches",
value: {
...saved,
applicableScope: [...saved.applicableScope],
selectedApplicableScope: [...saved.selectedApplicableScope],
...withKeys,
applicableScope: [...withKeys.applicableScope],
selectedApplicableScope: [...withKeys.selectedApplicableScope],
},
};
}
@@ -7,12 +7,13 @@
* `markCreateFlowInteraction` live in the parent.
*/
import { memo, useCallback } from "react";
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;
@@ -32,6 +33,11 @@ function DecisionApproachEditFieldsComponent({
const m = useMessages();
const t = m.create.customRule.decisionApproaches;
const scopes = useMemo(
() => withDecisionApproachKeyResourceScopes(value.applicableScope),
[value.applicableScope],
);
const patch = useCallback(
<K extends keyof DecisionApproachDetailEntry>(
key: K,
@@ -53,7 +59,7 @@ function DecisionApproachEditFieldsComponent({
<ApplicableScopeField
label={t.sectionHeadings.applicableScope}
addLabel={t.scopeAddButtonLabel}
scopes={value.applicableScope}
scopes={scopes}
selectedScopes={value.selectedApplicableScope}
readOnly={readOnly}
onToggleScope={(scope) =>
@@ -33,6 +33,13 @@ import { DecisionApproachEditFields } from "../../components/methodEditFields";
import CustomMethodCardWizard from "../../components/CustomMethodCardWizard";
import { uploadCreateFlowFile } from "../../../../../lib/create/uploadToServer";
import { decisionApproachPresetFor } from "../../../../../lib/create/finalReviewChipPresets";
import {
applyDecisionApproachKeyResources,
decisionApproachKeyResourceIdsFromLabels,
selectedKeyResourceLabelsFromCheckedIds,
stringArraysEqual,
syncDecisionApproachKeyResourceDetails,
} from "../../../../../lib/create/decisionApproachKeyResources";
import type { CustomMethodCardFieldBlock } from "../../../../../lib/create/customMethodCardFieldBlocks";
import { mergePresetMethodsWithCustom } from "../../../../../lib/create/mergePresetMethodsWithCustom";
import { moveFacetSelectionIdToFront } from "../../../../../lib/create/methodCardSelectionOrder";
@@ -71,9 +78,6 @@ export function DecisionApproachesScreen() {
const customizeSnapshotRef = useRef<
MethodCardCustomizeSnapshot<DecisionApproachDetailEntry> | null
>(null);
const [messageBoxCheckedIds, setMessageBoxCheckedIds] = useState<string[]>(
[],
);
const [expanded, setExpanded] = useState(false);
const [createModalOpen, setCreateModalOpen] = useState(false);
const [pendingCardId, setPendingCardId] = useState<string | null>(null);
@@ -90,6 +94,11 @@ export function DecisionApproachesScreen() {
>(null);
const selectedIds = state.selectedDecisionApproachIds ?? [];
const messageBoxCheckedIds = state.selectedDecisionKeyResourceIds ?? [];
const selectedKeyResourceLabels = useMemo(
() => selectedKeyResourceLabelsFromCheckedIds(messageBoxCheckedIds),
[messageBoxCheckedIds],
);
const messageBoxItems: InfoMessageBoxItem[] = useMemo(
() =>
@@ -137,26 +146,52 @@ export function DecisionApproachesScreen() {
const handleMessageBoxCheckboxChange = useCallback(
(id: string, checked: boolean) => {
markCreateFlowInteraction();
setMessageBoxCheckedIds((prev) =>
checked ? [...prev, id] : prev.filter((x) => x !== id),
);
const nextCheckedIds = checked
? [...messageBoxCheckedIds, id]
: messageBoxCheckedIds.filter((x) => x !== id);
const nextLabels =
selectedKeyResourceLabelsFromCheckedIds(nextCheckedIds);
if (pendingDraft) {
setPendingDraft(
applyDecisionApproachKeyResources(pendingDraft, nextLabels),
);
}
updateState({
selectedDecisionKeyResourceIds: nextCheckedIds,
decisionApproachDetailsById: syncDecisionApproachKeyResourceDetails(
state.decisionApproachDetailsById,
selectedIds,
nextLabels,
decisionApproachPresetFor,
),
});
},
[markCreateFlowInteraction],
[
markCreateFlowInteraction,
messageBoxCheckedIds,
pendingDraft,
selectedIds,
state.decisionApproachDetailsById,
updateState,
],
);
const seedDraft = useCallback(
(id: string): DecisionApproachDetailEntry => {
const saved = state.decisionApproachDetailsById?.[id];
if (saved) {
return {
...saved,
applicableScope: [...saved.applicableScope],
selectedApplicableScope: [...saved.selectedApplicableScope],
};
}
return decisionApproachPresetFor(id);
const base = saved
? {
...saved,
applicableScope: [...saved.applicableScope],
selectedApplicableScope: [...saved.selectedApplicableScope],
}
: decisionApproachPresetFor(id);
return applyDecisionApproachKeyResources(
base,
selectedKeyResourceLabels,
);
},
[state.decisionApproachDetailsById],
[selectedKeyResourceLabels, state.decisionApproachDetailsById],
);
const handleCardSelect = useCallback(
@@ -202,8 +237,31 @@ export function DecisionApproachesScreen() {
(next: DecisionApproachDetailEntry) => {
markCreateFlowInteraction();
setPendingDraft(next);
const nextCheckedIds = decisionApproachKeyResourceIdsFromLabels(
next.selectedApplicableScope,
);
if (stringArraysEqual(nextCheckedIds, messageBoxCheckedIds)) {
return;
}
const nextLabels =
selectedKeyResourceLabelsFromCheckedIds(nextCheckedIds);
updateState({
selectedDecisionKeyResourceIds: nextCheckedIds,
decisionApproachDetailsById: syncDecisionApproachKeyResourceDetails(
state.decisionApproachDetailsById,
selectedIds,
nextLabels,
decisionApproachPresetFor,
),
});
},
[markCreateFlowInteraction],
[
markCreateFlowInteraction,
messageBoxCheckedIds,
selectedIds,
state.decisionApproachDetailsById,
updateState,
],
);
const isSelectedCardModal =
@@ -594,7 +652,10 @@ export function DecisionApproachesScreen() {
},
decisionApproachDetailsById: {
...(state.decisionApproachDetailsById ?? {}),
[id]: decisionApproachPresetFor(id),
[id]: applyDecisionApproachKeyResources(
decisionApproachPresetFor(id),
selectedKeyResourceLabels,
),
},
customMethodCardFieldBlocksById: {
...(state.customMethodCardFieldBlocksById ?? {}),
@@ -606,6 +667,7 @@ export function DecisionApproachesScreen() {
markCreateFlowInteraction,
pendingDraft,
selectedIds,
selectedKeyResourceLabels,
state.customMethodCardFieldBlocksById,
state.customMethodCardMetaById,
state.decisionApproachDetailsById,
+6
View File
@@ -165,6 +165,12 @@ export interface CreateFlowState {
>;
membershipMethodDetailsById?: Record<string, MembershipMethodDetailEntry>;
decisionApproachDetailsById?: Record<string, DecisionApproachDetailEntry>;
/**
* Checked “key resource” ids from the decision-approaches InfoMessageBox
* (`amend`, `finances`, `project`, `discipline`). Selecting one also selects
* the matching Applicable Scope chip on every decision approach.
*/
selectedDecisionKeyResourceIds?: string[];
conflictManagementDetailsById?: Record<
string,
ConflictManagementDetailEntry