Skip to content
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 9 additions & 6 deletions app/livechat/client/views/app/livechatCustomFieldForm.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,19 @@
<div class="input-line">
<label>{{_ "Field"}}</label>
<div>
<input type="text" class="rc-input__element" name="field" value="{{customField._id}}" readonly="{{$exists customField._id}}" placeholder="{{_ "Field"}}" />
<input type="text" class="rc-input__element custom-field-input" name="field" value="{{customField._id}}" readonly="{{$exists customField._id}}" placeholder="{{_ "Field"}}" />
</div>
</div>
<div class="input-line">
<label>{{_ "Label"}}</label>
<div>
<input type="text" class="rc-input__element" name="label" value="{{customField.label}}" placeholder="{{_ "Label"}}" />
<input type="text" class="rc-input__element custom-field-input" name="label" value="{{customField.label}}" placeholder="{{_ "Label"}}" />
</div>
</div>
<div class="input-line">
<label>{{_ "Scope"}}</label>
<div>
<select name="scope" class="rc-input__element">
<select name="scope" class="rc-input__element custom-field-input">
<option value="visitor" selected="{{$eq customField.scope 'visitor'}}">{{_ "Visitor"}}</option>
<option value="room" selected="{{$eq customField.scope 'room'}}">{{_ "Room"}}</option>
</select>
Expand All @@ -28,18 +28,21 @@
<div class="input-line">
<label>{{_ "Visibility"}}</label>
<div>
<select name="visibility" class="rc-input__element">
<select name="visibility" class="rc-input__element custom-field-input">
<option value="visible" selected="{{$eq customField.visibility 'visible'}}">{{_ "Visible"}}</option>
<option value="hidden" selected="{{$eq customField.visibility 'hidden'}}">{{_ "Hidden"}}</option>
</select>
</div>
</div>
<div class="input-line">
<label>{{_ "Regexp_validation"}}</label>
<label>{{_ "Validation"}}</label>
<div>
<input type="text" class="rc-input__element" name="regexp" value="{{customField.regexp}}" placeholder="{{_ "Regexp_validation"}}" />
<input type="text" class="rc-input__element custom-field-input" name="regexp" value="{{customField.regexp}}" placeholder="{{_ "Regexp_validation"}}" />
</div>
</div>
{{#if customFieldsTemplate}}
{{> Template.dynamic template=customFieldsTemplate data=dataContext }}
{{/if}}
</fieldset>
<div class="rc-button__group submit">
<button class="rc-button back" type="button"><i class="icon-left-big"></i><span>{{_ "Back"}}</span></button>
Expand Down
29 changes: 29 additions & 0 deletions app/livechat/client/views/app/livechatCustomFieldForm.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,24 @@ import { Template } from 'meteor/templating';
import toastr from 'toastr';

import { t, handleError } from '../../../../utils';
import { getCustomFormTemplate } from './customTemplates/register';
import './livechatCustomFieldForm.html';
import { APIClient } from '../../../../utils/client';

Template.livechatCustomFieldForm.helpers({
customField() {
return Template.instance().customField.get();
},

customFieldsTemplate() {
return getCustomFormTemplate('livechatCustomFieldsAdditionalForm');
},

dataContext() {
// To make the dynamic template reactive we need to pass a ReactiveVar through the data property
// because only the dynamic template data will be reloaded
return Template.instance().localFields;
},
});

Template.livechatCustomFieldForm.events({
Expand Down Expand Up @@ -45,6 +56,16 @@ Template.livechatCustomFieldForm.events({
regexp: regexp.trim(),
};

instance.$('.additional-field').each((i, el) => {
const elField = instance.$(el);
const name = elField.attr('name');
let value = elField.val();
if (['true', 'false'].includes(value) && el.tagName === 'SELECT') {
value = value === 'true';
}
customFieldData[name] = value;
});

Meteor.call('livechat:saveCustomField', _id, customFieldData, function(error) {
$btn.html(oldBtnValue);
if (error) {
Expand All @@ -60,12 +81,20 @@ Template.livechatCustomFieldForm.events({
e.preventDefault();
FlowRouter.go('livechat-customfields');
},

'change .custom-field-input'(e, instance) {
const { target: { name, value } } = e;
instance.localFields.set({ ...instance.localFields.get(), [name]: value });
},
});

Template.livechatCustomFieldForm.onCreated(async function() {
this.customField = new ReactiveVar({});
this.localFields = new ReactiveVar({});

const { customField } = await APIClient.v1.get(`livechat/custom-fields/${ FlowRouter.getParam('_id') }`);
if (customField) {
this.customField.set(customField);
this.localFields.set({ ...customField });
}
});
23 changes: 4 additions & 19 deletions app/livechat/client/views/app/tabbar/visitorEdit.html
Original file line number Diff line number Diff line change
Expand Up @@ -30,17 +30,9 @@ <h3>{{username}}</h3>
</div>
</label>
</div>

{{#if canViewCustomFields }}
{{#each visitorCustomFields}}
<div class="rc-input rc-form-group rc-form-group--small">
<label class="rc-input__label">
<div class="rc-input__title">{{label}}</div>
<div class="rc-input__wrapper">
<input class="rc-input__element" type="text" name="{{name}}" autocomplete="off" data-visitorLivechatData="true" value="{{value}}" disabled="{{canOnlyViewCustomFields}}">
</div>
</label>
</div>
{{#each field in visitorCustomFields}}
{{> visitorEditCustomField field }}
{{/each}}
{{/if}}
{{/with}}
Expand Down Expand Up @@ -94,15 +86,8 @@ <h3>{{_ "Conversation" }}</h3>
</ul>
</div>
{{#if canViewCustomFields }}
{{#each roomCustomFields}}
<div class="rc-input rc-form-group rc-form-group--small">
<label class="rc-input__label">
<div class="rc-input__title">{{label}}</div>
<div class="rc-input__wrapper">
<input class="rc-input__element" type="text" name="{{name}}" autocomplete="off" data-roomLivechatData="true" value="{{value}}" disabled="{{canOnlyViewCustomFields}}">
</div>
</label>
</div>
{{#each field in roomCustomFields}}
{{> visitorEditCustomField field }}
{{/each}}
{{/if}}
{{/with}}
Expand Down
34 changes: 12 additions & 22 deletions app/livechat/client/views/app/tabbar/visitorEdit.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,16 @@ import { getCustomFormTemplate } from '../customTemplates/register';

const CUSTOM_FIELDS_COUNT = 100;

const getCustomFieldsByScope = (customFields = [], data = {}, filter, disabled) =>
customFields
.filter(({ visibility, scope }) => visibility !== 'hidden' && scope === filter)
.map(({ _id: name, scope, label, ...extraData }) => {
const value = data[name] ? data[name] : '';
return { name, label, scope, value, disabled, ...extraData };
});

const isCustomFieldDisabled = () => hasPermission('view-livechat-room-customfields') && !hasPermission('edit-livechat-room-customfields');
Comment thread
renatobecker marked this conversation as resolved.
Outdated

Template.visitorEdit.helpers({
visitor() {
return Template.instance().visitor.get();
Expand All @@ -20,28 +30,16 @@ Template.visitorEdit.helpers({
return hasAtLeastOnePermission(['view-livechat-room-customfields', 'edit-livechat-room-customfields']);
},

canOnlyViewCustomFields() {
return hasPermission('view-livechat-room-customfields') && !hasPermission('edit-livechat-room-customfields');
},

visitorCustomFields() {
const customFields = Template.instance().customFields.get();
if (!customFields || customFields.length === 0) {
return [];
}

const fields = [];
const visitor = Template.instance().visitor.get();
const { livechatData = {} } = visitor || {};

customFields.forEach((field) => {
if (field.visibility !== 'hidden' && field.scope === 'visitor') {
const value = livechatData[field._id] ? livechatData[field._id] : '';
fields.push({ name: field._id, label: field.label, value });
}
});

return fields;
return getCustomFieldsByScope(customFields, livechatData, 'visitor', isCustomFieldDisabled());
},

room() {
Expand All @@ -54,18 +52,10 @@ Template.visitorEdit.helpers({
return [];
}

const fields = [];
const room = Template.instance().room.get();
const { livechatData = {} } = room || {};

customFields.forEach((field) => {
if (field.visibility !== 'hidden' && field.scope === 'room') {
const value = livechatData[field._id] ? livechatData[field._id] : '';
fields.push({ name: field._id, label: field.label, value });
}
});

return fields;
return getCustomFieldsByScope(customFields, livechatData, 'room', isCustomFieldDisabled());
},

email() {
Expand Down
22 changes: 22 additions & 0 deletions app/livechat/client/views/app/tabbar/visitorEditCustomField.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<template name="visitorEditCustomField">
<div class="rc-input rc-form-group rc-form-group--small">
<label class="rc-input__label">
<div class="rc-input__title">{{label}}</div>
{{#if $eq type 'select'}}
<div class="rc-select">
<select name="{{name}}" class="rc-select__element" data-visitorLivechatData="{{$eq scope 'visitor'}}" data-roomLivechatData="{{$eq scope 'room'}}" disabled="{{disabled}}">
<option value=""></option>
{{#each optionsList}}
<option value="{{.}}" selected="{{selectedField . ..}}">{{.}}</option>
{{/each}}
</select>
{{> icon block="rc-select__arrow" icon="arrow-down" }}
</div>
{{else}}
<div class="rc-input__wrapper">
<input class="rc-input__element" type="text" name="{{name}}" autocomplete="off" data-visitorLivechatData="{{$eq scope 'visitor'}}" data-roomLivechatData="{{$eq scope 'room'}}" disabled="{{disabled}}" value="{{value}}">
</div>
{{/if}}
</label>
</div>
</template>
21 changes: 21 additions & 0 deletions app/livechat/client/views/app/tabbar/visitorEditCustomField.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { Template } from 'meteor/templating';

import './visitorEditCustomField.html';

Template.visitorEditCustomField.helpers({
optionsList() {
if (!this.options) {
return [];
}

return this.options.split(',');
},
selectedField(current) {
const { fieldData: { value } } = Template.instance();
Comment thread
renatobecker marked this conversation as resolved.
Outdated
return value.trim() === current.trim();
},
});

Template.visitorEditCustomField.onCreated(function() {
this.fieldData = Template.currentData() || {};
Comment thread
renatobecker marked this conversation as resolved.
Outdated
});
1 change: 1 addition & 0 deletions app/livechat/client/views/regular.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import './app/livechatRoomTagSelector';
import './app/tabbar/agentEdit';
import './app/tabbar/agentInfo';
import './app/tabbar/visitorEdit';
import './app/tabbar/visitorEditCustomField';
import './app/tabbar/visitorForward';
import './app/tabbar/visitorHistory';
import './app/tabbar/visitorInfo';
Expand Down
2 changes: 1 addition & 1 deletion app/livechat/server/api/lib/livechat.js
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ export function settings() {
}

export async function getExtraConfigInfo(room) {
return callbacks.run('livechat.onLoadConfigApi', room);
return callbacks.run('livechat.onLoadConfigApi', { room });
}

export function onCheckRoomParams(params) {
Expand Down
5 changes: 3 additions & 2 deletions app/livechat/server/api/v1/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,9 @@ API.v1.addRoute('livechat/config', {
room = findOpenRoom(token);
agent = room && room.servedBy && findAgent(room.servedBy._id);
}
const extraConfig = room && Promise.await(getExtraConfigInfo(room));
Object.assign(config, { online: status, guest, room, agent }, extraConfig);
const extra = Promise.await(getExtraConfigInfo(room));
const { config: extraConfig = {} } = extra || {};
Object.assign(config, { online: status, guest, room, agent }, { ...extraConfig });

return API.v1.success({ config });
} catch (e) {
Expand Down
6 changes: 4 additions & 2 deletions app/livechat/server/methods/saveCustomField.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ Meteor.methods({
check(customFieldData, Match.ObjectIncluding({ field: String, label: String, scope: String, visibility: String, regexp: String }));

if (!/^[0-9a-zA-Z-_]+$/.test(customFieldData.field)) {
throw new Meteor.Error('error-invalid-custom-field-nmae', 'Invalid custom field name. Use only letters, numbers, hyphens and underscores.', { method: 'livechat:saveCustomField' });
throw new Meteor.Error('error-invalid-custom-field-name', 'Invalid custom field name. Use only letters, numbers, hyphens and underscores.', { method: 'livechat:saveCustomField' });
}

if (_id) {
Expand All @@ -26,6 +26,8 @@ Meteor.methods({
throw new Meteor.Error('error-invalid-custom-field', 'Custom Field Not found', { method: 'livechat:saveCustomField' });
}
}
return LivechatCustomField.createOrUpdateCustomField(_id, customFieldData.field, customFieldData.label, customFieldData.scope, customFieldData.visibility, { regexp: customFieldData.regexp });

const { field, label, scope, visibility, ...extraData } = customFieldData;
return LivechatCustomField.createOrUpdateCustomField(_id, field, label, scope, visibility, { ...extraData });
},
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<template name="livechatCustomFieldsAdditionalForm">
<div class="input-line">
<label>{{_ "Required"}}</label>
<div>
<select name="required" class="rc-input__element additional-field">
<option value="false" selected="{{$eq customField.required false}}">{{_ "No"}}</option>
<option value="true" selected="{{$eq customField.required true}}">{{_ "Yes"}}</option>
</select>
</div>
</div>
<div class="input-line">
<label>{{_ "Type"}}</label>
<div>
<select name="type" class="rc-input__element additional-field">
<option value="input" selected="{{$eq customField.type 'input'}}">{{_ "Input"}}</option>
<option value="select" selected="{{$eq customField.type 'select'}}">{{_ "Select"}}</option>
</select>
</div>
</div>
<div class="input-line">
<label>{{_ "Default_value"}}</label>
<div>
<input type="text" class="rc-input__element additional-field" name="defaultValue" value="{{customField.defaultValue}}" placeholder="{{_ "Default_value"}}" />
</div>
</div>
<div class="input-line">
<label>{{_ "Options"}}</label>
<div>
<input type="text" class="rc-input__element additional-field" name="options" disabled="{{$neq customField.type 'select'}}" value="{{customField.options}}" placeholder="{{_ "Livechat_custom_fields_options_placeholder"}}" />
</div>
</div>
<div class="input-line">
<label>{{_ "Public"}}</label>
<div>
<select name="public" class="rc-input__element additional-field" disabled="{{$eq customField.visibility 'hidden'}}">
<option value="false" selected="{{$eq customField.public false}}">{{_ "No"}}</option>
<option value="true" selected="{{$eq customField.public true}}">{{_ "Yes"}}</option>
</select>
</div>
<div class="settings-description secondary-font-color">{{{_ "Livechat_custom_fields_public_description"}}}</div>
</div>
</template>
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { ReactiveVar } from 'meteor/reactive-var';
import { Template } from 'meteor/templating';
import toastr from 'toastr';

import './livechatCustomFieldsAdditionalForm.html';
import { t } from '../../../../../../../app/utils/client';

Template.livechatCustomFieldsAdditionalForm.helpers({
customField() {
return Template.instance().customField.get();
},
});

Template.livechatCustomFieldsAdditionalForm.onCreated(function() {
this.customField = new ReactiveVar({});

this.autorun(() => {
// To make this template reactive we expect a ReactiveVar through the data property,
// because the parent form may not be rerender, only the dynamic template data
this.customField.set({ ...this.data.get() });
});
});

Template.livechatCustomFieldsAdditionalForm.events({
'change .additional-field'(e, instance) {
const { target: { name, value } } = e;
instance.customField.set({ ...instance.customField.get(), [name]: value });
},

'blur [name="options"]'(e) {
const { currentTarget: { value } } = e;
if (value.trim() !== '' && !/^([a-zA-Z0-9-_ ]+)(,\s*[a-zA-Z0-9-_ ]+)*$/i.test(value)) {
toastr.error(t('error-invalid-value'));
e.currentTarget.focus();
}
},
});
Loading