diff --git a/apps/meteor/client/components/GenericError/GenericError.tsx b/apps/meteor/client/components/GenericError/GenericError.tsx new file mode 100644 index 00000000000..22f9923dea2 --- /dev/null +++ b/apps/meteor/client/components/GenericError/GenericError.tsx @@ -0,0 +1,31 @@ +import { Box, States, StatesIcon, StatesTitle, StatesActions, StatesAction } from '@rocket.chat/fuselage'; +import type { Keys as IconName } from '@rocket.chat/icons'; +import { useTranslation } from '@rocket.chat/ui-contexts'; +import React from 'react'; + +type GenericErrorProps = { + icon?: IconName; + title?: string; + buttonTitle?: string; + buttonAction?: () => void; +}; + +const GenericError = ({ icon = 'magnifier', title, buttonTitle, buttonAction }: GenericErrorProps) => { + const t = useTranslation(); + + return ( + + + + {title || t('Something_went_wrong')} + {buttonAction && ( + + {buttonTitle || t('Reload_page')} + + )} + + + ); +}; + +export default GenericError; diff --git a/apps/meteor/client/components/GenericError/index.ts b/apps/meteor/client/components/GenericError/index.ts new file mode 100644 index 00000000000..e38d015fe74 --- /dev/null +++ b/apps/meteor/client/components/GenericError/index.ts @@ -0,0 +1 @@ +export { default } from './GenericError'; diff --git a/apps/meteor/client/views/omnichannel/routes.ts b/apps/meteor/client/views/omnichannel/routes.ts index 9d2ed96e8b1..11de2598e6a 100644 --- a/apps/meteor/client/views/omnichannel/routes.ts +++ b/apps/meteor/client/views/omnichannel/routes.ts @@ -128,7 +128,7 @@ registerOmnichannelRoute('/tags/:context?/:id?', { registerOmnichannelRoute('/triggers/:context?/:id?', { name: 'omnichannel-triggers', - component: lazy(() => import('./triggers/TriggersPage')), + component: lazy(() => import('./triggers/TriggersRoute')), }); registerOmnichannelRoute('/current/:id?/:tab?/:context?', { diff --git a/apps/meteor/client/views/omnichannel/triggers/EditTrigger.tsx b/apps/meteor/client/views/omnichannel/triggers/EditTrigger.tsx new file mode 100644 index 00000000000..1de0373deb4 --- /dev/null +++ b/apps/meteor/client/views/omnichannel/triggers/EditTrigger.tsx @@ -0,0 +1,306 @@ +import type { ILivechatTrigger, ILivechatTriggerCondition, Serialized } from '@rocket.chat/core-typings'; +import type { SelectOption } from '@rocket.chat/fuselage'; +import { + FieldGroup, + Button, + ButtonGroup, + Box, + Field, + FieldLabel, + FieldRow, + FieldError, + TextInput, + ToggleSwitch, + Select, + TextAreaInput, +} from '@rocket.chat/fuselage'; +import { useUniqueId } from '@rocket.chat/fuselage-hooks'; +import { useToastMessageDispatch, useRouter, useEndpoint, useTranslation } from '@rocket.chat/ui-contexts'; +import { useMutation, useQueryClient } from '@tanstack/react-query'; +import React, { useMemo } from 'react'; +import { Controller, useFieldArray, useForm } from 'react-hook-form'; + +import { + ContextualbarScrollableContent, + ContextualbarTitle, + ContextualbarFooter, + Contextualbar, + ContextualbarHeader, + ContextualbarClose, +} from '../../../components/Contextualbar'; + +const getInitialValues = (triggerData: Serialized | undefined) => ({ + name: triggerData?.name || '', + description: triggerData?.description || '', + enabled: triggerData?.enabled || true, + runOnce: !!triggerData?.runOnce || false, + conditions: triggerData?.conditions.map(({ name, value }) => ({ name: name || 'page-url', value: value || '' })) || [ + { name: 'page-url' as unknown as ILivechatTriggerCondition['name'], value: '' }, + ], + actions: triggerData?.actions.map(({ name, params }) => ({ + name: name || '', + params: { + sender: params?.sender || 'queue', + msg: params?.msg || '', + name: params?.name || '', + }, + })) || [ + { + name: 'send-message', + params: { + sender: 'queue', + msg: '', + name: '', + }, + }, + ], +}); + +type TriggersPayload = { + name: string; + description: string; + enabled: boolean; + runOnce: boolean; + // In the future, this will be an array + conditions: ILivechatTrigger['conditions']; + // In the future, this will be an array + actions: ILivechatTrigger['actions']; +}; + +const EditTrigger = ({ triggerData }: { triggerData?: Serialized }) => { + const t = useTranslation(); + const router = useRouter(); + const queryClient = useQueryClient(); + const dispatchToastMessage = useToastMessageDispatch(); + + const saveTrigger = useEndpoint('POST', '/v1/livechat/triggers'); + + const { + control, + handleSubmit, + formState: { isDirty, errors }, + watch, + } = useForm({ mode: 'onBlur', values: getInitialValues(triggerData) }); + + const { fields: conditionsFields } = useFieldArray({ + control, + name: 'conditions', + }); + + const { fields: actionsFields } = useFieldArray({ + control, + name: 'actions', + }); + + const { description, conditions, actions } = watch(); + + const conditionOptions: SelectOption[] = useMemo( + () => [ + ['page-url', t('Visitor_page_URL')], + ['time-on-site', t('Visitor_time_on_site')], + ['chat-opened-by-visitor', t('Chat_opened_by_visitor')], + ['after-guest-registration', t('After_guest_registration')], + ], + [t], + ); + + const conditionValuePlaceholders: { [conditionName: string]: string } = useMemo( + () => ({ + 'page-url': t('Enter_a_regex'), + 'time-on-site': t('Time_in_seconds'), + }), + [t], + ); + + const senderOptions: SelectOption[] = useMemo( + () => [ + ['queue', t('Impersonate_next_agent_from_queue')], + ['custom', t('Custom_agent')], + ], + [t], + ); + + const saveTriggerMutation = useMutation({ + mutationFn: saveTrigger, + onSuccess: () => { + dispatchToastMessage({ type: 'success', message: t('Saved') }); + queryClient.invalidateQueries(['livechat-triggers']); + router.navigate('/omnichannel/triggers'); + }, + onError: (error) => { + dispatchToastMessage({ type: 'error', message: error }); + }, + }); + + const handleSave = async (data: TriggersPayload) => { + saveTriggerMutation.mutateAsync({ ...data, _id: triggerData?._id }); + }; + + const formId = useUniqueId(); + const enabledField = useUniqueId(); + const runOnceField = useUniqueId(); + const nameField = useUniqueId(); + const descriptionField = useUniqueId(); + const conditionField = useUniqueId(); + const actionField = useUniqueId(); + const actionMessageField = useUniqueId(); + + return ( + + + {triggerData?._id ? t('Edit_Trigger') : t('New_Trigger')} + router.navigate('/omnichannel/triggers')} /> + + +
+ + + + {t('Enabled')} + + } + /> + + + + + + {t('Run_only_once_for_each_visitor')} + + } + /> + + + + + + {t('Name')} + + + ( + + )} + /> + + {errors?.name && ( + + {errors?.name.message} + + )} + + + {t('Description')} + + } + /> + + + {conditionsFields.map((_, index) => { + const conditionValuePlaceholder = conditionValuePlaceholders[conditions[index].name]; + return ( + + {t('Condition')} + + + )} + /> + + {actions[index].params?.sender === 'custom' && ( + + } + /> + + )} + + ( + + )} + /> + + {errors.actions?.[index]?.params?.msg && ( + + {errors.actions?.[index]?.params?.msg?.message} + + )} + + ))} + +
+
+ + + + + + +
+ ); +}; + +export default EditTrigger; diff --git a/apps/meteor/client/views/omnichannel/triggers/EditTriggerPage.js b/apps/meteor/client/views/omnichannel/triggers/EditTriggerPage.js deleted file mode 100644 index cc2edf7b919..00000000000 --- a/apps/meteor/client/views/omnichannel/triggers/EditTriggerPage.js +++ /dev/null @@ -1,104 +0,0 @@ -import { FieldGroup, Button, ButtonGroup } from '@rocket.chat/fuselage'; -import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; -import { useToastMessageDispatch, useRoute, useEndpoint, useTranslation } from '@rocket.chat/ui-contexts'; -import React from 'react'; - -import { ContextualbarScrollableContent, ContextualbarFooter } from '../../../components/Contextualbar'; -import { useForm } from '../../../hooks/useForm'; -import TriggersForm from './TriggersForm'; - -const getInitialValues = ({ - name, - description, - enabled, - runOnce, - conditions: [{ name: condName, value: condValue }], - actions: [ - { - action: actName, - params: { sender: actSender, msg: actMsg, name: actSenderName }, - }, - ], -}) => ({ - name: name ?? '', - description: description ?? '', - enabled: !!enabled, - runOnce: !!runOnce, - conditions: { - name: condName ?? 'page-url', - value: condValue ?? '', - }, - actions: { - name: actName ?? '', - params: { - sender: actSender ?? 'queue', - msg: actMsg ?? '', - name: actSenderName ?? '', - }, - }, -}); - -const EditTriggerPage = ({ data, onSave }) => { - const dispatchToastMessage = useToastMessageDispatch(); - const t = useTranslation(); - - const router = useRoute('omnichannel-triggers'); - - const save = useEndpoint('POST', '/v1/livechat/triggers'); - - const { values, handlers, hasUnsavedChanges } = useForm(getInitialValues(data)); - - const handleSave = useMutableCallback(async () => { - try { - const { - actions: { - params: { sender, msg, name }, - }, - ...restValues - } = values; - await save({ - _id: data._id, - ...restValues, - conditions: [values.conditions], - actions: [ - { - name: 'send-message', - params: { - sender, - msg, - ...(sender === 'custom' && { name }), - }, - }, - ], - }); - dispatchToastMessage({ type: 'success', message: t('Saved') }); - onSave(); - router.push({}); - } catch (error) { - dispatchToastMessage({ type: 'error', message: error }); - } - }); - - const { name } = values; - - const canSave = name && hasUnsavedChanges; - - return ( - <> - - - - - - - - - - - - ); -}; - -export default EditTriggerPage; diff --git a/apps/meteor/client/views/omnichannel/triggers/EditTriggerPageContainer.js b/apps/meteor/client/views/omnichannel/triggers/EditTriggerPageContainer.js deleted file mode 100644 index 6252ce51c19..00000000000 --- a/apps/meteor/client/views/omnichannel/triggers/EditTriggerPageContainer.js +++ /dev/null @@ -1,25 +0,0 @@ -import { Callout } from '@rocket.chat/fuselage'; -import { useTranslation } from '@rocket.chat/ui-contexts'; -import React from 'react'; - -import PageSkeleton from '../../../components/PageSkeleton'; -import { AsyncStatePhase } from '../../../hooks/useAsyncState'; -import { useEndpointData } from '../../../hooks/useEndpointData'; -import EditTriggerPage from './EditTriggerPage'; - -const EditTriggerPageContainer = ({ id, onSave }) => { - const t = useTranslation(); - const { value: data, phase: state } = useEndpointData('/v1/livechat/triggers/:_id', { keys: { _id: id } }); - - if (state === AsyncStatePhase.LOADING) { - return ; - } - - if (state === AsyncStatePhase.REJECTED || !data?.trigger) { - return {t('Error')}: error; - } - - return ; -}; - -export default EditTriggerPageContainer; diff --git a/apps/meteor/client/views/omnichannel/triggers/EditTriggerWithData.tsx b/apps/meteor/client/views/omnichannel/triggers/EditTriggerWithData.tsx new file mode 100644 index 00000000000..10a54494c4f --- /dev/null +++ b/apps/meteor/client/views/omnichannel/triggers/EditTriggerWithData.tsx @@ -0,0 +1,28 @@ +import type { ILivechatTrigger } from '@rocket.chat/core-typings'; +import { Callout } from '@rocket.chat/fuselage'; +import { useEndpoint, useTranslation } from '@rocket.chat/ui-contexts'; +import { useQuery } from '@tanstack/react-query'; +import React from 'react'; + +import { ContextualbarSkeleton } from '../../../components/Contextualbar'; +import EditTrigger from './EditTrigger'; + +const EditTriggerWithData = ({ triggerId }: { triggerId: ILivechatTrigger['_id'] }) => { + const t = useTranslation(); + const getTriggersById = useEndpoint('GET', '/v1/livechat/triggers/:_id', { _id: triggerId }); + const { data, isLoading, isError } = useQuery(['livechat-getTriggersById', triggerId], async () => getTriggersById(), { + refetchOnWindowFocus: false, + }); + + if (isLoading) { + return ; + } + + if (isError) { + return {t('Error')}; + } + + return ; +}; + +export default EditTriggerWithData; diff --git a/apps/meteor/client/views/omnichannel/triggers/NewTriggerPage.js b/apps/meteor/client/views/omnichannel/triggers/NewTriggerPage.js deleted file mode 100644 index 9f696ebe1aa..00000000000 --- a/apps/meteor/client/views/omnichannel/triggers/NewTriggerPage.js +++ /dev/null @@ -1,94 +0,0 @@ -import { Button, FieldGroup, ButtonGroup } from '@rocket.chat/fuselage'; -import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; -import { useToastMessageDispatch, useRoute, useEndpoint, useTranslation } from '@rocket.chat/ui-contexts'; -import React, { useMemo } from 'react'; - -import { ContextualbarScrollableContent, ContextualbarFooter } from '../../../components/Contextualbar'; -import { useForm } from '../../../hooks/useForm'; -import TriggersForm from './TriggersForm'; - -const NewTriggerPage = ({ onSave }) => { - const dispatchToastMessage = useToastMessageDispatch(); - const t = useTranslation(); - - const router = useRoute('omnichannel-triggers'); - - const save = useEndpoint('POST', '/v1/livechat/triggers'); - - const { values, handlers } = useForm({ - name: '', - description: '', - enabled: true, - runOnce: false, - conditions: { - name: 'page-url', - value: '', - }, - actions: { - name: '', - params: { - sender: 'queue', - msg: '', - name: '', - }, - }, - }); - - const handleSave = useMutableCallback(async () => { - try { - const { - actions: { - params: { sender, msg, name }, - }, - ...restValues - } = values; - await save({ - ...restValues, - conditions: [values.conditions], - actions: [ - { - name: 'send-message', - params: { - sender, - msg, - ...(sender === 'custom' && { name }), - }, - }, - ], - }); - dispatchToastMessage({ type: 'success', message: t('Saved') }); - onSave(); - router.push({}); - } catch (error) { - dispatchToastMessage({ type: 'error', message: error }); - } - }); - - const { - name, - actions: { - params: { msg }, - }, - } = values; - - const canSave = useMemo(() => name && msg, [name, msg]); - - return ( - <> - - - - - - - - - - - - ); -}; - -export default NewTriggerPage; diff --git a/apps/meteor/client/views/omnichannel/triggers/TriggersForm.stories.tsx b/apps/meteor/client/views/omnichannel/triggers/TriggersForm.stories.tsx index 37595518279..4bc7fe37c7c 100644 --- a/apps/meteor/client/views/omnichannel/triggers/TriggersForm.stories.tsx +++ b/apps/meteor/client/views/omnichannel/triggers/TriggersForm.stories.tsx @@ -1,13 +1,12 @@ import { FieldGroup, Box } from '@rocket.chat/fuselage'; -import { action } from '@storybook/addon-actions'; import type { ComponentMeta, ComponentStory } from '@storybook/react'; import React from 'react'; -import TriggersForm from './TriggersForm'; +import EditTrigger from './EditTrigger'; export default { - title: 'Omnichannel/TriggersForm', - component: TriggersForm, + title: 'Omnichannel/EditTrigger', + component: EditTrigger, decorators: [ (fn) => ( @@ -15,35 +14,6 @@ export default { ), ], -} as ComponentMeta; +} as ComponentMeta; -export const Default: ComponentStory = (args) => ; -Default.storyName = 'TriggersForm'; -Default.args = { - values: { - name: '', - description: '', - enabled: true, - runOnce: false, - conditions: { - name: 'page-url', - value: '', - }, - actions: { - name: '', - params: { - sender: 'queue', - msg: '', - name: '', - }, - }, - }, - handlers: { - handleName: action('handleName'), - handleDescription: action('handleDescription'), - handleEnabled: action('handleEnabled'), - handleRunOnce: action('handleRunOnce'), - handleConditions: action('handleConditions'), - handleActions: action('handleActions'), - }, -}; +export const Default: ComponentStory = (args) => ; diff --git a/apps/meteor/client/views/omnichannel/triggers/TriggersForm.tsx b/apps/meteor/client/views/omnichannel/triggers/TriggersForm.tsx deleted file mode 100644 index 7c947605941..00000000000 --- a/apps/meteor/client/views/omnichannel/triggers/TriggersForm.tsx +++ /dev/null @@ -1,212 +0,0 @@ -import type { SelectOption } from '@rocket.chat/fuselage'; -import { Box, Field, FieldLabel, FieldRow, FieldError, TextInput, ToggleSwitch, Select, TextAreaInput } from '@rocket.chat/fuselage'; -import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; -import { useTranslation } from '@rocket.chat/ui-contexts'; -import type { ComponentProps, FC, FormEvent } from 'react'; -import React, { useMemo, useState } from 'react'; - -import { useComponentDidUpdate } from '../../../hooks/useComponentDidUpdate'; - -type TriggerConditions = { - name: string; - value: string | number; -}; - -type TriggerActions = { - name: string; - params: { - sender: string | undefined; - msg: string; - name: string; - }; -}; - -type TriggersFormProps = { - values: { - name: string; - description: string; - enabled: boolean; - runOnce: boolean; - // In the future, this will be an array - conditions: TriggerConditions; - // In the future, this will be an array - actions: TriggerActions; - }; - handlers: { - handleName: (event: FormEvent) => void; - handleDescription: (event: FormEvent) => void; - handleEnabled: (event: FormEvent) => void; - handleRunOnce: (event: FormEvent) => void; - handleConditions: (value: TriggerConditions) => void; - handleActions: (value: TriggerActions) => void; - }; - className?: ComponentProps['className']; -}; - -const TriggersForm: FC = ({ values, handlers, className }) => { - const [nameError, setNameError] = useState(''); - const [msgError, setMsgError] = useState(''); - const t = useTranslation(); - const { name, description, enabled, runOnce, conditions, actions } = values; - - const { handleName, handleDescription, handleEnabled, handleRunOnce, handleConditions, handleActions } = handlers; - - const { name: conditionName, value: conditionValue } = conditions; - - const { - params: { sender: actionSender, msg: actionMsg, name: actionAgentName }, - } = actions; - - const conditionOptions: SelectOption[] = useMemo( - () => [ - ['page-url', t('Visitor_page_URL')], - ['time-on-site', t('Visitor_time_on_site')], - ['chat-opened-by-visitor', t('Chat_opened_by_visitor')], - ['after-guest-registration', t('After_guest_registration')], - ], - [t], - ); - - const conditionValuePlaceholders: { [conditionName: string]: string } = useMemo( - () => ({ - 'page-url': t('Enter_a_regex'), - 'time-on-site': t('Time_in_seconds'), - }), - [t], - ); - - const conditionValuePlaceholder = conditionValuePlaceholders[conditionName]; - - const senderOptions: SelectOption[] = useMemo( - () => [ - ['queue', t('Impersonate_next_agent_from_queue')], - ['custom', t('Custom_agent')], - ], - [t], - ); - - const handleConditionName = useMutableCallback((name) => { - handleConditions({ - name, - value: '', - }); - }); - - const handleConditionValue = useMutableCallback(({ currentTarget: { value } }) => { - handleConditions({ - ...conditions, - value, - }); - }); - - const handleActionAgentName = useMutableCallback(({ currentTarget: { value: name } }) => { - handleActions({ - ...actions, - params: { - ...actions.params, - name, - }, - }); - }); - - const handleActionSender = useMutableCallback((sender) => { - handleActions({ - ...actions, - params: { - ...actions.params, - sender, - }, - }); - }); - - const handleActionMessage = useMutableCallback(({ currentTarget: { value: msg } }) => { - handleActions({ - ...actions, - params: { - ...actions.params, - msg, - }, - }); - }); - useComponentDidUpdate(() => { - setNameError(!name ? t('The_field_is_required', t('Name')) : ''); - }, [t, name]); - useComponentDidUpdate(() => { - setMsgError(!actionMsg ? t('The_field_is_required', t('Message')) : ''); - }, [t, actionMsg]); - return ( - <> - - - {t('Enabled')} - - - - - - - - {t('Run_only_once_for_each_visitor')} - - - - - - - {t('Name')}* - - - - {nameError} - - - {t('Description')} - - - - - - {t('Condition')} - - - - {actionSender === 'custom' && ( - - - - )} - - - - {msgError} - - - ); -}; - -export default TriggersForm; diff --git a/apps/meteor/client/views/omnichannel/triggers/TriggersPage.tsx b/apps/meteor/client/views/omnichannel/triggers/TriggersPage.tsx index 457f12b39a7..79eea71b623 100644 --- a/apps/meteor/client/views/omnichannel/triggers/TriggersPage.tsx +++ b/apps/meteor/client/views/omnichannel/triggers/TriggersPage.tsx @@ -1,60 +1,30 @@ import { Button } from '@rocket.chat/fuselage'; -import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; -import { useRoute, useRouteParameter, usePermission, useTranslation } from '@rocket.chat/ui-contexts'; -import React, { useRef, useCallback } from 'react'; +import { useRouteParameter, useRouter, useTranslation } from '@rocket.chat/ui-contexts'; +import React from 'react'; -import { Contextualbar, ContextualbarTitle, ContextualbarHeader, ContextualbarClose } from '../../../components/Contextualbar'; import Page from '../../../components/Page'; -import NotAuthorizedPage from '../../notAuthorized/NotAuthorizedPage'; -import EditTriggerPageContainer from './EditTriggerPageContainer'; -import NewTriggerPage from './NewTriggerPage'; +import EditTrigger from './EditTrigger'; +import EditTriggerWithData from './EditTriggerWithData'; import TriggersTable from './TriggersTable'; const TriggersPage = () => { const t = useTranslation(); const id = useRouteParameter('id'); const context = useRouteParameter('context'); - const router = useRoute('omnichannel-triggers'); - const canViewTriggers = usePermission('view-livechat-triggers'); - - const reload = useRef(() => null); - - const handleReload = useCallback(() => { - reload.current(); - }, []); - - const handleAdd = useMutableCallback(() => { - router.push({ context: 'new' }); - }); - - const handleCloseContextualbar = useMutableCallback(() => { - router.push({}); - }); - - if (!canViewTriggers) { - return ; - } + const router = useRouter(); return ( - + - + - {context && ( - - - {t('Trigger')} - - - {context === 'edit' && } - {context === 'new' && } - - )} + {context === 'edit' && id && } + {context === 'new' && } ); }; diff --git a/apps/meteor/client/views/omnichannel/triggers/TriggersRoute.tsx b/apps/meteor/client/views/omnichannel/triggers/TriggersRoute.tsx new file mode 100644 index 00000000000..7cf2d06bd3b --- /dev/null +++ b/apps/meteor/client/views/omnichannel/triggers/TriggersRoute.tsx @@ -0,0 +1,17 @@ +import { usePermission } from '@rocket.chat/ui-contexts'; +import React from 'react'; + +import NotAuthorizedPage from '../../notAuthorized/NotAuthorizedPage'; +import TriggersPage from './TriggersPage'; + +const TriggersRoute = () => { + const canViewTriggers = usePermission('view-livechat-triggers'); + + if (!canViewTriggers) { + return ; + } + + return ; +}; + +export default TriggersRoute; diff --git a/apps/meteor/client/views/omnichannel/triggers/TriggersTable.tsx b/apps/meteor/client/views/omnichannel/triggers/TriggersTable.tsx index 729ef41d435..8c0ea0213da 100644 --- a/apps/meteor/client/views/omnichannel/triggers/TriggersTable.tsx +++ b/apps/meteor/client/views/omnichannel/triggers/TriggersTable.tsx @@ -1,11 +1,11 @@ -import { Callout, Pagination } from '@rocket.chat/fuselage'; +import { Pagination } from '@rocket.chat/fuselage'; import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; import { useTranslation, useEndpoint, useRouter } from '@rocket.chat/ui-contexts'; import { useQuery, hashQueryKey } from '@tanstack/react-query'; -import type { MutableRefObject } from 'react'; -import React, { useEffect, useMemo, useState } from 'react'; +import React, { useMemo, useState } from 'react'; -import GenericNoResults from '../../../components/GenericNoResults/GenericNoResults'; +import GenericError from '../../../components/GenericError'; +import GenericNoResults from '../../../components/GenericNoResults'; import { GenericTable, GenericTableHeader, @@ -16,7 +16,7 @@ import { import { usePagination } from '../../../components/GenericTable/hooks/usePagination'; import TriggersRow from './TriggersRow'; -const TriggersTable = ({ reload }: { reload: MutableRefObject<() => void> }) => { +const TriggersTable = () => { const t = useTranslation(); const router = useRouter(); @@ -34,20 +34,12 @@ const TriggersTable = ({ reload }: { reload: MutableRefObject<() => void> }) => const [defaultQuery] = useState(hashQueryKey([query])); const queryHasChanged = defaultQuery !== hashQueryKey([query]); - useEffect(() => { - reload.current = refetch; - }, [reload, refetch]); - - if (isError) { - return {t('Error')}; - } - const headers = ( <> {t('Name')} {t('Description')} {t('Enabled')} - {t('Remove')} + ); @@ -94,6 +86,7 @@ const TriggersTable = ({ reload }: { reload: MutableRefObject<() => void> }) => /> )} + {isError && } ); }; diff --git a/apps/meteor/tests/e2e/omnichannel/omnichannel-triggers.spec.ts b/apps/meteor/tests/e2e/omnichannel/omnichannel-triggers.spec.ts index 9db221723eb..d1adc22d7bb 100644 --- a/apps/meteor/tests/e2e/omnichannel/omnichannel-triggers.spec.ts +++ b/apps/meteor/tests/e2e/omnichannel/omnichannel-triggers.spec.ts @@ -6,7 +6,7 @@ import { Users } from '../fixtures/userStates'; import { OmnichannelLiveChat, HomeOmnichannel } from '../page-objects'; import { test, expect } from '../utils/test'; -test.describe.serial('Omnichannel Triggers', () => { +test.describe.parallel('omnichannel-triggers', () => { let triggersName: string; let triggerMessage: string; let poLiveChat: OmnichannelLiveChat; diff --git a/apps/meteor/tests/e2e/page-objects/omnichannel-triggers.ts b/apps/meteor/tests/e2e/page-objects/omnichannel-triggers.ts index a90171f7442..2ac39a4b52f 100644 --- a/apps/meteor/tests/e2e/page-objects/omnichannel-triggers.ts +++ b/apps/meteor/tests/e2e/page-objects/omnichannel-triggers.ts @@ -17,33 +17,17 @@ export class OmnichannelTriggers { } get inputName(): Locator { - return this.page.locator('[placeholder="Name"]'); + return this.page.locator('input[name="name"]'); } get inputDescription(): Locator { - return this.page.locator('[placeholder="Description"]'); - } - - get addTime(): Locator { - return this.page.locator('[placeholder="Time in seconds"]'); - } - - get impersonateAgentListBox(): Locator { - return this.page.locator('ol[role="listbox"] >> text=Impersonate next agent from queue'); - } - - get textArea(): Locator { - return this.page.locator('textarea'); + return this.page.locator('input[name="description"]'); } get btnSave(): Locator { return this.page.locator('button >> text="Save"'); } - get firstRowInTable() { - return this.page.locator('table tr:first-child td:first-child'); - } - firstRowInTriggerTable(triggersName1: string) { return this.page.locator(`text="${triggersName1}"`); } @@ -56,14 +40,6 @@ export class OmnichannelTriggers { return this.toastMessage.locator('role=button'); } - get inputSearch() { - return this.page.locator('[placeholder="Search"]'); - } - - get pageTitle() { - return this.page.locator('[data-qa-type="PageHeader-title"]'); - } - get btnDeletefirstRowInTable() { return this.page.locator('table tr:first-child td:last-child button'); } @@ -76,33 +52,33 @@ export class OmnichannelTriggers { return this.page.locator('text=Trigger removed'); } - get inputCondition(): Locator { - return this.page.locator('button', { has: this.page.locator('select[name="condition"]') }); + get conditionLabel(): Locator { + return this.page.locator('label >> text="Condition"') } get inputConditionValue(): Locator { - return this.page.locator('input[name="conditionValue"]'); + return this.page.locator('input[name="conditions.0.value"]'); } - get inputSender(): Locator { - return this.page.locator('button', { has: this.page.locator('select[name="sender"]') }); + get actionLabel(): Locator { + return this.page.locator('label >> text="Action"') } get inputAgentName(): Locator { - return this.page.locator('input[name="agentName"]'); + return this.page.locator('input[name="actions.0.params.name"]'); } get inputTriggerMessage(): Locator { - return this.page.locator('textarea[name="triggerMessage"]'); + return this.page.locator('textarea[name="actions.0.params.msg"]'); } async selectCondition(condition: string) { - await this.inputCondition.click(); + await this.conditionLabel.click(); await this.page.locator(`li.rcx-option[data-key="${condition}"]`).click(); } async selectSender(sender: 'queue' | 'custom') { - await this.inputSender.click(); + await this.actionLabel.click(); await this.page.locator(`li.rcx-option[data-key="${sender}"]`).click(); } diff --git a/packages/rest-typings/src/v1/omnichannel.ts b/packages/rest-typings/src/v1/omnichannel.ts index fcdbf17bb94..1d2f7a7469b 100644 --- a/packages/rest-typings/src/v1/omnichannel.ts +++ b/packages/rest-typings/src/v1/omnichannel.ts @@ -3615,7 +3615,7 @@ export type OmnichannelEndpoints = { POST: (params: POSTLivechatTriggersParams) => void; }; '/v1/livechat/triggers/:_id': { - GET: () => { trigger: ILivechatTrigger | null }; + GET: () => { trigger: ILivechatTrigger }; DELETE: () => void; }; '/v1/livechat/rooms': {