Skip to content
Snippets Groups Projects
Unverified Commit 3ae6fbb1 authored by Martin Schoeler's avatar Martin Schoeler Committed by GitHub
Browse files

Chore: Remove & Test old closeChat templates (#26631)

parent 12481d59
No related branches found
No related tags found
No related merge requests found
......@@ -3,6 +3,5 @@ import './voip';
import './ui';
import './tabBar';
import './startup/notifyUnreadRooms';
import './views/app/dialog/closeRoom';
import './stylesheets/livechat.css';
import './externalFrame';
<template name="closeRoom">
<section class="create-channel">
<div class="create-channel__wrapper">
<p class="create-channel__description">{{_ "Close_room_description"}}</p>
<form id="close-room" class="close-room__content">
<div class="create-channel__inputs">
<div class="rc-input {{#if invalidComment}}rc-input--error{{/if}}">
<label class="rc-input__label">
<div class="rc-input__wrapper">
<input name="comment" id="comment" type="text" class="rc-input__element"
placeholder="{{_ "Please_add_a_comment"}}" autofocus>
</div>
</label>
{{#if invalidComment}}
<div class="rc-input__error">
<div class="rc-input__error-icon">
{{> icon block="rc-input__error-icon" icon="warning" classes="rc-input__error-icon-svg"}}
</div>
<div class="rc-input__error-message">{{_ "Please_add_a_comment_to_close_the_room"}}</div>
</div>
{{/if}}
</div>
<div class="rc-input {{#if invalidTags}}rc-input--error{{/if}}">
{{#if hasAvailableTags}}
<div class="rc-form-group rc-form-group--small rc-form-group--inline">
<select id="tagSelect" class="rc-input rc-input--small rc-form-item-inline">
<option value="placeholder" disabled selected>{{_ "Select_tag"}}</option>
{{#each availableUserTags}}
<option value="{{_id}}">{{this}}</option>
{{/each}}
</select>
<button id="addTag" class="rc-button rc-button--primary rc-form-item-inline"><i class="icon-plus"></i></button>
</div>
{{else}}
<label class="rc-input__label">
<div class="rc-input__wrapper">
<div class="rc-input__icon">
{{> icon icon='edit' }}
</div>
<input id="tagInput" class="rc-input__element" type="text" name="tags" autocomplete="off" placeholder="{{tagsPlaceHolder}}">
</div>
</label>
{{/if}}
{{#if invalidTags}}
<div class="rc-input__error">
<div class="rc-input__error-icon">
{{> icon block="rc-input__error-icon" icon="warning" classes="rc-input__error-icon-svg"}}
</div>
<div class="rc-input__error-message">{{_ "error-tags-must-be-assigned-before-closing-chat"}}</div>
</div>
{{/if}}
</div>
<div class="rc-form-group rc-form-group--small">
<ul id="tags" class="chip-container current-room-tags">
{{#each tags}}
<li class="remove-tag" title="{{this}}">
{{#if canRemoveTag availableUserTags this}}
<i class="icon icon-cancel-circled"></i>
{{/if}}
{{this}}
</li>
{{/each}}
</ul>
</div>
</div>
</form>
<div class="rc-input">
<button form="close-room" class="rc-button rc-button--primary js-close-room">{{_ " Confirm "}}</button>
</div>
</div>
</section>
</template>
import { Meteor } from 'meteor/meteor';
import { Template } from 'meteor/templating';
import { ReactiveVar } from 'meteor/reactive-var';
import { TAPi18n } from 'meteor/rocketchat:tap-i18n';
import { settings } from '../../../../../settings';
import { modal } from '../../../../../ui-utils/client';
import { APIClient, t } from '../../../../../utils';
import { hasAnyRole } from '../../../../../authorization';
import { dispatchToastMessage } from '../../../../../../client/lib/toast';
import './closeRoom.html';
const validateRoomComment = (comment) => {
if (!settings.get('Livechat_request_comment_when_closing_conversation')) {
return true;
}
return comment?.length > 0;
};
const validateRoomTags = (tagsRequired, tags) => {
if (!tagsRequired) {
return true;
}
return tags?.length > 0;
};
const checkUserTagPermission = (availableUserTags = [], tag) => {
if (hasAnyRole(Meteor.userId(), ['admin', 'livechat-manager'])) {
return true;
}
return availableUserTags.includes(tag);
};
Template.closeRoom.helpers({
invalidComment() {
return Template.instance().invalidComment.get();
},
tags() {
return Template.instance().tags.get();
},
invalidTags() {
return Template.instance().invalidTags.get();
},
availableUserTags() {
return Template.instance().availableUserTags.get();
},
tagsPlaceHolder() {
let placeholder = TAPi18n.__('Enter_a_tag');
if (!Template.instance().tagsRequired.get()) {
placeholder = placeholder.concat(`(${TAPi18n.__('Optional')})`);
}
return placeholder;
},
hasAvailableTags() {
const tags = Template.instance().availableTags.get();
return tags?.length > 0;
},
canRemoveTag(availableUserTags, tag) {
return checkUserTagPermission(availableUserTags, tag);
},
});
Template.closeRoom.events({
async 'submit .close-room__content'(e, instance) {
e.preventDefault();
e.stopPropagation();
const comment = instance.$('#comment').val();
instance.invalidComment.set(!validateRoomComment(comment));
if (instance.invalidComment.get()) {
return;
}
const tagsRequired = instance.tagsRequired.get();
const tags = instance.tags.get();
instance.invalidTags.set(!validateRoomTags(tagsRequired, tags));
if (instance.invalidTags.get()) {
return;
}
Meteor.call('livechat:closeRoom', this.rid, comment, { clientAction: true, tags }, function (error /* , result*/) {
if (error) {
console.log(error);
dispatchToastMessage({ type: 'error', message: error });
return;
}
modal.open({
title: t('Chat_closed'),
text: t('Chat_closed_successfully'),
type: 'success',
timer: 1000,
showConfirmButton: false,
});
});
},
'click .remove-tag'(e, instance) {
e.stopPropagation();
e.preventDefault();
const tag = this.valueOf();
const availableTags = instance.availableTags.get();
const hasAvailableTags = availableTags?.length > 0;
const availableUserTags = instance.availableUserTags.get();
if (hasAvailableTags && !checkUserTagPermission(availableUserTags, tag)) {
return;
}
let tags = instance.tags.get();
tags = tags.filter((el) => el !== tag);
instance.tags.set(tags);
},
'click #addTag'(e, instance) {
e.stopPropagation();
e.preventDefault();
if ($('#tagSelect').find(':selected').is(':disabled')) {
return;
}
const tags = [...instance.tags.get()];
const tagVal = $('#tagSelect').val();
if (tagVal === '' || tags.includes(tagVal)) {
return;
}
tags.push(tagVal);
instance.tags.set(tags);
$('#tagSelect').val('placeholder');
},
'keydown #tagInput'(e, instance) {
if (e.which === 13) {
e.stopPropagation();
e.preventDefault();
const tags = [...instance.tags.get()];
const tagVal = $('#tagInput').val();
if (tagVal === '' || tags.includes(tagVal)) {
return;
}
tags.push(tagVal);
instance.tags.set(tags);
$('#tagInput').val('');
}
},
});
Template.closeRoom.onRendered(function () {
this.find('#comment').focus();
});
Template.closeRoom.onCreated(async function () {
this.tags = new ReactiveVar([]);
this.invalidComment = new ReactiveVar(false);
this.invalidTags = new ReactiveVar(false);
this.tagsRequired = new ReactiveVar(false);
this.availableTags = new ReactiveVar([]);
this.availableUserTags = new ReactiveVar([]);
this.agentDepartments = new ReactiveVar([]);
this.onEnterTag = () => this.invalidTags.set(!validateRoomTags(this.tagsRequired.get(), this.tags.get()));
const { rid } = Template.currentData();
const { room } = await APIClient.get(`/v1/rooms.info`, { roomId: rid });
this.tags.set(room?.tags || []);
if (room?.departmentId) {
const { department } = await APIClient.get(`/v1/livechat/department/${room.departmentId}`, { includeAgents: false });
this.tagsRequired.set(department?.requestTagBeforeClosingChat);
}
const uid = Meteor.userId();
const { departments } = await APIClient.get(`/v1/livechat/agents/${uid}/departments`);
const agentDepartments = departments.map((dept) => dept.departmentId);
this.agentDepartments.set(agentDepartments);
Meteor.call('livechat:getTagsList', (err, tagsList) => {
this.availableTags.set(tagsList);
const isAdmin = hasAnyRole(uid, ['admin', 'livechat-manager']);
const availableTags = tagsList
.filter(({ departments }) => isAdmin || departments.length === 0 || departments.some((i) => agentDepartments.includes(i)))
.map(({ name }) => name);
this.availableUserTags.set(availableTags);
});
});
<template name="livechatRoomTagSelector">
{{#if hasAvailableTags}}
<div class="rc-select">
<select class="rc-select__element" name="tags">
<option class="rc-select__option" value="">{{_ "All"}}</option>
{{#each availableTags}}
<option class="rc-select__option" value="{{name}}">{{name}}</option>
{{/each}}
</select>
{{> icon block="rc-select__arrow" icon="arrow-down" }}
</div>
{{else}}
<div class="rc-input__wrapper">
<input autocomplete="off" type="text" placeholder="{{_ "Tags"}}" class="rc-input__element" name="tags">
</div>
{{/if}}
</template>
import { Meteor } from 'meteor/meteor';
import { Template } from 'meteor/templating';
import { ReactiveVar } from 'meteor/reactive-var';
import './livechatRoomTagSelector.html';
Template.livechatRoomTagSelector.helpers({
availableTags() {
return Template.instance().availableTags.get();
},
hasAvailableTags() {
const tags = Template.instance().availableTags.get();
return tags && tags.length > 0;
},
});
Template.livechatRoomTagSelector.onCreated(function () {
this.availableTags = new ReactiveVar([]);
Meteor.call('livechat:getTagsList', (err, tagsList) => {
this.availableTags.set(tagsList);
});
});
import './app/livechatReadOnly';
import './app/livechatNotSubscribed.html';
import './app/livechatRoomTagSelector';
import './app/tabbar/agentEdit';
import './app/tabbar/agentInfo';
import './app/tabbar/visitorEdit';
......
import { faker } from '@faker-js/faker';
import type { Browser, Page } from '@playwright/test';
import { test, expect } from './utils/test';
import { OmnichannelLiveChat, HomeChannel } from './page-objects';
const createAuxContext = async (browser: Browser, storageState: string): Promise<{ page: Page; poHomeChannel: HomeChannel }> => {
const page = await browser.newPage({ storageState });
const poHomeChannel = new HomeChannel(page);
await page.goto('/');
await page.locator('.main-content').waitFor();
return { page, poHomeChannel };
};
test.describe('Omnichannel close chat', () => {
let poLiveChat: OmnichannelLiveChat;
let newUser: { email: string; name: string };
let agent: { page: Page; poHomeChannel: HomeChannel };
test.beforeAll(async ({ api, browser }) => {
newUser = {
name: faker.name.firstName(),
email: faker.internet.email(),
};
// Set user user 1 as manager and agent
await api.post('/livechat/users/agent', { username: 'user1' });
await api.post('/livechat/users/manager', { username: 'user1' });
agent = await createAuxContext(browser, 'user1-session.json');
});
test.beforeEach(async ({ page }) => {
poLiveChat = new OmnichannelLiveChat(page);
});
test.afterAll(async ({ api }) => {
await api.delete('/livechat/users/agent/user1');
await api.delete('/livechat/users/manager/user1');
});
test('Receiving a message from visitor', async ({ page }) => {
await test.step('Expect send a message as a visitor', async () => {
await page.goto('/livechat');
await poLiveChat.btnOpenLiveChat('R').click();
await poLiveChat.sendMessage(newUser, false);
await poLiveChat.onlineAgentMessage.type('this_a_test_message_from_visitor');
await poLiveChat.btnSendMessageToOnlineAgent.click();
});
await test.step('Expect to have 1 omnichannel assigned to agent 1', async () => {
await agent.poHomeChannel.sidenav.openChat(newUser.name);
});
await test.step('Expect to be able to close an omnichannel to conversation', async () => {
await agent.poHomeChannel.content.btnCloseChat.click();
await agent.poHomeChannel.content.inputModalClosingComment.type('any_comment');
await agent.poHomeChannel.content.btnModalConfirm.click();
await expect(agent.poHomeChannel.toastSuccess).toBeVisible();
});
});
});
......@@ -110,6 +110,14 @@ export class HomeContent {
return this.page.locator('[data-qa-id="ToolBoxAction-balloon-arrow-top-right"]');
}
get btnCloseChat(): Locator {
return this.page.locator('[data-qa-id="ToolBoxAction-balloon-close-top-right"]');
}
get inputModalClosingComment(): Locator {
return this.page.locator('#modal-root input:nth-child(1)[name="comment"]');
}
get btnSendTranscript(): Locator {
return this.page.locator('[data-qa-id="ToolBoxAction-mail-arrow-top-right"]');
}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment