Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
88181a3
[ADD] Chapter 2&3 (Estate)
sopat-odoo Mar 10, 2026
15ba5f8
[IMP] Estate:Fixed Issues and Completed Chapter-4
sopat-odoo Mar 11, 2026
1039a39
[IMP] Estate:Partially Completed Chapter-5
sopat-odoo Mar 12, 2026
aab2bd1
[IMP] Estate:Completed Chapter-5
sopat-odoo Mar 13, 2026
ae34671
[IMP] Estate:Errors Fixed & Completed Chapter-6
sopat-odoo Mar 16, 2026
2db9029
[IMP] Estate: Completed Chapter-7
sopat-odoo Mar 18, 2026
1f35744
[IMP] estate: Partially Completed Chapter-8
sopat-odoo Mar 30, 2026
f5e96ec
[IMP] estate: Completed Chapter-8
sopat-odoo Apr 3, 2026
228fe82
[IMP] estate: Completed Chapter-9
sopat-odoo Apr 4, 2026
7b7fa47
[IMP] estate: Completed Ch-10
sopat-odoo Apr 5, 2026
a51fbd7
[IMP] estate: Partially Completed Ch-11
sopat-odoo Apr 7, 2026
6228369
[IMP] estate: Completed Ch-11
sopat-odoo Apr 9, 2026
1cd13b4
[IMP] estate: Completed Ch-12
sopat-odoo Apr 13, 2026
b58584b
[IMP] estate: Completed Task of Scheduling Visits
sopat-odoo Apr 14, 2026
38b0320
[IMP] estate: Completed SLA Task
sopat-odoo Apr 16, 2026
176abb4
[ADD] estate: Completed Chapter-13
sopat-odoo Apr 16, 2026
1857156
[IMP] estate: Completed Chapter-14
sopat-odoo Apr 16, 2026
331f9b6
[IMP] estate: Coding Guidelines Improvements
sopat-odoo Apr 17, 2026
62b91ad
[ADD] awesome_owl:Implement core OWL components and reactivity features
sopat-odoo May 6, 2026
d290f4c
[IMP] estate:add chatter in form view and email template on sold
sopat-odoo May 6, 2026
6b3a11d
[ADD] estate_crm:auto generate crm lead on offer and update stage
sopat-odoo May 7, 2026
8879c0e
[ADD] awesome_dashboard:build a dynamic dashboard
sopat-odoo May 9, 2026
ca889d2
[IMP] estate: Added review task
sopat-odoo May 14, 2026
b71c0c4
[ADD] bom_forecast: clean bom overview forecast view
sopat-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
7 changes: 6 additions & 1 deletion 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 @@ -21,10 +20,16 @@
'data': [
'views/views.xml',
],

'assets': {
'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.

102 changes: 102 additions & 0 deletions awesome_dashboard/static/src/dashboard/dashboard.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
/** @odoo-module **/
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/dashboard_item";
import { Dialog } from "@web/core/dialog/dialog";
import { CheckBox } from "@web/core/checkbox/checkbox";
import { browser } from "@web/core/browser/browser";
import "./statistics_service";
import "./dashboard_items";

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

setup() {
this.action = useService("action");
this.dialog = useService("dialog");
this.display = { controlPanel: {} };
this.stats = useService("awesome_dashboard.statistics");

this.state = useState({
disabledItems: JSON.parse(
browser.localStorage.getItem("disabledDashboardItems") || "[]"
)
});

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

get items() {
return this.allItems.filter(
item => !this.state.disabledItems.includes(item.id)
);
}

openConfiguration() {
this.dialog.add(ConfigurationDialog, {
items: this.allItems,
disabledItems: this.state.disabledItems,
onUpdate: this.updateConfiguration.bind(this),
});
}

updateConfiguration(newDisabledItems) {
this.state.disabledItems = newDisabledItems;
browser.localStorage.setItem(
"disabledDashboardItems",
JSON.stringify(newDisabledItems)
);
}

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"]],
});
}
}

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

setup() {
const disabled = Array.isArray(this.props.disabledItems)
? this.props.disabledItems
: [];

this.items = useState(
this.props.items.map(item => ({
...item,
enabled: !disabled.includes(item.id),
}))
);
}

onChange(checked, item) {
item.enabled = checked;
const disabled = this.items
.filter(i => !i.enabled)
.map(i => i.id);
this.props.onUpdate(disabled);
}

done() {
this.props.close();
}
}
registry
.category("lazy_components")
.add("awesome_dashboard.AwesomeDashboard", AwesomeDashboard);
10 changes: 10 additions & 0 deletions awesome_dashboard/static/src/dashboard/dashboard.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
.o_dashboard {
background-color: rgb(16, 18, 20);
}

@media (max-width: 768px) {

.o_dashboard_item {
width: 100% !important;
}
}
58 changes: 58 additions & 0 deletions awesome_dashboard/static/src/dashboard/dashboard.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<?xml version="1.0" encoding="UTF-8" ?>
<templates xml:space="preserve">

<t t-name="awesome_dashboard.AwesomeDashboard">
<Layout display="{ controlPanel: {} }" className="'o_dashboard'">
<t t-set-slot="layout-buttons">
<button class="btn btn-primary me-2" t-on-click="openCustomers">
Customers
</button>

<button class="btn btn-secondary" t-on-click="openLeads">
Leads
</button>
</t>

<t t-set-slot="control-panel-additional-actions">
<button class="btn btn-secondary fa fa-cog"
t-on-click="openConfiguration"/>
</t>

<div t-if="stats" class="d-flex flex-wrap gap-3">
<t t-foreach="items" t-as="item" t-key="item.id">
<DashboardItem size="item.size || 1">
<t t-set="itemProps"
t-value="item.props ? item.props(stats) : { data: stats }"/>

<t t-component="item.Component"
t-props="itemProps"/>

</DashboardItem>
</t>
</div>
</Layout>
</t>

<t t-name="awesome_dashboard.ConfigurationDialog">
<Dialog title="'Dashboard Configuration'">
<div class="p-3">
<p>Which cards do you want to see?</p>
<t t-foreach="items" t-as="item" t-key="item.id">
<CheckBox
value="item.enabled"
onChange="(checked) => this.onChange(checked, item)">
<span>
<t t-esc="item.description"/>
</span>
</CheckBox>
</t>
</div>

<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,9 @@
import { Component } from "@odoo/owl";

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

static slots = {
default: {},
};
}
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.DashboardItem">

<div class="p-3 border bg-white" t-att-style="'width: ' + (18 * props.size) + 'rem;'">
<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 @@
/** @odoo-module **/
import { dashboardItemRegistry } from "./dashboard_registry";
import { NumberCard } from "./number_card/number_card";
import { PieChartCard } from "./pie_chart_card/pie_chart_card";
import { _t } from "@web/core/l10n/translation";

dashboardItemRegistry.add("new_orders", {
id: "new_orders",
description: _t("New orders this month"),
Component: NumberCard,
size: 1,
props: (data) => ({
title: _t("Number of new orders"),
value: data?.nb_new_orders || 0,
}),
});

dashboardItemRegistry.add("total_amount", {
id: "total_amount",
description: _t("Total amount orders this month"),
Component: NumberCard,
size: 1,
props: (data) => ({
title: _t("Total amount"),
value: data?.total_amount || 0,
}),
});

dashboardItemRegistry.add("avg_quantity", {
id: "avg_quantity",
description: _t("Average amount of t-shirt"),
Component: NumberCard,
size: 1,
props: (data) => ({
title: _t("Average quantity"),
value: data?.average_quantity || 0,
}),
});

dashboardItemRegistry.add("average_time", {
id: "average_time",
description: _t("Average time new to sent/cancelled"),
Component: NumberCard,
size: 1,
props: (data) => ({
title: _t("Average time"),
value: data?.average_time || 0,
}),
});

dashboardItemRegistry.add("nb_cancelled_orders", {
id: "nb_cancelled_orders",
description: _t("Cancelled orders this month"),
Component: NumberCard,
size: 1,
props: (data) => ({
title: _t("Number of cancelled orders"),
value: data?.nb_cancelled_orders || 0,
}),
});

dashboardItemRegistry.add("pie_chart", {
id: "pie_chart",
description: _t("Shirt orders by size"),
Component: PieChartCard,
size: 2,
props: (data) => ({
title: _t("Orders by size"),
data: data?.orders_by_size || {},
}),
});
3 changes: 3 additions & 0 deletions awesome_dashboard/static/src/dashboard/dashboard_registry.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { registry } from "@web/core/registry";

export const dashboardItemRegistry = registry.category("awesome_dashboard.items");
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: String,
value: { type: Number, optional: true },
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<templates xml:space="preserve">

<t t-name="awesome_dashboard.NumberCard">
<div>
<div><t t-esc="props.title"/></div>
<h3><t t-esc="props.value"/></h3>
</div>
</t>
</templates>
34 changes: 34 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,34 @@
import { Component, onWillStart, onMounted, useRef } 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("chart");
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: [{
data: Object.values(this.props.data),
}],
},
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8" ?>
<templates xml:space="preserve">

<t t-name="awesome_dashboard.PieChart">
<canvas t-ref="chart" style="max-width: 300px;"/>
</t>
</templates>
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
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: String,
data: 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">

<t t-name="awesome_dashboard.PieChartCard">
<div>
<div><t t-esc="props.title"/></div>
<PieChart data="props.data"/>
</div>
</t>

</templates>
Loading