[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:
Prakash Prajapati
2019-09-02 08:53:42 +00:00
committed by Aaron Bohy
co-authored by Mohammed Shekha Aaron Bohy
parent 3978a0838f
commit 50464f5461
7 changed files with 117 additions and 13 deletions
@@ -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' },
+3 -3
View File
@@ -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);
+2 -2
View File
@@ -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