Skip to content
Open
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
6e4093e
[ADD] estate: Init and manifest
wimar-odoo Oct 22, 2025
66a0801
[IMP] estate: Models and Basic Fields
wimar-odoo Oct 22, 2025
82e151b
[IMP] estate: Security - A Brief Introduction
wimar-odoo Oct 22, 2025
cd40928
[IMP] estate: Finally, Some UI to Play With
wimar-odoo Oct 23, 2025
9a9a4f3
[IMP] estate: Basic Views
wimar-odoo Oct 23, 2025
9a90bd7
[FIX] estate: Remove useless string parameter from model attributes
wimar-odoo Oct 23, 2025
db9ded5
[FIX] estate: adjustments after first review
wimar-odoo Oct 23, 2025
fe7da46
[IMP] estate: Chapter 7 - Types, Tags and Offers
wimar-odoo Oct 24, 2025
edaf208
[IMP] estate: Chapter 8 - Computed, Inverse, and Onchange Fields
wimar-odoo Oct 24, 2025
1d073e0
[IMP] estate: Chapter 9 - Button Actions
wimar-odoo Oct 24, 2025
4353811
[IMP] estate: Chapter 10 - SQL and Python Constraints
wimar-odoo Oct 24, 2025
cad2ef0
[IMP] estate: Chapter 11 - Sprinkles
wimar-odoo Oct 27, 2025
fb58afd
[FIX] estate: PR review adjustments
wimar-odoo Oct 27, 2025
a9e718c
[IMP] estate: Chapter 12 - Inheritance
wimar-odoo Oct 27, 2025
9081d57
[ADD] estate_account: Chapter 13 - Other modules
wimar-odoo Oct 28, 2025
f1448b2
[IMP] estate: Chapter 14 - Kanban view
wimar-odoo Oct 28, 2025
b48fa58
[IMP] awesome_owl: Chapter 1 - Intro Owl Components
wimar-odoo Oct 30, 2025
bd25287
[FIX] estate: last review adjustments
wimar-odoo Oct 30, 2025
816de58
[IMP] awesome_dashboard: Chapter 2 - Build a dashboard
wimar-odoo Oct 31, 2025
eefcaf9
[FIX] awesome_owl: Last review adjustments
wimar-odoo Nov 3, 2025
2918ab0
[FIX] awesome_dashboard: Last review adjustments
wimar-odoo Nov 3, 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
10 changes: 10 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -127,3 +127,13 @@ dmypy.json

# Pyre type checker
.pyre/

# JetBrains specific folders
.idea/
*/.idea/

# Odoo specific folders
.run
*/.run/
.odev
*/.odev/
3 changes: 1 addition & 2 deletions awesome_dashboard/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
# -*- coding: utf-8 -*-

from . import controllers
from . import models
2 changes: 1 addition & 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 @@ -20,6 +19,7 @@

'data': [
'views/views.xml',
'views/res_users_views.xml',
],
'assets': {
'web.assets_backend': [
Expand Down
2 changes: 1 addition & 1 deletion awesome_dashboard/controllers/controllers.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
logger = logging.getLogger(__name__)

class AwesomeDashboard(http.Controller):
@http.route('/awesome_dashboard/statistics', type='json', auth='user')
@http.route('/awesome_dashboard/statistics', type='jsonrpc', auth='user')
def get_statistics(self):
"""
Returns a dict of statistics about the orders:
Expand Down
87 changes: 87 additions & 0 deletions awesome_dashboard/i18n/en_US.po
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * awesome_dashboard
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 19.0+e\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-10-31 14:36+0000\n"
"PO-Revision-Date: 2025-10-31 14:36+0000\n"
"Last-Translator: \n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: \n"

#. module: awesome_dashboard
#. odoo-javascript
#: code:addons/awesome_dashboard/static/src/dashboard/dashboard_item.js:0
msgid "Average amount of t-shirt by order this month"
msgstr ""

#. module: awesome_dashboard
#. odoo-javascript
#: code:addons/awesome_dashboard/static/src/dashboard/dashboard_item.js:0
msgid "Average time for an order to go from 'new' to 'sent' or 'cancelled'"
msgstr ""

#. module: awesome_dashboard
#: model:ir.ui.menu,name:awesome_dashboard.menu_root
msgid "Awesome Dashboard"
msgstr ""

#. module: awesome_dashboard
#. odoo-javascript
#: code:addons/awesome_dashboard/static/src/dashboard/dashboard.xml:0
msgid "Customers"
msgstr ""

#. module: awesome_dashboard
#: model:ir.actions.client,name:awesome_dashboard.dashboard
#: model:ir.ui.menu,name:awesome_dashboard.dashboard_menu
msgid "Dashboard"
msgstr ""

#. module: awesome_dashboard
#. odoo-javascript
#: code:addons/awesome_dashboard/static/src/dashboard/dashboard_configuration/dashboard_configuration.xml:0
msgid "Done"
msgstr ""

#. module: awesome_dashboard
#. odoo-javascript
#: code:addons/awesome_dashboard/static/src/dashboard/dashboard.xml:0
msgid "Leads"
msgstr ""

#. module: awesome_dashboard
#. odoo-javascript
#: code:addons/awesome_dashboard/static/src/dashboard/dashboard_item.js:0
msgid "Number of cancelled orders this month"
msgstr ""

#. module: awesome_dashboard
#. odoo-javascript
#: code:addons/awesome_dashboard/static/src/dashboard/dashboard_item.js:0
msgid "Number of new orders this month"
msgstr ""

#. module: awesome_dashboard
#. odoo-javascript
#: code:addons/awesome_dashboard/static/src/dashboard/dashboard_item.js:0
msgid "Shirt orders by size"
msgstr ""

#. module: awesome_dashboard
#. odoo-javascript
#: code:addons/awesome_dashboard/static/src/dashboard/dashboard_item.js:0
msgid "Total amount of new orders this month"
msgstr ""

#. module: awesome_dashboard
#. odoo-javascript
#: code:addons/awesome_dashboard/static/src/dashboard/dashboard_configuration/dashboard_configuration.xml:0
msgid "Which cards do you wish to see ?"
msgstr ""
87 changes: 87 additions & 0 deletions awesome_dashboard/i18n/fr_BE.po
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * awesome_dashboard
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 19.0+e\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-10-31 14:33+0000\n"
"PO-Revision-Date: 2025-10-31 14:33+0000\n"
"Last-Translator: \n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: \n"

#. module: awesome_dashboard
#. odoo-javascript
#: code:addons/awesome_dashboard/static/src/dashboard/dashboard_item.js:0
msgid "Average amount of t-shirt by order this month"
msgstr "Moyenne du nombre de t-shirt par commande ce mois-ci"

#. module: awesome_dashboard
#. odoo-javascript
#: code:addons/awesome_dashboard/static/src/dashboard/dashboard_item.js:0
msgid "Average time for an order to go from 'new' to 'sent' or 'cancelled'"
msgstr ""

#. module: awesome_dashboard
#: model:ir.ui.menu,name:awesome_dashboard.menu_root
msgid "Awesome Dashboard"
msgstr ""

#. module: awesome_dashboard
#. odoo-javascript
#: code:addons/awesome_dashboard/static/src/dashboard/dashboard.xml:0
msgid "Customers"
msgstr ""

#. module: awesome_dashboard
#: model:ir.actions.client,name:awesome_dashboard.dashboard
#: model:ir.ui.menu,name:awesome_dashboard.dashboard_menu
msgid "Dashboard"
msgstr ""

#. module: awesome_dashboard
#. odoo-javascript
#: code:addons/awesome_dashboard/static/src/dashboard/dashboard_configuration/dashboard_configuration.xml:0
msgid "Done"
msgstr ""

#. module: awesome_dashboard
#. odoo-javascript
#: code:addons/awesome_dashboard/static/src/dashboard/dashboard.xml:0
msgid "Leads"
msgstr ""

#. module: awesome_dashboard
#. odoo-javascript
#: code:addons/awesome_dashboard/static/src/dashboard/dashboard_item.js:0
msgid "Number of cancelled orders this month"
msgstr ""

#. module: awesome_dashboard
#. odoo-javascript
#: code:addons/awesome_dashboard/static/src/dashboard/dashboard_item.js:0
msgid "Number of new orders this month"
msgstr ""

#. module: awesome_dashboard
#. odoo-javascript
#: code:addons/awesome_dashboard/static/src/dashboard/dashboard_item.js:0
msgid "Shirt orders by size"
msgstr ""

#. module: awesome_dashboard
#. odoo-javascript
#: code:addons/awesome_dashboard/static/src/dashboard/dashboard_item.js:0
msgid "Total amount of new orders this month"
msgstr ""

#. module: awesome_dashboard
#. odoo-javascript
#: code:addons/awesome_dashboard/static/src/dashboard/dashboard_configuration/dashboard_configuration.xml:0
msgid "Which cards do you wish to see ?"
msgstr ""
1 change: 1 addition & 0 deletions awesome_dashboard/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import res_users
30 changes: 30 additions & 0 deletions awesome_dashboard/models/res_users.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
from odoo import models, fields, api


class ResUsers(models.Model):
_inherit = "res.users"

average_quantity = fields.Boolean(default=True)
average_time = fields.Boolean(default=True)
nb_new_orders = fields.Boolean(default=True)
nb_cancelled_orders = fields.Boolean(default=True)
total_amount = fields.Boolean(default=True)
orders_by_size = fields.Boolean(default=True)

@api.model
def get_dashboard_config(self):
user = self.env.user
return {
'average_quantity': user.average_quantity,
'average_time': user.average_time,
'nb_new_orders': user.nb_new_orders,
'nb_cancelled_orders': user.nb_cancelled_orders,
'total_amount': user.total_amount,
'orders_by_size': user.orders_by_size,
}

@api.model
def set_dashboard_config(self, config):
user = self.env.user
user.write(config)
return True
8 changes: 0 additions & 8 deletions awesome_dashboard/static/src/dashboard.js

This file was deleted.

93 changes: 93 additions & 0 deletions awesome_dashboard/static/src/dashboard/dashboard.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import { Component, useState, onWillStart } 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 { PieChart } from "./piechart/piechart";
import { DashboardConfiguration } from "./dashboard_configuration/dashboard_configuration";


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

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

this.allItems = registry.category("awesome_dashboard").getAll();
this.state = useState({
hiddenItems: []
});

onWillStart(async () => {
await this.loadHiddenItems();
});
}

get items() {
const visibleItems = {};
Object.entries(this.allItems).forEach(([itemId, item]) => {
if (!this.state.hiddenItems.includes(item.backend_attribute)) {
visibleItems[itemId] = item;
}
});
return visibleItems;

Choose a reason for hiding this comment

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

In general, when you want to "migrate" from an array to a single value (here a dict), .reduce(...) is the solution :)

(just for your information)

Suggested change
const visibleItems = {};
Object.entries(this.allItems).forEach(([itemId, item]) => {
if (!this.state.hiddenItems.includes(item.backend_attribute)) {
visibleItems[itemId] = item;
}
});
return visibleItems;
return Object.entries(this.allItems).reduce((acc, [itemId, item]) => {
if (!this.state.hiddenItems.includes(item.backend_attribute)) {
acc[itemId] = item;
}
return acc;
}, {});

}

async loadHiddenItems() {
try {
const config = await this.orm.call('res.users', 'get_dashboard_config', []);
const hiddenItems = [];
for (const [itemId, isVisible] of Object.entries(config)) {
if (!isVisible) {
hiddenItems.push(itemId);
}
}
this.state.hiddenItems = hiddenItems;

Choose a reason for hiding this comment

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

I'd use an array instead of a dict (it's easier to handle, at list it's more readable) but it's only the tutorial so it's ok.
But this part could be more "modern" like

Suggested change
const hiddenItems = [];
for (const [itemId, isVisible] of Object.entries(config)) {
if (!isVisible) {
hiddenItems.push(itemId);
}
}
this.state.hiddenItems = hiddenItems;
this.state.hiddenItems = Object.entries(config)
.filter(([itemId, isVisible]) => !isVisible)
.map(([itemId]) => itemId);

} catch (e) {
console.error("Failed to load dashboard configuration:", e);
this.state.hiddenItems = [];
}
}

async saveHiddenItems(hiddenItems) {
try {
const config = {};
for (const item of Object.values(this.allItems)) {
config[item.backend_attribute] = !hiddenItems.includes(item.backend_attribute);
}

Choose a reason for hiding this comment

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

Same here for the reduce (but nothing mandatory)

Suggested change
const config = {};
for (const item of Object.values(this.allItems)) {
config[item.backend_attribute] = !hiddenItems.includes(item.backend_attribute);
}
const config = Object.values(this.allItems).reduce((acc, item) => {
acc[item.backend_attribute] = !hiddenItems.includes(item.backend_attribute);
return acc;
}, {});

await this.orm.call('res.users', 'set_dashboard_config', [config]);
this.state.hiddenItems = hiddenItems;
} catch (e) {
console.error("Failed to save dashboard configuration:", e);
}
}

openConfiguration() {
this.dialog.add(DashboardConfiguration, {
items: this.allItems,
hiddenItems: this.state.hiddenItems,
onSave: async (hiddenItems) => {
await this.saveHiddenItems(hiddenItems);
}
});
}

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

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

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

.item_card {
background-color: white;
border: black solid 1px;
margin: 5px;
padding: 10px;
color: black;
}

Choose a reason for hiding this comment

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

missing eol

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'">
<header>
<t t-set-slot="layout-buttons">
<button t-on-click="openCustomers" type="button" class="btn btn-primary">Customers</button>
<button t-on-click="openLeads" type="button" class="btn btn-primary">Leads</button>
<button class="btn d-flex align-items-center gap-2" t-on-click="openConfiguration"><i class="fa fa-fw fa-gear"/></button>
</t>
</header>
<div class="d-flex flex-wrap">
<t t-foreach="Object.entries(items)" t-as="entry" t-key="entry[0]">
<t t-set="itemId" t-value="entry[0]"/>
<t t-set="item" t-value="entry[1]"/>
<DashboardItem size="item.size || 1">
<t t-set="itemProp" t-value="item.props ? item.props(statisticsService.data) : {'data': statisticsService.data}"/>
<t t-component="item.Component" t-props="itemProp" />
</DashboardItem>
</t>
</div>
</Layout>
</t>

</templates>
Loading