Skip to content

Commit

Permalink
feat: Allow admins to control if visitors can close omnichannel conve…
Browse files Browse the repository at this point in the history
…rsations (#33139)
  • Loading branch information
KevLehman committed Sep 9, 2024
1 parent 06dbf72 commit 7c14fd1
Show file tree
Hide file tree
Showing 15 changed files with 117 additions and 2 deletions.
8 changes: 8 additions & 0 deletions .changeset/healthy-rivers-nail.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
"@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.
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.
1 change: 1 addition & 0 deletions apps/meteor/app/livechat/imports/server/rest/appearance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand Down
1 change: 1 addition & 0 deletions apps/meteor/app/livechat/server/api/lib/appearance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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',
],
},
};
Expand Down
1 change: 1 addition & 0 deletions apps/meteor/app/livechat/server/api/lib/livechat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
4 changes: 4 additions & 0 deletions apps/meteor/app/livechat/server/api/v1/room.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,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');
Expand Down
1 change: 1 addition & 0 deletions apps/meteor/app/livechat/server/lib/LivechatTyped.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1079,6 +1079,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';
Expand Down
15 changes: 15 additions & 0 deletions apps/meteor/client/views/omnichannel/appearance/AppearanceForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ const AppearanceForm = () => {
const livechatWidgetPositionField = useUniqueId();
const livechatBackgroundField = useUniqueId();
const livechatHideSystemMessagesField = useUniqueId();
const omnichannelVisitorsCanCloseConversationField = useUniqueId();

return (
<Accordion>
Expand Down Expand Up @@ -140,6 +141,20 @@ const AppearanceForm = () => {
/>
</FieldRow>
</Field>
<Field>
<FieldRow>
<FieldLabel htmlFor={omnichannelVisitorsCanCloseConversationField}>
{t('Omnichannel_allow_visitors_to_close_conversation')}
</FieldLabel>
<Controller
name='Omnichannel_allow_visitors_to_close_conversation'
control={control}
render={({ field: { value, ...field } }) => (
<ToggleSwitch id={omnichannelVisitorsCanCloseConversationField} {...field} checked={value} />
)}
/>
</FieldRow>
</Field>
</FieldGroup>
</Accordion.Item>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<LivechatAppearanceSettings>;
Expand Down
7 changes: 7 additions & 0 deletions apps/meteor/server/settings/omnichannel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
52 changes: 52 additions & 0 deletions apps/meteor/tests/e2e/omnichannel/omnichannel-livechat.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down Expand Up @@ -93,6 +94,57 @@ 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 }) => {
await api.post('/livechat/users/agent', { username: 'user1' });
});

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 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 setSettingValueById(api, 'Livechat_allow_visitor_closing_chat', 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;
Expand Down
19 changes: 19 additions & 0 deletions apps/meteor/tests/end-to-end/api/livechat/00-rooms.ts
Original file line number Diff line number Diff line change
Expand Up @@ -689,6 +689,25 @@ 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 () => {
const { room, visitor } = await startANewLivechatRoomAndTakeIt();
await request
.post(api('livechat/room.close'))
.send({
token: visitor.token,
rid: room._id,
})
.expect(400);
});
});
});

describe('livechat/room.forward', () => {
Expand Down
2 changes: 2 additions & 0 deletions packages/i18n/src/locales/en.i18n.json
Original file line number Diff line number Diff line change
Expand Up @@ -4037,6 +4037,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 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",
Expand Down
2 changes: 2 additions & 0 deletions packages/livechat/src/routes/Chat/connector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export const ChatConnector: FunctionalComponent<{ path: string; default: boolean
nameFieldRegistrationForm,
emailFieldRegistrationForm,
limitTextLength,
visitorsCanCloseChat,
},
messages: { conversationFinishedMessage },
theme: { title = '' } = {},
Expand Down Expand Up @@ -94,6 +95,7 @@ export const ChatConnector: FunctionalComponent<{ path: string; default: boolean
ongoingCall={ongoingCall}
messageListPosition={messageListPosition}
theme={theme}
visitorsCanCloseChat={visitorsCanCloseChat}
/>
);
};
Expand Down
4 changes: 2 additions & 2 deletions packages/livechat/src/routes/Chat/container.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 = () => {
Expand Down
1 change: 1 addition & 0 deletions packages/livechat/src/store/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ export type StoreState = {
hideWatermark?: boolean;
livechatLogo?: { url: string };
transcript?: boolean;
visitorsCanCloseChat?: boolean;
};
online?: boolean;
departments: Department[];
Expand Down

0 comments on commit 7c14fd1

Please sign in to comment.