Skip to content

Commit ec968fb

Browse files
committed
[ADD] awesome dashboard: implemented dashboard using owl js framework
get data from API using rpc and set in reactive varibale. created generic components and render loop via reactive array . implemented pie chart to categorized data. created setting for dashboard to view only selected cards.
1 parent 2f6fde4 commit ec968fb

14 files changed

+324
-19
lines changed

awesome_dashboard/__manifest__.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,10 @@
2323
],
2424
'assets': {
2525
'web.assets_backend': [
26-
'awesome_dashboard/static/src/**/*',
26+
'awesome_dashboard/static/src/lazy_load_wrapper.js',
27+
],
28+
'awesome_dashboard.dashboard': [
29+
'awesome_dashboard/static/src/dashboard/**/*',
2730
],
2831
},
2932
'license': 'AGPL-3'

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: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/** @odoo-module **/
2+
3+
import { Component, useState } from "@odoo/owl";
4+
import { useService } from "@web/core/utils/hooks";
5+
import { Layout } from "@web/search/layout";
6+
import { DashboardItem } from "./dashboard_item";
7+
import { Piechart } from "./piechart";
8+
import { registry } from "@web/core/registry";
9+
import { DBModal } from "./dashboard_setting_modal";
10+
11+
export class AwesomeDashboard extends Component {
12+
static template = "awesome_dashboard.AwesomeDashboard";
13+
14+
static components = { Layout, DashboardItem, Piechart };
15+
16+
setup() {
17+
this.action = useService("action");
18+
this.statisticService = useService("load_statistics");
19+
this.data = useState(this.statisticService);
20+
this.dialog = useService("dialog");
21+
}
22+
23+
openMyModal() {
24+
this.dialog.add(DBModal, {
25+
items: this.data.stats,
26+
chart: this.data.chartData,
27+
});
28+
}
29+
30+
viewCustomers() {
31+
this.action.doAction("base.action_partner_form");
32+
}
33+
34+
viewLeads() {
35+
this.action.doAction({
36+
type: "ir.actions.act_window",
37+
target: "current",
38+
res_model: "crm.lead",
39+
views: [
40+
[false, "form"],
41+
[false, "list"],
42+
],
43+
});
44+
}
45+
}
46+
47+
registry.category("lazy_components").add("AwesomeDashboard", AwesomeDashboard);
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
.o_dashboard {
2+
background-color: #111827;
3+
.db-item-title {
4+
font-size:18px;
5+
}
6+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?xml version="1.0" encoding="UTF-8" ?>
2+
<templates xml:space="preserve">
3+
4+
<t t-name="awesome_dashboard.AwesomeDashboard">
5+
<Layout className="'o_dashboard h-100'" display="{ controlPanel: {} }">
6+
<t t-set-slot="control-panel-create-button">
7+
<button class="btn btn-primary" t-on-click="viewCustomers">Customers</button>
8+
<button class="btn btn-primary ms-2" t-on-click="viewLeads">Leads</button>
9+
<span class="fa fa-gear cursor-pointer mt-2 mx-2" t-on-click="openMyModal"/>
10+
</t>
11+
<div class="flex-wrap d-flex gap-3 p-3">
12+
<t t-if="this.data.stats.length > 0">
13+
<t t-foreach="this.data.stats" t-as="stat" t-key="stat.title">
14+
<t t-if="stat.isVisible">
15+
<DashboardItem title="stat.title" value="stat.value" size="stat.size"/>
16+
</t>
17+
</t>
18+
<t t-if="this.data.chartData.isVisible">
19+
<Piechart chartData="this.data.chartData"/>
20+
</t>
21+
</t>
22+
</div>
23+
</Layout>
24+
</t>
25+
26+
</templates>
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 DashboardItem extends Component {
6+
static template = "awesome_dashboard.dashboard_item";
7+
8+
static components = {};
9+
10+
static props = ["size", "title", "value"];
11+
12+
static defaultProps = {
13+
size: 1,
14+
};
15+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<?xml version="1.0" encoding="UTF-8" ?>
2+
<templates xml:space="preserve">
3+
4+
<t t-name="awesome_dashboard.dashboard_item">
5+
<div t-att-style="`width:${18*props.size}rem`" class="bg-light p-3 rounded">
6+
<p t-out="props.title" class="db-item-title"/>
7+
<h1 t-out="props.value" class="text-success"/>
8+
</div>
9+
</t>
10+
11+
</templates>
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import { Component } from "@odoo/owl";
2+
import { Dialog } from "@web/core/dialog/dialog";
3+
4+
export class DBModal extends Component {
5+
static template = "awesome_dashboard.db_modal";
6+
static components = { Dialog };
7+
8+
static props = ['items','chart']
9+
10+
setup() {
11+
this.items = this.props.items;
12+
this.chart = this.props.chart;
13+
this.visibleList = this.items.reduce((acc, crr) => {
14+
if (crr?.isVisible) {
15+
acc?.push(crr?.id);
16+
}
17+
return acc;
18+
}, []);
19+
20+
if (this.chart.isVisible) {
21+
this.visibleList.push("chart");
22+
}
23+
}
24+
25+
handleItemToggle = (_, id) => {
26+
if (this.visibleList.includes(id)) {
27+
this.visibleList = this.visibleList.filter((i) => i !== id);
28+
} else {
29+
this.visibleList.push(id);
30+
}
31+
};
32+
33+
handleApplySetting() {
34+
this.items.forEach((item) => {
35+
item.isVisible = this.visibleList.includes(item?.id);
36+
});
37+
38+
this.chart.isVisible = this.visibleList.includes("chart");
39+
40+
localStorage.setItem(
41+
"dashboardItemVisibility",
42+
JSON.stringify(this.visibleList)
43+
);
44+
this.props.close();
45+
}
46+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?xml version="1.0" encoding="UTF-8" ?>
2+
<templates xml:space="preserve">
3+
<t t-name="awesome_dashboard.db_modal">
4+
<Dialog title="'Dashboard items configuration'">
5+
<h4>Which cards do you wish to see ?</h4>
6+
<t t-foreach="this.items" t-as="item" t-key="item.title">
7+
<div class="d-flex align-items-center mb-1">
8+
<input type="checkbox" t-on-change="(e)=>handleItemToggle(e,item.id)" t-att-checked="item.isVisible"/>
9+
<p class="mb-0 ms-2" t-out="item.title"/>
10+
</div>
11+
</t>
12+
<div class="d-flex align-items-center mb-1">
13+
<input type="checkbox" t-on-change="(e)=>handleItemToggle(e,'chart')" t-att-checked="this.chart.isVisible"/>
14+
<p class="mb-0 ms-2">T-shirts Sales by size</p>
15+
</div>
16+
<t t-set-slot="footer">
17+
<button class="btn btn-primary" t-on-click="handleApplySetting">Apply</button>
18+
</t>
19+
</Dialog>
20+
</t>
21+
</templates>
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import { Component, onWillStart, useRef, useEffect } from "@odoo/owl";
2+
import { loadJS } from "@web/core/assets";
3+
4+
export class Piechart extends Component {
5+
static template = "awesome_dashboard.piechart";
6+
7+
static components = {};
8+
9+
static props = ["chartData"];
10+
11+
setup() {
12+
this.canvasRef = useRef("canvas");
13+
this.chart = null;
14+
this.chartData = this.props.chartData;
15+
onWillStart(() => loadJS(["/web/static/lib/Chart/Chart.js"]));
16+
useEffect(
17+
() => this.renderChart(),
18+
() => [this.chartData]
19+
);
20+
}
21+
22+
renderChart() {
23+
if (this.chart) {
24+
this.chart.destroy();
25+
}
26+
this.chart = new Chart(this.canvasRef.el, {
27+
data: this.chartData,
28+
type: "pie",
29+
options: {
30+
responsive: true,
31+
maintainAspectRatio: false,
32+
plugins: {
33+
legend: {
34+
position: "bottom",
35+
labels: {
36+
padding: 20,
37+
usePointStyle: true,
38+
pointStyle: "rect",
39+
font: {
40+
size: 16,
41+
weight: "bold",
42+
color: "#fff",
43+
},
44+
},
45+
},
46+
title: {
47+
display: true,
48+
text: "T-Shirt Sales by Size",
49+
font: {
50+
size: 16,
51+
weight: "bold",
52+
},
53+
padding: 0,
54+
},
55+
},
56+
},
57+
});
58+
}
59+
}
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+
4+
<t t-name="awesome_dashboard.piechart">
5+
<div class="bg-light p-3 rounded">
6+
<canvas t-ref="canvas"/>
7+
</div>
8+
</t>
9+
10+
</templates>
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import { registry } from "@web/core/registry";
2+
import { rpc } from "@web/core/network/rpc";
3+
// import { memoize } from "@web/core/utils/functions";
4+
import { reactive } from "@odoo/owl";
5+
6+
const statsMap = {
7+
average_quantity: { id: 1, title: "Average quanitity order" },
8+
average_time: { id: 2, title: "Average time for order from new to sent" },
9+
nb_cancelled_orders: { id: 3, title: "Number of cancelled order this month" },
10+
nb_new_orders: { id: 4, title: "Number of new orders this month" },
11+
total_amount: { id: 5, title: "Total amount of new orders" },
12+
};
13+
14+
const statisticService = {
15+
start() {
16+
let stats = reactive([]);
17+
let chartData = reactive({});
18+
const loadStatistics = async () => {
19+
const result = await rpc("/awesome_dashboard/statistics");
20+
const dbItemVisibility = localStorage.getItem("dashboardItemVisibility");
21+
let formatedres = Object.entries(result).reduce((prev, [key, value]) => {
22+
const item = statsMap[key];
23+
24+
if (item) {
25+
prev.push({
26+
id: item?.id,
27+
title: item?.title,
28+
size: item?.title?.length > 30 ? 2 : 1,
29+
value,
30+
isVisible: dbItemVisibility
31+
? dbItemVisibility.includes(item?.id)
32+
: true,
33+
});
34+
} else if (typeof value === "object") {
35+
chartData.labels = Object.keys(value);
36+
chartData.datasets = [
37+
{
38+
label: "Order by size",
39+
data: Object.values(value),
40+
},
41+
];
42+
chartData.isVisible = dbItemVisibility
43+
? dbItemVisibility.includes("chart")
44+
: true;
45+
}
46+
return prev;
47+
}, []);
48+
49+
stats?.push(...formatedres);
50+
51+
return { stats, chartData };
52+
};
53+
54+
loadStatistics();
55+
// setInterval(loadStatistics,10000);
56+
return {
57+
stats,
58+
chartData,
59+
};
60+
},
61+
};
62+
63+
registry.category("services").add("load_statistics", statisticService);
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
/** @odoo-module **/
2+
3+
import { registry } from "@web/core/registry";
4+
import { LazyComponent } from "@web/core/assets";
5+
import { xml, Component } from "@odoo/owl";
6+
7+
class DashoboardLazyLoader extends Component {
8+
static components = { LazyComponent };
9+
static template = xml`
10+
<LazyComponent bundle="'awesome_dashboard.dashboard'" Component="'AwesomeDashboard'" />
11+
`;
12+
}
13+
14+
registry
15+
.category("actions")
16+
.add("awesome_dashboard.dashboard", DashoboardLazyLoader);

0 commit comments

Comments
 (0)