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..9fef9e5022c --- /dev/null +++ b/awesome_dashboard/static/src/components/dashboard_item/dashboard_item.xml @@ -0,0 +1,20 @@ + + + + +
+
+
+
+
+
+ +

+ +

+
+
+
+
+ +
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 c4fb245621b..fab97c06b4a 100644 --- a/awesome_dashboard/static/src/dashboard.js +++ b/awesome_dashboard/static/src/dashboard.js @@ -1,8 +1,43 @@ -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"; +import { PieChart } from "./components/pie_chart/pie_chart"; class AwesomeDashboard extends Component { static template = "awesome_dashboard.AwesomeDashboard"; + static components = { Layout, DashboardItem, PieChart }; + + setup() { + this.action = useService("action"); + const dashboardService = useService("awesome_dashboard.statistics"); + + onWillStart(async () => { + this.statistics = await dashboardService.loadStatistics() + }); + } + + 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.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..ab57f9ff1de 100644 --- a/awesome_dashboard/static/src/dashboard.xml +++ b/awesome_dashboard/static/src/dashboard.xml @@ -2,7 +2,40 @@ - hello dashboard + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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); 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..85fefd4ccf9 --- /dev/null +++ b/awesome_owl/static/src/components/card/card.js @@ -0,0 +1,14 @@ +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 new file mode 100644 index 00000000000..976144ac7db --- /dev/null +++ b/awesome_owl/static/src/components/card/card.xml @@ -0,0 +1,18 @@ + + + + +
+
+
+
+ +
+
+ +
+
+
+
+ +
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..e080fdff05d --- /dev/null +++ b/awesome_owl/static/src/components/counter/counter.js @@ -0,0 +1,15 @@ +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/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/components/todo_list/todo_item.js b/awesome_owl/static/src/components/todo_list/todo_item.js new file mode 100644 index 00000000000..14f6d1b4fd3 --- /dev/null +++ b/awesome_owl/static/src/components/todo_list/todo_item.js @@ -0,0 +1,23 @@ +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 + } + }, + 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 new file mode 100644 index 00000000000..e5e5596b579 --- /dev/null +++ b/awesome_owl/static/src/components/todo_list/todo_item.xml @@ -0,0 +1,18 @@ + + + + +
+ + . +

+ +
+
+ +
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..9565d3417c6 --- /dev/null +++ b/awesome_owl/static/src/components/todo_list/todo_list.js @@ -0,0 +1,38 @@ +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"; + static components = { TodoItem }; + + setup() { + this.todos = useState([]); + this.inputAutofocusRef = useAutoFocus("input") + + this.toggleState = this.toggleState.bind(this) + this.deleteElement = this.deleteElement.bind(this) + } + + 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 = "" + } + } + + toggleState(refId) { + 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 new file mode 100644 index 00000000000..0dad37feaff --- /dev/null +++ b/awesome_owl/static/src/components/todo_list/todo_list.xml @@ -0,0 +1,13 @@ + + + + +
+ + + + +
+
+ +
diff --git a/awesome_owl/static/src/playground.js b/awesome_owl/static/src/playground.js index 4ac769b0aa5..f0eedc105c9 100644 --- a/awesome_owl/static/src/playground.js +++ b/awesome_owl/static/src/playground.js @@ -1,5 +1,23 @@ -import { Component } from "@odoo/owl"; +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, TodoList }; + + sum = useState({ value: 0 }) + + card1Content = "
some content
" + card2Content = markup("
some content
") + + 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 4fb905d59f9..1fb638fb529 100644 --- a/awesome_owl/static/src/playground.xml +++ b/awesome_owl/static/src/playground.xml @@ -3,7 +3,30 @@
- hello world +
+ hello world +
+
+ + +
+
+

The sum is:

+
+
+ + + + + + + + + +
+
+ +
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; +}