Skip to content

Commit a1ca72d

Browse files
committed
[IMP] awesome_dashboard: Build dynamic, generic dashboard with OWL
Purpose: Create a reusable and extensible dashboard using OWL framework features. Approach: Added layout, navigation buttons, and dashboard items Integrated server calls, caching, and pie chart display Enabled real-time updates and lazy loading Made dashboard generic, extensible, and interactive Impact: Delivers a flexible dashboard with live data and modular components.
1 parent aa0188c commit a1ca72d

File tree

19 files changed

+393
-40
lines changed

19 files changed

+393
-40
lines changed

awesome_dashboard/__manifest__.py

Lines changed: 18 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,28 @@
11
# -*- coding: utf-8 -*-
22
{
3-
'name': "Awesome Dashboard",
4-
5-
'summary': """
3+
"name": "Awesome Dashboard",
4+
"summary": """
65
Starting module for "Discover the JS framework, chapter 2: Build a dashboard"
76
""",
8-
9-
'description': """
7+
"description": """
108
Starting module for "Discover the JS framework, chapter 2: Build a dashboard"
119
""",
12-
13-
'author': "Odoo",
14-
'website': "https://www.odoo.com/",
15-
'category': 'Tutorials/AwesomeDashboard',
16-
'version': '0.1',
17-
'application': True,
18-
'installable': True,
19-
'depends': ['base', 'web', 'mail', 'crm'],
20-
21-
'data': [
22-
'views/views.xml',
10+
"author": "Odoo",
11+
"website": "https://www.odoo.com/",
12+
"category": "Tutorials/AwesomeDashboard",
13+
"version": "0.1",
14+
"application": True,
15+
"installable": True,
16+
"depends": ["base", "web", "mail", "crm"],
17+
"data": [
18+
"views/views.xml",
2319
],
24-
'assets': {
25-
'web.assets_backend': [
26-
'awesome_dashboard/static/src/**/*',
20+
"assets": {
21+
"web.assets_backend": [
22+
"awesome_dashboard/static/src/**/*",
23+
("remove", "awesome_dashboard/static/src/dashboard/**/*"),
2724
],
25+
"awesome_dashboard.dashboard": ["awesome_dashboard/static/src/dashboard/**/*"],
2826
},
29-
'license': 'AGPL-3'
27+
"license": "AGPL-3",
3028
}

awesome_dashboard/static/src/dashboard.js

Lines changed: 0 additions & 10 deletions
This file was deleted.

awesome_dashboard/static/src/dashboard.xml

Lines changed: 0 additions & 8 deletions
This file was deleted.
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
/** @odoo-module **/
2+
3+
import { Component, useState } from "@odoo/owl";
4+
import { registry } from "@web/core/registry";
5+
import { Layout } from "@web/search/layout";
6+
import { useService } from "@web/core/utils/hooks";
7+
import { DashboardItem } from "./dashboard_item/dashboard_item";
8+
import { Dialog } from "@web/core/dialog/dialog";
9+
import { CheckBox } from "@web/core/checkbox/checkbox";
10+
import { browser } from "@web/core/browser/browser";
11+
12+
class AwesomeDashboard extends Component {
13+
static template = "awesome_dashboard.AwesomeDashboard";
14+
static components = { Layout, DashboardItem };
15+
16+
setup() {
17+
this.action = useService("action");
18+
this.statistics = useState(useService("awesome_dashboard.statistics"));
19+
this.dialog = useService("dialog");
20+
this.display = {
21+
controlPanel: {},
22+
};
23+
// this.items = DashboardItems;
24+
this.items = registry.category("awesome_dashboard").getAll();
25+
this.state = useState({
26+
disabledItems: browser.localStorage.getItem("disabledDashboardItems")?.split(",") || []
27+
});
28+
}
29+
openConfiguration() {
30+
this.dialog.add(ConfigurationDialog, {
31+
items: this.items,
32+
disabledItems: this.state.disabledItems,
33+
onUpdateConfiguration: this.updateConfiguration.bind(this),
34+
})
35+
36+
}
37+
updateConfiguration(newDisabledItems) {
38+
this.state.disabledItems = newDisabledItems;
39+
}
40+
openCustomers() {
41+
this.action.doAction("base.action_partner_form");
42+
}
43+
openLeads() {
44+
this.action.doAction({
45+
type: "ir.actions.act_window",
46+
name: "All leads",
47+
res_model: "crm.lead",
48+
views: [
49+
[false, "list"],
50+
[false, "form"],
51+
],
52+
});
53+
}
54+
}
55+
class ConfigurationDialog extends Component {
56+
static template = "awesome_dashboard.ConfigurationDialog";
57+
static components = { Dialog, CheckBox };
58+
static props = ["close", "items", "disabledItems", "onUpdateConfiguration"];
59+
60+
setup() {
61+
this.items = useState(this.props.items.map((item) => {
62+
return {
63+
...item,
64+
enabled: !this.props.disabledItems.includes(item.id),
65+
}
66+
}));
67+
}
68+
69+
done() {
70+
this.props.close();
71+
}
72+
73+
onChange(checked, changedItem) {
74+
changedItem.enabled = checked;
75+
const newDisabledItems = Object.values(this.items).filter(
76+
(item) => !item.enabled
77+
).map((item) => item.id)
78+
79+
browser.localStorage.setItem(
80+
"disabledDashboardItems",
81+
newDisabledItems,
82+
);
83+
84+
this.props.onUpdateConfiguration(newDisabledItems);
85+
}
86+
87+
}
88+
registry.category("lazy_components").add("AwesomeDashboard", AwesomeDashboard);
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
.o_dashboard {
2+
background-color: #2c0f36;
3+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<templates xml:space="preserve">
3+
4+
<t t-name="awesome_dashboard.AwesomeDashboard">
5+
<Layout display="display" className="'o_dashboard h-100'">
6+
<t t-set-slot="layout-buttons">
7+
<button class="btn btn-primary" t-on-click="openCustomers">Customers</button>
8+
<button class="btn btn-primary" t-on-click="openLeads">Leads</button>
9+
</t>
10+
<t t-set-slot="control-panel-additional-actions">
11+
<button t-on-click="openConfiguration" class="btn p-0 ms-1 border-0">
12+
<i class="fa fa-cog"></i>
13+
</button>
14+
</t>
15+
<div class="d-flex flex-wrap" t-if="statistics.isReady">
16+
<t t-foreach="items" t-as="item" t-key="item.id">
17+
<DashboardItem t-if="!state.disabledItems.includes(item.id)" size="item.size || 1">
18+
<t t-set="itemProp" t-value="item.props ? item.props(statistics) : {'data': statistics}"/>
19+
<t t-component="item.Component" t-props="itemProp" />
20+
</DashboardItem>
21+
</t>
22+
</div>
23+
</Layout>
24+
</t>
25+
<t t-name="awesome_dashboard.ConfigurationDialog">
26+
<Dialog title="'Dashboard items configuration'">
27+
Which cards do you whish to see ?
28+
<t t-foreach="items" t-as="item" t-key="item.id">
29+
<CheckBox value="item.enabled" onChange="(ev) => this.onChange(ev, item)">
30+
<t t-esc="item.description"/>
31+
</CheckBox>
32+
</t>
33+
<t t-set-slot="footer">
34+
<button class="btn btn-primary" t-on-click="done">
35+
Done
36+
</button>
37+
</t>
38+
</Dialog>
39+
</t>
40+
</templates>
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/** @odoo-module **/
2+
3+
import { Component } from "@odoo/owl";
4+
5+
export class DashboardItem extends Component {
6+
static template = "awesome_dashboard.DashboardItem";
7+
8+
static props = {
9+
slots: {
10+
type: Object,
11+
shape: {
12+
default: Object
13+
},
14+
},
15+
size: {
16+
type: Number,
17+
default: 1,
18+
optional: true,
19+
},
20+
};
21+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<templates xml:space="preserve">
3+
<t t-name="awesome_dashboard.DashboardItem">
4+
<div class="card m-2 border-dark" t-attf-style="width: {{18*props.size}}rem;">
5+
<div class="card-body">
6+
<t t-slot="default"/>
7+
</div>
8+
</div>
9+
</t>
10+
</templates>
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/** @odoo-module */
2+
3+
import { NumberCard } from "./number_card/number_card";
4+
import { PieChartCard } from "./pie_chart_card/pie_chart_card";
5+
import { registry } from "@web/core/registry";
6+
7+
const DashboardItems = [
8+
{
9+
id: "average_quantity",
10+
description: "Average amount of t-shirt",
11+
Component: NumberCard,
12+
props: (data) => ({
13+
title: "Average amount of t-shirt by order this month",
14+
value: data.average_quantity,
15+
})
16+
},
17+
{
18+
id: "average_time",
19+
description: "Average time for an order",
20+
Component: NumberCard,
21+
props: (data) => ({
22+
title: "Average time for an order to go from 'new' to 'sent' or 'cancelled'",
23+
value: data.average_time,
24+
})
25+
},
26+
{
27+
id: "number_new_orders",
28+
description: "New orders this month",
29+
Component: NumberCard,
30+
props: (data) => ({
31+
title: "Number of new orders this month",
32+
value: data.nb_new_orders,
33+
})
34+
},
35+
{
36+
id: "cancelled_orders",
37+
description: "Cancelled orders this month",
38+
Component: NumberCard,
39+
props: (data) => ({
40+
title: "Number of cancelled orders this month",
41+
value: data.nb_cancelled_orders,
42+
})
43+
},
44+
{
45+
id: "amount_new_orders",
46+
description: "amount orders this month",
47+
Component: NumberCard,
48+
props: (data) => ({
49+
title: "Total amount of new orders this month",
50+
value: data.total_amount,
51+
})
52+
},
53+
{
54+
id: "pie_chart",
55+
description: "Shirt orders by size",
56+
Component: PieChartCard,
57+
size: 2,
58+
props: (data) => ({
59+
title: "Shirt orders by size",
60+
values: data.orders_by_size,
61+
})
62+
}
63+
]
64+
DashboardItems.forEach(item => {
65+
registry.category("awesome_dashboard").add(item.id, item);
66+
});
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/** @odoo-module **/
2+
3+
import { Component } from "@odoo/owl";
4+
5+
export class NumberCard extends Component {
6+
static template = "awesome_dashboard.NumberCard";
7+
static props = {
8+
title: {
9+
type: String,
10+
},
11+
value: {
12+
type: Number,
13+
}
14+
}
15+
}

0 commit comments

Comments
 (0)