From 0780eba7a4e4655995ed52cf7a7165405a35643b Mon Sep 17 00:00:00 2001 From: Kevin Aleman Date: Fri, 23 Aug 2024 10:12:58 -0600 Subject: [PATCH 1/9] Add new setting to enable/disable visitors from closing a conversation --- .../imports/server/rest/appearance.ts | 1 + .../app/livechat/server/api/lib/appearance.ts | 1 + .../app/livechat/server/api/lib/livechat.ts | 1 + .../meteor/app/livechat/server/api/v1/room.ts | 4 ++ .../app/livechat/server/lib/LivechatTyped.ts | 1 + .../omnichannel/appearance/AppearanceForm.tsx | 15 ++++++ .../omnichannel/appearance/AppearancePage.tsx | 1 + apps/meteor/server/settings/omnichannel.ts | 7 +++ .../omnichannel/omnichannel-livechat.spec.ts | 52 +++++++++++++++++++ .../tests/end-to-end/api/livechat/00-rooms.ts | 18 +++++++ packages/i18n/src/locales/en.i18n.json | 2 + .../livechat/src/routes/Chat/connector.tsx | 2 + .../livechat/src/routes/Chat/container.js | 4 +- 13 files changed, 107 insertions(+), 2 deletions(-) diff --git a/apps/meteor/app/livechat/imports/server/rest/appearance.ts b/apps/meteor/app/livechat/imports/server/rest/appearance.ts index 48863fc9e5d3..7496b6243abe 100644 --- a/apps/meteor/app/livechat/imports/server/rest/appearance.ts +++ b/apps/meteor/app/livechat/imports/server/rest/appearance.ts @@ -51,6 +51,7 @@ API.v1.addRoute( 'Livechat_background', 'Livechat_widget_position', 'Livechat_hide_system_messages', + 'Omnichannel_allow_visitors_to_close_conversation', ]; const valid = settings.every((setting) => validSettingList.includes(setting._id)); diff --git a/apps/meteor/app/livechat/server/api/lib/appearance.ts b/apps/meteor/app/livechat/server/api/lib/appearance.ts index 785413ead9d1..0fc7d3547b2c 100644 --- a/apps/meteor/app/livechat/server/api/lib/appearance.ts +++ b/apps/meteor/app/livechat/server/api/lib/appearance.ts @@ -28,6 +28,7 @@ export async function findAppearance(): Promise<{ appearance: ISetting[] }> { 'Livechat_background', 'Livechat_widget_position', 'Livechat_hide_system_messages', + 'Omnichannel_allow_visitors_to_close_conversation', ], }, }; diff --git a/apps/meteor/app/livechat/server/api/lib/livechat.ts b/apps/meteor/app/livechat/server/api/lib/livechat.ts index a922edd40899..8041566d796e 100644 --- a/apps/meteor/app/livechat/server/api/lib/livechat.ts +++ b/apps/meteor/app/livechat/server/api/lib/livechat.ts @@ -142,6 +142,7 @@ export async function settings({ businessUnit = '' }: { businessUnit?: string } hiddenSystemMessages: initSettings.Livechat_hide_system_messages, livechatLogo: initSettings.Assets_livechat_widget_logo, hideWatermark: initSettings.Livechat_hide_watermark || false, + visitorsCanCloseChat: initSettings.Omnichannel_allow_visitors_to_close_conversation, }, theme: { title: initSettings.Livechat_title, diff --git a/apps/meteor/app/livechat/server/api/v1/room.ts b/apps/meteor/app/livechat/server/api/v1/room.ts index b0f45a63ff87..3b796b8b0cf7 100644 --- a/apps/meteor/app/livechat/server/api/v1/room.ts +++ b/apps/meteor/app/livechat/server/api/v1/room.ts @@ -98,6 +98,10 @@ API.v1.addRoute( async post() { const { rid, token } = this.bodyParams; + if (!rcSettings.get('Omnichannel_allow_visitors_to_close_conversation')) { + throw new Error('error-not-allowed-to-close-conversation'); + } + const visitor = await findGuest(token); if (!visitor) { throw new Error('invalid-token'); diff --git a/apps/meteor/app/livechat/server/lib/LivechatTyped.ts b/apps/meteor/app/livechat/server/lib/LivechatTyped.ts index be79d565f6de..24de367c5cbc 100644 --- a/apps/meteor/app/livechat/server/lib/LivechatTyped.ts +++ b/apps/meteor/app/livechat/server/lib/LivechatTyped.ts @@ -1068,6 +1068,7 @@ class LivechatClass { 'Livechat_background', 'Assets_livechat_widget_logo', 'Livechat_hide_watermark', + 'Omnichannel_allow_visitors_to_close_conversation', ] as const; type SettingTypes = (typeof validSettings)[number] | 'Livechat_Show_Connecting'; diff --git a/apps/meteor/client/views/omnichannel/appearance/AppearanceForm.tsx b/apps/meteor/client/views/omnichannel/appearance/AppearanceForm.tsx index 4253ff023ee9..a4435398d9a9 100644 --- a/apps/meteor/client/views/omnichannel/appearance/AppearanceForm.tsx +++ b/apps/meteor/client/views/omnichannel/appearance/AppearanceForm.tsx @@ -52,6 +52,7 @@ const AppearanceForm = () => { const livechatWidgetPositionField = useUniqueId(); const livechatBackgroundField = useUniqueId(); const livechatHideSystemMessagesField = useUniqueId(); + const omnichannelVisitorsCanCloseConversationField = useUniqueId(); return ( @@ -140,6 +141,20 @@ const AppearanceForm = () => { /> + + + + {t('Omnichannel_allow_visitors_to_close_conversation')} + + ( + + )} + /> + + diff --git a/apps/meteor/client/views/omnichannel/appearance/AppearancePage.tsx b/apps/meteor/client/views/omnichannel/appearance/AppearancePage.tsx index a2cfb7b8103b..b90c32af6a7d 100644 --- a/apps/meteor/client/views/omnichannel/appearance/AppearancePage.tsx +++ b/apps/meteor/client/views/omnichannel/appearance/AppearancePage.tsx @@ -28,6 +28,7 @@ type LivechatAppearanceSettings = { Livechat_conversation_finished_text: string; Livechat_enable_message_character_limit: boolean; Livechat_message_character_limit: number; + Omnichannel_allow_visitors_to_close_conversation: boolean; }; type AppearanceSettings = Partial; diff --git a/apps/meteor/server/settings/omnichannel.ts b/apps/meteor/server/settings/omnichannel.ts index ed1daa8ce228..c86cd6674d4e 100644 --- a/apps/meteor/server/settings/omnichannel.ts +++ b/apps/meteor/server/settings/omnichannel.ts @@ -157,6 +157,13 @@ export const createOmniSettings = () => i18nLabel: 'Show_agent_email', }); + await this.add('Omnichannel_allow_visitors_to_close_conversation', true, { + type: 'boolean', + group: 'Omnichannel', + public: true, + enableQuery: omnichannelEnabledQuery, + }); + await this.add('Livechat_request_comment_when_closing_conversation', true, { type: 'boolean', group: 'Omnichannel', diff --git a/apps/meteor/tests/e2e/omnichannel/omnichannel-livechat.spec.ts b/apps/meteor/tests/e2e/omnichannel/omnichannel-livechat.spec.ts index bf14584ed89f..550c0ea93601 100644 --- a/apps/meteor/tests/e2e/omnichannel/omnichannel-livechat.spec.ts +++ b/apps/meteor/tests/e2e/omnichannel/omnichannel-livechat.spec.ts @@ -93,6 +93,58 @@ test.describe.serial('OC - Livechat', () => { }); }); +test.describe.serial('OC - Livechat - Visitors closing the room is disabled', () => { + let poLiveChat: OmnichannelLiveChat; + let poHomeOmnichannel: HomeOmnichannel; + + test.beforeAll(async ({ api }) => { + const statusCode = (await api.post('/livechat/users/agent', { username: 'user1' })).status(); + expect(statusCode).toBe(200); + }); + + test.beforeAll(async ({ browser, api }) => { + const { page: livechatPage } = await createAuxContext(browser, Users.user1, '/livechat', false); + + poLiveChat = new OmnichannelLiveChat(livechatPage, api); + }); + + test.beforeAll(async ({ browser, api }) => { + await api.post('/settings/Omnichannel_allow_visitors_to_close_conversation', { value: false }); + const { page: omniPage } = await createAuxContext(browser, Users.user1, '/', true); + poHomeOmnichannel = new HomeOmnichannel(omniPage); + }); + + test.afterAll(async ({ api }) => { + await api.post('/settings/Omnichannel_allow_visitors_to_close_conversation', { value: true }); + await api.delete('/livechat/users/agent/user1'); + await poLiveChat.page.close(); + }); + + test('OC - Livechat - Close Chat disabled', async () => { + await poLiveChat.page.reload(); + await poLiveChat.openAnyLiveChat(); + await poLiveChat.sendMessage(firstVisitor, false); + await poLiveChat.onlineAgentMessage.fill('this_a_test_message_from_user'); + await poLiveChat.btnSendMessageToOnlineAgent.click(); + + await test.step('expect to close a livechat conversation', async () => { + await expect(poLiveChat.btnOptions).not.toBeVisible(); + await expect(poLiveChat.btnCloseChat).not.toBeVisible(); + }); + }); + + test('OC - Livechat - Close chat disabled, agents can close', async () => { + await poHomeOmnichannel.sidenav.openChat(firstVisitor.name); + + await test.step('expect livechat conversation to be closed by agent', async () => { + await poHomeOmnichannel.content.btnCloseChat.click(); + await poHomeOmnichannel.content.closeChatModal.inputComment.fill('this_is_a_test_comment'); + await poHomeOmnichannel.content.closeChatModal.btnConfirm.click(); + await expect(poHomeOmnichannel.toastSuccess).toBeVisible(); + }); + }); +}); + test.describe.serial('OC - Livechat - Resub after close room', () => { let poLiveChat: OmnichannelLiveChat; let poHomeOmnichannel: HomeOmnichannel; diff --git a/apps/meteor/tests/end-to-end/api/livechat/00-rooms.ts b/apps/meteor/tests/end-to-end/api/livechat/00-rooms.ts index f3ae205c7090..2dae9cd92f16 100644 --- a/apps/meteor/tests/end-to-end/api/livechat/00-rooms.ts +++ b/apps/meteor/tests/end-to-end/api/livechat/00-rooms.ts @@ -689,6 +689,24 @@ describe('LIVECHAT - rooms', () => { expect(latestRoom).to.not.have.property('pdfTranscriptFileId'); }, ); + + describe('Special case: visitors closing is disabled', () => { + before(async () => { + await updateSetting('Omnichannel_allow_visitors_to_close_conversation', false); + }); + after(async () => { + await updateSetting('Omnichannel_allow_visitors_to_close_conversation', true); + }); + it('should not allow visitor to close a conversation', async () => { + await request + .post(api('livechat/room.close')) + .send({ + token: visitor.token, + rid: room._id, + }) + .expect(400); + }); + }); }); describe('livechat/room.forward', () => { diff --git a/packages/i18n/src/locales/en.i18n.json b/packages/i18n/src/locales/en.i18n.json index 204cc75b79f5..9105664b2bfb 100644 --- a/packages/i18n/src/locales/en.i18n.json +++ b/packages/i18n/src/locales/en.i18n.json @@ -4035,6 +4035,8 @@ "Omnichannel_Reports_Summary": "Gain insights into your operation and export your metrics.", "Omnichannel_max_fallback_forward_depth": "Maximum fallback forward departments depth", "Omnichannel_max_fallback_forward_depth_Description": "Maximum number of hops that a room being transfered will do when the target department has a Fallback Forward Department set up. When limit is reached, chat won't be transferred and process will stop. Depending on your configuration, setting a high number may cause performance issues.", + "Omnichannel_allow_visitors_to_close_conversation": "Allow visitors to close ongoing conversations", + "Omnichannel_allow_visitors_to_close_conversation_Description": "When disabled, visitors won't be able to close an ongoing conversation either via UI or via API.", "On": "On", "on-hold-livechat-room": "On Hold Omnichannel Room", "on-hold-livechat-room_description": "Permission to on hold omnichannel room", diff --git a/packages/livechat/src/routes/Chat/connector.tsx b/packages/livechat/src/routes/Chat/connector.tsx index 36e574b246b1..3c72f9ae88cf 100644 --- a/packages/livechat/src/routes/Chat/connector.tsx +++ b/packages/livechat/src/routes/Chat/connector.tsx @@ -22,6 +22,7 @@ export const ChatConnector: FunctionalComponent<{ path: string; default: boolean nameFieldRegistrationForm, emailFieldRegistrationForm, limitTextLength, + visitorsCanCloseChat, }, messages: { conversationFinishedMessage }, theme: { title = '' } = {}, @@ -94,6 +95,7 @@ export const ChatConnector: FunctionalComponent<{ path: string; default: boolean ongoingCall={ongoingCall} messageListPosition={messageListPosition} theme={theme} + visitorsCanCloseChat={visitorsCanCloseChat} /> ); }; diff --git a/packages/livechat/src/routes/Chat/container.js b/packages/livechat/src/routes/Chat/container.js index 19172cc7fe5a..43ff281c6472 100644 --- a/packages/livechat/src/routes/Chat/container.js +++ b/packages/livechat/src/routes/Chat/container.js @@ -288,8 +288,8 @@ class ChatContainer extends Component { }; canFinishChat = () => { - const { room, connecting } = this.props; - return room !== undefined || connecting; + const { room, connecting, visitorsCanCloseChat } = this.props; + return visitorsCanCloseChat && (room !== undefined || connecting); }; canRemoveUserData = () => { From 41154b44eabccf468775f9e641c41d27fd8c35dd Mon Sep 17 00:00:00 2001 From: Kevin Aleman Date: Fri, 23 Aug 2024 10:39:22 -0600 Subject: [PATCH 2/9] ts --- packages/livechat/src/store/index.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/livechat/src/store/index.tsx b/packages/livechat/src/store/index.tsx index f8629ce693cc..e7d4b8caae17 100644 --- a/packages/livechat/src/store/index.tsx +++ b/packages/livechat/src/store/index.tsx @@ -59,6 +59,7 @@ export type StoreState = { hideWatermark?: boolean; livechatLogo?: { url: string }; transcript?: boolean; + visitorsCanCloseChat?: boolean; }; online?: boolean; departments: Department[]; From 8640afed2fb48fc298992f78f11bad0f810efa66 Mon Sep 17 00:00:00 2001 From: Kevin Aleman Date: Fri, 23 Aug 2024 11:44:05 -0600 Subject: [PATCH 3/9] Create healthy-rivers-nail.md --- .changeset/healthy-rivers-nail.md | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 .changeset/healthy-rivers-nail.md diff --git a/.changeset/healthy-rivers-nail.md b/.changeset/healthy-rivers-nail.md new file mode 100644 index 000000000000..abe7d2a8ba21 --- /dev/null +++ b/.changeset/healthy-rivers-nail.md @@ -0,0 +1,8 @@ +--- +"@rocket.chat/meteor": patch +"@rocket.chat/i18n": patch +"@rocket.chat/livechat": patch +--- + +Added new setting `Allow visitors to finish conversations` that allows admins to decide if omnichannel visitors can close a conversation or not. This doesn't affect agent's capabilities of room closing, neither apps using the livechat bridge to close rooms. +However, if currently your integration relies on `livechat/room.close` endpoint for closing conversations, it's advised to use the authenticated version `livechat/room.closeByUser` of it before turning off this setting. From d43f38782386a4a2a171f25a2336076a5447591b Mon Sep 17 00:00:00 2001 From: Kevin Aleman Date: Fri, 23 Aug 2024 11:44:55 -0600 Subject: [PATCH 4/9] Apply suggestions from code review --- packages/i18n/src/locales/en.i18n.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/i18n/src/locales/en.i18n.json b/packages/i18n/src/locales/en.i18n.json index 9105664b2bfb..756d3839f015 100644 --- a/packages/i18n/src/locales/en.i18n.json +++ b/packages/i18n/src/locales/en.i18n.json @@ -4035,8 +4035,8 @@ "Omnichannel_Reports_Summary": "Gain insights into your operation and export your metrics.", "Omnichannel_max_fallback_forward_depth": "Maximum fallback forward departments depth", "Omnichannel_max_fallback_forward_depth_Description": "Maximum number of hops that a room being transfered will do when the target department has a Fallback Forward Department set up. When limit is reached, chat won't be transferred and process will stop. Depending on your configuration, setting a high number may cause performance issues.", - "Omnichannel_allow_visitors_to_close_conversation": "Allow visitors to close ongoing conversations", - "Omnichannel_allow_visitors_to_close_conversation_Description": "When disabled, visitors won't be able to close an ongoing conversation either via UI or via API.", + "Omnichannel_allow_visitors_to_close_conversation": "Allow visitors to finish conversations", + "Omnichannel_allow_visitors_to_close_conversation_Description": "When disabled, visitors won't be able to finish an ongoing conversation either via UI or via API.", "On": "On", "on-hold-livechat-room": "On Hold Omnichannel Room", "on-hold-livechat-room_description": "Permission to on hold omnichannel room", From 828a2aec692dd0a5dcf8baa9eb79e8f93207117f Mon Sep 17 00:00:00 2001 From: Kevin Aleman Date: Fri, 23 Aug 2024 12:02:16 -0600 Subject: [PATCH 5/9] Update apps/meteor/tests/end-to-end/api/livechat/00-rooms.ts --- apps/meteor/tests/end-to-end/api/livechat/00-rooms.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/apps/meteor/tests/end-to-end/api/livechat/00-rooms.ts b/apps/meteor/tests/end-to-end/api/livechat/00-rooms.ts index 2dae9cd92f16..44e83a7ae592 100644 --- a/apps/meteor/tests/end-to-end/api/livechat/00-rooms.ts +++ b/apps/meteor/tests/end-to-end/api/livechat/00-rooms.ts @@ -698,6 +698,10 @@ describe('LIVECHAT - rooms', () => { await updateSetting('Omnichannel_allow_visitors_to_close_conversation', true); }); it('should not allow visitor to close a conversation', async () => { + const { + room, + visitor, + } = await startANewLivechatRoomAndTakeIt(); await request .post(api('livechat/room.close')) .send({ From c8fba8f95632947bd737f482e5fb93537ddf7547 Mon Sep 17 00:00:00 2001 From: Kevin Aleman Date: Fri, 23 Aug 2024 12:24:31 -0600 Subject: [PATCH 6/9] lint --- apps/meteor/tests/end-to-end/api/livechat/00-rooms.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/apps/meteor/tests/end-to-end/api/livechat/00-rooms.ts b/apps/meteor/tests/end-to-end/api/livechat/00-rooms.ts index 44e83a7ae592..f3f7bc4ae6cf 100644 --- a/apps/meteor/tests/end-to-end/api/livechat/00-rooms.ts +++ b/apps/meteor/tests/end-to-end/api/livechat/00-rooms.ts @@ -698,10 +698,7 @@ describe('LIVECHAT - rooms', () => { await updateSetting('Omnichannel_allow_visitors_to_close_conversation', true); }); it('should not allow visitor to close a conversation', async () => { - const { - room, - visitor, - } = await startANewLivechatRoomAndTakeIt(); + const { room, visitor } = await startANewLivechatRoomAndTakeIt(); await request .post(api('livechat/room.close')) .send({ From 2b068c7c1dad56f221d73f998af6a111d15621b3 Mon Sep 17 00:00:00 2001 From: Kevin Aleman Date: Fri, 23 Aug 2024 13:29:43 -0600 Subject: [PATCH 7/9] Update .changeset/healthy-rivers-nail.md Co-authored-by: Matheus Barbosa Silva <36537004+matheusbsilva137@users.noreply.github.com> --- .changeset/healthy-rivers-nail.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.changeset/healthy-rivers-nail.md b/.changeset/healthy-rivers-nail.md index abe7d2a8ba21..a8da9bec846e 100644 --- a/.changeset/healthy-rivers-nail.md +++ b/.changeset/healthy-rivers-nail.md @@ -1,7 +1,7 @@ --- -"@rocket.chat/meteor": patch -"@rocket.chat/i18n": patch -"@rocket.chat/livechat": patch +"@rocket.chat/meteor": minor +"@rocket.chat/i18n": minor +"@rocket.chat/livechat": minor --- Added new setting `Allow visitors to finish conversations` that allows admins to decide if omnichannel visitors can close a conversation or not. This doesn't affect agent's capabilities of room closing, neither apps using the livechat bridge to close rooms. From f911045daae21582140f6bf54ab63bb4a98051a7 Mon Sep 17 00:00:00 2001 From: Kevin Aleman Date: Mon, 26 Aug 2024 07:47:30 -0600 Subject: [PATCH 8/9] Update omnichannel-livechat.spec.ts --- apps/meteor/tests/e2e/omnichannel/omnichannel-livechat.spec.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/apps/meteor/tests/e2e/omnichannel/omnichannel-livechat.spec.ts b/apps/meteor/tests/e2e/omnichannel/omnichannel-livechat.spec.ts index 550c0ea93601..e8044ddffd40 100644 --- a/apps/meteor/tests/e2e/omnichannel/omnichannel-livechat.spec.ts +++ b/apps/meteor/tests/e2e/omnichannel/omnichannel-livechat.spec.ts @@ -98,8 +98,7 @@ test.describe.serial('OC - Livechat - Visitors closing the room is disabled', () let poHomeOmnichannel: HomeOmnichannel; test.beforeAll(async ({ api }) => { - const statusCode = (await api.post('/livechat/users/agent', { username: 'user1' })).status(); - expect(statusCode).toBe(200); + await api.post('/livechat/users/agent', { username: 'user1' }); }); test.beforeAll(async ({ browser, api }) => { From 437f6a8d05b2eacea292b8a2dec539be6e36a6bb Mon Sep 17 00:00:00 2001 From: Kevin Aleman Date: Mon, 26 Aug 2024 12:55:15 -0600 Subject: [PATCH 9/9] yet another cr --- .../tests/e2e/omnichannel/omnichannel-livechat.spec.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/meteor/tests/e2e/omnichannel/omnichannel-livechat.spec.ts b/apps/meteor/tests/e2e/omnichannel/omnichannel-livechat.spec.ts index e8044ddffd40..405e7f82e3c4 100644 --- a/apps/meteor/tests/e2e/omnichannel/omnichannel-livechat.spec.ts +++ b/apps/meteor/tests/e2e/omnichannel/omnichannel-livechat.spec.ts @@ -2,6 +2,7 @@ import { createFakeVisitor } from '../../mocks/data'; import { createAuxContext } from '../fixtures/createAuxContext'; import { Users } from '../fixtures/userStates'; import { HomeOmnichannel, OmnichannelLiveChat } from '../page-objects'; +import { setSettingValueById } from '../utils'; import { createAgent } from '../utils/omnichannel/agents'; import { test, expect } from '../utils/test'; @@ -108,13 +109,13 @@ test.describe.serial('OC - Livechat - Visitors closing the room is disabled', () }); test.beforeAll(async ({ browser, api }) => { - await api.post('/settings/Omnichannel_allow_visitors_to_close_conversation', { value: false }); + await setSettingValueById(api, 'Livechat_allow_visitor_closing_chat', false); const { page: omniPage } = await createAuxContext(browser, Users.user1, '/', true); poHomeOmnichannel = new HomeOmnichannel(omniPage); }); test.afterAll(async ({ api }) => { - await api.post('/settings/Omnichannel_allow_visitors_to_close_conversation', { value: true }); + await setSettingValueById(api, 'Livechat_allow_visitor_closing_chat', true); await api.delete('/livechat/users/agent/user1'); await poLiveChat.page.close(); });