diff --git a/client/sidebar/sections/Omnichannel.js b/client/sidebar/sections/Omnichannel.js index e17db6bbea9b1..0b76c661d8768 100644 --- a/client/sidebar/sections/Omnichannel.js +++ b/client/sidebar/sections/Omnichannel.js @@ -1,17 +1,20 @@ import React from 'react'; import { Sidebar } from '@rocket.chat/fuselage'; +import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; +import { useToastMessageDispatch } from '../../contexts/ToastMessagesContext'; import { useTranslation } from '../../contexts/TranslationContext'; import { useMethod } from '../../contexts/ServerContext'; import { useOmnichannelShowQueueLink, useOmnichannelAgentAvailable, useOmnichannelQueueLink, useOmnichannelDirectoryLink } from '../../contexts/OmnichannelContext'; const OmnichannelSection = React.memo((props) => { - const method = useMethod('livechat:changeLivechatStatus'); + const changeAgentStatus = useMethod('livechat:changeLivechatStatus'); const t = useTranslation(); const agentAvailable = useOmnichannelAgentAvailable(); const showOmnichannelQueueLink = useOmnichannelShowQueueLink(); const queueLink = useOmnichannelQueueLink(); const directoryLink = useOmnichannelDirectoryLink(); + const dispatchToastMessage = useToastMessageDispatch(); const icon = { title: agentAvailable ? t('Available') : t('Not_Available'), @@ -23,12 +26,20 @@ const OmnichannelSection = React.memo((props) => { title: t('Contact_Center'), icon: 'contact', }; + const handleStatusChange = useMutableCallback(async () => { + try { + await changeAgentStatus(); + } catch (error) { + dispatchToastMessage({ type: 'error', message: error }); + console.log(error); + } + }); return {t('Omnichannel')} {showOmnichannelQueueLink && } - { method(); }}/> + ; diff --git a/client/views/omnichannel/businessHours/BusinessHoursFormContainer.js b/client/views/omnichannel/businessHours/BusinessHoursFormContainer.js index 7501928aa38dc..5a8aa37d80cc7 100644 --- a/client/views/omnichannel/businessHours/BusinessHoursFormContainer.js +++ b/client/views/omnichannel/businessHours/BusinessHoursFormContainer.js @@ -1,4 +1,4 @@ -import React, { useEffect } from 'react'; +import React, { useEffect, useState } from 'react'; import { FieldGroup, Box } from '@rocket.chat/fuselage'; import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; import { useSubscription } from 'use-subscription'; @@ -23,9 +23,12 @@ const getInitalData = ({ workHours }) => ({ const cleanFunc = () => {}; -const BusinessHoursFormContainer = ({ data, saveRef, onChange }) => { +const BusinessHoursFormContainer = ({ data, saveRef, onChange = () => {} }) => { const forms = useSubscription(formsSubscription); + const [hasChangesMultiple, setHasChangesMultiple] = useState(false); + const [hasChangesTimeZone, setHasChangesTimeZone] = useState(false); + const { useBusinessHoursTimeZone = cleanFunc, useBusinessHoursMultiple = cleanFunc, @@ -45,13 +48,13 @@ const BusinessHoursFormContainer = ({ data, saveRef, onChange }) => { saveRef.current.form = values; useEffect(() => { - onChange(hasUnsavedChanges); + onChange(hasUnsavedChanges || (showMultipleBHForm && hasChangesMultiple) || (showTimezone && hasChangesTimeZone)); }); return - {showMultipleBHForm && MultipleBHForm && } - {showTimezone && TimezoneForm && } + {showMultipleBHForm && MultipleBHForm && } + {showTimezone && TimezoneForm && } ; diff --git a/client/views/omnichannel/businessHours/BusinessHoursPage.js b/client/views/omnichannel/businessHours/BusinessHoursPage.js index 55c2c9198248c..f3a6dacaff9f5 100644 --- a/client/views/omnichannel/businessHours/BusinessHoursPage.js +++ b/client/views/omnichannel/businessHours/BusinessHoursPage.js @@ -22,14 +22,14 @@ const BusinessHoursPage = () => { return - - + - + ; }; diff --git a/client/views/omnichannel/businessHours/BusinessHoursRouter.js b/client/views/omnichannel/businessHours/BusinessHoursRouter.js index 55f9b937039c9..24b989d14cc2b 100644 --- a/client/views/omnichannel/businessHours/BusinessHoursRouter.js +++ b/client/views/omnichannel/businessHours/BusinessHoursRouter.js @@ -28,7 +28,7 @@ const BusinessHoursRouter = () => { }, [context, isSingleBH, router, type]); if ((context === 'edit' && type) || (isSingleBH && (context !== 'edit' || type !== 'default'))) { - return ; + return type ? : null; } if (context === 'new') { diff --git a/client/views/omnichannel/businessHours/EditBusinessHoursPage.js b/client/views/omnichannel/businessHours/EditBusinessHoursPage.js index 260a0a0f6f4c4..535d601c01838 100644 --- a/client/views/omnichannel/businessHours/EditBusinessHoursPage.js +++ b/client/views/omnichannel/businessHours/EditBusinessHoursPage.js @@ -23,7 +23,7 @@ const EditBusinessHoursPage = ({ id, type }) => { const saveData = useRef({ form: {} }); - const [hasUnsavedChanges, setHasUnsavedChanges] = useState(false); + const [hasChanges, setHasChanges] = useState(false); const save = useMethod('livechat:saveBusinessHour'); const deleteBH = useMethod('livechat:removeBusinessHour'); @@ -112,7 +112,7 @@ const EditBusinessHoursPage = ({ id, type }) => { {type === 'custom' && } - @@ -121,7 +121,7 @@ const EditBusinessHoursPage = ({ id, type }) => { setHasUnsavedChanges(hasChanges)} /> + onChange={setHasChanges} /> ; }; diff --git a/client/views/omnichannel/businessHours/NewBusinessHoursPage.js b/client/views/omnichannel/businessHours/NewBusinessHoursPage.js index 74199b6e007ad..f23b596916944 100644 --- a/client/views/omnichannel/businessHours/NewBusinessHoursPage.js +++ b/client/views/omnichannel/businessHours/NewBusinessHoursPage.js @@ -1,4 +1,4 @@ -import React, { useRef } from 'react'; +import React, { useRef, useState } from 'react'; import { Button, ButtonGroup } from '@rocket.chat/fuselage'; import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; @@ -35,6 +35,8 @@ const NewBusinessHoursPage = () => { const t = useTranslation(); const dispatchToastMessage = useToastMessageDispatch(); + const [hasChanges, setHasChanges] = useState(false); + const saveData = useRef({ form: {} }); const save = useMethod('livechat:saveBusinessHour'); @@ -83,13 +85,13 @@ const NewBusinessHoursPage = () => { - - + ; }; diff --git a/ee/app/livechat-enterprise/server/business-hour/Multiple.ts b/ee/app/livechat-enterprise/server/business-hour/Multiple.ts index 35b1177712498..22f2e67e3d519 100644 --- a/ee/app/livechat-enterprise/server/business-hour/Multiple.ts +++ b/ee/app/livechat-enterprise/server/business-hour/Multiple.ts @@ -6,8 +6,8 @@ import { } from '../../../../../app/livechat/server/business-hour/AbstractBusinessHour'; import { ILivechatBusinessHour } from '../../../../../definition/ILivechatBusinessHour'; import { LivechatDepartmentRaw } from '../../../../../app/models/server/raw/LivechatDepartment'; -import { LivechatDepartmentAgentsRaw } from '../../../models/server/raw/LivechatDepartmentAgents'; -import { LivechatDepartment, LivechatDepartmentAgents } from '../../../../../app/models/server/raw'; +import LivechatDepartmentAgentsRaw from '../../../models/server/raw/LivechatDepartmentAgents'; +import { LivechatDepartment } from '../../../../../app/models/server/raw'; import { filterBusinessHoursThatMustBeOpened } from '../../../../../app/livechat/server/business-hour/Helper'; import { closeBusinessHour, openBusinessHour, removeBusinessHourByAgentIds } from './Helper'; @@ -19,7 +19,7 @@ interface IBusinessHoursExtraProperties extends ILivechatBusinessHour { export class MultipleBusinessHoursBehavior extends AbstractBusinessHourBehavior implements IBusinessHourBehavior { private DepartmentsRepository: LivechatDepartmentRaw = LivechatDepartment; - private DepartmentsAgentsRepository = LivechatDepartmentAgents as LivechatDepartmentAgentsRaw; + private DepartmentsAgentsRepository = LivechatDepartmentAgentsRaw; constructor() { super(); diff --git a/ee/app/models/server/index.js b/ee/app/models/server/index.js index 30d5dd386edc0..c44afe4be4bb1 100644 --- a/ee/app/models/server/index.js +++ b/ee/app/models/server/index.js @@ -7,6 +7,7 @@ import CannedResponseRaw from './raw/CannedResponse'; import LivechatPriorityRaw from './raw/LivechatPriority'; import LivechatTagRaw from './raw/LivechatTag'; import LivechatUnitMonitorsRaw from './raw/LivechatUnitMonitors'; +import LivechatDepartmentAgentsRaw from './raw/LivechatDepartmentAgents'; import './models/LivechatDepartment'; import './models/LivechatRooms'; import './models/LivechatInquiry'; @@ -24,4 +25,5 @@ export { LivechatUnitMonitorsRaw, LivechatPriority, LivechatPriorityRaw, + LivechatDepartmentAgentsRaw, }; diff --git a/ee/app/models/server/raw/LivechatDepartmentAgents.ts b/ee/app/models/server/raw/LivechatDepartmentAgents.ts index c64046c4f4b6e..51f4c343056b6 100644 --- a/ee/app/models/server/raw/LivechatDepartmentAgents.ts +++ b/ee/app/models/server/raw/LivechatDepartmentAgents.ts @@ -1,4 +1,5 @@ import { LivechatDepartmentAgentsRaw as Raw } from '../../../../../app/models/server/raw/LivechatDepartmentAgents'; +import { LivechatDepartmentAgents } from '../../../../../app/models/server'; export class LivechatDepartmentAgentsRaw extends Raw { findAgentsByAgentIdAndBusinessHourId(agentId: string, businessHourId: string): Promise> { @@ -24,3 +25,5 @@ export class LivechatDepartmentAgentsRaw extends Raw { return this.col.aggregate([match, lookup, unwind, withBusinessHourId, project]).toArray(); } } + +export default new LivechatDepartmentAgentsRaw(LivechatDepartmentAgents.model.rawCollection()); diff --git a/ee/client/omnichannel/additionalForms/BusinessHoursMultiple.js b/ee/client/omnichannel/additionalForms/BusinessHoursMultiple.js index 98efc572e9db0..9280866237a81 100644 --- a/ee/client/omnichannel/additionalForms/BusinessHoursMultiple.js +++ b/ee/client/omnichannel/additionalForms/BusinessHoursMultiple.js @@ -14,12 +14,15 @@ const getInitialData = (data = {}) => ({ departments: mapDepartments(data.departments), }); -const BusinessHoursMultipleContainer = ({ onChange, data: initialData, className }) => { +const BusinessHoursMultipleContainer = ({ onChange, data: initialData, className, hasChangesAndIsValid = () => {} }) => { const { value: data, phase: state } = useEndpointData('livechat/department'); - const { values, handlers } = useForm(getInitialData(initialData)); + const { values, handlers, hasUnsavedChanges } = useForm(getInitialData(initialData)); + + const { name } = values; onChange(values); + hasChangesAndIsValid(hasUnsavedChanges && !!name); const departmentList = useMemo(() => data && data.departments?.map(({ _id, name }) => [_id, name]), [data]); @@ -59,7 +62,7 @@ export const BusinessHoursMultiple = ({ values = {}, handlers = {}, className, d - {t('Name')} + {t('Name')}* diff --git a/ee/client/omnichannel/additionalForms/BusinessHoursTimeZone.js b/ee/client/omnichannel/additionalForms/BusinessHoursTimeZone.js index 19ab8046c31cc..874cc0a1c4205 100644 --- a/ee/client/omnichannel/additionalForms/BusinessHoursTimeZone.js +++ b/ee/client/omnichannel/additionalForms/BusinessHoursTimeZone.js @@ -1,14 +1,21 @@ -import React, { useMemo, useState } from 'react'; +import React, { useMemo } from 'react'; import { SelectFiltered, Field } from '@rocket.chat/fuselage'; -import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; import { useTranslation } from '../../../../client/contexts/TranslationContext'; import { useTimezoneNameList } from '../../../../client/hooks/useTimezoneNameList'; +import { useForm } from '../../../../client/hooks/useForm'; -const BusinessHoursTimeZone = ({ onChange, data, className }) => { +const getInitialData = (data = {}) => ({ + name: data ?? '', +}); + +const BusinessHoursTimeZone = ({ onChange, data, className, hasChanges = () => {} }) => { const t = useTranslation(); - const [timezone, setTimezone] = useState(data); + const { values, handlers, hasUnsavedChanges } = useForm(getInitialData(data)); + + const { name } = values; + const { handleName } = handlers; const timeZones = useTimezoneNameList(); @@ -17,18 +24,15 @@ const BusinessHoursTimeZone = ({ onChange, data, className }) => { t(name), ]), [t, timeZones]); - const handleChange = useMutableCallback((value) => { - setTimezone(value); - }); - - onChange({ name: timezone }); + onChange({ name }); + hasChanges(hasUnsavedChanges); return {t('Timezone')} - + ; }; diff --git a/packages/rocketchat-i18n/i18n/en.i18n.json b/packages/rocketchat-i18n/i18n/en.i18n.json index 949cd34027b72..fc07fcb11d0dc 100644 --- a/packages/rocketchat-i18n/i18n/en.i18n.json +++ b/packages/rocketchat-i18n/i18n/en.i18n.json @@ -2733,6 +2733,7 @@ "Newer_than_may_not_exceed_Older_than": "\"Newer than\" may not exceed \"Older than\"", "Nickname": "Nickname", "Nickname_Placeholder": "Enter your nickname...", + "No": "No", "No_available_agents_to_transfer": "No available agents to transfer", "No_Canned_Responses": "No Canned Responses", "No_channel_with_name_%s_was_found": "No channel with name \"%s\" was found!", diff --git a/packages/rocketchat-i18n/i18n/pt-BR.i18n.json b/packages/rocketchat-i18n/i18n/pt-BR.i18n.json index b2f70e96e0f41..4d0e1d47c3eb8 100644 --- a/packages/rocketchat-i18n/i18n/pt-BR.i18n.json +++ b/packages/rocketchat-i18n/i18n/pt-BR.i18n.json @@ -1575,7 +1575,7 @@ "Forward_to_user": "Encaminhar ao usuário", "Free": "Grátis", "Frequently_Used": "Usados frequentemente", - "Friday": "Sexta-Feira", + "Friday": "Sexta-feira", "From": "De", "From_Email": "Email De", "From_email_warning": "Aviso: O campo De está sujeito às configurações do seu servidor de emails.", @@ -2371,6 +2371,7 @@ "Newer_than_may_not_exceed_Older_than": "\"Mais recente que\" não pode exceder \"Mais antigo que\"", "Nickname": "Apelido", "Nickname_Placeholder": "Digite seu apelido...", + "No": "Não", "No_available_agents_to_transfer": "Nenhum agente disponível para transferir", "No_channel_with_name_%s_was_found": "Nenhum canal com nome \"%s\" foi encontrado!", "No_channels_yet": "Você não faz parte de nenhum canal ainda.", @@ -3181,6 +3182,7 @@ "Thursday": "Quinta-feira", "Time_in_seconds": "Tempo em segundos", "Timeouts": "Tempos limite", + "Timezone": "Fuso horário", "Title": "Título", "Title_bar_color": "Cor da barra de título", "Title_bar_color_offline": "Cor da barra de título quando offline", @@ -3230,7 +3232,7 @@ "Triggers": "Gatilhos", "Troubleshoot_Disable_Notifications": "Desativar as notificações", "True": "Sim", - "Tuesday": "terça", + "Tuesday": "Terça-feira", "Turn_OFF": "Desligar", "Turn_ON": "Ligar", "Two Factor Authentication": "Autenticação de dois fatores",