@altazion/commerce-sdk-htmx 26.618.8367 → 26.619.8376

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -658,8 +658,8 @@ const storesModuleDefinition = {
658
658
  case "findByLocation": {
659
659
  const latitude = requireNumber(payload.latitude, "latitude");
660
660
  const longitude = requireNumber(payload.longitude, "longitude");
661
- const radiusKm = payload.radiusKm === void 0 ? 50 : requireNumber(payload.radiusKm, "radiusKm");
662
- return client.stores.findByLocation(latitude, longitude, radiusKm);
661
+ const countryCode = payload.countryCode === void 0 ? "FRA" : requireString(payload.countryCode, "countryCode");
662
+ return client.stores.findByLocation(latitude, longitude, countryCode);
663
663
  }
664
664
  case "findByPostalCode": {
665
665
  const postalCode = requireString(payload.postalCode, "postalCode");
@@ -956,6 +956,179 @@ function resolveTemplate(templateId, handlebars, templateCache) {
956
956
  templateCache.set(templateId, compiled);
957
957
  return compiled;
958
958
  }
959
+ const DIALOG_SELECTOR = "[data-hz-store-picker]";
960
+ const TEMPLATE_ID = "hz-store-picker-store-tmpl";
961
+ const RESULTS_SELECTOR = "[data-store-picker-results]";
962
+ const HINT_SELECTOR = "[data-store-picker-hint]";
963
+ const FORM_SELECTOR = "[data-store-picker-form]";
964
+ const INPUT_SELECTOR = "[data-store-picker-input]";
965
+ function initStorePicker(client, handlebars) {
966
+ if (typeof document === "undefined") return () => {
967
+ };
968
+ let renderStore = null;
969
+ const templateEl = document.getElementById(TEMPLATE_ID);
970
+ if (templateEl && handlebars) {
971
+ try {
972
+ const tplSource = templateEl.innerHTML;
973
+ const compiled = handlebars.compile(tplSource);
974
+ renderStore = (store) => compiled(store);
975
+ } catch {
976
+ }
977
+ }
978
+ if (!renderStore) {
979
+ renderStore = fallbackRender;
980
+ }
981
+ function getDialog() {
982
+ return document.querySelector(DIALOG_SELECTOR);
983
+ }
984
+ function openDialog() {
985
+ var _a;
986
+ (_a = getDialog()) == null ? void 0 : _a.showModal();
987
+ }
988
+ function closeDialog() {
989
+ var _a;
990
+ (_a = getDialog()) == null ? void 0 : _a.close();
991
+ }
992
+ function getConfig() {
993
+ const dialog = getDialog();
994
+ return {
995
+ countryCode: (dialog == null ? void 0 : dialog.dataset.storePickerCountry) ?? "FRA",
996
+ feature: (dialog == null ? void 0 : dialog.dataset.storePickerFeature) ?? "InStoreOrder"
997
+ };
998
+ }
999
+ function renderResults(stores) {
1000
+ const dialog = getDialog();
1001
+ if (!dialog) return;
1002
+ const list = dialog.querySelector(RESULTS_SELECTOR);
1003
+ const hint = dialog.querySelector(HINT_SELECTOR);
1004
+ if (!list) return;
1005
+ if (stores.length === 0) {
1006
+ list.innerHTML = "";
1007
+ list.hidden = true;
1008
+ if (hint) {
1009
+ hint.textContent = "Aucun magasin trouvé dans ce secteur.";
1010
+ hint.hidden = false;
1011
+ }
1012
+ return;
1013
+ }
1014
+ list.innerHTML = stores.map((s) => renderStore(s)).join("");
1015
+ list.hidden = false;
1016
+ if (hint) hint.hidden = true;
1017
+ }
1018
+ function showSearching() {
1019
+ const dialog = getDialog();
1020
+ if (!dialog) return;
1021
+ const hint = dialog.querySelector(HINT_SELECTOR);
1022
+ const list = dialog.querySelector(RESULTS_SELECTOR);
1023
+ if (hint) {
1024
+ hint.textContent = "Recherche en cours…";
1025
+ hint.hidden = false;
1026
+ }
1027
+ if (list) list.hidden = true;
1028
+ }
1029
+ function showError(message) {
1030
+ const dialog = getDialog();
1031
+ if (!dialog) return;
1032
+ const hint = dialog.querySelector(HINT_SELECTOR);
1033
+ if (hint) {
1034
+ hint.textContent = message;
1035
+ hint.hidden = false;
1036
+ }
1037
+ }
1038
+ async function handlePostalCodeSearch(postalCode) {
1039
+ showSearching();
1040
+ const { countryCode, feature } = getConfig();
1041
+ try {
1042
+ const stores = await client.stores.findByPostalCode(postalCode, countryCode, 10, feature);
1043
+ renderResults(stores);
1044
+ } catch {
1045
+ showError("La recherche a échoué. Vérifiez votre code postal et réessayez.");
1046
+ }
1047
+ }
1048
+ async function handleGeolocate() {
1049
+ if (!navigator.geolocation) {
1050
+ showError("La géolocalisation n'est pas disponible sur cet appareil.");
1051
+ return;
1052
+ }
1053
+ showSearching();
1054
+ const { countryCode, feature } = getConfig();
1055
+ navigator.geolocation.getCurrentPosition(
1056
+ async (position) => {
1057
+ try {
1058
+ const stores = await client.stores.findByLocation(
1059
+ position.coords.latitude,
1060
+ position.coords.longitude,
1061
+ countryCode,
1062
+ 10,
1063
+ feature
1064
+ );
1065
+ renderResults(stores);
1066
+ } catch {
1067
+ showError("La recherche a échoué. Veuillez réessayer.");
1068
+ }
1069
+ },
1070
+ () => {
1071
+ showError("Impossible d'obtenir votre position. Vérifiez les autorisations de géolocalisation.");
1072
+ }
1073
+ );
1074
+ }
1075
+ async function handleSelectStore(storeGuid) {
1076
+ try {
1077
+ await client.session.associateStore(storeGuid);
1078
+ closeDialog();
1079
+ document.dispatchEvent(new CustomEvent("altazion:stores:store-associated", {
1080
+ bubbles: true,
1081
+ detail: { storeGuid }
1082
+ }));
1083
+ window.location.reload();
1084
+ } catch {
1085
+ showError("Impossible de sélectionner ce magasin. Veuillez réessayer.");
1086
+ }
1087
+ }
1088
+ function handleClick(event) {
1089
+ const target = event.target.closest("[data-action]");
1090
+ if (!target) return;
1091
+ const action = target.dataset.action;
1092
+ switch (action) {
1093
+ case "open-store-picker":
1094
+ openDialog();
1095
+ break;
1096
+ case "close-store-picker":
1097
+ closeDialog();
1098
+ break;
1099
+ case "store-picker-geolocate":
1100
+ void handleGeolocate();
1101
+ break;
1102
+ case "select-store": {
1103
+ const guid = target.dataset.storeGuid;
1104
+ if (guid) void handleSelectStore(guid);
1105
+ break;
1106
+ }
1107
+ }
1108
+ }
1109
+ function handleFormSubmit(event) {
1110
+ const form = event.target.closest(FORM_SELECTOR);
1111
+ if (!form) return;
1112
+ event.preventDefault();
1113
+ const input = form.querySelector(INPUT_SELECTOR);
1114
+ const postalCode = input == null ? void 0 : input.value.trim();
1115
+ if (postalCode) void handlePostalCodeSearch(postalCode);
1116
+ }
1117
+ document.addEventListener("click", handleClick);
1118
+ document.addEventListener("submit", handleFormSubmit);
1119
+ return () => {
1120
+ document.removeEventListener("click", handleClick);
1121
+ document.removeEventListener("submit", handleFormSubmit);
1122
+ };
1123
+ }
1124
+ function fallbackRender(store) {
1125
+ const name = store.label ?? store.code ?? "Magasin";
1126
+ const address = [store.streetAddress, store.postalCode, store.city].filter(Boolean).join(", ");
1127
+ return `<li class="hz-store-picker__item"><div class="hz-store-picker__item-info"><strong class="hz-store-picker__item-name">${escHtml(name)}</strong>` + (address ? `<address class="hz-store-picker__item-address">${escHtml(address)}</address>` : "") + `</div><button class="hz-store-picker__item-select" type="button" data-action="select-store" data-store-guid="${escHtml(store.guid)}">Choisir ce magasin</button></li>`;
1128
+ }
1129
+ function escHtml(str) {
1130
+ return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
1131
+ }
959
1132
  const productCardTemplate = '<article class="altz-product-card{{#unless (isAvailable availability)}} altz-product-card--unavailable{{/unless}}">\n {{#if imageUrl}}\n <a href="{{productUrl reference}}" class="altz-product-card__image-link" aria-label="{{name}}">\n <img\n src="{{thumbnailUrl imageUrl 400}}"\n alt="{{name}}"\n class="altz-product-card__image"\n loading="lazy"\n width="400"\n />\n </a>\n {{/if}}\n\n <div class="altz-product-card__body">\n <a href="{{productUrl reference}}" class="altz-product-card__title">\n {{name}}\n </a>\n\n <div class="altz-product-card__pricing">\n {{#if discount}}\n <span class="altz-product-card__price altz-product-card__price--discounted">\n {{formatPrice price}}\n </span>\n <span class="altz-product-card__price altz-product-card__price--original">\n {{formatPrice originalPrice}}\n </span>\n <span class="altz-product-card__discount-badge">\n {{discountPercent originalPrice price}}\n </span>\n {{else}}\n <span class="altz-product-card__price">\n {{formatPrice price}}\n </span>\n {{/if}}\n </div>\n\n {{#unless (isAvailable availability)}}\n <p class="altz-product-card__unavailable">{{unavailableLabel}}</p>\n {{/unless}}\n\n {{#if (isAvailable availability)}}\n <button\n class="altz-product-card__add-to-cart"\n hx-ext="altazion"\n hx-altazion-cart-action="addItem"\n hx-altazion-cart-reference="{{reference}}"\n hx-altazion-cart-quantity="1"\n hx-altazion-refresh="#altz-cart-mini"\n hx-indicator=".altz-spinner"\n >\n {{addToCartLabel}}\n </button>\n {{/if}}\n </div>\n</article>\n';
960
1133
  const cartMiniTemplate = '<div id="altz-cart-mini" class="altz-cart-mini">\n <div class="altz-cart-mini__header">\n <span class="altz-cart-mini__label">{{cartLabel}}</span>\n <span class="altz-cart-mini__count">{{cartCount}}</span>\n </div>\n\n {{#cartHasItems}}\n <ul class="altz-cart-mini__lines">\n {{#each lines}}\n <li class="altz-cart-mini__line">\n <span class="altz-cart-mini__line-name">{{this.productName}}</span>\n <span class="altz-cart-mini__line-qty">× {{this.quantity}}</span>\n <span class="altz-cart-mini__line-price">{{formatPrice this.unitPriceWithTax}}</span>\n </li>\n {{/each}}\n </ul>\n\n <div class="altz-cart-mini__total">\n <span>{{totalLabel}}</span>\n <strong>{{cartTotal}}</strong>\n </div>\n\n <a href="{{cartUrl}}" class="altz-cart-mini__cta">\n {{viewCartLabel}}\n </a>\n {{/cartHasItems}}\n\n {{^cartHasItems}}\n <p class="altz-cart-mini__empty">{{emptyLabel}}</p>\n {{/cartHasItems}}\n</div>\n';
961
1134
  const productListTemplate = '<section class="altz-product-list">\n {{#if title}}\n <h2 class="altz-product-list__title">{{title}}</h2>\n {{/if}}\n\n {{#if products.length}}\n <ul class="altz-product-list__grid" role="list">\n {{#each products}}\n <li class="altz-product-list__item">\n <article class="altz-product-card{{#unless (isAvailable this.availability)}} altz-product-card--unavailable{{/unless}}">\n {{#if this.imageUrl}}\n <a href="{{productUrl this.reference}}" class="altz-product-card__image-link" aria-label="{{this.name}}">\n <img\n src="{{thumbnailUrl this.imageUrl 300}}"\n alt="{{this.name}}"\n class="altz-product-card__image"\n loading="lazy"\n width="300"\n />\n </a>\n {{/if}}\n\n <div class="altz-product-card__body">\n <a href="{{productUrl this.reference}}" class="altz-product-card__title">\n {{this.name}}\n </a>\n\n <div class="altz-product-card__pricing">\n {{#if this.discount}}\n <span class="altz-product-card__price altz-product-card__price--discounted">\n {{formatPrice this.price}}\n </span>\n <span class="altz-product-card__price altz-product-card__price--original">\n {{formatPrice this.originalPrice}}\n </span>\n <span class="altz-product-card__discount-badge">\n {{discountPercent this.originalPrice this.price}}\n </span>\n {{else}}\n <span class="altz-product-card__price">\n {{formatPrice this.price}}\n </span>\n {{/if}}\n </div>\n\n {{#if (isAvailable this.availability)}}\n <button\n class="altz-product-card__add-to-cart"\n hx-ext="altazion"\n hx-altazion-cart-action="addItem"\n hx-altazion-cart-reference="{{this.reference}}"\n hx-altazion-cart-quantity="1"\n hx-altazion-refresh="#altz-cart-mini"\n hx-indicator=".altz-spinner"\n >\n {{../addToCartLabel}}\n </button>\n {{/if}}\n </div>\n </article>\n </li>\n {{/each}}\n </ul>\n {{else}}\n <p class="altz-product-list__empty">{{emptyLabel}}</p>\n {{/if}}\n</section>\n';
@@ -1041,6 +1214,7 @@ function initAltazionHtmx(config) {
1041
1214
  registerCartHelpers(handlebars, getCart, defaults);
1042
1215
  }
1043
1216
  declarativeRenderer.render();
1217
+ const disposeStorePicker = initStorePicker(client, handlebarsRuntime);
1044
1218
  const offlineEl = typeof document !== "undefined" ? document.querySelector(offlineSelector) ?? document.body : null;
1045
1219
  let lastConnectivityStatus = null;
1046
1220
  const updateOfflineClass = () => {
@@ -1088,6 +1262,7 @@ function initAltazionHtmx(config) {
1088
1262
  document.removeEventListener("altazion:marketing:items-loaded", syncMarketingItemsFromEvent);
1089
1263
  }
1090
1264
  declarativeRenderer.dispose();
1265
+ disposeStorePicker();
1091
1266
  unsubscribeConnectivity == null ? void 0 : unsubscribeConnectivity();
1092
1267
  }
1093
1268
  };