Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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 @@ -3,6 +3,9 @@ __pycache__/
*.py[cod]
*$py.class

# Ruff filter
*.toml

# C extensions
*.so

Expand Down
8 changes: 5 additions & 3 deletions awesome_dashboard/__manifest__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
{
'name': "Awesome Dashboard",

Expand All @@ -23,8 +22,11 @@
],
'assets': {
'web.assets_backend': [
'awesome_dashboard/static/src/**/*',
'awesome_dashboard/static/src/dashboard_action.js',
],
'awesome_dashboard.dashboard': [
'awesome_dashboard/static/src/dashboard/**/*',
],
},
'license': 'AGPL-3'
'license': 'AGPL-3',
}
Binary file added awesome_dashboard/static/img/dashboard_bg.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
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.

69 changes: 69 additions & 0 deletions awesome_dashboard/static/src/dashboard/Dashboard/dashboard.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
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 "../DashboardItem/dashboarditem";
import { DashboardSettings } from "../DashboardSettings/dashboardsettings";
import "../dashboard_items";

const STORAGE_KEY = "awesome_dashboard.removed_item_ids";

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

setup() {
this.allItems = registry.category("awesome_dashboard").getAll();
this.actionService = useService("action");
this.dialogService = useService("dialog");
this.statisticsService = useService("awesome_dashboard.statistics");
this.statistics = useState(this.statisticsService.statistics);
this.state = useState({
removedItemIds: this.loadRemovedItemIds(),
});
}

loadRemovedItemIds() {
try {
const value = localStorage.getItem(STORAGE_KEY);
const parsed = value ? JSON.parse(value) : [];
return Array.isArray(parsed) ? parsed : [];
} catch {
return [];
}
}

saveRemovedItemIds(removedItemIds) {
localStorage.setItem(STORAGE_KEY, JSON.stringify(removedItemIds));
this.state.removedItemIds = removedItemIds;
}

get items() {
const removedIds = new Set(this.state.removedItemIds);
return this.allItems.filter((item) => !removedIds.has(item.id));
}

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

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

openSettings() {
this.dialogService.add(DashboardSettings, {
title: "Dashboard settings",
removedItemIds: this.state.removedItemIds,
applyConfiguration: (removedItemIds) => this.saveRemovedItemIds(removedItemIds),
});
}

}

registry.category("lazy_components").add("AwesomeDashboard", AwesomeDashboard);
22 changes: 22 additions & 0 deletions awesome_dashboard/static/src/dashboard/Dashboard/dashboard.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
.o_dashboard {
background-image: url("/awesome_dashboard/static/img/dashboard_bg.jpg");
background-repeat: no-repeat;
background-position: center;
background-size: cover;

.o_dashboard_row {
display: flex;
flex-wrap: wrap;
justify-content: center;
}

.o_header_dashboard_item {
font-size: 1.5em;
}

.o_average_quantity {
font-size: 2em;
color: #008528;
font-weight: bold;
}
}
23 changes: 23 additions & 0 deletions awesome_dashboard/static/src/dashboard/Dashboard/dashboard.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8" ?>
<templates xml:space="preserve">

<t t-name="awesome_dashboard.AwesomeDashboard">
<Layout className="'o_dashboard h-100'" display="{ controlPanel: {} }">
<button type="button" class="btn btn-primary m-3" t-on-click="openCustomers">Customers</button>
<button type="button" class="btn btn-primary m-3" t-on-click="openLeads">Leads</button>
<button type="button" class="btn btn-primary" t-on-click="openSettings" title="Settings" aria-label="Settings">
<i class="fa fa-gear" role="img" aria-hidden="true"/>
</button>

<div class="o_dashboard_row">
<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(statistics) : {'data': statistics}"/>
<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,13 @@
import { Component } from "@odoo/owl";

export class DashboardItem extends Component {
static template = "awesome_dashboard.dashboardItem";
static props = {
size: { type: Number, default: 1, optional: true }
};

get cardStyle() {
const width = 18 * this.props.size;
return `width: ${width}rem;`;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
.o_dashboard_item {
background: white;
border-radius: 10px;
padding: 16px;
margin: 8px;
opacity: 0.9;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
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="o_dashboard_item" t-att-style="cardStyle">
<t t-slot="default" />
</div>
</t>

</templates>
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { Component, useState } from "@odoo/owl";
import { registry } from "@web/core/registry";
import { Dialog } from "@web/core/dialog/dialog";

export class DashboardSettings extends Component {
static template = "awesome_dashboard.settings";
static components = { Dialog };
static props = {
title: { type: String, optional: true },
removedItemIds: { type: Array, optional: true },
applyConfiguration: { type: Function },
close: { type: Function, optional: true },
};

setup() {
this.items = registry.category("awesome_dashboard").getAll();
const removedIds = new Set(this.props.removedItemIds || []);
this.state = useState({
enabledById: Object.fromEntries(this.items.map((item) => [item.id, !removedIds.has(item.id)])),
});
}

isItemEnabled(item) {
return this.state.enabledById[item.id] ?? true;
}

onToggleItem(ev) {
const itemId = ev.currentTarget.dataset.itemId;
this.state.enabledById[itemId] = ev.currentTarget.checked;
}

onApply() {
const removedItemIds = this.items
.filter((item) => !this.state.enabledById[item.id])
.map((item) => item.id);
this.props.applyConfiguration(removedItemIds);
if (this.props.close) {
this.props.close();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
.o_dashboard_settings_item {
display: flex;
align-items: center;
}

.o_header_settings_p {
margin: 0.5em;
font-size: 1.5em;
}

.o_dashboard_settings_checkbox {
width: 1.5em;
height: 1.5em;
margin: 1em;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8" ?>
<templates xml:space="preserve">

<t t-name="awesome_dashboard.settings">
<Dialog title="props.title || 'Dashboard settings'">
<t t-foreach="items" t-as="item" t-key="item.id">
<div class="o_dashboard_settings_item">
<input type="checkbox" t-att-checked="isItemEnabled(item)" t-att-data-item-id="item.id" t-on-change="onToggleItem" class="o_dashboard_settings_checkbox" id="checkbox-{{item.id}}"/>
<p class="o_header_settings_p"><t t-esc="item.description"/></p>
</div>
</t>
<t t-set-slot="footer">
<button type="button" class="btn btn-primary" t-on-click="onApply">Apply</button>
</t>
</Dialog>
</t>

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

export class NumberCard extends Component {
static template = "awesome_dashboard.NumberCard";
static props = {
title: { type: String },
value: { type: [Number, String] },
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8" ?>
<templates xml:space="preserve">

<t t-name="awesome_dashboard.NumberCard">
<p class="o_header_dashboard_item"><t t-esc="props.title"/></p>
<p class="o_average_quantity"><t t-esc="props.value"/></p>
</t>

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

export class PieChart extends Component {
static template = "awesome_dashboard.PieChart";
static props = {
labels: { type: Array },
values: { type: Array },
};

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

onWillStart(async () => {
await loadJS("/web/static/lib/Chart/Chart.js");
});

onMounted(() => {
const chartData = {
labels: this.props.labels,
datasets: [{
data: this.props.values,
}],
};

this.chart = new Chart(this.canvasRef.el, {
type: "pie",
data: chartData,
options: {
responsive: true,
maintainAspectRatio: false,
},
});
});

onWillUpdateProps((nextProps) => {
if (!this.chart) {
return;
}
this.chart.data.labels = nextProps.labels;
this.chart.data.datasets[0].data = nextProps.values;
this.chart.update();
});

}
}
10 changes: 10 additions & 0 deletions awesome_dashboard/static/src/dashboard/PieChart/piechart.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.PieChart">
<div>
<canvas t-ref="canvas"/>
</div>
</t>

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

export class PieChartCard extends Component {
static template = "awesome_dashboard.PieChartCard";
static components = { PieChart };
static props = {
title: { type: String },
labels: { type: Array },
values: { type: Array },
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8" ?>
<templates xml:space="preserve">

<t t-name="awesome_dashboard.PieChartCard">
<p class="o_header_dashboard_item"><t t-esc="props.title"/></p>
<PieChart labels="props.labels" values="props.values"/>
</t>

</templates>
Loading