From 6a257dbb78fefe0541c7c5e5dc3c298c3993db31 Mon Sep 17 00:00:00 2001 From: akanshaaa19 Date: Tue, 17 Mar 2026 09:15:17 +0530 Subject: [PATCH 01/13] refactor: replace deprecated Apollo onCompleted/onError callbacks with async/await --- .../Layout/Navigation/SideMenus/SideMenus.tsx | 8 +- src/containers/Flow/FlowList/FlowList.tsx | 82 +++++++---------- src/containers/Form/FormLayout.tsx | 90 +++++++++---------- src/containers/HSM/HSM.tsx | 35 +++----- src/containers/List/List.tsx | 25 +++--- src/containers/Trigger/Trigger.tsx | 16 ++-- 6 files changed, 111 insertions(+), 145 deletions(-) diff --git a/src/components/UI/Layout/Navigation/SideMenus/SideMenus.tsx b/src/components/UI/Layout/Navigation/SideMenus/SideMenus.tsx index 4339eadefa..1aac4d2015 100644 --- a/src/components/UI/Layout/Navigation/SideMenus/SideMenus.tsx +++ b/src/components/UI/Layout/Navigation/SideMenus/SideMenus.tsx @@ -67,19 +67,17 @@ const SideMenus = ({ opened }: SideMenusProps) => { const { t } = useTranslation(); // handle count for notifictions - const [notificationCount, setNotificationCount] = useState(0); - const [getNotificationCount] = useLazyQuery(GET_NOTIFICATIONS_COUNT, { + const [getNotificationCount, { data: notificationData }] = useLazyQuery(GET_NOTIFICATIONS_COUNT, { variables: { filter: { is_read: false, }, }, fetchPolicy: 'cache-and-network', - onCompleted: (countData) => { - setNotificationCount(countData.countNotifications); - }, }); + const notificationCount = notificationData?.countNotifications ?? 0; + useEffect(() => { getNotificationCount(); }, []); diff --git a/src/containers/Flow/FlowList/FlowList.tsx b/src/containers/Flow/FlowList/FlowList.tsx index 3166b8bea6..41a6b96ed1 100644 --- a/src/containers/Flow/FlowList/FlowList.tsx +++ b/src/containers/Flow/FlowList/FlowList.tsx @@ -75,7 +75,6 @@ export const FlowList = () => { const { t } = useTranslation(); const [filter, setFilter] = useState(true); const [selectedtag, setSelectedTag] = useState(null); - const [flowName, setFlowName] = useState(''); const [importing, setImporting] = useState(false); const [importStatus, setImportStatus] = useState([]); const [showDialog, setShowDialog] = useState(false); @@ -89,28 +88,10 @@ export const FlowList = () => { releaseFlow(); }, []); - const [importFlow] = useMutation(IMPORT_FLOW, { - onCompleted: (result: any) => { - const { status } = result.importFlow; - setImportStatus(status); - setImporting(false); - }, - onError: (error: any) => { - setNotification('An error occured while importing the flow', 'warning'); - setImporting(false); - }, - }); + const [importFlow] = useMutation(IMPORT_FLOW); const [exportFlowMutation] = useLazyQuery(EXPORT_FLOW, { fetchPolicy: 'network-only', - onCompleted: async ({ exportFlow }) => { - const { exportData } = exportFlow; - await exportFlowMethod(exportData, flowName); - setNotification('Flow exported successfully'); - }, - onError: (error: any) => { - setErrorMessage(error); - }, }); const [updatePinned] = useMutation(PIN_FLOW); @@ -123,41 +104,40 @@ export const FlowList = () => { navigate(`/flow/${id}/edit`, { state: 'copy' }); }; - const exportFlow = (id: any, item: any) => { - setFlowName(item.name); - exportFlowMutation({ variables: { id } }); + const exportFlow = async (id: any, item: any) => { + try { + const result = await exportFlowMutation({ variables: { id } }); + const { exportData } = result.data.exportFlow; + await exportFlowMethod(exportData, item.name); + setNotification('Flow exported successfully'); + } catch (error: any) { + setErrorMessage(error); + } }; - const handlePin = (updateFlowId: any, pin: boolean = false) => { - if (pin) { - updatePinned({ - variables: { - updateFlowId, - input: { - isPinned: true, - }, - }, - onCompleted: () => { - setRefreshList(!refreshList); - setNotification('Flow pinned successfully'); - }, - }); - } else { - updatePinned({ - variables: { - updateFlowId, - input: { - isPinned: false, - }, - }, - onCompleted: () => { - setRefreshList(!refreshList); - setNotification('Flow unpinned successfully'); - }, - }); + const handleImport = async (result: string) => { + try { + const { data } = await importFlow({ variables: { flow: result } }); + const { status } = data.importFlow; + setImportStatus(status); + } catch { + setNotification('An error occured while importing the flow', 'warning'); + } finally { + setImporting(false); } }; + const handlePin = async (updateFlowId: any, pin: boolean = false) => { + await updatePinned({ + variables: { + updateFlowId, + input: { isPinned: pin }, + }, + }); + setRefreshList(!refreshList); + setNotification(pin ? 'Flow pinned successfully' : 'Flow unpinned successfully'); + }; + let dialog; const displayPinned = (isPinned: boolean, id: any) => { @@ -250,7 +230,7 @@ export const FlowList = () => { setImporting(true)} - afterImport={(result: string) => importFlow({ variables: { flow: result } })} + afterImport={handleImport} /> ); diff --git a/src/containers/Form/FormLayout.tsx b/src/containers/Form/FormLayout.tsx index cfb5ad4092..3efb8023bb 100644 --- a/src/containers/Form/FormLayout.tsx +++ b/src/containers/Form/FormLayout.tsx @@ -153,13 +153,11 @@ export const FormLayout = ({ }: FormLayoutProps) => { const [showDialog, setShowDialog] = useState(false); const [formSubmitted, setFormSubmitted] = useState(false); - const [languageId, setLanguageId] = useState(''); const [formCancelled, setFormCancelled] = useState(false); const [action, setAction] = useState(false); const [link, setLink] = useState(undefined); const [deleted, setDeleted] = useState(false); const [saveClick, onSaveClick] = useState(false); - const [isLoadedData, setIsLoadedData] = useState(false); const [customError, setCustomError] = useState(null); const [showConfirmationDialog, setShowConfirmationDialog] = useState(false); const params = useParams(); @@ -251,6 +249,50 @@ export const FormLayout = ({ } }; + const capitalListItemName = listItemName[0].toUpperCase() + listItemName.slice(1); + + let itemId = entityId; + if (!itemId) { + itemId = params.id; + } + + let variables: any = itemId ? { [idType]: itemId } : false; + if (listItem === 'credential') { + variables = params.type ? { shortcode: params.type } : false; + } + + const organization = useQuery(USER_LANGUAGES, { + skip: !languageSupport, + }); + + const { loading, error, data: itemData, refetch } = useQuery(getItemQuery, { + variables, + skip: !itemId, + fetchPolicy: getQueryFetchPolicy, + onCompleted: (data) => { + if (data) { + const loadedItem = data[listItem]?.[listItem] ?? data[Object.keys(data)[0]]?.[listItem]; + if (loadedItem && setStates) { + setStates(loadedItem); + } + } + }, + }); + + const fetchedItem: any = itemData + ? (itemData[listItem]?.[listItem] ?? itemData[Object.keys(itemData)[0]]?.[listItem] ?? null) + : null; + + const isLoadedData = Boolean(fetchedItem); + + const languageId = fetchedItem + ? languageSupport + ? fetchedItem.language.id + : null + : !itemId && organization.data + ? organization.data.currentUser.user.organization.defaultLanguage.id + : ''; + const formik = useFormik({ initialValues: { languageId, @@ -284,16 +326,6 @@ export const FormLayout = ({ } }, [entityId]); - const capitalListItemName = listItemName[0].toUpperCase() + listItemName.slice(1); - let item: any = null; - - let itemId = entityId; - if (!itemId) { - itemId = params.id; - } - - let variables: any = itemId ? { [idType]: itemId } : false; - const [deleteItem] = useMutation(deleteItemQuery, { onCompleted: () => { setNotification(`${capitalListItemName} deleted successfully`); @@ -312,39 +344,6 @@ export const FormLayout = ({ ], }); - // get the organization for current user and have languages option set to that. - - const organization = useQuery(USER_LANGUAGES, { - skip: !languageSupport, - onCompleted: (data: any) => { - if (!itemId) { - setLanguageId(data.currentUser.user.organization.defaultLanguage.id); - } - }, - }); - if (listItem === 'credential') { - variables = params.type ? { shortcode: params.type } : false; - } - - const { loading, error, refetch } = useQuery(getItemQuery, { - variables, - skip: !itemId, - fetchPolicy: getQueryFetchPolicy, - onCompleted: (data) => { - if (data) { - item = data[listItem] ? data[listItem][listItem] : data[Object.keys(data)[0]][listItem]; - if (item) { - setIsLoadedData(true); - setLink(data[listItem] ? data[listItem][listItem][linkParameter] : item.linkParameter); - setLanguageId(languageSupport ? item.language.id : null); - if (setStates) { - setStates(item); - } - } - } - }, - }); - const camelCaseItem = listItem[0].toUpperCase() + listItem.slice(1); const [updateItem] = useMutation(updateItemQuery, { @@ -448,7 +447,6 @@ export const FormLayout = ({ afterSave(data, saveClick); } } - setIsLoadedData(true); onSaveClick(false); }, refetchQueries: () => { diff --git a/src/containers/HSM/HSM.tsx b/src/containers/HSM/HSM.tsx index 48328476a2..dacc955d82 100644 --- a/src/containers/HSM/HSM.tsx +++ b/src/containers/HSM/HSM.tsx @@ -127,20 +127,9 @@ export const HSM = () => { setUploadedFile(null); }; - const [uploadMedia] = useMutation(UPLOAD_MEDIA, { - onCompleted: (data: { uploadMedia: string }) => { - setAttachmentURL(data.uploadMedia); - setNotification('File uploaded successfully'); - setUploadingFile(false); - }, - onError: (error: Error) => { - console.error('Upload error:', error); - setNotification('File upload failed. Please try again.'); - resetUploadState(); - }, - }); + const [uploadMedia] = useMutation(UPLOAD_MEDIA); - const handleFileUpload = (file: File): void => { + const handleFileUpload = async (file: File): Promise => { if (!file) return; const mediaName = file.name; @@ -156,12 +145,16 @@ export const HSM = () => { setType({ id: 'DOCUMENT', label: 'DOCUMENT' }); } - uploadMedia({ - variables: { - media: file, - extension, - }, - }); + try { + const result = await uploadMedia({ variables: { media: file, extension } }); + setAttachmentURL(result.data.uploadMedia); + setNotification('File uploaded successfully'); + setUploadingFile(false); + } catch (error) { + console.error('Upload error:', error); + setNotification('File upload failed. Please try again.', 'error'); + resetUploadState(); + } }; let attachmentOptions = mediaOptions; @@ -736,8 +729,8 @@ export const HSM = () => { helperText: uploadedFile ? `File uploaded: ${uploadedFile.name}` : t( - 'Please provide a sample attachment for approval purpose. You may send a similar but different attachment when sending the HSM to users.' - ), + 'Please provide a sample attachment for approval purpose. You may send a similar but different attachment when sending the HSM to users.' + ), inputProp: { onBlur: (event: any) => { setAttachmentURL(event.target.value.trim()); diff --git a/src/containers/List/List.tsx b/src/containers/List/List.tsx index f373374387..08710e074a 100644 --- a/src/containers/List/List.tsx +++ b/src/containers/List/List.tsx @@ -381,14 +381,6 @@ export const List = ({ // Make a new count request for a new count of the # of rows from this query in the back-end. if (deleteItemQuery) { [deleteItem] = useMutation(deleteItemQuery, { - onCompleted: () => { - setNotification(`${capitalListItemName} deleted successfully`); - checkUserRole(); - countQuery && refetchCount(); - if (refetchValues) { - refetchValues(filterPayload()); - } - }, refetchQueries: () => { if (refetchQueries) return refetchQueries.map((refetchQuery: any) => ({ @@ -397,9 +389,6 @@ export const List = ({ })); return []; }, - onError: () => { - setNotification(`Sorry! An error occurred!`, 'warning'); - }, }); } @@ -412,9 +401,19 @@ export const List = ({ setDeleteItemID(null); }; - const deleteHandler = (id: number) => { + const deleteHandler = async (id: number) => { const variables = deleteModifier.variables ? deleteModifier.variables(id) : { id }; - deleteItem({ variables }); + try { + await deleteItem({ variables }); + setNotification(`${capitalListItemName} deleted successfully`); + checkUserRole(); + countQuery && refetchCount(); + if (refetchValues) { + refetchValues(filterPayload()); + } + } catch { + setNotification(`Sorry! An error occurred!`, 'warning'); + } }; const handleDeleteItem = () => { diff --git a/src/containers/Trigger/Trigger.tsx b/src/containers/Trigger/Trigger.tsx index 040961a96c..71e7363b2f 100644 --- a/src/containers/Trigger/Trigger.tsx +++ b/src/containers/Trigger/Trigger.tsx @@ -246,24 +246,22 @@ export const Trigger = () => { variables: isEditing ? setVariables() : setVariables({ groupType }), }); - const [validateTriggerFlow, { loading }] = useMutation(VALIDATE_TRIGGER, { - onCompleted: ({ validateTrigger }) => { - if (!validateTrigger.success && validateTrigger.errors && validateTrigger.errors.length > 0) { - setTriggerFlowWarning(validateTrigger.errors[0].message); - } - }, - }); + const [validateTriggerFlow, { loading }] = useMutation(VALIDATE_TRIGGER); - const handleFlowChange = (flow: any) => { + const handleFlowChange = async (flow: any) => { setTriggerFlowWarning(undefined); if (flow) { - validateTriggerFlow({ + const result = await validateTriggerFlow({ variables: { input: { flowId: flow.id, }, }, }); + const { validateTrigger } = result.data; + if (!validateTrigger.success && validateTrigger.errors && validateTrigger.errors.length > 0) { + setTriggerFlowWarning(validateTrigger.errors[0].message); + } } }; From 750725f60ab608fc2f630b994a0c72654606f651 Mon Sep 17 00:00:00 2001 From: akanshaaa19 Date: Tue, 17 Mar 2026 10:56:55 +0530 Subject: [PATCH 02/13] refactor: update mutation handling to use async/await for better error management in HSMList and Providers components --- src/containers/HSM/HSMList/HSMList.tsx | 117 +++++++++--------- .../SettingList/Providers/Providers.tsx | 5 + 2 files changed, 63 insertions(+), 59 deletions(-) diff --git a/src/containers/HSM/HSMList/HSMList.tsx b/src/containers/HSM/HSMList/HSMList.tsx index f67c0cea71..adc9e29cf1 100644 --- a/src/containers/HSM/HSMList/HSMList.tsx +++ b/src/containers/HSM/HSMList/HSMList.tsx @@ -90,45 +90,9 @@ export const HSMList = () => { const { t } = useTranslation(); const navigate = useNavigate(); - const [bulkApplyTemplates] = useMutation(BULK_APPLY_TEMPLATES, { - onCompleted: (data: any) => { - setImporting(false); - if (data && data.bulkApplyTemplates) { - exportCsvFile(data.bulkApplyTemplates.csv_rows, 'result'); - setNotification(t('Templates applied successfully. Please check the csv file for the results')); - } - }, - onError: () => { - setImporting(false); - setNotification(t('An error occured! Please check the format of the file'), 'warning'); - }, - }); - - const [syncHsmTemplates] = useMutation(SYNC_HSM_TEMPLATES, { - fetchPolicy: 'network-only', - onCompleted: (data) => { - if (data.errors) { - setNotification(t('Sorry, failed to sync HSM updates.'), 'warning'); - } else { - setNotification(t('HSM queued for sync. Check notifications for updates.'), 'success'); - } - setSyncTemplateLoad(false); - }, - onError: () => { - setNotification(t('Sorry, failed to sync HSM updates.'), 'warning'); - setSyncTemplateLoad(false); - }, - }); - - const [importTemplatesMutation] = useMutation(IMPORT_TEMPLATES, { - onCompleted: (data: any) => { - setImporting(false); - const { errors } = data.importTemplates; - if (errors && errors.length > 0) { - setNotification(t('Error importing templates'), 'warning'); - } - }, - }); + const [bulkApplyTemplates] = useMutation(BULK_APPLY_TEMPLATES); + const [syncHsmTemplates] = useMutation(SYNC_HSM_TEMPLATES, { fetchPolicy: 'network-only' }); + const [importTemplatesMutation] = useMutation(IMPORT_TEMPLATES); const { data: tags } = useQuery(GET_TAGS, { variables: {}, @@ -222,9 +186,60 @@ export const HSMList = () => { columnStyles, }; - const handleHsmUpdates = () => { + const handleHsmUpdates = async () => { setSyncTemplateLoad(true); - syncHsmTemplates(); + try { + const { data } = await syncHsmTemplates(); + if (data.errors) { + setNotification(t('Sorry, failed to sync HSM updates.'), 'warning'); + } else { + setNotification(t('HSM queued for sync. Check notifications for updates.'), 'success'); + } + } catch { + setNotification(t('Sorry, failed to sync HSM updates.'), 'warning'); + } finally { + setSyncTemplateLoad(false); + } + }; + + const handleBulkApply = async (result: string, media: any) => { + const extension = getFileExtension(media.name); + if (extension !== 'csv') { + setNotification('Please upload a valid CSV file', 'warning'); + setImporting(false); + } else { + try { + const { data } = await bulkApplyTemplates({ variables: { data: result } }); + if (data?.bulkApplyTemplates) { + exportCsvFile(data.bulkApplyTemplates.csv_rows, 'result'); + setNotification(t('Templates applied successfully. Please check the csv file for the results')); + } + } catch { + setNotification(t('An error occured! Please check the format of the file'), 'warning'); + } finally { + setImporting(false); + } + } + }; + + const handleImportTemplates = async (result: string, media: any) => { + const extension = getFileExtension(media.name); + if (extension !== 'csv') { + setNotification('Please upload a valid CSV file', 'warning'); + setImporting(false); + } else { + try { + const { data } = await importTemplatesMutation({ variables: { data: result } }); + const { errors } = data.importTemplates; + if (errors && errors.length > 0) { + setNotification(t('Error importing templates'), 'warning'); + } + } catch { + setNotification(t('Error importing templates'), 'warning'); + } finally { + setImporting(false); + } + } }; let filterValue: any = ''; @@ -351,15 +366,7 @@ export const HSMList = () => { setImporting(true)} - afterImport={(result: string, media: any) => { - const extension = getFileExtension(media.name); - if (extension !== 'csv') { - setNotification('Please upload a valid CSV file', 'warning'); - setImporting(false); - } else { - bulkApplyTemplates({ variables: { data: result } }); - } - }} + afterImport={handleBulkApply} /> @@ -372,15 +379,7 @@ export const HSMList = () => { setImporting(true)} - afterImport={(result: string, media: any) => { - const extension = getFileExtension(media.name); - if (extension !== 'csv') { - setNotification('Please upload a valid CSV file', 'warning'); - setImporting(false); - } else { - importTemplatesMutation({ variables: { data: result } }); - } - }} + afterImport={handleImportTemplates} /> ); diff --git a/src/containers/SettingList/Providers/Providers.tsx b/src/containers/SettingList/Providers/Providers.tsx index 95930a9e9a..4332b23537 100644 --- a/src/containers/SettingList/Providers/Providers.tsx +++ b/src/containers/SettingList/Providers/Providers.tsx @@ -197,6 +197,11 @@ export const Providers = () => { addField(fields); setKeys(providerKeys); setSecrets(providerSecrets); + + const credentialData = credential?.credential?.credential; + if (credentialData) { + setCredential(credentialData); + } }); } }, [providerData, credential]); From 29851a6daf128c9107de8ef2393f51ac92543208 Mon Sep 17 00:00:00 2001 From: akanshaaa19 Date: Wed, 18 Mar 2026 09:56:57 +0530 Subject: [PATCH 03/13] refactor: remove deprecated tier state and update to use qualityRatingTier from GET_QUALITY_RATING query --- .../Organization/Organisation.test.tsx | 8 +++---- .../SettingList/Organization/Organization.tsx | 22 ++++++++----------- 2 files changed, 13 insertions(+), 17 deletions(-) diff --git a/src/containers/SettingList/Organization/Organisation.test.tsx b/src/containers/SettingList/Organization/Organisation.test.tsx index 362d21f330..f4e90fd344 100644 --- a/src/containers/SettingList/Organization/Organisation.test.tsx +++ b/src/containers/SettingList/Organization/Organisation.test.tsx @@ -10,7 +10,7 @@ const user = userEvent.setup(); const mocks = ORGANIZATION_MOCKS; const wrapper = ( - + @@ -60,7 +60,7 @@ test('it renders component and clicks cancel', async () => { test('it renders component in edit mode', async () => { const { getByText, getByTestId } = render( - + @@ -98,7 +98,7 @@ test('it renders component in edit mode', async () => { test('it renders confirmation popup with new phone number when allowBotNumberUpdate is true', async () => { const { getByText, getByTestId } = render( - + @@ -130,7 +130,7 @@ test('it renders confirmation popup with new phone number when allowBotNumberUpd test('It does not show confirmation popup with new phone number when allowBotNumberUpdate is false', async () => { const { getByText, getByTestId } = render( - + diff --git a/src/containers/SettingList/Organization/Organization.tsx b/src/containers/SettingList/Organization/Organization.tsx index d8141e26a9..0e8c242c73 100644 --- a/src/containers/SettingList/Organization/Organization.tsx +++ b/src/containers/SettingList/Organization/Organization.tsx @@ -34,35 +34,31 @@ export const Organization = () => { const [defaultLanguage, setDefaultLanguage] = useState(null); const [signaturePhrase, setSignaturePhrase] = useState(''); const [phone, setPhone] = useState(''); - const [tier, setTier] = useState(); const [lowBalanceThreshold, setLowBalanceThreshold] = useState(''); const [criticalBalanceThreshold, setCriticalBalanceThreshold] = useState(''); const [sendWarningMail, setSendWarningMail] = useState(false); const { t } = useTranslation(); + const { data: languages } = useQuery(GET_LANGUAGES, { + variables: { opts: { order: 'ASC' } }, + }); + + const { data: tierData } = useQuery(GET_QUALITY_RATING); + const qualityRatingTier = tierData?.qualityRating?.currentLimit; + const States = { name, activeLanguages, defaultLanguage, signaturePhrase, phone, - tier, + tier: qualityRatingTier, lowBalanceThreshold, criticalBalanceThreshold, sendWarningMail, }; - const { data: languages } = useQuery(GET_LANGUAGES, { - variables: { opts: { order: 'ASC' } }, - }); - - useQuery(GET_QUALITY_RATING, { - onCompleted: (tierData) => { - if (tierData) setTier(tierData.qualityRating?.currentLimit); - }, - }); - const [getOrg, { data: orgData }] = useLazyQuery(GET_ORGANIZATION); const setSettings = (data: any) => { @@ -202,7 +198,7 @@ export const Organization = () => { type: 'text', placeholder: t('WhatsApp tier'), label: t('WhatsApp tier'), - skip: !tier, + skip: !qualityRatingTier, disabled: true, }, { From 9b5cc5122e9471c8f4fb3ac014e8c2ff98770dc9 Mon Sep 17 00:00:00 2001 From: akanshaaa19 Date: Wed, 18 Mar 2026 09:57:03 +0530 Subject: [PATCH 04/13] refactor: enhance error handling and notifications in Flow, HSMList, and Trigger components; update language ID fallback in FormLayout; improve Providers component logic; add new translations for CSV validation messages --- src/containers/Flow/FlowList/FlowList.tsx | 26 +++++++++---------- src/containers/Form/FormLayout.tsx | 2 +- src/containers/HSM/HSMList/HSMList.tsx | 22 +++++++++------- .../SettingList/Providers/Providers.tsx | 10 +++---- src/containers/Trigger/Trigger.tsx | 22 +++++++++------- src/i18n/en/en.json | 6 +++-- 6 files changed, 48 insertions(+), 40 deletions(-) diff --git a/src/containers/Flow/FlowList/FlowList.tsx b/src/containers/Flow/FlowList/FlowList.tsx index 41a6b96ed1..49c6e4a967 100644 --- a/src/containers/Flow/FlowList/FlowList.tsx +++ b/src/containers/Flow/FlowList/FlowList.tsx @@ -128,14 +128,18 @@ export const FlowList = () => { }; const handlePin = async (updateFlowId: any, pin: boolean = false) => { - await updatePinned({ - variables: { - updateFlowId, - input: { isPinned: pin }, - }, - }); - setRefreshList(!refreshList); - setNotification(pin ? 'Flow pinned successfully' : 'Flow unpinned successfully'); + try { + await updatePinned({ + variables: { + updateFlowId, + input: { isPinned: pin }, + }, + }); + setRefreshList(!refreshList); + setNotification(pin ? 'Flow pinned successfully' : 'Flow unpinned successfully'); + } catch { + setNotification('Failed to update pin status', 'warning'); + } }; let dialog; @@ -227,11 +231,7 @@ export const FlowList = () => { } const importButton = ( - setImporting(true)} - afterImport={handleImport} - /> + setImporting(true)} afterImport={handleImport} /> ); const templateFlowActions = [ diff --git a/src/containers/Form/FormLayout.tsx b/src/containers/Form/FormLayout.tsx index 3efb8023bb..ec28b4a7f6 100644 --- a/src/containers/Form/FormLayout.tsx +++ b/src/containers/Form/FormLayout.tsx @@ -287,7 +287,7 @@ export const FormLayout = ({ const languageId = fetchedItem ? languageSupport - ? fetchedItem.language.id + ? fetchedItem.language?.id ?? '' : null : !itemId && organization.data ? organization.data.currentUser.user.organization.defaultLanguage.id diff --git a/src/containers/HSM/HSMList/HSMList.tsx b/src/containers/HSM/HSMList/HSMList.tsx index adc9e29cf1..53b21bc065 100644 --- a/src/containers/HSM/HSMList/HSMList.tsx +++ b/src/containers/HSM/HSMList/HSMList.tsx @@ -190,7 +190,8 @@ export const HSMList = () => { setSyncTemplateLoad(true); try { const { data } = await syncHsmTemplates(); - if (data.errors) { + const errors = data?.syncHsmTemplate?.errors; + if (!data?.syncHsmTemplate || errors?.length) { setNotification(t('Sorry, failed to sync HSM updates.'), 'warning'); } else { setNotification(t('HSM queued for sync. Check notifications for updates.'), 'success'); @@ -205,13 +206,18 @@ export const HSMList = () => { const handleBulkApply = async (result: string, media: any) => { const extension = getFileExtension(media.name); if (extension !== 'csv') { - setNotification('Please upload a valid CSV file', 'warning'); + setNotification(t('Please upload a valid CSV file'), 'warning'); setImporting(false); } else { try { const { data } = await bulkApplyTemplates({ variables: { data: result } }); - if (data?.bulkApplyTemplates) { - exportCsvFile(data.bulkApplyTemplates.csv_rows, 'result'); + const response = data?.bulkApplyTemplates; + if (response?.csv_rows) { + exportCsvFile(response.csv_rows, 'result'); + } + if (response?.errors?.length) { + setNotification(t('Templates were processed with errors. Please check the csv file for details.'), 'warning'); + } else if (response) { setNotification(t('Templates applied successfully. Please check the csv file for the results')); } } catch { @@ -225,7 +231,7 @@ export const HSMList = () => { const handleImportTemplates = async (result: string, media: any) => { const extension = getFileExtension(media.name); if (extension !== 'csv') { - setNotification('Please upload a valid CSV file', 'warning'); + setNotification(t('Please upload a valid CSV file'), 'warning'); setImporting(false); } else { try { @@ -363,11 +369,7 @@ export const HSMList = () => { View Sample - setImporting(true)} - afterImport={handleBulkApply} - /> + setImporting(true)} afterImport={handleBulkApply} /> ); diff --git a/src/containers/SettingList/Providers/Providers.tsx b/src/containers/SettingList/Providers/Providers.tsx index 4332b23537..b5161bceb9 100644 --- a/src/containers/SettingList/Providers/Providers.tsx +++ b/src/containers/SettingList/Providers/Providers.tsx @@ -197,12 +197,12 @@ export const Providers = () => { addField(fields); setKeys(providerKeys); setSecrets(providerSecrets); - - const credentialData = credential?.credential?.credential; - if (credentialData) { - setCredential(credentialData); - } }); + + const credentialData = credential?.credential?.credential; + if (credentialData) { + setCredential(credentialData); + } } }, [providerData, credential]); diff --git a/src/containers/Trigger/Trigger.tsx b/src/containers/Trigger/Trigger.tsx index 71e7363b2f..6b4d91ebf1 100644 --- a/src/containers/Trigger/Trigger.tsx +++ b/src/containers/Trigger/Trigger.tsx @@ -251,16 +251,20 @@ export const Trigger = () => { const handleFlowChange = async (flow: any) => { setTriggerFlowWarning(undefined); if (flow) { - const result = await validateTriggerFlow({ - variables: { - input: { - flowId: flow.id, + try { + const result = await validateTriggerFlow({ + variables: { + input: { + flowId: flow.id, + }, }, - }, - }); - const { validateTrigger } = result.data; - if (!validateTrigger.success && validateTrigger.errors && validateTrigger.errors.length > 0) { - setTriggerFlowWarning(validateTrigger.errors[0].message); + }); + const { validateTrigger } = result.data; + if (!validateTrigger.success && validateTrigger.errors && validateTrigger.errors.length > 0) { + setTriggerFlowWarning(validateTrigger.errors[0].message); + } + } catch { + setTriggerFlowWarning('Failed to validate flow. Please try again.'); } } }; diff --git a/src/i18n/en/en.json b/src/i18n/en/en.json index 8820ea7ce5..15216a97c5 100644 --- a/src/i18n/en/en.json +++ b/src/i18n/en/en.json @@ -566,5 +566,7 @@ "Knowledge Base": "Knowledge Base", "Notes (Optional)": "Notes (Optional)", "Add notes on changes made to this assistant": "Add notes on changes made to this assistant", - "A new version is being created": "A new version is being created" -} + "A new version is being created": "A new version is being created", + "Please upload a valid CSV file": "Please upload a valid CSV file", + "Templates were processed with errors. Please check the csv file for details.": "Templates were processed with errors. Please check the csv file for details." +} \ No newline at end of file From 8c983231ae40c4d7dc87b00758c230282a9045f1 Mon Sep 17 00:00:00 2001 From: akanshaaa19 Date: Wed, 18 Mar 2026 11:22:39 +0530 Subject: [PATCH 05/13] refactor: improve error handling in FormLayout; update notification messages in HSMList and Trigger components; add missing translation for flow validation error --- src/containers/Form/FormLayout.tsx | 245 +++++++++++-------------- src/containers/HSM/HSMList/HSMList.tsx | 4 +- src/containers/Trigger/Trigger.tsx | 2 +- src/i18n/en/en.json | 3 +- 4 files changed, 117 insertions(+), 137 deletions(-) diff --git a/src/containers/Form/FormLayout.tsx b/src/containers/Form/FormLayout.tsx index ec28b4a7f6..040400efdb 100644 --- a/src/containers/Form/FormLayout.tsx +++ b/src/containers/Form/FormLayout.tsx @@ -2,7 +2,7 @@ import { useState, Fragment, useEffect } from 'react'; import { Navigate, useParams } from 'react-router'; import { Field, useFormik, FormikProvider } from 'formik'; // eslint-disable-next-line no-unused-vars -import { DocumentNode, ApolloError, useQuery, useMutation } from '@apollo/client'; +import { DocumentNode, useQuery, useMutation } from '@apollo/client'; import { Typography } from '@mui/material'; import { useTranslation } from 'react-i18next'; @@ -207,9 +207,96 @@ export const FormLayout = ({ } }; - const performTask = (payload: any) => { - if (itemId) { - if (isLoadedData) { + const handleUpdateCompleted = (data: any) => { + setShowConfirmationDialog(false); + let itemUpdatedObject: any = Object.keys(data)[0]; + itemUpdatedObject = data[itemUpdatedObject]; + const updatedItem = itemUpdatedObject[listItem]; + const { errors, message } = itemUpdatedObject; + + if (errors) { + if (customHandler) { + customHandler(errors); + } else { + setErrorMessage(errors[0]); + } + } else if (updatedItem && typeof updatedItem.isValid === 'boolean' && !updatedItem.isValid) { + if (customError) { + const codeErrors = { code: 'Failed to compile code. Please check again' }; + customError.setErrors(codeErrors); + } + } else { + if (type === 'copy') setLink(updatedItem[linkParameter]); + if (additionalQuery) { + additionalQuery(itemId); + } + + if (saveOnPageChange || saveClick) { + setFormSubmitted(true); + let message = `${capitalListItemName} edited successfully!`; + if (type === 'copy') { + message = copyNotification; + } + setNotification(message); + } else { + setNotification('Your changes have been autosaved'); + } + if (afterSave) { + afterSave(data, saveClick, message); + } + } + onSaveClick(false); + }; + + const handleCreateCompleted = (data: any) => { + setShowConfirmationDialog(false); + let itemCreatedObject: any = `create${camelCaseItem}`; + itemCreatedObject = data[itemCreatedObject]; + const itemCreated = itemCreatedObject[listItem]; + + const { errors } = itemCreatedObject; + if (errors) { + if (customHandler) { + customHandler(errors); + } else { + setErrorMessage(errors[0]); + } + } else if (itemCreated && typeof itemCreated.isValid === 'boolean' && !itemCreated.isValid) { + if (customError) { + const codeErrors = { code: 'Failed to compile code. Please check again' }; + customError.setErrors(codeErrors); + } + } else { + if (additionalQuery) { + additionalQuery(itemCreated.id); + } + if (!itemId) setLink(itemCreated[linkParameter]); + if (saveOnPageChange || saveClick) { + setFormSubmitted(true); + setNotification(`${capitalListItemName} created successfully!`); + } else { + setNotification('Your changes have been autosaved'); + } + if (afterSave) { + afterSave(data, saveClick); + } + } + onSaveClick(false); + }; + + const handleMutationError = (e: any) => { + setShowConfirmationDialog(false); + onSaveClick(false); + if (customHandler) { + customHandler(e.message); + } else { + setErrorMessage(e); + } + }; + + const performTask = async (payload: any) => { + try { + if (itemId && isLoadedData) { let idKey = idType; let idVal = itemId; @@ -223,29 +310,26 @@ export const FormLayout = ({ if (idType === 'organizationId') { idKey = 'id'; idVal = payloadBody.billingId; - // Clearning unnecessary fields delete payloadBody.billingId; } - updateItem({ + const { data } = await updateItem({ variables: { [idKey]: idVal, input: payloadBody, }, }); + if (data) handleUpdateCompleted(data); } else { - createItem({ + const { data } = await createItem({ variables: { input: payload, }, }); + if (data) handleCreateCompleted(data); } - } else { - createItem({ - variables: { - input: payload, - }, - }); + } catch (e: any) { + handleMutationError(e); } }; @@ -269,20 +353,18 @@ export const FormLayout = ({ variables, skip: !itemId, fetchPolicy: getQueryFetchPolicy, - onCompleted: (data) => { - if (data) { - const loadedItem = data[listItem]?.[listItem] ?? data[Object.keys(data)[0]]?.[listItem]; - if (loadedItem && setStates) { - setStates(loadedItem); - } - } - }, }); const fetchedItem: any = itemData ? (itemData[listItem]?.[listItem] ?? itemData[Object.keys(itemData)[0]]?.[listItem] ?? null) : null; + useEffect(() => { + if (fetchedItem && setStates) { + setStates(fetchedItem); + } + }, [itemData]); + const isLoadedData = Boolean(fetchedItem); const languageId = fetchedItem @@ -327,14 +409,6 @@ export const FormLayout = ({ }, [entityId]); const [deleteItem] = useMutation(deleteItemQuery, { - onCompleted: () => { - setNotification(`${capitalListItemName} deleted successfully`); - setDeleted(true); - }, - onError: (err: ApolloError) => { - setShowDialog(false); - setErrorMessage(err); - }, awaitRefetchQueries: true, refetchQueries: [ { @@ -347,60 +421,6 @@ export const FormLayout = ({ const camelCaseItem = listItem[0].toUpperCase() + listItem.slice(1); const [updateItem] = useMutation(updateItemQuery, { - onCompleted: (data) => { - setShowConfirmationDialog(false); - let itemUpdatedObject: any = Object.keys(data)[0]; - itemUpdatedObject = data[itemUpdatedObject]; - const updatedItem = itemUpdatedObject[listItem]; - const { errors, message } = itemUpdatedObject; - - if (errors) { - if (customHandler) { - customHandler(errors); - } else { - setErrorMessage(errors[0]); - } - } else if (updatedItem && typeof updatedItem.isValid === 'boolean' && !updatedItem.isValid) { - if (customError) { - // this is a custom error for extensions. We need to move this out of this component - const codeErrors = { code: 'Failed to compile code. Please check again' }; - customError.setErrors(codeErrors); - } - } else { - if (type === 'copy') setLink(updatedItem[linkParameter]); - if (additionalQuery) { - additionalQuery(itemId); - } - - if (saveOnPageChange || saveClick) { - setFormSubmitted(true); - // display successful message after update - let message = `${capitalListItemName} edited successfully!`; - if (type === 'copy') { - message = copyNotification; - } - setNotification(message); - } else { - setNotification('Your changes have been autosaved'); - } - // emit data after save - if (afterSave) { - afterSave(data, saveClick, message); - } - } - onSaveClick(false); - }, - onError: (e: ApolloError) => { - setShowConfirmationDialog(false); - onSaveClick(false); - if (customHandler) { - customHandler(e.message); - } else { - setErrorMessage(e); - } - - return null; - }, refetchQueries: () => { if (refetchQueries) return refetchQueries.map((refetchQuery: any) => ({ @@ -412,43 +432,6 @@ export const FormLayout = ({ }); const [createItem] = useMutation(createItemQuery, { - onCompleted: (data) => { - setShowConfirmationDialog(false); - let itemCreatedObject: any = `create${camelCaseItem}`; - itemCreatedObject = data[itemCreatedObject]; - const itemCreated = itemCreatedObject[listItem]; - - const { errors } = itemCreatedObject; - if (errors) { - if (customHandler) { - customHandler(errors); - } else { - setErrorMessage(errors[0]); - } - } else if (itemCreated && typeof itemCreated.isValid === 'boolean' && !itemCreated.isValid) { - if (customError) { - const codeErrors = { code: 'Failed to compile code. Please check again' }; - customError.setErrors(codeErrors); - } - } else { - if (additionalQuery) { - additionalQuery(itemCreated.id); - } - if (!itemId) setLink(itemCreated[linkParameter]); - if (saveOnPageChange || saveClick) { - setFormSubmitted(true); - // display successful message after create - setNotification(`${capitalListItemName} created successfully!`); - } else { - setNotification('Your changes have been autosaved'); - } - // emit data after save - if (afterSave) { - afterSave(data, saveClick); - } - } - onSaveClick(false); - }, refetchQueries: () => { if (refetchQueries) return refetchQueries.map((refetchQuery: any) => ({ @@ -458,17 +441,6 @@ export const FormLayout = ({ return []; }, - onError: (e: ApolloError) => { - setShowConfirmationDialog(false); - onSaveClick(false); - if (customHandler) { - customHandler(e.message); - } else { - setErrorMessage(e); - } - - return null; - }, }); if (loading) return ; @@ -640,8 +612,15 @@ export const FormLayout = ({ ); - const handleDeleteItem = () => { - deleteItem({ variables: { id: itemId } }); + const handleDeleteItem = async () => { + try { + await deleteItem({ variables: { id: itemId } }); + setNotification(`${capitalListItemName} deleted successfully`); + setDeleted(true); + } catch (err: any) { + setShowDialog(false); + setErrorMessage(err); + } }; let dialogBox; diff --git a/src/containers/HSM/HSMList/HSMList.tsx b/src/containers/HSM/HSMList/HSMList.tsx index 53b21bc065..1e329814f7 100644 --- a/src/containers/HSM/HSMList/HSMList.tsx +++ b/src/containers/HSM/HSMList/HSMList.tsx @@ -236,7 +236,7 @@ export const HSMList = () => { } else { try { const { data } = await importTemplatesMutation({ variables: { data: result } }); - const { errors } = data.importTemplates; + const errors = data?.importTemplates?.errors; if (errors && errors.length > 0) { setNotification(t('Error importing templates'), 'warning'); } @@ -289,7 +289,7 @@ export const HSMList = () => { loading={syncTemplateLoad} className={styles.HsmUpdates} data-testid="updateHsm" - onClick={() => handleHsmUpdates()} + onClick={handleHsmUpdates} aria-hidden="true" > SYNC HSM diff --git a/src/containers/Trigger/Trigger.tsx b/src/containers/Trigger/Trigger.tsx index 6b4d91ebf1..d8733099e2 100644 --- a/src/containers/Trigger/Trigger.tsx +++ b/src/containers/Trigger/Trigger.tsx @@ -264,7 +264,7 @@ export const Trigger = () => { setTriggerFlowWarning(validateTrigger.errors[0].message); } } catch { - setTriggerFlowWarning('Failed to validate flow. Please try again.'); + setTriggerFlowWarning(t('Failed to validate flow. Please try again.')); } } }; diff --git a/src/i18n/en/en.json b/src/i18n/en/en.json index 15216a97c5..800d9e2893 100644 --- a/src/i18n/en/en.json +++ b/src/i18n/en/en.json @@ -568,5 +568,6 @@ "Add notes on changes made to this assistant": "Add notes on changes made to this assistant", "A new version is being created": "A new version is being created", "Please upload a valid CSV file": "Please upload a valid CSV file", - "Templates were processed with errors. Please check the csv file for details.": "Templates were processed with errors. Please check the csv file for details." + "Templates were processed with errors. Please check the csv file for details.": "Templates were processed with errors. Please check the csv file for details.", + "Failed to validate flow. Please try again.": "Failed to validate flow. Please try again." } \ No newline at end of file From eddb8a730ed88516bcd1dba6d1701fb1310e6aed Mon Sep 17 00:00:00 2001 From: akanshaaa19 Date: Wed, 18 Mar 2026 11:30:04 +0530 Subject: [PATCH 06/13] refactor: update notification message handling in FormLayout; rename flow parameter in Trigger component --- src/containers/Form/FormLayout.tsx | 15 ++++++++++----- src/containers/Trigger/Trigger.tsx | 6 +++--- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/src/containers/Form/FormLayout.tsx b/src/containers/Form/FormLayout.tsx index 040400efdb..c3624f1c2b 100644 --- a/src/containers/Form/FormLayout.tsx +++ b/src/containers/Form/FormLayout.tsx @@ -233,11 +233,11 @@ export const FormLayout = ({ if (saveOnPageChange || saveClick) { setFormSubmitted(true); - let message = `${capitalListItemName} edited successfully!`; + let notificationMessage = `${capitalListItemName} edited successfully!`; if (type === 'copy') { - message = copyNotification; + notificationMessage = copyNotification; } - setNotification(message); + setNotification(notificationMessage); } else { setNotification('Your changes have been autosaved'); } @@ -349,7 +349,12 @@ export const FormLayout = ({ skip: !languageSupport, }); - const { loading, error, data: itemData, refetch } = useQuery(getItemQuery, { + const { + loading, + error, + data: itemData, + refetch, + } = useQuery(getItemQuery, { variables, skip: !itemId, fetchPolicy: getQueryFetchPolicy, @@ -369,7 +374,7 @@ export const FormLayout = ({ const languageId = fetchedItem ? languageSupport - ? fetchedItem.language?.id ?? '' + ? (fetchedItem.language?.id ?? '') : null : !itemId && organization.data ? organization.data.currentUser.user.organization.defaultLanguage.id diff --git a/src/containers/Trigger/Trigger.tsx b/src/containers/Trigger/Trigger.tsx index d8733099e2..d9ad526be1 100644 --- a/src/containers/Trigger/Trigger.tsx +++ b/src/containers/Trigger/Trigger.tsx @@ -248,14 +248,14 @@ export const Trigger = () => { const [validateTriggerFlow, { loading }] = useMutation(VALIDATE_TRIGGER); - const handleFlowChange = async (flow: any) => { + const handleFlowChange = async (input: any) => { setTriggerFlowWarning(undefined); - if (flow) { + if (input) { try { const result = await validateTriggerFlow({ variables: { input: { - flowId: flow.id, + flowId: input.id, }, }, }); From f55d9fd73b63a19ebe3bfc8a9ab79e5b96ab0e69 Mon Sep 17 00:00:00 2001 From: akanshaaa19 Date: Wed, 18 Mar 2026 12:06:29 +0530 Subject: [PATCH 07/13] refactor: enhance loading state handling in FormLayout; clean up HSMList component by removing unused import and import templates logic; fix formatting in SettingList test helper --- src/containers/Form/FormLayout.tsx | 6 +++- src/containers/HSM/HSMList/HSMList.tsx | 36 +------------------ .../SettingList/SettingList.test.helper.ts | 16 ++++----- 3 files changed, 14 insertions(+), 44 deletions(-) diff --git a/src/containers/Form/FormLayout.tsx b/src/containers/Form/FormLayout.tsx index c3624f1c2b..8ae4dec970 100644 --- a/src/containers/Form/FormLayout.tsx +++ b/src/containers/Form/FormLayout.tsx @@ -448,11 +448,15 @@ export const FormLayout = ({ }, }); - if (loading) return ; + if (loading || languageSupport) return ; if (error) { setErrorMessage(error); return null; } + if (languageSupport && organization.error) { + setErrorMessage(organization.error); + return null; + } const cancelHandler = () => { // for chat screen searches diff --git a/src/containers/HSM/HSMList/HSMList.tsx b/src/containers/HSM/HSMList/HSMList.tsx index 1e329814f7..e5b33a52b1 100644 --- a/src/containers/HSM/HSMList/HSMList.tsx +++ b/src/containers/HSM/HSMList/HSMList.tsx @@ -16,7 +16,7 @@ import PendingIcon from 'assets/images/icons/Template/Pending.svg?react'; import { BULK_APPLY_SAMPLE_LINK } from 'config'; import { List } from 'containers/List/List'; import { RaiseToGupShup } from 'containers/HSM/RaiseToGupshupDialog/RaiseToGupShup'; -import { GUPSHUP_ENTERPRISE_SHORTCODE, STANDARD_DATE_TIME_FORMAT } from 'common/constants'; +import { STANDARD_DATE_TIME_FORMAT } from 'common/constants'; import { templateInfo, templateStatusInfo } from 'common/HelpData'; import { setNotification } from 'common/notification'; import { WhatsAppToJsx } from 'common/RichEditor'; @@ -228,26 +228,6 @@ export const HSMList = () => { } }; - const handleImportTemplates = async (result: string, media: any) => { - const extension = getFileExtension(media.name); - if (extension !== 'csv') { - setNotification(t('Please upload a valid CSV file'), 'warning'); - setImporting(false); - } else { - try { - const { data } = await importTemplatesMutation({ variables: { data: result } }); - const errors = data?.importTemplates?.errors; - if (errors && errors.length > 0) { - setNotification(t('Error importing templates'), 'warning'); - } - } catch { - setNotification(t('Error importing templates'), 'warning'); - } finally { - setImporting(false); - } - } - }; - let filterValue: any = ''; const statusList = ['Approved', 'Pending', 'Rejected', 'Failed']; const defaultSortBy = 'STATUS'; @@ -374,20 +354,6 @@ export const HSMList = () => { ); - if (provider === GUPSHUP_ENTERPRISE_SHORTCODE) { - secondaryButton = ( -
- {syncHSMButton} - setImporting(true)} - afterImport={handleImportTemplates} - /> -
- ); - button.show = false; - } - const handleView = (id: any) => { navigate(`/template/${id}/edit`); }; diff --git a/src/containers/SettingList/SettingList.test.helper.ts b/src/containers/SettingList/SettingList.test.helper.ts index af6edddcf5..90e2598674 100644 --- a/src/containers/SettingList/SettingList.test.helper.ts +++ b/src/containers/SettingList/SettingList.test.helper.ts @@ -7,7 +7,7 @@ import { getOrganizationSettings, getCredential, getQualityRating, - getOrganizationSettingsAllowBot + getOrganizationSettingsAllowBot, } from 'mocks/Organization'; import { FLOW_STATUS_PUBLISHED, setVariables } from 'common/constants'; import { UPDATE_ORGANIZATION } from 'graphql/mutations/Organization'; @@ -96,7 +96,7 @@ const updateOrganizationMock = { setting: { lowBalanceThreshold: '10', criticalBalanceThreshold: '5', - sendWarningMail: false + sendWarningMail: false, }, }, }, @@ -163,7 +163,7 @@ const updateOrganizationMock = { criticalBalanceThreshold: '3', lowBalanceThreshold: '10', sendWarningMail: true, - allowBotNumberUpdate: false + allowBotNumberUpdate: false, }, shortcode: 'glific', }, @@ -185,7 +185,7 @@ const updateOrganizationMock2 = { setting: { lowBalanceThreshold: '10', criticalBalanceThreshold: '5', - sendWarningMail: false + sendWarningMail: false, }, }, }, @@ -252,7 +252,7 @@ const updateOrganizationMock2 = { criticalBalanceThreshold: '3', lowBalanceThreshold: '10', sendWarningMail: true, - allowBotNumberUpdate: false + allowBotNumberUpdate: false, }, shortcode: 'glific', }, @@ -269,7 +269,7 @@ export const ORGANIZATION_MOCKS = [ flowsMock, ...getOrganizationQuery, updateOrganizationMock, - updateOrganizationMock2 + updateOrganizationMock2, ]; export const ORGANIZATION_MOCKS2 = [ @@ -280,5 +280,5 @@ export const ORGANIZATION_MOCKS2 = [ flowsMock, ...getOrganizationSettingsAllowBot, updateOrganizationMock, - updateOrganizationMock2 -] + updateOrganizationMock2, +]; From 98d1d6f74fe34bbcf537598d079a0b2f4a206a65 Mon Sep 17 00:00:00 2001 From: akanshaaa19 Date: Wed, 18 Mar 2026 12:15:33 +0530 Subject: [PATCH 08/13] refactor: improve loading state handling in FormLayout; remove unused ProviderContext import in HSMList --- src/containers/Form/FormLayout.tsx | 3 ++- src/containers/HSM/HSMList/HSMList.tsx | 4 +--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/containers/Form/FormLayout.tsx b/src/containers/Form/FormLayout.tsx index 8ae4dec970..237bdac9b8 100644 --- a/src/containers/Form/FormLayout.tsx +++ b/src/containers/Form/FormLayout.tsx @@ -448,7 +448,8 @@ export const FormLayout = ({ }, }); - if (loading || languageSupport) return ; + if (loading) return ; + if (error) { setErrorMessage(error); return null; diff --git a/src/containers/HSM/HSMList/HSMList.tsx b/src/containers/HSM/HSMList/HSMList.tsx index e5b33a52b1..390fcb5303 100644 --- a/src/containers/HSM/HSMList/HSMList.tsx +++ b/src/containers/HSM/HSMList/HSMList.tsx @@ -1,7 +1,7 @@ import { useMutation, useQuery } from '@apollo/client'; import { FormControl, MenuItem, Select } from '@mui/material'; import dayjs from 'dayjs'; -import { useContext, useEffect, useState } from 'react'; +import { useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useNavigate } from 'react-router'; @@ -39,7 +39,6 @@ import { import { FILTER_TEMPLATES, GET_TEMPLATES_COUNT } from 'graphql/queries/Template'; import styles from './HSMList.module.css'; -import { ProviderContext } from 'context/session'; import { useSearchParams } from 'react-router'; const templateIcon = ; @@ -86,7 +85,6 @@ export const HSMList = () => { const [syncTemplateLoad, setSyncTemplateLoad] = useState(false); const [searchParams, setSearchParams] = useSearchParams(); - const { provider } = useContext(ProviderContext); const { t } = useTranslation(); const navigate = useNavigate(); From 1723d5800a5b8f3511a2dddf0aa6b7dce941769f Mon Sep 17 00:00:00 2001 From: akanshaaa19 Date: Wed, 18 Mar 2026 12:30:36 +0530 Subject: [PATCH 09/13] refactor: streamline FormLayout logic; remove redundant variable declarations in HSMList --- src/containers/Form/FormLayout.tsx | 26 ++++++++++++-------------- src/containers/HSM/HSMList/HSMList.tsx | 8 +------- 2 files changed, 13 insertions(+), 21 deletions(-) diff --git a/src/containers/Form/FormLayout.tsx b/src/containers/Form/FormLayout.tsx index 237bdac9b8..94b5402e61 100644 --- a/src/containers/Form/FormLayout.tsx +++ b/src/containers/Form/FormLayout.tsx @@ -162,6 +162,18 @@ export const FormLayout = ({ const [showConfirmationDialog, setShowConfirmationDialog] = useState(false); const params = useParams(); + const capitalListItemName = listItemName[0].toUpperCase() + listItemName.slice(1); + const camelCaseItem = listItem[0].toUpperCase() + listItem.slice(1); + let itemId = entityId; + if (!itemId) { + itemId = params.id; + } + + let variables: any = itemId ? { [idType]: itemId } : false; + if (listItem === 'credential') { + variables = params.type ? { shortcode: params.type } : false; + } + const saveHandler = ({ languageId: languageIdValue, ...itemData }: any) => { let payload = { ...itemData, @@ -333,18 +345,6 @@ export const FormLayout = ({ } }; - const capitalListItemName = listItemName[0].toUpperCase() + listItemName.slice(1); - - let itemId = entityId; - if (!itemId) { - itemId = params.id; - } - - let variables: any = itemId ? { [idType]: itemId } : false; - if (listItem === 'credential') { - variables = params.type ? { shortcode: params.type } : false; - } - const organization = useQuery(USER_LANGUAGES, { skip: !languageSupport, }); @@ -423,8 +423,6 @@ export const FormLayout = ({ ], }); - const camelCaseItem = listItem[0].toUpperCase() + listItem.slice(1); - const [updateItem] = useMutation(updateItemQuery, { refetchQueries: () => { if (refetchQueries) diff --git a/src/containers/HSM/HSMList/HSMList.tsx b/src/containers/HSM/HSMList/HSMList.tsx index 390fcb5303..da4a94cbe6 100644 --- a/src/containers/HSM/HSMList/HSMList.tsx +++ b/src/containers/HSM/HSMList/HSMList.tsx @@ -30,12 +30,7 @@ import HelpIcon from 'components/UI/HelpIcon/HelpIcon'; import { Loading } from 'components/UI/Layout/Loading/Loading'; import { GET_TAGS } from 'graphql/queries/Tags'; -import { - BULK_APPLY_TEMPLATES, - DELETE_TEMPLATE, - IMPORT_TEMPLATES, - SYNC_HSM_TEMPLATES, -} from 'graphql/mutations/Template'; +import { BULK_APPLY_TEMPLATES, DELETE_TEMPLATE, SYNC_HSM_TEMPLATES } from 'graphql/mutations/Template'; import { FILTER_TEMPLATES, GET_TEMPLATES_COUNT } from 'graphql/queries/Template'; import styles from './HSMList.module.css'; @@ -90,7 +85,6 @@ export const HSMList = () => { const [bulkApplyTemplates] = useMutation(BULK_APPLY_TEMPLATES); const [syncHsmTemplates] = useMutation(SYNC_HSM_TEMPLATES, { fetchPolicy: 'network-only' }); - const [importTemplatesMutation] = useMutation(IMPORT_TEMPLATES); const { data: tags } = useQuery(GET_TAGS, { variables: {}, From 1d0c1c67889620b01170edd0c19a87e314068d38 Mon Sep 17 00:00:00 2001 From: akanshaaa19 Date: Wed, 18 Mar 2026 12:43:03 +0530 Subject: [PATCH 10/13] refactor: enhance FlowList tests; add error handling for import, export, and pin flow operations; update mocks and rendering logic --- .../Flow/FlowList/FlowList.test.tsx | 130 +++++++++++++++--- src/containers/HSM/HSMList/HSMList.test.tsx | 36 ++++- src/containers/Trigger/Trigger.test.tsx | 44 ++++++ src/mocks/Tag.tsx | 2 - 4 files changed, 188 insertions(+), 24 deletions(-) diff --git a/src/containers/Flow/FlowList/FlowList.test.tsx b/src/containers/Flow/FlowList/FlowList.test.tsx index 3f6a658bb9..f7196531a9 100644 --- a/src/containers/Flow/FlowList/FlowList.test.tsx +++ b/src/containers/Flow/FlowList/FlowList.test.tsx @@ -23,7 +23,8 @@ import { Flow } from '../Flow'; import { getFilterTagQuery } from 'mocks/Tag'; import { getRoleNameQuery } from 'mocks/Role'; import * as Notification from 'common/notification'; -import { IMPORT_FLOW } from 'graphql/mutations/Flow'; +import { IMPORT_FLOW, PIN_FLOW } from 'graphql/mutations/Flow'; +import { EXPORT_FLOW } from 'graphql/queries/Flow'; const isActiveFilter = { isActive: true, isTemplate: false }; @@ -42,21 +43,17 @@ const mocks = [ getFlowCountNewQuery, getFlowQuery({ id: 1 }), getFlowQuery({ id: '1' }), - importFlow, releaseFlow, - exportFlow, getFilterTagQuery, getRoleNameQuery, getFlowCountQuery({ isTemplate: true }), filterTemplateFlows, - pinFlowQuery('2', true), - pinFlowQuery('1'), ...getOrganizationQuery, ]; const normalize = (s: string) => s.replace(/\s+/g, ' ').trim().toLowerCase(); -const flowList = ( - +const flowList = (customMocks?: any[]) => ( + @@ -81,7 +78,7 @@ const notificationSpy = vi.spyOn(Notification, 'setNotification'); describe('', () => { test('should render Flow', async () => { - const { getByText, getByTestId } = render(flowList); + const { getByText, getByTestId } = render(flowList()); expect(getByTestId('loading')).toBeInTheDocument(); await waitFor(() => { expect(getByText('Flows')); @@ -94,7 +91,7 @@ describe('', () => { }); test('should search flow and check if flow keywords are present below the name', async () => { - const { getByText, getByTestId, queryByPlaceholderText } = render(flowList); + const { getByText, getByTestId, queryByPlaceholderText } = render(flowList()); await waitFor(() => { // type "Help Workflow" in search box and enter expect(getByTestId('searchInput')).toBeInTheDocument(); @@ -121,7 +118,7 @@ describe('', () => { }); test('click on Make a copy', async () => { - const { getAllByTestId } = render(flowList); + const { getAllByTestId } = render(flowList()); await waitFor(() => { expect(getAllByTestId('copy-icon')[0]).toBeInTheDocument(); @@ -135,7 +132,7 @@ describe('', () => { }); test('should import flow using json file', async () => { - render(flowList); + render(flowList([...mocks, importFlow])); await waitFor(() => { expect(screen.getAllByTestId('import-icon')[0]).toBeInTheDocument(); @@ -167,7 +164,7 @@ describe('', () => { test('should export flow to json file', async () => { globalThis.URL.createObjectURL = vi.fn(); - render(flowList); + render(flowList([...mocks, exportFlow])); await waitFor(() => { screen.getAllByTestId('MoreIcon'); @@ -183,7 +180,7 @@ describe('', () => { }); test('should create from scratch ', async () => { - render(flowList); + render(flowList()); await waitFor(() => { expect(screen.getByText('Flows')).toBeInTheDocument(); @@ -203,7 +200,7 @@ describe('', () => { }); test('it should pin/unpin the flows', async () => { - render(flowList); + render(flowList([...mocks, pinFlowQuery('2', true), pinFlowQuery('1')])); await waitFor(() => { expect(screen.getByText('Flows')).toBeInTheDocument(); @@ -223,7 +220,7 @@ describe('', () => { }); test('it should navigate to create page with selected tag', async () => { - render(flowList); + render(flowList()); await waitFor(() => { expect(screen.getByText('Flows')).toBeInTheDocument(); @@ -247,7 +244,7 @@ describe('', () => { }); test('should navigate to edit page on clicking the edit button', async () => { - render(flowList); + render(flowList()); await waitFor(() => { expect(screen.getByText('Flows')).toBeInTheDocument(); @@ -267,7 +264,7 @@ describe('', () => { }); test('should open responder link dialog on clicking the share button', async () => { - render(flowList); + render(flowList()); await waitFor(() => { expect(screen.getByText('Flows')).toBeInTheDocument(); @@ -281,7 +278,7 @@ describe('', () => { }); test('should show warning when no keywords are selected and share button is clicked', async () => { - render(flowList); + render(flowList()); await waitFor(() => { expect(screen.getByText('Flows')).toBeInTheDocument(); @@ -297,7 +294,7 @@ describe('', () => { describe('Template flows', () => { test('it opens and closes dialog box', async () => { - render(flowList); + render(flowList()); await waitFor(() => { expect(screen.getByText('Flows')).toBeInTheDocument(); @@ -322,7 +319,7 @@ describe('Template flows', () => { }); test('it shows and creates a template flows', async () => { - render(flowList); + render(flowList()); await waitFor(() => { expect(screen.getByText('Flows')).toBeInTheDocument(); @@ -348,7 +345,7 @@ describe('Template flows', () => { }); test('click on Use it for templates', async () => { - render(flowList); + render(flowList()); await waitFor(() => { expect(screen.getByText('Flows')).toBeInTheDocument(); @@ -457,3 +454,94 @@ describe('Template flows', () => { }); }); }); + +describe('Error handling', () => { + test('should show error notification when export flow fails', async () => { + const exportFlowError = { + request: { + query: EXPORT_FLOW, + variables: { id: '1' }, + }, + error: new Error('Network error'), + }; + + render(flowList([...mocks, exportFlowError])); + + await waitFor(() => { + screen.getAllByTestId('MoreIcon'); + }); + + fireEvent.click(screen.getAllByTestId('MoreIcon')[0]); + + await waitFor(() => { + expect(screen.getAllByTestId('export-icon')[0]).toBeInTheDocument(); + }); + + fireEvent.click(screen.getAllByTestId('export-icon')[0]); + + await waitFor(() => { + expect(notificationSpy).toHaveBeenCalled(); + }); + }); + + test('should show error notification when import flow fails', async () => { + const importFlowError = { + request: { + query: IMPORT_FLOW, + }, + error: new Error('Import failed'), + variableMatcher: () => true, + }; + + class FileReaderMock { + onload: null | ((e: unknown) => void) = null; + result: string | null = null; + readAsText() { + const text = '{"flows":[]}'; + this.result = text; + setTimeout(() => { + if (this.onload) { + this.onload({ target: { result: text } } as unknown as ProgressEvent); + } + }, 0); + } + } + vi.stubGlobal('FileReader', FileReaderMock); + + render(flowList([...mocks, importFlowError])); + + await screen.findAllByTestId('import-icon'); + fireEvent.click(screen.getAllByTestId('import-icon')[0]); + + const file = new File(['{}'], 'test.json', { type: 'application/json' }); + const input = await screen.findByTestId('import'); + Object.defineProperty(input, 'files', { value: [file] }); + fireEvent.change(input); + + await waitFor(() => { + expect(notificationSpy).toHaveBeenCalledWith('An error occured while importing the flow', 'warning'); + }); + }); + + test('should show error notification when pin flow fails', async () => { + const pinFlowError = { + request: { + query: PIN_FLOW, + variables: { updateFlowId: '2', input: { isPinned: true } }, + }, + error: new Error('Pin failed'), + }; + + render(flowList([...mocks, pinFlowError])); + + await waitFor(() => { + expect(screen.getByText('Flows')).toBeInTheDocument(); + }); + + fireEvent.click(screen.getAllByTestId('pin-button')[0]); + + await waitFor(() => { + expect(notificationSpy).toHaveBeenCalledWith('Failed to update pin status', 'warning'); + }); + }); +}); diff --git a/src/containers/HSM/HSMList/HSMList.test.tsx b/src/containers/HSM/HSMList/HSMList.test.tsx index 5ae466a779..406de53f95 100644 --- a/src/containers/HSM/HSMList/HSMList.test.tsx +++ b/src/containers/HSM/HSMList/HSMList.test.tsx @@ -3,6 +3,7 @@ import { BrowserRouter as Router } from 'react-router'; import { MockedProvider } from '@apollo/client/testing'; import { HSM_LIST, bulkApplyMutation, bulkApplyMutationWIthError } from 'mocks/Template'; +import { BULK_APPLY_TEMPLATES } from 'graphql/mutations/Template'; import { HSMList } from './HSMList'; import userEvent from '@testing-library/user-event'; import { SYNC_HSM_TEMPLATES } from 'graphql/mutations/Template'; @@ -196,7 +197,7 @@ test('bulk apply templates', async () => { }); }); -test('bulk apply templates', async () => { +test('bulk apply templates with network error', async () => { const { getByTestId } = render(template(bulkApplyMutationWIthError)); await waitFor(() => { @@ -215,3 +216,36 @@ test('bulk apply templates', async () => { expect(setNotification).toHaveBeenCalledWith('An error occured! Please check the format of the file', 'warning'); }); }); + +test('bulk apply templates with application-level errors', async () => { + const bulkApplyWithAppErrors = { + request: { + query: BULK_APPLY_TEMPLATES, + variables: { data: 'csv data with errors' }, + }, + result: { + data: { + bulkApplyTemplates: { + errors: [{ message: 'Template not found' }], + csv_rows: 'Title,Status\nWelcome,Failed', + }, + }, + }, + }; + + const { getByTestId } = render(template(bulkApplyWithAppErrors)); + + await waitFor(() => { + expect(getByTestId('updateHsm')).toBeInTheDocument(); + }); + + const mockFile = new File(['csv data with errors'], 'testFile.csv', { type: 'text/csv' }); + fireEvent.change(getByTestId('import'), { target: { files: [mockFile] } }); + + await waitFor(() => { + expect(setNotification).toHaveBeenCalledWith( + 'Templates were processed with errors. Please check the csv file for details.', + 'warning' + ); + }); +}); diff --git a/src/containers/Trigger/Trigger.test.tsx b/src/containers/Trigger/Trigger.test.tsx index f3ad57469b..35b0c55793 100644 --- a/src/containers/Trigger/Trigger.test.tsx +++ b/src/containers/Trigger/Trigger.test.tsx @@ -4,6 +4,7 @@ import { MemoryRouter, Route, Routes } from 'react-router'; import { vi } from 'vitest'; import * as Notification from 'common/notification'; import { TRIGGER_MOCKS, createTriggerQuery, getTriggerQuery } from 'mocks/Trigger'; +import { VALIDATE_TRIGGER } from 'graphql/mutations/Trigger'; import { Trigger } from './Trigger'; import dayjs from 'dayjs'; import utc from 'dayjs'; @@ -604,3 +605,46 @@ describe('Whatsapp group collections', () => { }); }); }); + +describe('handleFlowChange error handling', () => { + test('should show warning when flow validation fails with network error', async () => { + const validateTriggerError = { + request: { + query: VALIDATE_TRIGGER, + variables: { input: { flowId: '2' } }, + }, + error: new Error('Network error'), + }; + + const mocksWithError = [ + validateTriggerError, + ...MOCKS.filter( + (m: any) => !(m?.request?.query === VALIDATE_TRIGGER && m?.request?.variables?.input?.flowId === '2') + ), + ]; + + render( + + + + } /> + } /> + + + + ); + + await waitFor(() => { + expect(screen.getByText('Select flow*')).toBeInTheDocument(); + }); + + const flowAutocomplete = screen.getAllByRole('combobox')[0]; + flowAutocomplete.focus(); + fireEvent.keyDown(flowAutocomplete, { key: 'ArrowDown' }); + fireEvent.click(screen.getByText('SoL Feedback')); + + await waitFor(() => { + expect(screen.getByText(/Failed to validate flow/)).toBeInTheDocument(); + }); + }); +}); diff --git a/src/mocks/Tag.tsx b/src/mocks/Tag.tsx index 9d06f9d97d..7f0a9d9fff 100644 --- a/src/mocks/Tag.tsx +++ b/src/mocks/Tag.tsx @@ -85,12 +85,10 @@ export const getFilterTagQuery = { data: { tags: [ { - __typename: 'Tag', id: '1', label: 'Messages', }, { - __typename: 'Tag', id: '2', label: 'Contacts', }, From b5c20036567ff6112e0e8d688121d0c201f952e8 Mon Sep 17 00:00:00 2001 From: akanshaaa19 Date: Mon, 23 Mar 2026 08:09:54 +0530 Subject: [PATCH 11/13] refactor: enhance saveHandler and related functions to support isSaveClick parameter; update notification handling in FormLayout --- src/containers/Form/FormLayout.tsx | 46 ++++++++++++++++-------------- 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/src/containers/Form/FormLayout.tsx b/src/containers/Form/FormLayout.tsx index 94b5402e61..e611f19874 100644 --- a/src/containers/Form/FormLayout.tsx +++ b/src/containers/Form/FormLayout.tsx @@ -174,7 +174,7 @@ export const FormLayout = ({ variables = params.type ? { shortcode: params.type } : false; } - const saveHandler = ({ languageId: languageIdValue, ...itemData }: any) => { + const saveHandler = ({ languageId: languageIdValue, ...itemData }: any, isSaveClick: boolean = false) => { let payload = { ...itemData, ...defaultAttribute, @@ -208,18 +208,18 @@ export const FormLayout = ({ const payloadCopy = payload; delete payloadCopy.attachmentURL; payloadCopy.messageMediaId = parseInt(data.data.createMessageMedia.messageMedia.id, 10); - performTask(payloadCopy); + performTask(payloadCopy, isSaveClick); } }) .catch((e: any) => { setErrorMessage(e); }); } else { - performTask(payload); + performTask(payload, isSaveClick); } }; - const handleUpdateCompleted = (data: any) => { + const handleUpdateCompleted = (data: any, isSaveClick: boolean) => { setShowConfirmationDialog(false); let itemUpdatedObject: any = Object.keys(data)[0]; itemUpdatedObject = data[itemUpdatedObject]; @@ -243,7 +243,7 @@ export const FormLayout = ({ additionalQuery(itemId); } - if (saveOnPageChange || saveClick) { + if (saveOnPageChange || isSaveClick) { setFormSubmitted(true); let notificationMessage = `${capitalListItemName} edited successfully!`; if (type === 'copy') { @@ -254,13 +254,13 @@ export const FormLayout = ({ setNotification('Your changes have been autosaved'); } if (afterSave) { - afterSave(data, saveClick, message); + afterSave(data, isSaveClick, message); } } onSaveClick(false); }; - const handleCreateCompleted = (data: any) => { + const handleCreateCompleted = (data: any, isSaveClick: boolean) => { setShowConfirmationDialog(false); let itemCreatedObject: any = `create${camelCaseItem}`; itemCreatedObject = data[itemCreatedObject]; @@ -283,14 +283,14 @@ export const FormLayout = ({ additionalQuery(itemCreated.id); } if (!itemId) setLink(itemCreated[linkParameter]); - if (saveOnPageChange || saveClick) { + if (saveOnPageChange || isSaveClick) { setFormSubmitted(true); setNotification(`${capitalListItemName} created successfully!`); } else { setNotification('Your changes have been autosaved'); } if (afterSave) { - afterSave(data, saveClick); + afterSave(data, isSaveClick); } } onSaveClick(false); @@ -306,7 +306,7 @@ export const FormLayout = ({ } }; - const performTask = async (payload: any) => { + const performTask = async (payload: any, isSaveClick: boolean) => { try { if (itemId && isLoadedData) { let idKey = idType; @@ -331,14 +331,14 @@ export const FormLayout = ({ input: payloadBody, }, }); - if (data) handleUpdateCompleted(data); + if (data) handleUpdateCompleted(data, isSaveClick); } else { const { data } = await createItem({ variables: { input: payload, }, }); - if (data) handleCreateCompleted(data); + if (data) handleCreateCompleted(data, isSaveClick); } } catch (e: any) { handleMutationError(e); @@ -541,13 +541,6 @@ export const FormLayout = ({ ) : null; - const onSaveButtonClick = (errors: any) => { - if (Object.keys(errors).length > 0) { - return; - } - onSaveClick(true); - }; - const form = (
@@ -577,8 +570,17 @@ export const FormLayout = ({ color="primary" onClick={() => { formik.validateForm().then((errors) => { - onSaveButtonClick(errors); - formik.submitForm(); + if (Object.keys(errors).length > 0) { + formik.submitForm(); + return; + } + onSaveClick(true); + if (confirmationState?.show) { + setShowConfirmationDialog(true); + } else { + setCustomError({ setErrors: formik.setErrors }); + saveHandler(formik.values, true); + } }); }} className={styles.Button} @@ -672,7 +674,7 @@ export const FormLayout = ({ { - saveHandler(formik.values); + saveHandler(formik.values, true); }} handleCancel={() => { onSaveClick(false); From 61b304fc8a8e5d48768e3137369a35a90459b4c0 Mon Sep 17 00:00:00 2001 From: akanshaaa19 Date: Mon, 23 Mar 2026 08:10:49 +0530 Subject: [PATCH 12/13] chore: update cypress testing workflow to checkout apollo_migration branch --- .github/workflows/cypress-testing.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/cypress-testing.yml b/.github/workflows/cypress-testing.yml index f51061446d..9c7ae3d0b3 100644 --- a/.github/workflows/cypress-testing.yml +++ b/.github/workflows/cypress-testing.yml @@ -98,6 +98,7 @@ jobs: git clone https://github.com/glific/cypress-testing.git echo done. go to dir. cd cypress-testing + git checkout apollo_migration cd .. cp -r cypress-testing/cypress cypress yarn add cypress@13.6.2 @@ -135,4 +136,4 @@ jobs: name: phoenix-server-log-shard-${{ matrix.shard }} path: ${{ env.artifacts_path }} if-no-files-found: warn - retention-days: 7 \ No newline at end of file + retention-days: 7 From 970defabdfd0a2ed7742fcc84570f52ead174ac4 Mon Sep 17 00:00:00 2001 From: akanshaaa19 Date: Mon, 23 Mar 2026 08:16:34 +0530 Subject: [PATCH 13/13] refactor: extract languageId logic into a separate function for improved readability --- src/containers/Form/FormLayout.tsx | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/containers/Form/FormLayout.tsx b/src/containers/Form/FormLayout.tsx index e611f19874..a780497992 100644 --- a/src/containers/Form/FormLayout.tsx +++ b/src/containers/Form/FormLayout.tsx @@ -372,13 +372,16 @@ export const FormLayout = ({ const isLoadedData = Boolean(fetchedItem); - const languageId = fetchedItem - ? languageSupport - ? (fetchedItem.language?.id ?? '') - : null - : !itemId && organization.data - ? organization.data.currentUser.user.organization.defaultLanguage.id - : ''; + const getLanguageIdValue = () => { + if (fetchedItem) { + return languageSupport ? (fetchedItem.language?.id ?? '') : null; + } + if (!itemId && organization.data) { + return organization.data.currentUser.user.organization.defaultLanguage.id; + } + return ''; + }; + const languageId = getLanguageIdValue(); const formik = useFormik({ initialValues: {