[IMP] todo: integrate mark as done in To-do app

A new field component is designed to offer a simple usage of mark as
done (i.e. only 'done' and 'not done'). This component is used in the
Todo app (in both form and kanban view) to change the state of
todos (private tasks).

Behavior details:
- When the button is checked, the state of the todo is changed to
01_done
- When the button is unchecked, the state is restored to its initial
value if it is different to '01_done' or to '01_in_progress' instead.

task-3085077
This commit is contained in:
Hugo Carlier (Huca)
2023-05-15 09:01:27 +02:00
parent 08ecf62d0c
commit 5b6cc2a3aa
7 changed files with 137 additions and 2 deletions
@@ -0,0 +1,75 @@
/** @odoo-module */
import { useState, onRendered, onMounted } from "@odoo/owl";
import { registry } from "@web/core/registry";
import { StateSelectionField, stateSelectionField } from "@web/views/fields/state_selection/state_selection_field";
export class TodoDoneCheckmark extends StateSelectionField {
setup() {
super.setup();
this.stateDone = useState({
isDone: false, //This state determines the appearance of the done checkmark and should only be actualized when the mouse leaves it (and atfer the form is loaded)
notReloadState: false, //used to avoid a change of the checkmark when re-rendering the form
});
onMounted(() => {
const fieldValue = this.props.record.data[this.props.name]
this.notDoneState = fieldValue == '1_done' ? '01_in_progress' : fieldValue;
});
onRendered(() => {
if (!this.stateDone.notReloadState) {
this.stateDone.isDone = this.props.record.data[this.props.name] == '1_done';
}
});
}
/**
* @private
* @param {InputEvent} ev
*/
actualizeDoneState(ev) {
this.stateDone.notReloadState = false;
}
/**
* @private
* @param {InputEvent} ev
*/
freezeDoneState(ev) {
this.stateDone.notReloadState = true;
}
/**
* @private
* @param {InputEvent} ev
*/
async onDoneToggled(ev) {
const value = this.props.record.data[this.props.name] != '1_done' ? '1_done' : this.notDoneState;
if (['kanban', 'list'].includes(this.props.viewType)) {
await super.updateRecord(value);
}
else {
await this.props.record.update({
[this.props.name]: value,
});
}
}
}
TodoDoneCheckmark.template = 'todo.TodoDoneCheckmark';
TodoDoneCheckmark.props = {
...stateSelectionField.component.props,
viewType: { type: String },
}
export const todoDoneCheckmark = {
...stateSelectionField,
component: TodoDoneCheckmark,
extractProps: (fieldInfo, dynamicInfo) => {
const props = stateSelectionField.extractProps(fieldInfo, dynamicInfo);
props.viewType = fieldInfo.viewType;
return props;
},
}
registry.category("fields").add("todo_done_checkmark", todoDoneCheckmark);
@@ -0,0 +1,27 @@
a.o_todo_done_button {
margin: 0px 5px;
@include o-hover-text-color($gray-400, $o-success);
&:hover:before {
content: "\f058";
}
&.done_button_enabled {
text-color: $o-success;
@include o-hover-text-color($o-success, $gray-400);
&:hover:before {
content: "\f05d";
}
}
}
a.o_todo_done_button_mobile {
margin: 0px 5px;
color: $gray-400;
&.done_button_enabled {
color: $o-success;
}
}
.o_todo_done_checkmark_cell {
width: 40px;
}
@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8" ?>
<templates xml:space="preserve">
<t t-name="todo.TodoDoneCheckmark" owl="1">
<t t-if="!env.isSmall">
<a title="Mark as done"
t-on-click.stop="onDoneToggled"
t-on-mouseleave="actualizeDoneState"
t-on-mouseover="freezeDoneState"
t-attf-class="o_todo_done_button fa fa-lg fa-check-circle{{!stateDone.isDone ? '-o' : ' done_button_enabled'}}"/>
</t>
<t t-else="">
<a title="Mark as done"
t-on-click.stop="onDoneToggled"
t-attf-class="o_todo_done_button_mobile fa fa-lg fa-check-circle{{!stateDone.isDone ? '-o' : ' done_button_enabled'}}"/>
</t>
</t>
</templates>
@@ -13,8 +13,8 @@
<t t-name="todo.TodoBreadcrumbs" t-inherit="web.Breadcrumbs" t-inherit-mode="primary" owl="1">
<xpath expr="//div[hasclass('o_last_breadcrumb_item')]//span" position="replace">
<t t-slot="control-panel-todo-name" />
<t t-slot="todo-mark-as-done-button" />
</xpath>
</t>
</templates>
@@ -2,11 +2,13 @@
import { FormController } from "@web/views/form/form_controller";
import { TodoEditableBreadcrumbName } from "@todo/components/todo_editable_breadcrumb_name/todo_editable_breadcrumb_name";
import { TodoDoneCheckmark } from "@todo/components/todo_done_checkmark/todo_done_checkmark";
import { onWillStart } from "@odoo/owl";
/**
* The FormController is overridden to be able to manage the edition of the name of a to-do directly
* in the breadcrumb.
* in the breadcrumb as well as the mark as done button next to it.
*/
export class TodoFormController extends FormController {
@@ -48,4 +50,5 @@ export class TodoFormController extends FormController {
Object.assign(TodoFormController.components, {
TodoEditableBreadcrumbName,
TodoDoneCheckmark,
});
@@ -12,6 +12,15 @@
id="'name'"
/>
</t>
<t t-set-slot="todo-mark-as-done-button">
<t t-set="record" t-value="model.root"/>
<TodoDoneCheckmark
name="'state'"
record="record"
id="'state'"
viewType="'form'"
/>
</t>
</xpath>
</t>
+2
View File
@@ -81,6 +81,7 @@
editable="bottom"
multi_edit="1"
js_class="todo_list">
<field name="state" widget="todo_done_checkmark" nolabel="1" options="{'hide_label': 1}"/>
<field name="name"/>
<field name="user_ids" optional="show" widget="many2many_avatar_user" options="{'no_quick_create': True}"/>
<field name="tag_ids" optional="show" widget="many2many_tags" options="{'color_field': 'color', 'no_create_edit': True}"/>
@@ -118,6 +119,7 @@
widget="statusbar"
options="{'clickable': '1', 'fold_field': 'fold'}"/>
<field name="active" invisible="1"/>
<field name="state" invisible="1"/>
</header>
<sheet class="o_todo_form_sheet_bg">
<widget name="web_ribbon" class="todo_archived" title="Archived" bg_color="text-bg-danger" attrs="{'invisible': [('active', '=', True)]}"/>