Skip to content

Commit 092308e

Browse files
committed
[Add] awesome_dashboard: done second course.
1 parent 6834ecc commit 092308e

19 files changed

+358
-20
lines changed

awesome_dashboard/__manifest__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,11 @@
2424
'assets': {
2525
'web.assets_backend': [
2626
'awesome_dashboard/static/src/**/*',
27+
('remove', 'awesome_dashboard/static/src/dashboard/**/*')
2728
],
29+
'awesome_dashboard.dashboard': [
30+
'awesome_dashboard/static/src/dashboard/**/*',
31+
]
2832
},
2933
'license': 'AGPL-3'
3034
}

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: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
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";
8+
import { items } from "./dashboard_items";
9+
import { NumberCard } from "./number_card";
10+
import { PieChartCard } from "./pie_chart_card";
11+
import { Dialog } from "@web/core/dialog/dialog";
12+
import { CheckBox } from "@web/core/checkbox/checkbox";
13+
import { browser } from "@web/core/browser/browser";
14+
15+
16+
17+
class AwesomeDashboard extends Component {
18+
static components = {DashboardItem, NumberCard, PieChartCard, Layout}
19+
static template = "awesome_dashboard.AwesomeDashboard";
20+
21+
setup() {
22+
this.state = useState({
23+
disabledItems: browser.localStorage.getItem("disabledDashboardItems")?.split(",") || []
24+
});
25+
this.action = useService("action");
26+
this.statisticsService = useService("awesome_dashboard.statistics");
27+
this.dialog = useService("dialog");
28+
this.statistics = useState(this.statisticsService.statistics);
29+
this.items = registry.category("awesome_dashboard").getAll();
30+
}
31+
32+
openConfiguration() {
33+
this.dialog.add(ConfigurationDialog, {
34+
items: this.items,
35+
disabledItems: this.state.disabledItems,
36+
onUpdateConfiguration: this.updateConfiguration.bind(this),
37+
})
38+
}
39+
40+
updateConfiguration(disabledItems) {
41+
this.state.disabledItems = disabledItems;
42+
}
43+
44+
openCustomers() {
45+
this.action.doAction("base.action_partner_form");
46+
}
47+
48+
openLeads() {
49+
this.action.doAction({
50+
type: "ir.actions.act_window",
51+
name: "Leads",
52+
res_model: "crm.lead",
53+
views: [[false, "list"], [false, "form"]],
54+
target: "current",
55+
});
56+
}
57+
}
58+
59+
60+
class ConfigurationDialog extends Component {
61+
static components = { Dialog, CheckBox };
62+
static template = "awesome_dashboard.ConfigurationDialog";
63+
static props = ["close", "items", "disabledItems", "onUpdateConfiguration"];
64+
65+
setup() {
66+
this.items = useState(this.props.items.map((item) => {
67+
return {
68+
...item,
69+
enabled: !this.props.disabledItems.includes(item.id),
70+
}
71+
}));
72+
}
73+
74+
done() {
75+
this.props.close();
76+
}
77+
78+
onChange(checked, changedItem) {
79+
changedItem.enabled = checked;
80+
const newDisabledItems = Object.values(this.items).filter(
81+
(item) => !item.enabled
82+
).map((item) => item.id)
83+
84+
browser.localStorage.setItem(
85+
"disabledDashboardItems",
86+
newDisabledItems,
87+
);
88+
89+
this.props.onUpdateConfiguration(newDisabledItems);
90+
}
91+
}
92+
93+
94+
registry.category("lazy_components").add("awesome_dashboard.dashboard", 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: gray;
3+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<?xml version="1.0" encoding="UTF-8" ?>
2+
<templates xml:space="preserve">
3+
4+
<t t-name="awesome_dashboard.AwesomeDashboard">
5+
<div class="mb-4 d-flex gap-1">
6+
<button class="btn btn-primary" t-on-click="openCustomers">Customers</button>
7+
<button class="btn btn-outline-secondary" t-on-click="openLeads">Leads</button>
8+
<t t-set-slot="control-panel-additional-actions">
9+
<button t-on-click="openConfiguration" class="btn p-0 ms-1 border-0">
10+
<i class="fa fa-cog"></i>
11+
</button>
12+
</t>
13+
</div>
14+
<Layout className="'o_dashboard h-100'" display="{controlPanel: {} }">
15+
<div class="dashboard grid">
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="itemProps" t-value="item.props ? item.props(statistics) : {'data': statistics}" />
19+
<t t-component="item.Component" t-props="itemProps" />
20+
</DashboardItem>
21+
</t>
22+
</div>
23+
</Layout>
24+
</t>
25+
26+
<t t-name="awesome_dashboard.ConfigurationDialog">
27+
<Dialog title="'Dashboard items configuration'">
28+
Which cards do you whish to see ?
29+
<t t-foreach="items" t-as="item" t-key="item.id">
30+
<CheckBox value="item.enabled" onChange="(ev) => this.onChange(ev, item)">
31+
<t t-esc="item.description"/>
32+
</CheckBox>
33+
</t>
34+
<t t-set-slot="footer">
35+
<button class="btn btn-primary" t-on-click="done">
36+
Done
37+
</button>
38+
</t>
39+
</Dialog>
40+
</t>
41+
42+
</templates>
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
/** @odoo-module **/
2+
3+
import { Component } from "@odoo/owl";
4+
5+
export class DashboardItem extends Component {
6+
static props = {size: {type: Number, default: 1}, slots: {type: Object}}
7+
static template = "awesome_dashboard.dashboard_item";
8+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<?xml version="1.0" encoding="UTF-8" ?>
2+
<templates xml:space="preserve">
3+
4+
<t t-name="awesome_dashboard.dashboard_item">
5+
<div class="card d-inline-block m-2" t-attf-style="width: ${18 * this.props.size}rem;">
6+
<div class="card-body">
7+
<t t-slot="default"/>
8+
</div>
9+
</div>
10+
</t>
11+
12+
</templates>
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/** @odoo-module **/
2+
3+
import { NumberCard } from "./number_card";
4+
import { PieChartCard } from "./pie_chart_card";
5+
import { registry } from "@web/core/registry";
6+
7+
const items = [
8+
{
9+
id: "cancelled_orders",
10+
description: "Cancelled orders this month",
11+
Component: NumberCard,
12+
props: (data) => ({
13+
title: "Number of cancelled orders this month",
14+
value: data.nb_cancelled_orders,
15+
})
16+
},
17+
{
18+
id: "amount_new_orders",
19+
description: "amount orders this month",
20+
Component: NumberCard,
21+
props: (data) => ({
22+
title: "Total amount of new orders this month",
23+
value: data.total_amount,
24+
})
25+
},
26+
{
27+
id: "pie_chart",
28+
description: "Shirt orders by size",
29+
Component: PieChartCard,
30+
size: 2,
31+
props: (data) => ({
32+
title: "Shirt orders by size",
33+
values: data.orders_by_size,
34+
})
35+
},
36+
];
37+
38+
39+
items.forEach(item => {
40+
registry.category("awesome_dashboard").add(item.id, item);
41+
});
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
/** @odoo-module **/
2+
3+
import { Component } from "@odoo/owl";
4+
5+
6+
export class NumberCard extends Component {
7+
static template = "awesome_dashboard.number_card";
8+
static props = {
9+
title: {type: String},
10+
value: {type: Number},
11+
};
12+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<?xml version="1.0" encoding="UTF-8" ?>
2+
<templates xml:space="preserve">
3+
4+
<t t-name="awesome_dashboard.number_card">
5+
<label><t t-esc="props.title"/></label>
6+
<h1 style="color: green;"><t t-esc="props.value"/></h1>
7+
</t>
8+
9+
</templates>
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/** @odoo-module **/
2+
3+
import { Component, onWillStart, onMounted, onWillUpdateProps } from "@odoo/owl";
4+
import { loadJS } from "@web/core/assets";
5+
6+
7+
export class PieChart extends Component {
8+
static props = {sizes: {type: Object}}
9+
static template = "awesome_dashboard.pie_chart";
10+
11+
setup() {
12+
onWillStart(async () => {
13+
await loadJS("/web/static/lib/Chart/Chart.js");
14+
});
15+
16+
onWillUpdateProps((props) => {
17+
this.chart.destroy();
18+
this.renderChart(props);
19+
});
20+
21+
onMounted(() => {
22+
this.renderChart(this.props);
23+
});
24+
}
25+
26+
renderChart(props) {
27+
const ctx = document.getElementById('myChart');
28+
const sizeData = props.sizes; // Assumes response like { sizes: { S: 100, M: 80, ... } }
29+
const labels = Object.keys(sizeData);
30+
const data = Object.values(sizeData);
31+
32+
this.chart = new Chart(ctx, {
33+
type: "pie",
34+
data: {
35+
labels: labels,
36+
datasets: [{
37+
data: data,
38+
backgroundColor: [
39+
"#FF6384", "#36A2EB", "#FFCE56", "#4BC0C0", "#9966FF"
40+
],
41+
}],
42+
},
43+
options: {
44+
responsive: true,
45+
plugins: {
46+
legend: {
47+
position: "bottom",
48+
},
49+
title: {
50+
display: true,
51+
text: "T-Shirt Sizes Sold",
52+
},
53+
},
54+
},
55+
});
56+
}
57+
58+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<?xml version="1.0" encoding="UTF-8" ?>
2+
<templates xml:space="preserve">
3+
4+
<t t-name="awesome_dashboard.pie_chart">
5+
<canvas id="myChart"></canvas>
6+
</t>
7+
8+
</templates>
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
/** @odoo-module **/
2+
3+
import { Component } from "@odoo/owl";
4+
import {PieChart} from "./pie_chart";
5+
6+
export class PieChartCard extends Component {
7+
static components = {PieChart}
8+
static template = "awesome_dashboard.pie_chart_card";
9+
static props = {
10+
title: {type: String},
11+
values: {type: Object},
12+
};
13+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<?xml version="1.0" encoding="UTF-8" ?>
2+
<templates xml:space="preserve">
3+
4+
<t t-name="awesome_dashboard.pie_chart_card">
5+
<label><t t-esc="props.title"/></label>
6+
<PieChart sizes="props.values"></PieChart>
7+
</t>
8+
9+
</templates>
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { registry } from "@web/core/registry";
2+
import { memoize } from "@web/core/utils/functions";
3+
import { rpc } from "@web/core/network/rpc";
4+
import { reactive } from "@odoo/owl";
5+
6+
7+
export const statisticsService = {
8+
start() {
9+
const statistics = reactive({});
10+
11+
async function loadStatistics() {
12+
const data = await rpc("/awesome_dashboard/statistics");
13+
Object.assign(statistics, data);
14+
}
15+
16+
loadStatistics();
17+
18+
setInterval(() => {
19+
loadStatistics();
20+
}, 10000);
21+
22+
return {
23+
statistics,
24+
};
25+
26+
},
27+
};
28+
29+
registry.category("services").add("awesome_dashboard.statistics", statisticsService);

0 commit comments

Comments
 (0)