Add button and custom modal flow implemented

This commit is contained in:
adilallo
2026-05-07 21:15:27 -06:00
parent dee2dd800e
commit 26bcd61ea3
43 changed files with 1444 additions and 81 deletions
@@ -1,7 +1,10 @@
"use client";
import { memo, useCallback, useEffect, useMemo, useRef, useState } from "react";
import { useMessages, useTranslation } from "../../../../contexts/MessagesContext";
import {
useMessages,
useTranslation,
} from "../../../../contexts/MessagesContext";
import type { CustomMethodCardFieldBlock } from "../../../../../lib/create/customMethodCardFieldBlocks";
import { CUSTOM_METHOD_CARD_WIZARD_MAX_FIELD_CHARS } from "../../../../../lib/create/customMethodCardWizardConstants";
import type { AddCustomFieldType } from "../../../../components/controls/AddCustomField/AddCustomField.types";
@@ -13,9 +16,10 @@ import type { CustomMethodCardWizardProps } from "./CustomMethodCardWizard.types
* `20066:14748`, `20094:48551`, `20066:14361`).
*/
const CustomMethodCardWizardContainer = memo<CustomMethodCardWizardProps>(
({ isOpen, onClose, onFinalize }) => {
({ isOpen, onClose, onFinalize, onPersistCustomUploadFile }) => {
const m = useMessages();
const t = useTranslation("common");
const tUpload = useTranslation("create.upload");
const w = m.create.customRule.customMethodCardWizard;
const copy = useMemo(
@@ -23,10 +27,23 @@ const CustomMethodCardWizardContainer = memo<CustomMethodCardWizardProps>(
step1: w.steps["1"],
step2: w.steps["2"],
step3: w.steps["3"],
step3BlocksList: w.step3BlocksList,
fieldTypeLabels: {
text: w.addCustomField.fieldTypes.text,
badges: w.addCustomField.fieldTypes.badges,
upload: w.addCustomField.fieldTypes.upload,
proportion: w.addCustomField.fieldTypes.proportion,
},
footerFinalize: w.footer.finalize,
fieldModals: w.fieldModals,
}),
[w.fieldModals, w.footer.finalize, w.steps],
[
w.addCustomField.fieldTypes,
w.fieldModals,
w.footer.finalize,
w.step3BlocksList,
w.steps,
],
);
const fieldBodiesCopy = useMemo(
@@ -58,6 +75,13 @@ const CustomMethodCardWizardContainer = memo<CustomMethodCardWizardProps>(
const [uploadFileName, setUploadFileName] = useState<string | undefined>(
undefined,
);
const [uploadAssetUrl, setUploadAssetUrl] = useState<string | undefined>(
undefined,
);
const [uploadFieldBusy, setUploadFieldBusy] = useState(false);
const [uploadFieldError, setUploadFieldError] = useState<string | null>(
null,
);
const [proportionBlockTitle, setProportionBlockTitle] = useState("");
const [proportionDefault, setProportionDefault] = useState(50);
@@ -70,6 +94,9 @@ const CustomMethodCardWizardContainer = memo<CustomMethodCardWizardProps>(
setBadgeOptions([]);
setUploadBlockTitle("");
setUploadFileName(undefined);
setUploadAssetUrl(undefined);
setUploadFieldBusy(false);
setUploadFieldError(null);
setProportionBlockTitle("");
setProportionDefault(50);
if (fileInputRef.current) {
@@ -135,10 +162,14 @@ const CustomMethodCardWizardContainer = memo<CustomMethodCardWizardProps>(
}
if (fieldTypeModal === "upload") {
const t0 = uploadBlockTitle.trim();
return (
const titleOk =
t0.length > 0 &&
t0.length <= CUSTOM_METHOD_CARD_WIZARD_MAX_FIELD_CHARS
);
t0.length <= CUSTOM_METHOD_CARD_WIZARD_MAX_FIELD_CHARS;
if (!titleOk) return false;
if (onPersistCustomUploadFile) {
return Boolean(uploadAssetUrl?.trim());
}
return true;
}
const t0 = proportionBlockTitle.trim();
return (
@@ -154,6 +185,8 @@ const CustomMethodCardWizardContainer = memo<CustomMethodCardWizardProps>(
proportionDefault,
textBlockTitle,
uploadBlockTitle,
uploadAssetUrl,
onPersistCustomUploadFile,
]);
const headerTitle =
@@ -213,13 +246,35 @@ const CustomMethodCardWizardContainer = memo<CustomMethodCardWizardProps>(
}, [resetFieldTypeDrafts]);
const handleFileChosen = useCallback(
(e: React.ChangeEvent<HTMLInputElement>) => {
async (e: React.ChangeEvent<HTMLInputElement>) => {
const file = e.target.files?.[0];
setUploadFileName(file?.name);
setUploadAssetUrl(undefined);
setUploadFieldError(null);
if (!file || !onPersistCustomUploadFile) return;
setUploadFieldBusy(true);
try {
const { url } = await onPersistCustomUploadFile(file);
setUploadAssetUrl(url);
} catch {
setUploadFieldError(tUpload("errors.generic"));
} finally {
setUploadFieldBusy(false);
}
},
[],
[onPersistCustomUploadFile, tUpload],
);
const handleClearPendingUpload = useCallback(() => {
setUploadFileName(undefined);
setUploadAssetUrl(undefined);
setUploadFieldError(null);
setUploadFieldBusy(false);
if (fileInputRef.current) {
fileInputRef.current.value = "";
}
}, []);
const handleBadgeAddOption = useCallback((label: string) => {
setBadgeOptions((prev) =>
prev.includes(label) ? prev : [...prev, label],
@@ -253,6 +308,9 @@ const CustomMethodCardWizardContainer = memo<CustomMethodCardWizardProps>(
id,
blockTitle: uploadBlockTitle.trim(),
fileName: uploadFileName,
...(uploadAssetUrl?.trim()
? { assetUrl: uploadAssetUrl.trim() }
: {}),
};
break;
default:
@@ -276,6 +334,7 @@ const CustomMethodCardWizardContainer = memo<CustomMethodCardWizardProps>(
textPlaceholderBody,
uploadBlockTitle,
uploadFileName,
uploadAssetUrl,
]);
const handleNext = useCallback(() => {
@@ -337,6 +396,13 @@ const CustomMethodCardWizardContainer = memo<CustomMethodCardWizardProps>(
onUploadBlockTitleChange: setUploadBlockTitle,
fileInputRef,
onFileChosen: handleFileChosen,
onClearPendingUpload: handleClearPendingUpload,
uploadAssetPreviewUrl: uploadAssetUrl,
uploadPersisting:
Boolean(fieldTypeModal === "upload" && uploadFieldBusy),
uploadBusyHint: tUpload("uploading"),
uploadErrorMessage:
fieldTypeModal === "upload" ? uploadFieldError : null,
proportionBlockTitle,
proportionDefault,
onProportionBlockTitleChange: setProportionBlockTitle,
@@ -348,6 +414,8 @@ const CustomMethodCardWizardContainer = memo<CustomMethodCardWizardProps>(
onBack={handleBack}
onNext={handleNext}
stepper={!fieldTypeModal}
draftFieldBlocks={draftFieldBlocks}
onDraftFieldBlocksReorder={setDraftFieldBlocks}
/>
);
},