Files
community-rule/app/(app)/create/screens/card/CommunicationMethodsScreen.tsx
T
adilalloandCursor a820739d07 Open create-flow method modals with editable fields and tertiary default copy.
Seeded section text should look like the form default state, not locked primary, and Customize stays on the kebab instead of unlocking the body.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-28 12:59:53 -06:00

783 lines
26 KiB
TypeScript

"use client";
/**
* `communication-methods` step — Figma “Flow — Compact Card Stack” (node `20246-15828`).
* Registry: `layoutKind: "card"` (`CREATE_FLOW_SCREEN_REGISTRY["communication-methods"]`).
*
* Lives under `screens/card/` (not `select/`): Figma **card stack** layout is a distinct shell from
* two-column chip **select** frames. Future card-stack steps get their own `*Screen.tsx` here and
* reuse `CardStack` / `CreateFlowStepShell` as needed.
*
* Card click opens the Figma create modal (node `20246-15829`) with three
* editable sections rendered by {@link CommunicationMethodEditFields}. The primary
* action is **Add Platform** for an unselected card and **Save** for a selected
* card. **Remove** is available from the kebab (same behavior as legacy
* footer remove via {@link removeMethodCardFromFacetSelection}).
*/
import { useState, useCallback, useMemo, useRef } from "react";
import { useMessages } from "../../../../contexts/MessagesContext";
import { useCreateFlow } from "../../context/CreateFlowContext";
import { useCreateFlowMdUp } from "../../hooks/useCreateFlowMdUp";
import { useDiscardCustomizeConfirm } from "../../hooks/useDiscardCustomizeConfirm";
import { useMethodCardDeckOrdering } from "../../hooks/useMethodCardDeckOrdering";
import { CreateFlowHeaderLockup } from "../../components/CreateFlowHeaderLockup";
import CardStack from "../../../../components/cards/CardStack";
import Create from "../../../../components/modals/Create";
import InlineTextButton from "../../../../components/buttons/InlineTextButton";
import { CreateFlowStepShell } from "../../components/CreateFlowStepShell";
import {
CREATE_FLOW_CARD_STACK_AREA_MAX_CLASS,
CREATE_FLOW_MD_CENTERED_SHELL_CLASS,
CREATE_FLOW_MD_UP_COLUMN_MAX_CLASS,
} from "../../components/createFlowLayoutTokens";
import { CommunicationMethodEditFields } from "../../components/methodEditFields";
import CustomMethodCardWizard from "../../components/CustomMethodCardWizard";
import { uploadCreateFlowFile } from "../../../../../lib/create/uploadToServer";
import { communicationPresetFor } from "../../../../../lib/create/finalReviewChipPresets";
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 { communicationMethodFacetMatchesPreset } 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 { CommunicationMethodDetailEntry } 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,
type MethodCardCustomizeSnapshot,
type MethodCardHeaderDraft,
} from "../../../../../lib/create/methodCardCustomizeSession";
export function CommunicationMethodsScreen() {
const m = useMessages();
const comm = m.create.customRule.communication;
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<CommunicationMethodDetailEntry> | null
>(null);
const [expanded, setExpanded] = useState(false);
const [createModalOpen, setCreateModalOpen] = useState(false);
const [pendingCardId, setPendingCardId] = useState<string | null>(null);
const [pendingDraft, setPendingDraft] =
useState<CommunicationMethodDetailEntry | 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);
const selectedIds = state.selectedCommunicationMethodIds ?? [];
const mergedMethods = useMemo(
() =>
mergePresetMethodsWithCustom(
comm.methods,
selectedIds,
state.customMethodCardMetaById,
),
[comm.methods, selectedIds, state.customMethodCardMetaById],
);
const { sampleCards, compactCardIds, methodById, recommendationsReady } =
useMethodCardDeckOrdering(
"communication",
mergedMethods,
selectedIds,
);
const handleOpenAddWizard = useCallback(() => {
markCreateFlowInteraction();
setWizardCustomizeCardId(null);
setWizardInitialValues(null);
setAddCustomWizardOpen(true);
}, [markCreateFlowInteraction]);
const title = expanded ? comm.page.expandedTitle : comm.page.compactTitle;
const description = expanded ? (
<>
{comm.page.expandedDescriptionBefore}
<InlineTextButton onClick={handleOpenAddWizard}>
{comm.page.compactDescriptionLinkLabel}
</InlineTextButton>
{comm.page.expandedDescriptionAfter}
</>
) : (
<>
{comm.page.compactDescriptionBefore}
<InlineTextButton onClick={handleOpenAddWizard}>
{comm.page.compactDescriptionLinkLabel}
</InlineTextButton>
{comm.page.compactDescriptionAfter}
</>
);
const seedDraft = useCallback(
(id: string): CommunicationMethodDetailEntry => {
const saved = state.communicationMethodDetailsById?.[id];
if (saved) {
return { ...saved };
}
return communicationPresetFor(id);
},
[state.communicationMethodDetailsById],
);
const handleCardClick = 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 ?? comm.confirmModal.title,
description:
meta?.supportText ??
method?.supportText ??
comm.confirmModal.description,
};
customizeSnapshotRef.current = captureMethodCardCustomizeSnapshot(
draft,
initialBlocks,
headerDraft,
);
setPendingCardId(id);
setPendingDraft(draft);
setDraftFieldBlocks(initialBlocks);
setCreateModalOpen(true);
},
[
comm.confirmModal.description,
comm.confirmModal.title,
markCreateFlowInteraction,
methodById,
seedDraft,
state.customMethodCardFieldBlocksById,
state.customMethodCardMetaById,
],
);
const handleDraftChange = useCallback(
(next: CommunicationMethodDetailEntry) => {
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 communicationMethodFacetMatchesPreset(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,
),
communicationMethodDetailsById: omitIdFromStringRecord(
prev.communicationMethodDetailsById,
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,
"communication",
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 ?? comm.confirmModal.title,
fallbackDescription:
method?.supportText ?? comm.confirmModal.description,
meta: state.customMethodCardMetaById,
persistedBlocks: state.customMethodCardFieldBlocksById,
draftFieldBlocks,
facetPrefill: pendingDraft
? {
group: "communication",
draft: pendingDraft,
headings: comm.sectionHeadings,
}
: undefined,
}),
);
setWizardCustomizeCardId(pendingCardId);
setCreateModalOpen(false);
setAddCustomWizardOpen(true);
}, [
comm.confirmModal.description,
comm.confirmModal.title,
comm.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.communicationMethodDetailsById?.[pendingCardId],
() => communicationPresetFor(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.communicationMethodDetailsById,
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,
communicationMethodDetailsById: maps.facetDetailsById,
customMethodCardFieldBlocksById: maps.customMethodCardFieldBlocksById,
});
pendingEphemeralDuplicateIdRef.current = newId;
customizeSnapshotRef.current = null;
setPendingCardId(newId);
setPendingDraft(structuredClone(detailsClone));
setDraftFieldBlocks(
blocksClone.length > 0 ? structuredClone(blocksClone) : null,
);
}, [
markCreateFlowInteraction,
modalKebabMenu.duplicateTitleSuffix,
pendingCardId,
pendingDraft,
draftFieldBlocks,
state.communicationMethodDetailsById,
state.customMethodCardFieldBlocksById,
state.customMethodCardMetaById,
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.communicationMethodDetailsById?.[pendingCardId],
() => communicationPresetFor(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.communicationMethodDetailsById,
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,
communicationMethodDetailsById: 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.communicationMethodDetailsById,
state.customMethodCardFieldBlocksById,
state.customMethodCardMetaById,
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 modalConfig = pendingCardId
? (() => {
const method = methodById.get(pendingCardId);
const meta = state.customMethodCardMetaById?.[pendingCardId];
const saveLabel = modalKebabMenu.saveEdits;
return {
title: meta?.label ?? method?.label ?? comm.confirmModal.title,
description:
meta?.supportText ??
method?.supportText ??
comm.confirmModal.description,
nextButtonText: isSelectedCardModal
? saveLabel
: comm.addPlatform.nextButtonText,
};
})()
: {
title: comm.confirmModal.title,
description: comm.confirmModal.description,
nextButtonText: comm.confirmModal.nextButtonText,
};
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({
selectedCommunicationMethodIds: moveFacetSelectionIdToFront(
selectedIds,
existingId,
),
customMethodCardMetaById: methodCardMetaWithCustomizeHeader(
state.customMethodCardMetaById,
existingId,
{ title, description },
),
...(pendingDraft
? {
communicationMethodDetailsById: {
...(state.communicationMethodDetailsById ?? {}),
[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({
selectedCommunicationMethodIds: moveFacetSelectionIdToFront(
selectedIds,
id,
),
customMethodCardMetaById: {
...(state.customMethodCardMetaById ?? {}),
[id]: { label: title, supportText: description },
},
communicationMethodDetailsById: {
...(state.communicationMethodDetailsById ?? {}),
[id]: communicationPresetFor(id),
},
customMethodCardFieldBlocksById: {
...(state.customMethodCardFieldBlocksById ?? {}),
[id]: fieldBlocks,
},
});
},
[
markCreateFlowInteraction,
pendingDraft,
selectedIds,
state.communicationMethodDetailsById,
state.customMethodCardFieldBlocksById,
state.customMethodCardMetaById,
updateState,
wizardCustomizeCardId,
],
);
const handleCreateModalPrimary = useCallback(() => {
if (!pendingCardId) {
void handleCreateModalClose();
return;
}
markCreateFlowInteraction();
const persistWizardBlocks =
modalUsesWizardFieldBlocksBody && draftFieldBlocks !== null;
if (selectedIds.includes(pendingCardId)) {
if (persistWizardBlocks) {
updateState({
customMethodCardFieldBlocksById: {
...(state.customMethodCardFieldBlocksById ?? {}),
[pendingCardId]: structuredClone(draftFieldBlocks ?? []),
},
});
} else if (pendingDraft) {
updateState({
communicationMethodDetailsById: {
...(state.communicationMethodDetailsById ?? {}),
[pendingCardId]: pendingDraft,
},
});
}
pendingEphemeralDuplicateIdRef.current = null;
customizeSnapshotRef.current = null;
void handleCreateModalClose();
return;
}
if (!pendingDraft) {
void handleCreateModalClose();
return;
}
updateState({
selectedCommunicationMethodIds: moveFacetSelectionIdToFront(
selectedIds,
pendingCardId,
),
communicationMethodDetailsById: {
...(state.communicationMethodDetailsById ?? {}),
[pendingCardId]: pendingDraft,
},
...(persistWizardBlocks
? {
customMethodCardFieldBlocksById: {
...(state.customMethodCardFieldBlocksById ?? {}),
[pendingCardId]: structuredClone(draftFieldBlocks ?? []),
},
}
: {}),
});
pendingEphemeralDuplicateIdRef.current = null;
customizeSnapshotRef.current = null;
void handleCreateModalClose();
}, [
draftFieldBlocks,
handleCreateModalClose,
markCreateFlowInteraction,
modalUsesWizardFieldBlocksBody,
pendingCardId,
pendingDraft,
selectedIds,
state,
updateState,
]);
return (
<>
<CreateFlowStepShell
variant="wideGridLoosePadding"
contentTopBelowMd="space-800"
className={CREATE_FLOW_MD_CENTERED_SHELL_CLASS}
>
<div className="flex w-full min-w-0 flex-col items-center gap-6">
<div className={CREATE_FLOW_MD_UP_COLUMN_MAX_CLASS}>
<CreateFlowHeaderLockup
title={title}
description={description}
justification="center"
/>
</div>
<div
className={CREATE_FLOW_CARD_STACK_AREA_MAX_CLASS}
aria-busy={!recommendationsReady}
>
{recommendationsReady && (
<CardStack
cards={sampleCards}
selectedIds={selectedIds}
onCardSelect={handleCardClick}
expanded={expanded}
onToggleExpand={() => {
markCreateFlowInteraction();
setExpanded((prev) => !prev);
}}
hasMore={true}
toggleLabel={comm.page.seeAllLink}
compactRecommendedLimit={5}
compactCardIds={compactCardIds}
compactDesktopLayout="flexWrap"
headerLockupSize={mdUp ? "L" : "M"}
/>
)}
</div>
</div>
<Create
isOpen={createModalOpen}
onClose={handleCreateModalClose}
onNext={handleCreateModalPrimary}
title={modalConfig.title}
description={modalConfig.description}
nextButtonText={modalConfig.nextButtonText}
showBackButton={false}
showNextButton={showMethodModalPrimary}
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)
}
/>
) : (
<CommunicationMethodEditFields
value={pendingDraft}
onChange={handleDraftChange}
/>
)
) : null}
</Create>
</CreateFlowStepShell>
<CustomMethodCardWizard
isOpen={addCustomWizardOpen}
onClose={handleCloseAddWizard}
initialValues={wizardInitialValues}
onFinalize={handleFinalizeCustomCard}
onPersistCustomUploadFile={(file) =>
uploadCreateFlowFile(file, "customMethodAttachment")
}
/>
{confirmDialog}
</>
);
}