Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
e99d693
[IMP] awesome_owl: Technical onboarding, chapter 1, step 1 to 5
fmalfroid Sep 22, 2025
a5aa410
[IMP] awesome_owl: Discover the web framework step 6: The sum of two …
fmalfroid Sep 22, 2025
cdc5670
[IMP] awesome_owl: Discover the web framework step 7: A todo list
fmalfroid Sep 22, 2025
045c2fd
[IMP] awesome_owl: Discover the web framework step 8: Use dynamic att…
fmalfroid Sep 22, 2025
103de17
[IMP] awesome_owl: Discover the web framework step 9: Adding a todo
fmalfroid Sep 22, 2025
9b751cc
[IMP] awesome_owl: Discover the web framework step 10: Focusing the i…
fmalfroid Sep 22, 2025
a1b31a0
[IMP] awesome_owl: Discover the web framework step 11: Toggling todos
fmalfroid Sep 23, 2025
295aaf0
[IMP] awesome_owl: Discover the web framework step 12: Deleting todos
fmalfroid Sep 23, 2025
6866ff9
[IMP] awesome_owl: Discover teh web framework step 13: Generic Card w…
fmalfroid Sep 23, 2025
1bf3b4a
[IMP] awesome_owl: Discover the web framework Chapter 1: Owl components
fmalfroid Sep 23, 2025
d8c3e56
[IMP] awesome_dashboard: Discover the web framework, Chapter 2, Step …
fmalfroid Sep 23, 2025
0cc3c1d
[IMP] awesome_dashboard: Discover the web framework, Chapter 2, step …
fmalfroid Sep 23, 2025
17662bf
[IMP] awesome_dashboard: Discover the web framework, Chapter 2, Step …
fmalfroid Sep 23, 2025
d2432e2
[IMP] awesome_dashboard: Discover the web framework, Chapter 2, Step …
fmalfroid Sep 23, 2025
fb73735
[IMP] awesome_dashboard: Discover the web framework, Chapter 2, Step …
fmalfroid Sep 23, 2025
15347e5
[IMP] awesome_dashboard: Discover the web framework, Chapter 2, Step …
fmalfroid Sep 23, 2025
cd512a5
[IMP] awesome_dashboard: Discover the web framework, Chapter 2, Step …
fmalfroid Sep 23, 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
28 changes: 27 additions & 1 deletion awesome_dashboard/static/src/dashboard.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,36 @@
/** @odoo-module **/

import { Component } from "@odoo/owl";
import { Component, useState } from "@odoo/owl";
import { registry } from "@web/core/registry";
import { useService } from "@web/core/utils/hooks";
import { Layout } from "@web/search/layout";
import { DashboardItem } from "./dashboard_item/dashboard_item";
import { PieChart } from "./dashboard_pie_chart/dashboard_pie_chart";

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

setup() {
this.action = useService("action");
this.title = "Awesome Dashboard";
this.statistics = useState(useService("awesome_dashboard.statistics"));
}

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',
})
}
}

registry.category("actions").add("awesome_dashboard.dashboard", AwesomeDashboard);
3 changes: 3 additions & 0 deletions awesome_dashboard/static/src/dashboard.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.o_dashboard {
background-color: gray;
}
47 changes: 46 additions & 1 deletion awesome_dashboard/static/src/dashboard.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,52 @@
<templates xml:space="preserve">

<t t-name="awesome_dashboard.AwesomeDashboard">
hello dashboard
<Layout className="'o_dashboard h-100'" display="{controlPanel: {} }">
<t t-set-slot="layout-buttons">
<button class="btn btn-primary" t-on-click="() => openCustomers()">Customers</button>
<button class="btn btn-primary" t-on-click="() => openLeads()">Leads</button>
</t>
<t t-set-slot="default">
<div class="d-flex flex-wrap">
<DashboardItem>
<t t-set-slot="header">
Number of new orders this month
</t>
<h1 class="text-success" t-esc="statistics.nb_new_orders"></h1>
</DashboardItem>
<DashboardItem>
<t t-set-slot="header">
Total amount of new orders this month
</t>
<h1 class="text-success" t-esc="statistics.total_amount"></h1>
</DashboardItem>
<DashboardItem size="2">
<t t-set-slot="header">
Average amount of t-shirt by order this month
</t>
<h1 class="text-success" t-esc="statistics.average_quantity"></h1>
</DashboardItem>
<DashboardItem>
<t t-set-slot="header">
Number of cancelled orders this month
</t>
<h1 class="text-success" t-esc="statistics.nb_cancelled_orders"></h1>
</DashboardItem>
<DashboardItem size="2">
<t t-set-slot="header">
Average time for an order to go from 'new' to 'sent' or 'cancelled'
</t>
<h1 class="text-success" t-esc="statistics.average_time"></h1>
</DashboardItem>
<DashboardItem size="2">
<t t-set-slot="header">
Shirt orders by size
</t>
<PieChart data="statistics.orders_by_size"/>
</DashboardItem>
</div>
</t>
</Layout>
</t>

</templates>
9 changes: 9 additions & 0 deletions awesome_dashboard/static/src/dashboard_item/dashboard_item.js
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 props = {
size: {type: Number, optional: true},
slots: {type: Object, optional: true}
}
}
15 changes: 15 additions & 0 deletions awesome_dashboard/static/src/dashboard_item/dashboard_item.xml
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 d-inline-block" t-att-style="'width: ' + (18 * (props.size || 1)) + 'rem;'">
<div class="card-header">
<h6><t t-slot="header"/></h6>
</div>
<div class="card-body d-flex align-items-center justify-content-center">
<t t-slot="default"/>
</div>
</div>
</t>

</templates>
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { Component, onWillStart, useEffect, onWillUnmount, 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('canvas');
onWillStart(() => loadJS("/web/static/lib/Chart/Chart.js"));
useEffect(() => this.renderChart());
onWillUnmount(() => {
if (this.chart) {
this.chart.destroy();
}
});
}

/**
* Creates and binds the chart on `canvasRef`.
*/
renderChart() {
if (this.chart) {
this.chart.destroy();
}
const ctx = this.canvasRef.el.getContext('2d');
this.chart = new Chart(ctx, this.getChartConfig());
}

/**
* @returns {object} Chart config for the current data
*/
getChartConfig() {
return {
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,8 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<t t-name="awesome_dashboard.PieChart">
<div class="o_pie_chart">
<canvas t-ref="canvas"/>
</div>
</t>
</odoo>
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { reactive } from "@odoo/owl";
import { registry } from "@web/core/registry";
import { rpc } from "@web/core/network/rpc";

const dashboardStatisticsService = {
start(env) {
const statistics = reactive({});

async function loadData() {
Object.assign(statistics, await rpc("/awesome_dashboard/statistics"));
}

setInterval(loadData, 10 * 60 * 1000);
loadData();

return statistics
},
};

registry.category("services").add("awesome_dashboard.statistics", dashboardStatisticsService);
1 change: 1 addition & 0 deletions awesome_owl/__manifest__.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
'web/static/src/scss/pre_variables.scss',
'web/static/lib/bootstrap/scss/_variables.scss',
'web/static/lib/bootstrap/scss/_maps.scss',
('include', 'web._assets_bootstrap_backend'),
('include', 'web._assets_bootstrap'),
('include', 'web._assets_core'),
'web/static/src/libs/fontawesome/css/font-awesome.css',
Expand Down
17 changes: 17 additions & 0 deletions awesome_owl/static/src/card/card.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { Component, useState } from "@odoo/owl";

export class Card extends Component {
static template = "awesome_owl.card";
static props = {
title: String,
slots: Object
};

setup() {
this.state = useState({ open: true });
}

toggle() {
this.state.open = !this.state.open;
}
}
19 changes: 19 additions & 0 deletions awesome_owl/static/src/card/card.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8" ?>

<templates xml:space="preserve">

<t t-name="awesome_owl.card">
<div class="card d-inline-block m-2" style="width: 18rem;">
<div class="card-header d-flex flex-row align-items-center">
<h5 class="card-title"><t t-out="props.title"/></h5>
<button class="btn btn-secondary ms-auto" t-on-click="toggle">
Toggle
</button>
</div>
<div class="card-body" t-if="state.open">
<t t-slot="default"/>
</div>
</div>
</t>

</templates>
19 changes: 19 additions & 0 deletions awesome_owl/static/src/counter/counter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { Component, useState } from "@odoo/owl";

export class Counter extends Component {
static template = "awesome_owl.counter";
static props = {
onChange: Function
};

setup() {
this.state = useState({ value: 0 });
}

increment() {
this.state.value++;
if ("onChange" in this.props) {
this.props.onChange();
}
}
}
12 changes: 12 additions & 0 deletions awesome_owl/static/src/counter/counter.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8" ?>

<templates xml:space="preserve">

<t t-name="awesome_owl.counter">
<div class="m-2 p-2 border d-flex flex-row align-items-center">
<span class="me-2">Counter: <t t-esc="state.value"/></span>
<button class="btn btn-primary" t-on-click="increment">Increment</button>
</div>
</t>

</templates>
20 changes: 17 additions & 3 deletions awesome_owl/static/src/playground.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,21 @@
/** @odoo-module **/

import { Component } from "@odoo/owl";
import { Component, markup, useState } from "@odoo/owl";
import { Counter } from "./counter/counter";
import { Card } from "./card/card";
import { TodoList } from "./todo_list/todo_list";

export class Playground extends Component {
static template = "awesome_owl.playground";
static components = { Counter, Card, TodoList };
static props = {};

html_value1 = markup("<a href='#'>some content</a>");
html_value2 = "<a href='#'>some content</a>";

setup() {
this.state = useState({ sum: 0 });
}

incrementSum() {
this.state.sum++;
}
}
27 changes: 26 additions & 1 deletion awesome_owl/static/src/playground.xml
Original file line number Diff line number Diff line change
@@ -1,9 +1,34 @@
<?xml version="1.0" encoding="UTF-8" ?>

<templates xml:space="preserve">

<t t-name="awesome_owl.playground">
<div class="p-3">
hello world
<div class="d-inline-block border p-3">
<div class="d-flex flex-row">
<Counter onChange.bind="incrementSum"/>
<Counter onChange.bind="incrementSum"/>
</div>
The sum is: <t t-esc="state.sum"/>
</div>
</div>
<div class="p-3">
<div class="border p-3 d-flex align-items-start w-auto">
<Card title="'Card 1'">
<t t-out="html_value1"/>
</Card>
<Card title="'Card 2'">
<t t-out="html_value2"/>
</Card>
<Card title="'Card 3'">
<Counter onChange.bind="incrementSum"/>
</Card>
</div>
</div>
<div class="p-3">
<div class="d-inline-block border p-3">
<TodoList/>
</div>
</div>
</t>

Expand Down
10 changes: 10 additions & 0 deletions awesome_owl/static/src/todo_list/todo_item.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { Component } from "@odoo/owl";

export class TodoItem extends Component {
static template = "awesome_owl.todo_item";
static props = {
todo: Object,
toggleState: Function,
removeTodo: Function
};
}
13 changes: 13 additions & 0 deletions awesome_owl/static/src/todo_list/todo_item.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_owl.todo_item">
<div class="d-flex flex-row" t-att-class="{'text-muted text-decoration-line-through' : props.todo.isCompleted}">
<input type="checkbox" class="form-check-input me-2" t-att-checked="props.todo.isCompleted" t-on-change="(ev) => props.toggleState(props.todo, ev)"/>
<t t-esc="props.todo.id"/>. <t t-esc="props.todo.description"/>
<span class="fa fa-remove text-danger align-self-center ms-3 cursor-pointer" t-on-click="(ev) => props.removeTodo(props.todo, ev)"/>
</div>
</t>

</templates>
Loading