"use client"; /** * Controlled field blocks for wizard-authored method cards in Create modals * (facet screens + final-review chip edit). When `onBlocksChange` is omitted, * blocks render read-only (disabled controls). * * Layout matches preset method editors ({@link CommunicationMethodEditFields}, * {@link DecisionApproachEditFields}): {@link ModalTextAreaField}, * {@link ApplicableScopeField} chip rows, {@link IncrementerBlock}. */ import { memo, useCallback, useRef, useState } from "react"; import { useMessages, useTranslation } from "../../../contexts/MessagesContext"; import Chip from "../../../components/controls/Chip"; import IncrementerBlock from "../../../components/controls/IncrementerBlock"; import InlineTextButton from "../../../components/buttons/InlineTextButton"; import Upload from "../../../components/controls/Upload"; import ApplicableScopeField from "./ApplicableScopeField"; import InputLabel from "../../../components/type/InputLabel"; import type { CustomMethodCardFieldBlock } from "../../../../lib/create/customMethodCardFieldBlocks"; import ModalTextAreaField from "./ModalTextAreaField"; import { uploadCreateFlowFile } from "../../../../lib/create/uploadToServer"; const TEXT_VALUE_MAX = 8000; export interface CustomMethodCardFieldBlocksSummaryProps { blocks: CustomMethodCardFieldBlock[]; /** When set, fields update the draft via immutable block-array replacements. */ onBlocksChange?: (_next: CustomMethodCardFieldBlock[]) => void; } function mapBlockById( blocks: CustomMethodCardFieldBlock[], blockId: string, mapFn: (_b: CustomMethodCardFieldBlock) => CustomMethodCardFieldBlock, ): CustomMethodCardFieldBlock[] { return blocks.map((b) => (b.id === blockId ? mapFn(b) : b)); } function CustomMethodCardUploadBlockRow({ block, blocks, patch, uploadFileInputAriaLabel, uploadHint, clearFileLabel, noFileChosen, }: { block: Extract; blocks: CustomMethodCardFieldBlock[]; patch: (_next: CustomMethodCardFieldBlock[]) => void; uploadFileInputAriaLabel: string; uploadHint: string; clearFileLabel: string; noFileChosen: string; }) { const uploadInputRef = useRef(null); const tUpload = useTranslation("create.upload"); const [busy, setBusy] = useState(false); const [errorMessage, setErrorMessage] = useState(null); const displayName = block.fileName?.trim() ? block.fileName : noFileChosen; const hasAsset = Boolean(block.assetUrl?.trim()); const previewAlt = block.fileName?.trim() || block.blockTitle || noFileChosen; return (
{!hasAsset ? (

{displayName}

) : null} {hasAsset ? ( // eslint-disable-next-line @next/next/no-img-element -- same-origin upload URL {previewAlt} ) : null} { const file = e.target.files?.[0]; e.target.value = ""; if (!file) return; setErrorMessage(null); setBusy(true); void (async () => { try { const { url } = await uploadCreateFlowFile( file, "customMethodAttachment", ); const name = file.name?.trim(); patch( mapBlockById(blocks, block.id, (b) => b.kind === "upload" ? { ...b, ...(name ? { fileName: name } : {}), assetUrl: url, } : b, ), ); } catch { setErrorMessage(tUpload("errors.generic")); } finally { setBusy(false); } })(); }} /> { if (!busy) uploadInputRef.current?.click(); }} /> {errorMessage ? (

{errorMessage}

) : null} {block.fileName?.trim() || block.assetUrl?.trim() ? ( patch( mapBlockById(blocks, block.id, (b) => b.kind === "upload" ? { ...b, fileName: undefined, assetUrl: undefined } : b, ), ) } > {clearFileLabel} ) : null}
); } function CustomMethodCardFieldBlocksSummaryComponent({ blocks, onBlocksChange, }: CustomMethodCardFieldBlocksSummaryProps) { const m = useMessages(); const wiz = m.create.customRule.customMethodCardWizard; const fm = wiz.fieldModals; const em = wiz.editModal; const emptyValue = em.readout.emptyValue; const noFileChosen = em.readout.noFileChosen; const readOnly = !onBlocksChange; const patch = useCallback( (next: CustomMethodCardFieldBlock[]) => { onBlocksChange?.(next); }, [onBlocksChange], ); return (
{blocks.map((block) => { if (block.kind === "text") { return ( patch( mapBlockById(blocks, block.id, (b) => b.kind === "text" ? { ...b, placeholderText: v.slice(0, TEXT_VALUE_MAX) } : b, ), ) } disabled={readOnly} /> ); } if (block.kind === "badges") { if (readOnly) { return (
{block.options.length > 0 ? (
{block.options.map((opt, idx) => ( ))}
) : (

{emptyValue}

)}
); } return ( patch( mapBlockById(blocks, block.id, (b) => b.kind === "badges" ? { ...b, options: b.options.filter((o) => o !== scope) } : b, ), ) } onAddScope={(scope) => patch( mapBlockById(blocks, block.id, (b) => { if (b.kind !== "badges") return b; if (b.options.includes(scope) || b.options.length >= 50) return b; return { ...b, options: [...b.options, scope] }; }), ) } /> ); } if (block.kind === "upload") { return (
{readOnly ? (
{block.assetUrl?.trim() ? ( // eslint-disable-next-line @next/next/no-img-element { ) : (

{noFileChosen}

)}
) : ( )}
); } return ( patch( mapBlockById(blocks, block.id, (b) => b.kind === "proportion" ? { ...b, defaultPercent: v } : b, ), ) } formatValue={(v) => `${v}%`} decrementAriaLabel={fm.proportion.decrementAriaLabel} incrementAriaLabel={fm.proportion.incrementAriaLabel} /> ); })}
); } const CustomMethodCardFieldBlocksSummary = memo( CustomMethodCardFieldBlocksSummaryComponent, ); CustomMethodCardFieldBlocksSummary.displayName = "CustomMethodCardFieldBlocksSummary"; export default CustomMethodCardFieldBlocksSummary;