Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
9a8f241
[IMP] awesomeclicker: add editor metadata in Python file
jupao-odoo Feb 16, 2026
169daed
[ADD] estate: initialize tutorial
jupao-odoo Feb 16, 2026
aa17f8b
[ADD] estate: define list, form and search views for properties
jupao-odoo Feb 17, 2026
e44c44e
[IMP] estate: add property types, tags and offers models
jupao-odoo Feb 17, 2026
7b475b6
[IMP] estate: chapter 8 - computed fields and onchange
jupao-odoo Feb 17, 2026
8b7b934
[REF] estate: add action buttons for properties and offers
jupao-odoo Feb 17, 2026
1b855f7
[REF] estate: add Python constraints for data consistency
jupao-odoo Feb 18, 2026
e30a69a
[IMP] estate: enhance property views, search and stats
jupao-odoo Feb 18, 2026
4508a76
[IMP] estate: add business logic on CRUD + inherit res.users with pro…
jupao-odoo Feb 19, 2026
71d9a81
[CLN] estate: ignore local config files
jupao-odoo Feb 19, 2026
2cc58a4
[FIX] estate: several fixes from PR remarks
jupao-odoo Feb 19, 2026
9b65e0a
[REF] estate_account: link estate and account, create invoices on pro…
jupao-odoo Feb 19, 2026
a2af77e
[IMP] estate: add kanban view for properties
jupao-odoo Feb 19, 2026
c0cc56a
[FIX] module_name: address PR feedback
jupao-odoo Feb 19, 2026
1fc90ff
[FIX] estate_account: restore correct invoice price and add license
jupao-odoo Feb 19, 2026
98b250c
[FIX] estate: apply PR review feedback
jupao-odoo Feb 20, 2026
3304ef2
[CLN] estate: fix code style and formatting
jupao-odoo Feb 20, 2026
3028c94
[FIX] ci: fix failing CI pipeline
jupao-odoo Feb 20, 2026
dffa3b2
[FIX] estate: remove unused elements from Kanban view
jupao-odoo Feb 20, 2026
e205ffa
[FIX] estate_account: correct admin fees and use float_compare
jupao-odoo Feb 20, 2026
3b255e1
[FIX] estate: keep status field invisible for offer decorations
jupao-odoo Feb 20, 2026
f7c72e6
[FIX] estate: remove invisible columns from view
jupao-odoo Feb 20, 2026
7b20935
[IMP] awesome_owl: add reactive counter in Playground component
jupao-odoo Feb 23, 2026
baeca0a
[ADD] awesome_owl: introduce reusable Card component with props
jupao-odoo Feb 23, 2026
7c23553
[IMP] awesome_owl: support HTML content in Card component
jupao-odoo Feb 23, 2026
3b625a7
[IMP] awesome_owl: add props validation to Card component
jupao-odoo Feb 23, 2026
65b7c35
[IMP] awesome_owl: implement sum of two Counter components via callba…
jupao-odoo Feb 23, 2026
1c84d45
[ADD] awesome_owl: implement TodoList and TodoItem components with dy…
jupao-odoo Feb 23, 2026
5fe6e52
[IMP] awesome_owl: allow adding new todos in TodoList
jupao-odoo Feb 23, 2026
06b7abd
[FIX] awesome_owl: correct issues in Card and Counter components
jupao-odoo Feb 23, 2026
3293cbc
[FIX] awesome_owl: fix Card component and EstateProperty issues
jupao-odoo Feb 23, 2026
c5e2e5c
[IMP] awesome_owl: add autofocus, toggle, and delete features to Todo…
jupao-odoo Feb 23, 2026
6304f12
[IMP] awesome_owl: make Card generic with slots and add toggle feature
jupao-odoo Feb 23, 2026
8b8b108
[FIX] estate: fix flake8 errors in estate_property
jupao-odoo Feb 23, 2026
4ac34d7
[FIX] awesome_owl & estate: fix TodoList, TodoItem, utils, Card, and …
jupao-odoo Feb 23, 2026
a513edc
[FIX] awesome_owl : fix component and property issues
jupao-odoo Feb 23, 2026
173ddac
[FIX] awesome_owl : fix component and property issues
jupao-odoo Feb 23, 2026
d05103d
[IMP] awesome_dashboard: add Layout to AwesomeDashboard
jupao-odoo Feb 23, 2026
ed4ed74
[IMP] awesome_dashboard: add quick navigation buttons with action ser…
jupao-odoo Feb 24, 2026
4f5dce2
[ADD] awesome_dashboard: introduce reusable DashboardItem component
jupao-odoo Feb 24, 2026
65c7bc2
[IMP] awesome_dashboard: fetch and display dashboard statistics
jupao-odoo Feb 24, 2026
bc8312e
[IMP] awesome_dashboard: cache statistics with dedicated service
jupao-odoo Feb 24, 2026
d296649
[REF] awesome_dashboard: move control panel buttons to Layout slot
jupao-odoo Feb 24, 2026
ce585d6
[ADD] awesome_dashboard: add PieChart component with lazy-loaded Char…
jupao-odoo Feb 25, 2026
4b4f0bb
[IMP] awesome_dashboard: add live refresh to statistics service
jupao-odoo Feb 25, 2026
be259d0
[IMP] awesome_dashboard: enable lazy loading for dashboard
jupao-odoo Feb 25, 2026
483bd50
[REF] awesome_dashboard: make dashboard generic with dynamic items
jupao-odoo Feb 25, 2026
08f9b67
[IMP] awesome_dashboard: make dashboard extensible with registry
jupao-odoo Feb 25, 2026
df82d76
[IMP] awesome_dashboard: add customizable dashboard items with local …
jupao-odoo Feb 25, 2026
492ac07
[REF] awesome_dashboard: remove unnecessary @odoo-module headers
jupao-odoo Feb 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
129 changes: 0 additions & 129 deletions .gitignore

This file was deleted.

1 change: 0 additions & 1 deletion awesome_dashboard/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
# -*- coding: utf-8 -*-

from . import controllers
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', # loader
],
'awesome_dashboard.dashboard': [
'awesome_dashboard/static/src/dashboard/**/*',
'awesome_dashboard/static/src/dashboard/xml/**/*',
Copy link

@ltinel ltinel Feb 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need for this :) By the way, XML and JS files don't need to be in separate folders. If you want, you can create one folder per component (e.g. move both pie_chart.xml and pie_chart.js into a pie_chart/ folder), but it's better to have the XML and JS files of the same component next to each other.

],
},
'license': 'AGPL-3'
Expand Down
2 changes: 1 addition & 1 deletion awesome_dashboard/controllers/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
# -*- coding: utf-8 -*-

from . import controllers
from . import controllers
2 changes: 1 addition & 1 deletion awesome_dashboard/controllers/controllers.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

logger = logging.getLogger(__name__)


class AwesomeDashboard(http.Controller):
@http.route('/awesome_dashboard/statistics', type='jsonrpc', auth='user')
def get_statistics(self):
Expand All @@ -33,4 +34,3 @@ def get_statistics(self):
},
'total_amount': random.randint(100, 1000)
}

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.

48 changes: 48 additions & 0 deletions awesome_dashboard/static/src/dashboard/awesome_dashboard.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { Component, useState } from "@odoo/owl";
import { useService } from "@web/core/utils/hooks";
import { statisticsStore } from "./services/statistics_service";
import { DashboardItem } from "./dashboard_item";
import { dashboardItemRegistry } from "./dashboard_registry";
import { DashboardSettingsDialog } from "./dashboard_settings_dialog";

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

static props = {
onSettingsOpen: Function, // callback to expose settings API
};

setup() {
this.dialog = useService("dialog");
this.stats = useState(statisticsStore);

this.allItems = dashboardItemRegistry.getAll();

// reactive items
this.state = useState({
items: this.getFilteredItems(),
});

// expose the API
if (this.props.onSettingsOpen) {
this.props.onSettingsOpen(() => this.openSettings());
}
}

getFilteredItems() {
const removed = JSON.parse(
localStorage.getItem("awesome_dashboard.removed_items") || "[]"
);
return this.allItems.filter(item => !removed.includes(item.id));
}

openSettings() {
this.dialog.add(DashboardSettingsDialog, {
items: this.allItems,
onApply: () => {
this.state.items = this.getFilteredItems();
},
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { Component } from "@odoo/owl";
import { Layout } from "@web/search/layout";
import { AwesomeDashboard } from "./awesome_dashboard";
import { registry } from "@web/core/registry";

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

setup() {
this.action = this.env.services.action;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
this.action = this.env.services.action;
this.action = useService("action");

this.dashboardOpenSettings = null;

this.setDashboardSettingsCallback = (fn) => {
this.dashboardOpenSettings = fn;
};
}

openSettings() {
if (this.dashboardOpenSettings) {
this.dashboardOpenSettings();
}
}

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

get layoutProps() {
return { display: { controlPanel: {}, className: "o_dashboard h-100" } };
}
}

// Register for lazy loading
registry.category("lazy_components").add("AwesomeDashboard", AwesomeDashboardWrapper);
7 changes: 7 additions & 0 deletions awesome_dashboard/static/src/dashboard/dashboard.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
.o_dashboard {
background-color: grey;
}

.o_dashboard .card {
border-radius: 12px;
}
18 changes: 18 additions & 0 deletions awesome_dashboard/static/src/dashboard/dashboard_item.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Component } from "@odoo/owl";

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

// Default value
static defaultProps = {
size: 1, // if parent doesn't provide `size`, it will be 1
};

static props = {
size: { type: Number, optional: true },
};

get width() {
return `width: ${18 * this.props.size}rem`;
}
}
65 changes: 65 additions & 0 deletions awesome_dashboard/static/src/dashboard/dashboard_items.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { dashboardItemRegistry } from "./dashboard_registry";
import { NumberCard } from "./number_card";
import { PieChartCard } from "./pie_chart_card";

dashboardItemRegistry.add("new_orders", {
id: "new_orders",
description: "New orders this month",
Component: NumberCard,
props: (stats) => ({
title: "New Orders",
value: stats.nb_new_orders,
}),
});

dashboardItemRegistry.add("total_amount", {
id: "total_amount",
description: "Total amount this month",
Component: NumberCard,
props: (stats) => ({
title: "Total Amount",
value: stats.total_amount,
}),
});

dashboardItemRegistry.add("average_quantity", {
id: "average_quantity",
description: "Average T-Shirts per order",
Component: NumberCard,
props: (stats) => ({
title: "Avg T-Shirts / Order",
value: stats.average_quantity,
}),
});

dashboardItemRegistry.add("cancelled_orders", {
id: "cancelled_orders",
description: "Cancelled orders this month",
Component: NumberCard,
props: (stats) => ({
title: "Cancelled Orders",
value: stats.nb_cancelled_orders,
}),
});

dashboardItemRegistry.add("avg_time", {
id: "avg_time",
description: "Average processing time",
Component: NumberCard,
size: 2,
props: (stats) => ({
title: "Avg Processing Time (hours)",
value: stats.average_time,
}),
});

dashboardItemRegistry.add("tshirt_sizes", {
id: "tshirt_sizes",
description: "T-Shirt sizes sold",
Component: PieChartCard,
size: 2,
props: (stats) => ({
title: "T-Shirt Sizes Sold",
data: stats.orders_by_size,
}),
});
4 changes: 4 additions & 0 deletions awesome_dashboard/static/src/dashboard/dashboard_registry.js
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can include this in the dashboard_items file :)

Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { registry } from "@web/core/registry";

// Create a new registry category for dashboard items
export const dashboardItemRegistry = registry.category("awesome_dashboard.items");
Loading