diff --git a/awesome_dashboard/__manifest__.py b/awesome_dashboard/__manifest__.py
index 31406e8addb..9cb889db903 100644
--- a/awesome_dashboard/__manifest__.py
+++ b/awesome_dashboard/__manifest__.py
@@ -24,6 +24,10 @@
'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/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/dashboard.js b/awesome_dashboard/static/src/dashboard/dashboard.js
new file mode 100644
index 00000000000..ee40fe11a73
--- /dev/null
+++ b/awesome_dashboard/static/src/dashboard/dashboard.js
@@ -0,0 +1,106 @@
+/** @odoo-module **/
+
+import { _t } from "@web/core/l10n/translation";
+import { Layout } from "@web/search/layout"
+import { Component, useState } from "@odoo/owl";
+import { registry } from "@web/core/registry";
+import { useService } from "@web/core/utils/hooks";
+
+import { DashboardItem } from "./dashboard_item/dashboard_item";
+import { NumberCard } from "./dashboard_item/cards/number_card";
+import { PieChartCard } from "./dashboard_item/cards/pie_chart_card";
+import { Dialog } from "@web/core/dialog/dialog";
+import { CheckBox } from "@web/core/checkbox/checkbox";
+import { browser } from "@web/core/browser/browser";
+
+
+class AwesomeDashboard extends Component {
+ static template = "awesome_dashboard.AwesomeDashboard";
+ static components = { Layout, DashboardItem, NumberCard, PieChartCard, Dialog }
+
+ setup() {
+ this.action = useService("action");
+ this.statisticsService = useService("awesome_dashboard.statistics");
+
+ this.statistics = useState(this.statisticsService);
+
+ this.items = registry.category("awesome_dashboard").getAll();
+
+ this.state = useState({
+ disabledItems: browser.localStorage.getItem("disabledDashboardItems")?.split(",") || []
+ });
+
+ this.dialogService = useService("dialog");
+ }
+
+ openCustomers() {
+ this.action.doAction("base.action_partner_form");
+ }
+
+ openLeads() {
+ this.action.doAction({
+ type: 'ir.actions.act_window',
+ name: "All leads",
+ res_model: 'crm.lead',
+ views: [[false, 'list'], [false, 'form']],
+ });
+ }
+
+ updateConfiguration(newDisabledItems) {
+ this.state.disabledItems = newDisabledItems;
+ }
+
+ openDialog() {
+ this.dialog = this.dialogService.add(ConfigurationDialog, {
+ title: _t("Dashboard items configuration"),
+ contentHeader: _t("Which cards do you want to see ?"),
+ items: this.items,
+ disabledItemsIds: this.state.disabledItems,
+ updateConfiguration: (newDisabledItems) => this.updateConfiguration(newDisabledItems)
+ });
+ }
+}
+
+class ConfigurationDialog extends Component {
+ static template = "awesome_dashboard.ConfigurationDialog";
+ static components = { Dialog, CheckBox }
+
+ static props = {
+ title: { type: String, optional: true },
+ contentHeader: { type: String, optional: true },
+ items: Object,
+ disabledItemsIds: Object,
+ updateConfiguration: Function,
+ close: { type: Function }
+ }
+
+ static defaultProps = {
+ title: _t("Dashboard items configuration"),
+ contentHeader: _t("Which cards do you want to see ?"),
+ }
+
+ setup() {
+ this.items = useState(this.props.items.map((item) => {
+ return {
+ ...item,
+ enabled: !this.props.disabledItemsIds.includes(item.id)
+ }
+ }));
+ }
+
+ onChange(checked, changedItem) {
+ changedItem.enabled = checked;
+ }
+
+ onApply() {
+ const disabledItems = this.items.filter((item) => !item.enabled).map((item) => item.id);
+
+ browser.localStorage.setItem("disabledDashboardItems", disabledItems);
+
+ this.props.updateConfiguration(disabledItems);
+
+ this.props.close();
+ }
+}
+
+registry.category("lazy_components").add("AwesomeDashboard", AwesomeDashboard);
diff --git a/awesome_dashboard/static/src/dashboard/dashboard.scss b/awesome_dashboard/static/src/dashboard/dashboard.scss
new file mode 100644
index 00000000000..769fc1e72f9
--- /dev/null
+++ b/awesome_dashboard/static/src/dashboard/dashboard.scss
@@ -0,0 +1,3 @@
+.o_dashboard {
+ background-color: gray;
+}
\ No newline at end of file
diff --git a/awesome_dashboard/static/src/dashboard/dashboard.xml b/awesome_dashboard/static/src/dashboard/dashboard.xml
new file mode 100644
index 00000000000..b76861b0b5d
--- /dev/null
+++ b/awesome_dashboard/static/src/dashboard/dashboard.xml
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/awesome_dashboard/static/src/dashboard/dashboard_item/cards/number_card.js b/awesome_dashboard/static/src/dashboard/dashboard_item/cards/number_card.js
new file mode 100644
index 00000000000..a14a6fe461d
--- /dev/null
+++ b/awesome_dashboard/static/src/dashboard/dashboard_item/cards/number_card.js
@@ -0,0 +1,15 @@
+import { Component } from "@odoo/owl";
+
+export class NumberCard extends Component {
+ static template = "awesome_dashboard.number_card";
+
+ static props = {
+ title: { type: String },
+ number: { type: Number },
+ };
+
+ setup() {
+
+ }
+}
+
diff --git a/awesome_dashboard/static/src/dashboard/dashboard_item/cards/number_card.xml b/awesome_dashboard/static/src/dashboard/dashboard_item/cards/number_card.xml
new file mode 100644
index 00000000000..432f8e81083
--- /dev/null
+++ b/awesome_dashboard/static/src/dashboard/dashboard_item/cards/number_card.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/awesome_dashboard/static/src/dashboard/dashboard_item/cards/pie_chart_card.js b/awesome_dashboard/static/src/dashboard/dashboard_item/cards/pie_chart_card.js
new file mode 100644
index 00000000000..1b3ae58f3ac
--- /dev/null
+++ b/awesome_dashboard/static/src/dashboard/dashboard_item/cards/pie_chart_card.js
@@ -0,0 +1,17 @@
+import { Component } from "@odoo/owl";
+import { PieChart } from "../charts/pie_chart";
+
+export class PieChartCard extends Component {
+ static template = "awesome_dashboard.pie_chart_card";
+ static components = { PieChart };
+
+ static props = {
+ label: String,
+ data: { type: Object }
+ };
+
+ setup() {
+
+ }
+}
+
diff --git a/awesome_dashboard/static/src/dashboard/dashboard_item/cards/pie_chart_card.xml b/awesome_dashboard/static/src/dashboard/dashboard_item/cards/pie_chart_card.xml
new file mode 100644
index 00000000000..f5523adf258
--- /dev/null
+++ b/awesome_dashboard/static/src/dashboard/dashboard_item/cards/pie_chart_card.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
diff --git a/awesome_dashboard/static/src/dashboard/dashboard_item/charts/pie_chart.js b/awesome_dashboard/static/src/dashboard/dashboard_item/charts/pie_chart.js
new file mode 100644
index 00000000000..1f87f934cbb
--- /dev/null
+++ b/awesome_dashboard/static/src/dashboard/dashboard_item/charts/pie_chart.js
@@ -0,0 +1,52 @@
+
+import { Component, onWillStart, onWillUnmount, onMounted, useRef, onWillUpdateProps } from "@odoo/owl";
+import { loadJS } from "@web/core/assets";
+
+
+export class PieChart extends Component {
+ static template = "awesome_dashboard.pie_chart"
+
+ static props = {
+ data: { type: Object, optional: true }
+ }
+
+ setup() {
+ this.canvasRef = useRef("canvas");
+ this.chart = null
+
+ onWillStart(async () => {
+ await loadJS(["/web/static/lib/Chart/Chart.js"]);
+ })
+
+
+ onMounted(() => this.renderChart());
+
+ onWillUnmount(() => {
+ if (this.chart) {
+ this.chart.destroy();
+ }
+ });
+
+ onWillUpdateProps(() => this.renderChart());
+ }
+
+ renderChart() {
+ if (this.chart) {
+ this.chart.destroy();
+ }
+
+ if (this.props.data) {
+ let data = {
+ datasets: [{
+ data: Object.values(this.props.data)
+ }],
+ labels: Object.keys(this.props.data)
+ }
+
+ this.chart = new Chart(this.canvasRef.el, {
+ type: 'pie',
+ data: data
+ });
+ }
+ }
+}
\ No newline at end of file
diff --git a/awesome_dashboard/static/src/dashboard/dashboard_item/charts/pie_chart.xml b/awesome_dashboard/static/src/dashboard/dashboard_item/charts/pie_chart.xml
new file mode 100644
index 00000000000..921e0b7f115
--- /dev/null
+++ b/awesome_dashboard/static/src/dashboard/dashboard_item/charts/pie_chart.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
diff --git a/awesome_dashboard/static/src/dashboard/dashboard_item/dashboard_item.js b/awesome_dashboard/static/src/dashboard/dashboard_item/dashboard_item.js
new file mode 100644
index 00000000000..987e9e9bc2b
--- /dev/null
+++ b/awesome_dashboard/static/src/dashboard/dashboard_item/dashboard_item.js
@@ -0,0 +1,19 @@
+import { Component } from "@odoo/owl";
+
+export class DashboardItem extends Component {
+ static template = "awesome_dashboard.dashboard_item";
+
+ static props = {
+ size: { type: Number, default: 1, optional: true },
+ slots: Object,
+ };
+
+ static defaultProps = {
+ size: 1,
+ };
+
+ setup() {
+
+ }
+}
+
diff --git a/awesome_dashboard/static/src/dashboard/dashboard_item/dashboard_item.xml b/awesome_dashboard/static/src/dashboard/dashboard_item/dashboard_item.xml
new file mode 100644
index 00000000000..28aaabeacd0
--- /dev/null
+++ b/awesome_dashboard/static/src/dashboard/dashboard_item/dashboard_item.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
diff --git a/awesome_dashboard/static/src/dashboard/dashboard_item/dashboard_items.js b/awesome_dashboard/static/src/dashboard/dashboard_item/dashboard_items.js
new file mode 100644
index 00000000000..4ffb7c866d1
--- /dev/null
+++ b/awesome_dashboard/static/src/dashboard/dashboard_item/dashboard_items.js
@@ -0,0 +1,65 @@
+import { _t } from "@web/core/l10n/translation";
+import { NumberCard } from "./cards/number_card";
+import { PieChartCard } from "./cards/pie_chart_card";
+import { registry } from "@web/core/registry";
+
+const dashboardItems = [
+ {
+ id: "average_quantity",
+ description: _t("Average amount of t-shirt"),
+ Component: NumberCard,
+ props: (data) => ({
+ title: _t("Average amount of t-shirt by order this month"),
+ number: data.average_quantity
+ }),
+ },
+ {
+ id: "average_time",
+ description: _t("Average time for an order to go complete"),
+ Component: NumberCard,
+ props: (data) => ({
+ title: _t("Average time for an order to go from 'new' to 'sent' or 'cancelled'"),
+ number: data.average_time
+ }),
+ },
+ {
+ id: "nb_new_orders",
+ description: _t("Number of new orders"),
+ Component: NumberCard,
+ props: (data) => ({
+ title: _t("Number of new orders this month"),
+ number: data.nb_new_orders
+ }),
+ },
+ {
+ id: "nb_cancelled_orders",
+ description: _t("Number of cancelled orders"),
+ Component: NumberCard,
+ props: (data) => ({
+ title: _t("Number of cancelled orders this month"),
+ number: data.nb_cancelled_orders
+ }),
+ },
+ {
+ id: "total_amount",
+ description: _t("Total amount of new orders"),
+ Component: NumberCard,
+ props: (data) => ({
+ title: _t("Total amount of new orders this month"),
+ number: data.total_amount
+ }),
+ },
+ {
+ id: "orders_by_size",
+ description: _t("Shirt orders by size"),
+ Component: PieChartCard,
+ props: (data) => ({
+ label: _t("Shirt orders by size"),
+ data: data.orders_by_size
+ }),
+ },
+]
+
+dashboardItems.forEach(item => {
+ registry.category("awesome_dashboard").add(item.id, item)
+})
\ No newline at end of file
diff --git a/awesome_dashboard/static/src/dashboard/services/statistics_service.js b/awesome_dashboard/static/src/dashboard/services/statistics_service.js
new file mode 100644
index 00000000000..b4433392a28
--- /dev/null
+++ b/awesome_dashboard/static/src/dashboard/services/statistics_service.js
@@ -0,0 +1,26 @@
+import { registry } from "@web/core/registry";
+import { rpc } from "@web/core/network/rpc";
+import { memoize } from "@web/core/utils/functions";
+import { reactive } from "@odoo/owl";
+
+
+const statisticsService = {
+ start() {
+ const stats = reactive({ isReady: false });
+
+ async function loadData() {
+ const fetchedStats = await rpc("/awesome_dashboard/statistics");
+ Object.assign(stats, fetchedStats, { isReady: true });
+ }
+
+ loadData();
+
+ setInterval(async () => {
+ loadData();
+ }, 1000*10);
+
+ return stats
+ },
+};
+
+registry.category("services").add("awesome_dashboard.statistics", statisticsService);
\ No newline at end of file
diff --git a/awesome_dashboard/static/src/dashboard_loader.js b/awesome_dashboard/static/src/dashboard_loader.js
new file mode 100644
index 00000000000..2d2a6446ead
--- /dev/null
+++ b/awesome_dashboard/static/src/dashboard_loader.js
@@ -0,0 +1,14 @@
+/** @odoo-module **/
+
+import { registry } from "@web/core/registry";
+import { Component, xml } from "@odoo/owl";
+import { LazyComponent } from "@web/core/assets";
+
+export class AwesomeDashboardLoader extends Component {
+ static components = { LazyComponent };
+ static template = xml`
+
+ `;
+}
+
+registry.category("actions").add("awesome_dashboard.dashboard", AwesomeDashboardLoader);
\ No newline at end of file
diff --git a/awesome_owl/static/src/card/card.js b/awesome_owl/static/src/card/card.js
new file mode 100644
index 00000000000..960a95c25b5
--- /dev/null
+++ b/awesome_owl/static/src/card/card.js
@@ -0,0 +1,19 @@
+import { Component, useState } from "@odoo/owl";
+
+export class Card extends Component {
+ static template = "awesome_owl.card";
+
+ static props = {
+ title: { type: String },
+ slots: Object,
+ };
+
+ setup() {
+ this.show = useState({ value: true });
+ }
+
+ toggleShow() {
+ this.show.value = !this.show.value;
+ }
+}
+
diff --git a/awesome_owl/static/src/card/card.xml b/awesome_owl/static/src/card/card.xml
new file mode 100644
index 00000000000..722a3820758
--- /dev/null
+++ b/awesome_owl/static/src/card/card.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/awesome_owl/static/src/counter/counter.js b/awesome_owl/static/src/counter/counter.js
new file mode 100644
index 00000000000..0013713c708
--- /dev/null
+++ b/awesome_owl/static/src/counter/counter.js
@@ -0,0 +1,27 @@
+/** @odoo-module **/
+
+import { Component, useState, onMounted } 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: 1 });
+
+ onMounted(() => {
+ if (this.props.onChange != null) {
+ this.props.onChange();
+ }
+ })
+ }
+
+ increment() {
+ this.state.value += 1;
+ if (this.props.onChange != null) {
+ 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..f7e7de4461e
--- /dev/null
+++ b/awesome_owl/static/src/counter/counter.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+ Counter:
+
+
+
+
+
+
diff --git a/awesome_owl/static/src/playground.js b/awesome_owl/static/src/playground.js
index 657fb8b07bb..499c33a8f7d 100644
--- a/awesome_owl/static/src/playground.js
+++ b/awesome_owl/static/src/playground.js
@@ -1,7 +1,26 @@
/** @odoo-module **/
-import { Component } from "@odoo/owl";
+import { Component, useState, markup } from "@odoo/owl";
+import { Counter } from "./counter/counter.js";
+import { Card } from "./card/card.js"
+import { TodoList } from "./todo/todo_list.js";
export class Playground extends Component {
static template = "awesome_owl.playground";
+
+ static components = { Counter, Card, TodoList };
+
+ setup() {
+ this.state = {
+ title: "My title",
+ safeHtml: markup("
This is HTML content
"),
+ unsafeHtml: "This will be escaped
",
+ }
+ this.sum = useState({ value: 0 });
+ 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 4fb905d59f9..071e47f0d38 100644
--- a/awesome_owl/static/src/playground.xml
+++ b/awesome_owl/static/src/playground.xml
@@ -3,7 +3,26 @@
- hello world
+
+
+
+
+ The sum is:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
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..61b0e0effdb
--- /dev/null
+++ b/awesome_owl/static/src/todo/todo_item.js
@@ -0,0 +1,19 @@
+import { Component, useState } from "@odoo/owl";
+
+export class TodoItem extends Component {
+ static template = "awesome_owl.todo_item"
+
+ static props = {
+ id: { type: Number },
+ description: { type: String, optional: true },
+ isCompleted: { type: Boolean, optional: true },
+ toggleState: { type: Function, optional: true },
+ removeTodo: { type: Function, optional: true }
+ }
+
+ setup() {
+ //this.id = this.props.id;
+ //this.description = useState({ value: this.props.description != null ? this.props.description : 'No description' });
+ //this.isCompleted = useState({ value: this.props.isCompleted != null ? this.props.isCompleted : false });
+ }
+}
\ No newline at end of file
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..6d175c3ade2
--- /dev/null
+++ b/awesome_owl/static/src/todo/todo_item.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
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..f4f82784f50
--- /dev/null
+++ b/awesome_owl/static/src/todo/todo_list.js
@@ -0,0 +1,48 @@
+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.cpt = 1;
+ this.newTaskInputRef = useRef("newTaskInput");
+
+ onMounted(() => {
+ this.newTaskInputRef.el.focus();
+ })
+ }
+
+ addTodo(ev) {
+ if (ev.target.value.length > 0 & ev.keyCode === 13) {
+ this.todos.push({
+ id: this.cpt,
+ description: ev.target.value,
+ isCompleted: false,
+ toggleState: (id) => { this.toggleState(id); },
+ removeTodo: (id) => { this.removeTodo(id); }
+ });
+ ev.target.value = "";
+ this.cpt += 1;
+ }
+ }
+
+ toggleState(todoId) {
+ let currentTodo = this.todos.find((todoItem) => todoItem.id == todoId);
+ if (currentTodo) {
+ currentTodo.isCompleted = !currentTodo.isCompleted;
+ }
+ }
+
+ removeTodo(todoId) {
+ let todoToRemoveIndex = this.todos.findIndex((todoItem) => todoItem.id == todoId);
+ if (todoToRemoveIndex >= 0) {
+ this.todos.splice(todoToRemoveIndex, 1);
+ }
+ }
+
+}
\ No newline at end of file
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..1fd6b0bebb5
--- /dev/null
+++ b/awesome_owl/static/src/todo/todo_list.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+