define("modules/crm/views/scheduler/scheduler", ["exports", "view", "vis-data", "vis-timeline", "moment", "jquery"], function (_exports, _view, _visData, _visTimeline, _moment, _jquery) {
"use strict";
Object.defineProperty(_exports, "__esModule", {
value: true
});
_exports.default = void 0;
_view = _interopRequireDefault(_view);
_moment = _interopRequireDefault(_moment);
_jquery = _interopRequireDefault(_jquery);
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM – Open Source CRM application.
* Copyright (C) 2014-2026 EspoCRM, Inc.
* Website: https://www.espocrm.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License version 3.
*
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
class SchedulerView extends _view.default {
// language=Handlebars
templateContent = `
').addClass('revert-margin').text(this.translate('No Data')));
}
afterRender() {
let $timeline = this.$timeline = this.$el.find('.timeline');
this.noDataShown = false;
this.$timeline.empty();
this.initGroupsDataSet();
this.initDates();
if (!$timeline.get(0)) {
return;
}
$timeline.get(0).innerHTML = '';
if (!this.start || !this.end || !this.userIdList.length) {
this.showNoData();
this.trigger('no-data');
return;
}
this.destroyTimeline();
if (this.lastHeight) {
$timeline.css('min-height', this.lastHeight + 'px');
}
this.fetch(this.start, this.end, eventList => {
let itemsDataSet = new _visData.DataSet(eventList);
// noinspection SpellCheckingInspection
this.timeline = new _visTimeline.Timeline($timeline.get(0), itemsDataSet, this.groupsDataSet, {
dataAttributes: 'all',
start: this.start.toDate(),
end: this.end.toDate(),
rollingMode: {
follow: false // fixes slow render
},
moment: date => {
let m = (0, _moment.default)(date);
if (date && date.noTimeZone) {
return m;
}
return m.tz(this.getDateTime().getTimeZone());
},
xss: {
filterOptions: {
onTag: (tag, html) => html
}
},
format: this.getFormatObject(),
zoomable: false,
moveable: true,
orientation: 'top',
groupEditable: false,
editable: {
add: false,
updateTime: false,
updateGroup: false,
remove: false
},
showCurrentTime: true,
locales: {
myLocale: {
current: this.translate('current', 'labels', 'Calendar'),
time: this.translate('time', 'labels', 'Calendar')
}
},
locale: 'myLocale',
margin: {
item: {
vertical: 12
},
axis: 6
}
});
$timeline.css('min-height', '');
// noinspection SpellCheckingInspection
this.timeline.on('rangechanged', e => {
e.skipClick = true;
this.blockClick = true;
setTimeout(function () {
this.blockClick = false;
}.bind(this), 100);
this.start = (0, _moment.default)(e.start);
this.end = (0, _moment.default)(e.end);
this.updateRange();
});
setTimeout(() => {
this.lastHeight = $timeline.height();
}, 500);
});
}
updateEvent() {
let eventList = Espo.Utils.cloneDeep(this.busyEventList);
let convertedEventList = this.convertEventList(eventList);
this.addEvent(convertedEventList);
let itemsDataSet = new _visData.DataSet(convertedEventList);
this.timeline.setItems(itemsDataSet);
}
updateRange() {
if (this.start.unix() < this.fetchedStart.unix() + this.rangeMarginThreshold || this.end.unix() > this.fetchedEnd.unix() - this.rangeMarginThreshold) {
this.runFetch();
}
}
initDates(update) {
this.start = null;
this.end = null;
let startS = this.model.get(this.startField);
let endS = this.model.get(this.endField);
if (this.model.get('isAllDay')) {
startS = this.model.get(this.startDateField);
endS = this.model.get(this.endDateField);
}
if (!startS || !endS) {
return;
}
if (this.model.get('isAllDay')) {
this.eventStart = _moment.default.tz(startS, this.getDateTime().getTimeZone());
this.eventEnd = _moment.default.tz(endS, this.getDateTime().getTimeZone());
this.eventEnd.add(1, 'day');
} else {
this.eventStart = _moment.default.utc(startS).tz(this.getDateTime().getTimeZone());
this.eventEnd = _moment.default.utc(endS).tz(this.getDateTime().getTimeZone());
}
let diff = this.eventEnd.diff(this.eventStart, 'hours');
this.start = this.eventStart.clone();
this.end = this.eventEnd.clone();
if (diff < 0) {
this.end = this.start.clone();
}
if (diff < 1) {
diff = 1;
}
this.start.add(-diff * this.rangeMultiplierLeft, 'hours');
this.end.add(diff * this.rangeMultiplierRight, 'hours');
this.start.startOf('hour');
this.end.endOf('hour');
if (!update) {
this.fetchedStart = null;
this.fetchedEnd = null;
}
}
runFetch() {
this.fetch(this.start, this.end, eventList => {
let itemsDataSet = new _visData.DataSet(eventList);
this.timeline.setItems(itemsDataSet);
});
}
fetch(from, to, callback) {
from = from.clone().add(-1 * this.leftMargin, 'seconds');
to = to.clone().add(this.rightMargin, 'seconds');
let fromString = from.utc().format(this.getDateTime().internalDateTimeFormat);
let toString = to.utc().format(this.getDateTime().internalDateTimeFormat);
let url = 'Timeline/busyRanges?from=' + fromString + '&to=' + toString + '&userIdList=' + encodeURIComponent(this.userIdList.join(',')) + '&entityType=' + this.model.entityType;
if (this.model.id) {
url += '&entityId=' + this.model.id;
}
Espo.Ajax.getRequest(url).then(data => {
this.fetchedStart = from.clone();
this.fetchedEnd = to.clone();
let eventList = [];
for (let userId in data) {
let itemList = /** @type {Object.
} */data[userId].filter(item => !item.isBusyRange).concat(data[userId].filter(item => item.isBusyRange));
itemList.forEach(item => {
item.userId = userId;
eventList.push(item);
});
}
this.busyEventList = Espo.Utils.cloneDeep(eventList);
let convertedEventList = this.convertEventList(eventList);
this.addEvent(convertedEventList);
callback(convertedEventList);
});
}
addEvent(list) {
this.getCurrentItemList().forEach(item => {
list.push(item);
});
}
getCurrentItemList() {
let list = [];
let o = {
start: this.eventStart.clone(),
end: this.eventEnd.clone(),
type: 'background',
style: 'z-index: 4; opacity: 0.6;',
className: 'event-range'
};
let color = this.getColorFromScopeName(this.model.entityType);
if (color) {
o.style += '; border-color: ' + color;
let rgb = this.hexToRgb(color);
o.style += '; background-color: rgba(' + rgb.r + ', ' + rgb.g + ', ' + rgb.b + ', 0.01)';
}
this.userIdList.forEach(id => {
let c = Espo.Utils.clone(o);
c.group = id;
c.id = 'event-' + id;
list.push(c);
});
return list;
}
convertEventList(list) {
let resultList = [];
list.forEach(item => {
let event = this.convertEvent(item);
if (!event) {
return;
}
resultList.push(event);
});
return resultList;
}
/**
* @param {Object.} o
* @return {Object}
*/
convertEvent(o) {
let event;
if (o.isBusyRange) {
event = {
className: 'busy',
group: o.userId,
'date-start': o.dateStart,
'date-end': o.dateEnd,
type: 'background'
};
} else if (o.isWorkingRange) {
event = {
className: 'working',
group: o.userId,
'date-start': o.dateStart,
'date-end': o.dateEnd,
type: 'background'
};
} else if (o.isNonWorkingRange) {
event = {
className: 'non-working',
group: o.userId,
'date-start': o.dateStart,
'date-end': o.dateEnd,
type: 'background'
};
let color = this.colors['bg'];
event.style = 'background-color:' + color + ';';
event.style += 'border-color:' + color + ';';
}
if (o.dateStart) {
if (!o.dateStartDate) {
event.start = this.getDateTime().toMoment(o.dateStart);
} else {
event.start = _moment.default.tz(o.dateStartDate, this.getDateTime().getTimeZone());
}
}
if (o.dateEnd) {
if (!o.dateEndDate) {
event.end = this.getDateTime().toMoment(o.dateEnd);
} else {
event.end = _moment.default.tz(o.dateEndDate, this.getDateTime().getTimeZone());
}
}
if (o.isBusyRange || o.isNonWorkingRange) {
return event;
}
}
initGroupsDataSet() {
let list = [];
let userIdList = Espo.Utils.clone(this.model.get(this.usersField + 'Ids') || []);
let assignedUserId = this.model.get(this.assignedUserField + 'Id');
let names = this.model.get(this.usersField + 'Names') || {};
if (!this.eventAssignedUserIsAttendeeDisabled && assignedUserId) {
if (!~userIdList.indexOf(assignedUserId)) {
userIdList.unshift(assignedUserId);
}
names[assignedUserId] = this.model.get(this.assignedUserField + 'Name');
}
this.userIdList = userIdList;
userIdList.forEach((id, i) => {
list.push({
id: id,
content: this.getGroupContent(id, names[id] || id),
order: i
});
});
this.groupsDataSet = new _visData.DataSet(list);
}
getGroupContent(id, name) {
if (this.calendarType === 'single') {
return (0, _jquery.default)('').text(name).get(0).outerHTML;
}
let avatarHtml = this.getAvatarHtml(id);
if (avatarHtml) {
avatarHtml += ' ';
}
return (0, _jquery.default)('').append((0, _jquery.default)(avatarHtml), (0, _jquery.default)('').attr('data-id', id).addClass('group-title').text(name)).get(0).innerHTML;
}
getAvatarHtml(id) {
if (this.getConfig().get('avatarsDisabled')) {
return '';
}
let t;
let cache = this.getCache();
if (cache) {
t = cache.get('app', 'timestamp');
} else {
t = Date.now();
}
// noinspection HtmlRequiredAltAttribute,RequiredAttributes
return (0, _jquery.default)('
').addClass('avatar avatar-link').attr('width', '16').attr('src', this.getBasePath() + '?entryPoint=avatar&size=small&id=' + id + '&t=' + t).get(0).outerHTML;
}
getFormatObject() {
return {
minorLabels: {
millisecond: 'SSS',
second: 's',
minute: this.getDateTime().getTimeFormat(),
hour: this.getDateTime().getTimeFormat(),
weekday: 'ddd D',
day: 'D',
month: 'MMM',
year: 'YYYY'
},
majorLabels: {
millisecond: this.getDateTime().getTimeFormat() + ' ss',
second: this.getDateTime().getReadableDateFormat() + ' HH:mm',
minute: 'ddd D MMMM',
hour: 'ddd D MMMM',
weekday: 'MMMM YYYY',
day: 'MMMM YYYY',
month: 'YYYY',
year: ''
}
};
}
getColorFromScopeName(scope) {
return this.getMetadata().get(['clientDefs', scope, 'color']) || this.getMetadata().get(['clientDefs', 'Calendar', 'colors', scope]);
}
hexToRgb(hex) {
let result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
return result ? {
r: parseInt(result[1], 16),
g: parseInt(result[2], 16),
b: parseInt(result[3], 16)
} : null;
}
}
var _default = _exports.default = SchedulerView;
});
define("modules/crm/views/calendar/timeline", ["exports", "view", "vis-data", "vis-timeline", "moment", "jquery", "helpers/record-modal", "crm:views/calendar/modals/shared-options"], function (_exports, _view, _visData, _visTimeline, _moment, _jquery, _recordModal, _sharedOptions) {
"use strict";
Object.defineProperty(_exports, "__esModule", {
value: true
});
_exports.default = void 0;
_view = _interopRequireDefault(_view);
_moment = _interopRequireDefault(_moment);
_jquery = _interopRequireDefault(_jquery);
_recordModal = _interopRequireDefault(_recordModal);
_sharedOptions = _interopRequireDefault(_sharedOptions);
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM – Open Source CRM application.
* Copyright (C) 2014-2026 EspoCRM, Inc.
* Website: https://www.espocrm.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License version 3.
*
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
/** @module modules/crm/views/calendar/timeline */
class TimelineView extends _view.default {
template = 'crm:calendar/timeline';
eventAttributes = [];
colors = {};
/**
* @private
* @type {string[]}
*/
allDayScopeList;
/**
* @private
* @type {string[]}
*/
scopeList = ['Meeting', 'Call', 'Task'];
/**
* @private
* @type {string[]}
*/
enabledScopeList;
/**
* @private
* @type {string[]}
*/
onlyDateScopeList;
header = true;
modeList = [];
defaultMode = 'timeline';
maxRange = 120;
rangeMarginThreshold = 12 * 3600;
leftMargin = 24 * 3600;
rightMargin = 48 * 3600;
calendarType = 'single';
calendarTypeList = ['single', 'shared'];
zoomPercentage = 1;
/** @type {Timeline} */
timeline;
events = {
/** @this TimelineView */
'click button[data-action="today"]': function () {
this.actionToday();
},
/** @this TimelineView */
'click [data-action="mode"]': function (e) {
const mode = (0, _jquery.default)(e.currentTarget).data('mode');
this.selectMode(mode);
},
/** @this TimelineView */
'click [data-action="refresh"]': function () {
this.actionRefresh();
},
/** @this TimelineView */
'click [data-action="toggleScopeFilter"]': function (e) {
const $target = (0, _jquery.default)(e.currentTarget);
const filterName = $target.data('name');
const $check = $target.find('.filter-check-icon');
if ($check.hasClass('hidden')) {
$check.removeClass('hidden');
} else {
$check.addClass('hidden');
}
e.stopPropagation(e);
this.toggleScopeFilter(filterName);
},
/** @this TimelineView */
'click [data-action="toggleCalendarType"]': function (e) {
const $target = (0, _jquery.default)(e.currentTarget);
const calendarType = $target.data('name');
$target.parent().parent().find('.calendar-type-check-icon').addClass('hidden');
const $check = $target.find('.calendar-type-check-icon');
if ($check.hasClass('hidden')) {
$check.removeClass('hidden');
}
$target.closest('.calendar-type-button-group').find('.calendar-type-label').text(this.getCalendarTypeLabel(calendarType));
const $showSharedCalendarOptions = this.$el.find('> .button-container button[data-action="showSharedCalendarOptions"]');
if (calendarType === 'shared') {
$showSharedCalendarOptions.removeClass('hidden');
} else {
$showSharedCalendarOptions.addClass('hidden');
}
this.selectCalendarType(calendarType);
},
/** @this TimelineView */
'click button[data-action="showSharedCalendarOptions"]': function () {
this.actionShowSharedCalendarOptions();
}
};
/**
* @param {{
* userId?: string,
* userName?: string|null,
* mode?: string|null,
* date?: string|null,
* $container?: JQuery,
* suppressLoadingAlert?: boolean,
* containerSelector?: string,
* enabledScopeList?: string[],
* calendarType?: string,
* userList?: string[],
* header?: boolean,
* onSave?: function(),
* }} options
*/
constructor(options) {
super(options);
this.options = options;
}
data() {
const calendarTypeDataList = this.getCalendarTypeDataList();
return {
mode: this.mode,
header: this.header,
calendarType: this.calendarType,
calendarTypeDataList: calendarTypeDataList,
calendarTypeSelectEnabled: calendarTypeDataList.length > 1,
calendarTypeLabel: this.getCalendarTypeLabel(this.calendarType),
isCustomViewAvailable: this.isCustomViewAvailable
};
}
setup() {
this.date = this.options.date || this.getDateTime().getToday();
this.mode = this.options.mode || this.defaultMode;
this.header = 'header' in this.options ? this.options.header : this.header;
this.$container = this.options.$container;
this.colors = Espo.Utils.clone(this.getMetadata().get('clientDefs.Calendar.colors') || this.colors || {});
this.modeList = this.getMetadata().get('clientDefs.Calendar.modeList') || this.modeList || [];
this.scopeList = this.getConfig().get('calendarEntityList') || Espo.Utils.clone(this.scopeList);
this.allDayScopeList = this.getMetadata().get('clientDefs.Calendar.allDayScopeList') ?? [];
this.scopeList.forEach(scope => {
if (this.getMetadata().get(`scopes.${scope}.calendarOneDay`) && !this.allDayScopeList.includes(scope)) {
this.allDayScopeList.push(scope);
}
});
this.colors = {
...this.colors,
...this.getHelper().themeManager.getParam('calendarColors')
};
this.isCustomViewAvailable = this.getAcl().getPermissionLevel('userCalendar') !== 'no';
if (this.options.userId) {
this.isCustomViewAvailable = false;
}
const scopeList = [];
this.scopeList.forEach(scope => {
if (this.getAcl().check(scope)) {
scopeList.push(scope);
}
});
this.scopeList = scopeList;
if (this.header) {
this.enabledScopeList = this.getStoredEnabledScopeList() || Espo.Utils.clone(this.scopeList);
} else {
this.enabledScopeList = this.options.enabledScopeList || Espo.Utils.clone(this.scopeList);
}
if (Object.prototype.toString.call(this.enabledScopeList) !== '[object Array]') {
this.enabledScopeList = [];
}
this.enabledScopeList.forEach(item => {
const color = this.getMetadata().get(['clientDefs', item, 'color']);
if (color) {
this.colors[item] = color;
}
});
this.onlyDateScopeList = this.scopeList.filter(scope => {
return this.getMetadata().get(`entityDefs.${scope}.fields.dateStart.type`) === 'date';
});
if (this.options.calendarType) {
this.calendarType = this.options.calendarType;
} else {
if (this.options.userId) {
this.calendarType = 'single';
} else {
this.calendarType = this.getStorage().get('calendar', 'timelineType') || 'shared';
}
}
if (this.getAcl().getPermissionLevel('userCalendar') === 'no') {
if (this.calendarType === 'shared') {
this.calendarType = 'single';
}
}
if (!~this.calendarTypeList.indexOf(this.calendarType)) {
this.calendarType = 'single';
}
if (this.header) {
this.createView('modeButtons', 'crm:views/calendar/mode-buttons', {
selector: '.mode-buttons',
isCustomViewAvailable: this.isCustomViewAvailable,
modeList: this.modeList,
scopeList: this.scopeList,
mode: this.mode
});
}
}
// noinspection JSUnusedGlobalSymbols
/**
* @return {import('./mode-buttons').default}
*/
getModeButtonsView() {
return this.getView('modeButtons');
}
selectMode(mode) {
this.trigger('change:mode', mode);
}
getCalendarTypeDataList() {
const list = [];
const o = {
type: 'single',
disabled: this.calendarType !== 'single',
label: this.getCalendarTypeLabel('single')
};
list.push(o);
if (this.options.userId) {
return list;
}
if (this.getAcl().getPermissionLevel('userCalendar') !== 'no') {
list.push({
type: 'shared',
label: this.getCalendarTypeLabel('shared'),
disabled: this.calendarType !== 'shared'
});
}
return list;
}
getCalendarTypeLabel(type) {
let label;
if (type === 'single') {
if (this.options.userId) {
label = this.options.userName || this.options.userId;
} else {
label = this.getUser().get('name');
}
label = this.getHelper().escapeString(label);
return label;
}
if (type === 'shared') {
return this.translate('Shared', 'labels', 'Calendar');
}
}
selectCalendarType(name) {
this.calendarType = name;
this.initUserList();
this.initGroupsDataSet();
this.timeline.setGroups(this.groupsDataSet);
this.runFetch();
this.getStorage().set('calendar', 'timelineType', name);
}
toggleScopeFilter(name) {
const index = this.enabledScopeList.indexOf(name);
if (!~index) {
this.enabledScopeList.push(name);
} else {
this.enabledScopeList.splice(index, 1);
}
this.storeEnabledScopeList(this.enabledScopeList);
this.runFetch();
}
getStoredEnabledScopeList() {
const key = 'calendarEnabledScopeList';
return this.getStorage().get('state', key) || null;
}
storeEnabledScopeList(enabledScopeList) {
const key = 'calendarEnabledScopeList';
this.getStorage().set('state', key, enabledScopeList);
}
getTitle() {
let title = '';
if (this.options.userId && this.options.userName) {
title += ' (' + this.options.userName + ')';
}
title = this.getHelper().escapeString(title);
return title;
}
/**
* @param {Object.} o
* @return {Object}
*/
convertEvent(o) {
const userId = o.userId || this.userList[0].id || this.getUser().id;
let event;
if (o.isBusyRange) {
event = {
className: 'busy',
group: userId,
'date-start': o.dateStart,
'date-end': o.dateEnd,
type: 'background'
};
} else if (o.isWorkingRange) {
event = {
className: 'working',
group: userId,
'date-start': o.dateStart,
'date-end': o.dateEnd,
type: 'background'
};
} else if (o.isNonWorkingRange) {
event = {
className: 'non-working',
group: userId,
'date-start': o.dateStart,
'date-end': o.dateEnd,
type: 'background'
};
} else {
event = {
content: this.getHelper().escapeString(o.name),
title: this.getHelper().escapeString(o.name),
id: userId + '-' + o.scope + '-' + o.id,
group: userId,
'record-id': o.id,
scope: o.scope,
status: o.status,
'date-start': o.dateStart,
'date-end': o.dateEnd,
type: 'range',
className: 'clickable',
color: o.color
};
}
this.eventAttributes.forEach(attr => {
event[attr] = o[attr];
});
if (o.dateStart || o.dateStartDate) {
if (!o.dateStartDate) {
event.start = this.getDateTime().toMoment(o.dateStart);
} else {
event.start = _moment.default.tz(o.dateStartDate, this.getDateTime().getTimeZone());
}
}
if (o.dateEnd || o.dateEndDate) {
if (!o.dateEndDate) {
event.end = this.getDateTime().toMoment(o.dateEnd);
} else {
event.end = _moment.default.tz(o.dateEndDate, this.getDateTime().getTimeZone());
}
}
if (o.dateStartDate && !this.allDayScopeList.includes(o.scope) && event.end) {
event.end = event.end.clone().add(1, 'days');
}
if (o.isBusyRange) {
return event;
}
if (this.allDayScopeList.includes(o.scope)) {
event.type = 'box';
if (event.end) {
if (o.dateEndDate) {
event.start = event.end.clone().add(1, 'days');
} else {
event.start = event.end.clone();
}
}
} else {
if (!event.end || !event.start) return;
}
this.fillColor(event);
if (!o.isNonWorkingRange) {
this.handleStatus(event);
}
return event;
}
/**
* @param {string} scope
* @return {string[]}
*/
getEventTypeCompletedStatusList(scope) {
return this.getMetadata().get(['scopes', scope, 'completedStatusList']) || [];
}
/**
* @param {string} scope
* @return {string[]}
*/
getEventTypeCanceledStatusList(scope) {
return this.getMetadata().get(['scopes', scope, 'canceledStatusList']) || [];
}
fillColor(event) {
let key = event.scope;
if (event.className === 'non-working') {
key = 'bg';
}
let color = this.colors[key];
if (event.color) {
color = event.color;
}
if (!color) {
color = this.getColorFromScopeName(event.scope);
}
if (event.status && (this.getEventTypeCompletedStatusList(event.scope).includes(event.status) || this.getEventTypeCanceledStatusList(event.scope).includes(event.status))) {
color = this.shadeColor(color, 0.4);
}
event.style = event.style || '';
event.style += 'background-color:' + color + ';';
event.style += 'border-color:' + color + ';';
}
handleStatus(event) {
if (this.getEventTypeCanceledStatusList(event.scope).includes(event.status)) {
event.className += ' event-canceled';
}
}
shadeColor(color, percent) {
if (color === 'transparent') {
return color;
}
if (this.getThemeManager().getParam('isDark')) {
percent *= -1;
}
const alpha = color.substring(7);
const f = parseInt(color.slice(1, 7), 16),
t = percent < 0 ? 0 : 255,
p = percent < 0 ? percent * -1 : percent,
R = f >> 16,
G = f >> 8 & 0x00FF,
B = f & 0x0000FF;
return "#" + (0x1000000 + (Math.round((t - R) * p) + R) * 0x10000 + (Math.round((t - G) * p) + G) * 0x100 + (Math.round((t - B) * p) + B)).toString(16).slice(1) + alpha;
}
convertEventList(list) {
const resultList = [];
list.forEach(item => {
const event = this.convertEvent(item);
if (!event) {
return;
}
resultList.push(event);
});
return resultList;
}
afterRender() {
if (this.options.containerSelector) {
this.$container = (0, _jquery.default)(this.options.containerSelector);
}
const $timeline = this.$timeline = this.$el.find('div.timeline');
this.initUserList();
this.initDates();
this.initGroupsDataSet();
this.fetchEvents(this.start, this.end, eventList => {
const itemsDataSet = new _visData.DataSet(eventList);
this.timeline = new _visTimeline.Timeline($timeline.get(0), itemsDataSet, this.groupsDataSet, {
dataAttributes: 'all',
start: this.start.toDate(),
end: this.end.toDate(),
rollingMode: {
follow: false // fixes slow render
},
xss: {
filterOptions: {
onTag: (tag, html) => html
}
},
moment: /** Record */date => {
const m = (0, _moment.default)(date);
if (date && date.noTimeZone) {
return m;
}
// noinspection JSUnresolvedReference
return m.tz(this.getDateTime().getTimeZone());
},
format: this.getFormatObject(),
zoomMax: 24 * 3600 * 1000 * this.maxRange,
zoomMin: 1000 * 60 * 15,
orientation: 'top',
groupEditable: false,
editable: {
add: false,
updateTime: false,
updateGroup: false,
remove: false
},
locales: {
myLocale: {
current: this.translate('current', 'labels', 'Calendar'),
time: this.translate('time', 'labels', 'Calendar')
}
},
locale: 'myLocale',
margin: {
item: {
vertical: 12
},
axis: 6
}
});
this.timeline.on('click', e => {
if (this.blockClick) {
return;
}
if (e.item) {
const $item = this.$el.find('.timeline .vis-item[data-id="' + e.item + '"]');
const id = $item.attr('data-record-id');
const scope = $item.attr('data-scope');
if (id && scope) {
this.viewEvent(scope, id);
}
return;
}
if (e.what === 'background' && e.group && e.time) {
const dateStart = (0, _moment.default)(e.time).utc().format(this.getDateTime().internalDateTimeFormat);
this.createEvent(dateStart, e.group);
}
});
// noinspection SpellCheckingInspection
this.timeline.on('rangechanged', e => {
e.skipClick = true;
this.blockClick = true;
setTimeout(() => {
this.blockClick = false;
}, 100);
this.start = (0, _moment.default)(e.start);
this.end = (0, _moment.default)(e.end);
this.triggerView();
if (this.start.unix() < this.fetchedStart.unix() + this.rangeMarginThreshold || this.end.unix() > this.fetchedEnd.unix() - this.rangeMarginThreshold) {
this.runFetch();
}
});
this.once('remove', () => {
this.timeline.destroy();
});
});
}
/**
* @private
* @param {string} dateStart
* @param {string} [userId]
*/
async createEvent(dateStart, userId) {
if (!dateStart) {
const time = (this.timeline.getWindow().end - this.timeline.getWindow().start) / 2 + this.timeline.getWindow().start;
dateStart = (0, _moment.default)(time).utc().format(this.getDateTime().internalDateTimeFormat);
if (this.date === this.getDateTime().getToday()) {
dateStart = (0, _moment.default)().utc().format(this.getDateTime().internalDateTimeFormat);
}
}
const attributes = {
dateStart: dateStart
};
if (userId) {
let userName;
this.userList.forEach(item => {
if (item.id === userId) {
userName = item.name;
}
});
attributes.assignedUserId = userId;
attributes.assignedUserName = userName || userId;
}
const scopeList = this.enabledScopeList.filter(it => !this.onlyDateScopeList.includes(it));
Espo.Ui.notifyWait();
const view = await this.createView('dialog', 'crm:views/calendar/modals/edit', {
attributes: attributes,
enabledScopeList: scopeList,
scopeList: this.scopeList
});
this.listenTo(view, 'before:save', () => {
if (this.options.onSave) {
this.options.onSave();
}
});
this.listenTo(view, 'after:save', () => this.runFetch());
await view.render();
Espo.Ui.notify();
}
/**
*
* @param {string} scope
* @param {string} id
*/
async viewEvent(scope, id) {
const helper = new _recordModal.default();
/** @type {import('views/modals/detail').default} */
let modalView;
modalView = await helper.showDetail(this, {
entityType: scope,
id: id,
removeDisabled: false,
beforeSave: () => {
if (this.options.onSave) {
this.options.onSave();
}
},
beforeDestroy: () => {
if (this.options.onSave) {
this.options.onSave();
}
},
afterSave: (model, o) => {
if (!o.bypassClose) {
modalView.close();
}
this.runFetch();
},
afterDestroy: () => {
this.runFetch();
}
});
}
runFetch() {
this.fetchEvents(this.start, this.end, eventList => {
const itemsDataSet = new _visData.DataSet(eventList);
this.timeline.setItems(itemsDataSet);
this.triggerView();
});
}
getFormatObject() {
return {
minorLabels: {
millisecond: 'SSS',
second: 's',
minute: this.getDateTime().getTimeFormat(),
hour: this.getDateTime().getTimeFormat(),
weekday: 'ddd D',
day: 'D',
month: 'MMM',
year: 'YYYY'
},
majorLabels: {
millisecond: this.getDateTime().getTimeFormat() + ' ss',
second: this.getDateTime().getReadableDateFormat() + ' HH:mm',
minute: 'ddd D MMMM',
hour: 'ddd D MMMM',
weekday: 'MMMM YYYY',
day: 'MMMM YYYY',
month: 'YYYY',
year: ''
}
};
}
triggerView() {
const m = this.start.clone().add(Math.round((this.end.unix() - this.start.unix()) / 2), 'seconds');
const date = m.format(this.getDateTime().internalDateFormat);
this.date = date;
this.trigger('view', date, this.mode);
}
initUserList() {
if (this.options.userList) {
this.userList = Espo.Utils.clone(this.options.userList);
if (!this.userList.length) {
this.userList.push({
id: this.getUser().id,
name: this.getUser().get('name')
});
}
return;
}
this.userList = [];
if (this.calendarType === 'single') {
if (this.options.userId) {
this.userList.push({
id: this.options.userId,
name: this.options.userName || this.options.userId
});
return;
}
this.userList.push({
id: this.getUser().id,
name: this.getUser().get('name')
});
return;
}
if (this.calendarType === 'shared') {
this.getSharedCalenderUserList().forEach(item => {
this.userList.push({
id: item.id,
name: item.name
});
});
}
}
storeUserList() {
this.getPreferences().save({
'sharedCalendarUserList': Espo.Utils.clone(this.userList)
}, {
patch: true
});
}
getSharedCalenderUserList() {
const list = Espo.Utils.clone(this.getPreferences().get('sharedCalendarUserList'));
if (list && list.length) {
let isBad = false;
list.forEach(item => {
if (typeof item !== 'object' || !item.id || !item.name) {
isBad = true;
}
});
if (!isBad) {
return list;
}
}
return [{
id: this.getUser().id,
name: this.getUser().get('name')
}];
}
initDates() {
if (this.date) {
this.start = _moment.default.tz(this.date, this.getDateTime().getTimeZone());
} else {
this.start = _moment.default.tz(this.getDateTime().getTimeZone());
}
this.end = this.start.clone();
this.end.add(1, 'day');
this.fetchedStart = null;
this.fetchedEnd = null;
}
initGroupsDataSet() {
const list = [];
this.userList.forEach((user, i) => {
list.push({
id: user.id,
content: this.getGroupContent(user.id, user.name),
order: i
});
});
this.groupsDataSet = new _visData.DataSet(list);
}
getGroupContent(id, name) {
if (this.calendarType === 'single') {
return (0, _jquery.default)('').text(name).get(0).outerHTML;
}
let avatarHtml = this.getAvatarHtml(id);
if (avatarHtml) {
avatarHtml += ' ';
}
return avatarHtml + (0, _jquery.default)('').attr('data-id', id).addClass('group-title').text(name).get(0).outerHTML;
}
getAvatarHtml(id) {
if (this.getConfig().get('avatarsDisabled')) {
return '';
}
let t;
const cache = this.getCache();
if (cache) {
t = cache.get('app', 'timestamp');
} else {
t = Date.now();
}
// noinspection HtmlRequiredAltAttribute,RequiredAttributes
return (0, _jquery.default)('
').addClass('avatar avatar-link').attr('width', '16').attr('src', this.getBasePath() + '?entryPoint=avatar&size=small&id=' + id + '&t=' + t).get(0).outerHTML;
}
fetchEvents(from, to, callback) {
if (!this.options.suppressLoadingAlert) {
Espo.Ui.notifyWait();
}
from = from.clone().add(-1 * this.leftMargin, 'seconds');
to = to.clone().add(this.rightMargin, 'seconds');
const fromString = from.utc().format(this.getDateTime().internalDateTimeFormat);
const toString = to.utc().format(this.getDateTime().internalDateTimeFormat);
let url = 'Timeline?from=' + fromString + '&to=' + toString;
const userIdList = this.userList.map(user => {
return user.id;
});
if (userIdList.length === 1) {
url += '&userId=' + userIdList[0];
} else {
url += '&userIdList=' + encodeURIComponent(userIdList.join(','));
}
url += '&scopeList=' + encodeURIComponent(this.enabledScopeList.join(','));
Espo.Ajax.getRequest(url).then(data => {
this.fetchedStart = from.clone();
this.fetchedEnd = to.clone();
const eventList = [];
for (const userId in data) {
const userEventList = data[userId];
userEventList.forEach(item => {
item.userId = userId;
eventList.push(item);
});
}
const convertedEventList = this.convertEventList(eventList);
callback(convertedEventList);
Espo.Ui.notify(false);
});
}
async actionShowSharedCalendarOptions() {
const view = new _sharedOptions.default({
users: this.userList,
onApply: data => {
this.userList = data.users;
this.storeUserList();
this.initGroupsDataSet();
this.timeline.setGroups(this.groupsDataSet);
this.runFetch();
}
});
await this.assignView('modal', view);
await view.render();
}
actionRefresh() {
this.runFetch();
const iconEl = this.element.querySelector('button[data-action="refresh"] > span');
if (iconEl) {
iconEl.classList.add('animation-spin-fast');
setTimeout(() => iconEl.classList.remove('animation-spin-fast'), 500);
}
}
getColorFromScopeName(scope) {
const additionalColorList = this.getMetadata().get('clientDefs.Calendar.additionalColorList') || [];
if (!additionalColorList.length) {
return;
}
const colors = this.getMetadata().get('clientDefs.Calendar.colors') || {};
const scopeList = this.getConfig().get('calendarEntityList') || [];
let index = 0;
let j = 0;
for (let i = 0; i < scopeList.length; i++) {
if (scopeList[i] in colors) {
continue;
}
if (scopeList[i] === scope) {
index = j;
break;
}
j++;
}
index = index % additionalColorList.length;
this.colors[scope] = additionalColorList[index];
return this.colors[scope];
}
// noinspection JSUnusedGlobalSymbols
actionPrevious() {
const start = this.timeline.getWindow().start;
this.timeline.moveTo(start);
this.triggerView();
}
// noinspection JSUnusedGlobalSymbols
actionNext() {
const end = this.timeline.getWindow().end;
this.timeline.moveTo(end);
this.triggerView();
}
actionToday() {
this.timeline.moveTo((0, _moment.default)().toDate());
this.triggerView();
}
// noinspection JSUnusedGlobalSymbols
actionZoomOut() {
this.timeline.zoomOut(this.zoomPercentage);
this.triggerView();
}
// noinspection JSUnusedGlobalSymbols
actionZoomIn() {
this.timeline.zoomIn(this.zoomPercentage);
}
}
var _default = _exports.default = TimelineView;
});