Files
community-rule/app/(app)/create/screens/right-rail/DecisionApproachesScreen.tsx
T

861 lines
28 KiB
TypeScript

"use client";
/**
* `decision-approaches` step — Figma “Flow — Right Rail” (node `20523-23509`).
* Registry: `CREATE_FLOW_SCREEN_REGISTRY["decision-approaches"]` (`layoutKind: "right-rail"`).
*
* Layout matches {@link CreateFlowTwoColumnSelectShell}: one column below `lg` (1024px), two columns
* at `lg+` with a scrollable rail — same breakpoint and height chain as select steps, distinct content.
*
* Card click opens the Figma "Add Approach" create modal (node `20870-72155`) with five controls
* rendered by {@link DecisionApproachEditFields}: Core Principle, Applicable Scope, Step-by-Step
* Instructions, Consensus Level, and Objections & Deadlocks. The same field set is reused on
* `/create/final-review` — see `FinalReviewChipEditModal`. Confirm persists both the chip
* selection and any user edits as a `decisionApproachDetailsById[id]` override; section
* defaults come from `messages/en/create/customRule/decisionApproaches.json` and will be
* replaced with DB-driven content.
*/
import { useState, useCallback, useMemo, useRef } from "react";
import CardStack from "../../../../components/cards/CardStack";
import HeaderLockup from "../../../../components/type/HeaderLockup";
import Create from "../../../../components/modals/Create";
import InlineTextButton from "../../../../components/buttons/InlineTextButton";
import InfoMessageBox from "../../../../components/controls/InfoMessageBox";
import type { InfoMessageBoxItem } from "../../../../components/controls/InfoMessageBox/InfoMessageBox.types";
import { useMessages } from "../../../../contexts/MessagesContext";
import { useCreateFlow } from "../../context/CreateFlowContext";
import { useCreateFlowMdUp } from "../../hooks/useCreateFlowMdUp";
import { useBeforeUnloadGuard } from "../../../../hooks/useBeforeUnloadGuard";
import { useDiscardCustomizeConfirm } from "../../hooks/useDiscardCustomizeConfirm";
import { useMethodCardDeckOrdering } from "../../hooks/useMethodCardDeckOrdering";
import { CreateFlowTwoColumnSelectShell } from "../../components/CreateFlowTwoColumnSelectShell";
import { DecisionApproachEditFields } from "../../components/methodEditFields";
import CustomMethodCardWizard from "../../components/CustomMethodCardWizard";
import { uploadCreateFlowFile } from "../../../../../lib/create/uploadToServer";
import { decisionApproachPresetFor } from "../../../../../lib/create/finalReviewChipPresets";
import {
applyDecisionApproachKeyResources,
decisionApproachKeyResourceCheckboxIds,
selectedKeyResourceLabelsFromCheckedIds,
} from "../../../../../lib/create/decisionApproachKeyResources";
import type { CustomMethodCardFieldBlock } from "../../../../../lib/create/customMethodCardFieldBlocks";
import { mergePresetMethodsWithCustom } from "../../../../../lib/create/mergePresetMethodsWithCustom";
import { moveFacetSelectionIdToFront } from "../../../../../lib/create/methodCardSelectionOrder";
import { isCustomMethodCardId } from "../../../../../lib/create/isCustomMethodCardId";
import { decisionApproachFacetMatchesPreset } from "../../../../../lib/create/methodCardFacetMatchesPresetForId";
import { usesWizardFieldBlocksModalBody } from "../../../../../lib/create/usesWizardFieldBlocksModalBody";
import { removeMethodCardFromFacetSelection } from "../../../../../lib/create/removeMethodCardFromFacetSelection";
import {
cloneMethodCardBlocksForDuplicate,
cloneMethodCardDetailsForDuplicate,
duplicateMethodCardTitle,
forkMethodCardFacetMapsForDuplicate,
omitIdFromStringRecord,
} from "../../../../../lib/create/duplicateMethodCardModalDraft";
import type { DecisionApproachDetailEntry } from "../../types";
import CustomMethodCardModalBody from "../../components/CustomMethodCardModalBody";
import { buildCustomRuleModalKebabMenu } from "../../components/customRuleModalKebabMenu";
import { methodCardMetaWithCustomizeHeader } from "../../../../../lib/create/methodCardCustomizeMetaPatch";
import { buildMethodCardWizardInitialValues } from "../../../../../lib/create/methodCardWizardPrefill";
import type { MethodCardWizardInitialValues } from "../../../../../lib/create/methodCardWizardPrefill";
import {
captureMethodCardCustomizeSnapshot,
isMethodCardCustomizeUnloadBlocked,
type MethodCardCustomizeSnapshot,
type MethodCardHeaderDraft,
} from "../../../../../lib/create/methodCardCustomizeSession";
export function DecisionApproachesScreen() {
const m = useMessages();
const da = m.create.customRule.decisionApproaches;
const modalKebabMenu = m.create.customRule.modalKebabMenu;
const mdUp = useCreateFlowMdUp();
const { confirmDiscard, confirmDialog } = useDiscardCustomizeConfirm();
const { state, updateState, replaceState, markCreateFlowInteraction } =
useCreateFlow();
const pendingEphemeralDuplicateIdRef = useRef<string | null>(null);
const customizeSnapshotRef = useRef<
MethodCardCustomizeSnapshot<DecisionApproachDetailEntry> | null
>(null);
const [expanded, setExpanded] = useState(false);
const [createModalOpen, setCreateModalOpen] = useState(false);
const [pendingCardId, setPendingCardId] = useState<string | null>(null);
const [pendingDraft, setPendingDraft] =
useState<DecisionApproachDetailEntry | null>(null);
const [addCustomWizardOpen, setAddCustomWizardOpen] = useState(false);
const [wizardCustomizeCardId, setWizardCustomizeCardId] = useState<
string | null
>(null);
const [wizardInitialValues, setWizardInitialValues] =
useState<MethodCardWizardInitialValues | null>(null);
const [draftFieldBlocks, setDraftFieldBlocks] = useState<
CustomMethodCardFieldBlock[] | null
>(null);
useBeforeUnloadGuard(
isMethodCardCustomizeUnloadBlocked(
createModalOpen,
customizeSnapshotRef.current,
pendingDraft,
draftFieldBlocks,
customizeSnapshotRef.current?.headerDraft ?? null,
),
);
const selectedIds = state.selectedDecisionApproachIds ?? [];
const messageBoxCheckedIds = decisionApproachKeyResourceCheckboxIds({
detailsById: state.decisionApproachDetailsById,
selectedApproachIds: selectedIds,
reminderIds: state.selectedDecisionKeyResourceIds,
openDraft: pendingDraft,
});
const messageBoxItems: InfoMessageBoxItem[] = useMemo(
() =>
da.messageBox.items.map((item) => ({
id: item.id,
label: item.label,
})),
[da.messageBox.items],
);
const mergedMethods = useMemo(
() =>
mergePresetMethodsWithCustom(
da.methods,
selectedIds,
state.customMethodCardMetaById,
),
[da.methods, selectedIds, state.customMethodCardMetaById],
);
const { sampleCards, compactCardIds, methodById, recommendationsReady } =
useMethodCardDeckOrdering(
"decisionApproaches",
mergedMethods,
selectedIds,
);
const handleOpenAddWizard = useCallback(() => {
markCreateFlowInteraction();
setWizardCustomizeCardId(null);
setWizardInitialValues(null);
setAddCustomWizardOpen(true);
}, [markCreateFlowInteraction]);
const sidebarDescription = (
<>
{da.sidebar.descriptionBefore}
<InlineTextButton onClick={handleOpenAddWizard}>
{da.sidebar.descriptionLinkLabel}
</InlineTextButton>
{da.sidebar.descriptionAfter}
</>
);
const handleMessageBoxCheckboxChange = useCallback(
(id: string, checked: boolean) => {
markCreateFlowInteraction();
const nextCheckedIds = checked
? [...messageBoxCheckedIds, id]
: messageBoxCheckedIds.filter((x) => x !== id);
const nextLabels =
selectedKeyResourceLabelsFromCheckedIds(nextCheckedIds);
if (pendingDraft) {
setPendingDraft(
applyDecisionApproachKeyResources(pendingDraft, nextLabels),
);
return;
}
updateState({
selectedDecisionKeyResourceIds: nextCheckedIds,
});
},
[
markCreateFlowInteraction,
messageBoxCheckedIds,
pendingDraft,
updateState,
],
);
const seedDraft = useCallback(
(id: string): DecisionApproachDetailEntry => {
const saved = state.decisionApproachDetailsById?.[id];
if (saved) {
return structuredClone(saved);
}
return decisionApproachPresetFor(id);
},
[state.decisionApproachDetailsById],
);
const handleCardSelect = useCallback(
(id: string) => {
markCreateFlowInteraction();
const draft = seedDraft(id);
const persistedBlocks = state.customMethodCardFieldBlocksById?.[id];
const initialBlocks =
Array.isArray(persistedBlocks) && persistedBlocks.length > 0
? structuredClone(persistedBlocks)
: null;
const method = methodById.get(id);
const meta = state.customMethodCardMetaById?.[id];
const headerDraft: MethodCardHeaderDraft = {
title: meta?.label ?? method?.label ?? da.confirmModal.title,
description:
meta?.supportText ??
method?.supportText ??
da.confirmModal.description,
};
customizeSnapshotRef.current = captureMethodCardCustomizeSnapshot(
draft,
initialBlocks,
headerDraft,
);
setPendingCardId(id);
setPendingDraft(draft);
setDraftFieldBlocks(initialBlocks);
setCreateModalOpen(true);
},
[
da.confirmModal.description,
da.confirmModal.title,
markCreateFlowInteraction,
methodById,
seedDraft,
state.customMethodCardFieldBlocksById,
state.customMethodCardMetaById,
],
);
const handleDraftChange = useCallback(
(next: DecisionApproachDetailEntry) => {
markCreateFlowInteraction();
setPendingDraft(next);
},
[markCreateFlowInteraction],
);
const isSelectedCardModal =
pendingCardId !== null && selectedIds.includes(pendingCardId);
const showMethodModalPrimary = true;
const customFacetDetailsMatchPreset = useMemo(() => {
if (!pendingCardId || !pendingDraft) return false;
if (!isCustomMethodCardId(pendingCardId, state.customMethodCardMetaById)) {
return false;
}
return decisionApproachFacetMatchesPreset(pendingDraft, pendingCardId);
}, [
pendingCardId,
pendingDraft,
state.customMethodCardMetaById,
]);
const modalUsesWizardFieldBlocksBody = useMemo(
() =>
Boolean(
pendingCardId &&
usesWizardFieldBlocksModalBody({
methodId: pendingCardId,
meta: state.customMethodCardMetaById,
fieldBlocksById: state.customMethodCardFieldBlocksById,
modalEditUnlocked: true,
draftFieldBlocks,
customFacetDetailsMatchPreset,
}),
),
[
customFacetDetailsMatchPreset,
draftFieldBlocks,
pendingCardId,
state.customMethodCardFieldBlocksById,
state.customMethodCardMetaById,
],
);
const handleCreateModalClose = useCallback(async () => {
if (
!(await confirmDiscard(
true,
customizeSnapshotRef.current,
pendingDraft,
draftFieldBlocks,
customizeSnapshotRef.current?.headerDraft ?? null,
))
) {
return;
}
customizeSnapshotRef.current = null;
const ephemeralId = pendingEphemeralDuplicateIdRef.current;
if (ephemeralId) {
pendingEphemeralDuplicateIdRef.current = null;
replaceState((prev) => ({
...prev,
customMethodCardMetaById: omitIdFromStringRecord(
prev.customMethodCardMetaById,
ephemeralId,
),
decisionApproachDetailsById: omitIdFromStringRecord(
prev.decisionApproachDetailsById,
ephemeralId,
),
customMethodCardFieldBlocksById: omitIdFromStringRecord(
prev.customMethodCardFieldBlocksById,
ephemeralId,
),
}));
}
setCreateModalOpen(false);
setPendingCardId(null);
setPendingDraft(null);
setDraftFieldBlocks(null);
}, [
confirmDiscard,
draftFieldBlocks,
pendingDraft,
replaceState,
]);
const handleRemoveSelectedFromModal = useCallback(async () => {
if (!pendingCardId || !selectedIds.includes(pendingCardId)) {
return;
}
markCreateFlowInteraction();
if (
!(await confirmDiscard(
true,
customizeSnapshotRef.current,
pendingDraft,
draftFieldBlocks,
customizeSnapshotRef.current?.headerDraft ?? null,
))
) {
return;
}
customizeSnapshotRef.current = null;
updateState(
removeMethodCardFromFacetSelection(
state,
"decisionApproaches",
pendingCardId,
),
);
await handleCreateModalClose();
}, [
confirmDiscard,
draftFieldBlocks,
handleCreateModalClose,
markCreateFlowInteraction,
pendingDraft,
pendingCardId,
selectedIds,
state,
updateState,
]);
const handleCustomize = useCallback(() => {
markCreateFlowInteraction();
if (!pendingCardId) {
return;
}
const method = methodById.get(pendingCardId);
setWizardInitialValues(
buildMethodCardWizardInitialValues({
cardId: pendingCardId,
fallbackTitle: method?.label ?? da.confirmModal.title,
fallbackDescription:
method?.supportText ?? da.confirmModal.description,
meta: state.customMethodCardMetaById,
persistedBlocks: state.customMethodCardFieldBlocksById,
draftFieldBlocks,
facetPrefill: pendingDraft
? {
group: "decisionApproaches",
draft: pendingDraft,
headings: da.sectionHeadings,
}
: undefined,
}),
);
setWizardCustomizeCardId(pendingCardId);
setCreateModalOpen(false);
setAddCustomWizardOpen(true);
}, [
da.confirmModal.description,
da.confirmModal.title,
da.sectionHeadings,
draftFieldBlocks,
markCreateFlowInteraction,
methodById,
pendingCardId,
pendingDraft,
state.customMethodCardFieldBlocksById,
state.customMethodCardMetaById,
]);
const handleDuplicateCustomCard = useCallback(() => {
if (
!pendingCardId ||
!isCustomMethodCardId(pendingCardId, state.customMethodCardMetaById)
) {
return;
}
markCreateFlowInteraction();
const newId = crypto.randomUUID();
const meta = state.customMethodCardMetaById![pendingCardId]!;
const detailsClone = cloneMethodCardDetailsForDuplicate(
pendingDraft,
state.decisionApproachDetailsById?.[pendingCardId],
() => decisionApproachPresetFor(newId),
);
const blocksClone = structuredClone(
draftFieldBlocks !== null
? draftFieldBlocks
: cloneMethodCardBlocksForDuplicate(
state.customMethodCardFieldBlocksById,
pendingCardId,
),
);
const suffix = modalKebabMenu.duplicateTitleSuffix;
const priorEphemeral = pendingEphemeralDuplicateIdRef.current;
const maps = forkMethodCardFacetMapsForDuplicate({
customMethodCardMetaById: state.customMethodCardMetaById,
facetDetailsById: state.decisionApproachDetailsById,
customMethodCardFieldBlocksById: state.customMethodCardFieldBlocksById,
omitId: priorEphemeral,
});
maps.customMethodCardMetaById[newId] = {
label: duplicateMethodCardTitle(meta.label, suffix),
supportText: meta.supportText,
};
maps.facetDetailsById[newId] = detailsClone;
maps.customMethodCardFieldBlocksById[newId] = blocksClone;
updateState({
customMethodCardMetaById: maps.customMethodCardMetaById,
decisionApproachDetailsById: maps.facetDetailsById,
customMethodCardFieldBlocksById: maps.customMethodCardFieldBlocksById,
});
pendingEphemeralDuplicateIdRef.current = newId;
customizeSnapshotRef.current = null;
setPendingCardId(newId);
setPendingDraft(structuredClone(detailsClone));
setDraftFieldBlocks(
blocksClone.length > 0 ? structuredClone(blocksClone) : null,
);
}, [
draftFieldBlocks,
markCreateFlowInteraction,
modalKebabMenu.duplicateTitleSuffix,
pendingCardId,
pendingDraft,
state.customMethodCardFieldBlocksById,
state.customMethodCardMetaById,
state.decisionApproachDetailsById,
updateState,
]);
const handleDuplicatePrefabCard = useCallback(() => {
if (
!pendingCardId ||
isCustomMethodCardId(pendingCardId, state.customMethodCardMetaById)
) {
return;
}
const method = methodById.get(pendingCardId);
if (!method || !pendingDraft) {
return;
}
markCreateFlowInteraction();
const newId = crypto.randomUUID();
const detailsClone = cloneMethodCardDetailsForDuplicate(
pendingDraft,
state.decisionApproachDetailsById?.[pendingCardId],
() => decisionApproachPresetFor(newId),
);
const blocksClone = structuredClone(
draftFieldBlocks !== null
? draftFieldBlocks
: cloneMethodCardBlocksForDuplicate(
state.customMethodCardFieldBlocksById,
pendingCardId,
),
);
const suffix = modalKebabMenu.duplicateTitleSuffix;
const priorEphemeral = pendingEphemeralDuplicateIdRef.current;
const maps = forkMethodCardFacetMapsForDuplicate({
customMethodCardMetaById: state.customMethodCardMetaById,
facetDetailsById: state.decisionApproachDetailsById,
customMethodCardFieldBlocksById: state.customMethodCardFieldBlocksById,
omitId: priorEphemeral,
});
maps.customMethodCardMetaById[newId] = {
label: duplicateMethodCardTitle(method.label, suffix),
supportText: method.supportText,
};
maps.facetDetailsById[newId] = detailsClone;
maps.customMethodCardFieldBlocksById[newId] = blocksClone;
updateState({
customMethodCardMetaById: maps.customMethodCardMetaById,
decisionApproachDetailsById: maps.facetDetailsById,
customMethodCardFieldBlocksById: maps.customMethodCardFieldBlocksById,
});
pendingEphemeralDuplicateIdRef.current = newId;
customizeSnapshotRef.current = null;
setPendingCardId(newId);
setPendingDraft(structuredClone(detailsClone));
setDraftFieldBlocks(
blocksClone.length > 0 ? structuredClone(blocksClone) : null,
);
}, [
draftFieldBlocks,
markCreateFlowInteraction,
methodById,
modalKebabMenu.duplicateTitleSuffix,
pendingCardId,
pendingDraft,
state.customMethodCardFieldBlocksById,
state.customMethodCardMetaById,
state.decisionApproachDetailsById,
updateState,
]);
const kebabMenuItems = useMemo(
() =>
buildCustomRuleModalKebabMenu(modalKebabMenu, {
showCustomize: true,
onCustomize: handleCustomize,
onDuplicate:
(state.editingPublishedRuleId?.trim() ?? "") !== "" || !pendingCardId
? undefined
: isCustomMethodCardId(
pendingCardId,
state.customMethodCardMetaById,
)
? handleDuplicateCustomCard
: handleDuplicatePrefabCard,
showRemove: isSelectedCardModal,
onRemove: handleRemoveSelectedFromModal,
}),
[
handleCustomize,
handleDuplicateCustomCard,
handleDuplicatePrefabCard,
handleRemoveSelectedFromModal,
isSelectedCardModal,
modalKebabMenu,
pendingCardId,
state.customMethodCardMetaById,
state.editingPublishedRuleId,
],
);
const handleToggleExpand = useCallback(() => {
markCreateFlowInteraction();
setExpanded((prev) => !prev);
}, [markCreateFlowInteraction]);
const handleCloseAddWizard = useCallback(() => {
const resumeCardId = wizardCustomizeCardId;
setAddCustomWizardOpen(false);
setWizardCustomizeCardId(null);
setWizardInitialValues(null);
if (resumeCardId && pendingCardId === resumeCardId) {
setCreateModalOpen(true);
}
}, [pendingCardId, wizardCustomizeCardId]);
const handleFinalizeCustomCard = useCallback(
({
title,
description,
fieldBlocks,
}: {
title: string;
description: string;
fieldBlocks: CustomMethodCardFieldBlock[];
}) => {
markCreateFlowInteraction();
const existingId = wizardCustomizeCardId;
if (existingId) {
updateState({
selectedDecisionApproachIds: moveFacetSelectionIdToFront(
selectedIds,
existingId,
),
customMethodCardMetaById: methodCardMetaWithCustomizeHeader(
state.customMethodCardMetaById,
existingId,
{ title, description },
),
...(pendingDraft
? {
decisionApproachDetailsById: {
...(state.decisionApproachDetailsById ?? {}),
[existingId]: pendingDraft,
},
}
: {}),
customMethodCardFieldBlocksById: {
...(state.customMethodCardFieldBlocksById ?? {}),
[existingId]: fieldBlocks,
},
});
if (pendingDraft) {
customizeSnapshotRef.current = captureMethodCardCustomizeSnapshot(
pendingDraft,
fieldBlocks,
{ title, description },
);
}
setDraftFieldBlocks(structuredClone(fieldBlocks));
setAddCustomWizardOpen(false);
return;
}
const id = crypto.randomUUID();
updateState({
selectedDecisionApproachIds: moveFacetSelectionIdToFront(
selectedIds,
id,
),
customMethodCardMetaById: {
...(state.customMethodCardMetaById ?? {}),
[id]: { label: title, supportText: description },
},
decisionApproachDetailsById: {
...(state.decisionApproachDetailsById ?? {}),
[id]: decisionApproachPresetFor(id),
},
customMethodCardFieldBlocksById: {
...(state.customMethodCardFieldBlocksById ?? {}),
[id]: fieldBlocks,
},
});
},
[
markCreateFlowInteraction,
pendingDraft,
selectedIds,
state.customMethodCardFieldBlocksById,
state.customMethodCardMetaById,
state.decisionApproachDetailsById,
updateState,
wizardCustomizeCardId,
],
);
const handleCreateModalPrimary = useCallback(() => {
if (!pendingCardId) {
void handleCreateModalClose();
return;
}
markCreateFlowInteraction();
const persistWizardBlocks =
modalUsesWizardFieldBlocksBody && draftFieldBlocks !== null;
if (selectedIds.includes(pendingCardId)) {
replaceState((prev) => {
if (persistWizardBlocks) {
return {
...prev,
customMethodCardFieldBlocksById: {
...(prev.customMethodCardFieldBlocksById ?? {}),
[pendingCardId]: structuredClone(draftFieldBlocks ?? []),
},
};
}
if (!pendingDraft) {
return prev;
}
return {
...prev,
decisionApproachDetailsById: {
...(prev.decisionApproachDetailsById ?? {}),
[pendingCardId]: structuredClone(pendingDraft),
},
};
});
pendingEphemeralDuplicateIdRef.current = null;
customizeSnapshotRef.current = null;
void handleCreateModalClose();
return;
}
if (!pendingDraft) {
void handleCreateModalClose();
return;
}
replaceState((prev) => ({
...prev,
selectedDecisionApproachIds: moveFacetSelectionIdToFront(
prev.selectedDecisionApproachIds ?? [],
pendingCardId,
),
decisionApproachDetailsById: {
...(prev.decisionApproachDetailsById ?? {}),
[pendingCardId]: structuredClone(pendingDraft),
},
...(persistWizardBlocks
? {
customMethodCardFieldBlocksById: {
...(prev.customMethodCardFieldBlocksById ?? {}),
[pendingCardId]: structuredClone(draftFieldBlocks ?? []),
},
}
: {}),
}));
pendingEphemeralDuplicateIdRef.current = null;
customizeSnapshotRef.current = null;
void handleCreateModalClose();
}, [
draftFieldBlocks,
handleCreateModalClose,
markCreateFlowInteraction,
modalUsesWizardFieldBlocksBody,
pendingCardId,
pendingDraft,
replaceState,
selectedIds,
]);
const modalConfig = pendingCardId
? (() => {
const method = methodById.get(pendingCardId);
const meta = state.customMethodCardMetaById?.[pendingCardId];
const saveLabel = modalKebabMenu.saveEdits;
return {
title: meta?.label ?? method?.label ?? da.confirmModal.title,
description:
meta?.supportText ??
method?.supportText ??
da.confirmModal.description,
nextButtonText: isSelectedCardModal
? saveLabel
: da.addApproach.nextButtonText,
};
})()
: {
title: da.confirmModal.title,
description: da.confirmModal.description,
nextButtonText: da.confirmModal.nextButtonText,
};
return (
<>
<CreateFlowTwoColumnSelectShell
contentTopBelowMd="space-800"
lgVerticalAlign="start"
header={
<div className="flex w-full min-w-0 flex-col gap-3">
<HeaderLockup
title={da.sidebar.title}
description={sidebarDescription}
size={mdUp ? "L" : "M"}
justification={mdUp ? "left" : "center"}
/>
<InfoMessageBox
title={da.messageBox.title}
items={messageBoxItems}
checkedIds={messageBoxCheckedIds}
onCheckboxChange={handleMessageBoxCheckboxChange}
/>
</div>
}
>
<div
className="flex w-full min-w-0 flex-col items-stretch gap-6 py-0"
aria-busy={!recommendationsReady}
>
{recommendationsReady && (
<CardStack
cards={sampleCards}
selectedIds={selectedIds}
onCardSelect={handleCardSelect}
expanded={expanded}
onToggleExpand={handleToggleExpand}
hasMore={true}
toggleLabel={da.cardStack.toggleSeeAll}
showLessLabel={da.cardStack.toggleShowLess}
title=""
description={
expanded ? (
<>
{da.cardStack.expandedStackDescriptionBefore}
<InlineTextButton onClick={handleOpenAddWizard}>
{da.sidebar.descriptionLinkLabel}
</InlineTextButton>
{da.cardStack.expandedStackDescriptionAfter}
</>
) : (
""
)
}
layout="singleStack"
compactRecommendedLimit={5}
compactCardIds={compactCardIds}
className="w-full"
headerLockupSize={mdUp ? "L" : "M"}
/>
)}
</div>
<Create
isOpen={createModalOpen}
onClose={handleCreateModalClose}
onNext={handleCreateModalPrimary}
title={modalConfig.title}
description={modalConfig.description}
nextButtonText={modalConfig.nextButtonText}
showBackButton={false}
showNextButton={showMethodModalPrimary}
showRemoveButton={isSelectedCardModal}
onRemove={handleRemoveSelectedFromModal}
backdropVariant="blurredYellow"
kebabTriggerAriaLabel={modalKebabMenu.triggerAriaLabel}
kebabMenuAriaLabel={modalKebabMenu.menuAriaLabel}
kebabMenuItems={kebabMenuItems}
>
{pendingCardId && pendingDraft ? (
modalUsesWizardFieldBlocksBody ? (
<CustomMethodCardModalBody
cardId={pendingCardId}
blocksById={state.customMethodCardFieldBlocksById}
blocksOverride={
draftFieldBlocks !== null ? draftFieldBlocks : undefined
}
policyMeta={state.customMethodCardMetaById?.[pendingCardId]}
showPolicyContentLockupWhenNoBlocks={
draftFieldBlocks === null || draftFieldBlocks.length === 0
}
onFieldBlocksChange={
draftFieldBlocks === null
? undefined
: (next) => setDraftFieldBlocks(next)
}
/>
) : (
<DecisionApproachEditFields
value={pendingDraft}
onChange={handleDraftChange}
/>
)
) : null}
</Create>
</CreateFlowTwoColumnSelectShell>
<CustomMethodCardWizard
isOpen={addCustomWizardOpen}
onClose={handleCloseAddWizard}
initialValues={wizardInitialValues}
onFinalize={handleFinalizeCustomCard}
onPersistCustomUploadFile={(file) =>
uploadCreateFlowFile(file, "customMethodAttachment")
}
/>
{confirmDialog}
</>
);
}