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
2e0af73
[ADD] estate: new 'estate' module
haahi-odoo Oct 30, 2025
ca8c609
[ADD] estate: new 'estate' module
haahi-odoo Oct 30, 2025
7a97626
[IMP] estate: Configure field defaults and UI menus
haahi-odoo Nov 5, 2025
8a7e9f8
[IMP] estate : Added list & form view
haahi-odoo Nov 6, 2025
d53dbb0
[FIX] estate : mismatch field name
haahi-odoo Nov 6, 2025
0f04867
[FIX] estate: indentation error
haahi-odoo Nov 6, 2025
167babd
[IMP] estate: Implement list, form, and search views with grouping fu…
haahi-odoo Nov 6, 2025
0001158
[IMP] estate: Enhanced Data Models and Functionalities
haahi-odoo Nov 7, 2025
dbb2ea3
[IMP] estate: added computed fields,inverse fun & onchange
haahi-odoo Nov 10, 2025
ca55e88
[FIX] estate: fixed depreciated sql contraint
haahi-odoo Nov 10, 2025
83b1935
[FIX] estate: fixed depreciated sql contraint
haahi-odoo Nov 10, 2025
f7cddde
[IMP] estate: added Inline Views, widgets, list order, Attributes, & …
haahi-odoo Nov 11, 2025
6436f6c
[IMP] estate: Added stat button, related fields & business logic updates
haahi-odoo Nov 12, 2025
de6b813
[IMP] estate : Added invoicing for sold properties and Kanban views
haahi-odoo Nov 17, 2025
cc9415c
[IMP] estate: code formation as per odoo guidelines
haahi-odoo Nov 17, 2025
549c84e
[IMP] awesome_owl: add counter, card components and todo lists
haahi-odoo Nov 21, 2025
c3096f6
[IMP] awesome_owl: added new layout, buttons and a dashboard item
haahi-odoo Nov 24, 2025
6ddbe47
[IMP] awesome_owl: added statistics, server calls and a service
haahi-odoo Nov 26, 2025
3c55826
[IMP] awesome_dashboard: added item list and pie chart
haahi-odoo Nov 27, 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
467 changes: 467 additions & 0 deletions .eslintignore

Large diffs are not rendered by default.

69 changes: 69 additions & 0 deletions .eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
{
"extends": ["eslint:recommended", "plugin:prettier/recommended"],
"parserOptions": {
"sourceType": "module",
"ecmaVersion": 2022
},
"env": {
"browser": true,
"es2022": true,
"qunit": true
},
"rules": {
"prettier/prettier": ["off", {
"tabWidth": 4,
"semi": true,
"singleQuote": false,
"printWidth": 100,
"endOfLine": "auto"
}],
"no-undef": "off",
"no-restricted-globals": ["off", "event", "self"],
"no-const-assign": ["off"],
"no-debugger": ["off"],
"no-dupe-class-members": ["off"],
"no-dupe-keys": ["off"],
"no-dupe-args": ["off"],
"no-dupe-else-if": ["off"],
"no-unsafe-negation": ["off"],
"no-duplicate-imports": ["off"],
"valid-typeof": ["off"],
"no-unused-vars": ["off", { "vars": "all", "args": "none", "ignoreRestSiblings": false, "caughtoffs": "all" }],
"curly": ["off", "all"],
"no-restricted-syntax": ["off", "PrivateIdentifier"],
"prefer-const": ["off", {
"destructuring": "all",
"ignoreReadBeforeAssign": true
}],
"arrow-body-style": ["off", "as-needed"]
},
"globals": {
"odoo": "readonly",
"$": "readonly",
"jQuery": "readonly",
"Chart": "readonly",
"fuzzy": "readonly",
"StackTrace": "readonly",
"QUnit": "readonly",
"luxon": "readonly",
"py": "readonly",
"FullCalendar": "readonly",
"globalThis": "readonly",
"ScrollSpy": "readonly",
"module": "readonly",
"chai": "readonly",
"describe": "readonly",
"it": "readonly",
"mocha": "readonly",
"DOMPurify": "readonly",
"Prism": "readonly",

"Alert": "readonly",
"Collapse": "readonly",
"Dropdown": "readonly",
"Modal": "readonly",
"Offcanvas": "readonly",
"Popover": "readonly",
"Tooltip": "readonly"
}
}
8 changes: 6 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,7 @@ __pycache__/
build/
develop-eggs/
dist/
downloads/
eggs/
downloads/eggs/
.eggs/
lib/
lib64/
Expand Down Expand Up @@ -127,3 +126,8 @@ dmypy.json

# Pyre type checker
.pyre/
.eslintignore
.eslintrc.json
jsconfig.json
package.json
node_modules/
6 changes: 5 additions & 1 deletion awesome_dashboard/__manifest__.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,11 @@
],
'assets': {
'web.assets_backend': [
'awesome_dashboard/static/src/**/*',
'awesome_dashboard/static/src/dashboard_action.js',
'awesome_dashboard/static/src/statistics_service.js',
],
'awesome_dashboard.dashboard': [
'awesome_dashboard/static/src/dashboard/**/*',
],
},
'license': 'AGPL-3'
Expand Down
4 changes: 1 addition & 3 deletions awesome_dashboard/controllers/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1 @@
# -*- coding: utf-8 -*-

from . import controllers
from . import controllers
5 changes: 2 additions & 3 deletions awesome_dashboard/controllers/controllers.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
# -*- coding: utf-8 -*-

import logging
import random

Expand Down Expand Up @@ -30,7 +28,8 @@ def get_statistics(self):
'm': random.randint(0, 150),
's': random.randint(0, 150),
'xl': random.randint(0, 150),
'l': random.randint(0, 150),
'xxl': random.randint(0, 150),
},
'total_amount': random.randint(100, 1000)
}

Binary file added awesome_dashboard/static/description/icon.png
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.

Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/** @odoo-module **/

import { Component, useState } from "@odoo/owl";
import { Dialog } from "@web/core/dialog/dialog";
import { useService } from "@web/core/utils/hooks";
import { registry } from "@web/core/registry";

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

setup() {
this.user = useService("user");
this.items = registry.category("awesome_dashboard").getEntries().map(([id, item]) => ({
id,
description: item.description,
enabled: !this.getDisabledItems().includes(id),
}));
this.state = useState({ items: this.items });
}

getDisabledItems() {
return this.user.settings?.awesome_dashboard_disabled_items || [];
}

toggleItem(item) {
item.enabled = !item.enabled;
}

async save() {
const disabledItems = this.state.items
.filter((item) => !item.enabled)
.map((item) => item.id);

await this.user.setUserSettings("awesome_dashboard_disabled_items", disabledItems);
window.location.reload();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8" ?>
<templates xml:space="preserve">
<t t-name="awesome_dashboard.ConfigDialog">
<Dialog title="'Dashboard Configuration'">
<div class="d-flex flex-column gap-2">
<div t-foreach="state.items" t-as="item" t-key="item.id" class="form-check">
<input class="form-check-input" type="checkbox" t-att-id="item.id" t-att-checked="item.enabled" t-on-change="() => this.toggleItem(item)"/>
<label class="form-check-label" t-att-for="item.id">
<t t-esc="item.description"/>
</label>
</div>
</div>
<t t-set-slot="footer">
<button class="btn btn-primary" t-on-click="save">Apply</button>
<button class="btn btn-secondary" t-on-click="props.close">Cancel</button>
</t>
</Dialog>
</t>
</templates>

54 changes: 54 additions & 0 deletions awesome_dashboard/static/src/dashboard/dashboard.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
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 { ConfigDialog } from "./config_dialog/config_dialog";
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.user = useService("user");
this.statistics = useState(useService("awesome_dashboard.statistics"));
this.display = {
controlPanel: {},
};
this.items = registry.category("awesome_dashboard").getEntries().map(([id, item]) => ({ id, ...item }));
}

get visibleItems() {
const disabledItems = this.getDisabledItems();
return this.items.filter((item) => !disabledItems.includes(item.id));
}

getDisabledItems() {
return this.user.settings?.awesome_dashboard_disabled_items || [];
}

openConfiguration() {
this.dialog.add(ConfigDialog, {});
}

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

openLeads() {
this.action.doAction({
type: "ir.actions.act_window",
name: "All 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: gray;
}
20 changes: 20 additions & 0 deletions awesome_dashboard/static/src/dashboard/dashboard.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8" ?>
<templates xml:space="preserve">
<t t-name="awesome_dashboard.AwesomeDashboard">
<Layout display="display" className="'o_dashboard h-100'">
<t t-set-slot="control-panel-buttons">
<button class="btn btn-primary" t-on-click="openCustomerView">Customers</button>
<button class="btn btn-primary" t-on-click="openLeads">Leads</button>
<button class="btn btn-secondary fa fa-cog" t-on-click="openConfiguration" title="Configuration"/>
</t>
<div class="d-flex flex-wrap">
<t t-foreach="visibleItems" t-as="item" t-key="item.id">
<DashboardItem size="item.size || 1" class="item.size &lt; 2 ? 'col-12 col-md-4' : 'col-12 col-md-8'">
<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,5 @@
/** @odoo-module **/

import "./dashboard_item/dashboard_item";
import "./pie_chart/pie_chart";
import "./dashboard";
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { Component } from "@odoo/owl";

export class DashboardItem extends Component {
static template = "awesome_dashboard.DashboardItem";
static props = {
slots: {
type: Object,
shape: {
default: Object,
},
optional: true,
},
Component: {
type: Function,
optional: true,
},
size: {
type: Number,
default: 1,
optional: true,
},
};
}
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 m-2 border-dark" t-attf-style="width: {{18*props.size}}rem;">
<div class="card-body">
<t t-if="props.Component">
<t t-component="props.Component"/>
</t>
<t t-else="">
<t t-slot="default"/>
</t>
</div>
</div>
</t>
</templates>
61 changes: 61 additions & 0 deletions awesome_dashboard/static/src/dashboard/dashboard_items.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { registry } from "@web/core/registry";
import { NumberCard } from "./number_card/number_card";
import { PieChartCard } from "./pie_chart_card/pie_chart_card";
import { _t } from "@web/core/l10n/translation";

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

dashboardItems.add("average_quantity", {
description: _t("Average amount of t-shirt"),
Component: NumberCard,
props: (data) => ({
title: _t("Average amount of t-shirt by order this month"),
value: data.average_quantity,
}),
});

dashboardItems.add("average_time", {
description: _t("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,
}),
});

dashboardItems.add("nb_new_orders", {
description: _t("Number of new orders"),
Component: NumberCard,
props: (data) => ({
title: _t("Number of new orders this month"),
value: data.nb_new_orders,
}),
});

dashboardItems.add("nb_cancelled_orders", {
description: _t("Number of cancelled orders"),
Component: NumberCard,
props: (data) => ({
title: _t("Number of cancelled orders this month"),
value: data.nb_cancelled_orders,
}),
});

dashboardItems.add("total_amount", {
description: _t("Total amount of new orders"),
Component: NumberCard,
props: (data) => ({
title: _t("Total amount of new orders this month"),
value: data.total_amount,
}),
});

dashboardItems.add("orders_by_size", {
description: _t("Orders by size"),
Component: PieChartCard,
size: 2,
props: (data) => ({
title: _t("Orders by size"),
data: data.orders_by_size,
}),
});
Loading