@asteby/storefront 0.3.0 → 0.4.0

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/mock.cjs CHANGED
@@ -1,5 +1,29 @@
1
1
  'use strict';
2
2
 
3
+ // src/geo.ts
4
+ var RAD = Math.PI / 180;
5
+ function distanceKm(a, b) {
6
+ const dLat = (b.lat - a.lat) * RAD;
7
+ const dLng = (b.lng - a.lng) * RAD;
8
+ const h = Math.sin(dLat / 2) ** 2 + Math.cos(a.lat * RAD) * Math.cos(b.lat * RAD) * Math.sin(dLng / 2) ** 2;
9
+ return 2 * 6371.0088 * Math.atan2(Math.sqrt(h), Math.sqrt(1 - h));
10
+ }
11
+ function isValidPoint(p) {
12
+ const { lat, lng } = p ?? {};
13
+ return Number.isFinite(lat) && Number.isFinite(lng) && Math.abs(lat) <= 90 && Math.abs(lng) <= 180 && !(lat === 0 && lng === 0);
14
+ }
15
+ function rankByDistance(items, origin, { radiusKm, limit, getPoint = (i) => i } = {}) {
16
+ const out = [];
17
+ for (const item of items) {
18
+ const p = getPoint(item);
19
+ if (!isValidPoint(p)) continue;
20
+ const d = distanceKm(origin, p);
21
+ if (radiusKm === void 0 || d <= radiusKm) out.push({ item, distanceKm: d });
22
+ }
23
+ out.sort((x, y) => x.distanceKm - y.distanceKm);
24
+ return limit === void 0 ? out : out.slice(0, Math.max(0, limit));
25
+ }
26
+
3
27
  // src/mock/seed.ts
4
28
  var MOCK_IMAGE_URL = (seed, width = 800, height = 800) => `https://picsum.photos/seed/${encodeURIComponent(seed)}/${width}/${height}`;
5
29
  var MOCK_CURRENCY = "MXN";
@@ -130,6 +154,73 @@ function buildCollections(imageUrl = MOCK_IMAGE_URL) {
130
154
  parent_handle: c.parent ?? null
131
155
  }));
132
156
  }
157
+ var MX_TZ = "America/Mexico_City";
158
+ var weekdays = (open, close, days = [1, 2, 3, 4, 5]) => days.map((day) => ({ day, open, close }));
159
+ function buildLocations(currency) {
160
+ const contacts = (phone) => ({ phone, email: null, whatsapp: null });
161
+ return [
162
+ {
163
+ id: "br_centro",
164
+ name: "Sucursal Centro",
165
+ kind: "store",
166
+ address: "Av. Principal 100, Col. Centro",
167
+ city: "Ciudad de M\xE9xico",
168
+ postal_code: "06000",
169
+ phone: "+52 55 0000 0001",
170
+ lat: 19.4326,
171
+ lng: -99.1332,
172
+ timezone: MX_TZ,
173
+ hours: "Mo-Fr 09:00-19:00, Sa 09:00-14:00",
174
+ opening_hours_text: "Lun\u2013Vie 9:00\u201319:00 \xB7 S\xE1b 9:00\u201314:00",
175
+ opening_hours: { weekly: [...weekdays("09:00", "19:00"), { day: 6, open: "09:00", close: "14:00" }] },
176
+ services: ["pickup", "returns"],
177
+ contacts: contacts("+52 55 0000 0001"),
178
+ pickup: { enabled: true, fee: null, prep_days: 0 }
179
+ },
180
+ {
181
+ id: "br_norte",
182
+ name: "Sucursal Norte",
183
+ kind: "store",
184
+ address: "Calle Secundaria 25, Col. Norte",
185
+ city: "Monterrey",
186
+ postal_code: "64000",
187
+ phone: null,
188
+ lat: 25.6866,
189
+ lng: -100.3161,
190
+ timezone: MX_TZ,
191
+ hours: "Mo-Sa 10:00-18:00",
192
+ opening_hours_text: "Lun\u2013S\xE1b 10:00\u201318:00",
193
+ opening_hours: { weekly: weekdays("10:00", "18:00", [1, 2, 3, 4, 5, 6]) },
194
+ services: ["pickup"],
195
+ contacts: contacts(null),
196
+ pickup: { enabled: true, fee: money(30, currency), prep_days: 1 }
197
+ },
198
+ {
199
+ id: "br_occidente",
200
+ name: "Punto de recolecci\xF3n Occidente",
201
+ kind: "pickup_point",
202
+ address: "Av. Vallarta 1500, Col. Americana",
203
+ city: "Guadalajara",
204
+ postal_code: "44100",
205
+ phone: "+52 33 0000 0003",
206
+ lat: 20.6736,
207
+ lng: -103.3897,
208
+ timezone: MX_TZ,
209
+ hours: "Mo-Fr 11:00-20:00",
210
+ opening_hours_text: "Lun\u2013Vie 11:00\u201320:00",
211
+ opening_hours: { weekly: weekdays("11:00", "20:00") },
212
+ services: ["pickup"],
213
+ contacts: contacts("+52 33 0000 0003"),
214
+ pickup: { enabled: false, fee: null, prep_days: 2 }
215
+ }
216
+ ];
217
+ }
218
+ var MOCK_POSTAL_CENTROIDS = {
219
+ "06000": { lat: 19.4326, lng: -99.1332 },
220
+ "64000": { lat: 25.6714, lng: -100.309 },
221
+ "44100": { lat: 20.6767, lng: -103.3475 },
222
+ "03100": { lat: 19.3885, lng: -99.1719 }
223
+ };
133
224
  function buildShop(currency, imageUrl = MOCK_IMAGE_URL) {
134
225
  return {
135
226
  name: "Mi tienda",
@@ -141,10 +232,7 @@ function buildShop(currency, imageUrl = MOCK_IMAGE_URL) {
141
232
  country_code: "MX",
142
233
  logo: { url: imageUrl("logo", 256, 256), alt: "Mi tienda", width: 256, height: 256 },
143
234
  contact: { email: "hola@mi-tienda.example", phone: "+52 55 0000 0000", whatsapp: "+5215500000000" },
144
- branches: [
145
- { id: "br_centro", name: "Sucursal Centro", address: "Av. Principal 100, Col. Centro", city: "Ciudad de M\xE9xico", phone: "+52 55 0000 0001", lat: 19.4326, lng: -99.1332, hours: "Mo-Fr 09:00-19:00, Sa 09:00-14:00" },
146
- { id: "br_norte", name: "Sucursal Norte", address: "Calle Secundaria 25, Col. Norte", city: "Monterrey", phone: null, lat: 25.6866, lng: -100.3161, hours: "Mo-Sa 10:00-18:00" }
147
- ],
235
+ branches: buildLocations(currency),
148
236
  policies: { free_shipping_min: money(999, currency), returns_days: 30 }
149
237
  };
150
238
  }
@@ -550,7 +638,7 @@ function createMockStorefront(options = {}) {
550
638
  const percent = codes.filter((d) => d.applicable).reduce((max, d) => Math.max(max, percentFor(d.code)), 0);
551
639
  const discount = Math.round(subtotal * percent / 100);
552
640
  let shipping = null;
553
- if (cart.delivery.method === "pickup") shipping = 0;
641
+ if (cart.delivery.method === "pickup") shipping = toCents(state.shop.branches.find((b) => b.id === cart.delivery.branch_id)?.pickup?.fee?.amount ?? "0");
554
642
  else if (cart.delivery.method === "shipping" && cart.delivery.rate_id) {
555
643
  const rate = shippingRates(subtotal - discount).find((r) => r.id === cart.delivery.rate_id);
556
644
  shipping = rate ? toCents(rate.cost.amount) : null;
@@ -631,6 +719,25 @@ function createMockStorefront(options = {}) {
631
719
  body: requireNode(state.collections.find((c) => c.handle === params[0]), `Collection ${params[0]}`),
632
720
  cache: "public"
633
721
  }));
722
+ const visibleLocations = () => state.shop.branches;
723
+ const round2 = (n) => Math.round(n * 100) / 100;
724
+ const withDistance = (origin) => rankByDistance(visibleLocations(), origin).map(({ item, distanceKm: distanceKm2 }) => ({ ...item, distance_km: Math.round(distanceKm2 * 10) / 10 }));
725
+ route("GET", "/locations", "catalog:read", ({ search }) => {
726
+ const cp = search.get("postal_code");
727
+ const origin = cp ? MOCK_POSTAL_CENTROIDS[cp] : void 0;
728
+ return { body: { nodes: origin ? withDistance(origin) : visibleLocations() }, cache: "public" };
729
+ });
730
+ route("GET", "/locations/nearest", "catalog:read", ({ search }) => {
731
+ const origin = { lat: Number(search.get("lat") ?? NaN), lng: Number(search.get("lng") ?? NaN) };
732
+ if (search.get("lat") === null || search.get("lng") === null || !isValidPoint(origin)) {
733
+ throw new HttpError(400, "validation", "lat and lng must be a valid point", "lat");
734
+ }
735
+ return { body: { nodes: withDistance({ lat: round2(origin.lat), lng: round2(origin.lng) }) }, cache: "private" };
736
+ });
737
+ route("GET", "/locations/:id", "catalog:read", ({ params }) => ({
738
+ body: requireNode(visibleLocations().find((b) => b.id === params[0]), `Location ${params[0]}`, "id"),
739
+ cache: "public"
740
+ }));
634
741
  route("GET", "/stock", "catalog:read", ({ search }) => {
635
742
  const skus = search.getAll("sku");
636
743
  if (skus.length > 250) throw new HttpError(400, "validation", "At most 250 SKUs", "sku");
@@ -714,7 +821,7 @@ function createMockStorefront(options = {}) {
714
821
  const cart = renderCart(getCartRecord(params[0]));
715
822
  const net = toCents(cart.cost.subtotal.amount) - toCents(cart.cost.discount.amount);
716
823
  return {
717
- body: { rates: shippingRates(net), pickup: state.shop.branches.map((b) => ({ branch_id: b.id, name: b.name })), partial: false, unavailable: [] },
824
+ body: { rates: shippingRates(net), pickup: state.shop.branches.filter((b) => b.pickup?.enabled !== false).map((b) => ({ branch_id: b.id, name: b.name, address: b.address, city: b.city })), partial: false, unavailable: [] },
718
825
  cache: "private"
719
826
  };
720
827
  });
@@ -732,7 +839,7 @@ function createMockStorefront(options = {}) {
732
839
  cart.delivery = { method: "shipping", rate_id: rateId, branch_id: null };
733
840
  } else if (body.method === "pickup") {
734
841
  const branchId = body.branch_id;
735
- if (typeof branchId !== "string" || !state.shop.branches.some((b) => b.id === branchId)) {
842
+ if (typeof branchId !== "string" || !state.shop.branches.some((b) => b.id === branchId && b.pickup?.enabled !== false)) {
736
843
  throw new HttpError(400, "validation", "Unknown branch_id", "branch_id");
737
844
  }
738
845
  cart.delivery = { method: "pickup", rate_id: null, branch_id: branchId };
@@ -1000,6 +1107,7 @@ function createMockStorefront(options = {}) {
1000
1107
  exports.MOCK_CURRENCY = MOCK_CURRENCY;
1001
1108
  exports.MOCK_CUSTOMER_EMAIL = MOCK_CUSTOMER_EMAIL;
1002
1109
  exports.MOCK_IMAGE_URL = MOCK_IMAGE_URL;
1110
+ exports.MOCK_POSTAL_CENTROIDS = MOCK_POSTAL_CENTROIDS;
1003
1111
  exports.createMockStorefront = createMockStorefront;
1004
1112
  //# sourceMappingURL=mock.cjs.map
1005
1113
  //# sourceMappingURL=mock.cjs.map