Add custom intervention modals
This commit is contained in:
+358
@@ -0,0 +1,358 @@
|
||||
"use client";
|
||||
|
||||
import { memo, useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||
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";
|
||||
import { CustomMethodCardWizardView } from "./CustomMethodCardWizard.view";
|
||||
import type { CustomMethodCardWizardProps } from "./CustomMethodCardWizard.types";
|
||||
|
||||
/**
|
||||
* Shared 3-step add-custom-method-card flow (Figma Modal / Create — nodes
|
||||
* `20066:14748`, `20094:48551`, `20066:14361`).
|
||||
*/
|
||||
const CustomMethodCardWizardContainer = memo<CustomMethodCardWizardProps>(
|
||||
({ isOpen, onClose, onFinalize }) => {
|
||||
const m = useMessages();
|
||||
const t = useTranslation("common");
|
||||
const w = m.create.customRule.customMethodCardWizard;
|
||||
|
||||
const copy = useMemo(
|
||||
() => ({
|
||||
step1: w.steps["1"],
|
||||
step2: w.steps["2"],
|
||||
step3: w.steps["3"],
|
||||
footerFinalize: w.footer.finalize,
|
||||
fieldModals: w.fieldModals,
|
||||
}),
|
||||
[w.fieldModals, w.footer.finalize, w.steps],
|
||||
);
|
||||
|
||||
const fieldBodiesCopy = useMemo(
|
||||
() => ({
|
||||
requiredHint: copy.fieldModals.requiredHint,
|
||||
text: copy.fieldModals.text,
|
||||
badges: copy.fieldModals.badges,
|
||||
upload: copy.fieldModals.upload,
|
||||
proportion: copy.fieldModals.proportion,
|
||||
}),
|
||||
[copy.fieldModals],
|
||||
);
|
||||
|
||||
const [wizardStep, setWizardStep] = useState<1 | 2 | 3>(1);
|
||||
const [policyTitle, setPolicyTitle] = useState("");
|
||||
const [policyDescription, setPolicyDescription] = useState("");
|
||||
const [addFieldExpanded, setAddFieldExpanded] = useState(false);
|
||||
const [fieldTypeModal, setFieldTypeModal] =
|
||||
useState<AddCustomFieldType | null>(null);
|
||||
const [draftFieldBlocks, setDraftFieldBlocks] = useState<
|
||||
CustomMethodCardFieldBlock[]
|
||||
>([]);
|
||||
|
||||
const [textBlockTitle, setTextBlockTitle] = useState("");
|
||||
const [textPlaceholderBody, setTextPlaceholderBody] = useState("");
|
||||
const [badgeBlockTitle, setBadgeBlockTitle] = useState("");
|
||||
const [badgeOptions, setBadgeOptions] = useState<string[]>([]);
|
||||
const [uploadBlockTitle, setUploadBlockTitle] = useState("");
|
||||
const [uploadFileName, setUploadFileName] = useState<string | undefined>(
|
||||
undefined,
|
||||
);
|
||||
const [proportionBlockTitle, setProportionBlockTitle] = useState("");
|
||||
const [proportionDefault, setProportionDefault] = useState(50);
|
||||
|
||||
const fileInputRef = useRef<HTMLInputElement>(null);
|
||||
|
||||
const resetFieldTypeDrafts = useCallback(() => {
|
||||
setTextBlockTitle("");
|
||||
setTextPlaceholderBody("");
|
||||
setBadgeBlockTitle("");
|
||||
setBadgeOptions([]);
|
||||
setUploadBlockTitle("");
|
||||
setUploadFileName(undefined);
|
||||
setProportionBlockTitle("");
|
||||
setProportionDefault(50);
|
||||
if (fileInputRef.current) {
|
||||
fileInputRef.current.value = "";
|
||||
}
|
||||
}, []);
|
||||
|
||||
const reset = useCallback(() => {
|
||||
setWizardStep(1);
|
||||
setPolicyTitle("");
|
||||
setPolicyDescription("");
|
||||
setAddFieldExpanded(false);
|
||||
setFieldTypeModal(null);
|
||||
setDraftFieldBlocks([]);
|
||||
resetFieldTypeDrafts();
|
||||
}, [resetFieldTypeDrafts]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isOpen) {
|
||||
reset();
|
||||
}
|
||||
}, [isOpen, reset]);
|
||||
|
||||
const dismiss = useCallback(() => {
|
||||
reset();
|
||||
onClose();
|
||||
}, [onClose, reset]);
|
||||
|
||||
const titleTrim = policyTitle.trim();
|
||||
const descriptionTrim = policyDescription.trim();
|
||||
|
||||
const stepValid = useMemo(() => {
|
||||
const titleOk =
|
||||
titleTrim.length > 0 &&
|
||||
titleTrim.length <= CUSTOM_METHOD_CARD_WIZARD_MAX_FIELD_CHARS;
|
||||
const descriptionOk =
|
||||
descriptionTrim.length > 0 &&
|
||||
descriptionTrim.length <= CUSTOM_METHOD_CARD_WIZARD_MAX_FIELD_CHARS;
|
||||
if (wizardStep === 1) return titleOk;
|
||||
if (wizardStep === 2) return descriptionOk;
|
||||
return titleOk && descriptionOk;
|
||||
}, [
|
||||
descriptionTrim.length,
|
||||
titleTrim.length,
|
||||
wizardStep,
|
||||
]);
|
||||
|
||||
const fieldModalStepValid = useMemo(() => {
|
||||
if (!fieldTypeModal) return false;
|
||||
if (fieldTypeModal === "text") {
|
||||
const t0 = textBlockTitle.trim();
|
||||
return (
|
||||
t0.length > 0 &&
|
||||
t0.length <= CUSTOM_METHOD_CARD_WIZARD_MAX_FIELD_CHARS
|
||||
);
|
||||
}
|
||||
if (fieldTypeModal === "badges") {
|
||||
const t0 = badgeBlockTitle.trim();
|
||||
return (
|
||||
t0.length > 0 &&
|
||||
t0.length <= CUSTOM_METHOD_CARD_WIZARD_MAX_FIELD_CHARS
|
||||
);
|
||||
}
|
||||
if (fieldTypeModal === "upload") {
|
||||
const t0 = uploadBlockTitle.trim();
|
||||
return (
|
||||
t0.length > 0 &&
|
||||
t0.length <= CUSTOM_METHOD_CARD_WIZARD_MAX_FIELD_CHARS
|
||||
);
|
||||
}
|
||||
const t0 = proportionBlockTitle.trim();
|
||||
return (
|
||||
t0.length > 0 &&
|
||||
t0.length <= CUSTOM_METHOD_CARD_WIZARD_MAX_FIELD_CHARS &&
|
||||
proportionDefault >= 1 &&
|
||||
proportionDefault <= 100
|
||||
);
|
||||
}, [
|
||||
badgeBlockTitle,
|
||||
fieldTypeModal,
|
||||
proportionBlockTitle,
|
||||
proportionDefault,
|
||||
textBlockTitle,
|
||||
uploadBlockTitle,
|
||||
]);
|
||||
|
||||
const headerTitle =
|
||||
wizardStep === 1
|
||||
? copy.step1.title
|
||||
: wizardStep === 2
|
||||
? copy.step2.title
|
||||
: copy.step3.title;
|
||||
|
||||
const headerDescription =
|
||||
wizardStep === 1
|
||||
? copy.step1.description
|
||||
: wizardStep === 2
|
||||
? copy.step2.description
|
||||
: copy.step3.description;
|
||||
|
||||
const fieldModalHeader = fieldTypeModal
|
||||
? copy.fieldModals[fieldTypeModal]
|
||||
: null;
|
||||
|
||||
const shellTitle = fieldModalHeader?.title ?? headerTitle;
|
||||
const shellDescription = fieldModalHeader?.description ?? headerDescription;
|
||||
|
||||
const nextLabel = fieldTypeModal
|
||||
? copy.fieldModals.addField
|
||||
: wizardStep === 3
|
||||
? copy.footerFinalize
|
||||
: t("buttons.next");
|
||||
|
||||
const shellNextDisabled = fieldTypeModal
|
||||
? !fieldModalStepValid
|
||||
: !stepValid;
|
||||
|
||||
const handleShellClose = useCallback(() => {
|
||||
if (fieldTypeModal) {
|
||||
setFieldTypeModal(null);
|
||||
return;
|
||||
}
|
||||
dismiss();
|
||||
}, [dismiss, fieldTypeModal]);
|
||||
|
||||
const handleBack = useCallback(() => {
|
||||
if (fieldTypeModal) {
|
||||
setFieldTypeModal(null);
|
||||
return;
|
||||
}
|
||||
if (wizardStep === 1) {
|
||||
dismiss();
|
||||
return;
|
||||
}
|
||||
setWizardStep((s) => (s === 2 ? 1 : 2));
|
||||
}, [dismiss, fieldTypeModal, wizardStep]);
|
||||
|
||||
const handleSelectFieldType = useCallback((ft: AddCustomFieldType) => {
|
||||
resetFieldTypeDrafts();
|
||||
setFieldTypeModal(ft);
|
||||
}, [resetFieldTypeDrafts]);
|
||||
|
||||
const handleFileChosen = useCallback(
|
||||
(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const file = e.target.files?.[0];
|
||||
setUploadFileName(file?.name);
|
||||
},
|
||||
[],
|
||||
);
|
||||
|
||||
const handleBadgeAddOption = useCallback((label: string) => {
|
||||
setBadgeOptions((prev) =>
|
||||
prev.includes(label) ? prev : [...prev, label],
|
||||
);
|
||||
}, []);
|
||||
|
||||
const appendFieldBlock = useCallback(() => {
|
||||
if (!fieldTypeModal || !fieldModalStepValid) return;
|
||||
const id = crypto.randomUUID();
|
||||
let block: CustomMethodCardFieldBlock;
|
||||
switch (fieldTypeModal) {
|
||||
case "text":
|
||||
block = {
|
||||
kind: "text",
|
||||
id,
|
||||
blockTitle: textBlockTitle.trim(),
|
||||
placeholderText: textPlaceholderBody,
|
||||
};
|
||||
break;
|
||||
case "badges":
|
||||
block = {
|
||||
kind: "badges",
|
||||
id,
|
||||
blockTitle: badgeBlockTitle.trim(),
|
||||
options: [...badgeOptions],
|
||||
};
|
||||
break;
|
||||
case "upload":
|
||||
block = {
|
||||
kind: "upload",
|
||||
id,
|
||||
blockTitle: uploadBlockTitle.trim(),
|
||||
fileName: uploadFileName,
|
||||
};
|
||||
break;
|
||||
default:
|
||||
block = {
|
||||
kind: "proportion",
|
||||
id,
|
||||
blockTitle: proportionBlockTitle.trim(),
|
||||
defaultPercent: proportionDefault,
|
||||
};
|
||||
}
|
||||
setDraftFieldBlocks((prev) => [...prev, block]);
|
||||
setFieldTypeModal(null);
|
||||
}, [
|
||||
badgeBlockTitle,
|
||||
badgeOptions,
|
||||
fieldModalStepValid,
|
||||
fieldTypeModal,
|
||||
proportionBlockTitle,
|
||||
proportionDefault,
|
||||
textBlockTitle,
|
||||
textPlaceholderBody,
|
||||
uploadBlockTitle,
|
||||
uploadFileName,
|
||||
]);
|
||||
|
||||
const handleNext = useCallback(() => {
|
||||
if (fieldTypeModal) {
|
||||
appendFieldBlock();
|
||||
return;
|
||||
}
|
||||
if (!stepValid) return;
|
||||
if (wizardStep === 3) {
|
||||
onFinalize({
|
||||
title: titleTrim,
|
||||
description: descriptionTrim,
|
||||
fieldBlocks: draftFieldBlocks,
|
||||
});
|
||||
dismiss();
|
||||
return;
|
||||
}
|
||||
setWizardStep((s) => (s === 1 ? 2 : 3));
|
||||
}, [
|
||||
appendFieldBlock,
|
||||
descriptionTrim,
|
||||
dismiss,
|
||||
draftFieldBlocks,
|
||||
fieldTypeModal,
|
||||
onFinalize,
|
||||
stepValid,
|
||||
titleTrim,
|
||||
wizardStep,
|
||||
]);
|
||||
|
||||
return (
|
||||
<CustomMethodCardWizardView
|
||||
isOpen={isOpen}
|
||||
onDismiss={handleShellClose}
|
||||
wizardStep={wizardStep}
|
||||
title={shellTitle}
|
||||
description={shellDescription}
|
||||
policyTitle={policyTitle}
|
||||
policyDescription={policyDescription}
|
||||
addFieldExpanded={addFieldExpanded}
|
||||
copy={copy}
|
||||
maxChars={CUSTOM_METHOD_CARD_WIZARD_MAX_FIELD_CHARS}
|
||||
onPolicyTitleChange={setPolicyTitle}
|
||||
onPolicyDescriptionChange={setPolicyDescription}
|
||||
onPressAddCustomField={() => setAddFieldExpanded(true)}
|
||||
onSelectFieldType={handleSelectFieldType}
|
||||
fieldTypeModal={fieldTypeModal}
|
||||
fieldBodiesCopy={fieldBodiesCopy}
|
||||
fieldBodiesProps={{
|
||||
textBlockTitle,
|
||||
textPlaceholderBody,
|
||||
onTextBlockTitleChange: setTextBlockTitle,
|
||||
onTextPlaceholderBodyChange: setTextPlaceholderBody,
|
||||
badgeBlockTitle,
|
||||
badgeOptions,
|
||||
onBadgeBlockTitleChange: setBadgeBlockTitle,
|
||||
onBadgeAddOption: handleBadgeAddOption,
|
||||
uploadBlockTitle,
|
||||
onUploadBlockTitleChange: setUploadBlockTitle,
|
||||
fileInputRef,
|
||||
onFileChosen: handleFileChosen,
|
||||
proportionBlockTitle,
|
||||
proportionDefault,
|
||||
onProportionBlockTitleChange: setProportionBlockTitle,
|
||||
onProportionDefaultChange: setProportionDefault,
|
||||
}}
|
||||
nextDisabled={shellNextDisabled}
|
||||
nextLabel={nextLabel}
|
||||
showBackButton
|
||||
onBack={handleBack}
|
||||
onNext={handleNext}
|
||||
stepper={!fieldTypeModal}
|
||||
/>
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
CustomMethodCardWizardContainer.displayName = "CustomMethodCardWizard";
|
||||
|
||||
export default CustomMethodCardWizardContainer;
|
||||
@@ -0,0 +1,120 @@
|
||||
import type { RefObject } from "react";
|
||||
import type { AddCustomFieldType } from "../../../../components/controls/AddCustomField/AddCustomField.types";
|
||||
import type { CustomMethodCardFieldBlock } from "../../../../../lib/create/customMethodCardFieldBlocks";
|
||||
|
||||
export interface CustomMethodCardWizardFieldBodiesCopy {
|
||||
requiredHint: string;
|
||||
text: {
|
||||
blockTitleLabel: string;
|
||||
blockTitlePlaceholder: string;
|
||||
placeholderLabel: string;
|
||||
placeholderFieldPlaceholder: string;
|
||||
};
|
||||
badges: {
|
||||
blockTitleLabel: string;
|
||||
blockTitlePlaceholder: string;
|
||||
optionsLabel: string;
|
||||
addOptionLabel: string;
|
||||
};
|
||||
upload: {
|
||||
blockTitleLabel: string;
|
||||
blockTitlePlaceholder: string;
|
||||
uploadFileInputAriaLabel: string;
|
||||
uploadHint: string;
|
||||
};
|
||||
proportion: {
|
||||
blockTitleLabel: string;
|
||||
blockTitlePlaceholder: string;
|
||||
defaultLabel: string;
|
||||
decrementAriaLabel: string;
|
||||
incrementAriaLabel: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface CustomMethodCardWizardCopy {
|
||||
step1: { title: string; description: string; fieldPlaceholder: string };
|
||||
step2: { title: string; description: string; fieldPlaceholder: string };
|
||||
step3: { title: string; description: string };
|
||||
footerFinalize: string;
|
||||
fieldModals: {
|
||||
addField: string;
|
||||
requiredHint: string;
|
||||
text: CustomMethodCardWizardFieldBodiesCopy["text"] & {
|
||||
title: string;
|
||||
description: string;
|
||||
};
|
||||
badges: CustomMethodCardWizardFieldBodiesCopy["badges"] & {
|
||||
title: string;
|
||||
description: string;
|
||||
};
|
||||
upload: CustomMethodCardWizardFieldBodiesCopy["upload"] & {
|
||||
title: string;
|
||||
description: string;
|
||||
};
|
||||
proportion: CustomMethodCardWizardFieldBodiesCopy["proportion"] & {
|
||||
title: string;
|
||||
description: string;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export interface CustomMethodCardWizardProps {
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
/** Called when the user completes step 3; parent assigns id and persists state. */
|
||||
onFinalize: (payload: {
|
||||
title: string;
|
||||
description: string;
|
||||
fieldBlocks: CustomMethodCardFieldBlock[];
|
||||
}) => void;
|
||||
}
|
||||
|
||||
export interface CustomMethodCardWizardFieldBodiesViewProps {
|
||||
fieldType: AddCustomFieldType;
|
||||
copy: CustomMethodCardWizardFieldBodiesCopy;
|
||||
textBlockTitle: string;
|
||||
textPlaceholderBody: string;
|
||||
onTextBlockTitleChange: (_v: string) => void;
|
||||
onTextPlaceholderBodyChange: (_v: string) => void;
|
||||
badgeBlockTitle: string;
|
||||
badgeOptions: string[];
|
||||
onBadgeBlockTitleChange: (_v: string) => void;
|
||||
onBadgeAddOption: (_v: string) => void;
|
||||
uploadBlockTitle: string;
|
||||
onUploadBlockTitleChange: (_v: string) => void;
|
||||
fileInputRef: RefObject<HTMLInputElement | null>;
|
||||
onFileChosen: (e: React.ChangeEvent<HTMLInputElement>) => void;
|
||||
proportionBlockTitle: string;
|
||||
proportionDefault: number;
|
||||
onProportionBlockTitleChange: (_v: string) => void;
|
||||
onProportionDefaultChange: (_v: number) => void;
|
||||
}
|
||||
|
||||
export interface CustomMethodCardWizardViewProps {
|
||||
isOpen: boolean;
|
||||
onDismiss: () => void;
|
||||
wizardStep: 1 | 2 | 3;
|
||||
title: string;
|
||||
description: string;
|
||||
policyTitle: string;
|
||||
policyDescription: string;
|
||||
addFieldExpanded: boolean;
|
||||
copy: CustomMethodCardWizardCopy;
|
||||
maxChars: number;
|
||||
onPolicyTitleChange: (v: string) => void;
|
||||
onPolicyDescriptionChange: (v: string) => void;
|
||||
onPressAddCustomField: () => void;
|
||||
onSelectFieldType: (t: AddCustomFieldType) => void;
|
||||
fieldTypeModal: AddCustomFieldType | null;
|
||||
fieldBodiesCopy: CustomMethodCardWizardFieldBodiesCopy;
|
||||
fieldBodiesProps: Omit<
|
||||
CustomMethodCardWizardFieldBodiesViewProps,
|
||||
"fieldType" | "copy"
|
||||
>;
|
||||
nextDisabled: boolean;
|
||||
nextLabel: string;
|
||||
showBackButton: boolean;
|
||||
onBack: () => void;
|
||||
onNext: () => void;
|
||||
stepper: boolean;
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
"use client";
|
||||
|
||||
import { memo } from "react";
|
||||
import Create from "../../../../components/modals/Create";
|
||||
import InputWithCounter from "../../../../components/controls/InputWithCounter";
|
||||
import TextArea from "../../../../components/controls/TextArea";
|
||||
import AddCustomField from "../../../../components/controls/AddCustomField";
|
||||
import { CustomMethodCardWizardFieldBodiesView } from "./CustomMethodCardWizardFieldBodies.view";
|
||||
import type { CustomMethodCardWizardViewProps } from "./CustomMethodCardWizard.types";
|
||||
|
||||
function CustomMethodCardWizardViewComponent({
|
||||
isOpen,
|
||||
onDismiss,
|
||||
wizardStep,
|
||||
title,
|
||||
description,
|
||||
policyTitle,
|
||||
policyDescription,
|
||||
addFieldExpanded,
|
||||
copy,
|
||||
maxChars,
|
||||
onPolicyTitleChange,
|
||||
onPolicyDescriptionChange,
|
||||
onPressAddCustomField,
|
||||
onSelectFieldType,
|
||||
fieldTypeModal,
|
||||
fieldBodiesCopy,
|
||||
fieldBodiesProps,
|
||||
nextDisabled,
|
||||
nextLabel,
|
||||
showBackButton,
|
||||
onBack,
|
||||
onNext,
|
||||
stepper,
|
||||
}: CustomMethodCardWizardViewProps) {
|
||||
return (
|
||||
<Create
|
||||
isOpen={isOpen}
|
||||
onClose={onDismiss}
|
||||
title={title}
|
||||
description={description}
|
||||
showBackButton={showBackButton}
|
||||
showNextButton
|
||||
onBack={onBack}
|
||||
onNext={onNext}
|
||||
nextButtonText={nextLabel}
|
||||
nextButtonDisabled={nextDisabled}
|
||||
currentStep={wizardStep}
|
||||
totalSteps={3}
|
||||
stepper={stepper}
|
||||
backdropVariant="blurredYellow"
|
||||
>
|
||||
{fieldTypeModal ? (
|
||||
<CustomMethodCardWizardFieldBodiesView
|
||||
fieldType={fieldTypeModal}
|
||||
copy={fieldBodiesCopy}
|
||||
{...fieldBodiesProps}
|
||||
/>
|
||||
) : null}
|
||||
{!fieldTypeModal && wizardStep === 1 ? (
|
||||
<InputWithCounter
|
||||
placeholder={copy.step1.fieldPlaceholder}
|
||||
value={policyTitle}
|
||||
onChange={onPolicyTitleChange}
|
||||
maxLength={maxChars}
|
||||
/>
|
||||
) : null}
|
||||
{!fieldTypeModal && wizardStep === 2 ? (
|
||||
<TextArea
|
||||
appearance="default"
|
||||
formHeader={false}
|
||||
placeholder={copy.step2.fieldPlaceholder}
|
||||
value={policyDescription}
|
||||
maxLength={maxChars}
|
||||
onChange={(e) => onPolicyDescriptionChange(e.target.value)}
|
||||
textHint={`${policyDescription.length}/${maxChars}`}
|
||||
className="w-full"
|
||||
rows={4}
|
||||
/>
|
||||
) : null}
|
||||
{!fieldTypeModal && wizardStep === 3 ? (
|
||||
<AddCustomField
|
||||
active={addFieldExpanded}
|
||||
onPressAdd={onPressAddCustomField}
|
||||
onSelectFieldType={onSelectFieldType}
|
||||
/>
|
||||
) : null}
|
||||
</Create>
|
||||
);
|
||||
}
|
||||
|
||||
export const CustomMethodCardWizardView = memo(
|
||||
CustomMethodCardWizardViewComponent,
|
||||
);
|
||||
CustomMethodCardWizardView.displayName = "CustomMethodCardWizardView";
|
||||
+161
@@ -0,0 +1,161 @@
|
||||
"use client";
|
||||
|
||||
import { memo } from "react";
|
||||
import InputWithCounter from "../../../../components/controls/InputWithCounter";
|
||||
import TextArea from "../../../../components/controls/TextArea";
|
||||
import TextInput from "../../../../components/controls/TextInput";
|
||||
import Upload from "../../../../components/controls/Upload";
|
||||
import IncrementerBlock from "../../../../components/controls/IncrementerBlock";
|
||||
import InputLabel from "../../../../components/type/InputLabel";
|
||||
import ApplicableScopeField from "../ApplicableScopeField";
|
||||
import { CUSTOM_METHOD_CARD_WIZARD_MAX_FIELD_CHARS } from "../../../../../lib/create/customMethodCardWizardConstants";
|
||||
import type { CustomMethodCardWizardFieldBodiesViewProps } from "./CustomMethodCardWizard.types";
|
||||
|
||||
const TEXT_PLACEHOLDER_MAX = 8000;
|
||||
|
||||
function CustomMethodCardWizardFieldBodiesViewComponent({
|
||||
fieldType,
|
||||
copy,
|
||||
textBlockTitle,
|
||||
textPlaceholderBody,
|
||||
onTextBlockTitleChange,
|
||||
onTextPlaceholderBodyChange,
|
||||
badgeBlockTitle,
|
||||
badgeOptions,
|
||||
onBadgeBlockTitleChange,
|
||||
onBadgeAddOption,
|
||||
uploadBlockTitle,
|
||||
onUploadBlockTitleChange,
|
||||
fileInputRef,
|
||||
onFileChosen,
|
||||
proportionBlockTitle,
|
||||
proportionDefault,
|
||||
onProportionBlockTitleChange,
|
||||
onProportionDefaultChange,
|
||||
}: CustomMethodCardWizardFieldBodiesViewProps) {
|
||||
if (fieldType === "text") {
|
||||
return (
|
||||
<div className="flex flex-col gap-[var(--spacing-scale-024)]">
|
||||
<InputWithCounter
|
||||
label={copy.text.blockTitleLabel}
|
||||
placeholder={copy.text.blockTitlePlaceholder}
|
||||
value={textBlockTitle}
|
||||
onChange={onTextBlockTitleChange}
|
||||
maxLength={CUSTOM_METHOD_CARD_WIZARD_MAX_FIELD_CHARS}
|
||||
showHelpIcon
|
||||
/>
|
||||
<div className="flex flex-col gap-2">
|
||||
<InputLabel
|
||||
label={copy.text.placeholderLabel}
|
||||
helpIcon
|
||||
size="s"
|
||||
palette="default"
|
||||
/>
|
||||
<TextArea
|
||||
formHeader={false}
|
||||
appearance="embedded"
|
||||
value={textPlaceholderBody}
|
||||
onChange={(e) => onTextPlaceholderBodyChange(e.target.value)}
|
||||
maxLength={TEXT_PLACEHOLDER_MAX}
|
||||
placeholder={copy.text.placeholderFieldPlaceholder}
|
||||
textHint={`${textPlaceholderBody.length}/${TEXT_PLACEHOLDER_MAX}`}
|
||||
className="w-full"
|
||||
rows={3}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (fieldType === "badges") {
|
||||
return (
|
||||
<div className="flex flex-col gap-[var(--spacing-scale-024)]">
|
||||
<div className="flex flex-col gap-2">
|
||||
<InputLabel
|
||||
label={copy.badges.blockTitleLabel}
|
||||
helpIcon
|
||||
helperText={copy.requiredHint}
|
||||
size="s"
|
||||
palette="default"
|
||||
/>
|
||||
<TextInput
|
||||
formHeader={false}
|
||||
placeholder={copy.badges.blockTitlePlaceholder}
|
||||
value={badgeBlockTitle}
|
||||
onChange={(e) => onBadgeBlockTitleChange(e.target.value)}
|
||||
maxLength={CUSTOM_METHOD_CARD_WIZARD_MAX_FIELD_CHARS}
|
||||
showHelpIcon={false}
|
||||
/>
|
||||
</div>
|
||||
<ApplicableScopeField
|
||||
label={copy.badges.optionsLabel}
|
||||
addLabel={copy.badges.addOptionLabel}
|
||||
scopes={badgeOptions}
|
||||
selectedScopes={badgeOptions}
|
||||
onToggleScope={() => {
|
||||
/* product: all badge options stay selected */
|
||||
}}
|
||||
onAddScope={onBadgeAddOption}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (fieldType === "upload") {
|
||||
return (
|
||||
<div className="flex flex-col gap-[var(--spacing-scale-024)]">
|
||||
<input
|
||||
ref={fileInputRef}
|
||||
type="file"
|
||||
className="sr-only"
|
||||
tabIndex={-1}
|
||||
aria-label={copy.upload.uploadFileInputAriaLabel}
|
||||
onChange={onFileChosen}
|
||||
/>
|
||||
<InputWithCounter
|
||||
label={copy.upload.blockTitleLabel}
|
||||
placeholder={copy.upload.blockTitlePlaceholder}
|
||||
value={uploadBlockTitle}
|
||||
onChange={onUploadBlockTitleChange}
|
||||
maxLength={CUSTOM_METHOD_CARD_WIZARD_MAX_FIELD_CHARS}
|
||||
showHelpIcon
|
||||
/>
|
||||
<Upload
|
||||
hintText={copy.upload.uploadHint}
|
||||
onClick={() => fileInputRef.current?.click()}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-[var(--spacing-scale-024)]">
|
||||
<InputWithCounter
|
||||
label={copy.proportion.blockTitleLabel}
|
||||
placeholder={copy.proportion.blockTitlePlaceholder}
|
||||
value={proportionBlockTitle}
|
||||
onChange={onProportionBlockTitleChange}
|
||||
maxLength={CUSTOM_METHOD_CARD_WIZARD_MAX_FIELD_CHARS}
|
||||
showHelpIcon
|
||||
/>
|
||||
<IncrementerBlock
|
||||
label={copy.proportion.defaultLabel}
|
||||
value={proportionDefault}
|
||||
min={1}
|
||||
max={100}
|
||||
step={1}
|
||||
onChange={onProportionDefaultChange}
|
||||
formatValue={(v) => `${v}%`}
|
||||
decrementAriaLabel={copy.proportion.decrementAriaLabel}
|
||||
incrementAriaLabel={copy.proportion.incrementAriaLabel}
|
||||
blockClassName="w-full"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export const CustomMethodCardWizardFieldBodiesView = memo(
|
||||
CustomMethodCardWizardFieldBodiesViewComponent,
|
||||
);
|
||||
CustomMethodCardWizardFieldBodiesView.displayName =
|
||||
"CustomMethodCardWizardFieldBodiesView";
|
||||
@@ -0,0 +1,2 @@
|
||||
export { default } from "./CustomMethodCardWizard.container";
|
||||
export type { CustomMethodCardWizardProps } from "./CustomMethodCardWizard.types";
|
||||
Reference in New Issue
Block a user