diff --git a/addons/mail/static/src/js/views/activity/activity_renderer.js b/addons/mail/static/src/js/views/activity/activity_renderer.js
index c75807a10f0..480bad864e7 100644
--- a/addons/mail/static/src/js/views/activity/activity_renderer.js
+++ b/addons/mail/static/src/js/views/activity/activity_renderer.js
@@ -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' },
diff --git a/addons/mail/static/tests/activity_tests.js b/addons/mail/static/tests/activity_tests.js
index 6725aab2e33..8134e6699f5 100644
--- a/addons/mail/static/tests/activity_tests.js
+++ b/addons/mail/static/tests/activity_tests.js
@@ -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');
diff --git a/addons/web/static/src/js/views/kanban/kanban_column_progressbar.js b/addons/web/static/src/js/views/kanban/kanban_column_progressbar.js
index 6a9caa99a39..3e9197d3fa2 100644
--- a/addons/web/static/src/js/views/kanban/kanban_column_progressbar.js
+++ b/addons/web/static/src/js/views/kanban/kanban_column_progressbar.js
@@ -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({
// 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]);
}
});
diff --git a/addons/web/static/src/scss/kanban_column_progressbar.scss b/addons/web/static/src/scss/kanban_column_progressbar.scss
index d70644f7e4a..bfea16934c5 100644
--- a/addons/web/static/src/scss/kanban_column_progressbar.scss
+++ b/addons/web/static/src/scss/kanban_column_progressbar.scss
@@ -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;
diff --git a/addons/web/static/src/scss/webclient.scss b/addons/web/static/src/scss/webclient.scss
index fca898f262a..d41d1fb0ba3 100644
--- a/addons/web/static/src/scss/webclient.scss
+++ b/addons/web/static/src/scss/webclient.scss
@@ -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;
diff --git a/addons/web/static/tests/views/kanban_tests.js b/addons/web/static/tests/views/kanban_tests.js
index 9e6b8ddd11d..4dbfb6d83fc 100644
--- a/addons/web/static/tests/views/kanban_tests.js
+++ b/addons/web/static/tests/views/kanban_tests.js
@@ -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:
+ '' +
+ '' +
+ '' +
+ '' +
+ '' +
+ '' +
+ '' +
+ '
' +
+ '' +
+ '',
+ 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:
+ '' +
+ '' +
+ '' +
+ '' +
+ '' +
+ '' +
+ '' +
+ '' +
+ '
' +
+ '' +
+ '',
+ 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);
diff --git a/doc/reference/views.rst b/doc/reference/views.rst
index 3955c5242d0..efe07c48076 100644
--- a/doc/reference/views.rst
+++ b/doc/reference/views.rst
@@ -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