From 974fefcecb6d471adb53669933b422585b8e191c Mon Sep 17 00:00:00 2001 From: Parth Vyas Date: Thu, 15 May 2025 15:08:19 +0530 Subject: [PATCH 1/6] [ADD] website_dynamic_snippet: implementation of dynamic snippet Implementation of the website dynamic snippet to learn about the dynamic snippet. This snippet fetches product categories from the backend and displays them as a list and images. --- addons/website_dynamic_snippet/__init__.py | 0 .../website_dynamic_snippet/__manifest__.py | 24 +++++ .../static/src/img/s_thumbnail.svg | 90 +++++++++++++++++++ .../src/snippets/s_sale_order_cards/000.js | 63 +++++++++++++ .../src/snippets/s_sale_order_cards/000.xml | 24 +++++ .../views/snippets.xml | 11 +++ .../views/snippets/s_sale_order_cards.xml | 37 ++++++++ 7 files changed, 249 insertions(+) create mode 100644 addons/website_dynamic_snippet/__init__.py create mode 100644 addons/website_dynamic_snippet/__manifest__.py create mode 100644 addons/website_dynamic_snippet/static/src/img/s_thumbnail.svg create mode 100644 addons/website_dynamic_snippet/static/src/snippets/s_sale_order_cards/000.js create mode 100644 addons/website_dynamic_snippet/static/src/snippets/s_sale_order_cards/000.xml create mode 100755 addons/website_dynamic_snippet/views/snippets.xml create mode 100644 addons/website_dynamic_snippet/views/snippets/s_sale_order_cards.xml diff --git a/addons/website_dynamic_snippet/__init__.py b/addons/website_dynamic_snippet/__init__.py new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/addons/website_dynamic_snippet/__manifest__.py b/addons/website_dynamic_snippet/__manifest__.py new file mode 100644 index 0000000000000..77bba4170b27f --- /dev/null +++ b/addons/website_dynamic_snippet/__manifest__.py @@ -0,0 +1,24 @@ +{ + "name": "Website Dynamic Snippet", + "version": "1.0", + "category": "Website/Website", + "depends": ["website_sale"], + "author": "Parth Vyas", + "website": "https://www.odoo.com", + "description": """ + This module provides a dynamic snippet for displaying sale order details on the website. + """, + "data": [ + "views/snippets.xml", + "views/snippets/s_sale_order_cards.xml", + ], + "assets": { + "web.assets_frontend": [ + "website_dynamic_snippet/static/src/snippets/s_sale_order_cards/000.js", + "website_dynamic_snippet/static/src/snippets/s_sale_order_cards/000.xml", + ], + }, + "installable": True, + "auto_install": True, + "license": "LGPL-3", +} diff --git a/addons/website_dynamic_snippet/static/src/img/s_thumbnail.svg b/addons/website_dynamic_snippet/static/src/img/s_thumbnail.svg new file mode 100644 index 0000000000000..c059743fb2e52 --- /dev/null +++ b/addons/website_dynamic_snippet/static/src/img/s_thumbnail.svg @@ -0,0 +1,90 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/addons/website_dynamic_snippet/static/src/snippets/s_sale_order_cards/000.js b/addons/website_dynamic_snippet/static/src/snippets/s_sale_order_cards/000.js new file mode 100644 index 0000000000000..96935cdd88b5d --- /dev/null +++ b/addons/website_dynamic_snippet/static/src/snippets/s_sale_order_cards/000.js @@ -0,0 +1,63 @@ +/** @odoo-module */ +import { renderToFragment } from "@web/core/utils/render"; +import publicWidget from "@web/legacy/js/public/public_widget"; + +publicWidget.registry.s_sale_order_cards = publicWidget.Widget.extend({ + selector: ".s_sale_order_cards", + disabledInEditableMode: false, + events: { + "click #load_more_orders": "loadMore", + }, + + init() { + this._super(...arguments); + this.orm = this.bindService("orm"); + this.noOfOrders = 10; + this.offset = 0; + this.orders = []; + }, + + async willStart() { + await this.fetchOrders(); + }, + + start() { + this.renderOrders(); + }, + + renderOrders() { + const target = this.el.querySelector("#sale_order_cards_container"); + const displayType = this.el.dataset.displayType ?? "card"; + if (this.orders.length) { + const cards = renderToFragment( + `website_dynamic_snippet.s_sale_order_cards.${displayType}`, + { orders: this.orders } + ); + target.innerHTML = ""; + target.appendChild(cards); + } + }, + + async fetchOrders() { + this.showConfirmOrders = this.el.dataset.showConfirmOrders === "true" ?? false; + this.noOfOrders = parseInt(this.el.dataset.noOfOrders) || 10; + const domain = this.showConfirmOrders ? [["state", "=", "sale"]] : []; + const temp_orders = await this.orm.searchRead( + "sale.order", + domain, + ["name", "partner_id", "state"], + { limit: this.noOfOrders, offset: this.offset } + ); + if (temp_orders.length === 0) { + const loadMoreButton = this.el.querySelector("#load_more_orders"); + loadMoreButton.classList.add("d-none"); + } + this.orders.push(...temp_orders); + }, + + async loadMore() { + this.offset += this.noOfOrders; + await this.fetchOrders(); + this.renderOrders(); + }, +}); diff --git a/addons/website_dynamic_snippet/static/src/snippets/s_sale_order_cards/000.xml b/addons/website_dynamic_snippet/static/src/snippets/s_sale_order_cards/000.xml new file mode 100644 index 0000000000000..764a04165bba5 --- /dev/null +++ b/addons/website_dynamic_snippet/static/src/snippets/s_sale_order_cards/000.xml @@ -0,0 +1,24 @@ + + + +
+
+
+
+

+ +
+
+
+
+ + +
  • +
    + Order: + Customer: + Status: +
    +
  • +
    +
    diff --git a/addons/website_dynamic_snippet/views/snippets.xml b/addons/website_dynamic_snippet/views/snippets.xml new file mode 100755 index 0000000000000..b45616b14ce8b --- /dev/null +++ b/addons/website_dynamic_snippet/views/snippets.xml @@ -0,0 +1,11 @@ + + + + + + diff --git a/addons/website_dynamic_snippet/views/snippets/s_sale_order_cards.xml b/addons/website_dynamic_snippet/views/snippets/s_sale_order_cards.xml new file mode 100644 index 0000000000000..9bf28933abedc --- /dev/null +++ b/addons/website_dynamic_snippet/views/snippets/s_sale_order_cards.xml @@ -0,0 +1,37 @@ + + + + + + From 5560787770196a24a70e7be5b2881f561bfad281 Mon Sep 17 00:00:00 2001 From: Parth Vyas Date: Tue, 27 May 2025 17:18:55 +0530 Subject: [PATCH 2/6] [IMP] website_dynamic_snippet: refactor sale order cards to use interaction refactor sale order cards to use interaction instead of public widget --- .../website_dynamic_snippet/__manifest__.py | 3 +- .../{000.js => sale_order_cards.js} | 58 ++++++++++--------- .../views/snippets/s_sale_order_cards.xml | 2 - 3 files changed, 31 insertions(+), 32 deletions(-) rename addons/website_dynamic_snippet/static/src/snippets/s_sale_order_cards/{000.js => sale_order_cards.js} (58%) diff --git a/addons/website_dynamic_snippet/__manifest__.py b/addons/website_dynamic_snippet/__manifest__.py index 77bba4170b27f..422c153dc1f6b 100644 --- a/addons/website_dynamic_snippet/__manifest__.py +++ b/addons/website_dynamic_snippet/__manifest__.py @@ -14,8 +14,7 @@ ], "assets": { "web.assets_frontend": [ - "website_dynamic_snippet/static/src/snippets/s_sale_order_cards/000.js", - "website_dynamic_snippet/static/src/snippets/s_sale_order_cards/000.xml", + "website_dynamic_snippet/static/src/snippets/**/*", ], }, "installable": True, diff --git a/addons/website_dynamic_snippet/static/src/snippets/s_sale_order_cards/000.js b/addons/website_dynamic_snippet/static/src/snippets/s_sale_order_cards/sale_order_cards.js similarity index 58% rename from addons/website_dynamic_snippet/static/src/snippets/s_sale_order_cards/000.js rename to addons/website_dynamic_snippet/static/src/snippets/s_sale_order_cards/sale_order_cards.js index 96935cdd88b5d..5e9007035d15c 100644 --- a/addons/website_dynamic_snippet/static/src/snippets/s_sale_order_cards/000.js +++ b/addons/website_dynamic_snippet/static/src/snippets/s_sale_order_cards/sale_order_cards.js @@ -1,48 +1,42 @@ -/** @odoo-module */ -import { renderToFragment } from "@web/core/utils/render"; -import publicWidget from "@web/legacy/js/public/public_widget"; - -publicWidget.registry.s_sale_order_cards = publicWidget.Widget.extend({ - selector: ".s_sale_order_cards", - disabledInEditableMode: false, - events: { - "click #load_more_orders": "loadMore", - }, - - init() { - this._super(...arguments); - this.orm = this.bindService("orm"); +import { Interaction } from "@web/public/interaction"; +import { registry } from "@web/core/registry"; + +export class SaleOrderCards extends Interaction { + static selector = ".s_sale_order_cards"; + dynamicContent = { + "#load_more_orders": { + "t-on-click": this.loadMore, + }, + }; + + setup() { this.noOfOrders = 10; this.offset = 0; this.orders = []; - }, + } async willStart() { await this.fetchOrders(); - }, + } start() { this.renderOrders(); - }, + } renderOrders() { const target = this.el.querySelector("#sale_order_cards_container"); const displayType = this.el.dataset.displayType ?? "card"; + const templateName = `website_dynamic_snippet.s_sale_order_cards.${displayType}`; if (this.orders.length) { - const cards = renderToFragment( - `website_dynamic_snippet.s_sale_order_cards.${displayType}`, - { orders: this.orders } - ); - target.innerHTML = ""; - target.appendChild(cards); + this.renderAt(templateName, { orders: this.orders }, target, "afterbegin"); } - }, + } async fetchOrders() { this.showConfirmOrders = this.el.dataset.showConfirmOrders === "true" ?? false; this.noOfOrders = parseInt(this.el.dataset.noOfOrders) || 10; const domain = this.showConfirmOrders ? [["state", "=", "sale"]] : []; - const temp_orders = await this.orm.searchRead( + const temp_orders = await this.services.orm.searchRead( "sale.order", domain, ["name", "partner_id", "state"], @@ -53,11 +47,19 @@ publicWidget.registry.s_sale_order_cards = publicWidget.Widget.extend({ loadMoreButton.classList.add("d-none"); } this.orders.push(...temp_orders); - }, + } async loadMore() { this.offset += this.noOfOrders; await this.fetchOrders(); this.renderOrders(); - }, -}); + } +} + +registry + .category("public.interactions") + .add("website.sale_order_cards", SaleOrderCards); + +registry + .category("public.interactions.edit") + .add("website.sale_order_cards", { Interaction: SaleOrderCards }); diff --git a/addons/website_dynamic_snippet/views/snippets/s_sale_order_cards.xml b/addons/website_dynamic_snippet/views/snippets/s_sale_order_cards.xml index 9bf28933abedc..eacb1ec0feb7a 100644 --- a/addons/website_dynamic_snippet/views/snippets/s_sale_order_cards.xml +++ b/addons/website_dynamic_snippet/views/snippets/s_sale_order_cards.xml @@ -23,10 +23,8 @@ List Card Date: Wed, 28 May 2025 18:56:40 +0530 Subject: [PATCH 3/6] [IMP] website_dynamic_snippet: implement interaction with preview behavior implement sale order cards interaction with preview behavior --- .../static/src/img/s_website_sale.png | Bin 0 -> 6786 bytes .../sale_order_cards.edit.js | 13 ++++++++++++ .../s_sale_order_cards/sale_order_cards.js | 10 ++++----- .../sale_order_cards.preview.js | 19 ++++++++++++++++++ .../views/snippets.xml | 14 ++++++++++--- .../views/snippets/s_sale_order_cards.xml | 2 +- 6 files changed, 48 insertions(+), 10 deletions(-) create mode 100644 addons/website_dynamic_snippet/static/src/img/s_website_sale.png create mode 100644 addons/website_dynamic_snippet/static/src/snippets/s_sale_order_cards/sale_order_cards.edit.js create mode 100644 addons/website_dynamic_snippet/static/src/snippets/s_sale_order_cards/sale_order_cards.preview.js diff --git a/addons/website_dynamic_snippet/static/src/img/s_website_sale.png b/addons/website_dynamic_snippet/static/src/img/s_website_sale.png new file mode 100644 index 0000000000000000000000000000000000000000..2a6460999e53fb3de52a35b74a8632fbafc3f8e8 GIT binary patch literal 6786 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 literal 0 HcmV?d00001 diff --git a/addons/website_dynamic_snippet/static/src/snippets/s_sale_order_cards/sale_order_cards.edit.js b/addons/website_dynamic_snippet/static/src/snippets/s_sale_order_cards/sale_order_cards.edit.js new file mode 100644 index 0000000000000..afb4cd73f4248 --- /dev/null +++ b/addons/website_dynamic_snippet/static/src/snippets/s_sale_order_cards/sale_order_cards.edit.js @@ -0,0 +1,13 @@ +import { registry } from "@web/core/registry"; +import { SaleOrderCards } from "./sale_order_cards" + +const SaleOrderCardsEdit = I => class extends I { + start() { + super.start(); + this.loadMoreButton.setAttribute("disabled", "true"); + } +}; + +registry + .category("public.interactions.edit") + .add("website_dynamic_snippet.s_sale_order_cards", { Interaction: SaleOrderCards, mixin: SaleOrderCardsEdit }); diff --git a/addons/website_dynamic_snippet/static/src/snippets/s_sale_order_cards/sale_order_cards.js b/addons/website_dynamic_snippet/static/src/snippets/s_sale_order_cards/sale_order_cards.js index 5e9007035d15c..e758c1bdf01c3 100644 --- a/addons/website_dynamic_snippet/static/src/snippets/s_sale_order_cards/sale_order_cards.js +++ b/addons/website_dynamic_snippet/static/src/snippets/s_sale_order_cards/sale_order_cards.js @@ -20,6 +20,8 @@ export class SaleOrderCards extends Interaction { } start() { + this.loadMoreButton = this.el.querySelector("#load_more_orders") + this.loadMoreButton.removeAttribute("disabled"); this.renderOrders(); } @@ -34,7 +36,7 @@ export class SaleOrderCards extends Interaction { async fetchOrders() { this.showConfirmOrders = this.el.dataset.showConfirmOrders === "true" ?? false; - this.noOfOrders = parseInt(this.el.dataset.noOfOrders) || 10; + this.noOfOrders = parseInt(this.el.dataset.noOfOrders); const domain = this.showConfirmOrders ? [["state", "=", "sale"]] : []; const temp_orders = await this.services.orm.searchRead( "sale.order", @@ -58,8 +60,4 @@ export class SaleOrderCards extends Interaction { registry .category("public.interactions") - .add("website.sale_order_cards", SaleOrderCards); - -registry - .category("public.interactions.edit") - .add("website.sale_order_cards", { Interaction: SaleOrderCards }); + .add("website_dynamic_snippet.s_sale_order_cards", SaleOrderCards); diff --git a/addons/website_dynamic_snippet/static/src/snippets/s_sale_order_cards/sale_order_cards.preview.js b/addons/website_dynamic_snippet/static/src/snippets/s_sale_order_cards/sale_order_cards.preview.js new file mode 100644 index 0000000000000..f720541856f99 --- /dev/null +++ b/addons/website_dynamic_snippet/static/src/snippets/s_sale_order_cards/sale_order_cards.preview.js @@ -0,0 +1,19 @@ +import { registry } from "@web/core/registry"; +import { SaleOrderCards } from "./sale_order_cards" + +const SaleOrderCardsPreview = I => class extends I { + dynamicContent = { + _root: { + "t-on-mouseenter": ()=> { + this.el.style.backgroundColor = "#f0f0f0" + }, + "t-on-mouseleave": () => { + this.el.style.backgroundColor = ""; + }, + }, + }; +}; + +registry + .category("public.interactions.preview") + .add("website_dynamic_snippet.s_sale_order_cards", { Interaction: SaleOrderCards, mixin: SaleOrderCardsPreview }); diff --git a/addons/website_dynamic_snippet/views/snippets.xml b/addons/website_dynamic_snippet/views/snippets.xml index b45616b14ce8b..12379bf031230 100755 --- a/addons/website_dynamic_snippet/views/snippets.xml +++ b/addons/website_dynamic_snippet/views/snippets.xml @@ -2,9 +2,17 @@ diff --git a/addons/website_dynamic_snippet/views/snippets/s_sale_order_cards.xml b/addons/website_dynamic_snippet/views/snippets/s_sale_order_cards.xml index eacb1ec0feb7a..e12d69ce2a9fa 100644 --- a/addons/website_dynamic_snippet/views/snippets/s_sale_order_cards.xml +++ b/addons/website_dynamic_snippet/views/snippets/s_sale_order_cards.xml @@ -1,7 +1,7 @@