From c7375d92f4c861c2328f649c9a46721e478fe826 Mon Sep 17 00:00:00 2001 From: prbo-odoo Date: Thu, 3 Jul 2025 12:03:44 +0530 Subject: [PATCH 01/10] [ADD] real_estate: added real_estate module and basic model setup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Reviewed Odoo server architecture and understood how addons and modules are structured - Created a new Odoo app and added app details in the manifest — now visible on the dashboard - Defined a basic model with fields, installed the module and tested it through the UI --- real_estate/__init__.py | 1 + real_estate/__manifest__.py | 10 ++++++++++ real_estate/models/__init__.py | 1 + real_estate/models/estate_property.py | 24 ++++++++++++++++++++++++ 4 files changed, 36 insertions(+) create mode 100644 real_estate/__init__.py create mode 100644 real_estate/__manifest__.py create mode 100644 real_estate/models/__init__.py create mode 100644 real_estate/models/estate_property.py diff --git a/real_estate/__init__.py b/real_estate/__init__.py new file mode 100644 index 00000000000..9a7e03eded3 --- /dev/null +++ b/real_estate/__init__.py @@ -0,0 +1 @@ +from . import models \ No newline at end of file diff --git a/real_estate/__manifest__.py b/real_estate/__manifest__.py new file mode 100644 index 00000000000..10d4960e07a --- /dev/null +++ b/real_estate/__manifest__.py @@ -0,0 +1,10 @@ +{ + 'name': "RealEstate", + 'depends': ['base'], + 'sequence': 1, + 'application': True, + 'category': 'Real Estate', + 'version': '1.0', + 'author': "prbo" + +} \ No newline at end of file diff --git a/real_estate/models/__init__.py b/real_estate/models/__init__.py new file mode 100644 index 00000000000..5e1963c9d2f --- /dev/null +++ b/real_estate/models/__init__.py @@ -0,0 +1 @@ +from . import estate_property diff --git a/real_estate/models/estate_property.py b/real_estate/models/estate_property.py new file mode 100644 index 00000000000..a30dd193f35 --- /dev/null +++ b/real_estate/models/estate_property.py @@ -0,0 +1,24 @@ +from odoo import fields, models + +class estate_property(models.Model): + _name = "estate.property" + _description = "Test Model" + + name = fields.Char(required=True) + description = fields.Text() + postcode = fields.Char() + date_availability = fields.Date(copy=False) + expected_price = fields.Float(required=True) + selling_price = fields.Float(readonly=True, copy=False) + bedrooms = fields.Integer(default=2) + living_area = fields.Integer() + facades = fields.Integer() + garage = fields.Boolean() + garden = fields.Boolean() + garden_area = fields.Integer() + garden_orientation = fields.Selection( + string='Garden Orientation', + selection=[('north', 'North'), ('south', 'South'), + ('east', 'East'), ('west', 'West')], + help="Direction of the garden" + ) \ No newline at end of file From da9925556329b27361b4838954579c560f32d419 Mon Sep 17 00:00:00 2001 From: prbo-odoo Date: Fri, 4 Jul 2025 11:04:43 +0530 Subject: [PATCH 02/10] [IMP] real_estate: applied security rules, UI menu and basic views for the model - Defined security access rules to control user permissions for viewing, creating and editing model records. - Added a menu item and corresponding action in the UI to enable easy navigation to the model. - Defined basic list and form views for visualizing and managing records within the Odoo interface. --- real_estate/__manifest__.py | 9 +- real_estate/data/ir.model.access.csv | 2 + real_estate/models/estate_property.py | 25 ++++- real_estate/security/ir.model.access.csv | 2 + real_estate/views/estate_menus.xml | 15 +++ real_estate/views/estate_property_views.xml | 100 ++++++++++++++++++++ 6 files changed, 149 insertions(+), 4 deletions(-) create mode 100644 real_estate/data/ir.model.access.csv create mode 100644 real_estate/security/ir.model.access.csv create mode 100644 real_estate/views/estate_menus.xml create mode 100644 real_estate/views/estate_property_views.xml diff --git a/real_estate/__manifest__.py b/real_estate/__manifest__.py index 10d4960e07a..18e57546169 100644 --- a/real_estate/__manifest__.py +++ b/real_estate/__manifest__.py @@ -2,9 +2,14 @@ 'name': "RealEstate", 'depends': ['base'], 'sequence': 1, + 'license': 'LGPL-3', 'application': True, 'category': 'Real Estate', 'version': '1.0', - 'author': "prbo" - + 'author': "prbo", + 'data': [ + 'security/ir.model.access.csv', + 'views/estate_property_views.xml', + 'views/estate_menus.xml' +] } \ No newline at end of file diff --git a/real_estate/data/ir.model.access.csv b/real_estate/data/ir.model.access.csv new file mode 100644 index 00000000000..682a453386d --- /dev/null +++ b/real_estate/data/ir.model.access.csv @@ -0,0 +1,2 @@ +id,name,model_id/id,group_id/id,perm_read,perm_write,perm_create,perm_unlink +access_estate_property_user,access.estate.property,model_estate_property,base.group_user,1,1,1,1 diff --git a/real_estate/models/estate_property.py b/real_estate/models/estate_property.py index a30dd193f35..9cc4d30a4d9 100644 --- a/real_estate/models/estate_property.py +++ b/real_estate/models/estate_property.py @@ -1,4 +1,5 @@ from odoo import fields, models +from datetime import date, timedelta class estate_property(models.Model): _name = "estate.property" @@ -7,7 +8,11 @@ class estate_property(models.Model): name = fields.Char(required=True) description = fields.Text() postcode = fields.Char() - date_availability = fields.Date(copy=False) + date_availability = fields.Date( + string="Available From", + copy=False, + default=lambda self: date.today() + timedelta(days=90) + ) expected_price = fields.Float(required=True) selling_price = fields.Float(readonly=True, copy=False) bedrooms = fields.Integer(default=2) @@ -21,4 +26,20 @@ class estate_property(models.Model): selection=[('north', 'North'), ('south', 'South'), ('east', 'East'), ('west', 'West')], help="Direction of the garden" - ) \ No newline at end of file + ) + active = fields.Boolean(default=True) + state = fields.Selection( + selection=[ + ('new', 'New'), + ('offer_received', 'Offer Received'), + ('offer_accepted', 'Offer Accepted'), + ('sold', 'Sold'), + ('cancelled', 'Cancelled') + ], + required=True, + copy=False, + default='new' +) + + + \ No newline at end of file diff --git a/real_estate/security/ir.model.access.csv b/real_estate/security/ir.model.access.csv new file mode 100644 index 00000000000..baafdc83896 --- /dev/null +++ b/real_estate/security/ir.model.access.csv @@ -0,0 +1,2 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_estate_property_user,access.estate.property,model_estate_property,,1,1,1,1 diff --git a/real_estate/views/estate_menus.xml b/real_estate/views/estate_menus.xml new file mode 100644 index 00000000000..fbe5d4d7f79 --- /dev/null +++ b/real_estate/views/estate_menus.xml @@ -0,0 +1,15 @@ + + + + + + + + + \ No newline at end of file diff --git a/real_estate/views/estate_property_views.xml b/real_estate/views/estate_property_views.xml new file mode 100644 index 00000000000..3a777f16533 --- /dev/null +++ b/real_estate/views/estate_property_views.xml @@ -0,0 +1,100 @@ + + + + Properties + estate.property + list,form + + + + + estate.property.list + estate.property + + + + + + + + + + + + + + + estate.property.form + estate.property + +
+ +

+ +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ + + + estate.property.search + estate.property + + + + + + + + + + + + + + + + + + + + + + + +
\ No newline at end of file From 815b516de4da528906e63b5dca2c123dc6a2a6ee Mon Sep 17 00:00:00 2001 From: prbo-odoo Date: Mon, 7 Jul 2025 10:19:36 +0530 Subject: [PATCH 03/10] [IMP] real_estate: applied relations for offer and properties Implemented an onchange method. Added action buttons to mark a property as Cancelled or Sold, with validation to prevent switching between these states. Created Accept and Refuse buttons for property offers, where accepting an offer sets the buyer (using a many2one relation with res.partner) and the selling price on the property. Also added a check to ensure only one offer can be accepted per property. --- real_estate/__manifest__.py | 3 + real_estate/models/__init__.py | 3 + real_estate/models/estate_property.py | 58 +++++++++++++++++-- real_estate/models/estate_property_offer.py | 51 ++++++++++++++++ real_estate/models/estate_property_tag.py | 7 +++ real_estate/models/estate_property_type.py | 7 +++ real_estate/security/ir.model.access.csv | 5 +- real_estate/views/estate_menus.xml | 12 ++++ .../views/estate_property_offer_view.xml | 39 +++++++++++++ .../views/estate_property_tag_view.xml | 30 ++++++++++ .../views/estate_property_type_view.xml | 8 +++ real_estate/views/estate_property_views.xml | 43 ++++++++++++-- 12 files changed, 257 insertions(+), 9 deletions(-) create mode 100644 real_estate/models/estate_property_offer.py create mode 100644 real_estate/models/estate_property_tag.py create mode 100644 real_estate/models/estate_property_type.py create mode 100644 real_estate/views/estate_property_offer_view.xml create mode 100644 real_estate/views/estate_property_tag_view.xml create mode 100644 real_estate/views/estate_property_type_view.xml diff --git a/real_estate/__manifest__.py b/real_estate/__manifest__.py index 18e57546169..b7e16426163 100644 --- a/real_estate/__manifest__.py +++ b/real_estate/__manifest__.py @@ -10,6 +10,9 @@ 'data': [ 'security/ir.model.access.csv', 'views/estate_property_views.xml', + 'views/estate_property_type_view.xml', + 'views/estate_property_tag_view.xml', + 'views/estate_property_offer_view.xml', 'views/estate_menus.xml' ] } \ No newline at end of file diff --git a/real_estate/models/__init__.py b/real_estate/models/__init__.py index 5e1963c9d2f..09b2099fe84 100644 --- a/real_estate/models/__init__.py +++ b/real_estate/models/__init__.py @@ -1 +1,4 @@ from . import estate_property +from . import estate_property_type +from . import estate_property_tag +from . import estate_property_offer \ No newline at end of file diff --git a/real_estate/models/estate_property.py b/real_estate/models/estate_property.py index 9cc4d30a4d9..b6caa660cad 100644 --- a/real_estate/models/estate_property.py +++ b/real_estate/models/estate_property.py @@ -1,10 +1,12 @@ -from odoo import fields, models +from odoo import fields, models, api from datetime import date, timedelta +from odoo.exceptions import UserError class estate_property(models.Model): _name = "estate.property" _description = "Test Model" + name = fields.Char(required=True) description = fields.Text() postcode = fields.Char() @@ -15,12 +17,17 @@ class estate_property(models.Model): ) expected_price = fields.Float(required=True) selling_price = fields.Float(readonly=True, copy=False) + + best_price = fields.Float(compute="_compute_best_price") + bedrooms = fields.Integer(default=2) - living_area = fields.Integer() + facades = fields.Integer() garage = fields.Boolean() garden = fields.Boolean() - garden_area = fields.Integer() + living_area = fields.Float() + garden_area = fields.Float() + total_area = fields.Float(compute="_compute_total_area") garden_orientation = fields.Selection( string='Garden Orientation', selection=[('north', 'North'), ('south', 'South'), @@ -40,6 +47,49 @@ class estate_property(models.Model): copy=False, default='new' ) + property_type = fields.Many2one("estate.property.type", string="Property Type", default=None) + buyer = fields.Many2one("res.partner", string="Buyer", copy=False) + seller = fields.Many2one("res.partner", string="Salesperson", default=lambda self: self.env.user) + tag_ids = fields.Many2many("estate.property.tag", string="Tags", default=None) + offer_ids = fields.One2many("estate.property.offer", "property_id", string="Offers") + + @api.depends("living_area", "garden_area") + def _compute_total_area(self): + for record in self: + record.total_area = record.living_area + record.garden_area + + + @api.depends("offer_ids.price") + def _compute_best_price(self): + for record in self: + if record.offer_ids: + record.best_price = max(record.offer_ids.mapped("price")) + else: + record.best_price = 0.0 + + + @api.onchange('garden') + def _onchange_garden(self): + if self.garden: + self.garden_area = 10 + self.garden_orientation = 'north' + else: + self.garden_area = 0 + self.garden_orientation = False + + + def action_sold(self): + for record in self: + if record.state == 'cancelled': + raise UserError("Cancelled property cannot be sold.") + record.state = 'sold' + return True + + def action_cancel(self): + for record in self: + if record.state == 'sold': + raise UserError("Sold property cannot be cancelled.") + record.state = 'cancelled' + return True - \ No newline at end of file diff --git a/real_estate/models/estate_property_offer.py b/real_estate/models/estate_property_offer.py new file mode 100644 index 00000000000..82c41fdfde3 --- /dev/null +++ b/real_estate/models/estate_property_offer.py @@ -0,0 +1,51 @@ +from odoo import models, fields, api +from datetime import timedelta +from odoo.exceptions import UserError + +class EstatePropertyOffer(models.Model): + _name = "estate.property.offer" + _description = "Property Offer" + + price = fields.Float() + status = fields.Selection([ + ('accepted', 'Accepted'), + ('refused', 'Refused') + ]) + partner_id = fields.Many2one("res.partner", string="Partner", required=True) + property_id = fields.Many2one("estate.property", string="Property", required=True) + + validity = fields.Integer(default=7) + date_deadline = fields.Date(compute="_compute_date_deadline", inverse="_inverse_date_deadline") + + @api.depends("create_date", "validity") + def _compute_date_deadline(self): + for record in self: + create_date = record.create_date or fields.Date.today() + record.date_deadline = create_date + timedelta(days=record.validity) + print("Computed date_deadline:", record.date_deadline) + + def _inverse_date_deadline(self): + for record in self: + create_date = record.create_date.date() if record.create_date else fields.Date.today() + record.validity = (record.date_deadline - create_date).days + + + def action_accept(self): + for record in self: + if record.property_id.state == 'sold': + raise UserError("You cannot accept an offer on a sold property.") + record.status = 'accepted' + record.property_id.selling_price = record.price + record.property_id.buyer = record.partner_id + record.property_id.state = 'offer_accepted' + + other_offers = self.search([ + ('property_id', '=', record.property_id.id), + ('id', '!=', record.id) + ]) + other_offers.write({'status': 'refused'}) + + def action_refuse(self): + for offer in self: + offer.status = 'refused' + return True diff --git a/real_estate/models/estate_property_tag.py b/real_estate/models/estate_property_tag.py new file mode 100644 index 00000000000..902eaac884f --- /dev/null +++ b/real_estate/models/estate_property_tag.py @@ -0,0 +1,7 @@ +from odoo import fields, models + +class estate_property_tag(models.Model): + _name = "estate.property.tag" + _description = "Property Tag" + + name = fields.Char(required=True) diff --git a/real_estate/models/estate_property_type.py b/real_estate/models/estate_property_type.py new file mode 100644 index 00000000000..c549ee85e64 --- /dev/null +++ b/real_estate/models/estate_property_type.py @@ -0,0 +1,7 @@ +from odoo import fields, models + + +class estate_property_type(models.Model): + _name = "estate.property.type" + _description = "property type" + name = fields.Char(required=True) \ No newline at end of file diff --git a/real_estate/security/ir.model.access.csv b/real_estate/security/ir.model.access.csv index baafdc83896..6af596881bc 100644 --- a/real_estate/security/ir.model.access.csv +++ b/real_estate/security/ir.model.access.csv @@ -1,2 +1,5 @@ id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink -access_estate_property_user,access.estate.property,model_estate_property,,1,1,1,1 +access_estate_property_user,access.estate.property,model_estate_property,base.group_user,1,1,1,1 +access_estate_property_type_user,access.estate.property.type,model_estate_property_type,base.group_user,1,1,1,1 +access_estate_property_tag_user,access.estate.property.tag,model_estate_property_tag,base.group_user,1,1,1,1 +access_estate_property_offer_user,access.estate.property.offer,model_estate_property_offer,base.group_user,1,1,1,1 \ No newline at end of file diff --git a/real_estate/views/estate_menus.xml b/real_estate/views/estate_menus.xml index fbe5d4d7f79..6f51d831c63 100644 --- a/real_estate/views/estate_menus.xml +++ b/real_estate/views/estate_menus.xml @@ -12,4 +12,16 @@ action="action_estate_property" sequence="10" /> + + + + + + \ No newline at end of file diff --git a/real_estate/views/estate_property_offer_view.xml b/real_estate/views/estate_property_offer_view.xml new file mode 100644 index 00000000000..26ca83e510e --- /dev/null +++ b/real_estate/views/estate_property_offer_view.xml @@ -0,0 +1,39 @@ + + + + estate.property.offer.list + estate.property.offer + + + + + + + + + + + + + estate.property.offer.form + estate.property.offer + +
+
+
+ + + + + + + + + + +
+
+
+
\ No newline at end of file diff --git a/real_estate/views/estate_property_tag_view.xml b/real_estate/views/estate_property_tag_view.xml new file mode 100644 index 00000000000..eb10b67146e --- /dev/null +++ b/real_estate/views/estate_property_tag_view.xml @@ -0,0 +1,30 @@ + + + + estate.property.tag.list + estate.property.tag + + + + + + + + + estate.property.tag.form + estate.property.tag + +
+ + +
+
+ + + Property Tags + estate.property.tag + list,form + + + +
diff --git a/real_estate/views/estate_property_type_view.xml b/real_estate/views/estate_property_type_view.xml new file mode 100644 index 00000000000..e8cc7f075d9 --- /dev/null +++ b/real_estate/views/estate_property_type_view.xml @@ -0,0 +1,8 @@ + + + + Property Types + estate.property.type + list,form + + \ No newline at end of file diff --git a/real_estate/views/estate_property_views.xml b/real_estate/views/estate_property_views.xml index 3a777f16533..f3ed881e3c5 100644 --- a/real_estate/views/estate_property_views.xml +++ b/real_estate/views/estate_property_views.xml @@ -19,6 +19,7 @@ + @@ -28,20 +29,28 @@ estate.property
+
+

+ - + + + + @@ -49,7 +58,6 @@ - @@ -60,7 +68,33 @@ - + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ +
+ + - Property Types - estate.property.type - list,form - - \ No newline at end of file + Property Types + estate.property.type + list,form + + diff --git a/real_estate/views/estate_property_views.xml b/real_estate/views/estate_property_views.xml index f3ed881e3c5..710a82a550a 100644 --- a/real_estate/views/estate_property_views.xml +++ b/real_estate/views/estate_property_views.xml @@ -4,23 +4,31 @@ Properties estate.property list,form + {'search_default_available': 1} - + estate.property.list estate.property - + + - + + + @@ -29,20 +37,33 @@ estate.property
-
-
+ + + + + + + + +
+ + + + + + +
+ + + + + Which cards do you whish to see ? + + + + + + + + + + + diff --git a/awesome_dashboard/static/src/dashboard/dashboard_item/dashboard_item.js b/awesome_dashboard/static/src/dashboard/dashboard_item/dashboard_item.js new file mode 100644 index 00000000000..4644048af21 --- /dev/null +++ b/awesome_dashboard/static/src/dashboard/dashboard_item/dashboard_item.js @@ -0,0 +1,18 @@ +import { Component } from "@odoo/owl"; + +export class DashboardItem extends Component { + static template = "awesome_dashboard.DashboardItem"; + static props = { + slots: { + type: Object, + shape: { + default: Object, + }, + }, + size: { + type: Number, + default: 1, + optional: true, + }, + }; +} diff --git a/awesome_dashboard/static/src/dashboard/dashboard_item/dashboard_item.xml b/awesome_dashboard/static/src/dashboard/dashboard_item/dashboard_item.xml new file mode 100644 index 00000000000..33e1fd98752 --- /dev/null +++ b/awesome_dashboard/static/src/dashboard/dashboard_item/dashboard_item.xml @@ -0,0 +1,10 @@ + + + +
+
+ +
+
+
+
diff --git a/awesome_dashboard/static/src/dashboard/dashboard_items.js b/awesome_dashboard/static/src/dashboard/dashboard_items.js new file mode 100644 index 00000000000..9d6236be7d9 --- /dev/null +++ b/awesome_dashboard/static/src/dashboard/dashboard_items.js @@ -0,0 +1,66 @@ +/** @odoo-module */ + +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(item => { + registry.category("awesome_dashboard").add(item.id, item); +}); diff --git a/awesome_dashboard/static/src/dashboard/number_card/number_card.js b/awesome_dashboard/static/src/dashboard/number_card/number_card.js new file mode 100644 index 00000000000..0de2c3f98b4 --- /dev/null +++ b/awesome_dashboard/static/src/dashboard/number_card/number_card.js @@ -0,0 +1,15 @@ +/** @odoo-module */ + +import { Component } from "@odoo/owl"; + +export class NumberCard extends Component { + static template = "awesome_dashboard.NumberCard"; + static props = { + title: { + type: String, + }, + value: { + type: Number, + } + } +} diff --git a/awesome_dashboard/static/src/dashboard/number_card/number_card.xml b/awesome_dashboard/static/src/dashboard/number_card/number_card.xml new file mode 100644 index 00000000000..3a0713623fa --- /dev/null +++ b/awesome_dashboard/static/src/dashboard/number_card/number_card.xml @@ -0,0 +1,9 @@ + + + + +
+ +
+
+
diff --git a/awesome_dashboard/static/src/dashboard/pie_chart/pie_chart.js b/awesome_dashboard/static/src/dashboard/pie_chart/pie_chart.js new file mode 100644 index 00000000000..ee259956902 --- /dev/null +++ b/awesome_dashboard/static/src/dashboard/pie_chart/pie_chart.js @@ -0,0 +1,49 @@ +/** @odoo-module */ + +import { loadJS } from "@web/core/assets"; +import { getColor } from "@web/core/colors/colors"; +import { + Component, + onWillStart, + useRef, + onMounted, + onWillUnmount, +} from "@odoo/owl"; + +export class PieChart extends Component { + static template = "awesome_dashboard.PieChart"; + static props = { + label: String, + data: Object, + }; + + setup() { + this.canvasRef = useRef("canvas"); + onWillStart(() => loadJS(["/web/static/lib/Chart/Chart.js"])); + onMounted(() => { + this.renderChart(); + }); + onWillUnmount(() => { + this.chart.destroy(); + }); + } + + renderChart() { + const labels = Object.keys(this.props.data); + const data = Object.values(this.props.data); + const color = labels.map((_, index) => getColor(index)); + this.chart = new Chart(this.canvasRef.el, { + type: "pie", + data: { + labels: labels, + datasets: [ + { + label: this.props.label, + data: data, + backgroundColor: color, + }, + ], + }, + }); + } +} diff --git a/awesome_dashboard/static/src/dashboard/pie_chart/pie_chart.xml b/awesome_dashboard/static/src/dashboard/pie_chart/pie_chart.xml new file mode 100644 index 00000000000..7003e2aa5f9 --- /dev/null +++ b/awesome_dashboard/static/src/dashboard/pie_chart/pie_chart.xml @@ -0,0 +1,10 @@ + + + +
+
+ +
+
+
+
diff --git a/awesome_dashboard/static/src/dashboard/pie_chart_card/pie_chart_card.js b/awesome_dashboard/static/src/dashboard/pie_chart_card/pie_chart_card.js new file mode 100644 index 00000000000..a28c2f48c6f --- /dev/null +++ b/awesome_dashboard/static/src/dashboard/pie_chart_card/pie_chart_card.js @@ -0,0 +1,17 @@ +/** @odoo-module */ + +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: { + type: String, + }, + values: { + type: Object, + }, + } +} diff --git a/awesome_dashboard/static/src/dashboard/pie_chart_card/pie_chart_card.xml b/awesome_dashboard/static/src/dashboard/pie_chart_card/pie_chart_card.xml new file mode 100644 index 00000000000..58a6811c83a --- /dev/null +++ b/awesome_dashboard/static/src/dashboard/pie_chart_card/pie_chart_card.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/awesome_dashboard/static/src/dashboard/statistics_service.js b/awesome_dashboard/static/src/dashboard/statistics_service.js new file mode 100644 index 00000000000..1049e290756 --- /dev/null +++ b/awesome_dashboard/static/src/dashboard/statistics_service.js @@ -0,0 +1,18 @@ +import { registry } from "@web/core/registry"; +import { reactive } from "@odoo/owl"; +import { rpc } from "@web/core/network/rpc"; + +const statisticsService = { + dependencies: [], + start() { + const statistics = reactive({ isReady: false }); + async function loadData() { + const updates = await rpc("/awesome_dashboard/statistics"); + Object.assign(statistics, updates, { isReady: true }); + } + setInterval(loadData, 10 * 1000); + loadData(); + return statistics; + }, +}; +registry.category("services").add("awesome_dashboard.statistics", statisticsService); diff --git a/awesome_dashboard/static/src/dashboard_loader.js b/awesome_dashboard/static/src/dashboard_loader.js new file mode 100644 index 00000000000..2c43f9ee72a --- /dev/null +++ b/awesome_dashboard/static/src/dashboard_loader.js @@ -0,0 +1,9 @@ +import { registry } from "@web/core/registry"; +import { LazyComponent } from "@web/core/assets"; +import { Component, xml } from "@odoo/owl"; + +class AwesomeDashboardLoader extends Component { + static components = { LazyComponent }; + static template = xml``; +} +registry.category("actions").add("awesome_dashboard.dashboard", AwesomeDashboardLoader); diff --git a/awesome_owl/__manifest__.py b/awesome_owl/__manifest__.py index 77abad510ef..d973a9d293b 100644 --- a/awesome_owl/__manifest__.py +++ b/awesome_owl/__manifest__.py @@ -27,6 +27,13 @@ 'views/templates.xml', ], 'assets': { + 'web.assets_qweb': [ + 'awesome_owl/static/src/counter/counter.xml', + 'awesome_owl/static/src/card/card.xml', + 'awesome_owl/static/src/todo/todo_list.xml', + 'awesome_owl/static/src/todo/todo_item.xml' + 'awesome_owl/static/src/playground.xml', + ], 'awesome_owl.assets_playground': [ ('include', 'web._assets_helpers'), 'web/static/src/scss/pre_variables.scss', diff --git a/awesome_owl/static/src/card/card.js b/awesome_owl/static/src/card/card.js new file mode 100644 index 00000000000..cdf30444260 --- /dev/null +++ b/awesome_owl/static/src/card/card.js @@ -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:{} + }; + setup() { + this.state = useState({ + isOpen: true, + }); + } + toggleOpen() { + this.state.isOpen = !this.state.isOpen; + } +} diff --git a/awesome_owl/static/src/card/card.xml b/awesome_owl/static/src/card/card.xml new file mode 100644 index 00000000000..23fd7eb472e --- /dev/null +++ b/awesome_owl/static/src/card/card.xml @@ -0,0 +1,20 @@ + + + +
+
+
+ +
+ +
+ +
+ +
+
+
+
+
diff --git a/awesome_owl/static/src/counter/counter.js b/awesome_owl/static/src/counter/counter.js new file mode 100644 index 00000000000..0ccc7b6a65b --- /dev/null +++ b/awesome_owl/static/src/counter/counter.js @@ -0,0 +1,17 @@ +import { Component , useState } from "@odoo/owl"; + +export class Counter extends Component { + static template = "awesome_owl.Counter"; + static props = { + onChange: { type: Function, optional: true }, + }; + setup() { + this.state = useState({ value: 1}); + }; + increment() { + this.state.value++; + if (this.props.onChange) { + this.props.onChange(this.state.value); + } + } +} diff --git a/awesome_owl/static/src/counter/counter.xml b/awesome_owl/static/src/counter/counter.xml new file mode 100644 index 00000000000..0ac1af29985 --- /dev/null +++ b/awesome_owl/static/src/counter/counter.xml @@ -0,0 +1,12 @@ + + + +
+

Counter:

+ +
+
+
diff --git a/awesome_owl/static/src/playground.js b/awesome_owl/static/src/playground.js index 657fb8b07bb..61ada9f1976 100644 --- a/awesome_owl/static/src/playground.js +++ b/awesome_owl/static/src/playground.js @@ -1,7 +1,27 @@ -/** @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/todo_list"; export class Playground extends Component { - static template = "awesome_owl.playground"; + static template = "awesome_owl.playground"; + static components = { Counter, Card, TodoList }; + static props = {}; + setup() { + this.state = useState({ + sum: 2, + }); + + this.counterValues = [1, 1]; + + (this.str1 = markup("
some content
")), + (this.str2 = "some content"); + } + updateCounterValue(index, newValue) { + this.counterValues[index] = newValue; + this.state.sum = this.counterValues.reduce((a, b) => a + b, 0); + } + + onChange1 = (val) => this.updateCounterValue(0, val); + onChange2 = (val) => this.updateCounterValue(1, val); } diff --git a/awesome_owl/static/src/playground.xml b/awesome_owl/static/src/playground.xml index 4fb905d59f9..b428403d631 100644 --- a/awesome_owl/static/src/playground.xml +++ b/awesome_owl/static/src/playground.xml @@ -1,10 +1,25 @@ - + + +
+ + +
+
+

Total Sum:

+
+
+ +
some content
+
- -
- hello world -
-
+ + + +
+
+ +
+
diff --git a/awesome_owl/static/src/todo/todo_item.js b/awesome_owl/static/src/todo/todo_item.js new file mode 100644 index 00000000000..05bf4894533 --- /dev/null +++ b/awesome_owl/static/src/todo/todo_item.js @@ -0,0 +1,24 @@ +/** @odoo-module **/ +import { Component } from "@odoo/owl"; + +export class TodoItem extends Component { + static template = "awesome_owl.TodoItem"; + static props = { + todo: { + type: Object, + shape: { + id: Number, + description: String, + isCompleted: Boolean, + }, + }, + toggleState: Function, + removeTodo: Function, + }; + onToggle() { + this.props.toggleState(this.props.todo.id); + } + onDelete(){ + this.props.removeTodo(this.props.todo.id); + } +} diff --git a/awesome_owl/static/src/todo/todo_item.xml b/awesome_owl/static/src/todo/todo_item.xml new file mode 100644 index 00000000000..67f3f8820db --- /dev/null +++ b/awesome_owl/static/src/todo/todo_item.xml @@ -0,0 +1,17 @@ + + + +
+ +
+ + . + +
+ + + + +
+
+
diff --git a/awesome_owl/static/src/todo/todo_list.js b/awesome_owl/static/src/todo/todo_list.js new file mode 100644 index 00000000000..a6a2e19e75c --- /dev/null +++ b/awesome_owl/static/src/todo/todo_list.js @@ -0,0 +1,45 @@ +/** @odoo-module **/ +import { Component, useState } from "@odoo/owl"; +import { TodoItem } from "./todo_item"; + +let nextId = 0; +export class TodoList extends Component { + static template = "awesome_owl.TodoList"; + static components = { TodoItem }; + static props = {}; + + setup() { + this.todos = useState([]); + this.inputRefValue = useState({ value: "" }); + this.toggleState = this.toggleState.bind(this) + this.removeTodo = this.removeTodo.bind(this) + } + + addTodo(ev) { + if (ev.keyCode === 13) { + const desc = this.inputRefValue.value.trim(); + if (!desc) return; + + this.todos.push({ + id: nextId++, + description: desc, + isCompleted: false, + }); + + this.inputRefValue.value = ""; + } + } + toggleState(id) { + const todo = this.todos.find((todo) => todo.id === id); + if (todo) { + todo.isCompleted = !todo.isCompleted; + } + } + + removeTodo(id) { + const index = this.todos.findIndex((todo) => todo.id === id); + if (index >= 0) { + this.todos.splice(index, 1); + } +} +} diff --git a/awesome_owl/static/src/todo/todo_list.xml b/awesome_owl/static/src/todo/todo_list.xml new file mode 100644 index 00000000000..f5f035befbc --- /dev/null +++ b/awesome_owl/static/src/todo/todo_list.xml @@ -0,0 +1,12 @@ + + + +
+ + + + +
+
+
diff --git a/real_estate/views/estate_property_tag_view.xml b/real_estate/views/estate_property_tag_view.xml index e0740a31ffb..9b492ade771 100644 --- a/real_estate/views/estate_property_tag_view.xml +++ b/real_estate/views/estate_property_tag_view.xml @@ -7,7 +7,6 @@ -