diff --git a/awesome_dashboard/static/src/dashboard.js b/awesome_dashboard/static/src/dashboard.js index c4fb245621b..ab5a1533a72 100644 --- a/awesome_dashboard/static/src/dashboard.js +++ b/awesome_dashboard/static/src/dashboard.js @@ -1,8 +1,40 @@ -import { Component } from "@odoo/owl"; +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 "./dashboardItem"; +import { rpc } from "@web/core/network/rpc"; +import { PieChart } from "./pieChart"; + class AwesomeDashboard extends Component { static template = "awesome_dashboard.AwesomeDashboard"; + static components = { Layout, DashboardItem, PieChart }; + + setup() { + this.action = useService("action"); + this.state = useState(useService("statistics")); + } + + async openCustomers() { + + this.action.doAction({ + type: 'ir.actions.act_window', + target: 'current', + res_model: 'res.partner', + views: [[false, 'kanban']], + }); + } + + async openLeads() { + + this.action.doAction({ + type: 'ir.actions.act_window', + target: 'current', + res_model: 'crm.lead', + views: [[false, 'list'], [false, 'kanban']], + }); + } } 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..721bb636aef --- /dev/null +++ b/awesome_dashboard/static/src/dashboard.scss @@ -0,0 +1,3 @@ +.o_dashboard { + background-color: #f8f9fa; +} diff --git a/awesome_dashboard/static/src/dashboard.xml b/awesome_dashboard/static/src/dashboard.xml index 1a2ac9a2fed..efbcf7ed3a5 100644 --- a/awesome_dashboard/static/src/dashboard.xml +++ b/awesome_dashboard/static/src/dashboard.xml @@ -1,8 +1,63 @@ - + - - hello dashboard + + + + + +
+ + + + Number of new orders this month +

+ +

+
+
+ + +Total amount of new orders this month + +

+ +

+
+
+ + + Average amount of t-shirt by order this month +

+ +

+
+
+ + + Number of cancelled orders this month +

+ +

+
+
+ + + Average time for an order to go from ‘new’ to ‘sent’ or ‘cancelled’ +

+ +

+
+
+ + + + + + + +
+
diff --git a/awesome_dashboard/static/src/dashboardItem.js b/awesome_dashboard/static/src/dashboardItem.js new file mode 100644 index 00000000000..972fa6a9363 --- /dev/null +++ b/awesome_dashboard/static/src/dashboardItem.js @@ -0,0 +1,12 @@ +import { Component} from "@odoo/owl"; + + +export class DashboardItem extends Component { + static template = "awesome_dashboard.DashboardItem"; + static components = { }; + static props = { + size: { type: Number, optional: true, default: 1 }, + slots: { type: Object, optional: true }, + }; +} + diff --git a/awesome_dashboard/static/src/dashboardItem.xml b/awesome_dashboard/static/src/dashboardItem.xml new file mode 100644 index 00000000000..0023f93b8b4 --- /dev/null +++ b/awesome_dashboard/static/src/dashboardItem.xml @@ -0,0 +1,11 @@ + + + +
+
+ +
+
+
+ +
diff --git a/awesome_dashboard/static/src/pieChart.js b/awesome_dashboard/static/src/pieChart.js new file mode 100644 index 00000000000..4939237a388 --- /dev/null +++ b/awesome_dashboard/static/src/pieChart.js @@ -0,0 +1,37 @@ +import { Component, onWillStart, useRef, useEffect } from "@odoo/owl"; +import { loadJS } from "@web/core/assets"; + + +export class PieChart extends Component { + static template = "awesome_dashboard.PieChart"; + + setup() { + onWillStart(() => loadJS("/web/static/lib/Chart/Chart.js")); + this.canvasRef = useRef("canvas"); + const data ={ + "m": 79, + "s": 90, + "xl": 49 + } + useEffect(() => { + + this.chart = new Chart( + this.canvasRef.el, + { + type: 'pie', + data: { + labels: Object.keys(data), + datasets: [{ + data: Object.values(data) + }] + }, + size: { + width: 1000, + height: 1000 + } + } + ) + }); + } + +} diff --git a/awesome_dashboard/static/src/pieChart.xml b/awesome_dashboard/static/src/pieChart.xml new file mode 100644 index 00000000000..a79f23d3b60 --- /dev/null +++ b/awesome_dashboard/static/src/pieChart.xml @@ -0,0 +1,10 @@ + + + +
+ +
+ +
+ +
diff --git a/awesome_dashboard/static/src/statsService.js b/awesome_dashboard/static/src/statsService.js new file mode 100644 index 00000000000..8adb6109448 --- /dev/null +++ b/awesome_dashboard/static/src/statsService.js @@ -0,0 +1,20 @@ +import { registry } from "@web/core/registry"; +import { rpc } from "@web/core/network/rpc"; +import { reactive } from "@odoo/owl"; + +const statistics = reactive({}); + +async function loadStatistics() { + const data = await rpc("/awesome_dashboard/statistics"); + Object.assign(statistics, data); +} + +export const statisticsService = { + start() { + loadStatistics(); + setInterval(loadStatistics, 10000); + return statistics; + }, +}; + +registry.category("services").add("statistics", statisticsService); diff --git a/awesome_owl/static/src/card/card.js b/awesome_owl/static/src/card/card.js new file mode 100644 index 00000000000..d73f13a37e2 --- /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: { type: Object, optional: true }, + }; + + setup() { + this.state = useState({ + isOpen: true, + }); + } + + onToggle() { + this.state.isOpen = !this.state.isOpen; + } +} diff --git a/awesome_owl/static/src/card/card.xml b/awesome_owl/static/src/card/card.xml new file mode 100644 index 00000000000..6e898945b4e --- /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..e53aafbb2bf --- /dev/null +++ b/awesome_owl/static/src/counter/counter.js @@ -0,0 +1,18 @@ +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({ + count: 0, + }); + } + + increment() { + this.props.onChange?.(++this.state.count); + } +} diff --git a/awesome_owl/static/src/counter/counter.xml b/awesome_owl/static/src/counter/counter.xml new file mode 100644 index 00000000000..3e062458945 --- /dev/null +++ b/awesome_owl/static/src/counter/counter.xml @@ -0,0 +1,13 @@ + + + + +
+

+ +

+ +
+
+ +
diff --git a/awesome_owl/static/src/playground.js b/awesome_owl/static/src/playground.js index 4ac769b0aa5..129d8faef04 100644 --- a/awesome_owl/static/src/playground.js +++ b/awesome_owl/static/src/playground.js @@ -1,5 +1,20 @@ -import { Component } from "@odoo/owl"; +import { Component, 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, TodoList }; + + setup() { + this.state = useState({ + sum: 0, + }); + } + + incrementSum(count) { + this.state.sum += 1; + } + } diff --git a/awesome_owl/static/src/playground.xml b/awesome_owl/static/src/playground.xml index 4fb905d59f9..a3b39899e6d 100644 --- a/awesome_owl/static/src/playground.xml +++ b/awesome_owl/static/src/playground.xml @@ -1,9 +1,24 @@ - + -
- hello world +
+ + + + + + + +
+

Sum: +

+
+
+ + + +
diff --git a/awesome_owl/static/src/todo/todoItem.js b/awesome_owl/static/src/todo/todoItem.js new file mode 100644 index 00000000000..afc46c3cab8 --- /dev/null +++ b/awesome_owl/static/src/todo/todoItem.js @@ -0,0 +1,15 @@ +import { Component, markup } from "@odoo/owl"; +import { Counter } from "../counter/counter"; + +export class TodoItem extends Component { + static template = "awesome_owl.todoItem"; + static components = { Counter }; + static props = { + id: { type: Number }, + description: { type: String }, + isCompleted: { type: Boolean}, + toggleState: { type: Function, optional: true }, + removeTodo: { type: Function, optional: true }, + }; + +} diff --git a/awesome_owl/static/src/todo/todoItem.xml b/awesome_owl/static/src/todo/todoItem.xml new file mode 100644 index 00000000000..cadd6b1366f --- /dev/null +++ b/awesome_owl/static/src/todo/todoItem.xml @@ -0,0 +1,16 @@ + + + + +
+ + + +
+
+ +
diff --git a/awesome_owl/static/src/todo/todoList.js b/awesome_owl/static/src/todo/todoList.js new file mode 100644 index 00000000000..547a681e662 --- /dev/null +++ b/awesome_owl/static/src/todo/todoList.js @@ -0,0 +1,36 @@ +import { Component, useState } from "@odoo/owl"; +import { TodoItem } from "./todoItem"; +import { useAutoFocus } from "../utils"; + +export class TodoList extends Component { + static template = "awesome_owl.todoList"; + static components = { TodoItem }; + + setup() { + this.state = useState({todos: []}); + this.idCounter = 1; + this.addTodoRef = useAutoFocus(); + } + + onAddTodo(ev) { + if (ev.key == "Enter" && ev.target.value.trim() !== "") { + const newTodo = { + id: this.idCounter++, + description: ev.target.value, + isCompleted: false, + }; + + this.state.todos.push(newTodo); + ev.target.value = ""; + } + } + + toggleState(todoId) { + const todo = this.state.todos?.find((t) => t.id === todoId); + if (todo) todo.isCompleted = !todo.isCompleted; + } + + removeTodo(todoId) { + this.state.todos = this.state.todos?.filter((t) => t.id !== todoId); + } +} diff --git a/awesome_owl/static/src/todo/todoList.xml b/awesome_owl/static/src/todo/todoList.xml new file mode 100644 index 00000000000..6a37f19dc81 --- /dev/null +++ b/awesome_owl/static/src/todo/todoList.xml @@ -0,0 +1,13 @@ + + + + + +
+
+ +
+
+
+ +
diff --git a/awesome_owl/static/src/utils.js b/awesome_owl/static/src/utils.js new file mode 100644 index 00000000000..001515a3200 --- /dev/null +++ b/awesome_owl/static/src/utils.js @@ -0,0 +1,13 @@ +import { onMounted, useRef } from "@odoo/owl"; + +export function useAutoFocus() { + const ref = useRef("add-todo-input"); + + onMounted(() => { + if (ref.el) { + ref.el.focus(); + } + }); + + return ref; +}