Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -127,3 +127,6 @@ dmypy.json

# Pyre type checker
.pyre/

# IDEs and Editors
.vscode/
3 changes: 3 additions & 0 deletions awesome_dashboard/__manifest__.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@
'web.assets_backend': [
'awesome_dashboard/static/src/**/*',
],
'awesome_dashboard.dashboard': [
'awesome_dashboard/static/src/dashboard/**/*',
],
},
'license': 'AGPL-3'
}
8 changes: 0 additions & 8 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.

108 changes: 108 additions & 0 deletions awesome_dashboard/static/src/dashboard/dashboard.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import { Component, useState, xml } from "@odoo/owl";
import { registry } from "@web/core/registry";
import { Layout } from "@web/search/layout";
import { useService } from "@web/core/utils/hooks";
import { _t } from "@web/core/l10n/translation";
import { DashboardItem } from "./dashboardItem/dashboardItem";
import { Dialog } from "@web/core/dialog/dialog";
import { CheckBox } from "@web/core/checkbox/checkbox";

class AwesomeDashboard extends Component {
static template = "awesome_dashboard.AwesomeDashboard";

static components = { Layout, DashboardItem };

static props = {
statistics: { type: Array, optional: true },
};

setup() {
this.display = {
controlPanel: {},
}

this.action = useService("action");

const statisticsService = useService("awesome_dashboard.statistics");

this.statistics = useState(statisticsService.statistics);

this.items = registry.category("awesome_dashboard").getAll();

this.dialog = useService("dialog");

this.state = useState({ disabledItemsIds: [] });
}

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

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

openConfiguration() {
this.dialog.add(ConfigurationDialog, {
title: _t("Dashboard Items Configuration"),
items: this.items,
disabledItemsIds: this.state.disabledItemsIds,
onApply: (newDisabledItemsIds) => {
this.state.disabledItemsIds = newDisabledItemsIds;
},
size: "medium",
showFooter: true,
});
}

}


class ConfigurationDialog extends Component {
static template = xml`
<Dialog title="props.title">
<div class="p-4">
<p t-esc="this._t('Which cards do you wish to see?')"/>
<t t-foreach="props.items" t-key="item.id" t-as="item">
<CheckBox
value="!props.disabledItemsIds.includes(item.id)"
t-on-change="() => this.toggleItem(item.id)"
>
<t t-esc="item.description"/>
</CheckBox>
</t>
</div>
<t t-set-slot="footer">
<button class="btn btn-primary" t-on-click="onApply">Apply</button>
</t>
</Dialog>
`;

static components = { Dialog, CheckBox };

setup() {
this._t = _t;
this.newDisabledItemsIds = [...this.props.disabledItemsIds];
}

toggleItem(itemId) {
if (this.newDisabledItemsIds.includes(itemId)) {
this.newDisabledItemsIds = this.newDisabledItemsIds.filter(id => id !== itemId);
} else {
this.newDisabledItemsIds = [...this.newDisabledItemsIds, itemId];
}
}

onApply() {
this.props.onApply(this.newDisabledItemsIds);
this.props.close();
}
}

registry.category("lazy_components").add("AwesomeDashboard", AwesomeDashboard);
3 changes: 3 additions & 0 deletions awesome_dashboard/static/src/dashboard/dashboard.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.o_dashboard {
background-color: gray;
}
26 changes: 26 additions & 0 deletions awesome_dashboard/static/src/dashboard/dashboard.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?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'">
<t t-set-slot="layout-buttons">
<button class="btn btn-primary" t-on-click="openCustomersKanban">Customers</button>
<button class="btn btn-primary" t-on-click="openCrmLeads">Leads</button>
</t>
<t t-set-slot="control-panel-additional-actions">
<button t-on-click="openConfiguration" class="btn p-0 ms-1 border-0">
<i class="fa fa-cog"></i>
</button>
</t>
<t t-foreach="items" t-key="item.id" t-as="item">
<t t-if="!state.disabledItemsIds.includes(item.id)">
<DashboardItem size="item.size || 1">
<t t-set="itemProp" t-value="item.props ? item.props(statistics) : {'data': statistics}"/>
<t t-component="item.Component" t-props="itemProp" />
</DashboardItem>
</t>
</t>
</Layout>
</t>

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

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

static props = {
size: { type: Number, optional: true},
slots: { type: Object, optional: true },
};

static defaultProps = {
size: 1,
};
}
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.dashboardItem">
<div class="p-3" t-attf-style="width: {{ props.size * 18 }}rem; background-color: white; color: black; border-radius: 5px; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); margin: 10px;">
<t t-slot="default"/>
</div>
</t>

</templates>
47 changes: 47 additions & 0 deletions awesome_dashboard/static/src/dashboard/dashboard_items.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { _t } from "@web/core/l10n/translation";
import { NumberCard } from "./numberCard/numberCard";
import { PieChartCard } from "./pieChartCard/pieChartCard";
import { registry } from "@web/core/registry";

const dashboardRegistry = registry.category("awesome_dashboard");

dashboardRegistry.add("average_quantity", {
id: "average_quantity",
description: _t("Average amount of t-shirts"),
Component: NumberCard,
size: 3,
props: (data) => ({
title: _t("Average Quantity"),
value: data.average_quantity
}),
});

dashboardRegistry.add("average_time", {
id: "average_time",
description: _t("Average time for an order"),
Component: NumberCard,
props: (data) => ({
title: _t("Average Time"),
value: data.average_time
}),
});

dashboardRegistry.add("nb_cancelled_orders", {
id: "nb_cancelled_orders",
description: _t("Number of cancelled orders"),
Component: NumberCard,
props: (data) => ({
title: _t("Cancelled Orders"),
value: data.nb_cancelled_orders
}),
});

dashboardRegistry.add("orders_by_size", {
id: "orders_by_size",
description: _t("Shirt orders by size"),
Component: PieChartCard,
props: (data) => ({
title: _t("Shirt orders by size"),
values: data.orders_by_size,
})
});
10 changes: 10 additions & 0 deletions awesome_dashboard/static/src/dashboard/numberCard/numberCard.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: { type: String},
value: { type: Number, optional: true },
};
}
11 changes: 11 additions & 0 deletions awesome_dashboard/static/src/dashboard/numberCard/numberCard.xml
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.numberCard">
<div>
<h3 style="color: black;"><t t-esc="props.title"/></h3>
<p t-if="props.value"><t t-esc="props.value"/></p>
</div>
</t>

</templates>
80 changes: 80 additions & 0 deletions awesome_dashboard/static/src/dashboard/pieChart/pieChart.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { Component, onWillStart, onWillUnmount, useEffect, useRef } from "@odoo/owl";
import { loadJS } from "@web/core/assets";


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

static props = {
title: { type: String },
values: { type: Object },
onSelect: { type: Function, optional: true },
}

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

onWillStart(() => loadJS("/web/static/lib/Chart/Chart.js"));

useEffect(() => {
this.renderChart();
}, () => [this.props.values]);
onWillUnmount(this.onWillUnmount);
}

onWillUnmount() {
if (this.chart) {
this.chart.destroy();
}
}

renderChart() {
const labels = Object.keys(this.props.values);

const data = Object.values(this.props.values);

if (this.chart) {
this.chart.destroy();
}

this.chart = new Chart(this.canvasRef.el, {
type: 'pie',
data: {
labels: labels,
datasets: [{
data: data,
backgroundColor: [
'#00A09D',
'#E9A13B',
'#212529',
'#D9534F',
'#5BC0DE',
],
}]
},
options: {
responsive: true,
plugins: {
legend: {
position: 'bottom',
}
},
},
});
}

onChartClick(ev) {
const chart = Chart.getChart(this.canvasRef.el);
const activePoints = chart.getElementsAtEventForMode(ev, 'nearest', { intersect: true }, true);

if (activePoints.length > 0) {
const index = activePoints[0].index;
const label = chart.data.labels[index];

if (this.props.onSelect) {
this.props.onSelect(label);
}
}
}
}
11 changes: 11 additions & 0 deletions awesome_dashboard/static/src/dashboard/pieChart/pieChart.xml
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.pieChart">
<div class="chart-container" style="background-color: white;">
<t t-esc="props.title"/>
<canvas t-ref="canvas" t-on-click="onChartClick"/>
</div>
</t>

</templates>
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { Component } from "@odoo/owl";
import { PieChart } from "../pieChart/pieChart";
import { useService } from "@web/core/utils/hooks";
import { _t } from "@web/core/l10n/translation";

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

static components = { PieChart };

static props = {
title: { type: String },
values: { type: Object },
};

setup() {
this.action = useService("action");
}

onSelectLabel(label) {
this.action.doAction({
type: 'ir.actions.act_window',
name: _t("Orders with size %s", [label]),
res_model: 'awesome_dashboard.orders',
domain: [['size', '=', label]],
views: [[false, 'list']],
target: 'current',
});
}
}
Loading