Close method-card Save like first add, keep key-resource chips on the approach they were chosen for, and leave expanded See-all stacks in catalog order.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -35,10 +35,6 @@ 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";
|
||||
@@ -1270,7 +1266,6 @@ 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":
|
||||
@@ -1335,18 +1330,12 @@ function seedDraftForTarget(
|
||||
const saved =
|
||||
state.decisionApproachDetailsById?.[target.overrideKey] ??
|
||||
decisionApproachPresetFor(target.overrideKey);
|
||||
const withKeys = applyDecisionApproachKeyResources(
|
||||
saved,
|
||||
selectedKeyResourceLabelsFromCheckedIds(
|
||||
state.selectedDecisionKeyResourceIds ?? [],
|
||||
),
|
||||
);
|
||||
return {
|
||||
groupKey: "decisionApproaches",
|
||||
value: {
|
||||
...withKeys,
|
||||
applicableScope: [...withKeys.applicableScope],
|
||||
selectedApplicableScope: [...withKeys.selectedApplicableScope],
|
||||
...saved,
|
||||
applicableScope: [...saved.applicableScope],
|
||||
selectedApplicableScope: [...saved.selectedApplicableScope],
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import { useMemo } from "react";
|
||||
import {
|
||||
mergeCompactCardIdsWithPinnedSelected,
|
||||
orderRankedMethodsWithPinnedSelection,
|
||||
} from "../../../../lib/create/methodCardDisplayOrder";
|
||||
import { mergeCompactCardIdsWithPinnedSelected } from "../../../../lib/create/methodCardDisplayOrder";
|
||||
import {
|
||||
deriveCompactCards,
|
||||
rankMethodsByScore,
|
||||
@@ -15,10 +12,10 @@ import {
|
||||
type MethodEntry = { id: string; label: string; supportText: string };
|
||||
|
||||
/**
|
||||
* Applies score ranking, compact-slot rules, then surfaces selected ids first in
|
||||
* `selected*Ids` order (most-recent add at index 0 via
|
||||
* {@link moveFacetSelectionIdToFront}). Selection-first applies whenever the facet
|
||||
* has any selection — not only after footer Confirm (`methodSectionsPinCommitted`).
|
||||
* Applies score ranking and compact-slot rules. Expanded CardStack order stays
|
||||
* the ranked catalog (selected cards keep their place). Compact slots still
|
||||
* pin selected ids first so a pick outside the unpinned top-N remains visible
|
||||
* when the stack is collapsed.
|
||||
*/
|
||||
export function useMethodCardDeckOrdering(
|
||||
section: RecommendationSection,
|
||||
@@ -35,16 +32,7 @@ export function useMethodCardDeckOrdering(
|
||||
);
|
||||
|
||||
const selectionShowcaseActive = selectedIds.length > 0;
|
||||
|
||||
const displayMethods = useMemo(
|
||||
() =>
|
||||
orderRankedMethodsWithPinnedSelection(
|
||||
rankedMethods,
|
||||
selectedIds,
|
||||
selectionShowcaseActive,
|
||||
),
|
||||
[rankedMethods, selectedIds, selectionShowcaseActive],
|
||||
);
|
||||
const displayMethods = rankedMethods;
|
||||
|
||||
const { compactCardIds: baseCompactCardIds, recommendedIds } = useMemo(
|
||||
() =>
|
||||
@@ -60,13 +48,13 @@ export function useMethodCardDeckOrdering(
|
||||
const compactCardIds = useMemo(
|
||||
() =>
|
||||
mergeCompactCardIdsWithPinnedSelected(
|
||||
displayMethods.map((m) => m.id),
|
||||
rankedMethods.map((m) => m.id),
|
||||
baseCompactCardIds,
|
||||
selectedIds,
|
||||
selectionShowcaseActive,
|
||||
5,
|
||||
),
|
||||
[displayMethods, baseCompactCardIds, selectedIds, selectionShowcaseActive],
|
||||
[rankedMethods, baseCompactCardIds, selectedIds, selectionShowcaseActive],
|
||||
);
|
||||
|
||||
const sampleCards = useMemo(
|
||||
|
||||
@@ -643,16 +643,9 @@ export function CommunicationMethodsScreen() {
|
||||
},
|
||||
});
|
||||
}
|
||||
if (pendingDraft) {
|
||||
customizeSnapshotRef.current = captureMethodCardCustomizeSnapshot(
|
||||
pendingDraft,
|
||||
persistWizardBlocks ? draftFieldBlocks : null,
|
||||
customizeSnapshotRef.current?.headerDraft ?? {
|
||||
title: "",
|
||||
description: "",
|
||||
},
|
||||
);
|
||||
}
|
||||
pendingEphemeralDuplicateIdRef.current = null;
|
||||
customizeSnapshotRef.current = null;
|
||||
void handleCreateModalClose();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -740,6 +733,7 @@ export function CommunicationMethodsScreen() {
|
||||
title={modalConfig.title}
|
||||
description={modalConfig.description}
|
||||
nextButtonText={modalConfig.nextButtonText}
|
||||
showBackButton={false}
|
||||
showNextButton={showMethodModalPrimary}
|
||||
backdropVariant="blurredYellow"
|
||||
kebabTriggerAriaLabel={modalKebabMenu.triggerAriaLabel}
|
||||
|
||||
@@ -644,16 +644,9 @@ export function ConflictManagementScreen() {
|
||||
},
|
||||
});
|
||||
}
|
||||
if (pendingDraft) {
|
||||
customizeSnapshotRef.current = captureMethodCardCustomizeSnapshot(
|
||||
pendingDraft,
|
||||
persistWizardBlocks ? draftFieldBlocks : null,
|
||||
customizeSnapshotRef.current?.headerDraft ?? {
|
||||
title: "",
|
||||
description: "",
|
||||
},
|
||||
);
|
||||
}
|
||||
pendingEphemeralDuplicateIdRef.current = null;
|
||||
customizeSnapshotRef.current = null;
|
||||
void handleCreateModalClose();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -741,6 +734,7 @@ export function ConflictManagementScreen() {
|
||||
title={modalConfig.title}
|
||||
description={modalConfig.description}
|
||||
nextButtonText={modalConfig.nextButtonText}
|
||||
showBackButton={false}
|
||||
showNextButton={showMethodModalPrimary}
|
||||
backdropVariant="blurredYellow"
|
||||
kebabTriggerAriaLabel={modalKebabMenu.triggerAriaLabel}
|
||||
|
||||
@@ -637,16 +637,9 @@ export function MembershipMethodsScreen() {
|
||||
},
|
||||
});
|
||||
}
|
||||
if (pendingDraft) {
|
||||
customizeSnapshotRef.current = captureMethodCardCustomizeSnapshot(
|
||||
pendingDraft,
|
||||
persistWizardBlocks ? draftFieldBlocks : null,
|
||||
customizeSnapshotRef.current?.headerDraft ?? {
|
||||
title: "",
|
||||
description: "",
|
||||
},
|
||||
);
|
||||
}
|
||||
pendingEphemeralDuplicateIdRef.current = null;
|
||||
customizeSnapshotRef.current = null;
|
||||
void handleCreateModalClose();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -734,6 +727,7 @@ export function MembershipMethodsScreen() {
|
||||
title={modalConfig.title}
|
||||
description={modalConfig.description}
|
||||
nextButtonText={modalConfig.nextButtonText}
|
||||
showBackButton={false}
|
||||
showNextButton={showMethodModalPrimary}
|
||||
backdropVariant="blurredYellow"
|
||||
kebabTriggerAriaLabel={modalKebabMenu.triggerAriaLabel}
|
||||
|
||||
@@ -35,10 +35,8 @@ import { uploadCreateFlowFile } from "../../../../../lib/create/uploadToServer";
|
||||
import { decisionApproachPresetFor } from "../../../../../lib/create/finalReviewChipPresets";
|
||||
import {
|
||||
applyDecisionApproachKeyResources,
|
||||
decisionApproachKeyResourceIdsFromLabels,
|
||||
decisionApproachKeyResourceCheckboxIds,
|
||||
selectedKeyResourceLabelsFromCheckedIds,
|
||||
stringArraysEqual,
|
||||
syncDecisionApproachKeyResourceDetails,
|
||||
} from "../../../../../lib/create/decisionApproachKeyResources";
|
||||
import type { CustomMethodCardFieldBlock } from "../../../../../lib/create/customMethodCardFieldBlocks";
|
||||
import { mergePresetMethodsWithCustom } from "../../../../../lib/create/mergePresetMethodsWithCustom";
|
||||
@@ -94,11 +92,12 @@ export function DecisionApproachesScreen() {
|
||||
>(null);
|
||||
|
||||
const selectedIds = state.selectedDecisionApproachIds ?? [];
|
||||
const messageBoxCheckedIds = state.selectedDecisionKeyResourceIds ?? [];
|
||||
const selectedKeyResourceLabels = useMemo(
|
||||
() => selectedKeyResourceLabelsFromCheckedIds(messageBoxCheckedIds),
|
||||
[messageBoxCheckedIds],
|
||||
);
|
||||
const messageBoxCheckedIds = decisionApproachKeyResourceCheckboxIds({
|
||||
detailsById: state.decisionApproachDetailsById,
|
||||
selectedApproachIds: selectedIds,
|
||||
reminderIds: state.selectedDecisionKeyResourceIds,
|
||||
openDraft: pendingDraft,
|
||||
});
|
||||
|
||||
const messageBoxItems: InfoMessageBoxItem[] = useMemo(
|
||||
() =>
|
||||
@@ -155,23 +154,16 @@ export function DecisionApproachesScreen() {
|
||||
setPendingDraft(
|
||||
applyDecisionApproachKeyResources(pendingDraft, nextLabels),
|
||||
);
|
||||
return;
|
||||
}
|
||||
updateState({
|
||||
selectedDecisionKeyResourceIds: nextCheckedIds,
|
||||
decisionApproachDetailsById: syncDecisionApproachKeyResourceDetails(
|
||||
state.decisionApproachDetailsById,
|
||||
selectedIds,
|
||||
nextLabels,
|
||||
decisionApproachPresetFor,
|
||||
),
|
||||
});
|
||||
},
|
||||
[
|
||||
markCreateFlowInteraction,
|
||||
messageBoxCheckedIds,
|
||||
pendingDraft,
|
||||
selectedIds,
|
||||
state.decisionApproachDetailsById,
|
||||
updateState,
|
||||
],
|
||||
);
|
||||
@@ -179,19 +171,12 @@ export function DecisionApproachesScreen() {
|
||||
const seedDraft = useCallback(
|
||||
(id: string): DecisionApproachDetailEntry => {
|
||||
const saved = state.decisionApproachDetailsById?.[id];
|
||||
const base = saved
|
||||
? {
|
||||
...saved,
|
||||
applicableScope: [...saved.applicableScope],
|
||||
selectedApplicableScope: [...saved.selectedApplicableScope],
|
||||
}
|
||||
: decisionApproachPresetFor(id);
|
||||
return applyDecisionApproachKeyResources(
|
||||
base,
|
||||
selectedKeyResourceLabels,
|
||||
);
|
||||
if (saved) {
|
||||
return structuredClone(saved);
|
||||
}
|
||||
return decisionApproachPresetFor(id);
|
||||
},
|
||||
[selectedKeyResourceLabels, state.decisionApproachDetailsById],
|
||||
[state.decisionApproachDetailsById],
|
||||
);
|
||||
|
||||
const handleCardSelect = useCallback(
|
||||
@@ -237,31 +222,8 @@ 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,
|
||||
messageBoxCheckedIds,
|
||||
selectedIds,
|
||||
state.decisionApproachDetailsById,
|
||||
updateState,
|
||||
],
|
||||
[markCreateFlowInteraction],
|
||||
);
|
||||
|
||||
const isSelectedCardModal =
|
||||
@@ -652,10 +614,7 @@ export function DecisionApproachesScreen() {
|
||||
},
|
||||
decisionApproachDetailsById: {
|
||||
...(state.decisionApproachDetailsById ?? {}),
|
||||
[id]: applyDecisionApproachKeyResources(
|
||||
decisionApproachPresetFor(id),
|
||||
selectedKeyResourceLabels,
|
||||
),
|
||||
[id]: decisionApproachPresetFor(id),
|
||||
},
|
||||
customMethodCardFieldBlocksById: {
|
||||
...(state.customMethodCardFieldBlocksById ?? {}),
|
||||
@@ -667,7 +626,6 @@ export function DecisionApproachesScreen() {
|
||||
markCreateFlowInteraction,
|
||||
pendingDraft,
|
||||
selectedIds,
|
||||
selectedKeyResourceLabels,
|
||||
state.customMethodCardFieldBlocksById,
|
||||
state.customMethodCardMetaById,
|
||||
state.decisionApproachDetailsById,
|
||||
@@ -687,31 +645,30 @@ export function DecisionApproachesScreen() {
|
||||
modalUsesWizardFieldBlocksBody && draftFieldBlocks !== null;
|
||||
|
||||
if (selectedIds.includes(pendingCardId)) {
|
||||
if (persistWizardBlocks) {
|
||||
updateState({
|
||||
customMethodCardFieldBlocksById: {
|
||||
...(state.customMethodCardFieldBlocksById ?? {}),
|
||||
[pendingCardId]: structuredClone(draftFieldBlocks ?? []),
|
||||
},
|
||||
});
|
||||
} else if (pendingDraft) {
|
||||
updateState({
|
||||
replaceState((prev) => {
|
||||
if (persistWizardBlocks) {
|
||||
return {
|
||||
...prev,
|
||||
customMethodCardFieldBlocksById: {
|
||||
...(prev.customMethodCardFieldBlocksById ?? {}),
|
||||
[pendingCardId]: structuredClone(draftFieldBlocks ?? []),
|
||||
},
|
||||
};
|
||||
}
|
||||
if (!pendingDraft) {
|
||||
return prev;
|
||||
}
|
||||
return {
|
||||
...prev,
|
||||
decisionApproachDetailsById: {
|
||||
...(state.decisionApproachDetailsById ?? {}),
|
||||
[pendingCardId]: pendingDraft,
|
||||
...(prev.decisionApproachDetailsById ?? {}),
|
||||
[pendingCardId]: structuredClone(pendingDraft),
|
||||
},
|
||||
});
|
||||
}
|
||||
if (pendingDraft) {
|
||||
customizeSnapshotRef.current = captureMethodCardCustomizeSnapshot(
|
||||
pendingDraft,
|
||||
persistWizardBlocks ? draftFieldBlocks : null,
|
||||
customizeSnapshotRef.current?.headerDraft ?? {
|
||||
title: "",
|
||||
description: "",
|
||||
},
|
||||
);
|
||||
}
|
||||
};
|
||||
});
|
||||
pendingEphemeralDuplicateIdRef.current = null;
|
||||
customizeSnapshotRef.current = null;
|
||||
void handleCreateModalClose();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -719,24 +676,25 @@ export function DecisionApproachesScreen() {
|
||||
void handleCreateModalClose();
|
||||
return;
|
||||
}
|
||||
updateState({
|
||||
replaceState((prev) => ({
|
||||
...prev,
|
||||
selectedDecisionApproachIds: moveFacetSelectionIdToFront(
|
||||
selectedIds,
|
||||
prev.selectedDecisionApproachIds ?? [],
|
||||
pendingCardId,
|
||||
),
|
||||
decisionApproachDetailsById: {
|
||||
...(state.decisionApproachDetailsById ?? {}),
|
||||
[pendingCardId]: pendingDraft,
|
||||
...(prev.decisionApproachDetailsById ?? {}),
|
||||
[pendingCardId]: structuredClone(pendingDraft),
|
||||
},
|
||||
...(persistWizardBlocks
|
||||
? {
|
||||
customMethodCardFieldBlocksById: {
|
||||
...(state.customMethodCardFieldBlocksById ?? {}),
|
||||
...(prev.customMethodCardFieldBlocksById ?? {}),
|
||||
[pendingCardId]: structuredClone(draftFieldBlocks ?? []),
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
});
|
||||
}));
|
||||
pendingEphemeralDuplicateIdRef.current = null;
|
||||
customizeSnapshotRef.current = null;
|
||||
void handleCreateModalClose();
|
||||
@@ -747,9 +705,8 @@ export function DecisionApproachesScreen() {
|
||||
modalUsesWizardFieldBlocksBody,
|
||||
pendingCardId,
|
||||
pendingDraft,
|
||||
replaceState,
|
||||
selectedIds,
|
||||
state,
|
||||
updateState,
|
||||
]);
|
||||
|
||||
const modalConfig = pendingCardId
|
||||
@@ -840,6 +797,7 @@ export function DecisionApproachesScreen() {
|
||||
title={modalConfig.title}
|
||||
description={modalConfig.description}
|
||||
nextButtonText={modalConfig.nextButtonText}
|
||||
showBackButton={false}
|
||||
showNextButton={showMethodModalPrimary}
|
||||
backdropVariant="blurredYellow"
|
||||
kebabTriggerAriaLabel={modalKebabMenu.triggerAriaLabel}
|
||||
|
||||
Reference in New Issue
Block a user