From 6ef0532fdbc8fca631d5cf6da512b79aec11c98e Mon Sep 17 00:00:00 2001 From: Kevin Aleman Date: Wed, 17 Jul 2024 12:44:39 -0600 Subject: [PATCH 01/17] always send transcript --- .../app/livechat/server/lib/LivechatTyped.ts | 44 ++++---------- .../app/livechat/server/lib/localTypes.ts | 31 ++++++++++ .../server/lib/parseTranscriptRequest.ts | 59 +++++++++++++++++++ apps/meteor/server/settings/omnichannel.ts | 13 +++- 4 files changed, 113 insertions(+), 34 deletions(-) create mode 100644 apps/meteor/app/livechat/server/lib/localTypes.ts create mode 100644 apps/meteor/app/livechat/server/lib/parseTranscriptRequest.ts diff --git a/apps/meteor/app/livechat/server/lib/LivechatTyped.ts b/apps/meteor/app/livechat/server/lib/LivechatTyped.ts index bb56eb81ceb31..12826e72e75a5 100644 --- a/apps/meteor/app/livechat/server/lib/LivechatTyped.ts +++ b/apps/meteor/app/livechat/server/lib/LivechatTyped.ts @@ -74,6 +74,8 @@ import { parseAgentCustomFields, updateDepartmentAgents, validateEmail, normaliz import { QueueManager } from './QueueManager'; import { RoutingManager } from './RoutingManager'; import { isDepartmentCreationAvailable } from './isDepartmentCreationAvailable'; +import { parseTranscriptRequest } from './parseTranscriptRequest'; +import type { CloseRoomParams, CloseRoomParamsByUser, CloseRoomParamsByVisitor } from './localTypes' type RegisterGuestType = Partial> & { id?: string; @@ -82,36 +84,6 @@ type RegisterGuestType = Partial; - }; - pdfTranscript?: { - requestedBy: string; - }; - }; -}; - -export type CloseRoomParamsByUser = { - user: IUser | null; -} & GenericCloseRoomParams; - -export type CloseRoomParamsByVisitor = { - visitor: ILivechatVisitor; -} & GenericCloseRoomParams; - -export type CloseRoomParams = CloseRoomParamsByUser | CloseRoomParamsByVisitor; - type OfflineMessageData = { message: string; name: string; @@ -326,11 +298,12 @@ class LivechatClass { this.logger.debug(`DB updated for room ${room._id}`); + const transcriptRequested = !!transcriptRequest || (!settings.get('Livechat_enable_transcript') && settings.get('Livechat_transcript_send_always')); const message = { t: 'livechat-close', msg: comment, groupable: false, - transcriptRequested: !!transcriptRequest, + transcriptRequested, }; // Retrieve the closed room @@ -355,15 +328,18 @@ class LivechatClass { void Apps.self?.getBridges()?.getListenerBridge().livechatEvent(AppEvents.ILivechatRoomClosedHandler, newRoom); void Apps.self?.getBridges()?.getListenerBridge().livechatEvent(AppEvents.IPostLivechatRoomClosed, newRoom); }); + + const visitor = isRoomClosedByVisitorParams(params) ? params.visitor : undefined; + const opts = await parseTranscriptRequest(params.room, options, visitor); if (process.env.TEST_MODE) { await callbacks.run('livechat.closeRoom', { room: newRoom, - options, + options: opts, }); } else { callbacks.runAsync('livechat.closeRoom', { room: newRoom, - options, + options: opts, }); } @@ -1987,3 +1963,5 @@ class LivechatClass { } export const Livechat = new LivechatClass(); +export * from './localTypes' + diff --git a/apps/meteor/app/livechat/server/lib/localTypes.ts b/apps/meteor/app/livechat/server/lib/localTypes.ts new file mode 100644 index 0000000000000..c6acbbc5bcbd6 --- /dev/null +++ b/apps/meteor/app/livechat/server/lib/localTypes.ts @@ -0,0 +1,31 @@ +import type { IOmnichannelRoom, IUser, ILivechatVisitor } from '@rocket.chat/core-typings'; + +export type GenericCloseRoomParams = { + room: IOmnichannelRoom; + comment?: string; + options?: { + clientAction?: boolean; + tags?: string[]; + emailTranscript?: + | { + sendToVisitor: false; + } + | { + sendToVisitor: true; + requestData: NonNullable; + }; + pdfTranscript?: { + requestedBy: string; + }; + }; +}; + +export type CloseRoomParamsByUser = { + user: IUser | null; +} & GenericCloseRoomParams; + +export type CloseRoomParamsByVisitor = { + visitor: ILivechatVisitor; +} & GenericCloseRoomParams; + +export type CloseRoomParams = CloseRoomParamsByUser | CloseRoomParamsByVisitor; diff --git a/apps/meteor/app/livechat/server/lib/parseTranscriptRequest.ts b/apps/meteor/app/livechat/server/lib/parseTranscriptRequest.ts new file mode 100644 index 0000000000000..32d0e391a79bf --- /dev/null +++ b/apps/meteor/app/livechat/server/lib/parseTranscriptRequest.ts @@ -0,0 +1,59 @@ +import type { ILivechatVisitor, IOmnichannelRoom, IOmnichannelRoomClosingInfo, IUser } from '@rocket.chat/core-typings'; +import { LivechatVisitors, Users } from '@rocket.chat/models'; +import type { CloseRoomParams } from './localTypes'; + +import { settings } from '../../../settings/server'; + +export const parseTranscriptRequest = async ( + room: IOmnichannelRoom, + options: CloseRoomParams['options'], + visitor?: ILivechatVisitor, + user?: IUser, +): Promise { + const visitorDecideTranscript = settings.get('Livechat_enable_transcript'); + // visitor decides, no changes + if (visitorDecideTranscript) { + return options; + } + + // send always is disabled, no changes + const sendAlways = settings.get('Livechat_transcript_send_always'); + if (!sendAlways) { + return options; + } + + const visitorData = visitor || (await LivechatVisitors.findOneById(room.v._id)); + // no visitor, no changes + if (!visitorData) { + return options; + } + const visitorEmail = visitorData?.visitorEmails?.[0]?.address; + // visitor doesnt have email, no changes + if (!visitorEmail) { + return options; + } + + const defOptions = { projection: { _id: 1, username: 1, name: 1 } }; + const requestedBy = + user || + (room.servedBy && (await Users.findOneById(room.servedBy._id, defOptions))) || + (await Users.findOneById('rocket.cat', defOptions)); + + // no user available for backing request, no changes + if (!requestedBy) { + return options; + } + + return { + ...options, + emailTranscript: { + sendToVisitor: true, + requestData: { + email: visitorEmail, + requestedAt: new Date(), + subject: '', + requestedBy, + }, + }, + }; +}; diff --git a/apps/meteor/server/settings/omnichannel.ts b/apps/meteor/server/settings/omnichannel.ts index cc9da54748627..00af708df2fd6 100644 --- a/apps/meteor/server/settings/omnichannel.ts +++ b/apps/meteor/server/settings/omnichannel.ts @@ -404,12 +404,23 @@ export const createOmniSettings = () => enableQuery: [{ _id: 'FileUpload_Enabled', value: true }, omnichannelEnabledQuery], }); + // Making these 2 settings "depend" on each other + // Prevents us from having both as true and then asking visitor if it wants a Transcript + // But send it anyways because of send_always being enabled. So one can only be turned on + // if the other is off. await this.add('Livechat_enable_transcript', false, { type: 'boolean', group: 'Omnichannel', public: true, i18nLabel: 'Transcript_Enabled', - enableQuery: omnichannelEnabledQuery, + enableQuery: [{ _id: 'Livechat_transcript_send_always', value: false }, omnichannelEnabledQuery], + }); + + await this.add('Livechat_transcript_send_always', false, { + type: 'boolean', + group: 'Omnichannel', + public: true, + enableQuery: [{ _id: 'Livechat_enable_transcript', value: false }, omnichannelEnabledQuery], }); await this.add('Livechat_transcript_message', '', { From 8b41a863902e034f65b4e46b457e7a7cdf310a3a Mon Sep 17 00:00:00 2001 From: Kevin Aleman Date: Wed, 17 Jul 2024 13:16:46 -0600 Subject: [PATCH 02/17] update ui and dont send prompttranscript when not needed --- apps/meteor/app/livechat/server/lib/LivechatTyped.ts | 4 +++- .../client/components/Omnichannel/modals/CloseChatModal.tsx | 5 +++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/apps/meteor/app/livechat/server/lib/LivechatTyped.ts b/apps/meteor/app/livechat/server/lib/LivechatTyped.ts index 12826e72e75a5..485a1347c3ab7 100644 --- a/apps/meteor/app/livechat/server/lib/LivechatTyped.ts +++ b/apps/meteor/app/livechat/server/lib/LivechatTyped.ts @@ -316,7 +316,9 @@ class LivechatClass { this.logger.debug(`Sending closing message to room ${room._id}`); await sendMessage(chatCloser, message, newRoom); - await Message.saveSystemMessage('command', rid, 'promptTranscript', closeData.closedBy); + if (settings.get('Livechat_enable_transcript') && !settings.get('Livechat_transcript_send_always')) { + await Message.saveSystemMessage('command', rid, 'promptTranscript', closeData.closedBy); + } this.logger.debug(`Running callbacks for room ${newRoom._id}`); diff --git a/apps/meteor/client/components/Omnichannel/modals/CloseChatModal.tsx b/apps/meteor/client/components/Omnichannel/modals/CloseChatModal.tsx index 401448ceb3966..af31c19553be6 100644 --- a/apps/meteor/client/components/Omnichannel/modals/CloseChatModal.tsx +++ b/apps/meteor/client/components/Omnichannel/modals/CloseChatModal.tsx @@ -52,6 +52,7 @@ const CloseChatModal = ({ } = useForm(); const commentRequired = useSetting('Livechat_request_comment_when_closing_conversation') as boolean; + const alwaysSendTranscript = useSetting('Livechat_transcript_send_always'); const [tagRequired, setTagRequired] = useState(false); const tags = watch('tags'); @@ -65,7 +66,7 @@ const CloseChatModal = ({ const transcriptPDFPermission = usePermission('request-pdf-transcript'); const transcriptEmailPermission = usePermission('send-omnichannel-chat-transcript'); - const canSendTranscriptEmail = transcriptEmailPermission && visitorEmail; + const canSendTranscriptEmail = transcriptEmailPermission && visitorEmail && !alwaysSendTranscript; const canSendTranscriptPDF = transcriptPDFPermission && hasLicense; const canSendTranscript = canSendTranscriptEmail || canSendTranscriptPDF; @@ -77,7 +78,7 @@ const CloseChatModal = ({ ({ comment, tags, transcriptPDF, transcriptEmail, subject }): void => { const preferences = { omnichannelTranscriptPDF: !!transcriptPDF, - omnichannelTranscriptEmail: !!transcriptEmail, + omnichannelTranscriptEmail: alwaysSendTranscript ? true : !!transcriptEmail, }; const requestData = transcriptEmail && visitorEmail ? { email: visitorEmail, subject } : undefined; From 7cdef49d89d67f56b80e8f124afa6a3074e2b4e1 Mon Sep 17 00:00:00 2001 From: Kevin Aleman Date: Wed, 17 Jul 2024 13:22:40 -0600 Subject: [PATCH 03/17] lint --- .../app/livechat/server/lib/parseTranscriptRequest.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/meteor/app/livechat/server/lib/parseTranscriptRequest.ts b/apps/meteor/app/livechat/server/lib/parseTranscriptRequest.ts index 32d0e391a79bf..070047e338593 100644 --- a/apps/meteor/app/livechat/server/lib/parseTranscriptRequest.ts +++ b/apps/meteor/app/livechat/server/lib/parseTranscriptRequest.ts @@ -1,15 +1,15 @@ -import type { ILivechatVisitor, IOmnichannelRoom, IOmnichannelRoomClosingInfo, IUser } from '@rocket.chat/core-typings'; +import type { ILivechatVisitor, IOmnichannelRoom, IUser } from '@rocket.chat/core-typings'; import { LivechatVisitors, Users } from '@rocket.chat/models'; -import type { CloseRoomParams } from './localTypes'; import { settings } from '../../../settings/server'; +import type { CloseRoomParams } from './localTypes'; export const parseTranscriptRequest = async ( room: IOmnichannelRoom, options: CloseRoomParams['options'], visitor?: ILivechatVisitor, user?: IUser, -): Promise { +): Promise => { const visitorDecideTranscript = settings.get('Livechat_enable_transcript'); // visitor decides, no changes if (visitorDecideTranscript) { From e11be8cbac111b036054573ac07b861d15ffb824 Mon Sep 17 00:00:00 2001 From: Kevin Aleman Date: Wed, 17 Jul 2024 13:46:58 -0600 Subject: [PATCH 04/17] lint --- apps/meteor/app/livechat/server/lib/LivechatTyped.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/meteor/app/livechat/server/lib/LivechatTyped.ts b/apps/meteor/app/livechat/server/lib/LivechatTyped.ts index 485a1347c3ab7..390a694ad7c1d 100644 --- a/apps/meteor/app/livechat/server/lib/LivechatTyped.ts +++ b/apps/meteor/app/livechat/server/lib/LivechatTyped.ts @@ -74,8 +74,8 @@ import { parseAgentCustomFields, updateDepartmentAgents, validateEmail, normaliz import { QueueManager } from './QueueManager'; import { RoutingManager } from './RoutingManager'; import { isDepartmentCreationAvailable } from './isDepartmentCreationAvailable'; +import type { CloseRoomParams, CloseRoomParamsByUser, CloseRoomParamsByVisitor } from './localTypes'; import { parseTranscriptRequest } from './parseTranscriptRequest'; -import type { CloseRoomParams, CloseRoomParamsByUser, CloseRoomParamsByVisitor } from './localTypes' type RegisterGuestType = Partial> & { id?: string; @@ -298,7 +298,8 @@ class LivechatClass { this.logger.debug(`DB updated for room ${room._id}`); - const transcriptRequested = !!transcriptRequest || (!settings.get('Livechat_enable_transcript') && settings.get('Livechat_transcript_send_always')); + const transcriptRequested = + !!transcriptRequest || (!settings.get('Livechat_enable_transcript') && settings.get('Livechat_transcript_send_always')); const message = { t: 'livechat-close', msg: comment, @@ -1965,5 +1966,4 @@ class LivechatClass { } export const Livechat = new LivechatClass(); -export * from './localTypes' - +export * from './localTypes'; From 07f09e115bb51019e949713fd7b1f8a41b9533fc Mon Sep 17 00:00:00 2001 From: Kevin Aleman Date: Wed, 17 Jul 2024 15:01:01 -0600 Subject: [PATCH 05/17] test --- .../server/lib/parseTranscriptRequest.spec.ts | 124 ++++++++++++++++++ 1 file changed, 124 insertions(+) create mode 100644 apps/meteor/tests/unit/app/livechat/server/lib/parseTranscriptRequest.spec.ts diff --git a/apps/meteor/tests/unit/app/livechat/server/lib/parseTranscriptRequest.spec.ts b/apps/meteor/tests/unit/app/livechat/server/lib/parseTranscriptRequest.spec.ts new file mode 100644 index 0000000000000..235c9ea700091 --- /dev/null +++ b/apps/meteor/tests/unit/app/livechat/server/lib/parseTranscriptRequest.spec.ts @@ -0,0 +1,124 @@ +import { expect } from 'chai'; +import p from 'proxyquire'; +import sinon from 'sinon'; + +const modelsMock = { + Users: { + findOneById: sinon.stub(), + }, + LivechatVisitors: { + findOneById: sinon.stub(), + }, +}; + +const settingsGetMock = { + get: sinon.stub(), +}; + +const { parseTranscriptRequest } = p.noCallThru().load('../../../../../../app/livechat/server/lib/parseTranscriptRequest', { + '@rocket.chat/models': modelsMock, + '../../../settings/server': { settings: settingsGetMock }, +}); + +describe('parseTranscriptRequest', () => { + beforeEach(() => { + settingsGetMock.get.reset(); + modelsMock.Users.findOneById.reset(); + modelsMock.LivechatVisitors.findOneById.reset(); + }); + + it('should return options when Livechat_enable_transcript setting is true', async () => { + settingsGetMock.get.withArgs('Livechat_enable_transcript').returns(true); + const options = await parseTranscriptRequest({} as any, {} as any); + expect(options).to.be.deep.equal({}); + }); + + it('should return options when send always is disabled', async () => { + settingsGetMock.get.withArgs('Livechat_enable_transcript').returns(false); + settingsGetMock.get.withArgs('Livechat_transcript_send_always').returns(false); + const options = await parseTranscriptRequest({} as any, {} as any); + expect(options).to.be.deep.equal({}); + }); + + it('should return options when visitor is not provided and its not found on db', async () => { + settingsGetMock.get.withArgs('Livechat_enable_transcript').returns(false); + settingsGetMock.get.withArgs('Livechat_transcript_send_always').returns(true); + modelsMock.LivechatVisitors.findOneById.returns(null); + + const options = await parseTranscriptRequest({ v: { _id: '123' } } as any, {} as any); + expect(options).to.be.deep.equal({}); + }); + + it('should return options when visitor is passed but no email is found', async () => { + settingsGetMock.get.withArgs('Livechat_enable_transcript').returns(false); + settingsGetMock.get.withArgs('Livechat_transcript_send_always').returns(true); + modelsMock.LivechatVisitors.findOneById.returns({} as any); + + const options = await parseTranscriptRequest({ v: { _id: '123' } } as any, {} as any, { _id: '123' } as any); + expect(options).to.be.deep.equal({}); + }); + + it('should return options when visitor is fetched from db, but no email is found', async () => { + settingsGetMock.get.withArgs('Livechat_enable_transcript').returns(false); + settingsGetMock.get.withArgs('Livechat_transcript_send_always').returns(true); + modelsMock.LivechatVisitors.findOneById.returns({} as any); + + const options = await parseTranscriptRequest({ v: { _id: '123' } } as any, {} as any); + expect(options).to.be.deep.equal({}); + }); + + it('should return options when no user is passed, room is not being served and rocketcat is not present', async () => { + settingsGetMock.get.withArgs('Livechat_enable_transcript').returns(false); + settingsGetMock.get.withArgs('Livechat_transcript_send_always').returns(true); + modelsMock.Users.findOneById.returns(null); + modelsMock.LivechatVisitors.findOneById.returns({ visitorEmails: [{ address: 'abc@rocket.chat' }] } as any); + + const options = await parseTranscriptRequest({ v: { _id: '123' } } as any, {} as any); + expect(options).to.be.deep.equal({}); + }); + + it('should return options with transcriptRequest data when user is passed', async () => { + settingsGetMock.get.withArgs('Livechat_enable_transcript').returns(false); + settingsGetMock.get.withArgs('Livechat_transcript_send_always').returns(true); + modelsMock.LivechatVisitors.findOneById.returns({ visitorEmails: [{ address: 'abc@rocket.chat' }] } as any); + + const options = await parseTranscriptRequest({ v: { _id: '123' } } as any, {} as any, undefined, { _id: '123' } as any); + + expect(options).to.have.property('emailTranscript').that.is.an('object'); + expect(options.emailTranscript.requestData).to.have.property('email', 'abc@rocket.chat'); + expect(options.emailTranscript.requestData).to.have.property('subject', ''); + expect(options.emailTranscript.requestData.requestedBy).to.be.deep.equal({ _id: '123' }); + }); + + it('should return options with transcriptData when no user is passed, but theres an agent serving the room', async () => { + settingsGetMock.get.withArgs('Livechat_enable_transcript').returns(false); + settingsGetMock.get.withArgs('Livechat_transcript_send_always').returns(true); + modelsMock.Users.findOneById.returns({ _id: '123', username: 'kevsxxx', name: 'Kev' } as any); + modelsMock.LivechatVisitors.findOneById.returns({ visitorEmails: [{ address: 'abc@rocket.chat' }] } as any); + + const options = await parseTranscriptRequest({ v: { _id: '123' } } as any, {} as any); + + expect(options).to.have.property('emailTranscript').that.is.an('object'); + expect(options.emailTranscript.requestData).to.have.property('email', 'abc@rocket.chat'); + expect(options.emailTranscript.requestData).to.have.property('subject', ''); + expect(options.emailTranscript.requestData.requestedBy).to.be.deep.equal({ _id: '123', username: 'kevsxxx', name: 'Kev' }); + }); + + it('should return options with transcriptData when no user is passed, no agent is serving but rocket.cat is present', async () => { + settingsGetMock.get.withArgs('Livechat_enable_transcript').returns(false); + settingsGetMock.get.withArgs('Livechat_transcript_send_always').returns(true); + modelsMock.Users.findOneById.returns({ _id: 'rocket.cat', username: 'rocket.cat', name: 'Rocket Cat' } as any); + modelsMock.LivechatVisitors.findOneById.returns({ visitorEmails: [{ address: 'abc@rocket.chat' }] } as any); + + const options = await parseTranscriptRequest({ v: { _id: '123' } } as any, {} as any); + + expect(options).to.have.property('emailTranscript').that.is.an('object'); + expect(options.emailTranscript.requestData).to.have.property('email', 'abc@rocket.chat'); + expect(options.emailTranscript.requestData).to.have.property('subject', ''); + expect(options.emailTranscript.requestData.requestedBy).to.be.deep.equal({ + _id: 'rocket.cat', + username: 'rocket.cat', + name: 'Rocket Cat', + }); + }); +}); From 525052f36e9434619de07470e3b53be5a97c9fb6 Mon Sep 17 00:00:00 2001 From: Kevin Aleman Date: Wed, 17 Jul 2024 15:09:33 -0600 Subject: [PATCH 06/17] Create shaggy-hats-raise.md --- .changeset/shaggy-hats-raise.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/shaggy-hats-raise.md diff --git a/.changeset/shaggy-hats-raise.md b/.changeset/shaggy-hats-raise.md new file mode 100644 index 0000000000000..b6062fd9e41c7 --- /dev/null +++ b/.changeset/shaggy-hats-raise.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": patch +--- + +Added a new setting that allows admins to decide if email transcript should be sent all the times when a conversation is closed. This setting bypasses agent's preferences. For this setting to work, `Livechat_enable_transcript` should be off, meaning that visitors will no longer receive the option to decide if they want a transcript or not. From 628c93f3d39e23bf57ddc217006e041bc3b8bc3d Mon Sep 17 00:00:00 2001 From: Kevin Aleman Date: Wed, 17 Jul 2024 15:10:36 -0600 Subject: [PATCH 07/17] Update shaggy-hats-raise.md --- .changeset/shaggy-hats-raise.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/shaggy-hats-raise.md b/.changeset/shaggy-hats-raise.md index b6062fd9e41c7..884ee836fd513 100644 --- a/.changeset/shaggy-hats-raise.md +++ b/.changeset/shaggy-hats-raise.md @@ -2,4 +2,4 @@ "@rocket.chat/meteor": patch --- -Added a new setting that allows admins to decide if email transcript should be sent all the times when a conversation is closed. This setting bypasses agent's preferences. For this setting to work, `Livechat_enable_transcript` should be off, meaning that visitors will no longer receive the option to decide if they want a transcript or not. +Added a new setting `Livechat_transcript_send_always` that allows admins to decide if email transcript should be sent all the times when a conversation is closed. This setting bypasses agent's preferences. For this setting to work, `Livechat_enable_transcript` should be off, meaning that visitors will no longer receive the option to decide if they want a transcript or not. From 34882a13a17f1fb359735349275c7d534654b083 Mon Sep 17 00:00:00 2001 From: Kevin Aleman Date: Wed, 17 Jul 2024 15:13:01 -0600 Subject: [PATCH 08/17] translations --- packages/i18n/src/locales/en.i18n.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/i18n/src/locales/en.i18n.json b/packages/i18n/src/locales/en.i18n.json index f9fcf0163cafb..d1dffd0791d17 100644 --- a/packages/i18n/src/locales/en.i18n.json +++ b/packages/i18n/src/locales/en.i18n.json @@ -3267,6 +3267,8 @@ "Livechat_email_transcript_has_been_requested": "The transcript has been requested. It may take a few seconds.", "Livechat_transcript_request_has_been_canceled": "The chat transcription request has been canceled.", "Livechat_transcript_sent": "Omnichannel transcript sent", + "Livechat_transcript_send_always": "Always send email transcript to visitors", + "Livechat_transcript_send_always_Description": "Send email transcript to visitors automatically without asking visitor. This setting bypasses agent's preferences regarding email transcripts.", "Livechat_transfer_return_to_the_queue": "{{from}} returned the chat to the queue", "Livechat_transfer_return_to_the_queue_with_a_comment": "{{from}} returned the chat to the queue with a comment: {{comment}}", "Livechat_transfer_return_to_the_queue_auto_transfer_unanswered_chat": "{{from}} returned the chat to the queue since it was unanswered for {{duration}} seconds", From c34dc7cefbf37d9b833df23699abff3b40aaa327 Mon Sep 17 00:00:00 2001 From: Kevin Aleman Date: Thu, 18 Jul 2024 11:24:53 -0600 Subject: [PATCH 09/17] Update parseTranscriptRequest.spec.ts --- .../server/lib/parseTranscriptRequest.spec.ts | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/apps/meteor/tests/unit/app/livechat/server/lib/parseTranscriptRequest.spec.ts b/apps/meteor/tests/unit/app/livechat/server/lib/parseTranscriptRequest.spec.ts index 235c9ea700091..1f07dccfc7723 100644 --- a/apps/meteor/tests/unit/app/livechat/server/lib/parseTranscriptRequest.spec.ts +++ b/apps/meteor/tests/unit/app/livechat/server/lib/parseTranscriptRequest.spec.ts @@ -27,20 +27,20 @@ describe('parseTranscriptRequest', () => { modelsMock.LivechatVisitors.findOneById.reset(); }); - it('should return options when Livechat_enable_transcript setting is true', async () => { + it('should return `options` param with no changes when Livechat_enable_transcript setting is true', async () => { settingsGetMock.get.withArgs('Livechat_enable_transcript').returns(true); const options = await parseTranscriptRequest({} as any, {} as any); expect(options).to.be.deep.equal({}); }); - it('should return options when send always is disabled', async () => { + it('should return `options` param with no changes when send always is disabled', async () => { settingsGetMock.get.withArgs('Livechat_enable_transcript').returns(false); settingsGetMock.get.withArgs('Livechat_transcript_send_always').returns(false); const options = await parseTranscriptRequest({} as any, {} as any); expect(options).to.be.deep.equal({}); }); - it('should return options when visitor is not provided and its not found on db', async () => { + it('should return `options` param with no changes when visitor is not provided and its not found on db', async () => { settingsGetMock.get.withArgs('Livechat_enable_transcript').returns(false); settingsGetMock.get.withArgs('Livechat_transcript_send_always').returns(true); modelsMock.LivechatVisitors.findOneById.returns(null); @@ -49,7 +49,7 @@ describe('parseTranscriptRequest', () => { expect(options).to.be.deep.equal({}); }); - it('should return options when visitor is passed but no email is found', async () => { + it('should return `options` param with no changes when visitor is passed but no email is found', async () => { settingsGetMock.get.withArgs('Livechat_enable_transcript').returns(false); settingsGetMock.get.withArgs('Livechat_transcript_send_always').returns(true); modelsMock.LivechatVisitors.findOneById.returns({} as any); @@ -58,7 +58,7 @@ describe('parseTranscriptRequest', () => { expect(options).to.be.deep.equal({}); }); - it('should return options when visitor is fetched from db, but no email is found', async () => { + it('should return `options` param with no changes when visitor is fetched from db, but no email is found', async () => { settingsGetMock.get.withArgs('Livechat_enable_transcript').returns(false); settingsGetMock.get.withArgs('Livechat_transcript_send_always').returns(true); modelsMock.LivechatVisitors.findOneById.returns({} as any); @@ -67,7 +67,7 @@ describe('parseTranscriptRequest', () => { expect(options).to.be.deep.equal({}); }); - it('should return options when no user is passed, room is not being served and rocketcat is not present', async () => { + it('should return `options` param with no changes when no user is passed, room is not being served and rocketcat is not present', async () => { settingsGetMock.get.withArgs('Livechat_enable_transcript').returns(false); settingsGetMock.get.withArgs('Livechat_transcript_send_always').returns(true); modelsMock.Users.findOneById.returns(null); @@ -77,7 +77,7 @@ describe('parseTranscriptRequest', () => { expect(options).to.be.deep.equal({}); }); - it('should return options with transcriptRequest data when user is passed', async () => { + it('should return `options` param with `transcriptRequest` key attached when user is passed', async () => { settingsGetMock.get.withArgs('Livechat_enable_transcript').returns(false); settingsGetMock.get.withArgs('Livechat_transcript_send_always').returns(true); modelsMock.LivechatVisitors.findOneById.returns({ visitorEmails: [{ address: 'abc@rocket.chat' }] } as any); @@ -90,7 +90,7 @@ describe('parseTranscriptRequest', () => { expect(options.emailTranscript.requestData.requestedBy).to.be.deep.equal({ _id: '123' }); }); - it('should return options with transcriptData when no user is passed, but theres an agent serving the room', async () => { + it('should return `options` param with `transcriptRequest` key attached when no user is passed, but theres an agent serving the room', async () => { settingsGetMock.get.withArgs('Livechat_enable_transcript').returns(false); settingsGetMock.get.withArgs('Livechat_transcript_send_always').returns(true); modelsMock.Users.findOneById.returns({ _id: '123', username: 'kevsxxx', name: 'Kev' } as any); @@ -104,7 +104,7 @@ describe('parseTranscriptRequest', () => { expect(options.emailTranscript.requestData.requestedBy).to.be.deep.equal({ _id: '123', username: 'kevsxxx', name: 'Kev' }); }); - it('should return options with transcriptData when no user is passed, no agent is serving but rocket.cat is present', async () => { + it('should return `options` param with `transcriptRequest` key attached when no user is passed, no agent is serving but rocket.cat is present', async () => { settingsGetMock.get.withArgs('Livechat_enable_transcript').returns(false); settingsGetMock.get.withArgs('Livechat_transcript_send_always').returns(true); modelsMock.Users.findOneById.returns({ _id: 'rocket.cat', username: 'rocket.cat', name: 'Rocket Cat' } as any); From 9a196ebc8f2968d1a0eb7808bb533d91f545408d Mon Sep 17 00:00:00 2001 From: Kevin Aleman Date: Thu, 18 Jul 2024 11:25:05 -0600 Subject: [PATCH 10/17] Update .changeset/shaggy-hats-raise.md Co-authored-by: Marcos Spessatto Defendi --- .changeset/shaggy-hats-raise.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/shaggy-hats-raise.md b/.changeset/shaggy-hats-raise.md index 884ee836fd513..40ee9f8fbb55a 100644 --- a/.changeset/shaggy-hats-raise.md +++ b/.changeset/shaggy-hats-raise.md @@ -1,5 +1,5 @@ --- -"@rocket.chat/meteor": patch +"@rocket.chat/meteor": minor --- Added a new setting `Livechat_transcript_send_always` that allows admins to decide if email transcript should be sent all the times when a conversation is closed. This setting bypasses agent's preferences. For this setting to work, `Livechat_enable_transcript` should be off, meaning that visitors will no longer receive the option to decide if they want a transcript or not. From 4cff36cda1f9b4f706313cf29929d03b5e5384e2 Mon Sep 17 00:00:00 2001 From: Kevin Aleman Date: Mon, 22 Jul 2024 14:29:42 -0600 Subject: [PATCH 11/17] Update packages/i18n/src/locales/en.i18n.json --- 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 eb1678565f920..a13ec4e6b73cb 100644 --- a/packages/i18n/src/locales/en.i18n.json +++ b/packages/i18n/src/locales/en.i18n.json @@ -3270,8 +3270,8 @@ "Livechat_email_transcript_has_been_requested": "The transcript has been requested. It may take a few seconds.", "Livechat_transcript_request_has_been_canceled": "The chat transcription request has been canceled.", "Livechat_transcript_sent": "Omnichannel transcript sent", - "Livechat_transcript_send_always": "Always send email transcript to visitors", - "Livechat_transcript_send_always_Description": "Send email transcript to visitors automatically without asking visitor. This setting bypasses agent's preferences regarding email transcripts.", + "Livechat_transcript_send_always": "Always send conversation transcript to visitors via email", + "Livechat_transcript_send_always_Description": "Once finished, send conversation transcript via email to visitors automatically, regardless of agent's preferences.", "Livechat_transfer_return_to_the_queue": "{{from}} returned the chat to the queue", "Livechat_transfer_return_to_the_queue_with_a_comment": "{{from}} returned the chat to the queue with a comment: {{comment}}", "Livechat_transfer_return_to_the_queue_auto_transfer_unanswered_chat": "{{from}} returned the chat to the queue since it was unanswered for {{duration}} seconds", From 70a52a590b36b9bbed57ba0a5ec793cda286a28f Mon Sep 17 00:00:00 2001 From: Kevin Aleman Date: Tue, 23 Jul 2024 09:10:37 -0600 Subject: [PATCH 12/17] cr & qa suggestions --- .../server/lib/parseTranscriptRequest.ts | 4 +++- .../PreferencesConversationTranscript.tsx | 22 ++++++++++++++----- .../server/lib/parseTranscriptRequest.spec.ts | 20 ++++++++--------- packages/i18n/src/locales/en.i18n.json | 2 +- 4 files changed, 31 insertions(+), 17 deletions(-) diff --git a/apps/meteor/app/livechat/server/lib/parseTranscriptRequest.ts b/apps/meteor/app/livechat/server/lib/parseTranscriptRequest.ts index 070047e338593..bfd43e7c353f5 100644 --- a/apps/meteor/app/livechat/server/lib/parseTranscriptRequest.ts +++ b/apps/meteor/app/livechat/server/lib/parseTranscriptRequest.ts @@ -22,7 +22,9 @@ export const parseTranscriptRequest = async ( return options; } - const visitorData = visitor || (await LivechatVisitors.findOneById(room.v._id)); + const visitorData = + visitor || + (await LivechatVisitors.findOneById>((room.v._id, { projection: { visitorEmails: 1 } }))); // no visitor, no changes if (!visitorData) { return options; diff --git a/apps/meteor/client/views/account/omnichannel/PreferencesConversationTranscript.tsx b/apps/meteor/client/views/account/omnichannel/PreferencesConversationTranscript.tsx index 11bf6634a0e9d..0e2e836b2f88b 100644 --- a/apps/meteor/client/views/account/omnichannel/PreferencesConversationTranscript.tsx +++ b/apps/meteor/client/views/account/omnichannel/PreferencesConversationTranscript.tsx @@ -1,7 +1,7 @@ import { Accordion, Box, Field, FieldGroup, FieldLabel, FieldRow, FieldHint, Tag, ToggleSwitch } from '@rocket.chat/fuselage'; import { useUniqueId } from '@rocket.chat/fuselage-hooks'; -import { useTranslation, usePermission } from '@rocket.chat/ui-contexts'; -import React from 'react'; +import { useTranslation, usePermission, useSetting } from '@rocket.chat/ui-contexts'; +import React, { useEffect } from 'react'; import { useFormContext } from 'react-hook-form'; import { useHasLicenseModule } from '../../../hooks/useHasLicenseModule'; @@ -9,16 +9,28 @@ import { useHasLicenseModule } from '../../../hooks/useHasLicenseModule'; const PreferencesConversationTranscript = () => { const t = useTranslation(); - const { register } = useFormContext(); + const { register, setValue, watch } = useFormContext(); const hasLicense = useHasLicenseModule('livechat-enterprise'); + const alwaysSendEmailTranscript = useSetting('Livechat_transcript_send_always'); const canSendTranscriptPDF = usePermission('request-pdf-transcript'); - const canSendTranscriptEmail = usePermission('send-omnichannel-chat-transcript'); + const canSendTranscriptEmailPermission = usePermission('send-omnichannel-chat-transcript'); + const canSendTranscriptEmail = canSendTranscriptEmailPermission && !alwaysSendEmailTranscript; + const canSendTranscriptEmailPref = watch('omnichannelTranscriptEmail'); const cantSendTranscriptPDF = !canSendTranscriptPDF || !hasLicense; const omnichannelTranscriptPDF = useUniqueId(); const omnichannelTranscriptEmail = useUniqueId(); + useEffect(() => { + if (alwaysSendEmailTranscript && !canSendTranscriptEmailPref) { + setValue('omnichannelTranscriptEmail', true); + return; + } + + setValue('omnichannelTranscriptEmail', canSendTranscriptEmailPref); + }, [setValue, alwaysSendEmailTranscript, canSendTranscriptEmailPref]); + return ( @@ -42,7 +54,7 @@ const PreferencesConversationTranscript = () => { {t('Omnichannel_transcript_email')} - {!canSendTranscriptEmail && ( + {!canSendTranscriptEmailPermission && ( {t('No_permission')} diff --git a/apps/meteor/tests/unit/app/livechat/server/lib/parseTranscriptRequest.spec.ts b/apps/meteor/tests/unit/app/livechat/server/lib/parseTranscriptRequest.spec.ts index 1f07dccfc7723..0bbafc1113bb7 100644 --- a/apps/meteor/tests/unit/app/livechat/server/lib/parseTranscriptRequest.spec.ts +++ b/apps/meteor/tests/unit/app/livechat/server/lib/parseTranscriptRequest.spec.ts @@ -43,7 +43,7 @@ describe('parseTranscriptRequest', () => { it('should return `options` param with no changes when visitor is not provided and its not found on db', async () => { settingsGetMock.get.withArgs('Livechat_enable_transcript').returns(false); settingsGetMock.get.withArgs('Livechat_transcript_send_always').returns(true); - modelsMock.LivechatVisitors.findOneById.returns(null); + modelsMock.LivechatVisitors.findOneById.resolves(null); const options = await parseTranscriptRequest({ v: { _id: '123' } } as any, {} as any); expect(options).to.be.deep.equal({}); @@ -52,7 +52,7 @@ describe('parseTranscriptRequest', () => { it('should return `options` param with no changes when visitor is passed but no email is found', async () => { settingsGetMock.get.withArgs('Livechat_enable_transcript').returns(false); settingsGetMock.get.withArgs('Livechat_transcript_send_always').returns(true); - modelsMock.LivechatVisitors.findOneById.returns({} as any); + modelsMock.LivechatVisitors.findOneById.resolves({} as any); const options = await parseTranscriptRequest({ v: { _id: '123' } } as any, {} as any, { _id: '123' } as any); expect(options).to.be.deep.equal({}); @@ -61,7 +61,7 @@ describe('parseTranscriptRequest', () => { it('should return `options` param with no changes when visitor is fetched from db, but no email is found', async () => { settingsGetMock.get.withArgs('Livechat_enable_transcript').returns(false); settingsGetMock.get.withArgs('Livechat_transcript_send_always').returns(true); - modelsMock.LivechatVisitors.findOneById.returns({} as any); + modelsMock.LivechatVisitors.findOneById.resolves({} as any); const options = await parseTranscriptRequest({ v: { _id: '123' } } as any, {} as any); expect(options).to.be.deep.equal({}); @@ -70,8 +70,8 @@ describe('parseTranscriptRequest', () => { it('should return `options` param with no changes when no user is passed, room is not being served and rocketcat is not present', async () => { settingsGetMock.get.withArgs('Livechat_enable_transcript').returns(false); settingsGetMock.get.withArgs('Livechat_transcript_send_always').returns(true); - modelsMock.Users.findOneById.returns(null); - modelsMock.LivechatVisitors.findOneById.returns({ visitorEmails: [{ address: 'abc@rocket.chat' }] } as any); + modelsMock.Users.findOneById.resolves(null); + modelsMock.LivechatVisitors.findOneById.resolves({ visitorEmails: [{ address: 'abc@rocket.chat' }] } as any); const options = await parseTranscriptRequest({ v: { _id: '123' } } as any, {} as any); expect(options).to.be.deep.equal({}); @@ -80,7 +80,7 @@ describe('parseTranscriptRequest', () => { it('should return `options` param with `transcriptRequest` key attached when user is passed', async () => { settingsGetMock.get.withArgs('Livechat_enable_transcript').returns(false); settingsGetMock.get.withArgs('Livechat_transcript_send_always').returns(true); - modelsMock.LivechatVisitors.findOneById.returns({ visitorEmails: [{ address: 'abc@rocket.chat' }] } as any); + modelsMock.LivechatVisitors.findOneById.resolves({ visitorEmails: [{ address: 'abc@rocket.chat' }] } as any); const options = await parseTranscriptRequest({ v: { _id: '123' } } as any, {} as any, undefined, { _id: '123' } as any); @@ -93,8 +93,8 @@ describe('parseTranscriptRequest', () => { it('should return `options` param with `transcriptRequest` key attached when no user is passed, but theres an agent serving the room', async () => { settingsGetMock.get.withArgs('Livechat_enable_transcript').returns(false); settingsGetMock.get.withArgs('Livechat_transcript_send_always').returns(true); - modelsMock.Users.findOneById.returns({ _id: '123', username: 'kevsxxx', name: 'Kev' } as any); - modelsMock.LivechatVisitors.findOneById.returns({ visitorEmails: [{ address: 'abc@rocket.chat' }] } as any); + modelsMock.Users.findOneById.resolves({ _id: '123', username: 'kevsxxx', name: 'Kev' } as any); + modelsMock.LivechatVisitors.findOneById.resolves({ visitorEmails: [{ address: 'abc@rocket.chat' }] } as any); const options = await parseTranscriptRequest({ v: { _id: '123' } } as any, {} as any); @@ -107,8 +107,8 @@ describe('parseTranscriptRequest', () => { it('should return `options` param with `transcriptRequest` key attached when no user is passed, no agent is serving but rocket.cat is present', async () => { settingsGetMock.get.withArgs('Livechat_enable_transcript').returns(false); settingsGetMock.get.withArgs('Livechat_transcript_send_always').returns(true); - modelsMock.Users.findOneById.returns({ _id: 'rocket.cat', username: 'rocket.cat', name: 'Rocket Cat' } as any); - modelsMock.LivechatVisitors.findOneById.returns({ visitorEmails: [{ address: 'abc@rocket.chat' }] } as any); + modelsMock.Users.findOneById.resolves({ _id: 'rocket.cat', username: 'rocket.cat', name: 'Rocket Cat' } as any); + modelsMock.LivechatVisitors.findOneById.resolves({ visitorEmails: [{ address: 'abc@rocket.chat' }] } as any); const options = await parseTranscriptRequest({ v: { _id: '123' } } as any, {} as any); diff --git a/packages/i18n/src/locales/en.i18n.json b/packages/i18n/src/locales/en.i18n.json index a13ec4e6b73cb..59e63f2bb5a61 100644 --- a/packages/i18n/src/locales/en.i18n.json +++ b/packages/i18n/src/locales/en.i18n.json @@ -6267,7 +6267,7 @@ "Always_send_the_transcript_to_contacts_at_the_end_of_the_conversations": "Always send the transcript to contacts at the end of the conversations.", "Export_conversation_transcript_as_PDF": "Export conversation transcript as PDF", "Omnichannel_transcript_email": "Send chat transcript via email.", - "Accounts_Default_User_Preferences_omnichannelTranscriptEmail_Description": "Always send the transcript to contacts at the end of the conversations.", + "Accounts_Default_User_Preferences_omnichannelTranscriptEmail_Description": "Always send the transcript to contacts at the end of the conversations. This preference may be overriden by an admin setting.", "Omnichannel_transcript_pdf": "Export chat transcript as PDF.", "Accounts_Default_User_Preferences_omnichannelTranscriptPDF_Description": "Always export the transcript as PDF at the end of conversations.", "Contact_email": "Contact email", From 70f8928699a9e412aa236c70980d5bceb9fc11ee Mon Sep 17 00:00:00 2001 From: Kevin Aleman Date: Tue, 23 Jul 2024 09:37:57 -0600 Subject: [PATCH 13/17] jesus im dum --- apps/meteor/app/livechat/server/lib/parseTranscriptRequest.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/meteor/app/livechat/server/lib/parseTranscriptRequest.ts b/apps/meteor/app/livechat/server/lib/parseTranscriptRequest.ts index bfd43e7c353f5..76595a7ff6402 100644 --- a/apps/meteor/app/livechat/server/lib/parseTranscriptRequest.ts +++ b/apps/meteor/app/livechat/server/lib/parseTranscriptRequest.ts @@ -24,7 +24,7 @@ export const parseTranscriptRequest = async ( const visitorData = visitor || - (await LivechatVisitors.findOneById>((room.v._id, { projection: { visitorEmails: 1 } }))); + (await LivechatVisitors.findOneById>(room.v._id, { projection: { visitorEmails: 1 } })); // no visitor, no changes if (!visitorData) { return options; From 77b86daa16a6d6a09a67130d6252c7d8850662df Mon Sep 17 00:00:00 2001 From: Kevin Aleman Date: Tue, 23 Jul 2024 10:17:58 -0600 Subject: [PATCH 14/17] translations --- packages/i18n/src/locales/es.i18n.json | 2 +- packages/i18n/src/locales/fi.i18n.json | 1 - packages/i18n/src/locales/ru.i18n.json | 1 - packages/i18n/src/locales/sv.i18n.json | 1 - 4 files changed, 1 insertion(+), 4 deletions(-) diff --git a/packages/i18n/src/locales/es.i18n.json b/packages/i18n/src/locales/es.i18n.json index 6ddcf2b6479f1..c556b470a372c 100644 --- a/packages/i18n/src/locales/es.i18n.json +++ b/packages/i18n/src/locales/es.i18n.json @@ -4936,7 +4936,7 @@ "Always_send_the_transcript_to_contacts_at_the_end_of_the_conversations": "Envía siempre la transcripción a los contactos al final de las conversaciones.", "Export_conversation_transcript_as_PDF": "Exportar la transcripción de la conversación en PDF", "Omnichannel_transcript_email": "Enviar la transcripción del chat por correo electrónico.", - "Accounts_Default_User_Preferences_omnichannelTranscriptEmail_Description": "Envía siempre la transcripción a los contactos al final de las conversaciones.", + "Accounts_Default_User_Preferences_omnichannelTranscriptEmail_Description": "Envía siempre la transcripción a los contactos al final de las conversaciones. Esta preferncia puede ser sobreescrita por un administrador.", "Omnichannel_transcript_pdf": "Exporta la transcripción del chat en PDF.", "Accounts_Default_User_Preferences_omnichannelTranscriptPDF_Description": "Exporte siempre la transcripción en PDF al final de las conversaciones.", "Customer": "Cliente", diff --git a/packages/i18n/src/locales/fi.i18n.json b/packages/i18n/src/locales/fi.i18n.json index c8078bb2545f6..fcc101ca57f7d 100644 --- a/packages/i18n/src/locales/fi.i18n.json +++ b/packages/i18n/src/locales/fi.i18n.json @@ -5660,7 +5660,6 @@ "Always_send_the_transcript_to_contacts_at_the_end_of_the_conversations": "Lähetä tekstitallenne aina yhteyshenkilöille keskustelujen lopuksi.", "Export_conversation_transcript_as_PDF": "Vie keskustelun tekstitallenne PDF-muotoon", "Omnichannel_transcript_email": "Lähetä keskustelun tekstitallenne sähköpostitse.", - "Accounts_Default_User_Preferences_omnichannelTranscriptEmail_Description": "Lähetä tekstitallenne aina yhteyshenkilöille keskustelujen lopuksi.", "Omnichannel_transcript_pdf": "Vie keskustelun tekstitallenne PDF-muotoon.", "Accounts_Default_User_Preferences_omnichannelTranscriptPDF_Description": "Vie tekstitallenne aina PDF-muotoon keskustelujen lopuksi.", "Contact_email": "Yhteyshenkilön sähköpostiosoite", diff --git a/packages/i18n/src/locales/ru.i18n.json b/packages/i18n/src/locales/ru.i18n.json index f768764d0bc19..6f7b69447e29b 100644 --- a/packages/i18n/src/locales/ru.i18n.json +++ b/packages/i18n/src/locales/ru.i18n.json @@ -5073,7 +5073,6 @@ "Conversational_transcript": "Экспорт содержимого диалога", "Always_send_the_transcript_to_contacts_at_the_end_of_the_conversations": "Всегда отправлять содержимое чата контактам по окончанию диалога.", "Omnichannel_transcript_email": "Отправить содержимое диалога по электронной почте.", - "Accounts_Default_User_Preferences_omnichannelTranscriptEmail_Description": "Всегда отправлять содержимое чата контактам по окончанию диалога.", "Omnichannel_transcript_pdf": "Экспорт содержимого чата в PDF.", "Accounts_Default_User_Preferences_omnichannelTranscriptPDF_Description": "Всегда экспортировать содержимое чата по окончанию диалога.", "User_Status": "Пользовательский статус", diff --git a/packages/i18n/src/locales/sv.i18n.json b/packages/i18n/src/locales/sv.i18n.json index aafb5a36c9dc9..3436669c7e656 100644 --- a/packages/i18n/src/locales/sv.i18n.json +++ b/packages/i18n/src/locales/sv.i18n.json @@ -5664,7 +5664,6 @@ "Always_send_the_transcript_to_contacts_at_the_end_of_the_conversations": "Skicka alltid utskriften till kontakterna i slutet av samtalen.", "Export_conversation_transcript_as_PDF": "Exportera samtalsutskrift som PDF", "Omnichannel_transcript_email": "Skicka utskrift av chatten via e-post.", - "Accounts_Default_User_Preferences_omnichannelTranscriptEmail_Description": "Skicka alltid utskriften till kontakterna i slutet av samtalen.", "Omnichannel_transcript_pdf": "Exportera chattutskrift som PDF.", "Accounts_Default_User_Preferences_omnichannelTranscriptPDF_Description": "Exportera alltid utskriften som PDF-fil i slutet av samtalen.", "Contact_email": "Kontakt e-post", From afc7c5793d3593d968bce10a2f4c7d85e4fec036 Mon Sep 17 00:00:00 2001 From: Kevin Aleman Date: Tue, 23 Jul 2024 10:44:15 -0600 Subject: [PATCH 15/17] more expect --- .../app/livechat/server/lib/parseTranscriptRequest.spec.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/meteor/tests/unit/app/livechat/server/lib/parseTranscriptRequest.spec.ts b/apps/meteor/tests/unit/app/livechat/server/lib/parseTranscriptRequest.spec.ts index 0bbafc1113bb7..a1b2ce52d1a35 100644 --- a/apps/meteor/tests/unit/app/livechat/server/lib/parseTranscriptRequest.spec.ts +++ b/apps/meteor/tests/unit/app/livechat/server/lib/parseTranscriptRequest.spec.ts @@ -84,6 +84,7 @@ describe('parseTranscriptRequest', () => { const options = await parseTranscriptRequest({ v: { _id: '123' } } as any, {} as any, undefined, { _id: '123' } as any); + expect(modelsMock.LivechatVisitors.findOneById.getCall(0).firstArg).to.be.equal('123'); expect(options).to.have.property('emailTranscript').that.is.an('object'); expect(options.emailTranscript.requestData).to.have.property('email', 'abc@rocket.chat'); expect(options.emailTranscript.requestData).to.have.property('subject', ''); @@ -96,8 +97,9 @@ describe('parseTranscriptRequest', () => { modelsMock.Users.findOneById.resolves({ _id: '123', username: 'kevsxxx', name: 'Kev' } as any); modelsMock.LivechatVisitors.findOneById.resolves({ visitorEmails: [{ address: 'abc@rocket.chat' }] } as any); - const options = await parseTranscriptRequest({ v: { _id: '123' } } as any, {} as any); + const options = await parseTranscriptRequest({ v: { _id: '123' }, servedBy: { _id: '123' } } as any, {} as any); + expect(modelsMock.Users.findOneById.getCall(0).firstArg).to.be.equal('123'); expect(options).to.have.property('emailTranscript').that.is.an('object'); expect(options.emailTranscript.requestData).to.have.property('email', 'abc@rocket.chat'); expect(options.emailTranscript.requestData).to.have.property('subject', ''); @@ -112,6 +114,7 @@ describe('parseTranscriptRequest', () => { const options = await parseTranscriptRequest({ v: { _id: '123' } } as any, {} as any); + expect(modelsMock.Users.findOneById.getCall(0).firstArg).to.be.equal('rocket.cat'); expect(options).to.have.property('emailTranscript').that.is.an('object'); expect(options.emailTranscript.requestData).to.have.property('email', 'abc@rocket.chat'); expect(options.emailTranscript.requestData).to.have.property('subject', ''); From 0b0ebb67ca1f13a9b4b85950a7a2ff7758a3522b Mon Sep 17 00:00:00 2001 From: Kevin Aleman Date: Tue, 23 Jul 2024 13:19:33 -0600 Subject: [PATCH 16/17] effect --- .../PreferencesConversationTranscript.tsx | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/apps/meteor/client/views/account/omnichannel/PreferencesConversationTranscript.tsx b/apps/meteor/client/views/account/omnichannel/PreferencesConversationTranscript.tsx index 0e2e836b2f88b..f5d018ba40798 100644 --- a/apps/meteor/client/views/account/omnichannel/PreferencesConversationTranscript.tsx +++ b/apps/meteor/client/views/account/omnichannel/PreferencesConversationTranscript.tsx @@ -1,7 +1,7 @@ import { Accordion, Box, Field, FieldGroup, FieldLabel, FieldRow, FieldHint, Tag, ToggleSwitch } from '@rocket.chat/fuselage'; import { useUniqueId } from '@rocket.chat/fuselage-hooks'; import { useTranslation, usePermission, useSetting } from '@rocket.chat/ui-contexts'; -import React, { useEffect } from 'react'; +import React from 'react'; import { useFormContext } from 'react-hook-form'; import { useHasLicenseModule } from '../../../hooks/useHasLicenseModule'; @@ -22,14 +22,9 @@ const PreferencesConversationTranscript = () => { const omnichannelTranscriptPDF = useUniqueId(); const omnichannelTranscriptEmail = useUniqueId(); - useEffect(() => { - if (alwaysSendEmailTranscript && !canSendTranscriptEmailPref) { - setValue('omnichannelTranscriptEmail', true); - return; - } - - setValue('omnichannelTranscriptEmail', canSendTranscriptEmailPref); - }, [setValue, alwaysSendEmailTranscript, canSendTranscriptEmailPref]); + if (alwaysSendEmailTranscript && !canSendTranscriptEmailPref) { + setValue('omnichannelTranscriptEmail', true); + } return ( From e3ed19321192904913cddf0ce587f98e6ff92388 Mon Sep 17 00:00:00 2001 From: Kevin Aleman Date: Tue, 23 Jul 2024 13:51:50 -0600 Subject: [PATCH 17/17] martin --- .../account/omnichannel/OmnichannelPreferencesPage.tsx | 9 +++++++-- .../omnichannel/PreferencesConversationTranscript.tsx | 7 +------ packages/i18n/src/locales/pt-BR.i18n.json | 2 ++ 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/apps/meteor/client/views/account/omnichannel/OmnichannelPreferencesPage.tsx b/apps/meteor/client/views/account/omnichannel/OmnichannelPreferencesPage.tsx index 9abec01df1d05..8489b3cb4b8b9 100644 --- a/apps/meteor/client/views/account/omnichannel/OmnichannelPreferencesPage.tsx +++ b/apps/meteor/client/views/account/omnichannel/OmnichannelPreferencesPage.tsx @@ -1,5 +1,5 @@ import { ButtonGroup, Button, Box, Accordion } from '@rocket.chat/fuselage'; -import { useToastMessageDispatch, useTranslation, useEndpoint, useUserPreference } from '@rocket.chat/ui-contexts'; +import { useToastMessageDispatch, useTranslation, useEndpoint, useUserPreference, useSetting } from '@rocket.chat/ui-contexts'; import type { ReactElement } from 'react'; import React from 'react'; import { useForm, FormProvider } from 'react-hook-form'; @@ -17,12 +17,17 @@ const OmnichannelPreferencesPage = (): ReactElement => { const t = useTranslation(); const dispatchToastMessage = useToastMessageDispatch(); + const alwaysSendEmailTranscript = useSetting('Livechat_transcript_send_always'); const omnichannelTranscriptPDF = useUserPreference('omnichannelTranscriptPDF') ?? false; const omnichannelTranscriptEmail = useUserPreference('omnichannelTranscriptEmail') ?? false; const omnichannelHideConversationAfterClosing = useUserPreference('omnichannelHideConversationAfterClosing') ?? true; const methods = useForm({ - defaultValues: { omnichannelTranscriptPDF, omnichannelTranscriptEmail, omnichannelHideConversationAfterClosing }, + defaultValues: { + omnichannelTranscriptPDF, + omnichannelTranscriptEmail: alwaysSendEmailTranscript || omnichannelTranscriptEmail, + omnichannelHideConversationAfterClosing, + }, }); const { diff --git a/apps/meteor/client/views/account/omnichannel/PreferencesConversationTranscript.tsx b/apps/meteor/client/views/account/omnichannel/PreferencesConversationTranscript.tsx index f5d018ba40798..a003861c8d57b 100644 --- a/apps/meteor/client/views/account/omnichannel/PreferencesConversationTranscript.tsx +++ b/apps/meteor/client/views/account/omnichannel/PreferencesConversationTranscript.tsx @@ -9,23 +9,18 @@ import { useHasLicenseModule } from '../../../hooks/useHasLicenseModule'; const PreferencesConversationTranscript = () => { const t = useTranslation(); - const { register, setValue, watch } = useFormContext(); + const { register } = useFormContext(); const hasLicense = useHasLicenseModule('livechat-enterprise'); const alwaysSendEmailTranscript = useSetting('Livechat_transcript_send_always'); const canSendTranscriptPDF = usePermission('request-pdf-transcript'); const canSendTranscriptEmailPermission = usePermission('send-omnichannel-chat-transcript'); const canSendTranscriptEmail = canSendTranscriptEmailPermission && !alwaysSendEmailTranscript; - const canSendTranscriptEmailPref = watch('omnichannelTranscriptEmail'); const cantSendTranscriptPDF = !canSendTranscriptPDF || !hasLicense; const omnichannelTranscriptPDF = useUniqueId(); const omnichannelTranscriptEmail = useUniqueId(); - if (alwaysSendEmailTranscript && !canSendTranscriptEmailPref) { - setValue('omnichannelTranscriptEmail', true); - } - return ( diff --git a/packages/i18n/src/locales/pt-BR.i18n.json b/packages/i18n/src/locales/pt-BR.i18n.json index d56243509469d..6e5a59bda0a53 100644 --- a/packages/i18n/src/locales/pt-BR.i18n.json +++ b/packages/i18n/src/locales/pt-BR.i18n.json @@ -2713,6 +2713,8 @@ "Livechat_transcript_has_been_requested": "A transcrição da conversa foi solicitada.", "Livechat_transcript_request_has_been_canceled": "A solicitação da transcrição da conversa foi cancelada.", "Livechat_transcript_sent": "Transcrição de omnichannel enviada", + "Livechat_transcript_send_always": "Sempre enviar transcrição de conversas aos visitantes via email", + "Livechat_transcript_send_always_Description": "Assim que finalizada, enviar a transcrição da conversa via email aos visitantes automaticamente, independentemente das permissões do agente.", "Livechat_transfer_return_to_the_queue": "{{from}} retornou à conversa para a fila", "Livechat_transfer_to_agent": "{{from}} transferiu a conversa para {{to}}", "Livechat_transfer_to_agent_with_a_comment": "{{from}} transferiu a conversa para {{to}} com um comentário: {{comment}}",