From 2bbbc684c161020acebcb714a82d62ab6fe02455 Mon Sep 17 00:00:00 2001 From: "Cezar Bulancea (cebul)" Date: Tue, 28 Jul 2026 10:20:34 +0200 Subject: [PATCH 01/20] [IMP] awesome_owl: started chapter 1 1. Display the counter --- awesome_owl/static/src/playground.js | 10 +++++++++- awesome_owl/static/src/playground.xml | 5 +++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/awesome_owl/static/src/playground.js b/awesome_owl/static/src/playground.js index 4ac769b0aa5..bdc6b63600e 100644 --- a/awesome_owl/static/src/playground.js +++ b/awesome_owl/static/src/playground.js @@ -1,5 +1,13 @@ -import { Component } from "@odoo/owl"; +import { Component, useState } from "@odoo/owl"; export class Playground extends Component { static template = "awesome_owl.playground"; + + setup() { + this.state = useState({ value: 0 }); + } + + increment() { + this.state.value++; + } } diff --git a/awesome_owl/static/src/playground.xml b/awesome_owl/static/src/playground.xml index 4fb905d59f9..4c49cc33aac 100644 --- a/awesome_owl/static/src/playground.xml +++ b/awesome_owl/static/src/playground.xml @@ -2,8 +2,9 @@ -
- hello world +
+ Counter: +
From f8b082e5247ba32f570256bdffd9b60372cb0261 Mon Sep 17 00:00:00 2001 From: "Cezar Bulancea (cebul)" Date: Tue, 28 Jul 2026 11:01:02 +0200 Subject: [PATCH 02/20] [IMP] awesome_owl: chapter 1.2 Extract Counter in a sub component. --- awesome_owl/static/src/counter/counter.js | 13 +++++++++++++ awesome_owl/static/src/counter/counter.xml | 11 +++++++++++ awesome_owl/static/src/playground.js | 9 ++------- awesome_owl/static/src/playground.xml | 4 ++-- 4 files changed, 28 insertions(+), 9 deletions(-) create mode 100644 awesome_owl/static/src/counter/counter.js create mode 100644 awesome_owl/static/src/counter/counter.xml diff --git a/awesome_owl/static/src/counter/counter.js b/awesome_owl/static/src/counter/counter.js new file mode 100644 index 00000000000..9e8c7b7d081 --- /dev/null +++ b/awesome_owl/static/src/counter/counter.js @@ -0,0 +1,13 @@ +import { Component, useState } from "@odoo/owl"; + +export class Counter extends Component { + static template = "awesome_owl.counter"; + + setup() { + this.state = useState({ value: 0 }); + } + + increment() { + this.state.value++; + } +} diff --git a/awesome_owl/static/src/counter/counter.xml b/awesome_owl/static/src/counter/counter.xml new file mode 100644 index 00000000000..2bfb0bb11e0 --- /dev/null +++ b/awesome_owl/static/src/counter/counter.xml @@ -0,0 +1,11 @@ + + + + +
+ Counter: + +
+
+ +
diff --git a/awesome_owl/static/src/playground.js b/awesome_owl/static/src/playground.js index bdc6b63600e..1d4b7a90c9b 100644 --- a/awesome_owl/static/src/playground.js +++ b/awesome_owl/static/src/playground.js @@ -1,13 +1,8 @@ import { Component, useState } from "@odoo/owl"; +import { Counter } from "./counter/counter"; export class Playground extends Component { static template = "awesome_owl.playground"; - setup() { - this.state = useState({ value: 0 }); - } - - increment() { - this.state.value++; - } + static components = { Counter }; } diff --git a/awesome_owl/static/src/playground.xml b/awesome_owl/static/src/playground.xml index 4c49cc33aac..a64dca44305 100644 --- a/awesome_owl/static/src/playground.xml +++ b/awesome_owl/static/src/playground.xml @@ -3,8 +3,8 @@
- Counter: - + +
From 7286172ff7d4530a3065b8104600f54ad3b1b9ec Mon Sep 17 00:00:00 2001 From: "Cezar Bulancea (cebul)" Date: Tue, 28 Jul 2026 11:45:53 +0200 Subject: [PATCH 03/20] [IMP] awesome_owl: chpater 1.3 a simple card component --- awesome_owl/static/src/card/card.js | 5 +++++ awesome_owl/static/src/card/card.xml | 13 +++++++++++++ awesome_owl/static/src/playground.js | 4 ++-- awesome_owl/static/src/playground.xml | 4 ++-- 4 files changed, 22 insertions(+), 4 deletions(-) create mode 100644 awesome_owl/static/src/card/card.js create mode 100644 awesome_owl/static/src/card/card.xml diff --git a/awesome_owl/static/src/card/card.js b/awesome_owl/static/src/card/card.js new file mode 100644 index 00000000000..4283a8739b2 --- /dev/null +++ b/awesome_owl/static/src/card/card.js @@ -0,0 +1,5 @@ +import { Component } from "@odoo/owl"; + +export class Card extends Component { + static template = "awesome_owl.card"; +} diff --git a/awesome_owl/static/src/card/card.xml b/awesome_owl/static/src/card/card.xml new file mode 100644 index 00000000000..1b3ed432ba5 --- /dev/null +++ b/awesome_owl/static/src/card/card.xml @@ -0,0 +1,13 @@ + + + + +
+
+
+ +
+
+
+ +
diff --git a/awesome_owl/static/src/playground.js b/awesome_owl/static/src/playground.js index 1d4b7a90c9b..e8c47bbc7d4 100644 --- a/awesome_owl/static/src/playground.js +++ b/awesome_owl/static/src/playground.js @@ -1,8 +1,8 @@ import { Component, useState } from "@odoo/owl"; import { Counter } from "./counter/counter"; +import { Card } from "./card/card"; export class Playground extends Component { static template = "awesome_owl.playground"; - - static components = { Counter }; + static components = { Counter, Card }; } diff --git a/awesome_owl/static/src/playground.xml b/awesome_owl/static/src/playground.xml index a64dca44305..730e351134e 100644 --- a/awesome_owl/static/src/playground.xml +++ b/awesome_owl/static/src/playground.xml @@ -3,8 +3,8 @@
- - + +
From d654f38001fc2178cba2cda4bb9af610916f2c67 Mon Sep 17 00:00:00 2001 From: "Cezar Bulancea (cebul)" Date: Tue, 28 Jul 2026 13:30:09 +0200 Subject: [PATCH 04/20] [IMP] estate: chapter 1.4 using markup to display html --- awesome_owl/static/src/card/card.xml | 2 +- awesome_owl/static/src/playground.js | 5 ++++- awesome_owl/static/src/playground.xml | 4 ++-- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/awesome_owl/static/src/card/card.xml b/awesome_owl/static/src/card/card.xml index 1b3ed432ba5..dba38c0944c 100644 --- a/awesome_owl/static/src/card/card.xml +++ b/awesome_owl/static/src/card/card.xml @@ -5,7 +5,7 @@
- +
diff --git a/awesome_owl/static/src/playground.js b/awesome_owl/static/src/playground.js index e8c47bbc7d4..a6e3252f720 100644 --- a/awesome_owl/static/src/playground.js +++ b/awesome_owl/static/src/playground.js @@ -1,8 +1,11 @@ -import { Component, useState } from "@odoo/owl"; +import { Component, markup, useState } from "@odoo/owl"; import { Counter } from "./counter/counter"; import { Card } from "./card/card"; export class Playground extends Component { static template = "awesome_owl.playground"; static components = { Counter, Card }; + + value1 = "
content 1
" + value2 = markup("
content 2
") } diff --git a/awesome_owl/static/src/playground.xml b/awesome_owl/static/src/playground.xml index 730e351134e..58072231779 100644 --- a/awesome_owl/static/src/playground.xml +++ b/awesome_owl/static/src/playground.xml @@ -3,8 +3,8 @@
- - + +
From 3fbe5afcc021839fbcc2022978b74036a3eae7af Mon Sep 17 00:00:00 2001 From: "Cezar Bulancea (cebul)" Date: Tue, 28 Jul 2026 13:59:29 +0200 Subject: [PATCH 05/20] [IMP] awesome_owl: chapter 1.5 props validation --- awesome_owl/static/src/card/card.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/awesome_owl/static/src/card/card.js b/awesome_owl/static/src/card/card.js index 4283a8739b2..5630ee1cbb7 100644 --- a/awesome_owl/static/src/card/card.js +++ b/awesome_owl/static/src/card/card.js @@ -2,4 +2,9 @@ import { Component } from "@odoo/owl"; export class Card extends Component { static template = "awesome_owl.card"; + + static props = { + title: {type: String}, + content: {type: String} + }; } From 6ed078de156b4e62d2cbc37d209537b82ff06cf4 Mon Sep 17 00:00:00 2001 From: "Cezar Bulancea (cebul)" Date: Tue, 28 Jul 2026 15:05:48 +0200 Subject: [PATCH 06/20] [IMP] awesome_owl: chapter 1.6 The sum of the two counter. --- awesome_owl/static/src/counter/counter.js | 5 +++++ awesome_owl/static/src/playground.js | 8 ++++++++ awesome_owl/static/src/playground.xml | 11 ++++++++--- 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/awesome_owl/static/src/counter/counter.js b/awesome_owl/static/src/counter/counter.js index 9e8c7b7d081..6deb801667d 100644 --- a/awesome_owl/static/src/counter/counter.js +++ b/awesome_owl/static/src/counter/counter.js @@ -3,11 +3,16 @@ import { Component, useState } from "@odoo/owl"; export class Counter extends Component { static template = "awesome_owl.counter"; + static props = { + onChange: {type: Function, optional: true}, + } + setup() { this.state = useState({ value: 0 }); } increment() { this.state.value++; + this.props.onChange?.(); } } diff --git a/awesome_owl/static/src/playground.js b/awesome_owl/static/src/playground.js index a6e3252f720..31a0a69ba21 100644 --- a/awesome_owl/static/src/playground.js +++ b/awesome_owl/static/src/playground.js @@ -8,4 +8,12 @@ export class Playground extends Component { value1 = "
content 1
" value2 = markup("
content 2
") + + setup() { + this.sum = useState({ value: 0 }); + } + + incrementSum() { + this.sum.value++; + } } diff --git a/awesome_owl/static/src/playground.xml b/awesome_owl/static/src/playground.xml index 58072231779..542eb153aa6 100644 --- a/awesome_owl/static/src/playground.xml +++ b/awesome_owl/static/src/playground.xml @@ -2,9 +2,14 @@ -
- - +
+
+ + +
+
+ The sum is: +
From 56341bb8b3a2e60a7b8ec4d217fa104fc1300a27 Mon Sep 17 00:00:00 2001 From: "Cezar Bulancea (cebul)" Date: Wed, 29 Jul 2026 16:19:15 +0200 Subject: [PATCH 07/20] [IMP] awesome_owl: chapter 1.7 created a todo list and a todo item component --- awesome_owl/static/src/playground.js | 5 +++-- awesome_owl/static/src/playground.xml | 11 ++++++----- awesome_owl/static/src/todo/todoItem.js | 12 ++++++++++++ awesome_owl/static/src/todo/todoItem.xml | 12 ++++++++++++ awesome_owl/static/src/todo/todoList.js | 15 +++++++++++++++ awesome_owl/static/src/todo/todoList.xml | 12 ++++++++++++ 6 files changed, 60 insertions(+), 7 deletions(-) create mode 100644 awesome_owl/static/src/todo/todoItem.js create mode 100644 awesome_owl/static/src/todo/todoItem.xml create mode 100644 awesome_owl/static/src/todo/todoList.js create mode 100644 awesome_owl/static/src/todo/todoList.xml diff --git a/awesome_owl/static/src/playground.js b/awesome_owl/static/src/playground.js index 31a0a69ba21..2f8b081934c 100644 --- a/awesome_owl/static/src/playground.js +++ b/awesome_owl/static/src/playground.js @@ -1,10 +1,11 @@ import { Component, markup, useState } from "@odoo/owl"; import { Counter } from "./counter/counter"; import { Card } from "./card/card"; +import { TodoList } from "./todo/todoList"; export class Playground extends Component { - static template = "awesome_owl.playground"; - static components = { Counter, Card }; + static template = 'awesome_owl.playground'; + static components = { Counter, Card, TodoList }; value1 = "
content 1
" value2 = markup("
content 2
") diff --git a/awesome_owl/static/src/playground.xml b/awesome_owl/static/src/playground.xml index 542eb153aa6..5c962da99d7 100644 --- a/awesome_owl/static/src/playground.xml +++ b/awesome_owl/static/src/playground.xml @@ -1,15 +1,16 @@ - -
-
- - + +
+
+ +
The sum is:
+
diff --git a/awesome_owl/static/src/todo/todoItem.js b/awesome_owl/static/src/todo/todoItem.js new file mode 100644 index 00000000000..a8713ddd402 --- /dev/null +++ b/awesome_owl/static/src/todo/todoItem.js @@ -0,0 +1,12 @@ +import { Component } from "@odoo/owl" + +export class TodoItem extends Component { + static template = 'awesome_owl.todoItem'; + + static props = { + todo: { + type: Object, + shape: {id: Number, description: String, isCompleted: Boolean}, + }, + }; +} diff --git a/awesome_owl/static/src/todo/todoItem.xml b/awesome_owl/static/src/todo/todoItem.xml new file mode 100644 index 00000000000..b191015d5d1 --- /dev/null +++ b/awesome_owl/static/src/todo/todoItem.xml @@ -0,0 +1,12 @@ + + + + +
+ + . + +
+
+ +
diff --git a/awesome_owl/static/src/todo/todoList.js b/awesome_owl/static/src/todo/todoList.js new file mode 100644 index 00000000000..9928d65ad27 --- /dev/null +++ b/awesome_owl/static/src/todo/todoList.js @@ -0,0 +1,15 @@ +import { Component, useState } from "@odoo/owl" +import { TodoItem } from "./todoItem"; + +export class TodoList extends Component { + static template = 'awesome_owl.todoList'; + + setup() { + this.todos = useState([ + { id: 2, description: "wash the dishes", isCompleted: false }, + { id: 3, description: "buy milk", isCompleted: false }, + ]); + } + + static components = { TodoItem }; +} diff --git a/awesome_owl/static/src/todo/todoList.xml b/awesome_owl/static/src/todo/todoList.xml new file mode 100644 index 00000000000..578cea04ace --- /dev/null +++ b/awesome_owl/static/src/todo/todoList.xml @@ -0,0 +1,12 @@ + + + + +
+ + + +
+
+ +
From 9205e1d05056d6c632085b6b4b25b68649c6f767 Mon Sep 17 00:00:00 2001 From: "Cezar Bulancea (cebul)" Date: Wed, 29 Jul 2026 17:10:28 +0200 Subject: [PATCH 08/20] [IMP] awesome_owl: chapter 1.8 use dynamic attributes; crossing a finished task --- awesome_owl/static/src/todo/todoItem.xml | 2 +- awesome_owl/static/src/todo/todoList.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/awesome_owl/static/src/todo/todoItem.xml b/awesome_owl/static/src/todo/todoItem.xml index b191015d5d1..ec4a5998ad5 100644 --- a/awesome_owl/static/src/todo/todoItem.xml +++ b/awesome_owl/static/src/todo/todoItem.xml @@ -2,7 +2,7 @@ -
+
. diff --git a/awesome_owl/static/src/todo/todoList.js b/awesome_owl/static/src/todo/todoList.js index 9928d65ad27..badf38bc39d 100644 --- a/awesome_owl/static/src/todo/todoList.js +++ b/awesome_owl/static/src/todo/todoList.js @@ -6,7 +6,7 @@ export class TodoList extends Component { setup() { this.todos = useState([ - { id: 2, description: "wash the dishes", isCompleted: false }, + { id: 2, description: "wash the dishes", isCompleted: true }, { id: 3, description: "buy milk", isCompleted: false }, ]); } From 4a571b8344502908e9712fde8899ecb1cf7a7fda Mon Sep 17 00:00:00 2001 From: "Cezar Bulancea (cebul)" Date: Wed, 29 Jul 2026 17:52:12 +0200 Subject: [PATCH 09/20] [IMP] awesome_owl: chapter 1.9 adding a todo; we have an input field where we can introduce new task and when enter is pressed they are added to the todo list --- awesome_owl/static/src/todo/todoList.js | 21 ++++++++++++++++----- awesome_owl/static/src/todo/todoList.xml | 3 +++ 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/awesome_owl/static/src/todo/todoList.js b/awesome_owl/static/src/todo/todoList.js index badf38bc39d..dd6147892a8 100644 --- a/awesome_owl/static/src/todo/todoList.js +++ b/awesome_owl/static/src/todo/todoList.js @@ -3,13 +3,24 @@ import { TodoItem } from "./todoItem"; export class TodoList extends Component { static template = 'awesome_owl.todoList'; + static components = { TodoItem }; setup() { - this.todos = useState([ - { id: 2, description: "wash the dishes", isCompleted: true }, - { id: 3, description: "buy milk", isCompleted: false }, - ]); + this.todos = useState([]); + this.id = 1; } - static components = { TodoItem }; + addTodo(ev) { + if (ev.keyCode !== 13) + return; + + const value = ev.target.value.trim(); + + if (value.length === 0) + return; + + this.todos.push({id: this.id++, description: value, isCompleted: false}); + + ev.target.value = ""; + } } diff --git a/awesome_owl/static/src/todo/todoList.xml b/awesome_owl/static/src/todo/todoList.xml index 578cea04ace..15d5319dec0 100644 --- a/awesome_owl/static/src/todo/todoList.xml +++ b/awesome_owl/static/src/todo/todoList.xml @@ -3,6 +3,9 @@
+ From 9c585597db1eaead94b72307cdda503178c2a190 Mon Sep 17 00:00:00 2001 From: "Cezar Bulancea (cebul)" Date: Wed, 29 Jul 2026 19:09:54 +0200 Subject: [PATCH 10/20] [IMP] awesome_owl: chapter 1.10 focusing the input; created the function useAutofocus which focuses the input element so that the user can start typing todoitems right away --- awesome_owl/static/src/todo/todoList.js | 5 ++++- awesome_owl/static/src/todo/todoList.xml | 1 + awesome_owl/static/src/utils.js | 9 +++++++++ 3 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 awesome_owl/static/src/utils.js diff --git a/awesome_owl/static/src/todo/todoList.js b/awesome_owl/static/src/todo/todoList.js index dd6147892a8..355291fa531 100644 --- a/awesome_owl/static/src/todo/todoList.js +++ b/awesome_owl/static/src/todo/todoList.js @@ -1,5 +1,6 @@ -import { Component, useState } from "@odoo/owl" +import { Component, useState, useRef, onMounted } from "@odoo/owl" import { TodoItem } from "./todoItem"; +import { useAutofocus } from "../utils"; export class TodoList extends Component { static template = 'awesome_owl.todoList'; @@ -8,6 +9,8 @@ export class TodoList extends Component { setup() { this.todos = useState([]); this.id = 1; + + this.inputRef = useAutofocus('todo'); } addTodo(ev) { diff --git a/awesome_owl/static/src/todo/todoList.xml b/awesome_owl/static/src/todo/todoList.xml index 15d5319dec0..5a56cb230a0 100644 --- a/awesome_owl/static/src/todo/todoList.xml +++ b/awesome_owl/static/src/todo/todoList.xml @@ -5,6 +5,7 @@
diff --git a/awesome_owl/static/src/utils.js b/awesome_owl/static/src/utils.js new file mode 100644 index 00000000000..d9526b63e9d --- /dev/null +++ b/awesome_owl/static/src/utils.js @@ -0,0 +1,9 @@ +import { onMounted, useRef } from "@odoo/owl" + +export function useAutofocus(name) { + const ref = useRef(name); + onMounted(() => { + ref.el.focus(); + }); + return ref; +} From 079b03fcb4597d31098ac72a5a24551fd8b821a9 Mon Sep 17 00:00:00 2001 From: "Cezar Bulancea (cebul)" Date: Wed, 29 Jul 2026 20:05:27 +0200 Subject: [PATCH 11/20] [IMP] awesome_owl: chapter 1.11 toggling todos; now we can toggle todos if completed --- awesome_owl/static/src/playground.js | 1 + awesome_owl/static/src/todo/todoItem.js | 5 +++++ awesome_owl/static/src/todo/todoItem.xml | 7 ++++++- awesome_owl/static/src/todo/todoList.js | 7 +++++++ awesome_owl/static/src/todo/todoList.xml | 2 +- 5 files changed, 20 insertions(+), 2 deletions(-) diff --git a/awesome_owl/static/src/playground.js b/awesome_owl/static/src/playground.js index 2f8b081934c..c718b81f7b5 100644 --- a/awesome_owl/static/src/playground.js +++ b/awesome_owl/static/src/playground.js @@ -6,6 +6,7 @@ import { TodoList } from "./todo/todoList"; export class Playground extends Component { static template = 'awesome_owl.playground'; static components = { Counter, Card, TodoList }; + static props = {}; value1 = "
content 1
" value2 = markup("
content 2
") diff --git a/awesome_owl/static/src/todo/todoItem.js b/awesome_owl/static/src/todo/todoItem.js index a8713ddd402..c2c306df82a 100644 --- a/awesome_owl/static/src/todo/todoItem.js +++ b/awesome_owl/static/src/todo/todoItem.js @@ -8,5 +8,10 @@ export class TodoItem extends Component { type: Object, shape: {id: Number, description: String, isCompleted: Boolean}, }, + toggleState: Function, }; + + onChange() { + this.props.toggleState(this.props.todo.id); + } } diff --git a/awesome_owl/static/src/todo/todoItem.xml b/awesome_owl/static/src/todo/todoItem.xml index ec4a5998ad5..41a95074697 100644 --- a/awesome_owl/static/src/todo/todoItem.xml +++ b/awesome_owl/static/src/todo/todoItem.xml @@ -2,7 +2,12 @@ -
+
+ . diff --git a/awesome_owl/static/src/todo/todoList.js b/awesome_owl/static/src/todo/todoList.js index 355291fa531..d8bf6016409 100644 --- a/awesome_owl/static/src/todo/todoList.js +++ b/awesome_owl/static/src/todo/todoList.js @@ -5,6 +5,7 @@ import { useAutofocus } from "../utils"; export class TodoList extends Component { static template = 'awesome_owl.todoList'; static components = { TodoItem }; + static props = {}; setup() { this.todos = useState([]); @@ -26,4 +27,10 @@ export class TodoList extends Component { ev.target.value = ""; } + + toggleTodo(id) { + const todo = this.todos.find((t) => t.id === id); + if (todo) + todo.isCompleted = !todo.isCompleted; + } } diff --git a/awesome_owl/static/src/todo/todoList.xml b/awesome_owl/static/src/todo/todoList.xml index 5a56cb230a0..8cdd2ad27a1 100644 --- a/awesome_owl/static/src/todo/todoList.xml +++ b/awesome_owl/static/src/todo/todoList.xml @@ -8,7 +8,7 @@ t-ref='todo' t-on-keyup='addTodo'/> - +
From d1b843eb0e18b6ddc34dcc45cb2606c3c02bf6f2 Mon Sep 17 00:00:00 2001 From: "Cezar Bulancea (cebul)" Date: Thu, 30 Jul 2026 09:29:14 +0200 Subject: [PATCH 12/20] [IMP] awesome_owl: chapter 1.12 deleting todos --- awesome_owl/static/src/todo/todoItem.js | 5 +++++ awesome_owl/static/src/todo/todoItem.xml | 2 ++ awesome_owl/static/src/todo/todoList.js | 6 ++++++ awesome_owl/static/src/todo/todoList.xml | 4 +++- 4 files changed, 16 insertions(+), 1 deletion(-) diff --git a/awesome_owl/static/src/todo/todoItem.js b/awesome_owl/static/src/todo/todoItem.js index c2c306df82a..3c967b2e452 100644 --- a/awesome_owl/static/src/todo/todoItem.js +++ b/awesome_owl/static/src/todo/todoItem.js @@ -9,9 +9,14 @@ export class TodoItem extends Component { shape: {id: Number, description: String, isCompleted: Boolean}, }, toggleState: Function, + removeTodo: Function, }; onChange() { this.props.toggleState(this.props.todo.id); } + + onClick() { + this.props.removeTodo(this.props.todo.id); + } } diff --git a/awesome_owl/static/src/todo/todoItem.xml b/awesome_owl/static/src/todo/todoItem.xml index 41a95074697..2562016a15d 100644 --- a/awesome_owl/static/src/todo/todoItem.xml +++ b/awesome_owl/static/src/todo/todoItem.xml @@ -11,6 +11,8 @@ . +
diff --git a/awesome_owl/static/src/todo/todoList.js b/awesome_owl/static/src/todo/todoList.js index d8bf6016409..8219db7a4f4 100644 --- a/awesome_owl/static/src/todo/todoList.js +++ b/awesome_owl/static/src/todo/todoList.js @@ -33,4 +33,10 @@ export class TodoList extends Component { if (todo) todo.isCompleted = !todo.isCompleted; } + + removeTodo(id) { + const index = this.todos.findIndex((elem) => elem.id === id); + if (index >= 0) + this.todos.splice(index, 1); + } } diff --git a/awesome_owl/static/src/todo/todoList.xml b/awesome_owl/static/src/todo/todoList.xml index 8cdd2ad27a1..c726be69096 100644 --- a/awesome_owl/static/src/todo/todoList.xml +++ b/awesome_owl/static/src/todo/todoList.xml @@ -8,7 +8,9 @@ t-ref='todo' t-on-keyup='addTodo'/> - +
From 2356a2289e31f3f3c02d3231427f6f132cd05772 Mon Sep 17 00:00:00 2001 From: "Cezar Bulancea (cebul)" Date: Thu, 30 Jul 2026 09:45:40 +0200 Subject: [PATCH 13/20] [IMP] awesome_owl: chapter 1.13 generic cards with slots --- awesome_owl/static/src/card/card.js | 2 +- awesome_owl/static/src/card/card.xml | 2 +- awesome_owl/static/src/playground.xml | 3 +++ 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/awesome_owl/static/src/card/card.js b/awesome_owl/static/src/card/card.js index 5630ee1cbb7..76b34338eb6 100644 --- a/awesome_owl/static/src/card/card.js +++ b/awesome_owl/static/src/card/card.js @@ -5,6 +5,6 @@ export class Card extends Component { static props = { title: {type: String}, - content: {type: String} + slots: {type: Object, optional: true}, }; } diff --git a/awesome_owl/static/src/card/card.xml b/awesome_owl/static/src/card/card.xml index dba38c0944c..7ba32f17941 100644 --- a/awesome_owl/static/src/card/card.xml +++ b/awesome_owl/static/src/card/card.xml @@ -5,7 +5,7 @@
- +
diff --git a/awesome_owl/static/src/playground.xml b/awesome_owl/static/src/playground.xml index 5c962da99d7..f83edf84aec 100644 --- a/awesome_owl/static/src/playground.xml +++ b/awesome_owl/static/src/playground.xml @@ -11,6 +11,9 @@ The sum is:
+ + +
From 2144d3e867347b61fc07dd0c7f8e0ae808368ddc Mon Sep 17 00:00:00 2001 From: "Cezar Bulancea (cebul)" Date: Thu, 30 Jul 2026 10:40:49 +0200 Subject: [PATCH 14/20] [IMP] awesome_owl: chapter 1.14 minimizing card content using a toggle button --- awesome_owl/static/src/card/card.js | 10 +++++++++- awesome_owl/static/src/card/card.xml | 15 ++++++++++++--- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/awesome_owl/static/src/card/card.js b/awesome_owl/static/src/card/card.js index 76b34338eb6..3a6b0824382 100644 --- a/awesome_owl/static/src/card/card.js +++ b/awesome_owl/static/src/card/card.js @@ -1,4 +1,4 @@ -import { Component } from "@odoo/owl"; +import { Component, useState } from "@odoo/owl"; export class Card extends Component { static template = "awesome_owl.card"; @@ -7,4 +7,12 @@ export class Card extends Component { title: {type: String}, slots: {type: Object, optional: true}, }; + + setup() { + this.state = useState({ isOpen: true }); + } + + toggleCard() { + this.state.isOpen = !this.state.isOpen; + } } diff --git a/awesome_owl/static/src/card/card.xml b/awesome_owl/static/src/card/card.xml index 7ba32f17941..db6fd985c68 100644 --- a/awesome_owl/static/src/card/card.xml +++ b/awesome_owl/static/src/card/card.xml @@ -2,9 +2,18 @@ -
-
-
+
+
+ +
+ +
+
+ +
From f640755389592c8a9c0df376f4faaa8ac9140eb6 Mon Sep 17 00:00:00 2001 From: "Cezar Bulancea (cebul)" Date: Thu, 30 Jul 2026 14:07:19 +0200 Subject: [PATCH 15/20] [IMP] awesome_dashboard: chapter 2.1 a new layout --- awesome_dashboard/static/src/dashboard.js | 2 ++ awesome_dashboard/static/src/dashboard.xml | 4 +++- awesome_dashboard/static/src/scss/dashboard.scss | 3 +++ 3 files changed, 8 insertions(+), 1 deletion(-) create mode 100644 awesome_dashboard/static/src/scss/dashboard.scss diff --git a/awesome_dashboard/static/src/dashboard.js b/awesome_dashboard/static/src/dashboard.js index c4fb245621b..8008b065392 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 { Layout } from "@web/search/layout"; import { registry } from "@web/core/registry"; 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.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 + diff --git a/awesome_dashboard/static/src/scss/dashboard.scss b/awesome_dashboard/static/src/scss/dashboard.scss new file mode 100644 index 00000000000..6be5e0f83c9 --- /dev/null +++ b/awesome_dashboard/static/src/scss/dashboard.scss @@ -0,0 +1,3 @@ +.o_dashboard { + background-color: gray; +} From 24b422d586c8fb64b38174d7169d8cd99e2fb020 Mon Sep 17 00:00:00 2001 From: "Cezar Bulancea (cebul)" Date: Thu, 30 Jul 2026 15:06:07 +0200 Subject: [PATCH 16/20] [IMP] awesome_dashboard: chapter 2.2 add some buttons for quick navigation --- awesome_dashboard/static/src/dashboard.js | 22 ++++++++++++++++++++++ awesome_dashboard/static/src/dashboard.xml | 18 +++++++++++++----- 2 files changed, 35 insertions(+), 5 deletions(-) diff --git a/awesome_dashboard/static/src/dashboard.js b/awesome_dashboard/static/src/dashboard.js index 8008b065392..6c81bb77d42 100644 --- a/awesome_dashboard/static/src/dashboard.js +++ b/awesome_dashboard/static/src/dashboard.js @@ -1,10 +1,32 @@ import { Component } from "@odoo/owl"; +import { useService } from "@web/core/utils/hooks"; import { Layout } from "@web/search/layout"; import { registry } from "@web/core/registry"; +import { _t } from "@web/core/l10n/translation"; 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"); + } + + openLeads() { + this.action.doAction({ + type: 'ir.actions.act_window', + name: _t("CRM 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..2b92d654b1a 100644 --- a/awesome_dashboard/static/src/dashboard.xml +++ b/awesome_dashboard/static/src/dashboard.xml @@ -1,10 +1,18 @@ - + - - - hello dashboard + + + + + +
+ Hello Dashboard +
-
From 374001241ab06732dfd1444ab56333c163e7244c Mon Sep 17 00:00:00 2001 From: "Cezar Bulancea (cebul)" Date: Thu, 30 Jul 2026 16:05:14 +0200 Subject: [PATCH 17/20] [IMP] awesome_dashboard: chapter 2.3 add a dashboard item --- awesome_dashboard/static/src/dashboard.js | 3 ++- awesome_dashboard/static/src/dashboard.xml | 15 ++++++++++++--- .../static/src/dashboardItem/dashboardItem.js | 12 ++++++++++++ .../static/src/dashboardItem/dashboardItem.xml | 8 ++++++++ 4 files changed, 34 insertions(+), 4 deletions(-) create mode 100644 awesome_dashboard/static/src/dashboardItem/dashboardItem.js create mode 100644 awesome_dashboard/static/src/dashboardItem/dashboardItem.xml diff --git a/awesome_dashboard/static/src/dashboard.js b/awesome_dashboard/static/src/dashboard.js index 6c81bb77d42..3971d9e07e8 100644 --- a/awesome_dashboard/static/src/dashboard.js +++ b/awesome_dashboard/static/src/dashboard.js @@ -3,10 +3,11 @@ import { useService } from "@web/core/utils/hooks"; import { Layout } from "@web/search/layout"; import { registry } from "@web/core/registry"; import { _t } from "@web/core/l10n/translation"; +import { DashboardItem } from "./dashboardItem/dashboardItem"; 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 2b92d654b1a..e55586d68ba 100644 --- a/awesome_dashboard/static/src/dashboard.xml +++ b/awesome_dashboard/static/src/dashboard.xml @@ -6,12 +6,21 @@ - -
- Hello Dashboard + +
+ +
Card 1
+

This item defaults to size 1 (18rem width).

+
+ + +
Card 2
+

This item has size 2 (36rem width).

+
diff --git a/awesome_dashboard/static/src/dashboardItem/dashboardItem.js b/awesome_dashboard/static/src/dashboardItem/dashboardItem.js new file mode 100644 index 00000000000..c85498ddca3 --- /dev/null +++ b/awesome_dashboard/static/src/dashboardItem/dashboardItem.js @@ -0,0 +1,12 @@ +import { Component } from "@odoo/owl"; + +export class DashboardItem extends Component { + static template = 'awesome_dashboard.AwesomeDashboardItem'; + static props = { + size: { type: Number, optional: true }, + slots: { type: Object, optional: true }, + }; + static defaultProps = { + size: 1, + }; +} diff --git a/awesome_dashboard/static/src/dashboardItem/dashboardItem.xml b/awesome_dashboard/static/src/dashboardItem/dashboardItem.xml new file mode 100644 index 00000000000..946185c0306 --- /dev/null +++ b/awesome_dashboard/static/src/dashboardItem/dashboardItem.xml @@ -0,0 +1,8 @@ + + + +
+ +
+
+
From 5678263b25065a6771cfdf0f7d1d65ef446510b5 Mon Sep 17 00:00:00 2001 From: "Cezar Bulancea (cebul)" Date: Fri, 31 Jul 2026 10:07:36 +0200 Subject: [PATCH 18/20] [IMP] awesome_dashboard: chapter 2.4 Call the server, add some statistics --- awesome_dashboard/static/src/dashboard.js | 13 +++-- awesome_dashboard/static/src/dashboard.xml | 58 ++++++++++++++++------ 2 files changed, 53 insertions(+), 18 deletions(-) diff --git a/awesome_dashboard/static/src/dashboard.js b/awesome_dashboard/static/src/dashboard.js index 3971d9e07e8..14bf245c032 100644 --- a/awesome_dashboard/static/src/dashboard.js +++ b/awesome_dashboard/static/src/dashboard.js @@ -1,8 +1,11 @@ -import { Component } from "@odoo/owl"; +import { Component, onWillStart } from "@odoo/owl"; +import { _t } from "@web/core/l10n/translation"; +import { rpc } from "@web/core/network/rpc" +import { registry } from "@web/core/registry"; import { useService } from "@web/core/utils/hooks"; import { Layout } from "@web/search/layout"; -import { registry } from "@web/core/registry"; -import { _t } from "@web/core/l10n/translation"; + + import { DashboardItem } from "./dashboardItem/dashboardItem"; class AwesomeDashboard extends Component { @@ -11,6 +14,10 @@ class AwesomeDashboard extends Component { setup() { this.action = useService("action"); + + onWillStart(async () => { + this.statistics = await rpc("/awesome_dashboard/statistics"); + }) } openCustomers() { diff --git a/awesome_dashboard/static/src/dashboard.xml b/awesome_dashboard/static/src/dashboard.xml index e55586d68ba..4a8fdc358b2 100644 --- a/awesome_dashboard/static/src/dashboard.xml +++ b/awesome_dashboard/static/src/dashboard.xml @@ -1,26 +1,54 @@ - - - - - + +
-
-
Card 1
-

This item defaults to size 1 (18rem width).

+
+
+ +
+
Number of new orders this month
+
- -
Card 2
-

This item has size 2 (36rem width).

+ +
+
+ +
+
Total amount of new orders this month
+
+
+ + +
+
+ +
+
Average amount of t-shirt by order this month
+
+ + +
+
+ +
+
Number of cancelled orders this month
+
+
+ + +
+
+ +
+
Average time from 'new' to 'sent' or 'cancelled'
+
+
+
From 3324d69c16ee54b471d9742fdfee3cc49d40ed53 Mon Sep 17 00:00:00 2001 From: "Cezar Bulancea (cebul)" Date: Fri, 31 Jul 2026 13:51:30 +0200 Subject: [PATCH 19/20] [IMP] awesome_dashboard: chapter 2.5 cache network calls, create a service --- awesome_dashboard/static/src/dashboard.js | 7 +++---- awesome_dashboard/static/src/dashboard.xml | 9 +++++++++ .../static/src/services/statistics_service.js | 13 +++++++++++++ 3 files changed, 25 insertions(+), 4 deletions(-) create mode 100644 awesome_dashboard/static/src/services/statistics_service.js diff --git a/awesome_dashboard/static/src/dashboard.js b/awesome_dashboard/static/src/dashboard.js index 14bf245c032..ed78b00f3d1 100644 --- a/awesome_dashboard/static/src/dashboard.js +++ b/awesome_dashboard/static/src/dashboard.js @@ -1,11 +1,9 @@ import { Component, onWillStart } from "@odoo/owl"; import { _t } from "@web/core/l10n/translation"; -import { rpc } from "@web/core/network/rpc" import { registry } from "@web/core/registry"; import { useService } from "@web/core/utils/hooks"; import { Layout } from "@web/search/layout"; - import { DashboardItem } from "./dashboardItem/dashboardItem"; class AwesomeDashboard extends Component { @@ -14,10 +12,11 @@ class AwesomeDashboard extends Component { setup() { this.action = useService("action"); + const statisticsService = useService("awesome_dashboard.statistics"); onWillStart(async () => { - this.statistics = await rpc("/awesome_dashboard/statistics"); - }) + this.statistics = await statisticsService.loadStatistics(); + }); } openCustomers() { diff --git a/awesome_dashboard/static/src/dashboard.xml b/awesome_dashboard/static/src/dashboard.xml index 4a8fdc358b2..24ba37897e4 100644 --- a/awesome_dashboard/static/src/dashboard.xml +++ b/awesome_dashboard/static/src/dashboard.xml @@ -2,6 +2,15 @@ + + + + +
diff --git a/awesome_dashboard/static/src/services/statistics_service.js b/awesome_dashboard/static/src/services/statistics_service.js new file mode 100644 index 00000000000..526aad9acc6 --- /dev/null +++ b/awesome_dashboard/static/src/services/statistics_service.js @@ -0,0 +1,13 @@ +import { registry } from "@web/core/registry"; +import { memoize } from "@web/core/utils/functions"; +import { rpc } from "@web/core/network/rpc"; + +const statisticsService = { + start() { + return { + loadStatistics: memoize(() => rpc("/awesome_dashboard/statistics")), + }; + }, +}; + +registry.category("services").add("awesome_dashboard.statistics", statisticsService); From 3ef1a3ac8aef02e51622b39d1e866b71dd912456 Mon Sep 17 00:00:00 2001 From: "Cezar Bulancea (cebul)" Date: Fri, 31 Jul 2026 14:29:48 +0200 Subject: [PATCH 20/20] [IMP] awesome_dashboard: chapter 2.6 pie chart --- awesome_dashboard/static/src/dashboard.js | 3 +- awesome_dashboard/static/src/dashboard.xml | 5 +++ .../static/src/pieChart/pie_chart.js | 43 +++++++++++++++++++ .../static/src/pieChart/pie_chart.xml | 10 +++++ 4 files changed, 60 insertions(+), 1 deletion(-) create mode 100644 awesome_dashboard/static/src/pieChart/pie_chart.js create mode 100644 awesome_dashboard/static/src/pieChart/pie_chart.xml diff --git a/awesome_dashboard/static/src/dashboard.js b/awesome_dashboard/static/src/dashboard.js index ed78b00f3d1..4109e3d781b 100644 --- a/awesome_dashboard/static/src/dashboard.js +++ b/awesome_dashboard/static/src/dashboard.js @@ -5,10 +5,11 @@ import { useService } from "@web/core/utils/hooks"; import { Layout } from "@web/search/layout"; import { DashboardItem } from "./dashboardItem/dashboardItem"; +import { PieChart } from "./pieChart/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 24ba37897e4..90973a9daea 100644 --- a/awesome_dashboard/static/src/dashboard.xml +++ b/awesome_dashboard/static/src/dashboard.xml @@ -58,6 +58,11 @@
+ + Shirt orders by size + + +
diff --git a/awesome_dashboard/static/src/pieChart/pie_chart.js b/awesome_dashboard/static/src/pieChart/pie_chart.js new file mode 100644 index 00000000000..09c45b25cd8 --- /dev/null +++ b/awesome_dashboard/static/src/pieChart/pie_chart.js @@ -0,0 +1,43 @@ +import { loadJS } from "@web/core/assets"; +import { getColor } from "@web/core/colors/colors"; +import { Component, onWillStart, useRef, onMounted, onWillUnmount, onPatched } from "@odoo/owl"; + +export class PieChart extends Component { + static template = "awesome_dashboard.PieChart"; + static props = { + label: String, + data: Object, + }; + + setup() { + this.canvasRef = useRef("canvas"); + onWillStart(() => loadJS("/web/static/lib/Chart/Chart.js")); + onMounted(() => { + 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: "pie", + data: { + labels: labels, + datasets: [ + { + label: this.props.label, + data: data, + }, + ], + }, + }); + } +} diff --git a/awesome_dashboard/static/src/pieChart/pie_chart.xml b/awesome_dashboard/static/src/pieChart/pie_chart.xml new file mode 100644 index 00000000000..14e6684262c --- /dev/null +++ b/awesome_dashboard/static/src/pieChart/pie_chart.xml @@ -0,0 +1,10 @@ + + + +
+
+ +
+
+
+