diff --git a/.gitignore b/.gitignore
index b6e47617de1..61af8745c5f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -127,3 +127,5 @@ dmypy.json
# Pyre type checker
.pyre/
+
+.vscode/launch.js
diff --git a/.vscode/launch.json b/.vscode/launch.json
new file mode 100644
index 00000000000..cdc9bed8035
--- /dev/null
+++ b/.vscode/launch.json
@@ -0,0 +1,26 @@
+{
+ // Use IntelliSense to learn about possible attributes.
+ // Hover to view descriptions of existing attributes.
+ // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
+ "version": "0.2.0",
+ "configurations": [
+ {
+ "name": "Run (Update)",
+ "type": "debugpy",
+ "request": "launch",
+ "python": "/usr/bin/python3",
+ "program": "/home/odoo/odoo/odoo-bin",
+ "console": "integratedTerminal",
+ "args": [
+ "--database", "rd-demo",
+ "--dev", "xml",
+ // "-u", "we_guarantee",
+ "--addons-path", "/home/odoo/enterprise,/home/odoo/odoo/addons,/home/odoo/tutorials",
+ "--log-level", "info",
+ "--limit-time-real=0",
+ "--limit-time-cpu=0",
+ ],
+ "variablePresentation": {},
+ },
+ ]
+}
diff --git a/awesome_dashboard/__init__.py b/awesome_dashboard/__init__.py
index b0f26a9a602..aa4d0fd63a9 100644
--- a/awesome_dashboard/__init__.py
+++ b/awesome_dashboard/__init__.py
@@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
from . import controllers
+from . import models
diff --git a/awesome_dashboard/__manifest__.py b/awesome_dashboard/__manifest__.py
index 31406e8addb..2b96c7ca699 100644
--- a/awesome_dashboard/__manifest__.py
+++ b/awesome_dashboard/__manifest__.py
@@ -16,7 +16,7 @@
'version': '0.1',
'application': True,
'installable': True,
- 'depends': ['base', 'web', 'mail', 'crm'],
+ 'depends': ['base', 'web', 'mail', 'crm', 'sale'],
'data': [
'views/views.xml',
@@ -24,6 +24,11 @@
'assets': {
'web.assets_backend': [
'awesome_dashboard/static/src/**/*',
+ ('remove', 'awesome_dashboard/static/src/dashboard/**/*'),
+
+ ],
+ 'awesome_dashboard.dashboard': [
+ 'awesome_dashboard/static/src/dashboard/**/*',
],
},
'license': 'AGPL-3'
diff --git a/awesome_dashboard/controllers/__init__.py b/awesome_dashboard/controllers/__init__.py
index 457bae27e11..b0f26a9a602 100644
--- a/awesome_dashboard/controllers/__init__.py
+++ b/awesome_dashboard/controllers/__init__.py
@@ -1,3 +1,3 @@
# -*- coding: utf-8 -*-
-from . import controllers
\ No newline at end of file
+from . import controllers
diff --git a/awesome_dashboard/models/__init__.py b/awesome_dashboard/models/__init__.py
new file mode 100644
index 00000000000..82fcc4bfe96
--- /dev/null
+++ b/awesome_dashboard/models/__init__.py
@@ -0,0 +1 @@
+from . import res_users_settings
diff --git a/awesome_dashboard/models/res_users_settings.py b/awesome_dashboard/models/res_users_settings.py
new file mode 100644
index 00000000000..b6efc3c0c6c
--- /dev/null
+++ b/awesome_dashboard/models/res_users_settings.py
@@ -0,0 +1,10 @@
+# models/res_users.py
+from odoo import models, fields
+
+
+class ResUsersSettings(models.Model):
+ _inherit = ["res.users.settings"]
+
+ disabled_items = fields.Char(
+ string="Awesome Dashboard Disabled Items",
+ )
diff --git a/awesome_dashboard/static/src/dashboard.js b/awesome_dashboard/static/src/dashboard.js
deleted file mode 100644
index 637fa4bb972..00000000000
--- a/awesome_dashboard/static/src/dashboard.js
+++ /dev/null
@@ -1,10 +0,0 @@
-/** @odoo-module **/
-
-import { Component } from "@odoo/owl";
-import { registry } from "@web/core/registry";
-
-class AwesomeDashboard extends Component {
- static template = "awesome_dashboard.AwesomeDashboard";
-}
-
-registry.category("actions").add("awesome_dashboard.dashboard", AwesomeDashboard);
diff --git a/awesome_dashboard/static/src/dashboard.xml b/awesome_dashboard/static/src/dashboard.xml
deleted file mode 100644
index 1a2ac9a2fed..00000000000
--- a/awesome_dashboard/static/src/dashboard.xml
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
-
-
- hello dashboard
-
-
-
diff --git a/awesome_dashboard/static/src/dashboard/config_dialog/config_dialog.js b/awesome_dashboard/static/src/dashboard/config_dialog/config_dialog.js
new file mode 100644
index 00000000000..b6a39eced4d
--- /dev/null
+++ b/awesome_dashboard/static/src/dashboard/config_dialog/config_dialog.js
@@ -0,0 +1,36 @@
+import { Component, useState } from "@odoo/owl";
+import { Dialog } from "@web/core/dialog/dialog";
+import { CheckBox } from "@web/core/checkbox/checkbox";
+
+
+export class ConfigDialog extends Component {
+ static template = "awesome_dashboard.ConfigDialog";
+ static components = { Dialog, CheckBox };
+ static props = ["close", "items", "disabledItems", "onUpdateConfigs"];
+ setup() {
+
+ this.items = useState(this.props.items.map((item) => {
+ return {
+ ...item,
+ enabled: !this.props.disabledItems.includes(item.id),
+ }
+ }));
+
+ }
+
+ onChange(checkedItems, Item) {
+ Item.enabled = checkedItems;
+
+ const updatedDisabledItems = Object.values(this.items).filter(
+ (item) => !item.enabled
+ ).map((item) => item.id);
+
+ this.props.onUpdateConfigs(updatedDisabledItems);
+ }
+
+ done() {
+ this.props.close();
+ }
+
+}
+
diff --git a/awesome_dashboard/static/src/dashboard/config_dialog/config_dialog.xml b/awesome_dashboard/static/src/dashboard/config_dialog/config_dialog.xml
new file mode 100644
index 00000000000..975eba8d94a
--- /dev/null
+++ b/awesome_dashboard/static/src/dashboard/config_dialog/config_dialog.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
diff --git a/awesome_dashboard/static/src/dashboard/dashboard.css b/awesome_dashboard/static/src/dashboard/dashboard.css
new file mode 100644
index 00000000000..90a9bb80806
--- /dev/null
+++ b/awesome_dashboard/static/src/dashboard/dashboard.css
@@ -0,0 +1,11 @@
+.o_dashboard {
+ background-color: #f0f0f0;
+}
+@media (max-width: 767.98px) {
+ .o_dashboard .card {
+ width: 100% !important; /* force full width */
+ max-width: 100% !important;
+ display: block !important; /* no inline-block on mobile */
+ }
+}
+
diff --git a/awesome_dashboard/static/src/dashboard/dashboard.js b/awesome_dashboard/static/src/dashboard/dashboard.js
new file mode 100644
index 00000000000..7432e11252b
--- /dev/null
+++ b/awesome_dashboard/static/src/dashboard/dashboard.js
@@ -0,0 +1,52 @@
+import { Component, useState } from "@odoo/owl";
+import { registry } from "@web/core/registry";
+import { Layout } from "@web/search/layout";
+import { useService } from "@web/core/utils/hooks";
+import { DashboardItem } from "./dashboard_item";
+import { ConfigDialog } from "./config_dialog/config_dialog";
+import { _t } from "@web/core/l10n/translation";
+import { user } from "@web/core/user";
+
+class AwesomeDashboard extends Component {
+ static template = "awesome_dashboard.AwesomeDashboard";
+ static components = { Layout, DashboardItem }
+ setup() {
+ this.action = useService("action");
+ this.result = useState(useService("awesome_dashboard.statistics"));
+ this.items = registry.category("awesome_dashboard").getAll();
+ this.dialog = useService("dialog");
+
+ this.state = useState({
+ disabledItems: user.settings.disabled_items || [],
+ });
+ }
+ openCustomers() {
+ this.action.doAction("base.action_partner_form");
+ }
+ openConfigs() {
+ this.dialog.add(ConfigDialog, {
+ items: this.items,
+ disabledItems: this.state.disabledItems,
+ onUpdateConfigs: this.updateConfigs.bind(this),
+ });
+ }
+
+ updateConfigs(newDisabledItems) {
+ this.state.disabledItems = newDisabledItems;
+ user.setUserSettings("disabled_items", this.state.disabledItems)
+ }
+
+ openLeads() {
+
+ this.action.doAction({
+ type: 'ir.actions.act_window',
+ name: _t("CRM Leads"),
+ target: 'current',
+ res_model: 'crm.lead',
+ views: [[false,'list'],[false, 'form']],
+ });
+ }
+}
+
+registry.category("lazy_components").add("AwesomeDashboard", AwesomeDashboard);
+
diff --git a/awesome_dashboard/static/src/dashboard/dashboard.xml b/awesome_dashboard/static/src/dashboard/dashboard.xml
new file mode 100644
index 00000000000..43ee18533de
--- /dev/null
+++ b/awesome_dashboard/static/src/dashboard/dashboard.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/awesome_dashboard/static/src/dashboard/dashboard_item.js b/awesome_dashboard/static/src/dashboard/dashboard_item.js
new file mode 100644
index 00000000000..a905545373f
--- /dev/null
+++ b/awesome_dashboard/static/src/dashboard/dashboard_item.js
@@ -0,0 +1,19 @@
+import { Component } from "@odoo/owl";
+
+export class DashboardItem extends Component {
+ static template = "awesome_dashboard.DashboardItem";
+ static defaultProps = { size: 1};
+ static props = {
+ size: {
+ type: Number,
+ optional: true,
+ },
+ slots: {
+ type: Object,
+ },
+ };
+ get itemWidth() {
+ return 18*this.props.size
+ }
+}
+
diff --git a/awesome_dashboard/static/src/dashboard/dashboard_item.xml b/awesome_dashboard/static/src/dashboard/dashboard_item.xml
new file mode 100644
index 00000000000..cc26acdb805
--- /dev/null
+++ b/awesome_dashboard/static/src/dashboard/dashboard_item.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
diff --git a/awesome_dashboard/static/src/dashboard/dashboard_items.js b/awesome_dashboard/static/src/dashboard/dashboard_items.js
new file mode 100644
index 00000000000..3ee2f1b5ab1
--- /dev/null
+++ b/awesome_dashboard/static/src/dashboard/dashboard_items.js
@@ -0,0 +1,72 @@
+import { NumberCard } from "./numbercard/number_card";
+import { PieChartCard } from "./piechartcard/piechart_card";
+import { registry } from "@web/core/registry";
+import { _t } from "@web/core/l10n/translation";
+
+export const items = [
+ {
+ id: "average_quantity",
+ description: _t("Average amount of t-shirt"),
+ Component: NumberCard,
+ props: (data) => ({
+ title: _t("Average amount of tees ordered this month"),
+ value: data.average_quantity,
+ })
+ },
+
+ {
+ id: "average_time",
+ description: _t("Average time of an order"),
+ Component: NumberCard,
+ props: (data) => ({
+ title: _t("Average time for an order to go from 'new' to 'sent' or 'cancelled'"),
+ value: data.average_time,
+ })
+ },
+
+ {
+ id: "nb_new_orders",
+ description: _t("Number of new orders this month"),
+ Component: NumberCard,
+ props: (data) => ({
+ title: _t("Number of new orders this month"),
+ value: data.nb_new_orders,
+ })
+ },
+
+ {
+ id: "nb_cancelled_orders",
+ description: _t("Number of cancelled orders this month"),
+ Component: NumberCard,
+ props: (data) => ({
+ title: _t("Number of cancelled orders this month"),
+ value: data.nb_cancelled_orders,
+ })
+ },
+
+ {
+ id: "total_amount",
+ description: _t("Total amount of new orders this month"),
+ Component: NumberCard,
+ props: (data) => ({
+ title: _t("Total amount of new orders this month"),
+ value: data.total_amount,
+ })
+ },
+
+ {
+ id: "orders_pie_chart",
+ description: _t("Shirt order by size"),
+ Component: PieChartCard,
+ props: (data) => ({
+ title: _t("Shirt order by size"),
+ data: data.orders_by_size,
+ })
+ },
+
+]
+
+items.forEach(item => {
+ registry.category("awesome_dashboard").add(item.id, item);
+})
+
diff --git a/awesome_dashboard/static/src/dashboard/numbercard/number_card.js b/awesome_dashboard/static/src/dashboard/numbercard/number_card.js
new file mode 100644
index 00000000000..9b52b682dde
--- /dev/null
+++ b/awesome_dashboard/static/src/dashboard/numbercard/number_card.js
@@ -0,0 +1,14 @@
+import { Component } from "@odoo/owl";
+
+export class NumberCard extends Component {
+ static template = "awesome_dashboard.NumberCard";
+ static props = {
+ title: {
+ type: String,
+ },
+ value: {
+ type: Number,
+ },
+ };
+}
+
diff --git a/awesome_dashboard/static/src/dashboard/numbercard/number_card.xml b/awesome_dashboard/static/src/dashboard/numbercard/number_card.xml
new file mode 100644
index 00000000000..619c1fba23b
--- /dev/null
+++ b/awesome_dashboard/static/src/dashboard/numbercard/number_card.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
diff --git a/awesome_dashboard/static/src/dashboard/pie_chart/pie_chart.js b/awesome_dashboard/static/src/dashboard/pie_chart/pie_chart.js
new file mode 100644
index 00000000000..3b442df0cf9
--- /dev/null
+++ b/awesome_dashboard/static/src/dashboard/pie_chart/pie_chart.js
@@ -0,0 +1,71 @@
+import { Component, onWillStart,useRef, onMounted, onWillUnmount, useEnv } from "@odoo/owl";
+import { loadJS } from "@web/core/assets";
+import { _t } from "@web/core/l10n/translation";
+
+
+
+export class PieChart extends Component {
+ static template = "awesome_dashboard.PieChart";
+ static props = {
+ label: {
+ type: String,
+ optional: true,
+ },
+ data: Object,
+ clickview: {
+ type: Function,
+ optional: true,
+ },
+ }
+ setup() {
+ this.canvasRef = useRef("canvas_pie");
+ this.env = useEnv();
+ onWillStart(async () => loadJS("/web/static/lib/Chart/Chart.js"));
+ onMounted(()=> {
+ this.renderChart();
+ });
+
+ onWillUnmount(()=> {
+ this.chart?.destroy();
+ });
+
+ }
+
+
+
+ renderChart() {
+ this.chart = new Chart(this.canvasRef.el,{
+ type: 'pie',
+ data : {
+ labels : Object.keys(this.props.data),
+ datasets:[
+ {
+ label: this.props.label,
+ data : Object.values(this.props.data),
+ },
+ ],
+ },
+ options:{
+ events: ['click'],
+ onClick: (ev, section) => {
+ console.log(section);
+ const index = section[0].index
+ console.log("heere maybee ?");
+ const size = Object.keys(this.props.data)[index];
+ this.env.services.action.doAction({
+ type:'ir.actions.act_window',
+ name: _t("Orders - Size " + size),
+ target: 'current',
+ res_model: 'sale.order',
+ views: [[false,'list']],
+ domain: [['order_line.name', 'ilike', size]],
+
+ })
+
+ }
+ },
+ });
+
+ }
+}
+
diff --git a/awesome_dashboard/static/src/dashboard/pie_chart/pie_chart.xml b/awesome_dashboard/static/src/dashboard/pie_chart/pie_chart.xml
new file mode 100644
index 00000000000..5d5030f84cf
--- /dev/null
+++ b/awesome_dashboard/static/src/dashboard/pie_chart/pie_chart.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
diff --git a/awesome_dashboard/static/src/dashboard/piechartcard/piechart_card.js b/awesome_dashboard/static/src/dashboard/piechartcard/piechart_card.js
new file mode 100644
index 00000000000..66d17cf7216
--- /dev/null
+++ b/awesome_dashboard/static/src/dashboard/piechartcard/piechart_card.js
@@ -0,0 +1,17 @@
+import { Component } from "@odoo/owl";
+import { PieChart } from "../pie_chart/pie_chart";
+
+export class PieChartCard extends Component {
+ static components = { PieChart }
+ static template = "awesome_dashboard.PieChartCard";
+ static props = {
+ title: {
+ type: String,
+ },
+ data: {
+ type: Object,
+ },
+
+ };
+}
+
diff --git a/awesome_dashboard/static/src/dashboard/piechartcard/piechart_card.xml b/awesome_dashboard/static/src/dashboard/piechartcard/piechart_card.xml
new file mode 100644
index 00000000000..7ea9d414d97
--- /dev/null
+++ b/awesome_dashboard/static/src/dashboard/piechartcard/piechart_card.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
diff --git a/awesome_dashboard/static/src/dashboard/statistics_service.js b/awesome_dashboard/static/src/dashboard/statistics_service.js
new file mode 100644
index 00000000000..39bdcf5dd6a
--- /dev/null
+++ b/awesome_dashboard/static/src/dashboard/statistics_service.js
@@ -0,0 +1,24 @@
+import { registry } from "@web/core/registry";
+import { rpc } from "@web/core/network/rpc";
+import { reactive } from "@odoo/owl"
+
+
+const statisticsService = {
+ start() {
+
+ const statistics = reactive({isReady : false});
+
+ async function loadingData() {
+ const data = await rpc("/awesome_dashboard/statistics");
+ Object.assign(statistics,data,{isReady : true});
+
+ }
+
+ loadingData();
+ setInterval(loadingData, 10*60*1000);
+ return statistics;
+ },
+};
+
+registry.category("services").add("awesome_dashboard.statistics", statisticsService);
+
diff --git a/awesome_dashboard/static/src/dashboard_action.js b/awesome_dashboard/static/src/dashboard_action.js
new file mode 100644
index 00000000000..c00005754a6
--- /dev/null
+++ b/awesome_dashboard/static/src/dashboard_action.js
@@ -0,0 +1,13 @@
+import { Component, xml } from "@odoo/owl";
+import {LazyComponent } from "@web/core/assets";
+import { registry } from "@web/core/registry";
+
+export class AwesomeDashboardLoader extends Component {
+ static components = { LazyComponent };
+ static template = xml `
+
+ `;
+}
+
+registry.category("actions").add("awesome_dashboard.dashboard", AwesomeDashboardLoader);
+
diff --git a/awesome_owl/static/src/card/card.js b/awesome_owl/static/src/card/card.js
new file mode 100644
index 00000000000..bfa08c956e5
--- /dev/null
+++ b/awesome_owl/static/src/card/card.js
@@ -0,0 +1,24 @@
+import { Component, useState } from "@odoo/owl";
+
+export class Card extends Component {
+ static template = "awesome_owl.Card"
+
+ static props = {
+ title: String,
+ content:{
+ type: String,
+ optional: true,
+ },
+ slots: Object, // I added this to allow slots inside cards
+ };
+
+ setup(){
+ this.state = useState({open : true});
+ }
+
+ onToggleVisibility() {
+ this.state.open = !this.state.open;
+ }
+
+}
+
diff --git a/awesome_owl/static/src/card/card.xml b/awesome_owl/static/src/card/card.xml
new file mode 100644
index 00000000000..c9594077f40
--- /dev/null
+++ b/awesome_owl/static/src/card/card.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/awesome_owl/static/src/counter/counter.js b/awesome_owl/static/src/counter/counter.js
new file mode 100644
index 00000000000..c92194e61d6
--- /dev/null
+++ b/awesome_owl/static/src/counter/counter.js
@@ -0,0 +1,20 @@
+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/counter/counter.xml b/awesome_owl/static/src/counter/counter.xml
new file mode 100644
index 00000000000..15d6d402e58
--- /dev/null
+++ b/awesome_owl/static/src/counter/counter.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
diff --git a/awesome_owl/static/src/playground.js b/awesome_owl/static/src/playground.js
index 657fb8b07bb..37609ece487 100644
--- a/awesome_owl/static/src/playground.js
+++ b/awesome_owl/static/src/playground.js
@@ -1,7 +1,20 @@
-/** @odoo-module **/
-
-import { Component } from "@odoo/owl";
+import { Component, markup , useState} from "@odoo/owl";
+import { Card } from "./card/card";
+import { Counter } from "./counter/counter";
+import { TodoList } from "./todo/todo_list";
export class Playground extends Component {
static template = "awesome_owl.playground";
+ static components = { Card , Counter, TodoList };
+ setup() {
+ this.htmlContent = markup("
some content
");
+ this.state = useState({ sum : 0 });
+ this.incrementSum = this.incrementSum.bind(this);
+ }
+
+ incrementSum() {
+ this.state.sum++;
+ }
+
}
+
diff --git a/awesome_owl/static/src/playground.xml b/awesome_owl/static/src/playground.xml
index 4fb905d59f9..68cc2f03e05 100644
--- a/awesome_owl/static/src/playground.xml
+++ b/awesome_owl/static/src/playground.xml
@@ -1,10 +1,17 @@
-
-
- hello world
+
+
+
+
+
+
+
-
+
+
+
diff --git a/awesome_owl/static/src/todo/todo_item.js b/awesome_owl/static/src/todo/todo_item.js
new file mode 100644
index 00000000000..e59997f2475
--- /dev/null
+++ b/awesome_owl/static/src/todo/todo_item.js
@@ -0,0 +1,33 @@
+import { Component } 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,
+ },
+ },
+ toggleState: {
+ type: Function,
+ optional: true,
+ },
+ removeTodo: {
+ type: Function,
+ optional: true,
+ }
+ };
+
+ onToggleState() {
+ this.props.toggleState?.(this.props.todo.id);
+ }
+
+ onDeleteTodo() {
+ this.props.removeTodo?.(this.props.todo.id);
+ }
+}
+
diff --git a/awesome_owl/static/src/todo/todo_item.xml b/awesome_owl/static/src/todo/todo_item.xml
new file mode 100644
index 00000000000..0ba2d7c13b6
--- /dev/null
+++ b/awesome_owl/static/src/todo/todo_item.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+ .
+
+
+
+
+
+
+
diff --git a/awesome_owl/static/src/todo/todo_list.js b/awesome_owl/static/src/todo/todo_list.js
new file mode 100644
index 00000000000..9e76c5f4f08
--- /dev/null
+++ b/awesome_owl/static/src/todo/todo_list.js
@@ -0,0 +1,47 @@
+import { Component, useState, useRef, onMounted } 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([]);
+ this.state = useState({ counter_id : 0});
+ this.inputRef = useRef('newTodoInput');
+ onMounted(() => {
+ this.inputRef.el.focus();
+ })
+ }
+
+ addTodo(ev) {
+ if (ev.keyCode === 13 && ev.target.value){
+ const newTodo = {
+ id : this.state.counter_id,
+ description : ev.target.value,
+ isCompleted : false,
+ }
+ this.state.counter_id++;
+ this.todos.push(newTodo);
+ ev.target.value = "";
+ };
+
+ }
+
+ toggleTodoState(todoId) {
+ const todo = this.todos.find(t=>t.id === todoId);
+ if (todo) {
+ todo.isCompleted = !todo.isCompleted;
+ }
+ }
+
+ DeleteTodo(todoId) {
+ const todoIndex = this.todos.findIndex(t=>t.id === todoId);
+ if (todoIndex !== -1){
+ this.todos.splice(todoIndex, 1)
+ }
+ }
+}
+
diff --git a/awesome_owl/static/src/todo/todo_list.xml b/awesome_owl/static/src/todo/todo_list.xml
new file mode 100644
index 00000000000..2fc46642adc
--- /dev/null
+++ b/awesome_owl/static/src/todo/todo_list.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+