[IMP] web,calendar: display attendance status widget in calendar popover
before this commit, widgets which has specialData attribute e.g.many2manyattendee were not supported in calendar popover, as widgets with specialData attribute are meant to be supported in form view. after this commit, widgets with specialData attribute can be added in calendar popover, specialData method is called explicitly from calendar_popover to fetch special data required to render such widgets. with this commit we also did miscellaneous improvement in many2manyattendee - many2many_attendee status widget bullet not aligned with attendee name, with this commit bullet is aligned with attendee name. - if many2many_attendee does not have status value then do not display status else blank space is displayed before attendee name. - in community tentative status color in many2many attendee tags widget has o-brand-secondary color which is almost light grey, so due to badge light grey color and tentative status light grey color status is not displayed. To fix this issue, set tentative status color to simple grey which is bit dark then badge background color. task-2058767 closes odoo/odoo#46396 Signed-off-by: Aaron Bohy (aab) <aab@odoo.com>
This commit is contained in:
committed by
Aaron Bohy
parent
0cde1189c6
commit
47b0c0d2d4
@@ -8,7 +8,7 @@ var relationalFields = require('web.relational_fields');
|
||||
var session = require('web.session');
|
||||
var WebClient = require('web.WebClient');
|
||||
|
||||
var FieldMany2ManyTags = relationalFields.FieldMany2ManyTags;
|
||||
const FieldMany2ManyTagsAvatar = relationalFields.FieldMany2ManyTagsAvatar;
|
||||
|
||||
var CalendarNotification = Notification.extend({
|
||||
template: "CalendarNotification",
|
||||
@@ -137,7 +137,7 @@ BasicModel.include({
|
||||
},
|
||||
});
|
||||
|
||||
var Many2ManyAttendee = FieldMany2ManyTags.extend({
|
||||
const Many2ManyAttendee = FieldMany2ManyTagsAvatar.extend({
|
||||
// as this widget is model dependant (rpc on res.partner), use it in another
|
||||
// context probably won't work
|
||||
// supportedFieldTypes: ['many2many'],
|
||||
|
||||
@@ -3,13 +3,14 @@
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
border-radius: 5px;
|
||||
vertical-align: middle;
|
||||
|
||||
&.accepted {
|
||||
background-color: theme-color('success');
|
||||
}
|
||||
|
||||
&.needsAction {
|
||||
background-color: $o-brand-secondary;
|
||||
&.tentative {
|
||||
background-color: $o-main-color-muted;
|
||||
}
|
||||
|
||||
&.declined {
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<template>
|
||||
<t t-name="Many2ManyAttendeeTag" t-extend="FieldMany2ManyTag">
|
||||
<t t-name="Many2ManyAttendeeTag" t-extend="FieldMany2ManyTagAvatar">
|
||||
<t t-jquery="span:first" t-operation="prepend">
|
||||
<span t-if="attendeesData[el_index]" t-attf-class="o_calendar_invitation #{attendeesData[el_index].status}"/>
|
||||
<span t-if="attendeesData[el_index] and attendeesData[el_index].status and attendeesData[el_index].status !== 'needsAction'"
|
||||
t-attf-class="o_calendar_invitation #{attendeesData[el_index].status}"/>
|
||||
</t>
|
||||
</t>
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ QUnit.module('calendar', {
|
||||
},
|
||||
}, function () {
|
||||
QUnit.test("many2manyattendee widget: basic rendering", async function (assert) {
|
||||
assert.expect(9);
|
||||
assert.expect(12);
|
||||
|
||||
var form = await createView({
|
||||
View: FormView,
|
||||
@@ -55,7 +55,7 @@ QUnit.module('calendar', {
|
||||
"the event id should be passed as argument");
|
||||
return Promise.resolve([
|
||||
[1, "Jesus", "accepted", 0],
|
||||
[2, "Mahomet", "needsAction", 0],
|
||||
[2, "Mahomet", "tentative", 0],
|
||||
]);
|
||||
}
|
||||
return this._super.apply(this, arguments);
|
||||
@@ -71,8 +71,16 @@ QUnit.module('calendar', {
|
||||
"Jesus should attend the meeting");
|
||||
assert.strictEqual(form.$('.o_field_widget[name="partner_ids"] .badge[data-id="2"]').text().trim(), "Mahomet",
|
||||
"the tag should be correctly named");
|
||||
assert.hasClass(form.$('.o_field_widget[name="partner_ids"] .badge[data-id="2"] .o_calendar_invitation'),'needsAction',
|
||||
assert.hasClass(form.el.querySelector('.o_field_widget[name="partner_ids"] .badge[data-id="2"] .o_calendar_invitation'), 'tentative',
|
||||
"Mohamet should still confirm his attendance to the meeting");
|
||||
assert.hasClass(form.el.querySelector('.o_field_many2manytags'), 'avatar',
|
||||
"should have avatar class");
|
||||
assert.containsOnce(form, '.o_field_many2manytags.avatar.o_field_widget .badge:first img',
|
||||
"should have img tag");
|
||||
assert.hasAttrValue(form.$('.o_field_many2manytags.avatar.o_field_widget .badge:first img'),
|
||||
'data-src',
|
||||
'/web/image/partner/1/image_128',
|
||||
"should have correct avatar image");
|
||||
|
||||
form.destroy();
|
||||
});
|
||||
|
||||
@@ -257,7 +257,7 @@
|
||||
color="user_id">
|
||||
<field name="attendee_status"/>
|
||||
<field name="user_id" string="Responsible" filters="1" widget="many2one_avatar_user"/>
|
||||
<field name="partner_ids" widget="many2many_tags_avatar" write_model="calendar.contacts" write_field="partner_id" avatar_field="image_128"/>
|
||||
<field name="partner_ids" widget="many2manyattendee" write_model="calendar.contacts" write_field="partner_id" avatar_field="image_128"/>
|
||||
<field name="is_highlighted" invisible="1"/>
|
||||
<field name="description"/>
|
||||
<field name="privacy"/>
|
||||
|
||||
@@ -95,6 +95,27 @@ var CalendarPopover = Widget.extend(WidgetAdapterMixin, StandaloneFieldManagerMi
|
||||
// Private
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the AbstractField specialization that should be used for the
|
||||
* given field informations. If there is no mentioned specific widget to
|
||||
* use, determines one according the field type.
|
||||
*
|
||||
* @private
|
||||
* @param {Object} field
|
||||
* @param {Object} attrs
|
||||
* @returns {function|null} AbstractField specialization Class
|
||||
*/
|
||||
_getFieldWidgetClass(field, attrs) {
|
||||
let FieldWidget;
|
||||
if (attrs.widget) {
|
||||
FieldWidget = fieldRegistry.getAny(['form' + "." + attrs.widget, attrs.widget]);
|
||||
if (!FieldWidget) {
|
||||
console.warn("Missing widget: ", attrs.widget, " for field", attrs.name, "of type", field.type);
|
||||
}
|
||||
}
|
||||
return FieldWidget || fieldRegistry.getAny(['form' + "." + field.type, field.type, "abstract"]);
|
||||
},
|
||||
|
||||
/**
|
||||
* Generate fields to render into popover
|
||||
*
|
||||
@@ -104,11 +125,15 @@ var CalendarPopover = Widget.extend(WidgetAdapterMixin, StandaloneFieldManagerMi
|
||||
_processFields: function () {
|
||||
var self = this;
|
||||
var fieldsToGenerate = [];
|
||||
const fieldInformation = {};
|
||||
var fields = _.keys(this.displayFields);
|
||||
for (var i=0; i<fields.length; i++) {
|
||||
var fieldName = fields[i];
|
||||
var displayFieldInfo = self.displayFields[fieldName] || {attrs: {invisible: 1}};
|
||||
var fieldInfo = self.fields[fieldName];
|
||||
fieldInformation[fieldName] = {
|
||||
Widget: self._getFieldWidgetClass(fieldInfo, displayFieldInfo.attrs),
|
||||
};
|
||||
var field = {
|
||||
name: fieldName,
|
||||
string: displayFieldInfo.attrs.string || fieldInfo.string,
|
||||
@@ -145,9 +170,12 @@ var CalendarPopover = Widget.extend(WidgetAdapterMixin, StandaloneFieldManagerMi
|
||||
};
|
||||
|
||||
this.$fieldsList = [];
|
||||
return this.model.makeRecord(this.modelName, fieldsToGenerate).then(function (recordID) {
|
||||
return this.model.makeRecord(this.modelName, fieldsToGenerate, fieldInformation).then(async function (recordID) {
|
||||
var defs = [];
|
||||
|
||||
const recordDataPoint = self.model.localData[recordID];
|
||||
recordDataPoint.res_id = self.event.extendedProps.record.id;
|
||||
await self.model._fetchSpecialData(recordDataPoint);
|
||||
var record = self.model.get(recordID);
|
||||
_.each(fieldsToGenerate, function (field) {
|
||||
if (field.invisible) return;
|
||||
|
||||
@@ -2,15 +2,18 @@ odoo.define('web.calendar_tests', function (require) {
|
||||
"use strict";
|
||||
|
||||
var AbstractStorageService = require('web.AbstractStorageService');
|
||||
const BasicModel = require('web.BasicModel');
|
||||
var CalendarView = require('web.CalendarView');
|
||||
var CalendarRenderer = require('web.CalendarRenderer');
|
||||
var Dialog = require('web.Dialog');
|
||||
const fieldRegistry = require('web.field_registry');
|
||||
var ViewDialogs = require('web.view_dialogs');
|
||||
var fieldUtils = require('web.field_utils');
|
||||
var mixins = require('web.mixins');
|
||||
var RamStorage = require('web.RamStorage');
|
||||
var testUtils = require('web.test_utils');
|
||||
var session = require('web.session');
|
||||
const Widget = require('web.Widget');
|
||||
|
||||
var createActionManager = testUtils.createActionManager;
|
||||
|
||||
@@ -912,6 +915,49 @@ QUnit.module('Views', {
|
||||
calendar.destroy();
|
||||
});
|
||||
|
||||
QUnit.test('render popover with widget which has specialData attribute', async function (assert) {
|
||||
assert.expect(3);
|
||||
|
||||
await testUtils.mock.patch(BasicModel, {
|
||||
_fetchSpecialDataForMyWidget() {
|
||||
assert.step("_fetchSpecialDataForMyWidget");
|
||||
return Promise.resolve();
|
||||
},
|
||||
});
|
||||
|
||||
const MyWidget = Widget.extend({
|
||||
specialData: "_fetchSpecialDataForMyWidget",
|
||||
});
|
||||
|
||||
fieldRegistry.add('specialWidget', MyWidget);
|
||||
|
||||
const calendar = await createCalendarView({
|
||||
View: CalendarView,
|
||||
model: 'event',
|
||||
data: this.data,
|
||||
arch: `<calendar class="o_calendar_test"
|
||||
date_start="start"
|
||||
date_stop="stop"
|
||||
all_day="allday"
|
||||
mode="week">
|
||||
<field name="name" string="Custom Name" widget="specialWidget"/>
|
||||
<field name="partner_id"/>
|
||||
</calendar>`,
|
||||
archs,
|
||||
viewOptions: {
|
||||
initialDate,
|
||||
},
|
||||
});
|
||||
|
||||
const event4 = document.querySelectorAll(".fc-event")[0];
|
||||
await testUtils.dom.click(event4);
|
||||
assert.containsOnce(calendar, '.o_cw_popover', "should open a popover clicking on event");
|
||||
assert.verifySteps(["_fetchSpecialDataForMyWidget"]);
|
||||
|
||||
calendar.destroy();
|
||||
await testUtils.mock.unpatch(BasicModel);
|
||||
});
|
||||
|
||||
QUnit.test('attributes hide_date and hide_time', async function (assert) {
|
||||
assert.expect(1);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user