[IMP] project: Improve ux
Before this commit: - project.task > list view: Currently, the progress is displayed in purple if less than 100% and in red if above. The two colors don't contrast a lot, making it hard to read. In addition, it is not coherent with the color we usually apply for the remaining hours in the rest of the app. - onbording tour: the manual saving step is no longer necessary as records are now automatically saved. In addition, showing it doesn't provide a wow effect and is an extra opportunity to lose people in the process After this commit: - the following color code was applied for the progress bar: *if progress < 80 -> green *if progress is between 80 and 100 included -> orange *if progress > 100 -> red - step removed. task-3475170 closes odoo/odoo#132854 Related: odoo/enterprise#46162 Signed-off-by: Xavier Bol (xbo) <xbo@odoo.com>
This commit is contained in:
+21
@@ -0,0 +1,21 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import { registry } from "@web/core/registry";
|
||||
import { progressBarField, ProgressBarField } from "@web/views/fields/progress_bar/progress_bar_field";
|
||||
|
||||
export class ProjectTaskProgressBarField extends ProgressBarField {
|
||||
get progressBarColorClass() {
|
||||
if (this.currentValue > this.maxValue) {
|
||||
return super.progressBarColorClass;
|
||||
}
|
||||
|
||||
return this.currentValue < 80 ? "bg-success" : "bg-warning";
|
||||
}
|
||||
}
|
||||
|
||||
export const projectTaskProgressBarField = {
|
||||
...progressBarField,
|
||||
component: ProjectTaskProgressBarField,
|
||||
};
|
||||
|
||||
registry.category("fields").add("project_task_progressbar", projectTaskProgressBarField);
|
||||
@@ -35,11 +35,12 @@ export const getServerData = () => JSON.parse(JSON.stringify({
|
||||
fields: {
|
||||
name: { string: "Name", type: "string" },
|
||||
project_id: { string: "Project", type: "many2one", relation: "project.project" },
|
||||
progress: { string: "progress", type: "float" },
|
||||
},
|
||||
records: [
|
||||
{ id: 1, name: "Task 1\u00A0AdditionalInfo", project_id: 1 },
|
||||
{ id: 2, name: "Task 2\u00A0AdditionalInfo", project_id: 1 },
|
||||
{ id: 3, name: "Task 3\u00A0AdditionalInfo", project_id: 1 },
|
||||
{ id: 1, name: "Task 1\u00A0AdditionalInfo", project_id: 1, progress: 50 },
|
||||
{ id: 2, name: "Task 2\u00A0AdditionalInfo", project_id: 1, progress: 80 },
|
||||
{ id: 3, name: "Task 3\u00A0AdditionalInfo", project_id: 1, progress: 104 },
|
||||
],
|
||||
},
|
||||
},
|
||||
|
||||
@@ -64,4 +64,22 @@ QUnit.module("hr_timesheet", (hooks) => {
|
||||
assert.containsN(firstRow, '.o_list_many2one[name=task_id] .dropdown ul li:contains("AdditionalInfo")', 3);
|
||||
});
|
||||
|
||||
QUnit.test("project task progress bar color", async function (assert) {
|
||||
await makeView({
|
||||
serverData,
|
||||
type: "list",
|
||||
resModel: "project.task",
|
||||
arch: `
|
||||
<tree>
|
||||
<field name="name"/>
|
||||
<field name="project_id"/>
|
||||
<field name="progress" widget="project_task_progressbar" options="{'overflow_class': 'bg-danger'}"/>
|
||||
</tree>
|
||||
`,
|
||||
});
|
||||
|
||||
assert.containsOnce(target, "div.o_progressbar .bg-success", "Task 1 having progress = 50 < 80 => green color")
|
||||
assert.containsOnce(target, "div.o_progressbar .bg-warning", "Task 2 having progress = 80 >= 80 => orange color")
|
||||
assert.containsOnce(target, "div.o_progressbar .bg-success", "Task 3 having progress = 101 > 100 => red color")
|
||||
});
|
||||
});
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
<field name="subtask_effective_hours" string="Sub-tasks Hours Spent" widget="timesheet_uom" sum="Sub-tasks Hours Spent" optional="hide"/>
|
||||
<field name="total_hours_spent" string="Total Hours" widget="timesheet_uom" sum="Total Hours" optional="hide"/>
|
||||
<field name="remaining_hours" widget="timesheet_uom" sum="Remaining Hours" optional="hide" decoration-danger="progress >= 100" decoration-warning="progress >= 80 and progress < 100"/>
|
||||
<field name="progress" widget="progressbar" optional="hide" options="{'overflow_class': 'bg-danger'}"/>
|
||||
<field name="progress" widget="project_task_progressbar" optional="hide" options="{'overflow_class': 'bg-danger'}"/>
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='partner_id']" position="after">
|
||||
<field name="encode_uom_in_days" invisible="1"/>
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
<field name="subtask_effective_hours" widget="timesheet_uom" sum="Sub-tasks Hours Spent" optional="hide" groups="hr_timesheet.group_hr_timesheet_user"/>
|
||||
<field name="total_hours_spent" widget="timesheet_uom" sum="Total Hours" optional="hide" groups="hr_timesheet.group_hr_timesheet_user"/>
|
||||
<field name="remaining_hours" widget="timesheet_uom" sum="Remaining Hours" optional="hide" decoration-danger="progress >= 100" decoration-warning="progress >= 80 and progress < 100" groups="hr_timesheet.group_hr_timesheet_user"/>
|
||||
<field name="progress" widget="progressbar" optional="hide" options="{'overflow_class': 'bg-danger'}" groups="hr_timesheet.group_hr_timesheet_user"/>
|
||||
<field name="progress" widget="project_task_progressbar" optional="hide" options="{'overflow_class': 'bg-danger'}" groups="hr_timesheet.group_hr_timesheet_user"/>
|
||||
</xpath>
|
||||
<xpath expr="//label[@for='date_deadline']" position="before">
|
||||
<field name="encode_uom_in_days" invisible="1"/>
|
||||
@@ -148,7 +148,7 @@
|
||||
<field name="subtask_effective_hours" widget="timesheet_uom" optional="hide" groups="hr_timesheet.group_hr_timesheet_user"/>
|
||||
<field name="total_hours_spent" widget="timesheet_uom" optional="hide" groups="hr_timesheet.group_hr_timesheet_user"/>
|
||||
<field name="remaining_hours" widget="timesheet_uom" sum="Remaining Hours" optional="hide" decoration-danger="progress >= 100" decoration-warning="progress >= 80 and progress < 100" groups="hr_timesheet.group_hr_timesheet_user"/>
|
||||
<field name="progress" widget="progressbar" optional="hide" options="{'overflow_class': 'bg-danger'}" groups="hr_timesheet.group_hr_timesheet_user"/>
|
||||
<field name="progress" widget="project_task_progressbar" optional="hide" options="{'overflow_class': 'bg-danger'}" groups="hr_timesheet.group_hr_timesheet_user"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
@@ -166,7 +166,7 @@
|
||||
<field name="subtask_effective_hours" widget="timesheet_uom" sum="Sub-Tasks Total Effective Hours" optional="hide"/>
|
||||
<field name="total_hours_spent" widget="timesheet_uom" sum="Total Hours" optional="hide"/>
|
||||
<field name="remaining_hours" widget="timesheet_uom" sum="Total Remaining Hours" optional="hide" decoration-danger="progress >= 100" decoration-warning="progress >= 80 and progress < 100" invisible="allocated_hours == 0"/>
|
||||
<field name="progress" widget="progressbar" avg="Average of Progress" optional="show" groups="hr_timesheet.group_hr_timesheet_user" invisible="allocated_hours == 0" options="{'overflow_class': 'bg-danger'}" />
|
||||
<field name="progress" widget="project_task_progressbar" avg="Average of Progress" optional="show" groups="hr_timesheet.group_hr_timesheet_user" invisible="allocated_hours == 0" options="{'overflow_class': 'bg-danger'}" />
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
@@ -121,11 +121,6 @@ registry.category("web_tour.tours").add('project_tour', {
|
||||
}, {
|
||||
trigger: ".ui-autocomplete > li > a:not(:has(i.fa))",
|
||||
auto: true,
|
||||
}, {
|
||||
trigger: ".o_form_button_save",
|
||||
extra_trigger: '.o_form_project_tasks .o_form_dirty',
|
||||
content: markup(_t("You have unsaved changes - no worries! Odoo will automatically save it as you navigate.<br/> You can discard these changes from here or manually save your task.<br/>Let's save it manually.")),
|
||||
position: "bottom",
|
||||
}, {
|
||||
trigger: ".breadcrumb-item:not(.active):last",
|
||||
extra_trigger: '.o_form_project_tasks',
|
||||
|
||||
@@ -54,6 +54,10 @@ export class ProgressBarField extends Component {
|
||||
return this.props.record.data[this.maxValueField] || 100;
|
||||
}
|
||||
|
||||
get progressBarColorClass() {
|
||||
return this.currentValue > this.maxValue ? this.props.overflowClass : "bg-primary";
|
||||
}
|
||||
|
||||
formatCurrentValue(humanReadable = !this.state.isEditing) {
|
||||
const formatter = formatters.get(Number.isInteger(this.currentValue) ? "integer" : "float");
|
||||
return formatter(this.currentValue, { humanReadable });
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<div class="o_progressbar w-100 d-flex align-items-center" t-ref="numpadDecimal">
|
||||
<div t-if="props.title" class="o_progressbar_title text-nowrap pe-1"><t t-esc="props.title"/></div>
|
||||
<div class="o_progress align-middle overflow-hidden" aria-valuemin="0" t-att-aria-valuemax="this.maxValue" t-att-aria-valuenow="this.currentValue">
|
||||
<div t-attf-class="{{ this.currentValue > this.maxValue ? props.overflowClass : 'bg-primary' }} h-100" t-att-style="'width: min(' + 100 * this.currentValue / this.maxValue + '%, 100%)'"></div>
|
||||
<div t-attf-class="{{ progressBarColorClass }} h-100" t-att-style="'width: min(' + 100 * this.currentValue / this.maxValue + '%, 100%)'"></div>
|
||||
</div>
|
||||
<div class="o_progressbar_value d-flex">
|
||||
<t t-if="isPercentage">
|
||||
|
||||
Reference in New Issue
Block a user