[IMP] web,mail: kanban progressbar: filter on falsy value
This rev. makes the part of the progressbar corresponding to falsy records 'alive': the user can now click on it to filter on records whose value is false for the progressbar field. Task 1952430 closes odoo/odoo#35100 Signed-off-by: Aaron Bohy (aab) <aab@odoo.com> Co-authored-by: Mohammed Shekha <msh@odoo.com> Co-authored-by: Aaron Bohy <aab@odoo.com>
This commit is contained in:
committed by
Aaron Bohy
co-authored by
Mohammed Shekha
Aaron Bohy
parent
3978a0838f
commit
50464f5461
@@ -157,7 +157,17 @@ var ActivityRenderer = AbstractRenderer.extend({
|
||||
columnID: typeId,
|
||||
progressBarStates: {},
|
||||
}, {
|
||||
count: _.reduce(_.values(counts), function (x, y) { return x + y; }),
|
||||
count: _.reduce(_.values(counts), (x, y) => x + y),
|
||||
fields: {
|
||||
activity_state: {
|
||||
type: 'selection',
|
||||
selection: [
|
||||
['planned', _t('Planned')],
|
||||
['today', _t('Today')],
|
||||
['overdue', _t('Overdue')],
|
||||
],
|
||||
},
|
||||
},
|
||||
progressBarValues: {
|
||||
field: 'activity_state',
|
||||
colors: { planned: 'success', today: 'warning', overdue: 'danger' },
|
||||
|
||||
@@ -156,13 +156,13 @@ QUnit.test('activity view: simple activity rendering', async function (assert) {
|
||||
var $th1 = activity.$('table thead tr:first th:nth-child(2)');
|
||||
assert.containsOnce($th1, 'span:first:contains(Email)', 'should contain "Email" in header of first column');
|
||||
assert.containsOnce($th1, '.o_kanban_counter', 'should contain a progressbar in header of first column');
|
||||
assert.hasAttrValue($th1.find('.o_kanban_counter_progress .progress-bar:first'), 'data-original-title', '1 planned',
|
||||
assert.hasAttrValue($th1.find('.o_kanban_counter_progress .progress-bar:first'), 'data-original-title', '1 Planned',
|
||||
'the counter progressbars should be correctly displayed');
|
||||
assert.hasAttrValue($th1.find('.o_kanban_counter_progress .progress-bar:nth-child(2)'), 'data-original-title', '1 today',
|
||||
assert.hasAttrValue($th1.find('.o_kanban_counter_progress .progress-bar:nth-child(2)'), 'data-original-title', '1 Today',
|
||||
'the counter progressbars should be correctly displayed');
|
||||
var $th2 = activity.$('table thead tr:first th:nth-child(3)');
|
||||
assert.containsOnce($th2, 'span:first:contains(Call)', 'should contain "Call" in header of second column');
|
||||
assert.hasAttrValue($th2.find('.o_kanban_counter_progress .progress-bar:last'), 'data-original-title', '1 overdue',
|
||||
assert.hasAttrValue($th2.find('.o_kanban_counter_progress .progress-bar:nth-child(3)'), 'data-original-title', '1 Overdue',
|
||||
'the counter progressbars should be correctly displayed');
|
||||
assert.containsNone(activity, 'table thead tr:first th:nth-child(4) .o_kanban_counter',
|
||||
'should not contain a progressbar in header of 3rd column');
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
odoo.define('web.KanbanColumnProgressBar', function (require) {
|
||||
'use strict';
|
||||
|
||||
var Widget = require('web.Widget');
|
||||
const core = require('web.core');
|
||||
var session = require('web.session');
|
||||
var utils = require('web.utils');
|
||||
var Widget = require('web.Widget');
|
||||
|
||||
const _t = core._t;
|
||||
|
||||
var KanbanColumnProgressBar = Widget.extend({
|
||||
template: 'KanbanView.ColumnProgressBar',
|
||||
@@ -28,7 +31,9 @@ var KanbanColumnProgressBar = Widget.extend({
|
||||
|
||||
// <progressbar/> attributes
|
||||
this.fieldName = columnState.progressBarValues.field;
|
||||
this.colors = columnState.progressBarValues.colors;
|
||||
this.colors = _.extend({}, columnState.progressBarValues.colors, {
|
||||
__false: 'muted', // color to use for false value
|
||||
});
|
||||
this.sumField = columnState.progressBarValues.sum_field;
|
||||
|
||||
// Previous progressBar state
|
||||
@@ -55,7 +60,7 @@ var KanbanColumnProgressBar = Widget.extend({
|
||||
|
||||
this.$bars = {};
|
||||
_.each(this.colors, function (val, key) {
|
||||
self.$bars[val] = self.$('.bg-' + val + '-full');
|
||||
self.$bars[key] = self.$(`.progress-bar[data-filter=${key}]`);
|
||||
});
|
||||
this.$counter = this.$('.o_kanban_counter_side');
|
||||
this.$number = this.$counter.find('b');
|
||||
@@ -77,18 +82,22 @@ var KanbanColumnProgressBar = Widget.extend({
|
||||
// current use of progressbars
|
||||
|
||||
var subgroupCounts = {};
|
||||
let allSubgroupCount = 0;
|
||||
_.each(self.colors, function (val, key) {
|
||||
var subgroupCount = self.columnState.progressBarValues.counts[key] || 0;
|
||||
if (self.activeFilter === key && subgroupCount === 0) {
|
||||
self.activeFilter = false;
|
||||
}
|
||||
subgroupCounts[key] = subgroupCount;
|
||||
allSubgroupCount += subgroupCount;
|
||||
});
|
||||
subgroupCounts.__false = self.columnState.count - allSubgroupCount;
|
||||
|
||||
self.groupCount = self.columnState.count;
|
||||
self.subgroupCounts = subgroupCounts;
|
||||
self.prevTotalCounterValue = self.totalCounterValue;
|
||||
self.totalCounterValue = self.sumField ? (self.columnState.aggregateValues[self.sumField] || 0) : self.columnState.count;
|
||||
|
||||
self._notifyState();
|
||||
self._render();
|
||||
});
|
||||
@@ -121,7 +130,7 @@ var KanbanColumnProgressBar = Widget.extend({
|
||||
});
|
||||
this.trigger_up('tweak_column_records', {
|
||||
callback: function ($el, recordData) {
|
||||
var categoryValue = recordData[self.fieldName];
|
||||
var categoryValue = recordData[self.fieldName] ? recordData[self.fieldName] : '__false';
|
||||
_.each(self.colors, function (val, key) {
|
||||
$el.removeClass('oe_kanban_card_' + val);
|
||||
});
|
||||
@@ -134,8 +143,9 @@ var KanbanColumnProgressBar = Widget.extend({
|
||||
// Display and animate the progress bars
|
||||
var barNumber = 0;
|
||||
var barMinWidth = 6; // In %
|
||||
const selection = self.columnState.fields[self.fieldName].selection;
|
||||
_.each(self.colors, function (val, key) {
|
||||
var $bar = self.$bars[val];
|
||||
var $bar = self.$bars[key];
|
||||
var count = self.subgroupCounts && self.subgroupCounts[key] || 0;
|
||||
|
||||
if (!$bar) {
|
||||
@@ -143,7 +153,14 @@ var KanbanColumnProgressBar = Widget.extend({
|
||||
}
|
||||
|
||||
// Adapt tooltip
|
||||
$bar.attr('data-original-title', count + ' ' + key);
|
||||
let value;
|
||||
if (selection) { // progressbar on a field of type selection
|
||||
const option = selection.find(option => option[0] === key);
|
||||
value = option && option[1] || _t('Other');
|
||||
} else {
|
||||
value = key;
|
||||
}
|
||||
$bar.attr('data-original-title', count + ' ' + value);
|
||||
$bar.tooltip({
|
||||
delay: 0,
|
||||
trigger: 'hover',
|
||||
@@ -181,7 +198,8 @@ var KanbanColumnProgressBar = Widget.extend({
|
||||
end = 0;
|
||||
_.each(self.columnState.data, function (record) {
|
||||
var recordData = record.data;
|
||||
if (self.activeFilter === recordData[self.fieldName]) {
|
||||
if (self.activeFilter === recordData[self.fieldName] ||
|
||||
(self.activeFilter === '__false' && !recordData[self.fieldName])) {
|
||||
end += parseFloat(recordData[self.sumField]);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -110,6 +110,7 @@
|
||||
@include o-kanban-css-filter(success, theme-color('success'));
|
||||
@include o-kanban-css-filter(warning, theme-color('warning'));
|
||||
@include o-kanban-css-filter(danger, theme-color('danger'));
|
||||
@include o-kanban-css-filter(muted, theme-color('dark'));
|
||||
|
||||
&.o_kanban_group_show {
|
||||
display: flex;
|
||||
|
||||
@@ -78,6 +78,9 @@ div.o_boolean_toggle.custom-control.custom-checkbox {
|
||||
.bg-info-full {
|
||||
background-color: theme-color('info');
|
||||
}
|
||||
.bg-muted-full {
|
||||
background-color: #dee2e6;
|
||||
}
|
||||
|
||||
.o_web_accesskey_overlay {
|
||||
font-family: $font-family-sans-serif;
|
||||
|
||||
@@ -4624,6 +4624,78 @@ QUnit.module('Views', {
|
||||
kanban.destroy();
|
||||
});
|
||||
|
||||
QUnit.test('column progressbars: "false" bar is clickable', async function (assert) {
|
||||
assert.expect(8);
|
||||
|
||||
this.data.partner.records.push({id: 5, bar: true, foo: false, product_id: 5, state: "ghi"});
|
||||
var kanban = await createView({
|
||||
View: KanbanView,
|
||||
model: 'partner',
|
||||
data: this.data,
|
||||
arch:
|
||||
'<kanban>' +
|
||||
'<field name="bar"/>' +
|
||||
'<field name="int_field"/>' +
|
||||
'<progressbar field="foo" colors=\'{"yop": "success", "gnap": "warning", "blip": "danger"}\'/>' +
|
||||
'<templates><t t-name="kanban-box">' +
|
||||
'<div>' +
|
||||
'<field name="name"/>' +
|
||||
'</div>' +
|
||||
'</t></templates>' +
|
||||
'</kanban>',
|
||||
groupBy: ['bar'],
|
||||
});
|
||||
|
||||
assert.containsN(kanban, '.o_kanban_group', 2);
|
||||
assert.strictEqual(kanban.$('.o_kanban_counter:last .o_kanban_counter_side').text(), "4");
|
||||
assert.containsN(kanban, '.o_kanban_counter_progress:last .progress-bar', 4);
|
||||
assert.containsOnce(kanban, '.o_kanban_counter_progress:last .progress-bar[data-filter="__false"]',
|
||||
"should have false kanban color");
|
||||
assert.hasClass(kanban.$('.o_kanban_counter_progress:last .progress-bar[data-filter="__false"]'), 'bg-muted-full');
|
||||
|
||||
await testUtils.dom.click(kanban.$('.o_kanban_counter_progress:last .progress-bar[data-filter="__false"]'));
|
||||
|
||||
assert.hasClass(kanban.$('.o_kanban_counter_progress:last .progress-bar[data-filter="__false"]'), 'progress-bar-animated');
|
||||
assert.hasClass(kanban.$('.o_kanban_group:last'), 'o_kanban_group_show_muted');
|
||||
assert.strictEqual(kanban.$('.o_kanban_counter:last .o_kanban_counter_side').text(), "1");
|
||||
|
||||
kanban.destroy();
|
||||
});
|
||||
|
||||
QUnit.test('column progressbars: "false" bar with sum_field', async function (assert) {
|
||||
assert.expect(4);
|
||||
|
||||
this.data.partner.records.push({id: 5, bar: true, foo: false, int_field: 15, product_id: 5, state: "ghi"});
|
||||
var kanban = await createView({
|
||||
View: KanbanView,
|
||||
model: 'partner',
|
||||
data: this.data,
|
||||
arch:
|
||||
'<kanban>' +
|
||||
'<field name="bar"/>' +
|
||||
'<field name="int_field"/>' +
|
||||
'<field name="foo"/>' +
|
||||
'<progressbar field="foo" colors=\'{"yop": "success", "gnap": "warning", "blip": "danger"}\' sum_field="int_field"/>' +
|
||||
'<templates><t t-name="kanban-box">' +
|
||||
'<div>' +
|
||||
'<field name="name"/>' +
|
||||
'</div>' +
|
||||
'</t></templates>' +
|
||||
'</kanban>',
|
||||
groupBy: ['bar'],
|
||||
});
|
||||
|
||||
assert.containsN(kanban, '.o_kanban_group', 2);
|
||||
assert.strictEqual(kanban.$('.o_kanban_counter:last .o_kanban_counter_side').text(), "51");
|
||||
|
||||
await testUtils.dom.click(kanban.$('.o_kanban_counter_progress:last .progress-bar[data-filter="__false"]'));
|
||||
|
||||
assert.hasClass(kanban.$('.o_kanban_counter_progress:last .progress-bar[data-filter="__false"]'), 'progress-bar-animated');
|
||||
assert.strictEqual(kanban.$('.o_kanban_counter:last .o_kanban_counter_side').text(), "15");
|
||||
|
||||
kanban.destroy();
|
||||
});
|
||||
|
||||
QUnit.test('column progressbars should not crash in non grouped views', async function (assert) {
|
||||
assert.expect(3);
|
||||
|
||||
|
||||
@@ -1076,8 +1076,8 @@ Possible children of the view element are:
|
||||
the progressbar
|
||||
|
||||
``colors`` (required)
|
||||
JSON mapping the above field values to either "danger", "warning" or
|
||||
"success" colors
|
||||
JSON mapping the above field values to either "danger", "warning", "success"
|
||||
or "muted" colors
|
||||
|
||||
``sum_field`` (optional)
|
||||
the name of the field whose column's records' values will be summed and
|
||||
|
||||
Reference in New Issue
Block a user