[IMP] web: calendar improvements

This commit improves the calendar behavior.

 * Before this commit, the invisible attribute were not evaluated in the calendar popover.
 * Several alignment, label, improvements

taskid: 2342252
This commit is contained in:
Arnaud Joset
2021-02-25 08:25:51 +00:00
parent 0915ac5a0e
commit c9003f3f4b
8 changed files with 106 additions and 54 deletions
@@ -318,7 +318,7 @@ var CalendarController = AbstractController.extend({
var options = _.extend({}, this.options, event.options, {
context: context,
title: _.str.sprintf(_t('Create: %s'), (this.displayName || this.renderer.arch.attrs.string))
title: _t('New Event')
});
if (this.quick != null) {
@@ -335,10 +335,7 @@ var CalendarController = AbstractController.extend({
return;
}
var title = _t("Create");
if (this.renderer.arch.attrs.string) {
title += ': ' + this.renderer.arch.attrs.string;
}
const title = _t('New Event')
if (this.eventOpenPopup) {
if (this.previousOpen) { this.previousOpen.close(); }
this.previousOpen = new dialogs.FormViewDialog(self, {
@@ -489,7 +489,6 @@ return AbstractModel.extend({
_loadCalendar: function () {
var self = this;
this.data.fc_options = this._getFullCalendarOptions();
var defs = _.map(this.data.filters, this._loadFilter.bind(this));
return Promise.all(defs).then(function () {
@@ -550,11 +549,12 @@ return AbstractModel.extend({
var value = _.isArray(_value) ? _value[0] : _value;
var f = _.find(filter.filters, function (f) {return f.value === value;});
var formater = fieldUtils.format[_.contains(['many2many', 'one2many'], field.type) ? 'many2one' : field.type];
// By default, only current user partner is checked.
return {
'id': record.id,
'value': value,
'label': formater(_value, field),
'active': !f || f.active,
'active': f && f.active,
};
});
records.sort(function (f1,f2) {
@@ -573,7 +573,7 @@ return AbstractModel.extend({
var f = _.find(filter.filters, function (f) {return f.value === value;});
me = {
'value': value,
'label': session.name + _t(" [Me]"),
'label': session.name,
'active': !f || f.active,
};
}
@@ -197,8 +197,14 @@ var CalendarPopover = Widget.extend(WidgetAdapterMixin, StandaloneFieldManagerMi
fieldWidget.attrs.modifiers = fieldWidget.attrs.modifiers ? JSON.parse(fieldWidget.attrs.modifiers) : {};
}
self._registerWidget(recordID, field.name, fieldWidget);
// Only display the fields whose attributes does not make them invisible
let fieldClass = "list-group-item flex-shrink-0 d-flex flex-wrap";
if (fieldWidget.attrs && fieldWidget.attrs.modifiers) {
const fieldModifier = record.evalModifiers(fieldWidget.attrs.modifiers);
fieldClass += fieldModifier.invisible ? ' o_invisible_modifier' : '';
}
var $field = $('<li>', {class: 'list-group-item flex-shrink-0 d-flex flex-wrap'});
var $field = $('<li>', {class: fieldClass});
var $fieldLabel = $('<strong>', {class: 'mr-2', text: _.str.sprintf('%s : ', field.string)});
$fieldLabel.appendTo($field);
var $fieldContainer = $('<div>', {class: 'flex-grow-1'});
@@ -79,7 +79,7 @@ var QuickCreate = Dialog.extend({
var val = this.$('input').val().trim();
if (!val) {
this.$('label, input').addClass('o_field_invalid');
var warnings = _.str.sprintf('<ul><li>%s</li></ul>', _t("Summary"));
var warnings = _.str.sprintf('<ul><li>%s</li></ul>', _t("Meeting Subject"));
this.do_warn(_t("Invalid fields:"), warnings);
}
dataCalendar.title = val;
@@ -141,23 +141,19 @@ var SidebarFilter = Widget.extend(FieldManagerMixin, {
_onFilterRemove: function (e) {
var self = this;
var $filter = $(e.currentTarget).closest('.o_calendar_filter_item');
Dialog.confirm(this, _t("Do you really want to delete this filter from favorites ?"), {
confirm_callback: function () {
self._rpc({
model: self.write_model,
method: 'unlink',
args: [[$filter.data('id')]],
})
.then(function () {
self.trigger_up('changeFilter', {
'fieldName': self.fieldName,
'id': $filter.data('id'),
'active': false,
'value': $filter.data('value'),
});
});
},
});
self._rpc({
model: self.write_model,
method: 'unlink',
args: [[$filter.data('id')]],
})
.then(function () {
self.trigger_up('changeFilter', {
'fieldName': self.fieldName,
'id': $filter.data('id'),
'active': false,
'value': $filter.data('value'),
});
});
},
});
@@ -354,6 +350,7 @@ return AbstractRenderer.extend({
record: event.extendedProps.record,
color: this.getColor(event.extendedProps.color_index),
showTime: !self.hideTime && event.extendedProps.showTime,
showLocation: this.state.scale !== 'month'
};
this.qweb_context = qweb_context;
if (_.isEmpty(qweb_context.record)) {
@@ -423,6 +420,11 @@ return AbstractRenderer.extend({
$(self.calendarElement).find(`[data-event-id=${eventData.id}]`).addClass('o_cw_custom_highlight');
self._renderEventPopover(eventData, $(eventClickInfo.el));
},
selectAllow: function (event) {
if (event.end.getDate() === event.start.getDate() || event.allDay) {
return true;
}
},
yearDateClick: function (info) {
self._unselectEvent();
info.view.unselect();
+16 -1
View File
@@ -242,6 +242,16 @@ $o-cw-filter-avatar-size: 20px;
.fc-week-number {
background: none;
font-size: 1.2rem;
padding: 0.1rem 0.3rem 0.1rem 0 !important;
line-height: 1;
}
.fc-day-top {
text-align: center;
padding-top: 3px;
.fc-day-number {
float: none !important;
}
}
.fc-day-number {
@@ -251,6 +261,7 @@ $o-cw-filter-avatar-size: 20px;
color: gray('900');
font-weight: 400;
line-height: 1;
float: none !important;
}
.fc-day-top.fc-other-month {
@@ -682,6 +693,10 @@ $o-cw-filter-avatar-size: 20px;
background: none;
}
.o_footer_shrink {
padding-top: 0px;
padding-bottom: 0px;
}
.o_cw_popover_close {
cursor: pointer;
}
@@ -703,7 +718,7 @@ $o-cw-filter-avatar-size: 20px;
.o_cw_popover_fields_secondary {
max-height: 170px; // Fallback for old browsers
max-height: 25vh;
max-height: 75vh;
overflow: auto;
padding-bottom: 1px; // prevents the scrollbar to show when not needed
+3 -2
View File
@@ -15,6 +15,7 @@
<div t-att-style="typeof color === 'string' ? ('background-color:' + color) + ';' : ''" t-attf-class="#{record.is_highlighted ? 'o_event_hightlight' : ''} #{typeof color === 'number' ? _.str.sprintf('o_calendar_color_%s', color) : 'o_calendar_color_1'} #{record.is_hatched ? 'o_calendar_hatched o_calendar_hatched_' + (typeof color === 'number' ? color : 1): ''}">
<span t-if="showTime" class="fc-time"/>
<div class="o_event_title" t-esc="record.display_name"/>
<div t-if="showLocation and record.location" t-esc="record.location"/>
</div>
</t>
@@ -43,7 +44,7 @@
</li>
</ul>
<ul t-if="widget.isEventDetailsVisible()" class="list-group list-group-flush o_cw_popover_fields_secondary"/>
<div class="card-footer border-top">
<div t-attf-class="card-footer border-top #{ !widget.isEventEditable() &amp;&amp;!widget.isEventDeletable() ? 'o_footer_shrink' : '' }">
<a t-if="widget.isEventEditable()" href="#" class="btn btn-primary o_cw_popover_edit">Edit</a>
<a t-if="widget.isEventDeletable()" href="#" class="btn btn-secondary o_cw_popover_delete ml-2">Delete</a>
</div>
@@ -120,7 +121,7 @@
<main t-name="CalendarView.quick_create" class="o_calendar_quick_create">
<div class="form-group">
<label for='name' class='col-form-label o_form_label'>Summary:</label>
<label for='name' class='col-form-label o_form_label'>Meeting Subject:</label>
<input type='text' name='name' class="o_input o_required_modifier" t-att-value="widget.dataTemplate.name or None"/>
</div>
</main>
+55 -24
View File
@@ -198,6 +198,11 @@ QUnit.module('Views', {
var $sidebar = calendar.$('.o_calendar_sidebar');
// test view scales
assert.containsN(calendar, '.fc-event', 0,
"By default, only the events of the current user are displayed (0 in this case)");
// display all events
await testUtils.dom.click(calendar.$('.o_calendar_filter_item[data-value=all] input'));
assert.containsN(calendar, '.fc-event', 9,
"should display 9 events on the week (4 event + 5 days event)");
assert.containsN($sidebar, 'tr:has(.ui-state-active) td', 7,
@@ -209,6 +214,11 @@ QUnit.module('Views', {
"should highlight the target day in mini calendar");
await testUtils.dom.click(calendar.$buttons.find('.o_calendar_button_month')); // display all the month
// We display the events or partner 1 2 and 4. Partner 2 has nothing and Event 6 is for partner 6 (not displayed)
await testUtils.dom.click(calendar.$('.o_calendar_filter_item[data-value=all] input'));
await testUtils.dom.click(calendar.$('.o_calendar_filter .o_calendar_filter_item[data-value=1] input')[0]);
await testUtils.dom.click(calendar.$('.o_calendar_filter_item[data-value=2] input'));
assert.containsN(calendar, '.fc-event', 7,
"should display 7 events on the month (5 events + 2 week event - 1 'event 6' is filtered + 1 'Undefined event')");
assert.containsN($sidebar, 'td a', 31,
@@ -246,8 +256,6 @@ QUnit.module('Views', {
await testUtils.dom.click($sidebar.find('input[type="text"]'));
assert.strictEqual($('ul.ui-autocomplete li:not(.o_m2o_dropdown_option)').text(), "partner 4", "should display the last choice in one2many autocomplete"); // TODO: remove :not(.o_m2o_dropdown_option) because can't have "create & edit" choice
await testUtils.dom.click($sidebar.find('.o_calendar_filter_item .o_remove').first(), {allowInvisible: true});
assert.ok($('.modal-footer button.btn:contains(Ok)').length, "should display the confirm message");
await testUtils.dom.click($('.modal-footer button.btn:contains(Ok)'));
assert.containsN($sidebar, '.o_calendar_filter:has(h5:contains(attendees)) .o_calendar_filter_item', 3, "click on remove then should display 3 filter items for 'attendees'");
calendar.destroy();
});
@@ -430,7 +438,7 @@ QUnit.module('Views', {
await testUtils.dom.click($('.modal-footer button.btn:contains(Edit)'));
assert.strictEqual($('.modal-lg .o_form_view').length, 1, "should open the slow create dialog");
assert.strictEqual($('.modal-lg .modal-title').text(), "Create: Events",
assert.strictEqual($('.modal-lg .modal-title').text(), "New Event",
"should use the string attribute as modal title");
assert.strictEqual($('.modal-lg .o_form_view input[name="name"]').val(), "coucou",
"should have set the name from the quick create dialog");
@@ -544,7 +552,7 @@ QUnit.module('Views', {
testUtils.dom.triggerMouseEvent($cell, "mouseup");
await testUtils.nextTick();
assert.strictEqual($('.modal-sm .modal-title').text(), 'Create: Custom Events',
assert.strictEqual($('.modal-sm .modal-title').text(), 'New Event',
"should open the quick create dialog");
await testUtils.fields.editInput($('.modal-body input:first'), 'custom event in quick create');
@@ -597,7 +605,7 @@ QUnit.module('Views', {
testUtils.dom.triggerMouseEvent($cell, "mouseup");
await testUtils.nextTick();
assert.strictEqual($('.modal-sm .modal-title').text(), 'Create: Events',
assert.strictEqual($('.modal-sm .modal-title').text(), 'New Event',
"should open the quick create dialog");
await testUtils.fields.editInput($('.modal-body input:first'), 'new event in quick create');
@@ -607,7 +615,7 @@ QUnit.module('Views', {
// If the event is not default-prevented, a traceback will be raised, which we do not want
assert.ok(event.isDefaultPrevented(), "fail deferred event should have been default-prevented");
assert.strictEqual($('.modal-lg .modal-title').text(), 'Create: Events',
assert.strictEqual($('.modal-lg .modal-title').text(), 'New Event',
"should have switched to a bigger modal for an actual create rather than quickcreate");
assert.strictEqual($('.modal-lg main .o_form_view.o_form_editable').length, 1,
"should open the full event form view in a dialog");
@@ -1807,6 +1815,8 @@ QUnit.module('Views', {
assert.containsN(calendar, '.o_calendar_filter_items .o_cw_filter_avatar', 3,
"should have 3 avatars in the side bar");
await testUtils.dom.click(calendar.$('.o_calendar_filter_item[data-value=all] input'));
// Event 1
await testUtils.dom.click(calendar.$('.fc-event:first'));
assert.ok(calendar.$('.o_cw_popover').length, "should open a popover clicking on event");
@@ -2130,7 +2140,7 @@ QUnit.module('Views', {
});
QUnit.test('"all" filter', async function (assert) {
assert.expect(6);
assert.expect(8);
var interval = [
["start", "<=", "2016-12-17 23:59:59"],
@@ -2138,8 +2148,9 @@ QUnit.module('Views', {
];
var domains = [
interval.concat([["partner_ids", "in", [2,1]]]),
interval.concat([["partner_ids", "in", []]]),
interval.concat([["partner_ids", "in", [1]]]),
interval.concat([["partner_ids", "in", [2,1]]]),
interval,
];
@@ -2172,15 +2183,17 @@ QUnit.module('Views', {
return this._super.apply(this, arguments);
},
});
assert.containsN(calendar, '.fc-event', 9,
"should display 9 events on the week");
// By default, no user is selected
assert.containsN(calendar, '.fc-event', 0,
"should display 0 events on the week");
// Select the events only associated with partner 2
await testUtils.dom.click(calendar.$('.o_calendar_filter_item[data-id=2] input'));
await testUtils.dom.click(calendar.$('.o_calendar_filter_item[data-id=1] input'));
assert.containsN(calendar, '.fc-event', 4,
"should display 4 events on the week");
await testUtils.dom.click(calendar.$('.o_calendar_filter_item[data-value=2] input'));
assert.containsN(calendar, '.fc-event', 9,
"should display 9 events on the week");
// Click on the 'all' filter to reload all events
await testUtils.dom.click(calendar.$('.o_calendar_filter_item[data-value=all] input'));
assert.containsN(calendar, '.fc-event', 9,
@@ -2216,6 +2229,9 @@ QUnit.module('Views', {
initialDate: initialDate,
},
});
// By default no filter is selected. We check before continuing.
await testUtils.dom.click(calendar.$('.o_calendar_filter_item[data-value=1] input'));
await testUtils.dom.click(calendar.$('.o_calendar_filter_item[data-value=2] input'));
assert.containsN(calendar, '.o_calendar_filter', 2, "should display 2 filters");
@@ -2260,10 +2276,10 @@ QUnit.module('Views', {
},
}, {positionalClicks: true});
await testUtils.dom.click(calendar.$('.o_calendar_filter_item[data-value=4] input'));
// By default only
await testUtils.dom.click(calendar.$('.o_calendar_filter_item[data-value=1] input'));
assert.containsN(calendar, '.o_calendar_filter_item', 5, "should display 5 filter items");
assert.containsN(calendar, '.fc-event', 3, "should display 3 events");
assert.containsN(calendar, '.fc-event', 4, "should display 4 events");
// quick create a record
var left = calendar.$('.fc-bg td:eq(4)').offset().left+15;
@@ -2282,13 +2298,14 @@ QUnit.module('Views', {
await testUtils.dom.click($('.modal-footer button.btn:contains(Create)'));
assert.containsN(calendar, '.o_calendar_filter_item', 6, "should add the missing filter (active)");
assert.containsN(calendar, '.fc-event', 4, "should display the created item");
assert.containsN(calendar, '.fc-event', 5, "should display the created item");
await testUtils.nextTick();
// change default value for quick create an hide record
this.data.event.fields.user_id.default = 4;
this.data.event.fields.partner_id.default = 4;
// Disable our filter to create a record without displaying it
await testUtils.dom.click(calendar.$('.o_calendar_filter_item[data-value=4] input'));
// quick create and other record
left = calendar.$('.fc-bg td:eq(3)').offset().left+15;
top = calendar.$('.fc-slats tr:eq(12) td:first').offset().top+15;
@@ -2304,6 +2321,7 @@ QUnit.module('Views', {
assert.containsN(calendar, '.fc-event', 4, "should not display the created item");
await testUtils.dom.click(calendar.$('.o_calendar_filter_item[data-value=4] input'));
await testUtils.dom.click(calendar.$('.o_calendar_filter_item[data-value=2] input'));
assert.containsN(calendar, '.fc-event', 11, "should display all records");
@@ -2354,7 +2372,9 @@ QUnit.module('Views', {
initialDate: initialDate,
},
}, {positionalClicks: true});
// dislay all attendee calendars
await testUtils.dom.click(calendar.$('.o_calendar_filter_item[data-value=1] input'));
await testUtils.dom.click(calendar.$('.o_calendar_filter_item[data-value=2] input'));
await testUtils.dom.click(calendar.$('.o_calendar_filter_item[data-value=4] input'));
assert.containsN(calendar, '.o_calendar_filter_item', 5, "should display 5 filter items");
@@ -2434,7 +2454,8 @@ QUnit.module('Views', {
initialDate: initialDate,
},
});
// select needed partner filters
await testUtils.dom.click(calendar.$('.o_calendar_filter_item[data-value=1] input'));
await testUtils.dom.click(calendar.$('.o_calendar_filter_item[data-value=4] input'));
assert.containsN(calendar, '.o_calendar_filter_item', 5, "should display 5 filter items");
@@ -2515,8 +2536,11 @@ QUnit.module('Views', {
initialDate: initialDate,
},
});
// select filter for partner 1, 2 and 4
await testUtils.dom.click(calendar.$('.o_calendar_filter_item[data-value=1] input')[0]);
await testUtils.dom.click(calendar.$('.o_calendar_filter_item[data-value=2] input'));
await testUtils.dom.click(calendar.$('.o_calendar_filter_item[data-value=4] input'));
await testUtils.dom.click($('.o_calendar_button_prev'));
assert.containsN(calendar, '.o_calendar_filter_item', 6, "should display 6 filter items");
@@ -2541,7 +2565,8 @@ QUnit.module('Views', {
initialDate: initialDate,
},
});
await testUtils.dom.click(calendar.$('.o_calendar_filter_item[data-value=1] input'));
await testUtils.dom.click(calendar.$('.o_calendar_filter_item[data-value=2] input'));
assert.containsN(calendar, '.fc-event', 5,
"should display 5 events");
await testUtils.dom.click(calendar.$('.o_calendar_filter_item[data-value=all] input[type=checkbox]'));
@@ -2625,6 +2650,7 @@ QUnit.module('Views', {
}
},
});
await testUtils.dom.click(calendar.$('.o_calendar_filter_item[data-value=1] input'));
assert.containsOnce(calendar, '.fc-day-grid .fc-event-container',
"should be one event in the all day row");
assert.strictEqual(moment(calendar.model.data.data[0].r_start).date(), 14,
@@ -3677,8 +3703,6 @@ QUnit.module('Views', {
await testUtils.nextTick();
assert.equal($('.o_field_boolean.o_field_widget[name=allday] input').is(':checked'), false,
"The event must not have the all_day active");
await testUtils.dom.click($('.modal button.btn:contains(Discard)'));
calendar.destroy();
});
@@ -3710,6 +3734,10 @@ QUnit.module('Views', {
},
}, {positionalClicks: true});
// select all partner filters
await testUtils.dom.click(calendar.$('.o_calendar_filter_item[data-value=1] input'));
await testUtils.dom.click(calendar.$('.o_calendar_filter_item[data-value=2] input'));
// Check view
assert.containsN(calendar, '.fc-month', 12);
assert.strictEqual(
@@ -3821,6 +3849,9 @@ QUnit.module('Views', {
initialDate: initialDate,
},
});
// activate partner filter
await testUtils.dom.click(calendar.$('.o_calendar_filter_item[data-value=1] input'));
await testUtils.dom.click(calendar.$('.o_calendar_filter_item[data-value=2] input'));
function checkEvents(countMap) {
for (const [id, count] of Object.entries(countMap)) {