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..9edea259bc177 --- /dev/null +++ b/addons/website_dynamic_snippet/__manifest__.py @@ -0,0 +1,26 @@ +{ + "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/**/*", + ], + "web.assets_tests": [ + "website_dynamic_snippet/static/tests/tours/**/*", + ], + }, + "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/img/s_website_sale.png b/addons/website_dynamic_snippet/static/src/img/s_website_sale.png new file mode 100644 index 0000000000000..2a6460999e53f Binary files /dev/null and b/addons/website_dynamic_snippet/static/src/img/s_website_sale.png differ 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/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 new file mode 100644 index 0000000000000..3fddd118af3ef --- /dev/null +++ b/addons/website_dynamic_snippet/static/src/snippets/s_sale_order_cards/sale_order_cards.js @@ -0,0 +1,62 @@ +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.loadMoreButton = this.el.querySelector("#load_more_orders") + this.loadMoreButton.removeAttribute("disabled"); + this.renderOrders(); + } + + renderOrders() { + const target = this.el.querySelector("#sale_order_cards_container"); + target.innerHTML = ""; + const displayType = this.el.dataset.displayType ?? "card"; + const templateName = `website_dynamic_snippet.s_sale_order_cards.${displayType}`; + if (this.orders.length) { + this.renderAt(templateName, { orders: this.orders }, target); + } + } + + async fetchOrders() { + this.showConfirmOrders = this.el.dataset.showConfirmOrders === "true"; + this.noOfOrders = parseInt(this.el.dataset.noOfOrders); + const domain = this.showConfirmOrders ? [["state", "=", "sale"]] : []; + const temp_orders = await this.services.orm.searchRead( + "sale.order", + domain, + ["name", "partner_id", "state"], + { limit: this.noOfOrders, offset: this.offset } + ); + const loadMoreButton = this.el.querySelector("#load_more_orders"); + loadMoreButton.classList.toggle("d-none", temp_orders.length === 0); + this.orders.push(...temp_orders); + } + + async loadMore() { + this.offset += this.noOfOrders; + await this.fetchOrders(); + this.renderOrders(); + } +} + +registry + .category("public.interactions") + .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..3cbdf35796e15 --- /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/static/tests/tours/snippet_sale_order_cards.js b/addons/website_dynamic_snippet/static/tests/tours/snippet_sale_order_cards.js new file mode 100644 index 0000000000000..2b844e50b20a0 --- /dev/null +++ b/addons/website_dynamic_snippet/static/tests/tours/snippet_sale_order_cards.js @@ -0,0 +1,51 @@ +import { + changeOption, + clickOnSnippet, + insertSnippet, + registerWebsitePreviewTour, + clickOnSave, +} from '@website/js/tours/tour_utils'; + +registerWebsitePreviewTour("snippet_sale_order_cards", { + url: "/", + edition: true, +}, () => [ + ...insertSnippet({ id: "s_sale_order_cards", name: "Sale Order Cards", groupName: "Website Sale" }), + ...clickOnSnippet({ id: "s_sale_order_cards", name: "Sale Order Cards", groupName: "Website Sale" }), + changeOption("sale_order_cards_options", 'we-checkbox'), + { + content: "Check confirmed orders option applied", + trigger: ':iframe section[data-show-confirm-orders="true"]', + }, + changeOption("sale_order_cards_options", 'we-select[data-attribute-name="displayType"] we-toggler'), + changeOption("sale_order_cards_options", 'we-button[data-select-data-attribute="list"]'), + { + content: "Check display type list applied", + trigger: ':iframe section[data-display-type="list"]', + }, + changeOption("sale_order_cards_options", 'we-select[data-attribute-name="displayType"] we-toggler'), + changeOption("sale_order_cards_options", 'we-button[data-select-data-attribute="card"]'), + { + content: "Check display type card applied", + trigger: ':iframe section[data-display-type="card"]', + }, + { + content: "Change number of orders option", + trigger: 'we-input[data-attribute-name="noOfOrders"] input', + run: `edit 15 && click body`, + }, + { + content: "Check number of orders option applied", + trigger: ':iframe section[data-no-of-orders="15"]', + }, + ...clickOnSave(), + { + content: "Check for load more button", + trigger: ':iframe button[id="load_more_orders"]', + run: `click`, + }, + { + content: "Check for expected number of orders loaded", + trigger: ':iframe #sale_order_cards_container > :nth-child(25)', + }, +]); diff --git a/addons/website_dynamic_snippet/tests/__init__.py b/addons/website_dynamic_snippet/tests/__init__.py new file mode 100644 index 0000000000000..88d3819225ccf --- /dev/null +++ b/addons/website_dynamic_snippet/tests/__init__.py @@ -0,0 +1 @@ +from . import test_sale_order_cards diff --git a/addons/website_dynamic_snippet/tests/test_sale_order_cards.py b/addons/website_dynamic_snippet/tests/test_sale_order_cards.py new file mode 100644 index 0000000000000..ddb1347218ab6 --- /dev/null +++ b/addons/website_dynamic_snippet/tests/test_sale_order_cards.py @@ -0,0 +1,11 @@ +from odoo.tests import HttpCase, tagged + + +@tagged("post_install", "-at_install") +class TestSaleOrderCards(HttpCase): + def test_sale_order_cards_snippet(self): + self.start_tour( + self.env["website"].get_client_action_url("/"), + "snippet_sale_order_cards", + login="admin", + ) diff --git a/addons/website_dynamic_snippet/views/snippets.xml b/addons/website_dynamic_snippet/views/snippets.xml new file mode 100755 index 0000000000000..12379bf031230 --- /dev/null +++ b/addons/website_dynamic_snippet/views/snippets.xml @@ -0,0 +1,19 @@ + + + + + + + + + + sale, order, cards + + + + + 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..3c7f8c86b969b --- /dev/null +++ b/addons/website_dynamic_snippet/views/snippets/s_sale_order_cards.xml @@ -0,0 +1,36 @@ + + + + + + + + + Load More Orders... + + + + + + + + + + List + Card + + + + + +