From b16e6435638bf7e10fa10063f9a4f206e871eef9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9ry=20Debongnie?= Date: Sun, 9 Oct 2022 20:21:18 +0200 Subject: [PATCH 1/8] [ADD] Discover the JavaScript framework --- README.md | 12 +++--- awesome_clicker/__init__.py | 1 + awesome_clicker/__manifest__.py | 29 +++++++++++++ awesome_dashboard/__init__.py | 3 ++ awesome_dashboard/__manifest__.py | 30 +++++++++++++ awesome_dashboard/controllers/__init__.py | 3 ++ awesome_dashboard/controllers/controllers.py | 36 ++++++++++++++++ awesome_dashboard/static/src/dashboard.js | 10 +++++ awesome_dashboard/static/src/dashboard.xml | 8 ++++ awesome_dashboard/views/views.xml | 11 +++++ awesome_gallery/__init__.py | 2 + awesome_gallery/__manifest__.py | 26 ++++++++++++ awesome_gallery/models/__init__.py | 4 ++ awesome_gallery/models/ir_action.py | 10 +++++ awesome_gallery/models/ir_ui_view.py | 8 ++++ awesome_gallery/static/src/gallery_view.js | 3 ++ awesome_gallery/views/views.xml | 19 +++++++++ awesome_kanban/__init__.py | 1 + awesome_kanban/__manifest__.py | 26 ++++++++++++ .../static/src/awesome_kanban_view.js | 3 ++ awesome_kanban/views/views.xml | 15 +++++++ awesome_owl/__init__.py | 3 ++ awesome_owl/__manifest__.py | 42 +++++++++++++++++++ awesome_owl/controllers/__init__.py | 3 ++ awesome_owl/controllers/controllers.py | 10 +++++ awesome_owl/static/src/main.js | 11 +++++ awesome_owl/static/src/playground.js | 7 ++++ awesome_owl/static/src/playground.xml | 10 +++++ awesome_owl/views/templates.xml | 15 +++++++ 29 files changed, 355 insertions(+), 6 deletions(-) create mode 100644 awesome_clicker/__init__.py create mode 100644 awesome_clicker/__manifest__.py create mode 100644 awesome_dashboard/__init__.py create mode 100644 awesome_dashboard/__manifest__.py create mode 100644 awesome_dashboard/controllers/__init__.py create mode 100644 awesome_dashboard/controllers/controllers.py create mode 100644 awesome_dashboard/static/src/dashboard.js create mode 100644 awesome_dashboard/static/src/dashboard.xml create mode 100644 awesome_dashboard/views/views.xml create mode 100644 awesome_gallery/__init__.py create mode 100644 awesome_gallery/__manifest__.py create mode 100644 awesome_gallery/models/__init__.py create mode 100644 awesome_gallery/models/ir_action.py create mode 100644 awesome_gallery/models/ir_ui_view.py create mode 100644 awesome_gallery/static/src/gallery_view.js create mode 100644 awesome_gallery/views/views.xml create mode 100644 awesome_kanban/__init__.py create mode 100644 awesome_kanban/__manifest__.py create mode 100644 awesome_kanban/static/src/awesome_kanban_view.js create mode 100644 awesome_kanban/views/views.xml create mode 100644 awesome_owl/__init__.py create mode 100644 awesome_owl/__manifest__.py create mode 100644 awesome_owl/controllers/__init__.py create mode 100644 awesome_owl/controllers/controllers.py create mode 100644 awesome_owl/static/src/main.js create mode 100644 awesome_owl/static/src/playground.js create mode 100644 awesome_owl/static/src/playground.xml create mode 100644 awesome_owl/views/templates.xml diff --git a/README.md b/README.md index 0c6667bb6f2..89e6a2b2296 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,10 @@ # Odoo tutorials This repository hosts the code for the bases and solutions of the -[official Odoo tutorials](https://www.odoo.com/documentation/16.0/developer/howtos.html). +[official Odoo tutorials](https://www.odoo.com/documentation/17.0/developer/tutorials.html). -It has 2 branches for each Odoo version: one for the bases and one for -the solutions. For example, `16.0` and `16.0-solutions`. The first -contains the code of the modules that serve as base for the tutorials, -and the second contains the code of the same modules with the complete -solution. \ No newline at end of file +It has 3 branches for each Odoo version: one for the bases, one for +[Discover the JS framework](https://www.odoo.com/documentation/17.0/developer/tutorials/discover_js_framework.html) solutions and one for [Master the Odoo web framework](https://www.odoo.com/documentation/17.0/developer/tutorials/master_odoo_web_framework.html) solutions. For example, `17.0`, `17.0-discover-js-framework-solutions` and `17.0-master-odoo-web-framework-solutions`. +The first contains the code of the modules that serve as base for the tutorials, +and the others contains the code of each chapter with the complete +solution. diff --git a/awesome_clicker/__init__.py b/awesome_clicker/__init__.py new file mode 100644 index 00000000000..40a96afc6ff --- /dev/null +++ b/awesome_clicker/__init__.py @@ -0,0 +1 @@ +# -*- coding: utf-8 -*- diff --git a/awesome_clicker/__manifest__.py b/awesome_clicker/__manifest__.py new file mode 100644 index 00000000000..e57ef4d5bb0 --- /dev/null +++ b/awesome_clicker/__manifest__.py @@ -0,0 +1,29 @@ +# -*- coding: utf-8 -*- +{ + 'name': "Awesome Clicker", + + 'summary': """ + Starting module for "Master the Odoo web framework, chapter 1: Build a Clicker game" + """, + + 'description': """ + Starting module for "Master the Odoo web framework, chapter 1: Build a Clicker game" + """, + + 'author': "Odoo", + 'website': "https://www.odoo.com/", + 'category': 'Tutorials/AwesomeClicker', + 'version': '0.1', + 'application': True, + 'installable': True, + 'depends': ['base', 'web'], + + 'data': [], + 'assets': { + 'web.assets_backend': [ + 'awesome_clicker/static/src/**/*', + ], + + }, + 'license': 'AGPL-3' +} diff --git a/awesome_dashboard/__init__.py b/awesome_dashboard/__init__.py new file mode 100644 index 00000000000..b0f26a9a602 --- /dev/null +++ b/awesome_dashboard/__init__.py @@ -0,0 +1,3 @@ +# -*- coding: utf-8 -*- + +from . import controllers diff --git a/awesome_dashboard/__manifest__.py b/awesome_dashboard/__manifest__.py new file mode 100644 index 00000000000..31406e8addb --- /dev/null +++ b/awesome_dashboard/__manifest__.py @@ -0,0 +1,30 @@ +# -*- coding: utf-8 -*- +{ + 'name': "Awesome Dashboard", + + 'summary': """ + Starting module for "Discover the JS framework, chapter 2: Build a dashboard" + """, + + 'description': """ + Starting module for "Discover the JS framework, chapter 2: Build a dashboard" + """, + + 'author': "Odoo", + 'website': "https://www.odoo.com/", + 'category': 'Tutorials/AwesomeDashboard', + 'version': '0.1', + 'application': True, + 'installable': True, + 'depends': ['base', 'web', 'mail', 'crm'], + + 'data': [ + 'views/views.xml', + ], + 'assets': { + 'web.assets_backend': [ + 'awesome_dashboard/static/src/**/*', + ], + }, + 'license': 'AGPL-3' +} diff --git a/awesome_dashboard/controllers/__init__.py b/awesome_dashboard/controllers/__init__.py new file mode 100644 index 00000000000..457bae27e11 --- /dev/null +++ b/awesome_dashboard/controllers/__init__.py @@ -0,0 +1,3 @@ +# -*- coding: utf-8 -*- + +from . import controllers \ No newline at end of file diff --git a/awesome_dashboard/controllers/controllers.py b/awesome_dashboard/controllers/controllers.py new file mode 100644 index 00000000000..56d4a051287 --- /dev/null +++ b/awesome_dashboard/controllers/controllers.py @@ -0,0 +1,36 @@ +# -*- coding: utf-8 -*- + +import logging +import random + +from odoo import http +from odoo.http import request + +logger = logging.getLogger(__name__) + +class AwesomeDashboard(http.Controller): + @http.route('/awesome_dashboard/statistics', type='json', auth='user') + def get_statistics(self): + """ + Returns a dict of statistics about the orders: + 'average_quantity': the average number of t-shirts by order + 'average_time': the average time (in hours) elapsed between the + moment an order is created, and the moment is it sent + 'nb_cancelled_orders': the number of cancelled orders, this month + 'nb_new_orders': the number of new orders, this month + 'total_amount': the total amount of orders, this month + """ + + return { + 'average_quantity': random.randint(4, 12), + 'average_time': random.randint(4, 123), + 'nb_cancelled_orders': random.randint(0, 50), + 'nb_new_orders': random.randint(10, 200), + 'orders_by_size': { + 'm': random.randint(0, 150), + 's': random.randint(0, 150), + 'xl': random.randint(0, 150), + }, + 'total_amount': random.randint(100, 1000) + } + diff --git a/awesome_dashboard/static/src/dashboard.js b/awesome_dashboard/static/src/dashboard.js new file mode 100644 index 00000000000..637fa4bb972 --- /dev/null +++ b/awesome_dashboard/static/src/dashboard.js @@ -0,0 +1,10 @@ +/** @odoo-module **/ + +import { Component } from "@odoo/owl"; +import { registry } from "@web/core/registry"; + +class AwesomeDashboard extends Component { + static template = "awesome_dashboard.AwesomeDashboard"; +} + +registry.category("actions").add("awesome_dashboard.dashboard", AwesomeDashboard); diff --git a/awesome_dashboard/static/src/dashboard.xml b/awesome_dashboard/static/src/dashboard.xml new file mode 100644 index 00000000000..1a2ac9a2fed --- /dev/null +++ b/awesome_dashboard/static/src/dashboard.xml @@ -0,0 +1,8 @@ + + + + + hello dashboard + + + diff --git a/awesome_dashboard/views/views.xml b/awesome_dashboard/views/views.xml new file mode 100644 index 00000000000..47fb2b6f258 --- /dev/null +++ b/awesome_dashboard/views/views.xml @@ -0,0 +1,11 @@ + + + + Dashboard + awesome_dashboard.dashboard + + + + + + diff --git a/awesome_gallery/__init__.py b/awesome_gallery/__init__.py new file mode 100644 index 00000000000..a0fdc10fe11 --- /dev/null +++ b/awesome_gallery/__init__.py @@ -0,0 +1,2 @@ +# -*- coding: utf-8 -*- +from . import models diff --git a/awesome_gallery/__manifest__.py b/awesome_gallery/__manifest__.py new file mode 100644 index 00000000000..624766dca89 --- /dev/null +++ b/awesome_gallery/__manifest__.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- +{ + 'name': "Gallery View", + 'summary': """ + Starting module for "Master the Odoo web framework, chapter 3: Create a Gallery View" + """, + + 'description': """ + Starting module for "Master the Odoo web framework, chapter 3: Create a Gallery View" + """, + + 'version': '0.1', + 'application': True, + 'category': 'Tutorials/AwesomeGallery', + 'installable': True, + 'depends': ['web', 'contacts'], + 'data': [ + 'views/views.xml', + ], + 'assets': { + 'web.assets_backend': [ + 'awesome_gallery/static/src/**/*', + ], + }, + 'license': 'AGPL-3' +} diff --git a/awesome_gallery/models/__init__.py b/awesome_gallery/models/__init__.py new file mode 100644 index 00000000000..7f0930ee744 --- /dev/null +++ b/awesome_gallery/models/__init__.py @@ -0,0 +1,4 @@ +# -*- coding: utf-8 -*- +# import filename_python_file_within_folder_or_subfolder +from . import ir_action +from . import ir_ui_view diff --git a/awesome_gallery/models/ir_action.py b/awesome_gallery/models/ir_action.py new file mode 100644 index 00000000000..eae20acbf5c --- /dev/null +++ b/awesome_gallery/models/ir_action.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +from odoo import fields, models + + +class ActWindowView(models.Model): + _inherit = 'ir.actions.act_window.view' + + view_mode = fields.Selection(selection_add=[ + ('gallery', "Awesome Gallery") + ], ondelete={'gallery': 'cascade'}) diff --git a/awesome_gallery/models/ir_ui_view.py b/awesome_gallery/models/ir_ui_view.py new file mode 100644 index 00000000000..0c11b8298ac --- /dev/null +++ b/awesome_gallery/models/ir_ui_view.py @@ -0,0 +1,8 @@ +# -*- coding: utf-8 -*- +from odoo import fields, models + + +class View(models.Model): + _inherit = 'ir.ui.view' + + type = fields.Selection(selection_add=[('gallery', "Awesome Gallery")]) diff --git a/awesome_gallery/static/src/gallery_view.js b/awesome_gallery/static/src/gallery_view.js new file mode 100644 index 00000000000..db904d1f478 --- /dev/null +++ b/awesome_gallery/static/src/gallery_view.js @@ -0,0 +1,3 @@ +/** @odoo-module */ + +// TODO: Begin here! diff --git a/awesome_gallery/views/views.xml b/awesome_gallery/views/views.xml new file mode 100644 index 00000000000..56327365875 --- /dev/null +++ b/awesome_gallery/views/views.xml @@ -0,0 +1,19 @@ + + + + + Contacts + res.partner + kanban,tree,form,activity + + {'default_is_company': True} + +

+ Create a Contact in your address book +

+ Odoo helps you track all activities related to your contacts. +

+
+
+
+
diff --git a/awesome_kanban/__init__.py b/awesome_kanban/__init__.py new file mode 100644 index 00000000000..40a96afc6ff --- /dev/null +++ b/awesome_kanban/__init__.py @@ -0,0 +1 @@ +# -*- coding: utf-8 -*- diff --git a/awesome_kanban/__manifest__.py b/awesome_kanban/__manifest__.py new file mode 100644 index 00000000000..affef78bb12 --- /dev/null +++ b/awesome_kanban/__manifest__.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- +{ + 'name': "Awesome Kanban", + 'summary': """ + Starting module for "Master the Odoo web framework, chapter 4: Customize a kanban view" + """, + + 'description': """ + Starting module for "Master the Odoo web framework, chapter 4: Customize a kanban view. + """, + + 'version': '0.1', + 'application': True, + 'category': 'Tutorials/AwesomeKanban', + 'installable': True, + 'depends': ['web', 'crm'], + 'data': [ + 'views/views.xml', + ], + 'assets': { + 'web.assets_backend': [ + 'awesome_kanban/static/src/**/*', + ], + }, + 'license': 'AGPL-3' +} diff --git a/awesome_kanban/static/src/awesome_kanban_view.js b/awesome_kanban/static/src/awesome_kanban_view.js new file mode 100644 index 00000000000..9f33fc1300b --- /dev/null +++ b/awesome_kanban/static/src/awesome_kanban_view.js @@ -0,0 +1,3 @@ +/** @odoo-module */ + +// TODO: Define here your AwesomeKanban view diff --git a/awesome_kanban/views/views.xml b/awesome_kanban/views/views.xml new file mode 100644 index 00000000000..548b2907b6e --- /dev/null +++ b/awesome_kanban/views/views.xml @@ -0,0 +1,15 @@ + + + + + crm.lead.kanban.lead.awesome_gallery + crm.lead + + + + awesome_kanban + + + + + diff --git a/awesome_owl/__init__.py b/awesome_owl/__init__.py new file mode 100644 index 00000000000..457bae27e11 --- /dev/null +++ b/awesome_owl/__init__.py @@ -0,0 +1,3 @@ +# -*- coding: utf-8 -*- + +from . import controllers \ No newline at end of file diff --git a/awesome_owl/__manifest__.py b/awesome_owl/__manifest__.py new file mode 100644 index 00000000000..77abad510ef --- /dev/null +++ b/awesome_owl/__manifest__.py @@ -0,0 +1,42 @@ +# -*- coding: utf-8 -*- +{ + 'name': "Awesome Owl", + + 'summary': """ + Starting module for "Discover the JS framework, chapter 1: Owl components" + """, + + 'description': """ + Starting module for "Discover the JS framework, chapter 1: Owl components" + """, + + 'author': "Odoo", + 'website': "https://www.odoo.com", + + # Categories can be used to filter modules in modules listing + # Check https://github.com/odoo/odoo/blob/15.0/odoo/addons/base/data/ir_module_category_data.xml + # for the full list + 'category': 'Tutorials/AwesomeOwl', + 'version': '0.1', + + # any module necessary for this one to work correctly + 'depends': ['base', 'web'], + 'application': True, + 'installable': True, + 'data': [ + 'views/templates.xml', + ], + 'assets': { + 'awesome_owl.assets_playground': [ + ('include', 'web._assets_helpers'), + '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'), + ('include', 'web._assets_core'), + 'web/static/src/libs/fontawesome/css/font-awesome.css', + 'awesome_owl/static/src/**/*', + ], + }, + 'license': 'AGPL-3' +} diff --git a/awesome_owl/controllers/__init__.py b/awesome_owl/controllers/__init__.py new file mode 100644 index 00000000000..457bae27e11 --- /dev/null +++ b/awesome_owl/controllers/__init__.py @@ -0,0 +1,3 @@ +# -*- coding: utf-8 -*- + +from . import controllers \ No newline at end of file diff --git a/awesome_owl/controllers/controllers.py b/awesome_owl/controllers/controllers.py new file mode 100644 index 00000000000..bccfd6fe283 --- /dev/null +++ b/awesome_owl/controllers/controllers.py @@ -0,0 +1,10 @@ +from odoo import http +from odoo.http import request, route + +class OwlPlayground(http.Controller): + @http.route(['/awesome_owl'], type='http', auth='public') + def show_playground(self): + """ + Renders the owl playground page + """ + return request.render('awesome_owl.playground') diff --git a/awesome_owl/static/src/main.js b/awesome_owl/static/src/main.js new file mode 100644 index 00000000000..1af6c827e0b --- /dev/null +++ b/awesome_owl/static/src/main.js @@ -0,0 +1,11 @@ +import { whenReady } from "@odoo/owl"; +import { mountComponent } from "@web/env"; +import { Playground } from "./playground"; + +const config = { + dev: true, + name: "Owl Tutorial", +}; + +// Mount the Playground component when the document.body is ready +whenReady(() => mountComponent(Playground, document.body, config)); diff --git a/awesome_owl/static/src/playground.js b/awesome_owl/static/src/playground.js new file mode 100644 index 00000000000..657fb8b07bb --- /dev/null +++ b/awesome_owl/static/src/playground.js @@ -0,0 +1,7 @@ +/** @odoo-module **/ + +import { Component } from "@odoo/owl"; + +export class Playground extends Component { + static template = "awesome_owl.playground"; +} diff --git a/awesome_owl/static/src/playground.xml b/awesome_owl/static/src/playground.xml new file mode 100644 index 00000000000..4fb905d59f9 --- /dev/null +++ b/awesome_owl/static/src/playground.xml @@ -0,0 +1,10 @@ + + + + +
+ hello world +
+
+ +
diff --git a/awesome_owl/views/templates.xml b/awesome_owl/views/templates.xml new file mode 100644 index 00000000000..aa54c1a7241 --- /dev/null +++ b/awesome_owl/views/templates.xml @@ -0,0 +1,15 @@ + + + + + From 4f1d2554d6e399c49fb53ae2c7224c91a87aec26 Mon Sep 17 00:00:00 2001 From: "Antoine Vandevenne (anv)" Date: Tue, 19 Nov 2024 16:55:51 +0100 Subject: [PATCH 2/8] [IMP] README: update the repository's description --- README.md | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 89e6a2b2296..c80c29b24c8 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,11 @@ # Odoo tutorials -This repository hosts the code for the bases and solutions of the -[official Odoo tutorials](https://www.odoo.com/documentation/17.0/developer/tutorials.html). +This repository hosts the code for the bases of the modules used in the +[official Odoo tutorials](https://www.odoo.com/documentation/lastest/developer/tutorials.html). -It has 3 branches for each Odoo version: one for the bases, one for -[Discover the JS framework](https://www.odoo.com/documentation/17.0/developer/tutorials/discover_js_framework.html) solutions and one for [Master the Odoo web framework](https://www.odoo.com/documentation/17.0/developer/tutorials/master_odoo_web_framework.html) solutions. For example, `17.0`, `17.0-discover-js-framework-solutions` and `17.0-master-odoo-web-framework-solutions`. -The first contains the code of the modules that serve as base for the tutorials, -and the others contains the code of each chapter with the complete -solution. +It has 3 branches for each Odoo version: one for the bases, one for the +[Discover the JS framework](https://www.odoo.com/documentation/latest/developer/tutorials/discover_js_framework.html) +tutorial's solutions, and one for the +[Master the Odoo web framework](https://www.odoo.com/documentation/lastest/developer/tutorials/master_odoo_web_framework.html) +tutorial's solutions. For example, `17.0`, `17.0-discover-js-framework-solutions` and +`17.0-master-odoo-web-framework-solutions`. From 4c650f360a36fd3f64edddec4caa867799704213 Mon Sep 17 00:00:00 2001 From: "Florian(flg)" Date: Wed, 20 Nov 2024 08:07:03 +0000 Subject: [PATCH 3/8] [FIX] README.md: Fixing typos There are 2 typos in the README.md that lead to broken links. closes odoo/tutorials#196 X-original-commit: 460af3fa62388bf4d118b0dc7ec1bad9697cf02a Signed-off-by: Antoine Vandevenne (anv) --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index c80c29b24c8..a0158d919ee 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,11 @@ # Odoo tutorials This repository hosts the code for the bases of the modules used in the -[official Odoo tutorials](https://www.odoo.com/documentation/lastest/developer/tutorials.html). +[official Odoo tutorials](https://www.odoo.com/documentation/latest/developer/tutorials.html). It has 3 branches for each Odoo version: one for the bases, one for the [Discover the JS framework](https://www.odoo.com/documentation/latest/developer/tutorials/discover_js_framework.html) tutorial's solutions, and one for the -[Master the Odoo web framework](https://www.odoo.com/documentation/lastest/developer/tutorials/master_odoo_web_framework.html) +[Master the Odoo web framework](https://www.odoo.com/documentation/latest/developer/tutorials/master_odoo_web_framework.html) tutorial's solutions. For example, `17.0`, `17.0-discover-js-framework-solutions` and `17.0-master-odoo-web-framework-solutions`. From 0644d2becbe4b366594eceb214830607d87c6c56 Mon Sep 17 00:00:00 2001 From: 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 Date: Tue, 4 Feb 2025 15:28:46 +0530 Subject: [PATCH 4/8] [IMP] tutorials: changed Read.md --- README.md | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/README.md b/README.md index a0158d919ee..7db8f6b6b97 100644 --- a/README.md +++ b/README.md @@ -1,11 +1 @@ -# Odoo tutorials - -This repository hosts the code for the bases of the modules used in the -[official Odoo tutorials](https://www.odoo.com/documentation/latest/developer/tutorials.html). - -It has 3 branches for each Odoo version: one for the bases, one for the -[Discover the JS framework](https://www.odoo.com/documentation/latest/developer/tutorials/discover_js_framework.html) -tutorial's solutions, and one for the -[Master the Odoo web framework](https://www.odoo.com/documentation/latest/developer/tutorials/master_odoo_web_framework.html) -tutorial's solutions. For example, `17.0`, `17.0-discover-js-framework-solutions` and -`17.0-master-odoo-web-framework-solutions`. +This is read.me file! \ No newline at end of file From 50668505ff20ce876de29eff55a68f5c9d6d5b21 Mon Sep 17 00:00:00 2001 From: Garvish Panchal Date: Mon, 28 Apr 2025 16:16:34 +0530 Subject: [PATCH 5/8] Add dynamic content snippet --- .vscode/settings.json | 3 + website_dynamic_snippet/__init__.py | 0 website_dynamic_snippet/__manifest__.py | 17 +++++ .../src/js/dynamic_snippet_sale_orders.js | 67 +++++++++++++++++++ .../static/src/xml/sale_order_snippets.xml | 42 ++++++++++++ .../views/snippets/snippet_templates.xml | 23 +++++++ 6 files changed, 152 insertions(+) create mode 100644 .vscode/settings.json create mode 100644 website_dynamic_snippet/__init__.py create mode 100644 website_dynamic_snippet/__manifest__.py create mode 100644 website_dynamic_snippet/static/src/js/dynamic_snippet_sale_orders.js create mode 100644 website_dynamic_snippet/static/src/xml/sale_order_snippets.xml create mode 100644 website_dynamic_snippet/views/snippets/snippet_templates.xml diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000000..277f7c9898f --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "python.languageServer": "None" +} diff --git a/website_dynamic_snippet/__init__.py b/website_dynamic_snippet/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/website_dynamic_snippet/__manifest__.py b/website_dynamic_snippet/__manifest__.py new file mode 100644 index 00000000000..25f33c54c6a --- /dev/null +++ b/website_dynamic_snippet/__manifest__.py @@ -0,0 +1,17 @@ +{ + 'name': 'Website Dynamic Snippet', + 'version': '18.0.1.0.0', + 'category': 'Website', + 'depends': ['website_sale'], + 'data': [ + 'views/snippets/snippet_templates.xml', + ], + 'assets': { + 'web.assets_frontend': [ + '/website_dynamic_snippet/static/src/xml/sale_order_snippets.xml', + '/website_dynamic_snippet/static/src/js/dynamic_snippet_sale_orders.js', + ], + }, + 'installable': True, + 'application': True, +} diff --git a/website_dynamic_snippet/static/src/js/dynamic_snippet_sale_orders.js b/website_dynamic_snippet/static/src/js/dynamic_snippet_sale_orders.js new file mode 100644 index 00000000000..bcc9391f9fd --- /dev/null +++ b/website_dynamic_snippet/static/src/js/dynamic_snippet_sale_orders.js @@ -0,0 +1,67 @@ +/** @odoo-module **/ +import { renderToElement } from "@web/core/utils/render"; +import publicWidget from "@web/legacy/js/public/public_widget"; + +publicWidget.registry.get_product_tab = publicWidget.Widget.extend({ + selector: '.categories_section', + + init() { + this._super(...arguments); + this.orm = this.bindService("orm"); + this.offset = 0; + this.limit = 10; + }, + + async willStart() { + await this.loadCategories(); + }, + + events: { + 'click .load-more-button': '_onLoadMoreClick', + }, + + async loadCategories() { + const result = await this.orm.searchRead( + 'sale.order', + [], + ['id', 'name', 'partner_id', 'amount_total', 'state'], + { + offset: this.offset, + limit: this.limit, + order: 'id ASC', + } + ); + + if (result && result.length) { + const content = await renderToElement( + 'website_dynamic_snippet.sale_order_snippet', + { result: result } + ); + + if (this.offset === 0) { + this.el.innerHTML = ''; + this.el.appendChild(content); + } else { + const newRows = content.querySelectorAll('tbody > tr'); + const existingTbody = this.el.querySelector('tbody'); + if (existingTbody) { + newRows.forEach(row => { + existingTbody.appendChild(row); + }); + } + } + + this.offset += result.length; + } else { + const loadMoreButton = this.el.querySelector('.load-more-button'); + if (loadMoreButton) { + loadMoreButton.style.display = 'none'; + } + } + }, + + async _onLoadMoreClick(ev) { + ev.preventDefault(); + await this.loadCategories(); + }, +}); diff --git a/website_dynamic_snippet/static/src/xml/sale_order_snippets.xml b/website_dynamic_snippet/static/src/xml/sale_order_snippets.xml new file mode 100644 index 00000000000..294206d603c --- /dev/null +++ b/website_dynamic_snippet/static/src/xml/sale_order_snippets.xml @@ -0,0 +1,42 @@ + + + + +
+
+

Sale Order Details

+
+
+ + + + + + + + + + + + + + + + + +
NumberCustomer NameStatus
+ +
+ +
+ +
+ +
+
+
+
+
+
diff --git a/website_dynamic_snippet/views/snippets/snippet_templates.xml b/website_dynamic_snippet/views/snippets/snippet_templates.xml new file mode 100644 index 00000000000..3b501df9cc0 --- /dev/null +++ b/website_dynamic_snippet/views/snippets/snippet_templates.xml @@ -0,0 +1,23 @@ + + + + + + + + From c6eaeae39b722ea8288212c58c8e003bc343d207 Mon Sep 17 00:00:00 2001 From: Garvish Panchal Date: Wed, 30 Apr 2025 15:56:55 +0530 Subject: [PATCH 6/8] Add dynamic content snippet + 2 --- website_dynamic_snippet/__manifest__.py | 4 + .../src/js/dynamic_snippet_sale_orders.js | 8 +- .../s_dynamic_sale_order_snippet/options.js | 36 +++++++++ .../static/src/xml/sale_order_snippets.xml | 75 +++++++++++-------- .../views/snippets/snippet_templates.xml | 19 +++++ 5 files changed, 106 insertions(+), 36 deletions(-) create mode 100644 website_dynamic_snippet/static/src/snippets/s_dynamic_sale_order_snippet/options.js diff --git a/website_dynamic_snippet/__manifest__.py b/website_dynamic_snippet/__manifest__.py index 25f33c54c6a..97a46e802ff 100644 --- a/website_dynamic_snippet/__manifest__.py +++ b/website_dynamic_snippet/__manifest__.py @@ -10,7 +10,11 @@ 'web.assets_frontend': [ '/website_dynamic_snippet/static/src/xml/sale_order_snippets.xml', '/website_dynamic_snippet/static/src/js/dynamic_snippet_sale_orders.js', + '/website_dynamic_snippet/static/src/js/layout_switcher.js', ], + 'website.assets_wysiwyg': [ + 'website_dynamic_snippet/static/src/snippets/s_dynamic_sale_order_snippet/options.js', + ] }, 'installable': True, 'application': True, diff --git a/website_dynamic_snippet/static/src/js/dynamic_snippet_sale_orders.js b/website_dynamic_snippet/static/src/js/dynamic_snippet_sale_orders.js index bcc9391f9fd..6b18505690e 100644 --- a/website_dynamic_snippet/static/src/js/dynamic_snippet_sale_orders.js +++ b/website_dynamic_snippet/static/src/js/dynamic_snippet_sale_orders.js @@ -5,6 +5,10 @@ import publicWidget from "@web/legacy/js/public/public_widget"; publicWidget.registry.get_product_tab = publicWidget.Widget.extend({ selector: '.categories_section', + events: { + 'click .load-more-button': '_onLoadMoreClick', + }, + init() { this._super(...arguments); this.orm = this.bindService("orm"); @@ -16,10 +20,6 @@ publicWidget.registry.get_product_tab = publicWidget.Widget.extend({ await this.loadCategories(); }, - events: { - 'click .load-more-button': '_onLoadMoreClick', - }, - async loadCategories() { const result = await this.orm.searchRead( 'sale.order', diff --git a/website_dynamic_snippet/static/src/snippets/s_dynamic_sale_order_snippet/options.js b/website_dynamic_snippet/static/src/snippets/s_dynamic_sale_order_snippet/options.js new file mode 100644 index 00000000000..1417a46fdd1 --- /dev/null +++ b/website_dynamic_snippet/static/src/snippets/s_dynamic_sale_order_snippet/options.js @@ -0,0 +1,36 @@ +/** @odoo-module **/ + +import publicWidget from "@web_editor/js/public/widget"; + +publicWidget.registry.dynamic_snippet_sale_order = publicWidget.Widget.extend({ + selector: ".categories_section", + + xmlDependencies: ["/website_dynamic_snippet/static/src/xml/sale_order_snippet.xml"], + + start: function () { + this.layout = this.el.dataset.layout || 'list'; + this._render(); + return this._super(...arguments); + }, + + _render: function () { + this._rpc({ + route: '/your/data/route', + params: { /* your logic */ }, + }).then(data => { + this.$el.find(".card_list_wrapper").html( + qweb.render("website_dynamic_snippet.sale_order_snippet", { + result: data.orders, + layout: this.layout, + }) + ); + }); + }, + + onChangeOption(previewMode, widgetValue, params) { + if (['grid', 'list'].includes(widgetValue)) { + this.layout = widgetValue; + this._render(); + } + }, +}); diff --git a/website_dynamic_snippet/static/src/xml/sale_order_snippets.xml b/website_dynamic_snippet/static/src/xml/sale_order_snippets.xml index 294206d603c..f8bbd5a3d38 100644 --- a/website_dynamic_snippet/static/src/xml/sale_order_snippets.xml +++ b/website_dynamic_snippet/static/src/xml/sale_order_snippets.xml @@ -1,42 +1,53 @@ - -
-
-

Sale Order Details

-
-
- - +
+

Sale Order Details

+
+ + +
+ + + + + + + + + - - - + + + - - - - - - - - - - -
NumberCustomer NameStatus
NumberCustomer NameStatus
- -
- -
- -
- + + + + + + +
+ +
+
+
+
Order #
+

Customer:

+

Status:

+
+
+
+
+
+ +
+
-
+
diff --git a/website_dynamic_snippet/views/snippets/snippet_templates.xml b/website_dynamic_snippet/views/snippets/snippet_templates.xml index 3b501df9cc0..4024f57972e 100644 --- a/website_dynamic_snippet/views/snippets/snippet_templates.xml +++ b/website_dynamic_snippet/views/snippets/snippet_templates.xml @@ -20,4 +20,23 @@ + + + From e5881c1ebde74397613496742c8f27e59d04f0e7 Mon Sep 17 00:00:00 2001 From: Garvish Panchal Date: Fri, 2 May 2025 15:16:30 +0530 Subject: [PATCH 7/8] Add dynamic content snippet + 3 --- website_dynamic_snippet/__manifest__.py | 7 ++- .../s_dynamic_sale_order_snippet/000.js} | 33 +++++++++---- .../s_dynamic_sale_order_snippet/000.xml} | 11 ++--- .../s_dynamic_sale_order_snippet/options.js | 48 ++++++++----------- .../{snippet_templates.xml => snippets.xml} | 17 ++----- 5 files changed, 55 insertions(+), 61 deletions(-) rename website_dynamic_snippet/static/src/{js/dynamic_snippet_sale_orders.js => snippets/s_dynamic_sale_order_snippet/000.js} (59%) rename website_dynamic_snippet/static/src/{xml/sale_order_snippets.xml => snippets/s_dynamic_sale_order_snippet/000.xml} (92%) rename website_dynamic_snippet/views/snippets/{snippet_templates.xml => snippets.xml} (63%) diff --git a/website_dynamic_snippet/__manifest__.py b/website_dynamic_snippet/__manifest__.py index 97a46e802ff..66f3f32b8a3 100644 --- a/website_dynamic_snippet/__manifest__.py +++ b/website_dynamic_snippet/__manifest__.py @@ -4,13 +4,12 @@ 'category': 'Website', 'depends': ['website_sale'], 'data': [ - 'views/snippets/snippet_templates.xml', + 'views/snippets/snippets.xml', ], 'assets': { 'web.assets_frontend': [ - '/website_dynamic_snippet/static/src/xml/sale_order_snippets.xml', - '/website_dynamic_snippet/static/src/js/dynamic_snippet_sale_orders.js', - '/website_dynamic_snippet/static/src/js/layout_switcher.js', + 'website_dynamic_snippet/static/src/snippets/s_dynamic_sale_order_snippet/000.xml', + 'website_dynamic_snippet/static/src/snippets/s_dynamic_sale_order_snippet/000.js', ], 'website.assets_wysiwyg': [ 'website_dynamic_snippet/static/src/snippets/s_dynamic_sale_order_snippet/options.js', diff --git a/website_dynamic_snippet/static/src/js/dynamic_snippet_sale_orders.js b/website_dynamic_snippet/static/src/snippets/s_dynamic_sale_order_snippet/000.js similarity index 59% rename from website_dynamic_snippet/static/src/js/dynamic_snippet_sale_orders.js rename to website_dynamic_snippet/static/src/snippets/s_dynamic_sale_order_snippet/000.js index 6b18505690e..435201049b4 100644 --- a/website_dynamic_snippet/static/src/js/dynamic_snippet_sale_orders.js +++ b/website_dynamic_snippet/static/src/snippets/s_dynamic_sale_order_snippet/000.js @@ -4,7 +4,7 @@ import publicWidget from "@web/legacy/js/public/public_widget"; publicWidget.registry.get_product_tab = publicWidget.Widget.extend({ selector: '.categories_section', - + disabledInEditableMode: false, events: { 'click .load-more-button': '_onLoadMoreClick', }, @@ -21,9 +21,16 @@ publicWidget.registry.get_product_tab = publicWidget.Widget.extend({ }, async loadCategories() { + if(this.$target[0].dataset.setLayout === 'list'){ + this.layout = 'list'; + } else { + this.layout = 'grid'; + this.limit = 9; + } + const domain = this.$target[0].dataset.confirmOrderOnly === 'true' ? [['state', '=', 'sale']] : []; const result = await this.orm.searchRead( 'sale.order', - [], + domain, ['id', 'name', 'partner_id', 'amount_total', 'state'], { offset: this.offset, @@ -31,23 +38,28 @@ publicWidget.registry.get_product_tab = publicWidget.Widget.extend({ order: 'id ASC', } ); - if (result && result.length) { const content = await renderToElement( 'website_dynamic_snippet.sale_order_snippet', - { result: result } + { result: result, layout: this.layout }, ); + const newTableRows = content.querySelectorAll('.list_sale_order > tbody > tr'); + const existingTableBody = this.el.querySelector('.list_sale_order > tbody'); + + const newCardCols = content.querySelectorAll('.card_sale_order > .col-md-4'); + const existingCardWrapper = this.el.querySelector('.card_sale_order'); + if (this.offset === 0) { this.el.innerHTML = ''; this.el.appendChild(content); } else { - const newRows = content.querySelectorAll('tbody > tr'); - const existingTbody = this.el.querySelector('tbody'); - if (existingTbody) { - newRows.forEach(row => { - existingTbody.appendChild(row); - }); + if (existingTableBody && newTableRows.length) { + newTableRows.forEach(row => existingTableBody.appendChild(row)); + } + + if (existingCardWrapper && newCardCols.length) { + newCardCols.forEach(card => existingCardWrapper.appendChild(card)); } } @@ -60,6 +72,7 @@ publicWidget.registry.get_product_tab = publicWidget.Widget.extend({ } }, + async _onLoadMoreClick(ev) { ev.preventDefault(); await this.loadCategories(); diff --git a/website_dynamic_snippet/static/src/xml/sale_order_snippets.xml b/website_dynamic_snippet/static/src/snippets/s_dynamic_sale_order_snippet/000.xml similarity index 92% rename from website_dynamic_snippet/static/src/xml/sale_order_snippets.xml rename to website_dynamic_snippet/static/src/snippets/s_dynamic_sale_order_snippet/000.xml index f8bbd5a3d38..d35ec8660ab 100644 --- a/website_dynamic_snippet/static/src/xml/sale_order_snippets.xml +++ b/website_dynamic_snippet/static/src/snippets/s_dynamic_sale_order_snippet/000.xml @@ -5,8 +5,7 @@

Sale Order Details

- - +
@@ -26,8 +25,7 @@
Number
- -
+
@@ -41,7 +39,6 @@
-
- - + + diff --git a/website_dynamic_snippet/static/src/snippets/s_dynamic_sale_order_snippet/options.js b/website_dynamic_snippet/static/src/snippets/s_dynamic_sale_order_snippet/options.js index 1417a46fdd1..b20cc34ac74 100644 --- a/website_dynamic_snippet/static/src/snippets/s_dynamic_sale_order_snippet/options.js +++ b/website_dynamic_snippet/static/src/snippets/s_dynamic_sale_order_snippet/options.js @@ -1,36 +1,28 @@ -/** @odoo-module **/ +import options from "@web_editor/js/editor/snippets.options"; -import publicWidget from "@web_editor/js/public/widget"; - -publicWidget.registry.dynamic_snippet_sale_order = publicWidget.Widget.extend({ - selector: ".categories_section", - - xmlDependencies: ["/website_dynamic_snippet/static/src/xml/sale_order_snippet.xml"], - - start: function () { - this.layout = this.el.dataset.layout || 'list'; - this._render(); - return this._super(...arguments); +options.registry.DynamicSnippetOptions = options.Class.extend({ + confirmOrderOnly(previewMode, widgetValue) { + this.$target[0].dataset.confirmOrderOnly = widgetValue; }, - _render: function () { - this._rpc({ - route: '/your/data/route', - params: { /* your logic */ }, - }).then(data => { - this.$el.find(".card_list_wrapper").html( - qweb.render("website_dynamic_snippet.sale_order_snippet", { - result: data.orders, - layout: this.layout, - }) - ); - }); + setLayout(previewMode, widgetValue) { + this.$target[0].setAttribute("data-set-layout", widgetValue); }, - onChangeOption(previewMode, widgetValue, params) { - if (['grid', 'list'].includes(widgetValue)) { - this.layout = widgetValue; - this._render(); + //-------------------------------------------------------------------------- + // Private + //-------------------------------------------------------------------------- + + /** + * @override + */ + _computeWidgetState(methodName, params) { + if (methodName === "confirmOrderOnly") { + return this.$target[0].dataset.confirmOrderOnly; + } + if (methodName === "setLayout") { + return this.$target[0].getAttribute("data-set-layout"); } + return this._super(...arguments); }, }); diff --git a/website_dynamic_snippet/views/snippets/snippet_templates.xml b/website_dynamic_snippet/views/snippets/snippets.xml similarity index 63% rename from website_dynamic_snippet/views/snippets/snippet_templates.xml rename to website_dynamic_snippet/views/snippets/snippets.xml index 4024f57972e..73d4fc253b5 100644 --- a/website_dynamic_snippet/views/snippets/snippet_templates.xml +++ b/website_dynamic_snippet/views/snippets/snippets.xml @@ -22,21 +22,14 @@ - - + From e4d25a2f208aa9e22af74ef4837cca574223dacf Mon Sep 17 00:00:00 2001 From: Garvish Panchal Date: Wed, 7 May 2025 11:09:44 +0530 Subject: [PATCH 8/8] Add dynamic content snippet + 3 --- website_dynamic_snippet/__manifest__.py | 2 +- .../s_dynamic_sale_order_snippet/000.js | 83 +++++++++---------- .../s_dynamic_sale_order_snippet/000.xml | 77 +++++++++-------- 3 files changed, 85 insertions(+), 77 deletions(-) diff --git a/website_dynamic_snippet/__manifest__.py b/website_dynamic_snippet/__manifest__.py index 66f3f32b8a3..9dcadcbb89a 100644 --- a/website_dynamic_snippet/__manifest__.py +++ b/website_dynamic_snippet/__manifest__.py @@ -1,6 +1,6 @@ { 'name': 'Website Dynamic Snippet', - 'version': '18.0.1.0.0', + 'version': '18.4.1.0.0', 'category': 'Website', 'depends': ['website_sale'], 'data': [ diff --git a/website_dynamic_snippet/static/src/snippets/s_dynamic_sale_order_snippet/000.js b/website_dynamic_snippet/static/src/snippets/s_dynamic_sale_order_snippet/000.js index 435201049b4..b5df4367f37 100644 --- a/website_dynamic_snippet/static/src/snippets/s_dynamic_sale_order_snippet/000.js +++ b/website_dynamic_snippet/static/src/snippets/s_dynamic_sale_order_snippet/000.js @@ -1,33 +1,33 @@ /** @odoo-module **/ import { renderToElement } from "@web/core/utils/render"; -import publicWidget from "@web/legacy/js/public/public_widget"; +import { Interaction } from "@web/public/interaction"; +import { registry } from "@web/core/registry"; -publicWidget.registry.get_product_tab = publicWidget.Widget.extend({ - selector: '.categories_section', - disabledInEditableMode: false, - events: { - 'click .load-more-button': '_onLoadMoreClick', - }, +class SaleOrderSnippet extends Interaction { + static selector = ".categories_section"; + dynamicContent = { + ".load-more-button": { + "t-on-click": this._onLoadMoreClick, + } + }; - init() { - this._super(...arguments); - this.orm = this.bindService("orm"); + setup() { + this.orm = this.services.orm; + this.layout = this.el.dataset.setLayout === 'list' ? 'list' : 'grid'; this.offset = 0; - this.limit = 10; - }, + this.limit = this.layout === 'list' ? 10 : 9; + }; async willStart() { await this.loadCategories(); - }, + }; async loadCategories() { - if(this.$target[0].dataset.setLayout === 'list'){ - this.layout = 'list'; - } else { - this.layout = 'grid'; - this.limit = 9; - } - const domain = this.$target[0].dataset.confirmOrderOnly === 'true' ? [['state', '=', 'sale']] : []; + const template = this.layout === 'list' + ? 'website_dynamic_snippet.sale_order_snippet_list' + : 'website_dynamic_snippet.sale_order_snippet_grid'; + + const domain = this.el.dataset.confirmOrderOnly === 'true' ? [['state', '=', 'sale']] : []; const result = await this.orm.searchRead( 'sale.order', domain, @@ -40,27 +40,17 @@ publicWidget.registry.get_product_tab = publicWidget.Widget.extend({ ); if (result && result.length) { const content = await renderToElement( - 'website_dynamic_snippet.sale_order_snippet', - { result: result, layout: this.layout }, + template, + { result: result }, ); - - const newTableRows = content.querySelectorAll('.list_sale_order > tbody > tr'); - const existingTableBody = this.el.querySelector('.list_sale_order > tbody'); - - const newCardCols = content.querySelectorAll('.card_sale_order > .col-md-4'); - const existingCardWrapper = this.el.querySelector('.card_sale_order'); + const selector = this.layout === 'list' ? '.list_sale_order > tbody' : '.card_sale_order'; + const targetContainer = this.el.querySelector(selector); + const newContentContainer = content.querySelector(selector); if (this.offset === 0) { - this.el.innerHTML = ''; - this.el.appendChild(content); - } else { - if (existingTableBody && newTableRows.length) { - newTableRows.forEach(row => existingTableBody.appendChild(row)); - } - - if (existingCardWrapper && newCardCols.length) { - newCardCols.forEach(card => existingCardWrapper.appendChild(card)); - } + this.el.replaceChildren(content); + } else if (targetContainer && newContentContainer) { + targetContainer.append(...newContentContainer.children); } this.offset += result.length; @@ -70,11 +60,20 @@ publicWidget.registry.get_product_tab = publicWidget.Widget.extend({ loadMoreButton.style.display = 'none'; } } - }, - + }; async _onLoadMoreClick(ev) { ev.preventDefault(); await this.loadCategories(); - }, -}); + }; +}; + +registry + .category("public.interactions") + .add("s_dynamic_sale_order_snippet.get_product_tab", SaleOrderSnippet); +registry + .category("public.interactions.edit") + .add("s_dynamic_sale_order_snippet.get_product_tab", + { + Interaction : SaleOrderSnippet + }); diff --git a/website_dynamic_snippet/static/src/snippets/s_dynamic_sale_order_snippet/000.xml b/website_dynamic_snippet/static/src/snippets/s_dynamic_sale_order_snippet/000.xml index d35ec8660ab..bf563516b5d 100644 --- a/website_dynamic_snippet/static/src/snippets/s_dynamic_sale_order_snippet/000.xml +++ b/website_dynamic_snippet/static/src/snippets/s_dynamic_sale_order_snippet/000.xml @@ -1,44 +1,53 @@ - +

Sale Order Details

- - - +
+ + + + + + + + + - - - + + + - - - - - - - - - - -
NumberCustomer NameStatus
NumberCustomer NameStatus
-
- -
- -
-
-
-
Order #
-

Customer:

-

Status:

-
+ + + +
+ +
+
+
+
+ + +
+

Sale Order Details

+
+
+ +
+
+
+
Order #
+

Customer:

+

Status:

- -
-
+
+ +
-
- + +