diff --git a/apps/meteor/app/livechat/client/index.js b/apps/meteor/app/livechat/client/index.js index 23da09e08a290..cd78fbfed7fb0 100644 --- a/apps/meteor/app/livechat/client/index.js +++ b/apps/meteor/app/livechat/client/index.js @@ -3,6 +3,5 @@ import './voip'; import './ui'; import './tabBar'; import './startup/notifyUnreadRooms'; -import './views/app/dialog/closeRoom'; import './stylesheets/livechat.css'; import './externalFrame'; diff --git a/apps/meteor/app/livechat/client/views/app/dialog/closeRoom.html b/apps/meteor/app/livechat/client/views/app/dialog/closeRoom.html deleted file mode 100644 index 0cb20ad48d855..0000000000000 --- a/apps/meteor/app/livechat/client/views/app/dialog/closeRoom.html +++ /dev/null @@ -1,72 +0,0 @@ - diff --git a/apps/meteor/app/livechat/client/views/app/dialog/closeRoom.js b/apps/meteor/app/livechat/client/views/app/dialog/closeRoom.js deleted file mode 100644 index 2722c32477105..0000000000000 --- a/apps/meteor/app/livechat/client/views/app/dialog/closeRoom.js +++ /dev/null @@ -1,192 +0,0 @@ -import { Meteor } from 'meteor/meteor'; -import { Template } from 'meteor/templating'; -import { ReactiveVar } from 'meteor/reactive-var'; -import { TAPi18n } from 'meteor/rocketchat:tap-i18n'; - -import { settings } from '../../../../../settings'; -import { modal } from '../../../../../ui-utils/client'; -import { APIClient, t } from '../../../../../utils'; -import { hasAnyRole } from '../../../../../authorization'; -import { dispatchToastMessage } from '../../../../../../client/lib/toast'; -import './closeRoom.html'; - -const validateRoomComment = (comment) => { - if (!settings.get('Livechat_request_comment_when_closing_conversation')) { - return true; - } - - return comment?.length > 0; -}; - -const validateRoomTags = (tagsRequired, tags) => { - if (!tagsRequired) { - return true; - } - - return tags?.length > 0; -}; - -const checkUserTagPermission = (availableUserTags = [], tag) => { - if (hasAnyRole(Meteor.userId(), ['admin', 'livechat-manager'])) { - return true; - } - - return availableUserTags.includes(tag); -}; - -Template.closeRoom.helpers({ - invalidComment() { - return Template.instance().invalidComment.get(); - }, - tags() { - return Template.instance().tags.get(); - }, - invalidTags() { - return Template.instance().invalidTags.get(); - }, - availableUserTags() { - return Template.instance().availableUserTags.get(); - }, - tagsPlaceHolder() { - let placeholder = TAPi18n.__('Enter_a_tag'); - - if (!Template.instance().tagsRequired.get()) { - placeholder = placeholder.concat(`(${TAPi18n.__('Optional')})`); - } - - return placeholder; - }, - hasAvailableTags() { - const tags = Template.instance().availableTags.get(); - return tags?.length > 0; - }, - canRemoveTag(availableUserTags, tag) { - return checkUserTagPermission(availableUserTags, tag); - }, -}); - -Template.closeRoom.events({ - async 'submit .close-room__content'(e, instance) { - e.preventDefault(); - e.stopPropagation(); - - const comment = instance.$('#comment').val(); - instance.invalidComment.set(!validateRoomComment(comment)); - if (instance.invalidComment.get()) { - return; - } - - const tagsRequired = instance.tagsRequired.get(); - const tags = instance.tags.get(); - - instance.invalidTags.set(!validateRoomTags(tagsRequired, tags)); - if (instance.invalidTags.get()) { - return; - } - - Meteor.call('livechat:closeRoom', this.rid, comment, { clientAction: true, tags }, function (error /* , result*/) { - if (error) { - console.log(error); - dispatchToastMessage({ type: 'error', message: error }); - return; - } - - modal.open({ - title: t('Chat_closed'), - text: t('Chat_closed_successfully'), - type: 'success', - timer: 1000, - showConfirmButton: false, - }); - }); - }, - 'click .remove-tag'(e, instance) { - e.stopPropagation(); - e.preventDefault(); - - const tag = this.valueOf(); - const availableTags = instance.availableTags.get(); - const hasAvailableTags = availableTags?.length > 0; - const availableUserTags = instance.availableUserTags.get(); - if (hasAvailableTags && !checkUserTagPermission(availableUserTags, tag)) { - return; - } - - let tags = instance.tags.get(); - tags = tags.filter((el) => el !== tag); - instance.tags.set(tags); - }, - 'click #addTag'(e, instance) { - e.stopPropagation(); - e.preventDefault(); - - if ($('#tagSelect').find(':selected').is(':disabled')) { - return; - } - - const tags = [...instance.tags.get()]; - const tagVal = $('#tagSelect').val(); - if (tagVal === '' || tags.includes(tagVal)) { - return; - } - - tags.push(tagVal); - instance.tags.set(tags); - $('#tagSelect').val('placeholder'); - }, - 'keydown #tagInput'(e, instance) { - if (e.which === 13) { - e.stopPropagation(); - e.preventDefault(); - - const tags = [...instance.tags.get()]; - const tagVal = $('#tagInput').val(); - if (tagVal === '' || tags.includes(tagVal)) { - return; - } - - tags.push(tagVal); - instance.tags.set(tags); - $('#tagInput').val(''); - } - }, -}); - -Template.closeRoom.onRendered(function () { - this.find('#comment').focus(); -}); - -Template.closeRoom.onCreated(async function () { - this.tags = new ReactiveVar([]); - this.invalidComment = new ReactiveVar(false); - this.invalidTags = new ReactiveVar(false); - this.tagsRequired = new ReactiveVar(false); - this.availableTags = new ReactiveVar([]); - this.availableUserTags = new ReactiveVar([]); - this.agentDepartments = new ReactiveVar([]); - - this.onEnterTag = () => this.invalidTags.set(!validateRoomTags(this.tagsRequired.get(), this.tags.get())); - - const { rid } = Template.currentData(); - const { room } = await APIClient.get(`/v1/rooms.info`, { roomId: rid }); - this.tags.set(room?.tags || []); - - if (room?.departmentId) { - const { department } = await APIClient.get(`/v1/livechat/department/${room.departmentId}`, { includeAgents: false }); - this.tagsRequired.set(department?.requestTagBeforeClosingChat); - } - - const uid = Meteor.userId(); - const { departments } = await APIClient.get(`/v1/livechat/agents/${uid}/departments`); - const agentDepartments = departments.map((dept) => dept.departmentId); - this.agentDepartments.set(agentDepartments); - - Meteor.call('livechat:getTagsList', (err, tagsList) => { - this.availableTags.set(tagsList); - const isAdmin = hasAnyRole(uid, ['admin', 'livechat-manager']); - const availableTags = tagsList - .filter(({ departments }) => isAdmin || departments.length === 0 || departments.some((i) => agentDepartments.includes(i))) - .map(({ name }) => name); - this.availableUserTags.set(availableTags); - }); -}); diff --git a/apps/meteor/app/livechat/client/views/app/livechatRoomTagSelector.html b/apps/meteor/app/livechat/client/views/app/livechatRoomTagSelector.html deleted file mode 100644 index bd10b3cc1d872..0000000000000 --- a/apps/meteor/app/livechat/client/views/app/livechatRoomTagSelector.html +++ /dev/null @@ -1,17 +0,0 @@ - diff --git a/apps/meteor/app/livechat/client/views/app/livechatRoomTagSelector.js b/apps/meteor/app/livechat/client/views/app/livechatRoomTagSelector.js deleted file mode 100644 index b56bbc15ed75a..0000000000000 --- a/apps/meteor/app/livechat/client/views/app/livechatRoomTagSelector.js +++ /dev/null @@ -1,24 +0,0 @@ -import { Meteor } from 'meteor/meteor'; -import { Template } from 'meteor/templating'; -import { ReactiveVar } from 'meteor/reactive-var'; - -import './livechatRoomTagSelector.html'; - -Template.livechatRoomTagSelector.helpers({ - availableTags() { - return Template.instance().availableTags.get(); - }, - - hasAvailableTags() { - const tags = Template.instance().availableTags.get(); - return tags && tags.length > 0; - }, -}); - -Template.livechatRoomTagSelector.onCreated(function () { - this.availableTags = new ReactiveVar([]); - - Meteor.call('livechat:getTagsList', (err, tagsList) => { - this.availableTags.set(tagsList); - }); -}); diff --git a/apps/meteor/app/livechat/client/views/regular.js b/apps/meteor/app/livechat/client/views/regular.js index 17d05a4362ae1..185aa3aa49eed 100644 --- a/apps/meteor/app/livechat/client/views/regular.js +++ b/apps/meteor/app/livechat/client/views/regular.js @@ -1,6 +1,5 @@ import './app/livechatReadOnly'; import './app/livechatNotSubscribed.html'; -import './app/livechatRoomTagSelector'; import './app/tabbar/agentEdit'; import './app/tabbar/agentInfo'; import './app/tabbar/visitorEdit'; diff --git a/apps/meteor/tests/e2e/omnichannel-close-chat.spec.ts b/apps/meteor/tests/e2e/omnichannel-close-chat.spec.ts new file mode 100644 index 0000000000000..7c083a7b03e0e --- /dev/null +++ b/apps/meteor/tests/e2e/omnichannel-close-chat.spec.ts @@ -0,0 +1,62 @@ +import { faker } from '@faker-js/faker'; +import type { Browser, Page } from '@playwright/test'; + +import { test, expect } from './utils/test'; +import { OmnichannelLiveChat, HomeChannel } from './page-objects'; + +const createAuxContext = async (browser: Browser, storageState: string): Promise<{ page: Page; poHomeChannel: HomeChannel }> => { + const page = await browser.newPage({ storageState }); + const poHomeChannel = new HomeChannel(page); + await page.goto('/'); + await page.locator('.main-content').waitFor(); + + return { page, poHomeChannel }; +}; + +test.describe('Omnichannel close chat', () => { + let poLiveChat: OmnichannelLiveChat; + let newUser: { email: string; name: string }; + + let agent: { page: Page; poHomeChannel: HomeChannel }; + + test.beforeAll(async ({ api, browser }) => { + newUser = { + name: faker.name.firstName(), + email: faker.internet.email(), + }; + + // Set user user 1 as manager and agent + await api.post('/livechat/users/agent', { username: 'user1' }); + await api.post('/livechat/users/manager', { username: 'user1' }); + agent = await createAuxContext(browser, 'user1-session.json'); + }); + test.beforeEach(async ({ page }) => { + poLiveChat = new OmnichannelLiveChat(page); + }); + + test.afterAll(async ({ api }) => { + await api.delete('/livechat/users/agent/user1'); + await api.delete('/livechat/users/manager/user1'); + }); + + test('Receiving a message from visitor', async ({ page }) => { + await test.step('Expect send a message as a visitor', async () => { + await page.goto('/livechat'); + await poLiveChat.btnOpenLiveChat('R').click(); + await poLiveChat.sendMessage(newUser, false); + await poLiveChat.onlineAgentMessage.type('this_a_test_message_from_visitor'); + await poLiveChat.btnSendMessageToOnlineAgent.click(); + }); + + await test.step('Expect to have 1 omnichannel assigned to agent 1', async () => { + await agent.poHomeChannel.sidenav.openChat(newUser.name); + }); + + await test.step('Expect to be able to close an omnichannel to conversation', async () => { + await agent.poHomeChannel.content.btnCloseChat.click(); + await agent.poHomeChannel.content.inputModalClosingComment.type('any_comment'); + await agent.poHomeChannel.content.btnModalConfirm.click(); + await expect(agent.poHomeChannel.toastSuccess).toBeVisible(); + }); + }); +}); diff --git a/apps/meteor/tests/e2e/page-objects/fragments/home-content.ts b/apps/meteor/tests/e2e/page-objects/fragments/home-content.ts index a05994b711ac1..87b78d100ee60 100644 --- a/apps/meteor/tests/e2e/page-objects/fragments/home-content.ts +++ b/apps/meteor/tests/e2e/page-objects/fragments/home-content.ts @@ -110,6 +110,14 @@ export class HomeContent { return this.page.locator('[data-qa-id="ToolBoxAction-balloon-arrow-top-right"]'); } + get btnCloseChat(): Locator { + return this.page.locator('[data-qa-id="ToolBoxAction-balloon-close-top-right"]'); + } + + get inputModalClosingComment(): Locator { + return this.page.locator('#modal-root input:nth-child(1)[name="comment"]'); + } + get btnSendTranscript(): Locator { return this.page.locator('[data-qa-id="ToolBoxAction-mail-arrow-top-right"]'); }