|
| 1 | +import { nostojs } from "@nosto/nosto-js" |
| 2 | +import { postJSON } from "@/utils/fetch" |
| 3 | +import { customElement } from "../decorators" |
| 4 | +import { NostoElement } from "../Element" |
| 5 | +import { addRequest } from "../Campaign/orchestrator" |
| 6 | +import { JSONResult } from "@nosto/nosto-js/client" |
| 7 | + |
| 8 | +/** |
| 9 | + * BundledCampaign is a custom element that fetches Nosto placement results and renders the results |
| 10 | + * using a Shopify section template via the Bundled Section Rendering API. |
| 11 | + * Campaign result metadata is persisted in cart attributes |
| 12 | + * |
| 13 | + * @property {string} placement - The placement identifier for the campaign. |
| 14 | + * @property {string} section - The section to be used for Section Rendering API based rendering. |
| 15 | + */ |
| 16 | +@customElement("nosto-bundled-campaign") |
| 17 | +export class BundledCampaign extends NostoElement { |
| 18 | + /** @private */ |
| 19 | + static attributes = { |
| 20 | + placement: String, |
| 21 | + handles: String, |
| 22 | + section: String |
| 23 | + } |
| 24 | + |
| 25 | + placement!: string |
| 26 | + handles!: string |
| 27 | + section!: string |
| 28 | + |
| 29 | + async connectedCallback() { |
| 30 | + this.toggleAttribute("loading", true) |
| 31 | + try { |
| 32 | + await this.#initializeMarkup() |
| 33 | + } finally { |
| 34 | + this.toggleAttribute("loading", false) |
| 35 | + } |
| 36 | + } |
| 37 | + |
| 38 | + async #initializeMarkup() { |
| 39 | + const api = await new Promise(nostojs) |
| 40 | + const rec = (await addRequest({ |
| 41 | + placement: this.placement, |
| 42 | + responseMode: "JSON_ORIGINAL" // TODO use a responseMode that returns only the needed data |
| 43 | + })) as JSONResult |
| 44 | + if (!rec) { |
| 45 | + return |
| 46 | + } |
| 47 | + const handles = rec.products.map(product => product.handle).join(":") |
| 48 | + if (this.handles !== handles) { |
| 49 | + const markup = await getSectionMarkup(this, handles, rec) |
| 50 | + this.innerHTML = markup |
| 51 | + } |
| 52 | + api.attributeProductClicksInCampaign(this, rec) |
| 53 | + } |
| 54 | +} |
| 55 | + |
| 56 | +async function getSectionMarkup(element: BundledCampaign, handles: string, rec: JSONResult) { |
| 57 | + const target = new URL("/cart/update.js", window.location.href) |
| 58 | + const payload = { |
| 59 | + attributes: { |
| 60 | + [`nosto_${element.placement}_title`]: rec.title || "", |
| 61 | + [`nosto_${element.placement}_handles`]: handles |
| 62 | + }, |
| 63 | + sections: element.section, |
| 64 | + } |
| 65 | + const sectionHtml = await postJSON(target.href, payload) |
| 66 | + const parser = new DOMParser() |
| 67 | + const doc = parser.parseFromString(sectionHtml, "text/html") |
| 68 | + return doc.querySelector(`nosto-bundled-campaign[placement="${element.placement}"]`)?.innerHTML || "" |
| 69 | +} |
| 70 | + |
| 71 | +declare global { |
| 72 | + interface HTMLElementTagNameMap { |
| 73 | + "nosto-bundled-campaign": BundledCampaign |
| 74 | + } |
| 75 | +} |
0 commit comments