diff --git a/apps/meteor/app/api/server/v1/cloud.ts b/apps/meteor/app/api/server/v1/cloud.ts index 55b3f8588275..d904d40d84ff 100644 --- a/apps/meteor/app/api/server/v1/cloud.ts +++ b/apps/meteor/app/api/server/v1/cloud.ts @@ -71,6 +71,10 @@ API.v1.addRoute( return API.v1.unauthorized(); } + if (process.env.NODE_ENV === 'development') { + return API.v1.success({ offline: true }); + } + return API.v1.success({ offline: !(await registerPreIntentWorkspaceWizard()) }); }, }, diff --git a/apps/meteor/client/components/AutoCompleteAgent.tsx b/apps/meteor/client/components/AutoCompleteAgent.tsx index f2cbebe46920..059ac4251cc8 100644 --- a/apps/meteor/client/components/AutoCompleteAgent.tsx +++ b/apps/meteor/client/components/AutoCompleteAgent.tsx @@ -27,7 +27,7 @@ const AutoCompleteAgent = ({ haveAll = false, haveNoAgentsSelectedOption = false, excludeId, - showIdleAgents = false, + showIdleAgents = true, onlyAvailable = false, withTitle = false, onChange, diff --git a/apps/meteor/client/components/Omnichannel/hooks/useAgentsList.ts b/apps/meteor/client/components/Omnichannel/hooks/useAgentsList.ts index e2f6f80f2355..ef00a6c0b81d 100644 --- a/apps/meteor/client/components/Omnichannel/hooks/useAgentsList.ts +++ b/apps/meteor/client/components/Omnichannel/hooks/useAgentsList.ts @@ -29,7 +29,7 @@ export const useAgentsList = ( const reload = useCallback(() => setItemsList(new RecordList()), []); const getAgents = useEndpoint('GET', '/v1/livechat/users/agent'); - const { text, onlyAvailable = false, showIdleAgents = false, excludeId, haveAll, haveNoAgentsSelectedOption } = options; + const { text, onlyAvailable = false, showIdleAgents = true, excludeId, haveAll, haveNoAgentsSelectedOption } = options; useComponentDidUpdate(() => { options && reload(); diff --git a/apps/meteor/client/views/marketplace/hooks/useCategories.ts b/apps/meteor/client/views/marketplace/hooks/useCategories.ts index 4a457a146046..bf7a022f2ae3 100644 --- a/apps/meteor/client/views/marketplace/hooks/useCategories.ts +++ b/apps/meteor/client/views/marketplace/hooks/useCategories.ts @@ -21,11 +21,13 @@ export const useCategories = (): [CategoryDropDownGroups, selectedCategoriesList try { const fetchedCategories = await AppClientOrchestratorInstance.getCategories(); - const mappedCategories = fetchedCategories.map((currentCategory) => ({ - id: currentCategory.id, - label: currentCategory.title, - checked: false, - })); + const mappedCategories = fetchedCategories + .filter((currentCategory) => !currentCategory.hidden) + .map((currentCategory) => ({ + id: currentCategory.id, + label: currentCategory.title, + checked: false, + })); setCategories([ { diff --git a/apps/meteor/client/views/setupWizard/SetupWizardRoute.tsx b/apps/meteor/client/views/setupWizard/SetupWizardRoute.tsx index 82d9438513f7..2bc6c4f8a820 100644 --- a/apps/meteor/client/views/setupWizard/SetupWizardRoute.tsx +++ b/apps/meteor/client/views/setupWizard/SetupWizardRoute.tsx @@ -4,6 +4,7 @@ import type { ReactElement } from 'react'; import React from 'react'; import { useTranslation, I18nextProvider } from 'react-i18next'; +import ModalRegion from '../modal/ModalRegion'; import SetupWizardPage from './SetupWizardPage'; import { useBodyPosition } from './hooks/useBodyPosition'; import { useRouteLock } from './hooks/useRouteLock'; @@ -26,6 +27,7 @@ export const SetupWizardRoute = (): ReactElement | null => { + diff --git a/apps/meteor/ee/client/apps/@types/IOrchestrator.ts b/apps/meteor/ee/client/apps/@types/IOrchestrator.ts index 246fd18326c4..ac2642161c2e 100644 --- a/apps/meteor/ee/client/apps/@types/IOrchestrator.ts +++ b/apps/meteor/ee/client/apps/@types/IOrchestrator.ts @@ -23,6 +23,7 @@ export interface IAppExternalURL { export interface ICategory { createdDate: Date; description: string; + hidden: boolean; id: string; modifiedDate: Date; title: string; diff --git a/apps/meteor/tests/data/users.helper.js b/apps/meteor/tests/data/users.helper.js index 82ab8446547d..d69b0413ae0b 100644 --- a/apps/meteor/tests/data/users.helper.js +++ b/apps/meteor/tests/data/users.helper.js @@ -1,3 +1,4 @@ +import { UserStatus } from '@rocket.chat/core-typings'; import { api, credentials, request } from './api-data'; import { password } from './user'; @@ -34,12 +35,9 @@ export const login = (username, password) => }); export const deleteUser = async (user) => - request - .post(api('users.delete')) - .set(credentials) - .send({ - userId: user._id, - }); + request.post(api('users.delete')).set(credentials).send({ + userId: user._id, + }); export const getUserByUsername = (username) => new Promise((resolve) => { @@ -86,3 +84,9 @@ export const setUserActiveStatus = (userId, activeStatus = true) => }) .end(resolve); }); + +export const setUserStatus = (overrideCredentials = credentials, status = UserStatus.ONLINE) => + request.post(api('users.setStatus')).set(overrideCredentials).send({ + message: '', + status, + }); diff --git a/apps/meteor/tests/end-to-end/api/livechat/01-agents.ts b/apps/meteor/tests/end-to-end/api/livechat/01-agents.ts index 6f9120ea9094..044cdf498a88 100644 --- a/apps/meteor/tests/end-to-end/api/livechat/01-agents.ts +++ b/apps/meteor/tests/end-to-end/api/livechat/01-agents.ts @@ -1,4 +1,4 @@ -import type { ILivechatAgent, ILivechatDepartment, IUser } from '@rocket.chat/core-typings'; +import { UserStatus, type ILivechatAgent, type ILivechatDepartment, type IUser } from '@rocket.chat/core-typings'; import { expect } from 'chai'; import { after, before, describe, it } from 'mocha'; import type { Response } from 'supertest'; @@ -16,7 +16,7 @@ import { } from '../../../data/livechat/rooms'; import { updatePermission, updateSetting } from '../../../data/permissions.helper'; import { password } from '../../../data/user'; -import { createUser, deleteUser, getMe, login } from '../../../data/users.helper'; +import { createUser, deleteUser, getMe, login, setUserStatus } from '../../../data/users.helper'; describe('LIVECHAT - Agents', function () { this.retries(0); @@ -114,6 +114,68 @@ describe('LIVECHAT - Agents', function () { expect(res.body.users.every((u: { statusLivechat: string }) => u.statusLivechat === 'available')).to.be.true; }); }); + it('should return an array of available/unavailable agents when onlyAvailable is false', async () => { + await request + .get(api('livechat/users/agent')) + .set(credentials) + .expect('Content-Type', 'application/json') + .query({ onlyAvailable: false }) + .expect(200) + .expect((res: Response) => { + expect(res.body).to.have.property('success', true); + expect(res.body.users).to.be.an('array'); + expect(res.body).to.have.property('offset'); + expect(res.body).to.have.property('total'); + expect(res.body).to.have.property('count'); + expect( + res.body.users.every( + (u: { statusLivechat: string }) => !u.statusLivechat || ['available', 'not-available'].includes(u.statusLivechat), + ), + ).to.be.true; + }); + }); + + it('should return offline agents when showIdleAgents is true', async () => { + await setUserStatus(agent2.credentials, UserStatus.OFFLINE); + await request + .get(api('livechat/users/agent')) + .set(credentials) + .expect('Content-Type', 'application/json') + .query({ showIdleAgents: true }) + .expect(200) + .expect((res: Response) => { + expect(res.body).to.have.property('success', true); + expect(res.body.users).to.be.an('array'); + expect(res.body).to.have.property('offset'); + expect(res.body).to.have.property('total'); + expect(res.body).to.have.property('count'); + expect( + res.body.users.every( + (u: { status: UserStatus }) => + !u.status || [UserStatus.ONLINE, UserStatus.OFFLINE, UserStatus.AWAY, UserStatus.BUSY].includes(u.status), + ), + ).to.be.true; + }); + }); + + it('should return only online agents when showIdleAgents is false', async () => { + await setUserStatus(agent2.credentials, UserStatus.ONLINE); + await request + .get(api('livechat/users/agent')) + .set(credentials) + .expect('Content-Type', 'application/json') + .query({ showIdleAgents: false }) + .expect(200) + .expect((res: Response) => { + expect(res.body).to.have.property('success', true); + expect(res.body.users).to.be.an('array'); + expect(res.body).to.have.property('offset'); + expect(res.body).to.have.property('total'); + expect(res.body).to.have.property('count'); + expect(res.body.users.every((u: { status: UserStatus }) => u.status !== UserStatus.OFFLINE)).to.be.true; + }); + }); + it('should return an array of managers', async () => { await updatePermission('view-livechat-manager', ['admin']); await updatePermission('manage-livechat-agents', ['admin']); diff --git a/packages/rest-typings/src/v1/omnichannel.ts b/packages/rest-typings/src/v1/omnichannel.ts index ea341a3ae15d..fcdbf17bb94d 100644 --- a/packages/rest-typings/src/v1/omnichannel.ts +++ b/packages/rest-typings/src/v1/omnichannel.ts @@ -765,7 +765,7 @@ const LivechatUsersManagerGETSchema = { nullable: true, }, onlyAvailable: { - type: 'string', + type: 'boolean', nullable: true, }, excludeId: {