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

Chore: Remove & Test cannedResponse meteor templates (#26706)

parent 2155e04f
No related branches found
No related tags found
No related merge requests found
......@@ -2,7 +2,5 @@ import './tabBar';
import './startup/responses';
import './stylesheets/cannedResponses.css';
import './views/icons.html';
import './views/tabbar/cannedResponses';
import './views/tabbar/departmentCannedResponses';
import './views/popup/addMessagePopupCannedResponse';
import './views/popup/messagePopupCannedResponse.html';
<template name="cannedResponses">
{{#requiresPermission 'view-canned-responses'}}
{{#if isDetailScreen}}
{{#with cannedResponse}}
<ul class="canned-response-list">
{{#if isExistingResponse}}
<li>
<div class="canned-response-form-wrapper">
<div class="canned-response-creator-data">
<div class="canned-response-label">Created by</div>
<div class="canned-response-created-by canned-response-info">{{createdByString}}</div>
</div>
{{#if canDeleteResponse}}
<div class="canned-response-delete-button">
{{> icon block="rc-header__icon" icon="trash"}}
<a href="#" class="delete-canned-response" data-id="{{_id}}" >{{_ "Delete"}}</a>
</div>
{{/if}}
</div>
</li>
{{/if}}
<li>
<div class="canned-response-form-wrapper">
{{#if canModifyResponse}}
<fieldset>
<input type="hidden" name="id" value={{_id}}>
<div class="rc-input">
<label class="rc-input__label">
<div class="rc-input__title">{{_ "Shortcut"}}</div>
<div class="rc-input__wrapper">
<div class="rc-input__icon canned-response-shortcut-prefix">!</div>
<input class="rc-input__element" type="text" name="shortcut" autocomplete="off" value="{{shortcut}}">
</div>
</label>
</div>
<div class="rc-input">
<label class="rc-input__label">
<div class="rc-input__title">{{_ "Text"}}</div>
<div class="rc-input__wrapper">
<textarea class="rc-input__element" name="text" rows="8" style="height: auto">{{text}}</textarea>
</div>
</label>
</div>
<div class="rc-button__group">
<button class='rc-button rc-button--primary save-canned-response-btn'><span>{{_ "Save"}}</span></button>
<button class='rc-button rc-button--secondary cancel-canned-response-btn'><span>{{_ "Cancel"}}</span></button>
</div>
</fieldset>
{{else}}
<div>
<div class="canned-response-shortcut-preview">! {{shortcut}}</div>
<div class="canned-response-text-preview">{{text}}</div>
</div>
{{/if}}
</div>
</li>
</ul>
{{/with}}
{{#if isUserContext}}
<div class="rc-button__group canned-response-buttons">
{{#if isExistingResponse}}
<a href="#" class="rc-button rc-button--primary use-canned-response-btn">{{_ "Use_this_response"}}</a>
{{#if isPreviewMode}}
<button class='rc-button rc-button--secondary cancel-canned-response-btn'><span>{{_ "Cancel"}}</span></button>
{{/if}}
{{/if}}
</div>
{{/if}}
{{else}}
{{#if hasNoCannedResponses}}
<h2 class="thread-empty">{{_ "No_Canned_Responses"}}</h2>
{{else}}
<div class="canned-response-list-wrapper">
<ul class="canned-response-list">
<li class="canned-response-search-item">
<div class="canned-response-wrapper">
<div class="rc-input__wrapper">
<div class="rc-input__icon">
{{> icon block="rc-input__icon-svg" icon="magnifier" }}
</div>
<input type="text" class="rc-input__element js-search" name="response-search" id="response-search" placeholder="{{_ "Search"}}" autocomplete="off" value="{{searchText}}">
</div>
</div>
</li>
{{#each cannedResponses}}
<li data-id="{{_id}}" class="canned-response">
<div class="canned-response-wrapper">
<div class="canned-response-shortcut">{{shortcut}}</div>
<div class="canned-response-text">{{text}}</div>
<div class="canned-response-scope">{{scope}}</div>
</div>
</li>
{{/each}}
</ul>
</div>
{{/if}}
<div class="rc-button__group canned-response-buttons">
<a href="#" class="rc-button rc-button--primary new-canned-response-btn">{{_ "New_Canned_Response"}}</a>
</div>
{{/if}}
{{/requiresPermission}}
</template>
import { ReactiveVar } from 'meteor/reactive-var';
import { Meteor } from 'meteor/meteor';
import { Template } from 'meteor/templating';
import { CannedResponse } from '../../collections/CannedResponse';
import { t } from '../../../../../../app/utils';
import { chatMessages } from '../../../../../../app/ui/client';
import { dispatchToastMessage } from '../../../../../../client/lib/toast';
import './cannedResponses.html';
Template.cannedResponses.helpers({
cannedResponses() {
const searchText = Template.instance().searchText.get().toLocaleLowerCase();
let query = {};
if (Template.instance().context.get() === 'department') {
query = {
scope: 'department',
departmentId: Template.instance().departmentId,
};
}
if (searchText) {
const regex = new RegExp(searchText, 'i');
query.$or = [
{
shortcut: {
$regex: regex,
},
},
{
text: {
$regex: regex,
},
},
];
}
return CannedResponse.find(query);
},
hasNoCannedResponses() {
return CannedResponse.find().count() === 0;
},
isDetailScreen() {
return ['edit', 'new', 'view'].includes(Template.instance().action.get());
},
isNewResponse() {
return Template.instance().action.get() === 'new';
},
isExistingResponse() {
return ['edit', 'view'].includes(Template.instance().action.get());
},
isUserContext() {
return Template.instance().context.get() === 'user';
},
searchText() {
return Template.instance().searchText.get();
},
isDepartmentContext() {
return Template.instance().context.get() === 'department';
},
cannedResponse() {
return Template.instance().cannedResponse.get();
},
canDeleteResponse() {
const action = Template.instance().action.get();
if (action !== 'edit') {
return false;
}
const response = Template.instance().cannedResponse.get();
const context = Template.instance().context.get();
return response.scope === context;
},
canModifyResponse() {
const action = Template.instance().action.get();
if (action === 'new') {
return true;
}
const response = Template.instance().cannedResponse.get();
const context = Template.instance().context.get();
return response.scope === context;
},
createdByString() {
const username = this.createdBy;
const me = Meteor.users.findOne(Meteor.userId());
if (me.username === username) {
return t('Me');
}
return username;
},
isPreviewMode() {
return Template.instance().action.get() === 'view';
},
});
Template.cannedResponses.events({
'click .new-canned-response-btn'(e, instance) {
e.preventDefault();
e.stopPropagation();
instance.cannedResponse.set({
shortcut: '',
text: '',
});
instance.action.set('new');
},
'click .canned-response'(e, instance) {
e.preventDefault();
e.stopPropagation();
const { _id } = this;
const cannedResponse = CannedResponse.findOne({ _id });
if (!cannedResponse) {
dispatchToastMessage({ type: 'success', message: t('Invalid Canned Response') });
}
instance.cannedResponse.set(cannedResponse);
const canEdit = instance.context.get() === cannedResponse.scope;
instance.action.set(canEdit ? 'edit' : 'view');
},
'click .delete-canned-response'(e, instance) {
e.preventDefault();
e.stopPropagation();
const _id = instance.$('input[name=id]').val();
Meteor.call('removeCannedResponse', _id, (error) => {
if (error) {
dispatchToastMessage({ type: 'error', message: error });
return;
}
dispatchToastMessage({ type: 'success', message: t('Canned_Response_Removed') });
instance.action.set(null);
});
},
'click .cancel-canned-response-btn'(e, instance) {
e.preventDefault();
e.stopPropagation();
instance.action.set(null);
},
'click .use-canned-response-btn'(e, instance) {
e.preventDefault();
e.stopPropagation();
const { rid } = Template.currentData();
if (!rid) {
return;
}
const object = instance.cannedResponse.get();
const { input } = chatMessages[rid];
const { text } = object;
if (document.selection) {
input.focus();
const sel = document.selection.createRange();
sel.text = text;
return;
}
if (input.selectionStart || input.selectionStart === 0) {
const startPos = input.selectionStart;
const endPos = input.selectionEnd;
const prefix = input.value.substring(0, startPos);
const suffix = input.value.substring(endPos, input.value.length);
input.value = `${prefix}${text}${suffix}`;
input.selectionStart = startPos + text.length;
input.selectionEnd = startPos + text.length;
input.focus();
instance.action.set(null);
return;
}
input.value += text;
},
'click .save-canned-response-btn'(e, instance) {
e.preventDefault();
e.stopPropagation();
const _id = instance.$('input[name=id]').val();
const shortcut = instance.$('input[name=shortcut]').val();
const text = instance.$('textarea[name=text]').val();
const responseData = {
shortcut,
text,
};
const context = instance.context.get();
if (context === 'department') {
if (!instance.departmentId) {
dispatchToastMessage({ type: 'error', message: t('Invalid_Department') });
return;
}
responseData.scope = 'department';
responseData.departmentId = instance.departmentId;
} else {
responseData.scope = 'user';
}
Meteor.call('saveCannedResponse', _id, responseData, function (error /* , result*/) {
if (error) {
dispatchToastMessage({ type: 'error', message: error });
return;
}
dispatchToastMessage({ type: 'success', message: t('Saved') });
instance.action.set(null);
});
},
'keyup .js-search'(event, instance) {
instance.searchText.set(event.currentTarget.value || '');
},
});
Template.cannedResponses.onCreated(function () {
this.action = new ReactiveVar();
this.context = new ReactiveVar();
this.cannedResponse = new ReactiveVar();
this.searchText = new ReactiveVar('');
this.departmentId = null;
if (this.data && this.data.context) {
this.context.set(this.data.context);
if (this.data.context === 'department') {
this.departmentId = this.data.departmentId;
}
} else {
this.context.set('user');
}
});
<template name="departmentCannedResponses">
{{> cannedResponses context="department" departmentId=departmentId}}
</template>
\ No newline at end of file
import { FlowRouter } from 'meteor/kadira:flow-router';
import { Template } from 'meteor/templating';
import './departmentCannedResponses.html';
Template.departmentCannedResponses.helpers({
departmentId() {
return FlowRouter.getParam('_id');
},
});
Template.departmentCannedResponses.events({});
Template.departmentCannedResponses.onCreated(function () {});
import { faker } from '@faker-js/faker';
import type { Browser, Page } from '@playwright/test';
import { test } from './utils/test';
import { OmnichannelLiveChat, HomeChannel } from './page-objects';
import { IS_EE } from './config/constants';
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 Canned Responses Sidebar', () => {
test.skip(!IS_EE, 'Enterprise Only');
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 open canned responses sidebar and creation', async () => {
await agent.poHomeChannel.content.btnCannedResponses.click();
await agent.poHomeChannel.content.btnNewCannedResponse.click();
});
});
});
......@@ -130,6 +130,14 @@ export class HomeContent {
return this.page.locator('[data-qa-id="ToolBoxAction-mail-arrow-top-right"]');
}
get btnCannedResponses(): Locator {
return this.page.locator('[data-qa-id="ToolBoxAction-canned-response"]');
}
get btnNewCannedResponse(): Locator {
return this.page.locator('.rcx-vertical-bar button:has-text("Create")');
}
get inputModalAgentUserName(): Locator {
return this.page.locator('#modal-root input:nth-child(1)');
}
......
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