@cartbase/storefront 0.22.0 → 0.22.1

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.
@@ -0,0 +1,65 @@
1
+ import type { StorefrontClient } from "../api/http"
2
+ import { listPickupPoints, type PickupPointsResponse, type StorePickupPoint } from "../api/integrations"
3
+ import { distanceMeters, normalizeForMatch } from "./geocode"
4
+
5
+ // The reference pickers cache the full catalogue. Scope it to the SDK client
6
+ // (the store), carrier, mode and language instead of sharing one global list.
7
+ const catalogues = new WeakMap<StorefrontClient, Map<string, {
8
+ expires: number
9
+ promise: Promise<PickupPointsResponse>
10
+ }>>()
11
+
12
+ export function loadPickupPoints(
13
+ client: StorefrontClient,
14
+ provider: string,
15
+ mode: "office" | "locker",
16
+ locale: string
17
+ ): Promise<PickupPointsResponse> {
18
+ let cache = catalogues.get(client)
19
+ if (!cache) catalogues.set(client, cache = new Map())
20
+ const key = JSON.stringify([provider, mode, locale])
21
+ const hit = cache.get(key)
22
+ if (hit && hit.expires > Date.now()) return hit.promise
23
+ const promise = listPickupPoints(client, provider, { mode, catalogue: true, locale })
24
+ .catch((error) => {
25
+ if (cache.get(key)?.promise === promise) cache.delete(key)
26
+ throw error
27
+ })
28
+ cache.set(key, { expires: Date.now() + 10 * 60 * 1000, promise })
29
+ return promise
30
+ }
31
+
32
+ /** BoxNow's reference picker city-locks both lists, comparing both scripts. */
33
+ export function pickupPointsInCity(points: StorePickupPoint[], city: string): StorePickupPoint[] {
34
+ const needle = normalizeForMatch(city.trim())
35
+ if (!needle) return points
36
+ return points.filter((point) => normalizeForMatch(point.city).includes(needle) ||
37
+ (point.provider === "boxnow" && normalizeForMatch(point.address).includes(needle)))
38
+ }
39
+
40
+ /** The reference's full catalogue → distance sort → nearest three, in that order. */
41
+ export function nearestPickupPoints(
42
+ points: StorePickupPoint[],
43
+ position: { lat: number; lng: number } | null,
44
+ city: string
45
+ ): Array<{ point: StorePickupPoint; distance: number | null }> {
46
+ if (position && Number.isFinite(position.lat) && Number.isFinite(position.lng)) {
47
+ const located = points.filter((p) =>
48
+ p.latitude !== null && p.longitude !== null &&
49
+ Number.isFinite(p.latitude) && Number.isFinite(p.longitude)
50
+ )
51
+ if (located.length) return located.map((point) => ({ point,
52
+ distance: distanceMeters(position.lat, position.lng, point.latitude!, point.longitude!),
53
+ })).sort((a, b) => a.distance - b.distance).slice(0, 3)
54
+ }
55
+ return pickupPointsInCity(points, city).slice(0, 3).map((point) => ({ point, distance: null }))
56
+ }
57
+
58
+ /** The same catalogue powers search, including streets and either script. */
59
+ export function searchPickupPoints(points: StorePickupPoint[], search: string): StorePickupPoint[] {
60
+ const needle = normalizeForMatch(search.trim())
61
+ if (needle.length < 2) return []
62
+ return points.filter((point) => normalizeForMatch(
63
+ [point.name, point.city, point.address, point.postal_code].join(" ")
64
+ ).includes(needle)).slice(0, 8)
65
+ }