Skip to content
Open
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
6f5a7eb
[IMP] awesome_owl: add Card and Counter components
HashemKhaled Oct 27, 2025
5927c86
[IMP] awesome_owl: add card props validation
HashemKhaled Oct 27, 2025
55f4028
[IMP] awesome_owl: display the sum of two counters
HashemKhaled Oct 27, 2025
20d5c85
[IMP] awesome_owl: add a todo list
HashemKhaled Oct 27, 2025
a2c679a
[FIX] awesome_owl: use dynamic attributes
HashemKhaled Oct 27, 2025
dd51372
[IMP] awesome_owl: add a todo from the UI
HashemKhaled Oct 28, 2025
0d14be0
[IMP] awesome_owl: implement useAutofocus hook
HashemKhaled Oct 28, 2025
b1e3577
[IMP] awesome_owl: implement toggle functionality for todo items
HashemKhaled Oct 28, 2025
32e8847
[IMP] awesome_owl: delete todos
HashemKhaled Oct 28, 2025
07ae391
[IMP] awesome_owl: refactor Card component to use slots
HashemKhaled Oct 28, 2025
8ac0d11
[IMP] awesome_owl: toggle card content
HashemKhaled Oct 28, 2025
e286ff6
[IMP] awesome_dashboard: add layout with some buttons for quick navig…
HashemKhaled Oct 28, 2025
94c0020
[IMP] awesome_dashboard: add a dashboard item
HashemKhaled Oct 28, 2025
22dbdb4
[IMP] awesome_dashboard: call the server for adding some statistics
HashemKhaled Oct 29, 2025
4282ec3
[IMP] awesome_dashboard: implement statistics service
HashemKhaled Oct 29, 2025
2dfd044
[IMP] awesome_dashboard: display a pie chart
HashemKhaled Oct 29, 2025
1a863c6
[IMP] awesome_dashboard: lazy loading the dashboard
HashemKhaled Oct 29, 2025
639c920
[IMP] awesome_dashboard: make the dashboard generic
HashemKhaled Oct 29, 2025
939fb62
[IMP] awesome_dashboard: make the dashboard extensible
HashemKhaled Oct 29, 2025
b299103
[IMP] awesome_dashboard: add settings dialog for managing dashboard i…
HashemKhaled Oct 30, 2025
d4fa2a0
[FIX] awesome_dashboard: add missing newline at end of XML files
HashemKhaled Oct 30, 2025
d1884f2
[FIX] awesome_dashboard: fix default size handling in DashboardItem c…
HashemKhaled Oct 30, 2025
0f622fb
[FIX] awesome_owl: correct glob pattern for static assets inclusion
HashemKhaled Oct 30, 2025
281a073
[FIX] awesome_dashboard: remove redundant chart destruction in PieCha…
HashemKhaled Oct 30, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions awesome_dashboard/__manifest__.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@
'assets': {
'web.assets_backend': [
'awesome_dashboard/static/src/**/*',
('remove', 'awesome_dashboard/static/src/dashboard/**/*'),
],
'awesome_dashboard.dashboard': [
'awesome_dashboard/static/src/dashboard/**/*',
],
},
'license': 'AGPL-3'
Expand Down
10 changes: 0 additions & 10 deletions awesome_dashboard/static/src/dashboard.js

This file was deleted.

8 changes: 0 additions & 8 deletions awesome_dashboard/static/src/dashboard.xml

This file was deleted.

38 changes: 38 additions & 0 deletions awesome_dashboard/static/src/dashboard/dashboard.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/** @odoo-module **/

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 "./dashboard_item/dashboard_item";
import { PieChart } from "./pie_chart/pie_chart";

class AwesomeDashboard extends Component {
static components = { Layout, DashboardItem, PieChart };
static template = "awesome_dashboard.AwesomeDashboard";

setup() {
this.display = { controlPanel: {} };
this.action = useService("action");
this.stats = useState(useService("awesome_dashboard.statistics"));
this.items = registry.category("awesome_dashboard").getAll();
}

openCustomers() {
this.action.doAction("base.action_partner_form");
}

openLeads() {
this.action.doAction({
type: "ir.actions.act_window",
res_model: "crm.lead",
name: "Leads",
views: [
[false, "list"],
[false, "form"],
],
});
}
}

registry.category("lazy_components").add("AwesomeDashboard", AwesomeDashboard);
14 changes: 14 additions & 0 deletions awesome_dashboard/static/src/dashboard/dashboard.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
.o_dashboard {
background-color: gray;
}

.btn-primary {
background-color: purple;
border-color: purple;
}

.divider{
width:5px;
height:auto;
display:inline-block;
}
24 changes: 24 additions & 0 deletions awesome_dashboard/static/src/dashboard/dashboard.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<templates xml:space="preserve">

<t t-name="awesome_dashboard.AwesomeDashboard">
<Layout display="display" className="'o_dashboard h-100'">
<button type="button" class="btn btn-primary" t-on-click="openCustomers">
Customers
</button>
<div class="divider"/>
<button type="button" class="btn btn-primary" t-on-click="openLeads">
Leads
</button>
<div class="d-flex flex-wrap" t-if="stats.isLoaded">
<t t-foreach="items" t-as="item" t-key="item.id">
<DashboardItem size="item.size || 1">
<t t-set="itemProp" t-value="item.props ? item.props(stats) : {'data': stats}"/>
<t t-component="item.component" t-props="itemProp"/>
</DashboardItem>
</t>
</div>
</Layout>
</t>

</templates>
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { Component } from "@odoo/owl";

export class DashboardItem extends Component {
static template = "awesome_dashboard.DashboardItem";
static props = {
size: {
type: Number,
default: 1,
optional: true,
},
slots: {
type: Object,
optional: true,
},
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<templates xml:space="preserve">

<t t-name="awesome_dashboard.DashboardItem">
<div class="card d-inline-block m-2" t-attf-style="width: {{18 * this.props.size}}rem;">
<div class="card-body">
<t t-slot="default"/>
</div>
</div>
</t>
</templates>
66 changes: 66 additions & 0 deletions awesome_dashboard/static/src/dashboard/dashboard_items.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { NumberCard } from "./number_card/number_card";
import { PieChartCard } from "./pie_chart_card/pie_chart_card";
import { registry } from "@web/core/registry";

const dashboardItems = [
{
id: "average_quantity",
description: "Average amount of t-shirt by order this month",
component: NumberCard,
props: (data) => ({
title: "Average amount of t-shirt by order this month",
value: data.average_quantity,
}),
},
{
id: "average_time",
description:
"Average time for an order to go from 'new' to 'sent' or 'cancelled'",
component: NumberCard,
props: (data) => ({
title: "Average time for an order to go from 'new' to 'sent' or 'cancelled'",
value: data.average_time,
}),
},
{
id: "nb_new_orders",
description: "Number of new orders this month",
component: NumberCard,
props: (data) => ({
title: "Number of new orders this month",
value: data.nb_new_orders,
}),
},
{
id: "nb_cancelled_orders",
description: "Number of cancelled orders this month",
component: NumberCard,
props: (data) => ({
title: "Number of cancelled orders this month",
value: data.nb_cancelled_orders,
}),
},
{
id: "total_amount",
description: "Total amount of new orders this month",
component: NumberCard,
props: (data) => ({
title: "Total amount of new orders this month",
value: data.total_amount,
}),
},
{
id: "orders_by_size",
description: "Shirt orders by size",
component: PieChartCard,
size: 1.5,
props: (data) => ({
title: "Shirt orders by size",
values: data.orders_by_size,
}),
},
];

dashboardItems.forEach((item) => {
registry.category("awesome_dashboard").add(item.id, item);
});
10 changes: 10 additions & 0 deletions awesome_dashboard/static/src/dashboard/number_card/number_card.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { Component } from "@odoo/owl";

export class NumberCard extends Component {
static template = "awesome_dashboard.NumberCard";

static props = {
title: String,
value: Number,
};
}
10 changes: 10 additions & 0 deletions awesome_dashboard/static/src/dashboard/number_card/number_card.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<templates xml:space="preserve">

<t t-name="awesome_dashboard.NumberCard">
<t t-esc="props.title"/>
<div class="fs-1 fw-bold text-success text-center">
<t t-esc="props.value"/>
</div>
</t>
</templates>
52 changes: 52 additions & 0 deletions awesome_dashboard/static/src/dashboard/pie_chart/pie_chart.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import {
Component,
useEffect,
useRef,
onWillUnmount,
onWillStart,
} from "@odoo/owl";
import { loadJS } from "@web/core/assets";
import { getColor } from "@web/core/colors/colors";

export class PieChart extends Component {
static template = "awesome_dashboard.PieChart";
static props = {
label: { type: String, optional: true },
data: { type: Object },
};

setup() {
this.canvasRef = useRef("canvas");
this.chart = null;

onWillStart(() => loadJS(["/web/static/lib/Chart/Chart.js"]));
useEffect(() => this.renderChart());
onWillUnmount(() => {
if (this.chart) {
this.chart.destroy();
}
});
}

renderChart() {
const labels = Object.keys(this.props.data);
const values = Object.values(this.props.data);
const color = labels.map((_, idx) => getColor(idx * 2));
if (this.chart) {
this.chart.destroy();
}
this.chart = new Chart(this.canvasRef.el, {
type: "pie",
data: {
labels: labels,
datasets: [
{
label: this.props.label || "",
data: values,
backgroundColor: color,
},
],
},
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<templates xml:space="preserve">
<t t-name="awesome_dashboard.PieChart">
<canvas t-ref="canvas" />
</t>
</templates>
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { Component } from "@odoo/owl";
import { PieChart } from "../pie_chart/pie_chart";

export class PieChartCard extends Component {
static template = "awesome_dashboard.PieChartCard";
static components = { PieChart };

static props = {
title: String,
values: Object,
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<templates xml:space="preserve">

<t t-name="awesome_dashboard.PieChartCard">
<t t-esc="props.title"/>
<PieChart data="props.values" label="''"/>
</t>
</templates>
25 changes: 25 additions & 0 deletions awesome_dashboard/static/src/dashboard/statistics_service.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { registry } from "@web/core/registry";
import { rpc } from "@web/core/network/rpc";
import { reactive } from "@odoo/owl";

const statisticsService = {
async: ["loadStatistics"],

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this line necessary for the service ?

start() {
const statistics = reactive({ isLoaded: false });

async function loadStatistics() {
const result = await rpc("/awesome_dashboard/statistics");
statistics.isLoaded = true;
Object.assign(statistics, result);
}

setInterval(loadStatistics, 10 * 60 * 1000);
loadStatistics();

return statistics;
},
};

registry
.category("services")
.add("awesome_dashboard.statistics", statisticsService);
12 changes: 12 additions & 0 deletions awesome_dashboard/static/src/dashboard_action.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { Component, xml } from "@odoo/owl";
import { registry } from "@web/core/registry";
import { LazyComponent } from "@web/core/assets";

export class DashboardAction extends Component {
static components = { LazyComponent };
static template = xml`<LazyComponent bundle="'awesome_dashboard.dashboard'" Component="'AwesomeDashboard'" props="props"/>`;
}

registry
.category("actions")
.add("awesome_dashboard.dashboard", DashboardAction);
2 changes: 1 addition & 1 deletion awesome_owl/__manifest__.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
('include', 'web._assets_bootstrap'),
('include', 'web._assets_core'),
'web/static/src/libs/fontawesome/css/font-awesome.css',
'awesome_owl/static/src/**/*',
'awesome_owl/static/src/**/**',
],
},
'license': 'AGPL-3'
Expand Down
24 changes: 24 additions & 0 deletions awesome_owl/static/src/card/card.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { Component, useState } from "@odoo/owl";

export class Card extends Component {
static template = "awesome_owl.card";
static props = {
title: { type: String },
slots: {
type: Object,
shape: {
default: true,
},
},
};

setup() {
this.state = useState({
isOpen: true,
});
}

onToggle() {
this.state.isOpen = !this.state.isOpen;
}
}
Loading