')
- .addClass('item')
- .attr('data-key', key)
- );
-
- this.createItemView(item, key)
- .then(view => view.render())
- .then(() => {
- this.trigger('change');
- });
- },
-
- removeItem: function (key) {
- let index = this.itemKeyList.indexOf(key);
-
- if (key === -1) {
- return;
- }
-
- let itemList = this.getItemListFromModel();
-
- this.itemKeyList.splice(index, 1);
- itemList.splice(index, 1);
-
- this.model.set(this.name, itemList, {ui: true});
+ return view;
+ });
+ }
+ /**
+ * @returns {Promise}
+ */
+ createItemViews() {
+ this.itemKeyList.forEach(key => {
this.clearView(this.composeViewKey(key));
+ });
- this.$el.find(`.item[data-key="${key}"`).remove();
+ if (!this.model.has(this.name)) {
+ return Promise.resolve();
+ }
- this.trigger('change');
- },
+ const itemList = this.getItemListFromModel();
- fetch: function () {
- let itemList = [];
+ const promiseList = [];
- this.itemKeyList.forEach(key => {
- itemList.push(
- this.getItemView(key).fetch()
- );
+ this.itemKeyList.forEach((key, i) => {
+ const item = itemList[i];
+
+ const promise = this.createItemView(item, key);
+
+ promiseList.push(promise);
+ });
+
+ return Promise.all(promiseList);
+ }
+
+ /**
+ * @param {string} key
+ * @return {import('./time-ranges/item-edit').default}
+ */
+ getItemView(key) {
+ // noinspection JSValidateTypes
+ return this.getView(this.composeViewKey(key));
+ }
+
+ composeViewKey(key) {
+ return `item-${key}`;
+ }
+
+ /**
+ * @return {[string|null, string|null][]}
+ */
+ getItemListFromModel() {
+ return Espo.Utils.cloneDeep(this.model.get(this.name) || []);
+ }
+
+ addItem() {
+ const itemList = this.getItemListFromModel();
+
+ let value = null;
+
+ if (itemList.length) {
+ value = itemList[itemList.length - 1][1];
+ }
+
+ const item = [value, null];
+
+ itemList.push(item);
+
+ let key = this.itemKeyList[this.itemKeyList.length - 1];
+
+ if (typeof key === 'undefined') {
+ key = 0;
+ }
+
+ key++;
+
+ this.itemKeyList.push(key);
+
+ this.$el.find('.item-list').append(
+ $('
')
+ .addClass('item')
+ .attr('data-key', key)
+ );
+
+ this.createItemView(item, key)
+ .then(view => view.render())
+ .then(() => {
+ this.trigger('change');
});
+ }
- let data = {};
+ removeItem(key) {
+ const index = this.itemKeyList.indexOf(key);
- data[this.name] = Espo.Utils.cloneDeep(itemList);
+ if (key === -1) {
+ return;
+ }
- if (data[this.name].length === 0) {
- data[this.name] = null;
- }
+ const itemList = this.getItemListFromModel();
- return data;
- },
+ this.itemKeyList.splice(index, 1);
+ itemList.splice(index, 1);
- validateRequired: function () {
- if (!this.isRequired()) {
- return false;
- }
+ this.model.set(this.name, itemList, {ui: true});
- if (this.getItemListFromModel().length) {
- return false;
- }
+ this.clearView(this.composeViewKey(key));
- let msg = this.translate('fieldIsRequired', 'messages')
- .replace('{field}', this.getLabelText());
+ this.$el.find(`.item[data-key="${key}"`).remove();
- this.showValidationMessage(msg, '.add-item-container');
+ this.trigger('change');
+ }
- return true;
- },
+ fetch() {
+ const itemList = [];
- validateValid: function () {
- if (!this.isRangesInvalid()) {
- return false;
- }
+ this.itemKeyList.forEach(key => {
+ itemList.push(
+ this.getItemView(key).fetch()
+ );
+ });
- let msg = this.translate('fieldInvalid', 'messages')
- .replace('{field}', this.getLabelText());
+ const data = {};
- this.showValidationMessage(msg, '.add-item-container');
+ data[this.name] = Espo.Utils.cloneDeep(itemList);
- return true;
- },
+ if (data[this.name].length === 0) {
+ data[this.name] = null;
+ }
- isRangesInvalid: function () {
- let itemList = this.getItemListFromModel();
-
- for (let i = 0; i < itemList.length; i++) {
- let item = itemList[i];
-
- if (this.isRangeInvalid(item[0], item[1], true)) {
- return true;
- }
-
- if (i === 0) {
- continue;
- }
-
- let prevItem = item[i - 1];
-
- if (this.isRangeInvalid(prevItem[1], item[0])) {
- return true;
- }
- }
+ return data;
+ }
+ validateRequired() {
+ if (!this.isRequired()) {
return false;
- },
+ }
- /**
- * @param {string|null} from
- * @param {string|null} to
- * @param {boolean} [noEmpty]
- */
- isRangeInvalid: function (from, to, noEmpty) {
- if (from === null || to === null) {
+ if (this.getItemListFromModel().length) {
+ return false;
+ }
+
+ const msg = this.translate('fieldIsRequired', 'messages')
+ .replace('{field}', this.getLabelText());
+
+ this.showValidationMessage(msg, '.add-item-container');
+
+ return true;
+ }
+
+ validateValid() {
+ if (!this.isRangesInvalid()) {
+ return false;
+ }
+
+ const msg = this.translate('fieldInvalid', 'messages')
+ .replace('{field}', this.getLabelText());
+
+ this.showValidationMessage(msg, '.add-item-container');
+
+ return true;
+ }
+
+ isRangesInvalid() {
+ const itemList = this.getItemListFromModel();
+
+ for (let i = 0; i < itemList.length; i++) {
+ const item = itemList[i];
+
+ if (this.isRangeInvalid(item[0], item[1], true)) {
return true;
}
- let fromNumber = parseFloat(from.replace(':', '.'));
- let toNumber = parseFloat(to.replace(':', '.'));
-
- if (noEmpty && fromNumber === toNumber) {
- return true;
+ if (i === 0) {
+ continue;
}
- return fromNumber > toNumber;
- },
- });
-});
+ const prevItem = item[i - 1];
+
+ if (this.isRangeInvalid(prevItem[1], item[0])) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * @param {string|null} from
+ * @param {string|null} to
+ * @param {boolean} [noEmpty]
+ */
+ isRangeInvalid(from, to, noEmpty) {
+ if (from === null || to === null) {
+ return true;
+ }
+
+ const fromNumber = parseFloat(from.replace(':', '.'));
+ const toNumber = parseFloat(to.replace(':', '.'));
+
+ if (noEmpty && fromNumber === toNumber) {
+ return true;
+ }
+
+ return fromNumber > toNumber;
+ }
+}
diff --git a/client/src/views/working-time-calendar/fields/time-ranges/item-detail.js b/client/src/views/working-time-calendar/fields/time-ranges/item-detail.js
index bf27c0f94d..a722b57187 100644
--- a/client/src/views/working-time-calendar/fields/time-ranges/item-detail.js
+++ b/client/src/views/working-time-calendar/fields/time-ranges/item-detail.js
@@ -26,45 +26,39 @@
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
-define('views/working-time-calendar/fields/time-ranges/item-detail', ['view', 'lib!moment'],
-(Dep, /** @param {moment} */moment) => {
+import View from 'view';
+import moment from 'moment';
- /**
- * @extends module:view
- */
- class Class extends Dep
- {
- templateContent = `
- {{start}}
- –
- {{end}}
- `
+export default class extends View {
- data() {
- return {
- start: this.convertTimeToDisplay(this.value[0]),
- end: this.convertTimeToDisplay(this.value[1]),
- };
- }
+ templateContent = `
+ {{start}}
+ –
+ {{end}}
+ `
- setup() {
- this.value = this.options.value;
- }
-
- convertTimeToDisplay(value) {
- if (!value) {
- return '';
- }
-
- let m = moment(value, 'HH:mm');
-
- if (!m.isValid()) {
- return '';
- }
-
- return m.format(this.getDateTime().timeFormat);
- }
+ data() {
+ return {
+ start: this.convertTimeToDisplay(this.value[0]),
+ end: this.convertTimeToDisplay(this.value[1]),
+ };
}
- return Class;
-});
+ setup() {
+ this.value = this.options.value;
+ }
+
+ convertTimeToDisplay(value) {
+ if (!value) {
+ return '';
+ }
+
+ const m = moment(value, 'HH:mm');
+
+ if (!m.isValid()) {
+ return '';
+ }
+
+ return m.format(this.getDateTime().timeFormat);
+ }
+}
diff --git a/client/src/views/working-time-calendar/fields/time-ranges/item-edit.js b/client/src/views/working-time-calendar/fields/time-ranges/item-edit.js
index 0e3fdbe7e2..780ff7fac8 100644
--- a/client/src/views/working-time-calendar/fields/time-ranges/item-edit.js
+++ b/client/src/views/working-time-calendar/fields/time-ranges/item-edit.js
@@ -26,144 +26,144 @@
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
-define('views/working-time-calendar/fields/time-ranges/item-edit', ['view', 'lib!moment'], function (Dep, moment) {
+import View from 'view';
+import moment from 'moment';
- return Dep.extend({
+export default class extends View {
- // language=Handlebars
- templateContent: `
-
-
-
-
-
- –
-
-
-
-
-
+
+ // language=Handlebars
+ templateContent = `
+
+
+
- `,
+
+ –
+
+
+
+
+
+
+ `
- timeFormatMap: {
- 'HH:mm': 'H:i',
- 'hh:mm A': 'h:i A',
- 'hh:mm a': 'h:i a',
- 'hh:mmA': 'h:iA',
- 'hh:mma': 'h:ia',
- },
+ timeFormatMap = {
+ 'HH:mm': 'H:i',
+ 'hh:mm A': 'h:i A',
+ 'hh:mm a': 'h:i a',
+ 'hh:mmA': 'h:iA',
+ 'hh:mma': 'h:ia',
+ }
- minuteStep: 30,
+ minuteStep = 30
- data: function () {
- let data = {};
+ data () {
+ const data = {};
- data.start = this.convertTimeToDisplay(this.value[0]);
- data.end = this.convertTimeToDisplay(this.value[1]);
+ data.start = this.convertTimeToDisplay(this.value[0]);
+ data.end = this.convertTimeToDisplay(this.value[1]);
- data.key = this.key;
+ data.key = this.key;
- return data;
- },
+ return data;
+ }
- setup: function () {
- this.value = this.options.value || [null, null];
- this.key = this.options.key;
- },
+ setup () {
+ this.value = this.options.value || [null, null];
+ this.key = this.options.key;
+ }
- convertTimeToDisplay: function (value) {
- if (!value) {
- return '';
- }
+ convertTimeToDisplay(value) {
+ if (!value) {
+ return '';
+ }
- let m = moment(value, 'HH:mm');
+ const m = moment(value, 'HH:mm');
- if (!m.isValid()) {
- return '';
- }
+ if (!m.isValid()) {
+ return '';
+ }
- return m.format(this.getDateTime().timeFormat);
- },
+ return m.format(this.getDateTime().timeFormat);
+ }
- convertTimeFromDisplay: function (value) {
- if (!value) {
- return null;
- }
+ convertTimeFromDisplay(value) {
+ if (!value) {
+ return null;
+ }
- let m = moment(value, this.getDateTime().timeFormat);
+ const m = moment(value, this.getDateTime().timeFormat);
- if (!m.isValid()) {
- return null;
- }
+ if (!m.isValid()) {
+ return null;
+ }
- return m.format('HH:mm');
- },
+ return m.format('HH:mm');
+ }
- afterRender: function () {
- this.$start = this.$el.find('[data-name="start"]');
- this.$end = this.$el.find('[data-name="end"]');
+ afterRender() {
+ this.$start = this.$el.find('[data-name="start"]');
+ this.$end = this.$el.find('[data-name="end"]');
- this.initTimepicker(this.$start);
- this.initTimepicker(this.$end);
+ this.initTimepicker(this.$start);
+ this.initTimepicker(this.$end);
- this.setMinTime();
+ this.setMinTime();
- this.$start.on('change', () => this.setMinTime());
- },
+ this.$start.on('change', () => this.setMinTime());
+ }
- setMinTime: function () {
- let value = this.$start.val();
+ setMinTime() {
+ const value = this.$start.val();
- this.$end.timepicker('option', 'maxTime', this.convertTimeToDisplay('23:59'));
+ this.$end.timepicker('option', 'maxTime', this.convertTimeToDisplay('23:59'));
- if (!value) {
- this.$end.timepicker('option', 'minTime', null);
+ if (!value) {
+ this.$end.timepicker('option', 'minTime', null);
- return;
- }
+ return;
+ }
- this.$end.timepicker('option', 'minTime', value);
- },
+ this.$end.timepicker('option', 'minTime', value);
+ }
- initTimepicker: function ($el) {
- $el.timepicker({
- step: this.minuteStep,
- timeFormat: this.timeFormatMap[this.getDateTime().timeFormat],
- });
+ initTimepicker($el) {
+ $el.timepicker({
+ step: this.minuteStep,
+ timeFormat: this.timeFormatMap[this.getDateTime().timeFormat],
+ });
- $el.on('change', () => this.trigger('change'));
+ $el.on('change', () => this.trigger('change'));
+ $el.attr('autocomplete', 'espo-time-range-item');
+ }
- $el.attr('autocomplete', 'espo-time-range-item');
- },
-
- fetch: function () {
- return [
- this.convertTimeFromDisplay(this.$start.val()),
- this.convertTimeFromDisplay(this.$end.val()),
- ];
- },
- });
-});
+ fetch() {
+ return [
+ this.convertTimeFromDisplay(this.$start.val()),
+ this.convertTimeFromDisplay(this.$end.val()),
+ ];
+ }
+}