import { Controller } from "stimulus"; import { Liquid } from "liquidjs"; /* * Base controller, shared methods go here and other classes extend from * this. */ export class CartBaseController extends Controller { get spree() { return window.spree; } /* * Gets all products from storage * * @return [Array] */ get products() { if (!this.cart) return []; return this.cart.data.relationships.variants.data .map((x) => JSON.parse(this.storage.getItem(`cart:item:${x.id}`))) .filter((x) => x !== null); } /* * The storage */ get storage() { return window.localStorage; } /* * Temporary storage */ get storageTemp() { return window.sessionStorage; } get cart() { return JSON.parse(this.storage.getItem("cart")); } set cart(response) { this.storage.setItem("cart", JSON.stringify(response.success())); } get email() { return this.storageTemp.getItem("email"); } set email(email) { this.storageTemp.setItem("email", email); } /* * The orderToken * * @return [String] */ get token() { return this.storage.getItem("token"); } /* * The bearerToken * * @return [String] */ get bearerToken() { return this.storageTemp.getItem("bearerToken"); } set bearerToken(token) { this.storageTemp.setItem("bearerToken", token); } /* * Liquid renderer * * @return Liquid */ get engine() { if (!window.liquid) window.liquid = new Liquid(); return window.liquid; } /* * Updates the item counter */ counterUpdate() { const item_count = this.cart.data.attributes.item_count; window.dispatchEvent( new CustomEvent("cart:counter", { detail: { item_count } }) ); this.storage.setItem("cart:counter", item_count); } /* * Removes the brackets from the name or returns the name * * @return [String] */ idFromInputName(input) { const matches = input.name.match(/\[([^\]]+)\]$/); return matches === null ? input.name : matches[1]; } async handleFailure(response) { const data = { type: "primary" }; let template = "alert"; let notify = true; const site = window.site; const fail = response.fail(); switch (fail.name) { case "MisconfigurationError": data.content = fail.message; break; case "NoResponseError": data.content = site.i18n.alerts.no_response_error; break; case "SpreeError": data.content = site.i18n.alerts.spree_error; break; case "BasicSpreeError": // XXX: The order is missing, we need to start a new one if (fail.serverResponse.status === 404) { template = "recover_order"; data.content = site.i18n.alerts.recover_order; } else { data.content = response.fail().summary; } break; case "ExpandedSpreeError": const content = []; // XXX: La API devuelve los mensajes de error tal cual en la // llave `error` pero el cliente solo nos da acceso a `errors`. for (const field of Object.keys(fail.errors)) { if (!site.i18n.errors?.fields[field]) { console.error("El campo no tiene traducción", field); } content.push( `${site.i18n.errors?.fields[field]}: ${fail.errors[field].join( ", " )}` ); } data.content = content.join(". "); notify = false; break; default: data.content = fail.message; } if (notify) console.error(fail.name, data.content); window.dispatchEvent( new CustomEvent("notification", { detail: { template, data } }) ); } set formDisabled(state) { if (!this.hasFormTarget) return; this.formTarget.elements.forEach((x) => (x.disabled = !!state)); } assignShippingAddress() { if (!this.bearerToken) return; const bearerToken = this.bearerToken; const orderToken = this.token; this.spree.sutty.assignOrderShippingAddress( { bearerToken }, { orderToken } ); } notify(data = {}) { window.dispatchEvent( new CustomEvent("notification", { detail: { template: "alert", data } }) ); } /* * Visita una página con soporte para Turbolinks * * @param [String] URL */ visit(url) { try { Turbolinks.visit(url); } catch { window.location = url; } } async firstAddress(bearerToken) { if (!this._firstAddress) { const addresses = await this.spree.account.addressesList({ bearerToken }); if (addresses.isFail()) { this.handleFailure(addresses); return; } // XXX: Asumimos que si se registró tiene una dirección que vamos // a actualizar. this._firstAddress = addresses.success().data[0]; } return this._firstAddress; } async updateAddress(bearerToken, id, address) { const updateAddress = await this.spree.account.updateAddress( { bearerToken }, id, { address } ); if (updateAddress.isFail()) { this.handleFailure(updateAddress); return; } return updateAddress.success(); } async shippingMethods(orderToken) { const shippingMethods = await this.spree.checkout.shippingMethods( { orderToken }, { include: "shipping_rates" } ); if (shippingMethods.isFail()) { this.handleFailure(shippingMethods); return; } return shippingMethods.success(); } fireCajon(state = "open", cajon = "cajon") { window.dispatchEvent( new CustomEvent("cajon", { detail: { cajon, state } }) ); } formDataToObject(formData) { const object = {}; for (const field of formData) { if (field[0].startsWith("_ignore_")) continue; object[field[0]] = field[1]; } return object; } }