Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Allow admins to control if visitors can close omnichannel conversations #33139

Merged
merged 11 commits into from
Sep 9, 2024
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 @@ -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');
KevLehman marked this conversation as resolved.
Show resolved Hide resolved
}

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 @@ -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';
Expand Down
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 @@ -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);
MarcosSpessatto marked this conversation as resolved.
Show resolved Hide resolved
});

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 });
KevLehman marked this conversation as resolved.
Show resolved Hide resolved
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 });
KevLehman marked this conversation as resolved.
Show resolved Hide resolved
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
18 changes: 18 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,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 () => {
KevLehman marked this conversation as resolved.
Show resolved Hide resolved
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 @@ -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",
KevLehman marked this conversation as resolved.
Show resolved Hide resolved
"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.",
KevLehman marked this conversation as resolved.
Show resolved Hide resolved
"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 = () => {
lucas-a-pelegrino marked this conversation as resolved.
Show resolved Hide resolved
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
Loading