Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
e54ea97
[IMP] estate: chapter 2 add real estate module
yanla-odoo Mar 16, 2026
ab68f58
[IMP] estate: chapter 2 add more info to real estate manifest.py
yanla-odoo Mar 16, 2026
27ec85a
[IMP] estate: chapter 3 add model information and basic fields
yanla-odoo Mar 16, 2026
be2b9e4
[IMP] estate: chapter 4 add access rights
yanla-odoo Mar 16, 2026
d6bc713
[IMP] estate: chapter 5 add action
yanla-odoo Mar 16, 2026
cec72ad
[CLN] estate: code cleanup to make checks green
yanla-odoo Mar 16, 2026
f6264e1
[IMP] estate: chapter 5 add menus, fields, attributes, views
yanla-odoo Mar 16, 2026
b1d66fa
[LINT] estate: finally installed a linter
yanla-odoo Mar 16, 2026
54a0430
[IMP] estate: chapter 6 add basic views
yanla-odoo Mar 16, 2026
8086a69
[IMP] estate: chapter 7 relations between models
yanla-odoo Mar 16, 2026
5c21aad
[LINT] estate: code cleanup
yanla-odoo Mar 16, 2026
d802e95
[IMP] estate: chapter 8 computed fields and onchanges
yanla-odoo Mar 17, 2026
3cecc43
[CLN] estate: address PR comments
yanla-odoo Mar 17, 2026
24a782a
[IMP] estate: chapter 9 ready for some action
yanla-odoo Mar 17, 2026
909ffca
[IMP] estate: chapter 10 constraints
yanla-odoo Mar 17, 2026
5fbcd99
[IMP] estate: chapter 11 part 1 until editable offers and tags
yanla-odoo Mar 17, 2026
da387ce
[IMP] estate: chapter 11 part 2 finishing the sprinkles
yanla-odoo Mar 18, 2026
6420654
[IMP] estate: chapter 12 inheritance
yanla-odoo Mar 18, 2026
5a41737
[IMP] estate: chapter 13 add invoicing module
yanla-odoo Mar 18, 2026
1c9f18b
[LINT] estate: fix lint issues
yanla-odoo Mar 18, 2026
1f54f25
[IMP] estate: add check for empty values
yanla-odoo Mar 18, 2026
3b33710
[IMP] estate: chapter 14 add kanban view
yanla-odoo Mar 18, 2026
cce31cd
[IMP] awesome_owl: add owl components
yanla-odoo Mar 20, 2026
84a165d
[CLN] estate: general code clean up
yanla-odoo Mar 20, 2026
3d9504e
[CLN] estate: remove unused import
yanla-odoo Mar 20, 2026
b701262
[IMP] awesome_dashboard: chapter 2 build a dashboard
yanla-odoo Mar 24, 2026
951a868
[CLN] awesome_dashboard: remove console logs
yanla-odoo Mar 24, 2026
f3ba2e0
[IMP] estate, awesome_dashboard: address PR comments
yanla-odoo Mar 25, 2026
f692ae1
[IMP] estate: the extras
yanla-odoo Mar 27, 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
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'
}
51 changes: 49 additions & 2 deletions awesome_dashboard/static/src/dashboard.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,55 @@
import { Component } from "@odoo/owl";
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 { _t } from "@web/core/l10n/translation";
import { DashboardItem } from "./dashboard/dashboard_item/dashboard_item"
import { PieChart } from "./dashboard/pie_chart/pie_chart"
import { ConfigurationDialog } from "./dashboard/configuration_dialog/configuration_dialog";
import { browser } from "@web/core/browser/browser";


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

setup() {
this.action = useService("action");
this.statsService = useService("statisticsService");
this.state = useState({
statistics: {},
includedItemIds: JSON.parse(browser.localStorage.getItem("dashboard.includedItemIds") || "[]")
});
this.dialog = useService("dialog");

this.state.statistics = this.statsService.statistics;

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

openCustomersForm() {
this.action.doAction('base.action_partner_form')
}

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

openConfigurationSettings() {
this.dialog.add(ConfigurationDialog, {
items: this.items,
initialIncludedIds: this.state.includedItemIds,
onSave: (newIds) => {
this.state.includedItemIds = newIds;
}
})
}
}

registry.category("actions").add("awesome_dashboard.dashboard", AwesomeDashboard);
registry.category("lazy_components").add("AwesomeDashboard", AwesomeDashboard);
37 changes: 36 additions & 1 deletion awesome_dashboard/static/src/dashboard.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,42 @@
<templates xml:space="preserve">

<t t-name="awesome_dashboard.AwesomeDashboard">
hello dashboard
<Layout display="{controlPanel: {} }" className="'o_dashboard h-100'">
<t t-set-slot="layout-buttons">
<button type="button" class="btn btn-primary" t-on-click="openCustomersForm">Customers</button>
<button type="button" class="btn btn-primary" t-on-click="doAction">Leads</button>
</t>
<t t-set-slot="control-panel-additional-actions">
<button type="button" t-on-click="openConfigurationSettings">
<i class="fa fa-gear"></i>
</button>
</t>

<DashboardItem>
<t t-set-slot="header">
DashboardItem default size
</t>
</DashboardItem>
<DashboardItem size="2">
<t t-set-slot="header">
DashboardItem size=2
</t>
</DashboardItem>
<div class="d-flex gap-3">
<t t-log="state.includedItemIds"/>
<t t-foreach="items" t-as="item" t-key="item.id">
<t t-log="state.includedItemIds.includes(item.id)"/>
<DashboardItem t-if="state.includedItemIds.includes(item.id)" size="item.size || 1">
<t t-set-slot="header">
<t t-esc="item.description"/>
</t>
<t t-set="itemProp" t-value="item.props ? item.props(this.state.statistics) : {'data': this.state.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,43 @@
import { _t } from "@web/core/l10n/translation";
import { Component, useState } from "@odoo/owl";
import { Dialog } from "@web/core/dialog/dialog";
import { CheckBox } from "@web/core/checkbox/checkbox";
import { browser } from "@web/core/browser/browser";


export class ConfigurationDialog extends Component {
static template = "awesome_dashboard.ConfigurationDialog";
static components = { Dialog, CheckBox };

static props = {
close: Function,
items: { type: Object },
initialIncludedIds: { type: Array },
onSave: { type: Function },
};

setup() {
this.state = useState({
includedIds: new Set(this.props.initialIncludedIds),
});
}

async _done() {
const finalIds = Array.from(this.state.includedIds);

browser.localStorage.setItem("dashboard.includedItemIds", JSON.stringify(finalIds));

this.props.onSave(finalIds);
this.props.close();
}

onChange(itemId) {
if (this.state.includedIds.has(itemId)) {
this.state.includedIds.delete(itemId);
}
else {
this.state.includedIds.add(itemId);
}
}

}
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.ConfigurationDialog">
<Dialog title="'Dashboard items configuration'">
<p t-out="props.text" class="text-prewrap"/>
<t t-foreach="this.props.items" t-as="item" t-key="item.id">
<CheckBox value="state.includedIds.has(item.id)" onChange.bind="() => this.onChange(item.id)">
<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>
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: Object
};

static defaultProps = {
size: 1
};

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8" ?>
<templates xml:space="preserve">

<t t-name="awesome_dashboard.DashboardItem">
<div class="card border-primary mb-3" t-attf-style="width: {{ 18 * props.size }}rem;">
<div t-if="props.slots.header" class="card-header">
<t t-slot="header"/>
</div>
<div class="card-body">
<t t-slot="default"/>
</div>
</div>
</t>

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

import { NumberCard } from "./number_card/number_card";
import { PieChartCard } from "./pie_chart_card/pie_chart_card";
import { registry } from "@web/core/registry";


const items = [
{
id: "number_new_orders",
description: "new orders",
Component: NumberCard,
props: (data) => ({
title: "new orders",
value: data.nb_new_orders,
})
},
{
id: "average_quantity",
description: "average quantity",
Component: NumberCard,
props: (data) => ({
title: "average quantity",
value: data.average_quantity,
})
},
{
id: "average_time",
description: "average time",
Component: NumberCard,
props: (data) => ({
title: "average time between new and sent",
value: data.average_time,
})
},
{
id: "amount_new_orders",
description: "new orders this month",
Component: NumberCard,
props: (data) => ({
title: "new orders this month",
value: data.total_amount,
})
},
{
id: "pie_chart",
description: "orders by size",
Component: PieChartCard,
size: 2,
props: (data) => ({
title: "orders by size",
values: data.orders_by_size,
})
}
]

items.forEach(item => {
registry.category("awesome_dashboard").add(item.id, item);
});
12 changes: 12 additions & 0 deletions awesome_dashboard/static/src/dashboard/dashboard_loader.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { registry } from "@web/core/registry";
import { LazyComponent } from "@web/core/assets";
import { Component, xml } from "@odoo/owl";

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

registry.category("actions").add("awesome_dashboard.dashboard", DashboardLoader);
13 changes: 13 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,13 @@
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,8 @@
<?xml version="1.0" encoding="UTF-8" ?>
<templates xml:space="preserve">

<t t-name="awesome_dashboard.NumberCard" owl="1">
<t t-esc="props.value"/>
</t>

</templates>
44 changes: 44 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,44 @@
import { Component, onWillStart, onMounted, useRef, useEffect } from "@odoo/owl";
import { loadJS } from "@web/core/assets";


export class PieChart extends Component {
static template = "awesome_dashboard.PieChart";
static props = {
data: {type: Object}
};

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

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

onMounted(() => {
this.renderChart();
});

useEffect(() => {
if (this.chart) {
this.chart.data.labels = Object.keys(this.props.data);
this.chart.data.datasets[0].data = Object.values(this.props.data);
this.chart.update();
}
},
() => [this.props.data]);
}

renderChart() {
const config = {
type: 'pie',
data: {
labels: Object.keys(this.props.data),
datasets: [{
data: Object.values(this.props.data),
}]
}
};
this.chart = new Chart(this.canvasRef.el, config);
}
}

10 changes: 10 additions & 0 deletions awesome_dashboard/static/src/dashboard/pie_chart/pie_chart.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 class="o_commission_graph o_graph_piechart">
<canvas t-ref="canvas"/>
</div>
</t>

</templates>
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
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: {
type: String,
},
values: {
type: 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" owl="1">
<PieChart data="props.values"/>
</t>

</templates>
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.o_dashboard {
background-color: lightblue;
}
Loading