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

[NEW][ENTERPRISE] Omnichannel multiple business hours #17947

Merged
merged 63 commits into from
Jun 22, 2020
Merged
Show file tree
Hide file tree
Changes from 58 commits
Commits
Show all changes
63 commits
Select commit Hold shift + click to select a range
f5c6d6d
Change omnichannel office hours data structure
MarcosSpessatto Jun 4, 2020
387e801
Rename omnichannel office hours templates
MarcosSpessatto Jun 4, 2020
6d4b2e6
Renaming things from office hour to business hour
MarcosSpessatto Jun 5, 2020
a1adbbd
Partially remove LivechatOfficeHour model
MarcosSpessatto Jun 5, 2020
4803883
remove obsolete setting
MarcosSpessatto Jun 5, 2020
ebd93a9
Remove settings from the template
MarcosSpessatto Jun 5, 2020
5a911b3
Add support to cron jobs on omnichannel business hours
MarcosSpessatto Jun 9, 2020
acf7b70
Merge branch 'develop' into improvements/refactor-livechat-office-hours
MarcosSpessatto Jun 9, 2020
4f93201
Add dynamic template to omnichannel business hours
MarcosSpessatto Jun 9, 2020
7d3e345
Merge branch 'develop' into improvements/refactor-livechat-office-hours
MarcosSpessatto Jun 9, 2020
d94d750
Open and close business hours
MarcosSpessatto Jun 12, 2020
d3e2670
Remove references to LivechatOfficeHours
MarcosSpessatto Jun 12, 2020
bb4c82b
Merge branch 'develop' into improvements/refactor-livechat-office-hours
MarcosSpessatto Jun 12, 2020
25c4d88
Open and close business hour automatically on startup and setting cha…
MarcosSpessatto Jun 15, 2020
1becb7c
Merge branch 'develop' into improvements/refactor-livechat-office-hours
MarcosSpessatto Jun 15, 2020
a2b2c9b
Improve open and close business hours process
MarcosSpessatto Jun 16, 2020
c496ca2
Removing separated interfaces
MarcosSpessatto Jun 16, 2020
388afa0
Merge branch 'develop' into improvements/refactor-livechat-office-hours
MarcosSpessatto Jun 16, 2020
e0a0021
Merge branch 'develop' into improvements/refactor-livechat-office-hours
MarcosSpessatto Jun 17, 2020
0b05ad8
Merge branch 'develop' into improvements/refactor-livechat-office-hours
MarcosSpessatto Jun 17, 2020
9371c3d
Apply suggestions from review
MarcosSpessatto Jun 17, 2020
83e32f5
Merge branch 'develop' into improvements/refactor-livechat-office-hours
MarcosSpessatto Jun 17, 2020
c890eb1
Basic configuration to support multiple business hours
MarcosSpessatto Jun 18, 2020
5b866a3
Basic business hour insert and update operations
MarcosSpessatto Jun 18, 2020
931fa51
Remove business hour
MarcosSpessatto Jun 18, 2020
19e6288
Open multiple business hours automatically when the server starts
MarcosSpessatto Jun 19, 2020
5f2bd37
Fix leak of business logic
MarcosSpessatto Jun 19, 2020
1e133e5
Open multiple business hours
MarcosSpessatto Jun 19, 2020
4c3cda8
Close multiple business hour
MarcosSpessatto Jun 19, 2020
7765960
Merge branch 'develop' into improvements/refactor-livechat-office-hours
MarcosSpessatto Jun 19, 2020
36f3673
Merge branch 'develop' into improvements/refactor-livechat-office-hours
MarcosSpessatto Jun 19, 2020
7e48e71
Changing pt-br term
MarcosSpessatto Jun 19, 2020
c22bd6d
Merge branch 'develop' into improvements/refactor-livechat-office-hours
MarcosSpessatto Jun 19, 2020
96bf764
General improvements on business hour layout
MarcosSpessatto Jun 19, 2020
572ecea
Merge remote-tracking branch 'origin/improvements/refactor-livechat-o…
MarcosSpessatto Jun 19, 2020
caeb16e
UI improvements
MarcosSpessatto Jun 19, 2020
4e74a73
Merge branch 'develop' into improvements/refactor-livechat-office-hours
MarcosSpessatto Jun 19, 2020
243cf63
Apply suggestions from review
MarcosSpessatto Jun 19, 2020
113f364
Add departments conditions
MarcosSpessatto Jun 20, 2020
c76348c
Close business hours when remove department
MarcosSpessatto Jun 20, 2020
5554c67
Merge branch 'develop' into improvements/refactor-livechat-office-hours
MarcosSpessatto Jun 20, 2020
4e6bd27
Remove online verification
MarcosSpessatto Jun 20, 2020
87ff84c
Refactor business hour to new data structure
MarcosSpessatto Jun 20, 2020
e788448
Merge branch 'develop' into improvements/refactor-livechat-office-hours
MarcosSpessatto Jun 20, 2020
ef33a6a
Merge branch 'develop' into improvements/refactor-livechat-office-hours
MarcosSpessatto Jun 20, 2020
b724b3f
Change data structure
MarcosSpessatto Jun 21, 2020
ad3f2d7
Ignore if collection does not exist
MarcosSpessatto Jun 21, 2020
704c2e1
Merge branch 'develop' into improvements/refactor-livechat-office-hours
MarcosSpessatto Jun 21, 2020
25218ee
Merge remote-tracking branch 'origin/improvements/refactor-livechat-o…
MarcosSpessatto Jun 21, 2020
b5f3cc8
Merge branch 'develop' into improvements/refactor-livechat-office-hours
renatobecker Jun 21, 2020
ed5f027
Merge branch 'improvements/refactor-livechat-office-hours' into feat/…
MarcosSpessatto Jun 21, 2020
c6eb73f
Fix businessHourManager startup.
renatobecker Jun 21, 2020
e87e2aa
Remove console log.
renatobecker Jun 21, 2020
db5a462
Fix Multiple business hours
MarcosSpessatto Jun 21, 2020
448ac62
Merge remote-tracking branch 'origin/feat/multiple-business-hours' in…
MarcosSpessatto Jun 21, 2020
805a518
Merge branch 'develop' into feat/multiple-business-hours
MarcosSpessatto Jun 21, 2020
478019d
Merge branch 'develop' into feat/multiple-business-hours
renatobecker Jun 21, 2020
a1caa5e
open/close business hours when removing/adding agent to department.
renatobecker Jun 21, 2020
ff81137
Improve code
MarcosSpessatto Jun 21, 2020
32c2e02
Fix error on creating department.
renatobecker Jun 21, 2020
24e7b39
Add set to default when it is the last department
MarcosSpessatto Jun 22, 2020
f8fc7fb
Fix wrong condition
MarcosSpessatto Jun 22, 2020
8d72b7a
Merge branch 'develop' into feat/multiple-business-hours
renatobecker Jun 22, 2020
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
2 changes: 1 addition & 1 deletion app/livechat/client/route.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export const livechatManagerRoutes = FlowRouter.group({
name: 'livechat-manager',
});

const load = () => import('./views/admin');
export const load = () => import('./views/admin');

AccountBox.addRoute({
name: 'livechat-dashboard',
Expand Down
29 changes: 17 additions & 12 deletions app/livechat/client/views/app/business-hours/BusinessHours.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,33 @@
import { Meteor } from 'meteor/meteor';

import { IBusinessHour } from './IBusinessHour';
import { SingleBusinessHour } from './Single';
import { callbacks } from '../../../../../callbacks/client';
import { ILivechatBusinessHour } from '../../../../../../definition/ILivechatBusinessHour';

class BusinessHoursManager {
private businessHour: IBusinessHour;

onStartBusinessHourManager(businessHour: IBusinessHour): void {
this.registerBusinessHour(businessHour);
constructor(businessHour: IBusinessHour) {
this.setBusinessHourManager(businessHour);
}

setBusinessHourManager(businessHour: IBusinessHour): void {
this.registerBusinessHourMethod(businessHour);
}

registerBusinessHour(businessHour: IBusinessHour): void {
registerBusinessHourMethod(businessHour: IBusinessHour): void {
this.businessHour = businessHour;
}

getTemplate(): string {
return this.businessHour.getView();
}
}

export const businessHourManager = new BusinessHoursManager();
shouldShowCustomTemplate(businessHourData: ILivechatBusinessHour): boolean {
return this.businessHour.shouldShowCustomTemplate(businessHourData);
}

shouldShowBackButton(): boolean {
return this.businessHour.shouldShowBackButton();
}
}

Meteor.startup(() => {
const { BusinessHourClass } = callbacks.run('on-business-hour-start', { BusinessHourClass: SingleBusinessHour });
businessHourManager.onStartBusinessHourManager(new BusinessHourClass() as IBusinessHour);
});
export const businessHourManager = new BusinessHoursManager(new SingleBusinessHour() as IBusinessHour);
4 changes: 4 additions & 0 deletions app/livechat/client/views/app/business-hours/IBusinessHour.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
import { ILivechatBusinessHour } from '../../../../../../definition/ILivechatBusinessHour';

export interface IBusinessHour {
getView(): string;
shouldShowCustomTemplate(businessHourData: ILivechatBusinessHour): boolean;
shouldShowBackButton(): boolean;
}
8 changes: 8 additions & 0 deletions app/livechat/client/views/app/business-hours/Single.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,12 @@ export class SingleBusinessHour implements IBusinessHour {
getView(): string {
return 'livechatBusinessHoursForm';
}

shouldShowCustomTemplate(): boolean {
return false;
}

shouldShowBackButton(): boolean {
return false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,20 @@
{{#requiresPermission 'view-livechat-business-hours'}}
<form class="rocket-form" id="businessHoursForm">

{{#if customFieldsTemplate}}
{{> Template.dynamic template=customFieldsTemplate data=data }}
{{/if}}

<!-- days open -->
<fieldset>
<legend>{{_ "Open_days_of_the_week"}}</legend>
{{#each day in days}}
{{#if open day}}
<label class="dayOpenCheck"><input type="checkbox" name={{openName day}} checked>{{name day}}
</label>
<label class="dayOpenCheck"><input type="checkbox" name={{openName day}} checked>{{name
day}}</label>
{{else}}
<label class="dayOpenCheck"><input type="checkbox" name={{openName day}}>{{name day}}</label>
<label class="dayOpenCheck"><input type="checkbox" name={{openName day}}>{{name day}}
</label>
{{/if}}
{{/each}}
</fieldset>
Expand All @@ -29,14 +34,16 @@ <h1><strong>{{name day}}</strong></h1>
<tr>
<td>
<div style="margin-right:30px">
<input type="time" class="preview-settings rc-input__element" name={{startName
day}} id={{startName day}} value={{start day}} style="width=100px;">
<input type="time" class="preview-settings rc-input__element"
name={{startName day}} id={{startName day}} value={{start day}}
style="width=100px;">
</div>
</td>
<td>
<div style="margin-right:30px">
<input type="time" class="preview-settings rc-input__element" name={{finishName
day}} id={{finishName day}} value={{finish day}} style="width=100px;">
<input type="time" class="preview-settings rc-input__element"
name={{finishName day}} id={{finishName day}} value={{finish day}}
style="width=100px;">
</div>
</td>
</tr>
Expand All @@ -46,8 +53,9 @@ <h1><strong>{{name day}}</strong></h1>
</fieldset>


<div class="rc-button__group submit">
<button class="rc-button rc-button--primary"><i class="icon-floppy"></i>{{_ "Save"}}</button>
<div class="rc-button__group">
{{#if showBackButton}}<button class="rc-button back" type="button"><i class="icon-left-big"></i><span>{{_ "Back"}}</span></button>{{/if}}
<button class="rc-button rc-button--primary save"><i class="icon-floppy"></i><span>{{_ "Save"}}</span></button>
</div>
</form>
{{/requiresPermission}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,14 @@ import { Meteor } from 'meteor/meteor';
import { ReactiveVar } from 'meteor/reactive-var';
import { Template } from 'meteor/templating';
import { TAPi18n } from 'meteor/rocketchat:tap-i18n';
import { FlowRouter } from 'meteor/kadira:flow-router';
import toastr from 'toastr';
import moment from 'moment';

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

Template.livechatBusinessHoursForm.helpers({
days() {
Expand All @@ -33,6 +36,18 @@ Template.livechatBusinessHoursForm.helpers({
open(day) {
return Template.instance().dayVars[day.day].open.get();
},
customFieldsTemplate() {
if (!businessHourManager.shouldShowCustomTemplate(Template.instance().businessHour.get())) {
return;
}
return getCustomFormTemplate('livechatBusinessHoursForm');
},
showBackButton() {
return businessHourManager.shouldShowBackButton();
},
data() {
return Template.instance().businessHour;
},
});

const splitDayAndPeriod = (value) => value.split('_');
Expand Down Expand Up @@ -69,6 +84,12 @@ Template.livechatBusinessHoursForm.events({
instance[e.currentTarget.name].set(value);
}
},

'click button.back'(e/* , instance*/) {
e.preventDefault();
FlowRouter.go('livechat-business-hours');
},

'submit .rocket-form'(e, instance) {
e.preventDefault();

Expand All @@ -87,14 +108,23 @@ Template.livechatBusinessHoursForm.events({
});
}
}
Meteor.call('livechat:saveBusinessHour', {

const businessHourData = {
...instance.businessHour.get(),
workHours: days,
}, function(err /* ,result*/) {
};

instance.$('.customFormField').each((i, el) => {
const elField = instance.$(el);
const name = elField.attr('name');
businessHourData[name] = elField.val();
});
Meteor.call('livechat:saveBusinessHour', businessHourData, function(err /* ,result*/) {
if (err) {
return handleError(err);
}
toastr.success(t('Business_hours_updated'));
FlowRouter.go('livechat-business-hours');
});
},
});
Expand All @@ -112,7 +142,6 @@ const createDefaultBusinessHour = () => {
};
};


Template.livechatBusinessHoursForm.onCreated(async function() {
this.dayVars = createDefaultBusinessHour().workHours.reduce((acc, day) => {
acc[day.day] = {
Expand All @@ -124,21 +153,28 @@ Template.livechatBusinessHoursForm.onCreated(async function() {
}, {});
this.businessHour = new ReactiveVar({});

const { businessHour } = await APIClient.v1.get('livechat/business-hour');
this.businessHour.set({
...createDefaultBusinessHour(),
});
if (businessHour) {
this.businessHour.set(businessHour);
businessHour.workHours.forEach((d) => {
if (businessHour.timezone.name) {
this.dayVars[d.day].start.set(moment.utc(d.start.utc.time, 'HH:mm').tz(businessHour.timezone.name).format('HH:mm'));
this.dayVars[d.day].finish.set(moment.utc(d.finish.utc.time, 'HH:mm').tz(businessHour.timezone.name).format('HH:mm'));
} else {
this.dayVars[d.day].start.set(moment.utc(d.start.utc.time, 'HH:mm').local().format('HH:mm'));
this.dayVars[d.day].finish.set(moment.utc(d.finish.utc.time, 'HH:mm').local().format('HH:mm'));
}
this.dayVars[d.day].open.set(d.open);
});
}
this.autorun(async () => {
const id = FlowRouter.getParam('_id');
let url = 'livechat/business-hour';
if (id) {
url += `?_id=${ id }`;
}
const { businessHour } = await APIClient.v1.get(url);
if (businessHour) {
this.businessHour.set(businessHour);
businessHour.workHours.forEach((d) => {
if (businessHour.timezone.name) {
this.dayVars[d.day].start.set(moment.utc(d.start.utc.time, 'HH:mm').tz(businessHour.timezone.name).format('HH:mm'));
this.dayVars[d.day].finish.set(moment.utc(d.finish.utc.time, 'HH:mm').tz(businessHour.timezone.name).format('HH:mm'));
} else {
this.dayVars[d.day].start.set(moment.utc(d.start.utc.time, 'HH:mm').local().format('HH:mm'));
this.dayVars[d.day].finish.set(moment.utc(d.finish.utc.time, 'HH:mm').local().format('HH:mm'));
}
this.dayVars[d.day].open.set(d.open);
});
}
});
});
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
<template name="livechatMainBusinessHours">
{{#requiresPermission 'view-livechat-business-hours'}}
<div class="livechat-businessHours-div">
{{> Template.dynamic template=getTemplate}}
</div>
{{> Template.dynamic template=getTemplate}}
{{/requiresPermission}}
</template>
2 changes: 2 additions & 0 deletions app/livechat/client/views/app/customTemplates/register.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,6 @@ export const customFormTemplate = new Map();

export const addCustomFormTemplate = (form, customTemplateName) => customFormTemplate.set(form, customTemplateName);

export const removeCustomTemplate = (form) => customFormTemplate.delete(form);

export const getCustomFormTemplate = (form) => customFormTemplate.get(form);
6 changes: 5 additions & 1 deletion app/livechat/server/business-hour/AbstractBusinessHour.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,16 @@ import { LivechatBusinessHours, Users } from '../../../models/server/raw';
export interface IBusinessHour {
saveBusinessHour(businessHourData: ILivechatBusinessHour): Promise<void>;
allowAgentChangeServiceStatus(agentId: string): Promise<boolean>;
getBusinessHour(id: string): Promise<ILivechatBusinessHour>;
getBusinessHour(id: string): Promise<ILivechatBusinessHour | undefined>;
findHoursToCreateJobs(): Promise<IWorkHoursForCreateCronJobs[]>;
openBusinessHoursByDayHour(day: string, hour: string): Promise<void>;
closeBusinessHoursByDayAndHour(day: string, hour: string): Promise<void>;
removeBusinessHoursFromUsers(): Promise<void>;
removeBusinessHourById(id: string): Promise<void>;
removeBusinessHourFromUsers(departmentId: string, businessHourId: string): Promise<void>;
openBusinessHoursIfNeeded(): Promise<void>;
removeBusinessHourFromUsersByIds(userIds: Array<string>, businessHourId: string): Promise<void>;
addBusinessHourToUsersByIds(userIds: Array<string>, businessHourId: string): Promise<void>;
}

export abstract class AbstractBusinessHour {
Expand Down
53 changes: 46 additions & 7 deletions app/livechat/server/business-hour/BusinessHourManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { ILivechatBusinessHour } from '../../../../definition/ILivechatBusinessH
import { ICronJobs } from '../../../utils/server/lib/cron/Cronjobs';
import { IBusinessHour } from './AbstractBusinessHour';
import { settings } from '../../../settings/server';
import { ILivechatDepartment } from '../../../../definition/ILivechatDepartment';

const cronJobDayDict: Record<string, number> = {
Sunday: 0,
Expand Down Expand Up @@ -36,16 +37,25 @@ export class BusinessHourManager {
this.businessHour = businessHour;
}

async dispatchOnStartTasks(): Promise<void> {
await this.createCronJobsForWorkHours();
await this.openBusinessHoursIfNeeded();
}

async dispatchOnCloseTasks(): Promise<void> {
await this.removeBusinessHoursFromAgents();
await this.removeCronJobs();
}

async saveBusinessHour(businessHourData: ILivechatBusinessHour): Promise<void> {
await this.businessHour.saveBusinessHour(businessHourData);
if (!settings.get('Livechat_enable_business_hours')) {
return;
}
await this.createCronJobsForWorkHours();
await this.openBusinessHoursIfNeeded();
await this.dispatchOnStartTasks();
}

async getBusinessHour(id?: string): Promise<ILivechatBusinessHour> {
async getBusinessHour(id?: string): Promise<ILivechatBusinessHour | undefined> {
return this.businessHour.getBusinessHour(id as string);
}

Expand All @@ -56,11 +66,40 @@ export class BusinessHourManager {
return this.businessHour.allowAgentChangeServiceStatus(agentId);
}

removeCronJobs(): void {
async removeBusinessHourIdFromUsers(department: ILivechatDepartment): Promise<void> {
return this.businessHour.removeBusinessHourFromUsers(department._id, department.businessHourId as string);
}

async removeBusinessHourById(id: string): Promise<void> {
await this.businessHour.removeBusinessHourById(id);
if (!settings.get('Livechat_enable_business_hours')) {
return;
}
await this.createCronJobsForWorkHours();
await this.openBusinessHoursIfNeeded();
}

async removeBusinessHourFromUsersByIds(userIds: Array<string>, businessHourId: string): Promise<void> {
if (!settings.get('Livechat_enable_business_hours')) {
return;
}

await this.businessHour.removeBusinessHourFromUsersByIds(userIds, businessHourId);
}

async addBusinessHourToUsersByIds(userIds: Array<string>, businessHourId: string): Promise<void> {
if (!settings.get('Livechat_enable_business_hours')) {
return;
}

await this.businessHour.addBusinessHourToUsersByIds(userIds, businessHourId);
}

private removeCronJobs(): void {
this.cronJobsCache.forEach((jobName) => this.cronJobs.remove(jobName));
}

async createCronJobsForWorkHours(): Promise<void> {
private async createCronJobsForWorkHours(): Promise<void> {
this.removeCronJobs();
this.clearCronJobsCache();
const workHours = await this.businessHour.findHoursToCreateJobs();
Expand All @@ -83,11 +122,11 @@ export class BusinessHourManager {
});
}

async removeBusinessHoursFromAgents(): Promise<void> {
private async removeBusinessHoursFromAgents(): Promise<void> {
return this.businessHour.removeBusinessHoursFromUsers();
}

async openBusinessHoursIfNeeded(): Promise<void> {
private async openBusinessHoursIfNeeded(): Promise<void> {
return this.businessHour.openBusinessHoursIfNeeded();
}

Expand Down
Loading