From 178f166827a1d63d24b602c1b7e0ba02cb4093aa Mon Sep 17 00:00:00 2001 From: Arturo Yepez Date: Mon, 24 Aug 2026 11:44:04 +0200 Subject: [PATCH 01/19] [IMP] awesome_owl: displaying the counter --- awesome_owl/static/src/playground.js | 9 ++++++++- awesome_owl/static/src/playground.xml | 4 ++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/awesome_owl/static/src/playground.js b/awesome_owl/static/src/playground.js index 4ac769b0aa5..b3d295c3a99 100644 --- a/awesome_owl/static/src/playground.js +++ b/awesome_owl/static/src/playground.js @@ -1,5 +1,12 @@ -import { Component } from "@odoo/owl"; +import { Component, useState } from "@odoo/owl"; + export class Playground extends Component { static template = "awesome_owl.playground"; + + counter = useState({ value: 0 }); + + increment() { + this.counter.value += 1 + } } diff --git a/awesome_owl/static/src/playground.xml b/awesome_owl/static/src/playground.xml index 4fb905d59f9..208d1539c80 100644 --- a/awesome_owl/static/src/playground.xml +++ b/awesome_owl/static/src/playground.xml @@ -5,6 +5,10 @@
hello world
+
+

Value:

+ +
From 4ffc2d5e6f986d1a5a185313f31466338b75eab1 Mon Sep 17 00:00:00 2001 From: Arturo Yepez Date: Mon, 24 Aug 2026 11:48:55 +0200 Subject: [PATCH 02/19] [IMP] awesome_owl: extract logic into its own sub-component --- awesome_owl/static/src/components/counter/counter.js | 11 +++++++++++ awesome_owl/static/src/components/counter/counter.xml | 11 +++++++++++ awesome_owl/static/src/playground.js | 2 ++ awesome_owl/static/src/playground.xml | 1 + 4 files changed, 25 insertions(+) create mode 100644 awesome_owl/static/src/components/counter/counter.js create mode 100644 awesome_owl/static/src/components/counter/counter.xml diff --git a/awesome_owl/static/src/components/counter/counter.js b/awesome_owl/static/src/components/counter/counter.js new file mode 100644 index 00000000000..2eb2ce06527 --- /dev/null +++ b/awesome_owl/static/src/components/counter/counter.js @@ -0,0 +1,11 @@ +import { Component, useState } from "@odoo/owl"; + +export class Counter extends Component { + static template = "awesome_owl.counter"; + + counter = useState({ value: 0 }) + + increment() { + this.counter.value += 1 + } +} diff --git a/awesome_owl/static/src/components/counter/counter.xml b/awesome_owl/static/src/components/counter/counter.xml new file mode 100644 index 00000000000..4ae4e2a83b7 --- /dev/null +++ b/awesome_owl/static/src/components/counter/counter.xml @@ -0,0 +1,11 @@ + + + + +
+

Value:

+ +
+
+ +
diff --git a/awesome_owl/static/src/playground.js b/awesome_owl/static/src/playground.js index b3d295c3a99..689a0b531a2 100644 --- a/awesome_owl/static/src/playground.js +++ b/awesome_owl/static/src/playground.js @@ -1,8 +1,10 @@ import { Component, useState } from "@odoo/owl"; +import { Counter } from "./components/counter/counter"; export class Playground extends Component { static template = "awesome_owl.playground"; + static components = { Counter }; counter = useState({ value: 0 }); diff --git a/awesome_owl/static/src/playground.xml b/awesome_owl/static/src/playground.xml index 208d1539c80..fc995f1f02f 100644 --- a/awesome_owl/static/src/playground.xml +++ b/awesome_owl/static/src/playground.xml @@ -9,6 +9,7 @@

Value:

+ From 640d2697319d9a05e25bbfc6e483a6a93997a85f Mon Sep 17 00:00:00 2001 From: Arturo Yepez Date: Mon, 24 Aug 2026 15:24:07 +0200 Subject: [PATCH 03/19] [IMP] awesome_owl: new component for card A new component for a Card, with its own props was created --- awesome_owl/static/src/components/card/card.js | 7 +++++++ awesome_owl/static/src/components/card/card.xml | 15 +++++++++++++++ awesome_owl/static/src/playground.js | 3 ++- awesome_owl/static/src/playground.xml | 4 ++++ 4 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 awesome_owl/static/src/components/card/card.js create mode 100644 awesome_owl/static/src/components/card/card.xml diff --git a/awesome_owl/static/src/components/card/card.js b/awesome_owl/static/src/components/card/card.js new file mode 100644 index 00000000000..f9b3c770f7e --- /dev/null +++ b/awesome_owl/static/src/components/card/card.js @@ -0,0 +1,7 @@ +import { Component, useState } from "@odoo/owl"; + +export class Card extends Component { + static template = "awesome_owl.card"; + static props = ["title", "content"] + +} diff --git a/awesome_owl/static/src/components/card/card.xml b/awesome_owl/static/src/components/card/card.xml new file mode 100644 index 00000000000..c9e73c30213 --- /dev/null +++ b/awesome_owl/static/src/components/card/card.xml @@ -0,0 +1,15 @@ + + + + +
+
+
+

+ +

+
+
+
+ +
diff --git a/awesome_owl/static/src/playground.js b/awesome_owl/static/src/playground.js index 689a0b531a2..e0bc7cc6a46 100644 --- a/awesome_owl/static/src/playground.js +++ b/awesome_owl/static/src/playground.js @@ -1,10 +1,11 @@ import { Component, useState } from "@odoo/owl"; import { Counter } from "./components/counter/counter"; +import { Card } from "./components/card/card" export class Playground extends Component { static template = "awesome_owl.playground"; - static components = { Counter }; + static components = { Counter, Card }; counter = useState({ value: 0 }); diff --git a/awesome_owl/static/src/playground.xml b/awesome_owl/static/src/playground.xml index fc995f1f02f..bbda6aa8fdb 100644 --- a/awesome_owl/static/src/playground.xml +++ b/awesome_owl/static/src/playground.xml @@ -10,6 +10,10 @@ +
+ + +
From f2b3ec23806326be4f8208baa84ddcb4a6e7b2f6 Mon Sep 17 00:00:00 2001 From: Arturo Yepez Date: Mon, 24 Aug 2026 15:45:00 +0200 Subject: [PATCH 04/19] [IMP] awesome_owl: escape the props of the card Per Chapter 1 of the Web Framework tutorial --- awesome_owl/static/src/components/card/card.xml | 4 ++-- awesome_owl/static/src/playground.js | 5 ++++- awesome_owl/static/src/playground.xml | 4 ++-- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/awesome_owl/static/src/components/card/card.xml b/awesome_owl/static/src/components/card/card.xml index c9e73c30213..05f4c959138 100644 --- a/awesome_owl/static/src/components/card/card.xml +++ b/awesome_owl/static/src/components/card/card.xml @@ -4,9 +4,9 @@
-
+

- +

diff --git a/awesome_owl/static/src/playground.js b/awesome_owl/static/src/playground.js index e0bc7cc6a46..0a497fe2a09 100644 --- a/awesome_owl/static/src/playground.js +++ b/awesome_owl/static/src/playground.js @@ -1,4 +1,4 @@ -import { Component, useState } from "@odoo/owl"; +import { Component, useState, markup } from "@odoo/owl"; import { Counter } from "./components/counter/counter"; import { Card } from "./components/card/card" @@ -9,6 +9,9 @@ export class Playground extends Component { counter = useState({ value: 0 }); + card1Content = "
some content
" + card2Content = markup("
some content
") + increment() { this.counter.value += 1 } diff --git a/awesome_owl/static/src/playground.xml b/awesome_owl/static/src/playground.xml index bbda6aa8fdb..da4a23c0ef6 100644 --- a/awesome_owl/static/src/playground.xml +++ b/awesome_owl/static/src/playground.xml @@ -11,8 +11,8 @@
- - + +
From cee15643a16ed787038c4c80758df4b57eb6555f Mon Sep 17 00:00:00 2001 From: Arturo Yepez Date: Mon, 24 Aug 2026 16:27:54 +0200 Subject: [PATCH 05/19] [IMP] awesome_owl: bind callback functions Per Chapter 1, section 6 of the Web Framework tutorial --- awesome_owl/static/src/components/counter/counter.js | 4 ++++ awesome_owl/static/src/playground.js | 10 +++++++--- awesome_owl/static/src/playground.xml | 10 ++++++---- 3 files changed, 17 insertions(+), 7 deletions(-) diff --git a/awesome_owl/static/src/components/counter/counter.js b/awesome_owl/static/src/components/counter/counter.js index 2eb2ce06527..e080fdff05d 100644 --- a/awesome_owl/static/src/components/counter/counter.js +++ b/awesome_owl/static/src/components/counter/counter.js @@ -2,10 +2,14 @@ import { Component, useState } from "@odoo/owl"; export class Counter extends Component { static template = "awesome_owl.counter"; + static props = { + onChange: {type: Function, optional: true} + } counter = useState({ value: 0 }) increment() { this.counter.value += 1 + this.props.onChange?.() } } diff --git a/awesome_owl/static/src/playground.js b/awesome_owl/static/src/playground.js index 0a497fe2a09..44d2ca1ae92 100644 --- a/awesome_owl/static/src/playground.js +++ b/awesome_owl/static/src/playground.js @@ -7,12 +7,16 @@ export class Playground extends Component { static template = "awesome_owl.playground"; static components = { Counter, Card }; - counter = useState({ value: 0 }); + sum = useState({ value: 0 }) card1Content = "
some content
" card2Content = markup("
some content
") - increment() { - this.counter.value += 1 + setup() { + this.incrementSum = this.incrementSum.bind(this) + } + + incrementSum() { + this.sum.value += 1 } } diff --git a/awesome_owl/static/src/playground.xml b/awesome_owl/static/src/playground.xml index da4a23c0ef6..4953a6d0466 100644 --- a/awesome_owl/static/src/playground.xml +++ b/awesome_owl/static/src/playground.xml @@ -5,11 +5,13 @@
hello world
-
-

Value:

- +
+ + +
+
+

The sum is:

-
From 86cb57a7c14ecc67dec3d8a202e9fdf7e09e6ddd Mon Sep 17 00:00:00 2001 From: Arturo Yepez Date: Tue, 25 Aug 2026 11:06:27 +0200 Subject: [PATCH 06/19] [IMP] awesome_owl: new todo component Per Chapter 1, section 6 of the Web Framework tutorial --- .../src/components/todo_list/todo_item.js | 15 ++++++++++ .../src/components/todo_list/todo_item.xml | 12 ++++++++ .../src/components/todo_list/todo_list.js | 15 ++++++++++ .../src/components/todo_list/todo_list.xml | 12 ++++++++ awesome_owl/static/src/playground.js | 3 +- awesome_owl/static/src/playground.xml | 29 +++++++++++-------- 6 files changed, 73 insertions(+), 13 deletions(-) create mode 100644 awesome_owl/static/src/components/todo_list/todo_item.js create mode 100644 awesome_owl/static/src/components/todo_list/todo_item.xml create mode 100644 awesome_owl/static/src/components/todo_list/todo_list.js create mode 100644 awesome_owl/static/src/components/todo_list/todo_list.xml diff --git a/awesome_owl/static/src/components/todo_list/todo_item.js b/awesome_owl/static/src/components/todo_list/todo_item.js new file mode 100644 index 00000000000..ff433f53bfa --- /dev/null +++ b/awesome_owl/static/src/components/todo_list/todo_item.js @@ -0,0 +1,15 @@ +import { Component, useState } from "@odoo/owl"; + +export class TodoItem extends Component { + static template = "awesome_owl.todo_item"; + static props = { + todo: { + type: Object, + shape: { + id: Number, + description: String, + isCompleted: Boolean + } + } + } +} diff --git a/awesome_owl/static/src/components/todo_list/todo_item.xml b/awesome_owl/static/src/components/todo_list/todo_item.xml new file mode 100644 index 00000000000..88f7cd24f93 --- /dev/null +++ b/awesome_owl/static/src/components/todo_list/todo_item.xml @@ -0,0 +1,12 @@ + + + + +
+ . +

+
+
+
+ +
diff --git a/awesome_owl/static/src/components/todo_list/todo_list.js b/awesome_owl/static/src/components/todo_list/todo_list.js new file mode 100644 index 00000000000..44ba60be750 --- /dev/null +++ b/awesome_owl/static/src/components/todo_list/todo_list.js @@ -0,0 +1,15 @@ +import { Component, useState } from "@odoo/owl"; + +import { TodoItem } from "./todo_item"; + +export class TodoList extends Component { + static template = "awesome_owl.todo_list"; + static components = { TodoItem }; + + setup() { + this.todos = useState([ + { id: 2, description: "write tutorial", isCompleted: true }, + { id: 3, description: "buy milk", isCompleted: false } + ]); + } +} diff --git a/awesome_owl/static/src/components/todo_list/todo_list.xml b/awesome_owl/static/src/components/todo_list/todo_list.xml new file mode 100644 index 00000000000..7e3f1c654fa --- /dev/null +++ b/awesome_owl/static/src/components/todo_list/todo_list.xml @@ -0,0 +1,12 @@ + + + + +
+ + + +
+
+ +
diff --git a/awesome_owl/static/src/playground.js b/awesome_owl/static/src/playground.js index 44d2ca1ae92..f0eedc105c9 100644 --- a/awesome_owl/static/src/playground.js +++ b/awesome_owl/static/src/playground.js @@ -2,10 +2,11 @@ import { Component, useState, markup } from "@odoo/owl"; import { Counter } from "./components/counter/counter"; import { Card } from "./components/card/card" +import { TodoList } from "./components/todo_list/todo_list"; export class Playground extends Component { static template = "awesome_owl.playground"; - static components = { Counter, Card }; + static components = { Counter, Card, TodoList }; sum = useState({ value: 0 }) diff --git a/awesome_owl/static/src/playground.xml b/awesome_owl/static/src/playground.xml index 4953a6d0466..c3e0644c77f 100644 --- a/awesome_owl/static/src/playground.xml +++ b/awesome_owl/static/src/playground.xml @@ -3,18 +3,23 @@
- hello world -
-
- - -
-
-

The sum is:

-
-
- - +
+ hello world +
+
+ + +
+
+

The sum is:

+
+
+ + +
+
+ +
From bfc65702883639c768066a5c21648ed99164f822 Mon Sep 17 00:00:00 2001 From: Arturo Yepez Date: Tue, 25 Aug 2026 11:34:55 +0200 Subject: [PATCH 07/19] [IMP] awesome_owl: new behavior for completed todo items Per Chapter 1, section 8 of the Web Framework tutorial --- awesome_owl/static/src/components/todo_list/todo_item.xml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/awesome_owl/static/src/components/todo_list/todo_item.xml b/awesome_owl/static/src/components/todo_list/todo_item.xml index 88f7cd24f93..36dcfa7acd9 100644 --- a/awesome_owl/static/src/components/todo_list/todo_item.xml +++ b/awesome_owl/static/src/components/todo_list/todo_item.xml @@ -2,10 +2,9 @@ -
+
.

-
From a9f6cc1278b8d8440bc03dcecafc6478177d3bfb Mon Sep 17 00:00:00 2001 From: Arturo Yepez Date: Tue, 25 Aug 2026 14:20:38 +0200 Subject: [PATCH 08/19] [IMP] awesome_owl: enhance the behavior of the todo list component Per Chapter 1, section 9 of the Web Framework tutorial --- .../static/src/components/todo_list/todo_list.js | 14 ++++++++++---- .../static/src/components/todo_list/todo_list.xml | 1 + 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/awesome_owl/static/src/components/todo_list/todo_list.js b/awesome_owl/static/src/components/todo_list/todo_list.js index 44ba60be750..54ffad2eff9 100644 --- a/awesome_owl/static/src/components/todo_list/todo_list.js +++ b/awesome_owl/static/src/components/todo_list/todo_list.js @@ -7,9 +7,15 @@ export class TodoList extends Component { static components = { TodoItem }; setup() { - this.todos = useState([ - { id: 2, description: "write tutorial", isCompleted: true }, - { id: 3, description: "buy milk", isCompleted: false } - ]); + this.todos = useState([]); + } + + addTodo(event) { + if (event.keyCode === 13) { + const lastElement = this.todos.slice(-1)?.[0] + const lastId = lastElement?.id ?? 0 + this.todos.push({ id: lastId + 1, description: event.target.value, isCompleted: false }); + event.target.value = "" + } } } diff --git a/awesome_owl/static/src/components/todo_list/todo_list.xml b/awesome_owl/static/src/components/todo_list/todo_list.xml index 7e3f1c654fa..956b7963c04 100644 --- a/awesome_owl/static/src/components/todo_list/todo_list.xml +++ b/awesome_owl/static/src/components/todo_list/todo_list.xml @@ -3,6 +3,7 @@
+ From e1785425af49d71c13c621ff01a25bcddaa1981a Mon Sep 17 00:00:00 2001 From: Arturo Yepez Date: Tue, 25 Aug 2026 15:12:18 +0200 Subject: [PATCH 09/19] [IMP] awesome_owl: add autofocus behavior to todo list component Per Chapter 1, section 10 of the Web Framework tutorial. The implementation of autofocus was achieved through a custom hook --- .../src/components/todo_list/todo_list.js | 4 +++- .../src/components/todo_list/todo_list.xml | 2 +- awesome_owl/static/src/utils.js | 17 +++++++++++++++++ 3 files changed, 21 insertions(+), 2 deletions(-) create mode 100644 awesome_owl/static/src/utils.js diff --git a/awesome_owl/static/src/components/todo_list/todo_list.js b/awesome_owl/static/src/components/todo_list/todo_list.js index 54ffad2eff9..0bfefde9f90 100644 --- a/awesome_owl/static/src/components/todo_list/todo_list.js +++ b/awesome_owl/static/src/components/todo_list/todo_list.js @@ -1,6 +1,7 @@ -import { Component, useState } from "@odoo/owl"; +import { Component, useState, useRef, onMounted } from "@odoo/owl"; import { TodoItem } from "./todo_item"; +import { useAutoFocus } from "../../utils"; export class TodoList extends Component { static template = "awesome_owl.todo_list"; @@ -8,6 +9,7 @@ export class TodoList extends Component { setup() { this.todos = useState([]); + this.inputAutofocusRef = useAutoFocus("input") } addTodo(event) { diff --git a/awesome_owl/static/src/components/todo_list/todo_list.xml b/awesome_owl/static/src/components/todo_list/todo_list.xml index 956b7963c04..57c6cb651a0 100644 --- a/awesome_owl/static/src/components/todo_list/todo_list.xml +++ b/awesome_owl/static/src/components/todo_list/todo_list.xml @@ -3,7 +3,7 @@
- + diff --git a/awesome_owl/static/src/utils.js b/awesome_owl/static/src/utils.js new file mode 100644 index 00000000000..71d0587af2a --- /dev/null +++ b/awesome_owl/static/src/utils.js @@ -0,0 +1,17 @@ +import { useRef, onMounted } from "@odoo/owl"; + +/** + * Custom hook to autofocus an element on mounted + * + * @param {String} ref name that will be used for the element + * @returns the reference created for the element + */ +export function useAutoFocus(ref) { + const inputRef = useRef(ref) + + onMounted(() => { + inputRef.el?.focus(); + }) + + return inputRef; +} From 795f68f3370ac03cb058a1c01fa5d1a04d59818b Mon Sep 17 00:00:00 2001 From: Arturo Yepez Date: Tue, 25 Aug 2026 15:54:56 +0200 Subject: [PATCH 10/19] [IMP] awesome_owl: new checkbox to update state of current todo Per Chapter 1, section 11 of the Web Framework tutorial --- awesome_owl/static/src/components/todo_list/todo_item.js | 4 ++++ .../static/src/components/todo_list/todo_item.xml | 6 ++++++ awesome_owl/static/src/components/todo_list/todo_list.js | 9 ++++++++- .../static/src/components/todo_list/todo_list.xml | 2 +- 4 files changed, 19 insertions(+), 2 deletions(-) diff --git a/awesome_owl/static/src/components/todo_list/todo_item.js b/awesome_owl/static/src/components/todo_list/todo_item.js index ff433f53bfa..0c1cd953052 100644 --- a/awesome_owl/static/src/components/todo_list/todo_item.js +++ b/awesome_owl/static/src/components/todo_list/todo_item.js @@ -10,6 +10,10 @@ export class TodoItem extends Component { description: String, isCompleted: Boolean } + }, + toggleState: { + type: Function, + optional: true } } } diff --git a/awesome_owl/static/src/components/todo_list/todo_item.xml b/awesome_owl/static/src/components/todo_list/todo_item.xml index 36dcfa7acd9..4ea53c68bb2 100644 --- a/awesome_owl/static/src/components/todo_list/todo_item.xml +++ b/awesome_owl/static/src/components/todo_list/todo_item.xml @@ -3,6 +3,12 @@
+ .

diff --git a/awesome_owl/static/src/components/todo_list/todo_list.js b/awesome_owl/static/src/components/todo_list/todo_list.js index 0bfefde9f90..de897d5cd9f 100644 --- a/awesome_owl/static/src/components/todo_list/todo_list.js +++ b/awesome_owl/static/src/components/todo_list/todo_list.js @@ -9,7 +9,9 @@ export class TodoList extends Component { setup() { this.todos = useState([]); - this.inputAutofocusRef = useAutoFocus("input") + this.inputAutofocusRef = useAutoFocus("input") + + this.toggleState = this.toggleState.bind(this) } addTodo(event) { @@ -20,4 +22,9 @@ export class TodoList extends Component { event.target.value = "" } } + + toggleState(refId) { + const element = this.todos.find(({id}) => id === refId); + if (element) element.isCompleted = !element.isCompleted; + } } diff --git a/awesome_owl/static/src/components/todo_list/todo_list.xml b/awesome_owl/static/src/components/todo_list/todo_list.xml index 57c6cb651a0..d4c0e874171 100644 --- a/awesome_owl/static/src/components/todo_list/todo_list.xml +++ b/awesome_owl/static/src/components/todo_list/todo_list.xml @@ -5,7 +5,7 @@
- +
From 3028470eeb54773b69ad905b24efc3d3556db099 Mon Sep 17 00:00:00 2001 From: Arturo Yepez Date: Tue, 25 Aug 2026 16:31:53 +0200 Subject: [PATCH 11/19] [IMP] awesome_owl: add deletion for todo items Per Chapter 1, section 12 of the Web Framework tutorial --- awesome_owl/static/src/components/todo_list/todo_item.js | 4 ++++ awesome_owl/static/src/components/todo_list/todo_item.xml | 7 ++++--- awesome_owl/static/src/components/todo_list/todo_list.js | 8 ++++++++ awesome_owl/static/src/components/todo_list/todo_list.xml | 4 ++-- 4 files changed, 18 insertions(+), 5 deletions(-) diff --git a/awesome_owl/static/src/components/todo_list/todo_item.js b/awesome_owl/static/src/components/todo_list/todo_item.js index 0c1cd953052..14f6d1b4fd3 100644 --- a/awesome_owl/static/src/components/todo_list/todo_item.js +++ b/awesome_owl/static/src/components/todo_list/todo_item.js @@ -14,6 +14,10 @@ export class TodoItem extends Component { toggleState: { type: Function, optional: true + }, + deleteElement: { + type: Function, + optional: true } } } diff --git a/awesome_owl/static/src/components/todo_list/todo_item.xml b/awesome_owl/static/src/components/todo_list/todo_item.xml index 4ea53c68bb2..e5e5596b579 100644 --- a/awesome_owl/static/src/components/todo_list/todo_item.xml +++ b/awesome_owl/static/src/components/todo_list/todo_item.xml @@ -2,15 +2,16 @@ -
+
. -

+

+
diff --git a/awesome_owl/static/src/components/todo_list/todo_list.js b/awesome_owl/static/src/components/todo_list/todo_list.js index de897d5cd9f..9565d3417c6 100644 --- a/awesome_owl/static/src/components/todo_list/todo_list.js +++ b/awesome_owl/static/src/components/todo_list/todo_list.js @@ -12,6 +12,7 @@ export class TodoList extends Component { this.inputAutofocusRef = useAutoFocus("input") this.toggleState = this.toggleState.bind(this) + this.deleteElement = this.deleteElement.bind(this) } addTodo(event) { @@ -27,4 +28,11 @@ export class TodoList extends Component { const element = this.todos.find(({id}) => id === refId); if (element) element.isCompleted = !element.isCompleted; } + + deleteElement(refId) { + const index = this.todos.findIndex(({id}) => id === refId); + if (typeof index === "number") { + this.todos.splice(index, 1) + } + } } diff --git a/awesome_owl/static/src/components/todo_list/todo_list.xml b/awesome_owl/static/src/components/todo_list/todo_list.xml index d4c0e874171..0dad37feaff 100644 --- a/awesome_owl/static/src/components/todo_list/todo_list.xml +++ b/awesome_owl/static/src/components/todo_list/todo_list.xml @@ -3,9 +3,9 @@
- + - +
From d226fb543b705aa11ce799be182e70b77640c1ca Mon Sep 17 00:00:00 2001 From: Arturo Yepez Date: Tue, 25 Aug 2026 16:51:46 +0200 Subject: [PATCH 12/19] [IMP] awesome_owl: change content of the cards so they can be slots Per Chapter 1, seciton 13 of the Web Framework tutorial --- awesome_owl/static/src/components/card/card.js | 2 +- awesome_owl/static/src/components/card/card.xml | 6 +++--- awesome_owl/static/src/playground.xml | 11 +++++++++-- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/awesome_owl/static/src/components/card/card.js b/awesome_owl/static/src/components/card/card.js index f9b3c770f7e..570b04519a5 100644 --- a/awesome_owl/static/src/components/card/card.js +++ b/awesome_owl/static/src/components/card/card.js @@ -2,6 +2,6 @@ import { Component, useState } from "@odoo/owl"; export class Card extends Component { static template = "awesome_owl.card"; - static props = ["title", "content"] + static props = ["title", "slots?"] } diff --git a/awesome_owl/static/src/components/card/card.xml b/awesome_owl/static/src/components/card/card.xml index 05f4c959138..32b992d4333 100644 --- a/awesome_owl/static/src/components/card/card.xml +++ b/awesome_owl/static/src/components/card/card.xml @@ -5,9 +5,9 @@
-

- -

+
+ +
diff --git a/awesome_owl/static/src/playground.xml b/awesome_owl/static/src/playground.xml index c3e0644c77f..1fb638fb529 100644 --- a/awesome_owl/static/src/playground.xml +++ b/awesome_owl/static/src/playground.xml @@ -14,8 +14,15 @@

The sum is:

- - + + + + + + + + +
From f9dacc6fb1844f50b8e688ebcfa5af7d2ec96913 Mon Sep 17 00:00:00 2001 From: Arturo Yepez Date: Tue, 25 Aug 2026 16:59:28 +0200 Subject: [PATCH 13/19] [IMP] awesome_owl: new behavior to minimize card content Per Chapter 1, section 14 of the Web Framework tutorial --- awesome_owl/static/src/components/card/card.js | 7 +++++++ awesome_owl/static/src/components/card/card.xml | 7 +++++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/awesome_owl/static/src/components/card/card.js b/awesome_owl/static/src/components/card/card.js index 570b04519a5..85fefd4ccf9 100644 --- a/awesome_owl/static/src/components/card/card.js +++ b/awesome_owl/static/src/components/card/card.js @@ -3,5 +3,12 @@ import { Component, useState } from "@odoo/owl"; export class Card extends Component { static template = "awesome_owl.card"; static props = ["title", "slots?"] + + setup() { + this.open = useState({ value: true }); + } + toggleOpen() { + this.open.value = !this.open.value + } } diff --git a/awesome_owl/static/src/components/card/card.xml b/awesome_owl/static/src/components/card/card.xml index 32b992d4333..976144ac7db 100644 --- a/awesome_owl/static/src/components/card/card.xml +++ b/awesome_owl/static/src/components/card/card.xml @@ -4,8 +4,11 @@
-
-
+
+
+ +
+
From cd0e4497b3b000fa8fa6c08f121b76aa300adba5 Mon Sep 17 00:00:00 2001 From: Arturo Yepez Date: Wed, 26 Aug 2026 11:44:41 +0200 Subject: [PATCH 14/19] [IMP] awesome_dashboard: new layout implementation for the view Per Chapter 2, section 1 of the Web Framework tutorial --- awesome_dashboard/static/src/dashboard.js | 2 ++ awesome_dashboard/static/src/dashboard.scss | 3 +++ awesome_dashboard/static/src/dashboard.xml | 4 +++- 3 files changed, 8 insertions(+), 1 deletion(-) create mode 100644 awesome_dashboard/static/src/dashboard.scss diff --git a/awesome_dashboard/static/src/dashboard.js b/awesome_dashboard/static/src/dashboard.js index c4fb245621b..2d7c15e2fb9 100644 --- a/awesome_dashboard/static/src/dashboard.js +++ b/awesome_dashboard/static/src/dashboard.js @@ -1,8 +1,10 @@ import { Component } from "@odoo/owl"; import { registry } from "@web/core/registry"; +import { Layout } from "@web/search/layout" class AwesomeDashboard extends Component { static template = "awesome_dashboard.AwesomeDashboard"; + static components = { Layout }; } registry.category("actions").add("awesome_dashboard.dashboard", AwesomeDashboard); diff --git a/awesome_dashboard/static/src/dashboard.scss b/awesome_dashboard/static/src/dashboard.scss new file mode 100644 index 00000000000..32862ec0d82 --- /dev/null +++ b/awesome_dashboard/static/src/dashboard.scss @@ -0,0 +1,3 @@ +.o_dashboard { + background-color: gray; +} diff --git a/awesome_dashboard/static/src/dashboard.xml b/awesome_dashboard/static/src/dashboard.xml index 1a2ac9a2fed..c797406d63b 100644 --- a/awesome_dashboard/static/src/dashboard.xml +++ b/awesome_dashboard/static/src/dashboard.xml @@ -2,7 +2,9 @@ - hello dashboard + + hello dashboard + From 3cab36c2ba6168d52acb00de3507fa4cb01e1885 Mon Sep 17 00:00:00 2001 From: Arturo Yepez Date: Wed, 26 Aug 2026 15:17:07 +0200 Subject: [PATCH 15/19] [IMP] awesome_dashboard: new actions for the dashboard layout" Per Chapter 2, section 2 of the Web Framework tutorial --- awesome_dashboard/static/src/dashboard.js | 24 ++++++++++++++++++++++ awesome_dashboard/static/src/dashboard.xml | 4 ++++ 2 files changed, 28 insertions(+) diff --git a/awesome_dashboard/static/src/dashboard.js b/awesome_dashboard/static/src/dashboard.js index 2d7c15e2fb9..5d8da66a501 100644 --- a/awesome_dashboard/static/src/dashboard.js +++ b/awesome_dashboard/static/src/dashboard.js @@ -1,10 +1,34 @@ import { Component } from "@odoo/owl"; import { registry } from "@web/core/registry"; +import { useService } from "@web/core/utils/hooks"; +import { _t } from "@web/core/l10n/translation"; import { Layout } from "@web/search/layout" class AwesomeDashboard extends Component { static template = "awesome_dashboard.AwesomeDashboard"; static components = { Layout }; + + setup() { + this.action = useService("action"); + } + + openCustomers() { + this.action.doAction("base.action_partner_form"); + } + + async openLeads(activity) { + /* Dynamic action to open Leads with only list and form view */ + + this.action.doAction({ + type: "ir.actions.act_window", + name: "All leads", + res_model: "crm.lead", + views: [ + [false, "list"], + [false, "form"], + ], + }); + } } registry.category("actions").add("awesome_dashboard.dashboard", AwesomeDashboard); diff --git a/awesome_dashboard/static/src/dashboard.xml b/awesome_dashboard/static/src/dashboard.xml index c797406d63b..fca9a273920 100644 --- a/awesome_dashboard/static/src/dashboard.xml +++ b/awesome_dashboard/static/src/dashboard.xml @@ -3,6 +3,10 @@ + + + + hello dashboard From 10826e311cfc74d2f28b3091b19c2f84fe33d05d Mon Sep 17 00:00:00 2001 From: Arturo Yepez Date: Wed, 26 Aug 2026 16:01:14 +0200 Subject: [PATCH 16/19] [IMP] awesome_dashboard: new component for dashboard items Per Chapter 2, section 3 of the Web Framework tutorial --- .../dashboard_item/dashboard_item.js | 26 +++++++++++++++++++ .../dashboard_item/dashboard_item.xml | 17 ++++++++++++ awesome_dashboard/static/src/dashboard.js | 4 ++- awesome_dashboard/static/src/dashboard.xml | 10 ++++++- 4 files changed, 55 insertions(+), 2 deletions(-) create mode 100644 awesome_dashboard/static/src/components/dashboard_item/dashboard_item.js create mode 100644 awesome_dashboard/static/src/components/dashboard_item/dashboard_item.xml diff --git a/awesome_dashboard/static/src/components/dashboard_item/dashboard_item.js b/awesome_dashboard/static/src/components/dashboard_item/dashboard_item.js new file mode 100644 index 00000000000..ea2e1dcfb29 --- /dev/null +++ b/awesome_dashboard/static/src/components/dashboard_item/dashboard_item.js @@ -0,0 +1,26 @@ +import { Component, useState } from "@odoo/owl"; + +export class DashboardItem extends Component { + static template = "awesome_dashboard.dashboard_item"; + static props = { + title: {type: String, optional: true}, + slots: {type: Object, optional: true}, + size: {type: Number, optional: true} + }; + static defaultProps = { + title: "", + size: 1 + } + + setup() { + this.open = useState({ value: true }); + } + + toggleOpen() { + this.open.value = !this.open.value + } + + get widthSize() { + return `${18*this.props.size}rem;` + } +} diff --git a/awesome_dashboard/static/src/components/dashboard_item/dashboard_item.xml b/awesome_dashboard/static/src/components/dashboard_item/dashboard_item.xml new file mode 100644 index 00000000000..64338d53a23 --- /dev/null +++ b/awesome_dashboard/static/src/components/dashboard_item/dashboard_item.xml @@ -0,0 +1,17 @@ + + + + +
+
+
+
+
+
+ +
+
+
+
+ +
diff --git a/awesome_dashboard/static/src/dashboard.js b/awesome_dashboard/static/src/dashboard.js index 5d8da66a501..d37b34ba962 100644 --- a/awesome_dashboard/static/src/dashboard.js +++ b/awesome_dashboard/static/src/dashboard.js @@ -4,9 +4,11 @@ import { useService } from "@web/core/utils/hooks"; import { _t } from "@web/core/l10n/translation"; import { Layout } from "@web/search/layout" +import { DashboardItem } from "./components/dashboard_item/dashboard_item"; + class AwesomeDashboard extends Component { static template = "awesome_dashboard.AwesomeDashboard"; - static components = { Layout }; + static components = { Layout, DashboardItem }; setup() { this.action = useService("action"); diff --git a/awesome_dashboard/static/src/dashboard.xml b/awesome_dashboard/static/src/dashboard.xml index fca9a273920..647b60482e8 100644 --- a/awesome_dashboard/static/src/dashboard.xml +++ b/awesome_dashboard/static/src/dashboard.xml @@ -7,7 +7,15 @@ - hello dashboard + + hello dashboard + + + big dashboard item + + + Normal dashboard item + From e6a70dfd8518580cc2d39304aa5f5fec48f4ece9 Mon Sep 17 00:00:00 2001 From: Arturo Yepez Date: Wed, 26 Aug 2026 16:49:06 +0200 Subject: [PATCH 17/19] [IMP] awesome_dashboard: new display of statistics using item component Per Chapter 1, section 4 of the Web Framework tutorial --- .../dashboard_item/dashboard_item.xml | 3 ++ awesome_dashboard/static/src/dashboard.js | 9 +++++- awesome_dashboard/static/src/dashboard.xml | 28 +++++++++++++++---- 3 files changed, 33 insertions(+), 7 deletions(-) diff --git a/awesome_dashboard/static/src/components/dashboard_item/dashboard_item.xml b/awesome_dashboard/static/src/components/dashboard_item/dashboard_item.xml index 64338d53a23..9fef9e5022c 100644 --- a/awesome_dashboard/static/src/components/dashboard_item/dashboard_item.xml +++ b/awesome_dashboard/static/src/components/dashboard_item/dashboard_item.xml @@ -9,6 +9,9 @@
+

+ +

diff --git a/awesome_dashboard/static/src/dashboard.js b/awesome_dashboard/static/src/dashboard.js index d37b34ba962..4c7be464468 100644 --- a/awesome_dashboard/static/src/dashboard.js +++ b/awesome_dashboard/static/src/dashboard.js @@ -1,7 +1,8 @@ -import { Component } from "@odoo/owl"; +import { Component, onWillStart, useState } from "@odoo/owl"; import { registry } from "@web/core/registry"; import { useService } from "@web/core/utils/hooks"; import { _t } from "@web/core/l10n/translation"; +import { rpc } from "@web/core/network/rpc"; import { Layout } from "@web/search/layout" import { DashboardItem } from "./components/dashboard_item/dashboard_item"; @@ -12,6 +13,12 @@ class AwesomeDashboard extends Component { setup() { this.action = useService("action"); + this.statistics = useState({}); + + onWillStart(async () => { + const statistics = await rpc("/awesome_dashboard/statistics"); + this.statistics = statistics; + }); } openCustomers() { diff --git a/awesome_dashboard/static/src/dashboard.xml b/awesome_dashboard/static/src/dashboard.xml index 647b60482e8..2c266dbda49 100644 --- a/awesome_dashboard/static/src/dashboard.xml +++ b/awesome_dashboard/static/src/dashboard.xml @@ -7,14 +7,30 @@
- - hello dashboard + + + + - - big dashboard item + + + + - - Normal dashboard item + + + + + + + + + + + + + + From 055a52abec520fa43abe866b2df17e3aa4ed97b8 Mon Sep 17 00:00:00 2001 From: Arturo Yepez Date: Wed, 26 Aug 2026 17:23:01 +0200 Subject: [PATCH 18/19] [IMP] awesome_dashboard: new service to keep state of loaded statistics Per Chapter 2, section 5 of the Web Framework --- awesome_dashboard/static/src/dashboard.js | 5 ++--- awesome_dashboard/static/src/dashboard_service.js | 13 +++++++++++++ 2 files changed, 15 insertions(+), 3 deletions(-) create mode 100644 awesome_dashboard/static/src/dashboard_service.js diff --git a/awesome_dashboard/static/src/dashboard.js b/awesome_dashboard/static/src/dashboard.js index 4c7be464468..19eaf8418f1 100644 --- a/awesome_dashboard/static/src/dashboard.js +++ b/awesome_dashboard/static/src/dashboard.js @@ -13,11 +13,10 @@ class AwesomeDashboard extends Component { setup() { this.action = useService("action"); - this.statistics = useState({}); + const dashboardService = useService("awesome_dashboard.statistics"); onWillStart(async () => { - const statistics = await rpc("/awesome_dashboard/statistics"); - this.statistics = statistics; + this.statistics = await dashboardService.loadStatistics() }); } diff --git a/awesome_dashboard/static/src/dashboard_service.js b/awesome_dashboard/static/src/dashboard_service.js new file mode 100644 index 00000000000..f85e04d5d8a --- /dev/null +++ b/awesome_dashboard/static/src/dashboard_service.js @@ -0,0 +1,13 @@ +import { registry } from "@web/core/registry"; +import { rpc } from "@web/core/network/rpc"; +import { memoize } from "@web/core/utils/functions"; + +export const dashboardService = { + start() { + return { + loadStatistics: memoize(() => rpc("/awesome_dashboard/statistics")), + }; + } +} + +registry.category("services").add("awesome_dashboard.statistics", dashboardService); From 9980c0fff8ccc1340b6d0ff8da05363e296964b3 Mon Sep 17 00:00:00 2001 From: Arturo Yepez Date: Thu, 27 Aug 2026 13:30:48 +0200 Subject: [PATCH 19/19] [IMP] awesome_dashboard: new pie chart component Per Chapter 2, section 6 of the Web Framework tutorial --- .../src/components/pie_chart/pie_chart.js | 50 +++++++++++++++++++ .../src/components/pie_chart/pie_chart.xml | 10 ++++ awesome_dashboard/static/src/dashboard.js | 3 +- awesome_dashboard/static/src/dashboard.xml | 3 ++ 4 files changed, 65 insertions(+), 1 deletion(-) create mode 100644 awesome_dashboard/static/src/components/pie_chart/pie_chart.js create mode 100644 awesome_dashboard/static/src/components/pie_chart/pie_chart.xml diff --git a/awesome_dashboard/static/src/components/pie_chart/pie_chart.js b/awesome_dashboard/static/src/components/pie_chart/pie_chart.js new file mode 100644 index 00000000000..6576ade3ce2 --- /dev/null +++ b/awesome_dashboard/static/src/components/pie_chart/pie_chart.js @@ -0,0 +1,50 @@ +import { Component, useState, onWillStart, useRef, onMounted, onPatched, onWillUnmount, useEffect } from "@odoo/owl"; +import { loadJS } from "@web/core/assets"; + +export class PieChart extends Component { + static template = "awesome_dashboard.pie_chart"; + static props = { + title: {type: String, optional: true}, + data: { + type: Object, + values: { type: Number }, + optional: true + } + }; + + setup() { + this.canvasRef = useRef("canvas"); + + onWillStart(async () => loadJS("/web/static/lib/Chart/Chart.js")); + + useEffect(() => { + this.renderChart(); + }); + + onPatched(() => { + this.chart?.destroy(); + this.renderChart(); + }); + + onWillUnmount(() => { + this.chart?.destroy(); + }); + } + + renderChart() { + const labels = Object.keys(this.props.data); + const data = Object.values(this.props.data); + this.chart = new Chart(this.canvasRef.el, { + type: "doughnut", + data: { + labels: labels, + datasets: [ + { + data: data, + }, + ], + }, + }); + } + +} diff --git a/awesome_dashboard/static/src/components/pie_chart/pie_chart.xml b/awesome_dashboard/static/src/components/pie_chart/pie_chart.xml new file mode 100644 index 00000000000..d17d9ea5818 --- /dev/null +++ b/awesome_dashboard/static/src/components/pie_chart/pie_chart.xml @@ -0,0 +1,10 @@ + + + + +
+ +
+
+ +
diff --git a/awesome_dashboard/static/src/dashboard.js b/awesome_dashboard/static/src/dashboard.js index 19eaf8418f1..fab97c06b4a 100644 --- a/awesome_dashboard/static/src/dashboard.js +++ b/awesome_dashboard/static/src/dashboard.js @@ -6,10 +6,11 @@ import { rpc } from "@web/core/network/rpc"; import { Layout } from "@web/search/layout" import { DashboardItem } from "./components/dashboard_item/dashboard_item"; +import { PieChart } from "./components/pie_chart/pie_chart"; class AwesomeDashboard extends Component { static template = "awesome_dashboard.AwesomeDashboard"; - static components = { Layout, DashboardItem }; + static components = { Layout, DashboardItem, PieChart }; setup() { this.action = useService("action"); diff --git a/awesome_dashboard/static/src/dashboard.xml b/awesome_dashboard/static/src/dashboard.xml index 2c266dbda49..ab57f9ff1de 100644 --- a/awesome_dashboard/static/src/dashboard.xml +++ b/awesome_dashboard/static/src/dashboard.xml @@ -32,6 +32,9 @@
+ + +