-
Notifications
You must be signed in to change notification settings - Fork 2.6k
Web Tutorial PR: trcaz #967
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: 18.0
Are you sure you want to change the base?
Changes from all commits
dfc2387
f7b4680
dc2211e
a22285a
8611d61
70e3c05
aa4125a
bf99024
03af1db
86806ec
143eec9
3156e29
60483e6
2209933
adf412a
ac47f9b
71636b1
4115506
82f496d
cabcad9
036b648
164bc52
a502aa5
292c7b8
9f7c018
82c0d40
b62f551
113773f
6f4a297
0ad1da4
6aacfbe
3231834
44e0d49
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
This file was deleted.
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,73 @@ | ||||||
/** @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 { _t } from "@web/core/l10n/translation"; | ||||||
import { DashboardItem } from "./dashboard_item"; | ||||||
import { PieChart } from "./pie_chart/pie_chart"; | ||||||
import { browser } from "@web/core/browser/browser"; | ||||||
import { Dialog } from "@web/core/dialog/dialog"; | ||||||
import { CheckBox } from "@web/core/checkbox/checkbox"; | ||||||
|
||||||
class AwesomeDashboard extends Component { | ||||||
static template = "awesome_dashboard.AwesomeDashboard"; | ||||||
static components = { Layout, DashboardItem, PieChart }; | ||||||
|
||||||
setup() { | ||||||
this.action = useService("action"); | ||||||
this.stats = useState(useService("awesome_dashboard.statistics")); | ||||||
this.items = registry.category("awesome_dashboard").getAll(); | ||||||
this.dialog = useService("dialog"); | ||||||
this.state = useState({ itemsNotShown: browser.localStorage.getItem("itemsNotShown")?.split(",") || []}); | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
just localStorage should be enough. Or maybe I'm dreaming 📦 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. idk I had no idea how to do it so I just looked it up There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's confusing to have a state variable called state when you already have other state variables (stats). Either put There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. But isn't state used to pass mutable variables? I put the ones that change in this.state and the others just in this There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I never heard a rule like that, did you see it somewhere 👀 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No but it makes more sense to me :) |
||||||
} | ||||||
|
||||||
openPartners() { | ||||||
this.action.doAction("base.action_partner_form"); | ||||||
} | ||||||
|
||||||
async openLeads() { | ||||||
this.action.doAction({ | ||||||
type: 'ir.actions.act_window', | ||||||
name: _t('All Leads'), | ||||||
res_model: 'crm.lead', | ||||||
views: [ | ||||||
[false, 'list'], | ||||||
[false, 'form'] | ||||||
], | ||||||
}); | ||||||
} | ||||||
|
||||||
openSettingsDialog() { | ||||||
this.dialog.add(SettingsDialog, { | ||||||
items: this.items, | ||||||
dashboard: this, | ||||||
}); | ||||||
} | ||||||
} | ||||||
|
||||||
registry.category("lazy_components").add("awesome_dashboard.dashboard", AwesomeDashboard); | ||||||
|
||||||
class SettingsDialog extends Component { | ||||||
static template = "awesome_dashboard.SettingsDialog"; | ||||||
static components = { Dialog, CheckBox }; | ||||||
static props = ["dashboard", "items"]; | ||||||
|
||||||
setup() { | ||||||
this.items = useState(this.props.items); | ||||||
this.items.forEach((item) => { item.shown = !this.props.dashboard.state.itemsNotShown.includes(item.id)}); | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I hate everything about this. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That way I only have to pass 1 prop: the dashboard, and not: itemsNotShown and 15update methods 😄 |
||||||
} | ||||||
|
||||||
toggleItem (isChecked, item) { | ||||||
item.shown = isChecked; | ||||||
|
||||||
const newItemsNotShown = Object.values(this.items) | ||||||
.filter((i) => !i.shown) | ||||||
.map((i) => i.id) | ||||||
|
||||||
browser.localStorage.setItem("itemsNotShown", newItemsNotShown); | ||||||
|
||||||
this.props.dashboard.state.itemsNotShown = newItemsNotShown; | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. uuuhh so you're getting the parent as a prop, then changing the state of the parent in the child. Why don't you handle all of this in the parent. You can create the toggleItem (or similar) function in the parent and pass the function to the child as a prop There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I was gonna write a witty answer but instead I'll just commit another solution that will comply entirely with what I was gonna write & will probably irritate you even more There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I hope you like it |
||||||
} | ||||||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
.o_dashboard { | ||
background-color: purple | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
<?xml version="1.0" encoding="UTF-8" ?> | ||
<templates xml:space="preserve"> | ||
|
||
<t t-name="awesome_dashboard.AwesomeDashboard"> | ||
<Layout className="'o_dashboard h-100'" display="{controlPanel: {} }"> | ||
<t t-set-slot="layout-buttons"> | ||
<button class="btn btn-primary" t-on-click="() => this.openPartners()"> | ||
Customers | ||
</button> | ||
<button class="btn btn-primary" t-on-click="() => this.openLeads()"> | ||
Leads | ||
</button> | ||
</t> | ||
|
||
<t t-set-slot="control-panel-additional-actions"> | ||
<button t-on-click="() => this.openSettingsDialog()" class=""> | ||
<i class="fa fa-cog"/> | ||
</button> | ||
</t> | ||
|
||
<t t-foreach="items" t-as="item" t-key="item.id"> | ||
<DashboardItem size="item.size" t-if="!this.state.itemsNotShown.includes(item.id)"> | ||
<t t-set="itemProp" t-value="item.props ? item.props(this.stats) : {'data': this.stats}"/> | ||
<t t-component="item.Component" t-props="itemProp" /> | ||
</DashboardItem> | ||
</t> | ||
</Layout> | ||
</t> | ||
|
||
<t t-name="awesome_dashboard.SettingsDialog"> | ||
<Dialog title="'Configure your dashboard'"> | ||
Choose which items you want to display | ||
<t t-foreach="items" t-as="item" t-key="item.id"> | ||
<CheckBox value="item.shown" onChange="(ev) => this.toggleItem(ev, item)"> | ||
<t t-esc="item.description"/> | ||
</CheckBox> | ||
</t> | ||
</Dialog> | ||
</t> | ||
|
||
</templates> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
import { LazyComponent } from "@web/core/assets"; | ||
import { Component, xml, onWillStart, useState } from "@odoo/owl"; | ||
import { registry } from "@web/core/registry"; | ||
|
||
|
||
export class AwesomeDashboardLoader extends Component { | ||
static components = { LazyComponent }; | ||
static template = xml` | ||
<LazyComponent bundle="'awesome_dashboard.dashboard'" Component="'awesome_dashboard.dashboard'"/> | ||
`; | ||
} | ||
|
||
registry.category("actions").add("awesome_dashboard.dashboard", AwesomeDashboardLoader); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
import { Component } from "@odoo/owl"; | ||
|
||
export class DashboardItem extends Component { | ||
static template = "awesome_dashboard.DashboardItem" | ||
static props = { | ||
size: {type: Number, optional: true}, | ||
slots: {optional: true} | ||
}; | ||
|
||
setup() { | ||
this.size = this.props.size || 1; | ||
} | ||
} |
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="dashboard_item d-inline-block m-2" t-att-style="'width: ' + (18 * this.size) + 'rem; border: 1px solid black; border-radius: 5px'"> | ||
<div class="dashboard_item-body"> | ||
<h5 class="dashboard_item-title"><t t-esc="this.title"/></h5> | ||
<p class="dashboard_item-text"> | ||
<t t-slot="default"/> | ||
</p> | ||
</div> | ||
</div> | ||
</t> | ||
|
||
</templates> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
import { NumberCard } from "./number_card/number_card" | ||
import { PieChartCard } from "./pie_chart_card/pie_chart_card" | ||
import { registry } from "@web/core/registry"; | ||
|
||
const items = [ | ||
{ | ||
id: "average_quantity", | ||
description: "Average amount of t-shirt", | ||
Component: NumberCard, | ||
props: (data) => ({ | ||
title: "Average amount of t-shirt by order this month", | ||
value: data.average_quantity, | ||
}) | ||
}, | ||
{ | ||
id: "average_time", | ||
description: "Average time for an order", | ||
Component: NumberCard, | ||
props: (data) => ({ | ||
title: "Average time for an order to go from 'new' to 'sent' or 'cancelled'", | ||
value: data.average_time, | ||
}) | ||
}, | ||
{ | ||
id: "number_new_orders", | ||
description: "New orders this month", | ||
Component: NumberCard, | ||
props: (data) => ({ | ||
title: "Number of new orders this month", | ||
value: data.nb_new_orders, | ||
}) | ||
}, | ||
{ | ||
id: "cancelled_orders", | ||
description: "Cancelled orders this month", | ||
Component: NumberCard, | ||
props: (data) => ({ | ||
title: "Number of cancelled orders this month", | ||
value: data.nb_cancelled_orders, | ||
}) | ||
}, | ||
{ | ||
id: "amount_new_orders", | ||
description: "amount orders this month", | ||
Component: NumberCard, | ||
props: (data) => ({ | ||
title: "Total amount of new orders this month", | ||
value: data.total_amount, | ||
}) | ||
}, | ||
{ | ||
id: "pie_chart", | ||
description: "Shirt orders by size", | ||
Component: PieChartCard, | ||
size: 2, | ||
props: (data) => ({ | ||
title: "Shirt orders by size", | ||
values: data.orders_by_size, | ||
}) | ||
} | ||
] | ||
|
||
items.forEach(i => { | ||
registry.category("awesome_dashboard").add(i.id, i); | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
import { registry } from "@web/core/registry"; | ||
import { memoize } from "@web/core/utils/functions"; | ||
import { rpc } from "@web/core/network/rpc"; | ||
import { reactive } from "@odoo/owl"; | ||
|
||
const dashboardService = { | ||
start() { | ||
let stats = reactive({ | ||
average_quantity: 0, | ||
average_time: 0, | ||
nb_cancelled_orders: 0, | ||
nb_new_orders: 0, | ||
orders_by_size: {}, | ||
total_amount: 0 | ||
}); | ||
async function loadData() { | ||
const newStats = await rpc("/awesome_dashboard/statistics"); | ||
Object.assign(stats, newStats); | ||
} | ||
setInterval(loadData, 2*1000); | ||
loadData(); | ||
|
||
return stats; | ||
}, | ||
}; | ||
|
||
|
||
registry.category("services").add("awesome_dashboard.statistics", dashboardService); |
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: Number, | ||
}; | ||
} |
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_dashboard.NumberCard" owl="1"> | ||
<t t-esc="props.title"/> | ||
<div class="fs-1 fw-bold text-success text-center"> | ||
<t t-esc="props.value"/> | ||
</div> | ||
</t> | ||
|
||
</templates> |
Original file line number | Diff line number | Diff line change | ||||||||
---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,37 @@ | ||||||||||
import { Component, onMounted, onWillUnmount, onWillStart, useEffect, useRef } from "@odoo/owl"; | ||||||||||
import { loadJS } from "@web/core/assets"; | ||||||||||
|
||||||||||
export class PieChart extends Component { | ||||||||||
static template = "awesome_dashboard.PieChart"; | ||||||||||
static props = { data: {optional: false}}; | ||||||||||
|
||||||||||
setup() { | ||||||||||
this.canvasRef = useRef("canvas"); | ||||||||||
|
||||||||||
onWillStart(() => loadJS("/web/static/lib/Chart/Chart.js")); | ||||||||||
|
||||||||||
useEffect(() => { | ||||||||||
if (this.pieChart) { | ||||||||||
this.pieChart.destroy(); | ||||||||||
} | ||||||||||
Comment on lines
+14
to
+16
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||
this.data = Object.values(this.props.data); | ||||||||||
this.labels = Object.keys(this.props.data); | ||||||||||
|
||||||||||
this.buildChart(); | ||||||||||
}); | ||||||||||
|
||||||||||
onWillUnmount(() => { if (this.pieChart) {this.pieChart.destroy()}; }); | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hum hum... |
||||||||||
} | ||||||||||
|
||||||||||
buildChart() { | ||||||||||
this.pieChart = new Chart(this.canvasRef.el, { | ||||||||||
type: "pie", | ||||||||||
data: { | ||||||||||
labels: this.labels, | ||||||||||
datasets: [{ | ||||||||||
data: this.data, | ||||||||||
}], | ||||||||||
}, | ||||||||||
}); | ||||||||||
} | ||||||||||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,6 @@ | ||
<?xml version="1.0" encoding="UTF-8" ?> | ||
<templates xml:space="preserve"> | ||
|
||
<t t-name="awesome_dashboard.AwesomeDashboard"> | ||
hello dashboard | ||
<t t-name="awesome_dashboard.PieChart"> | ||
<canvas t-ref="canvas"/> | ||
</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, | ||
values: {optional: false}, | ||
}; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
<?xml version="1.0" encoding="UTF-8" ?> | ||
|
||
<templates xml:space="preserve"> | ||
|
||
<t t-name="awesome_dashboard.PieChartCard" owl="1"> | ||
<t t-esc="props.title"/> | ||
<PieChart data="props.values" label="''"/> | ||
</t> | ||
|
||
</templates> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
import { Component, useState } from "@odoo/owl"; | ||
cgun-odoo marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
export class Card extends Component { | ||
static template = "awesome_owl.card"; | ||
static props = { | ||
title: String, | ||
slots: { | ||
type: Object, | ||
shape: { | ||
default: true | ||
}, | ||
} | ||
} | ||
|
||
setup() { | ||
this.state = useState({open: true}); | ||
this.title = this.props.title; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👀
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
?