Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
537def1
[ADD] estate: init commit for estate module
Nov 18, 2025
7b4d00a
[IMP] estate: database field creation
Nov 18, 2025
19e2de2
[IMP] estate: access rights
Nov 18, 2025
7fab157
[IMP] estate: fixing linting issues
Nov 19, 2025
6e5d530
[IMP] estate: Creating ui action
Nov 19, 2025
ed34bfb
[IMP] estate: fix comments and apply suggestions
Nov 19, 2025
5d325ab
[IMP] estate: improvement to the state form
Nov 19, 2025
8649303
[IMP] estate: fixing styling issues
Nov 19, 2025
76adf17
[IMP] estate: adding features to the app
Nov 19, 2025
299f69d
[FIX] estate: fix for ci checks
Nov 20, 2025
fac371f
[IMP] estate: Adding features to the estate app
Nov 21, 2025
6b9945d
[IMP] estate: added feature to compute fields
Nov 21, 2025
063a6df
[FIX] estate: fixed style issues
Nov 21, 2025
6541bd9
[IMP] estate: added accept/refuse offer
Nov 21, 2025
4dab024
[FIX] estate: Fixed ci style & tutorial issues
Nov 21, 2025
c82c0f8
[IMP] estate: added acceptance criteria
Nov 24, 2025
5012b1c
[IMP] estate: Chapter 12: Inheritance
Nov 24, 2025
0050223
[FIX] estate: fixed warnings
Nov 24, 2025
d9de3d1
[IMP] estate: added invoicing
Nov 24, 2025
7698893
[IMP] estate: adding Kanban ViewKanban
Nov 24, 2025
5143ed6
[FIX] estate: fixed spaces for linting
Nov 24, 2025
95caa6b
[FIX] estate: fixing lint and style issues
Nov 25, 2025
da766e1
[FIX] estate: fixed the field hidden
Nov 25, 2025
cffbf01
[FIX] estate: fixing code guidance issue of field
Nov 25, 2025
239ac25
[FIX] estate: fixing commits issues and added file
Nov 28, 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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ MANIFEST
*.manifest
*.spec

#editor
.vscode/

# Installer logs
pip-log.txt
pip-delete-this-directory.txt
Expand Down
5 changes: 4 additions & 1 deletion awesome_dashboard/__manifest__.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,10 @@
],
'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'
Expand Down
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.

59 changes: 59 additions & 0 deletions awesome_dashboard/static/src/dashboard/dashboard.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
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";
import { DashboardConfigDialog } from "./dashboard_config_dialog";
import "./dashboard_items";
import "./statistics_service";

const DASHBOARD_CONFIG_KEY = "awesome_dashboard.config";

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

setup() {
this.action = useService("action");
this.dialog = useService("dialog");
const statisticsService = useService("awesome_dashboard.statistics");
this.statistics = useState(statisticsService.statistics);

const configStr = localStorage.getItem(DASHBOARD_CONFIG_KEY);
this.hiddenItems = configStr ? JSON.parse(configStr) : [];

const allItems = registry.category("awesome_dashboard").getAll();
this.items = allItems.filter(item => !this.hiddenItems.includes(item.id));
}

openConfiguration() {
this.dialog.add(DashboardConfigDialog, {
currentConfig: this.hiddenItems,
onApply: (hiddenItems) => {
this.hiddenItems = hiddenItems;
localStorage.setItem(DASHBOARD_CONFIG_KEY, JSON.stringify(hiddenItems));

const allItems = registry.category("awesome_dashboard").getAll();
this.items = allItems.filter(item => !this.hiddenItems.includes(item.id));
},
});
}

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

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

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: #fafafa;
}
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="{controlPanel: {}}" className="'o_dashboard h-100'">
<t t-set-slot="control-panel-additional-actions">
<button class="btn btn-primary" t-on-click="openCustomers">Customers</button>
<button class="btn btn-primary ms-2" t-on-click="openLeads">Leads</button>
<button class="btn btn-secondary ms-2" t-on-click="openConfiguration" title="Configure Dashboard">
<i class="fa fa-cog"/>
</button>
</t>
<div class="p-3 overflow-auto">
<div class="d-flex flex-wrap">
<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>
</div>
</Layout>
</t>

</templates>
35 changes: 35 additions & 0 deletions awesome_dashboard/static/src/dashboard/dashboard_config_dialog.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { Component } from "@odoo/owl";
import { Dialog } from "@web/core/dialog/dialog";
import { registry } from "@web/core/registry";

export class DashboardConfigDialog extends Component {
static template = "awesome_dashboard.DashboardConfigDialog";
static components = { Dialog };
static props = {
close: Function,
onApply: Function,
currentConfig: Array,
};

setup() {
this.allItems = registry.category("awesome_dashboard").getAll();
this.hiddenItems = new Set(this.props.currentConfig);
}

toggleItem = (itemId) => {
if (this.hiddenItems.has(itemId)) {
this.hiddenItems.delete(itemId);
} else {
this.hiddenItems.add(itemId);
}
}

isItemVisible = (itemId) => {
return !this.hiddenItems.has(itemId);
}

apply = () => {
this.props.onApply(Array.from(this.hiddenItems));
this.props.close();
}
}
30 changes: 30 additions & 0 deletions awesome_dashboard/static/src/dashboard/dashboard_config_dialog.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8" ?>
<templates xml:space="preserve">

<t t-name="awesome_dashboard.DashboardConfigDialog">
<Dialog title="'Configure Dashboard'" contentClass="'o_dashboard_config_dialog'">
<div class="mb-3">
<h5>Select items to display:</h5>
<t t-foreach="allItems" t-as="item" t-key="item.id">
<div class="form-check">
<input
type="checkbox"
class="form-check-input"
t-att-id="'item_' + item.id"
t-att-checked="isItemVisible(item.id)"
t-on-change="() => toggleItem(item.id)"
/>
<label class="form-check-label" t-att-for="'item_' + item.id">
<t t-esc="item.description"/>
</label>
</div>
</t>
</div>
<t t-set-slot="footer">
<button class="btn btn-secondary" t-on-click="props.close">Cancel</button>
<button class="btn btn-primary" t-on-click="apply">Apply</button>
</t>
</Dialog>
</t>

</templates>
14 changes: 14 additions & 0 deletions awesome_dashboard/static/src/dashboard/dashboard_item.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
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 `${18 * size}rem`;
}
}
10 changes: 10 additions & 0 deletions awesome_dashboard/static/src/dashboard/dashboard_item.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.DashboardItem">
<div class="card m-2 p-3 bg-white" t-attf-style="width: {{ width }}; min-height: 200px;">
<t t-slot="default"/>
</div>
</t>

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

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

dashboardRegistry.add("nb_new_orders", {
id: "nb_new_orders",
description: "New Orders",
Component: NumberCard,
props: (data) => ({
title: "New Orders",
value: data.nb_new_orders,
color: "primary",
}),
});

dashboardRegistry.add("total_amount", {
id: "total_amount",
description: "Total Amount",
Component: NumberCard,
props: (data) => ({
title: "Total Amount",
value: data.total_amount,
color: "success",
}),
});

dashboardRegistry.add("average_quantity", {
id: "average_quantity",
description: "Average Quantity",
Component: NumberCard,
props: (data) => ({
title: "Average Quantity",
value: data.average_quantity,
color: "info",
}),
});

dashboardRegistry.add("nb_cancelled_orders", {
id: "nb_cancelled_orders",
description: "Cancelled Orders",
Component: NumberCard,
props: (data) => ({
title: "Cancelled Orders",
value: data.nb_cancelled_orders,
color: "danger",
}),
});

dashboardRegistry.add("average_time", {
id: "average_time",
description: "Average Time",
Component: NumberCard,
props: (data) => ({
title: "Avg Time (hours)",
value: data.average_time,
color: "warning",
}),
});

dashboardRegistry.add("orders_by_size", {
id: "orders_by_size",
description: "T-Shirt Sizes",
Component: PieChartCard,
size: 2,
props: (data) => ({
title: "T-Shirt Sizes",
data: data.orders_by_size,
}),
});
10 changes: 10 additions & 0 deletions awesome_dashboard/static/src/dashboard/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, String],
color: { type: String, optional: true },
};
}
13 changes: 13 additions & 0 deletions awesome_dashboard/static/src/dashboard/number_card.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8" ?>
<templates xml:space="preserve">

<t t-name="awesome_dashboard.NumberCard">
<div class="text-center">
<div class="fs-1 fw-bold" t-attf-class="text-{{ props.color || 'primary' }}">
<t t-esc="props.value"/>
</div>
<div class="text-muted"><t t-esc="props.title"/></div>
</div>
</t>

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

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

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");
new Chart(ctx, {
type: "pie",
data: {
labels: Object.keys(this.props.data),
datasets: [{
label: "T-Shirt Sizes",
data: Object.values(this.props.data),
backgroundColor: [
"#FF6384",
"#36A2EB",
"#FFCE56",
"#4BC0C0",
"#9966FF",
],
}],
},
options: {
responsive: true,
maintainAspectRatio: false,
},
});
}
}
10 changes: 10 additions & 0 deletions awesome_dashboard/static/src/dashboard/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 style="height: 100%;">
<canvas t-ref="canvas"/>
</div>
</t>

</templates>
11 changes: 11 additions & 0 deletions awesome_dashboard/static/src/dashboard/pie_chart_card.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { Component } from "@odoo/owl";
import { PieChart } from "./pie_chart";

export class PieChartCard extends Component {
static template = "awesome_dashboard.PieChartCard";
static components = { PieChart };
static props = {
title: String,
data: Object,
};
}
Loading