diff --git a/src/containers/HSM/HSM.test.tsx b/src/containers/HSM/HSM.test.tsx index d926a138b..a1a2b89a9 100644 --- a/src/containers/HSM/HSM.test.tsx +++ b/src/containers/HSM/HSM.test.tsx @@ -147,6 +147,12 @@ describe('Add mode', () => { }); fireEvent.click(screen.getByText('Add Variable')); + fireEvent.click(screen.getByText('Add buttons')); + + fireEvent.change(screen.getByPlaceholderText('Button Title'), { target: { value: 'Call me' } }); + fireEvent.change(screen.getByPlaceholderText('Button Value'), { + target: { value: '9876543210' }, + }); await waitFor(() => { expect(screen.getByText('Hi, How are you {{1}}')).toBeInTheDocument(); diff --git a/src/containers/HSM/HSM.tsx b/src/containers/HSM/HSM.tsx index 243d72301..aece869be 100644 --- a/src/containers/HSM/HSM.tsx +++ b/src/containers/HSM/HSM.tsx @@ -49,6 +49,10 @@ const queries = { const templateIcon = ; const regexForShortcode = /^[a-z0-9_]+$/g; const dialogMessage = ' It will stop showing when you are drafting a customized message.'; +const buttonTypes: any = { + QUICK_REPLY: { value: '' }, + CALL_TO_ACTION: { type: 'phone_number', title: '', value: '' }, +}; export const HSM = () => { const [language, setLanguageId] = useState(null); @@ -70,7 +74,11 @@ export const HSM = () => { const [languageOptions, setLanguageOptions] = useState([]); const [validatingURL, setValidatingURL] = useState(false); const [isUrlValid, setIsUrlValid] = useState(); - const [templateType, setTemplateType] = useState(null); + const [templateType, setTemplateType] = useState(CALL_TO_ACTION); + const [dynamicUrlParams, setDynamicUrlParams] = useState({ + urlType: 'Static', + sampleSuffix: '', + }); const [sampleMessages, setSampleMessages] = useState({ type: 'TEXT', location: null, @@ -148,23 +156,34 @@ export const HSM = () => { }; const getLanguageId = (value: any) => { - let result: any; - const selected = languageOptions.find((option: any) => option.label === value); - result = selected; + if (!value.label) { + return; + } - if (result) setLanguageId(result); + const selected = languageOptions.find((option: any) => option.label === value.label); + setLanguageId(selected); }; // Creating payload for button template - const getButtonTemplatePayload = () => { - const buttons = templateButtons.reduce((result: any, button) => { + const getButtonTemplatePayload = (urlType: string, sampleSuffix: string) => { + const buttons = templateButtons.reduce((result: any, button: any) => { const { type: buttonType, value, title }: any = button; + if (templateType === CALL_TO_ACTION) { const typeObj: any = { phone_number: 'PHONE_NUMBER', url: 'URL', }; - const obj: any = { type: typeObj[buttonType], text: title, [buttonType]: value }; + let obj: any = { type: typeObj[buttonType], text: title, [buttonType]: value }; + + if (buttonType === 'url' && urlType === 'Dynamic') { + obj = { + type: typeObj[buttonType], + text: title, + [buttonType]: `${value}{{1}}`, + example: [`${value}${sampleSuffix}`], + }; + } result.push(obj); } @@ -188,6 +207,10 @@ export const HSM = () => { }; }; + const handleDynamicParamsChange = (value: any) => { + setDynamicUrlParams(value); + }; + const setStates = ({ isActive: isActiveValue, language: languageIdValue, @@ -265,6 +288,7 @@ export const HSM = () => { const setPayload = (payload: any) => { let payloadCopy = { ...payload, isHsm: true }; + const { urlType, sampleSuffix } = dynamicUrlParams; if (isEditing) { payloadCopy.shortcode = payloadCopy.newShortcode; } else { @@ -274,7 +298,7 @@ export const HSM = () => { payloadCopy.languageId = payload.language.id; payloadCopy.example = getExampleFromBody(payloadCopy.body, variables); if (isAddButtonChecked && templateType) { - const templateButtonData = getButtonTemplatePayload(); + const templateButtonData = getButtonTemplatePayload(urlType, sampleSuffix); Object.assign(payloadCopy, { ...templateButtonData }); } if (payloadCopy.type) { @@ -318,13 +342,9 @@ export const HSM = () => { const addTemplateButtons = (addFromTemplate: boolean = true) => { let buttons: any = []; - const buttonType: any = { - QUICK_REPLY: { value: '' }, - CALL_TO_ACTION: { type: '', title: '', value: '' }, - }; if (templateType) { - buttons = addFromTemplate ? [...templateButtons, buttonType[templateType]] : [buttonType[templateType]]; + buttons = addFromTemplate ? [...templateButtons, buttonTypes[templateType]] : [buttonTypes[templateType]]; } setTemplateButtons(buttons); @@ -358,8 +378,13 @@ export const HSM = () => { const handeInputChange = (event: any, row: any, index: any, eventType: any) => { const { value } = event.target; - const obj = { ...row }; - obj[eventType] = value; + let obj = { ...row }; + + if (eventType === 'type') { + obj = { type: value, title: '', value: '' }; + } else { + obj[eventType] = value; + } const result = templateButtons.map((val: any, idx: number) => { if (idx === index) return obj; @@ -369,6 +394,11 @@ export const HSM = () => { setTemplateButtons(result); }; + const handleTemplateTypeChange = (value: string) => { + setTemplateButtons([buttonTypes[value]]); + setTemplateType(value); + }; + const getMediaId = async (payload: any) => { const data = await createMediaMessage({ variables: { @@ -504,7 +534,9 @@ export const HSM = () => { onAddClick: addTemplateButtons, onRemoveClick: removeTemplateButtons, onInputChange: handeInputChange, - onTemplateTypeChange: (value: string) => setTemplateType(value), + onTemplateTypeChange: handleTemplateTypeChange, + dynamicUrlParams, + onDynamicParamsChange: handleDynamicParamsChange, }, { component: AutoComplete, @@ -630,6 +662,22 @@ export const HSM = () => { then: (schema) => schema.nullable().required(t('Element name is required.')), otherwise: (schema) => schema.nullable(), }), + templateButtons: Yup.array().of( + Yup.lazy(() => { + if (templateType === 'CALL_TO_ACTION') { + return Yup.object().shape({ + type: Yup.string().required('Type is required.'), + title: Yup.string().required('Title is required.'), + value: Yup.string().required('Value is required.'), + }); + } else if (templateType === 'QUICK_REPLY') { + return Yup.object().shape({ + value: Yup.string().required('Value is required.'), + }); + } + return Yup.object().shape({}); + }) + ), }; const FormSchema = Yup.object().shape(validation, [['type', 'attachmentURL']]); @@ -667,25 +715,26 @@ export const HSM = () => { }, [isAddButtonChecked]); useEffect(() => { - if (templateButtons.length > 0 && !isEditing) { - const parse = convertButtonsToTemplate(templateButtons, templateType); + const { message }: any = getTemplateAndButton(getExampleFromBody(body, variables)); - const parsedText = parse.length ? `| ${parse.join(' | ')}` : null; + if (!isEditing) { + let parse: any = []; + if (templateButtons.length > 0) { + parse = convertButtonsToTemplate(templateButtons, templateType); + } - const { message }: any = getTemplateAndButton(getExampleFromBody(body, variables)); + const parsedText = parse.length ? `| ${parse.join(' | ')}` : ''; + + let sampleText: any = message; + if (parsedText) { + sampleText = (message || ' ') + parsedText; + } - const sampleText: any = parsedText && message + parsedText; if (sampleText) { setSimulatorMessage(sampleText); } } - }, [templateButtons]); - - useEffect(() => { - if (!isEditing) { - setSimulatorMessage(getExampleFromBody(body, variables)); - } - }, [body, variables]); + }, [templateButtons, body, variables]); useEffect(() => { setVariables(getVariables(body, variables)); diff --git a/src/containers/TemplateOptions/TemplateOptions.module.css b/src/containers/TemplateOptions/TemplateOptions.module.css index ee47e6f82..00895f067 100644 --- a/src/containers/TemplateOptions/TemplateOptions.module.css +++ b/src/containers/TemplateOptions/TemplateOptions.module.css @@ -14,6 +14,9 @@ .CallToActionWrapper { composes: Font; + display: flex; + flex-direction: column; + gap: 0.5rem; } .CallToActionWrapper > div { @@ -42,7 +45,7 @@ .RadioStyles { composes: Font; - margin-bottom: 12px; + margin-bottom: 0.5rem; } .RadioLabel > span:last-child { @@ -57,10 +60,6 @@ display: block !important; } -.FormControl { - margin-bottom: 17px; -} - .FormControl > p { margin-left: 12px; } @@ -115,3 +114,19 @@ .Button { margin-top: 7px; } + +.FieldLabel { + font-weight: 500; + font-size: 16px; + line-height: 18px; + color: #555555; + padding-bottom: 14px; +} + +.DefaultInputRoot { + background-color: #ffffff; +} + +.StartAdornment { + padding-right: 0.5rem; +} diff --git a/src/containers/TemplateOptions/TemplateOptions.test.tsx b/src/containers/TemplateOptions/TemplateOptions.test.tsx index 870e96870..32eb97304 100644 --- a/src/containers/TemplateOptions/TemplateOptions.test.tsx +++ b/src/containers/TemplateOptions/TemplateOptions.test.tsx @@ -13,6 +13,11 @@ const props = (isAddButtonChecked: any, templateType: any, inputFields: any, for templateType, inputFields, form, + dynamicUrlParams: { + urlType: 'Static', + sampleSuffix: '', + }, + onDynamicParamsChange: () => {}, }); const callToAction = { type: 'phone_number', value: '', title: '' }; diff --git a/src/containers/TemplateOptions/TemplateOptions.tsx b/src/containers/TemplateOptions/TemplateOptions.tsx index 66d2215f8..e7f1e1932 100644 --- a/src/containers/TemplateOptions/TemplateOptions.tsx +++ b/src/containers/TemplateOptions/TemplateOptions.tsx @@ -1,4 +1,13 @@ -import { RadioGroup, FormControlLabel, Radio, TextField, FormHelperText, FormControl } from '@mui/material'; +import { + RadioGroup, + FormControlLabel, + Radio, + TextField, + FormHelperText, + FormControl, + Autocomplete, + Typography, +} from '@mui/material'; import { FieldArray } from 'formik'; import { Button } from 'components/UI/Form/Button/Button'; @@ -14,23 +23,27 @@ export interface TemplateOptionsProps { isAddButtonChecked: boolean; templateType: string | null; inputFields: Array; - form: { touched: any; errors: any; values: any }; + form: { touched: any; errors: any; values: any; setFieldValue: any }; onAddClick: any; onRemoveClick: any; onInputChange: any; onTemplateTypeChange: any; disabled: any; + dynamicUrlParams: any; + onDynamicParamsChange: any; } export const TemplateOptions = ({ isAddButtonChecked, templateType, inputFields, - form: { touched, errors, values }, + form: { touched, errors }, onAddClick, onRemoveClick, onTemplateTypeChange, onInputChange, disabled = false, + dynamicUrlParams, + onDynamicParamsChange, }: TemplateOptionsProps) => { const buttonTitle = 'Button Title'; const buttonValue = 'Button Value'; @@ -38,7 +51,8 @@ export const TemplateOptions = ({ CALL_TO_ACTION: 'Call to action', QUICK_REPLY: 'Quick Reply', }; - + const options = ['Static', 'Dynamic']; + const { urlType, sampleSuffix } = dynamicUrlParams; const handleAddClick = (helper: any, type: boolean) => { const obj = type ? { type: '', value: '', title: '' } : { value: '' }; helper.push(obj); @@ -133,15 +147,32 @@ export const TemplateOptions = ({ ) : null} + {type === 'url' && ( +
+ } + clearIcon={false} + value={urlType} + onChange={(event: any, newValue: string | null) => { + onDynamicParamsChange({ + ...dynamicUrlParams, + urlType: newValue, + }); + }} + /> +
+ )}
onInputChange(e, row, index, 'title')} + onChange={(e: any) => onInputChange(e, row, index, 'title')} className={styles.TextField} error={isError('title')} /> @@ -154,11 +185,11 @@ export const TemplateOptions = ({ onInputChange(e, row, index, 'value')} + onChange={(e: any) => onInputChange(e, row, index, 'value')} className={styles.TextField} error={isError('value')} /> @@ -167,7 +198,37 @@ export const TemplateOptions = ({ ) : null}
+ {urlType === 'Dynamic' && type === 'url' && ( +
+ + {`{{1}}`} + ), + }, + }} + onChange={(event) => + onDynamicParamsChange({ + ...dynamicUrlParams, + sampleSuffix: event.target.value, + }) + } + value={sampleSuffix} + /> + +
+ )} +
{inputFields.length === index + 1 && inputFields.length !== 2 ? addButton(arrayHelpers, true) : null}
@@ -182,22 +243,24 @@ export const TemplateOptions = ({ onInputChange(e, row, index, 'value')} + onChange={(e: any) => onInputChange(e, row, index, 'value')} className={styles.TextField} error={isError('value')} - InputProps={{ - endAdornment: inputFields.length > 1 && !disabled && ( - handleRemoveClick(arrayHelpers, index)} - /> - ), + slotProps={{ + input: { + endAdornment: inputFields.length > 1 && !disabled && ( + handleRemoveClick(arrayHelpers, index)} + /> + ), + }, }} /> {errors.templateButtons && touched.templateButtons && touched.templateButtons[index] ? ( @@ -251,7 +314,7 @@ export const TemplateOptions = ({ name="templateButtons" render={(arrayHelpers: any) => (
- {values.templateButtons.map((row: any, index: any) => ( + {inputFields.map((row: any, index: any) => (
{getButtons(row, index, arrayHelpers)}
))}
diff --git a/src/mocks/Template.tsx b/src/mocks/Template.tsx index 9e6bb62c5..888ca7fa1 100644 --- a/src/mocks/Template.tsx +++ b/src/mocks/Template.tsx @@ -280,6 +280,9 @@ export const templateMock = createTemplateMock({ languageId: '1', example: 'Hi, How are you [User]', shortcode: 'element_name', + hasButtons: true, + buttons: '[{"type":"PHONE_NUMBER","text":"Call me","phone_number":"9876543210"}]', + buttonType: 'CALL_TO_ACTION', }); export const quickReplyMock = createTemplateMock({ @@ -295,7 +298,7 @@ export const quickReplyMock = createTemplateMock({ example: 'Hi', shortcode: 'welcome', hasButtons: true, - buttons: '[{"type":"QUICK_REPLY","text":""},{"type":"QUICK_REPLY","text":""}]', + buttons: '[{"type":"QUICK_REPLY","text":"Yes"},{"type":"QUICK_REPLY","text":"No"}]', buttonType: 'QUICK_REPLY', }); @@ -312,7 +315,7 @@ export const ctaMock = createTemplateMock({ example: 'Hi', shortcode: 'welcome', hasButtons: true, - buttons: '[{"type":"PHONE_NUMBER","text":"","phone_number":""}]', + buttons: '[{"type":"PHONE_NUMBER","text":"Call me","phone_number":"9876543210"}]', buttonType: 'CALL_TO_ACTION', });