Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
15bd5fa
[ADD] estate : implement estate module and estate_property table cre…
aykhu-odoo Mar 10, 2026
52fa0f3
[IMP] estate : added access rights
aykhu-odoo Mar 11, 2026
99d70a3
[IMP] estate: added action and menu
aykhu-odoo Mar 12, 2026
53c0bc3
[IMP] estate: improved list view and form view
aykhu-odoo Mar 13, 2026
c83862c
[IMP] estate: Implemented model relations and search views
aykhu-odoo Mar 16, 2026
ff255a5
[IMP] estate: Added computed fields
aykhu-odoo Mar 24, 2026
d927193
[IMP] estate: Implemented onchange logic and validity with inverse fu…
aykhu-odoo Mar 26, 2026
2018ac5
[IMP] estate: add state transition actions with validation
aykhu-odoo Mar 30, 2026
e47d710
[IMP] estate: add offer state actions and enforce single acceptance
aykhu-odoo Mar 31, 2026
6fc1fcb
[IMP] estate: add SQL constraints for prices and uniqueness
aykhu-odoo Apr 2, 2026
cdba32e
[IMP] estate: enforce minimum selling price at 90% of expected
aykhu-odoo Apr 6, 2026
f4a48cc
[IMP] estate: ui improvements and stat button integration
aykhu-odoo Apr 8, 2026
fee412e
[IMP] estate: implement model and view inheritance
aykhu-odoo Apr 10, 2026
f00c08d
[IMP] estate: integrate accounting module and add kanban view
aykhu-odoo Apr 14, 2026
5241d27
[IMP] estate: add visit scheduling and issue reporting
aykhu-odoo Apr 23, 2026
54901bb
[IMP] estate: enhance offers workflow and UI improvements
aykhu-odoo Apr 20, 2026
5fa3a74
[IMP] awesome_owl: complete Owl component tutorial (counter, todos, c…
aykhu-odoo Apr 27, 2026
73e5118
[IMP] awesome_dashboard: complete Owl dashboard with configurable items
aykhu-odoo May 8, 2026
73a3fb9
[ADD] sale_rental_deposit: introduce deposit mechanism for rental pro…
aykhu-odoo May 15, 2026
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
5 changes: 4 additions & 1 deletion 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'
'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.

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


class AwesomeDashboard extends Component {
static template = "awesome_dashboard.AwesomeDashboard";
static components = { Layout, DashboardItem};
setup(){
this.action = useService("action");
this.statistics = useService("awesome_dashboard.statistics");
this.dialog = useService("dialog");
this.results = useState(this.statistics.stats);
this.items = registry.category("awesome_dashboard").getAll();
this.state = useState({
disabledItems: browser.localStorage.getItem("disabledDashboardItems")?.split(",") || []
});
}

openConfiguration() {
this.dialog.add(ConfigurationDialog, {
items: this.items,
disabledItems: this.state.disabledItems,
onUpdateConfiguration: this.updateConfiguration.bind(this),
})
}
updateConfiguration(newDisabledItems) {
this.state.disabledItems = newDisabledItems;
}
openCustomers() {
this.action.doAction({
type: "ir.actions.act_window",
res_model: "res.partner",
name: _t("Customers"),
views: [
[false, "kanban"],
],
});
}
openLeads() {
this.action.doAction({
type: "ir.actions.act_window",
res_model: "crm.lead",
name: _t("Leads"),
views: [
[false, "list"],
[false, "form"],
],
});
}
}

class ConfigurationDialog extends Component {
static template = "awesome_dashboard.ConfigurationDialog";
static components = { Dialog, CheckBox };
static props = ["close", "items", "disabledItems", "onUpdateConfiguration"];

setup() {
this.items = useState(this.props.items.map((item) => {
return {
...item,
enabled: !this.props.disabledItems.includes(item.id),
}
}));
}
done() {
this.props.close();
}
onChange(checked, changedItem) {
changedItem.enabled = checked;
const newDisabledItems = Object.values(this.items).filter(
(item) => !item.enabled
).map((item) => item.id)
browser.localStorage.setItem(
"disabledDashboardItems",
newDisabledItems,
);
this.props.onUpdateConfiguration(newDisabledItems);
}
}

registry.category("lazy_components").add("awesome_dashboard.dashboard", 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: grey;
}
38 changes: 38 additions & 0 deletions awesome_dashboard/static/src/dashboard/dashboard.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?xml version="1.0" encoding="UTF-8" ?>
<templates xml:space="preserve">
<t t-name="awesome_dashboard.AwesomeDashboard">
<Layout display="{ controlPanel: {} }" className="'o_dashboard h-100'">
<t t-set-slot="layout-buttons">
<button class="btn btn-primary" t-on-click="openCustomers">Customers</button>
<button class="btn btn-primary" t-on-click="openLeads">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>
<div class="d-flex flex-wrap p-3">
<t t-foreach="items" t-as="item" t-key="item.id">
<DashboardItem t-if="!state.disabledItems.includes(item.id)" size="item.size || 1"> <t t-set="itemProp" t-value="item.props ? item.props(results) : {'data': results}"/>
<t t-component="item.Component" t-props="itemProp" />
</DashboardItem>
</t>
</div>
</Layout>
</t>
<t t-name="awesome_dashboard.ConfigurationDialog">
<Dialog title="'Dashboard items configuration'">
Which cards do you whish to see ?
<t t-foreach="items" t-as="item" t-key="item.id">
<CheckBox value="item.enabled" onChange="(ev) => this.onChange(ev, item)">
<t t-esc="item.description"/>
</CheckBox>
</t>
<t t-set-slot="footer">
<button class="btn btn-primary" t-on-click="done">
Done
</button>
</t>
</Dialog>
</t>
</templates>
67 changes: 67 additions & 0 deletions awesome_dashboard/static/src/dashboard/dashboard_items.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { NumberCard } from "./number_card/number_card";
import { PieChartCard } from "./piechart_card/piechart_card";
import { registry } from "@web/core/registry";
import { _t } from "@web/core/l10n/translation";


const items = [
{
id: "average_quantity",
description: "Average amount of t-shirt",
Component: NumberCard,
props: (data) => ({
title: _t("Average amount of t-shirt by order this month"),
value: data.average_quantity,
})
},
{
id: "average_time",
description: "Average time for an order",
Component: NumberCard,
props: (data) => ({
title: _t("Average time for an order to go from 'new' to 'sent' or 'cancelled'"),
value: data.average_time,
})
},
{
id: "number_new_orders",
description: "New orders this month",
Component: NumberCard,
props: (data) => ({
title: _t("Number of new orders this month"),
value: data.nb_new_orders,
})
},
{
id: "cancelled_orders",
description: "Cancelled orders this month",
Component: NumberCard,
props: (data) => ({
title: _t("Number of cancelled orders this month"),
value: data.nb_cancelled_orders,
})
},
{
id: "amount_new_orders",
description: "amount orders this month",
Component: NumberCard,
props: (data) => ({
title: _t("Total amount of new orders this month"),
value: data.total_amount,
})
},
{
id: "pie_chart",
description: "Shirt orders by size",
Component: PieChartCard,
size: 2,
props: (data) => ({
title: _t("Shirt orders by size"),
data: data.orders_by_size,
})
}
]

items.forEach(item => {
registry.category("awesome_dashboard").add(item.id, item);
});
15 changes: 15 additions & 0 deletions awesome_dashboard/static/src/dashboard/item/dashboard_item.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { Component } 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 },
};
get width() {
const size = this.props.size || 1;
return `width: ${18 * size}rem;`;
}
}
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.DashboardItem">
<div class="o_dashboard_item card m-2 p-3" t-att-style="width">
<t t-slot="default"/>
</div>
</t>
</templates>
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: { type: String },
value: { type: Number },
};
}
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" owl="1">
<t t-name="awesome_dashboard.NumberCard">
<div>
<div t-esc="props.title" />
<h1 t-esc="props.value" />
</div>
</t>
</templates>
37 changes: 37 additions & 0 deletions awesome_dashboard/static/src/dashboard/piechart/piechart.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { Component, onWillStart, onMounted, useRef } from "@odoo/owl";
import { loadJS } from "@web/core/assets";


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

setup() {
this.canvasRef = useRef("canvas");
onWillStart(async () => {
await loadJS("/web/static/lib/Chart/Chart.js");
});
onMounted(() => {
this.renderChart();
});
}

renderChart() {
const ctx = this.canvasRef.el.getContext("2d");
const data = this.props.data || {};
new Chart(ctx, {
type: "pie",
data: {
labels: ["S", "M", "XL"],
datasets: [
{
data: [
data.s || 0,
data.m || 0,
data.xl || 0,
],
},
],
},
});
}
}
3 changes: 3 additions & 0 deletions awesome_dashboard/static/src/dashboard/piechart/piechart.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<t t-name="awesome_dashboard.PieChart">
<canvas t-ref="canvas"/>
</t>
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { Component } from "@odoo/owl";
import { PieChart } from "../piechart/piechart";


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

static components = { PieChart };
static props = {
title: { type: String },
values: { type: Object },
};
}
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" owl="1">
<t t-name="awesome_dashboard.PieChartCard">
<div class="o_card">
<h5><t t-esc="props.title"/></h5>
<div class="pie-container">
<PieChart data="props.data || {}"/>
</div>
</div>
</t>
</templates>
21 changes: 21 additions & 0 deletions awesome_dashboard/static/src/dashboard/statistics_service.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { registry } from "@web/core/registry";
import { rpc } from "@web/core/network/rpc";
import { reactive } from "@odoo/owl";


const statisticsService = {
start() {
const stats = reactive({});
async function loadStatistics() {
const result = await rpc("/awesome_dashboard/statistics");
Object.assign(stats, result);
}
loadStatistics();
setInterval(loadStatistics, 600000);
return {
stats,
};
},
};

registry.category("services").add("awesome_dashboard.statistics", statisticsService);
22 changes: 22 additions & 0 deletions awesome_dashboard/static/src/dashboard_action.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/** @odoo-module */

import { registry } from "@web/core/registry";
import { LazyComponent } from "@web/core/assets";
import { Component, xml } from "@odoo/owl";


class AwesomeDashboardAction extends Component {
static components = { LazyComponent };

static template = xml`
<LazyComponent
bundle="'awesome_dashboard.dashboard'"
Component="'awesome_dashboard.dashboard'"
/>
`;
}

registry.category("actions").add(
"awesome_dashboard.dashboard",
AwesomeDashboardAction
);
Loading