@cartbase/storefront 0.22.1 → 0.23.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/README.md +8 -6
- package/package.json +1 -1
- package/src/api/checkout.ts +12 -0
- package/src/checkout/carrier-marks.ts +10 -49
- package/src/checkout/checkout-client.tsx +1 -6
- package/src/checkout/fulfillment-option.ts +3 -1
- package/src/checkout/index.ts +1 -1
- package/src/checkout/pickup-option.ts +32 -8
- package/src/checkout/pickup-point-selector.tsx +12 -9
- package/src/checkout/pickup-points.ts +2 -3
- package/src/checkout/shipping-method-list.tsx +40 -157
- package/src/checkout/use-checkout-orchestration.ts +44 -172
package/README.md
CHANGED
|
@@ -47,12 +47,14 @@ NEXT_PUBLIC_CARTBASE_URL= # optional: only to point at a local or s
|
|
|
47
47
|
|
|
48
48
|
### Pickup points
|
|
49
49
|
|
|
50
|
-
The checkout pickup picker loads each carrier's complete eligible catalogue
|
|
51
|
-
through the store API, then shows the nearest three to the typed address.
|
|
52
|
-
Its search accepts Latin or Cyrillic; the mounted locale pack selects
|
|
53
|
-
Speedy's display language. Offices exclude automated lockers. BoxNow keeps
|
|
54
|
-
both lists within the checkout city. No carrier credentials or copied
|
|
55
|
-
catalogue are needed in the storefront.
|
|
50
|
+
The checkout pickup picker loads each carrier's complete eligible catalogue
|
|
51
|
+
through the store API, then shows the nearest three to the typed address.
|
|
52
|
+
Its search accepts Latin or Cyrillic; the mounted locale pack selects
|
|
53
|
+
Speedy's display language. Offices exclude automated lockers. BoxNow keeps
|
|
54
|
+
both lists within the checkout city. No carrier credentials or copied
|
|
55
|
+
catalogue are needed in the storefront. Every shipping option carries the
|
|
56
|
+
connected adapter's identity, mark, destination mode and pickup scope, so the
|
|
57
|
+
same picker and logo path work for every carrier without storefront branches.
|
|
56
58
|
|
|
57
59
|
### Tracking, in three lines
|
|
58
60
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cartbase/storefront",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.23.0",
|
|
4
4
|
"description": "Storefront SDK + UI component library for Cartbase stores: typed API client, checkout orchestration, cart drawer, product/catalog components, tracking. Source-shipped TypeScript — add it to transpilePackages.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
package/src/api/checkout.ts
CHANGED
|
@@ -49,6 +49,18 @@ export interface StoreShippingOption {
|
|
|
49
49
|
service_zone_id: string | null
|
|
50
50
|
shipping_profile_id: string | null
|
|
51
51
|
data: Record<string, unknown> | null
|
|
52
|
+
/** Carrier identity and destination, resolved by the platform adapter. */
|
|
53
|
+
carrier?: {
|
|
54
|
+
id: string
|
|
55
|
+
name: string
|
|
56
|
+
mark_url: string | null
|
|
57
|
+
fulfillment_option: {
|
|
58
|
+
id: string
|
|
59
|
+
mode: "address" | "office" | "locker"
|
|
60
|
+
label: string
|
|
61
|
+
} | null
|
|
62
|
+
pickup_scope: "city" | "global"
|
|
63
|
+
} | null
|
|
52
64
|
/** The option's display label (e.g. "Express") as `{ label }`, or null. */
|
|
53
65
|
type: { label: string } | null
|
|
54
66
|
/** Flat price in the cart currency; null without `cart_id` or when no price row matches. */
|
|
@@ -1,53 +1,14 @@
|
|
|
1
|
-
|
|
2
|
-
* The carrier's own mark beside its name on a checkout shipping row.
|
|
3
|
-
*
|
|
4
|
-
* A shopper picks a courier by recognising it, and until 2026-09-18 a store
|
|
5
|
-
* got names only unless it hunted down logo files and passed a map of its
|
|
6
|
-
* own (store-package card). The marks are the platform's: the same files
|
|
7
|
-
* the admin's integrations screens wear, served from the platform the store
|
|
8
|
-
* already talks to, so a carrier we add tomorrow arrives with its mark and
|
|
9
|
-
* no store copies an image anywhere.
|
|
10
|
-
*
|
|
11
|
-
* Keyed by the carrier's fulfillment option id, which the platform's
|
|
12
|
-
* registry owns and a shipping option carries in `data.id`. Both modes of a
|
|
13
|
-
* carrier wear the same mark: it names the courier, not the destination.
|
|
14
|
-
*/
|
|
15
|
-
|
|
16
|
-
const MARK: Record<string, { carrier: string; alt: string }> = {
|
|
17
|
-
"econt-address": { carrier: "econt", alt: "Econt" },
|
|
18
|
-
"econt-office": { carrier: "econt", alt: "Econt" },
|
|
19
|
-
"speedy-address": { carrier: "speedy", alt: "Speedy" },
|
|
20
|
-
"speedy-office": { carrier: "speedy", alt: "Speedy" },
|
|
21
|
-
"boxnow-locker": { carrier: "boxnow", alt: "BOX NOW" },
|
|
22
|
-
"pigeon-address": { carrier: "pigeon", alt: "Pigeon Express" },
|
|
23
|
-
"pigeon-office": { carrier: "pigeon", alt: "Pigeon Express" },
|
|
24
|
-
"pigeon-locker": { carrier: "pigeon", alt: "Pigeon Express" },
|
|
25
|
-
}
|
|
1
|
+
import type { StoreShippingOption } from "../api/checkout"
|
|
26
2
|
|
|
27
3
|
/**
|
|
28
|
-
* The
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
* `origin` is the SDK client's own (`client.origin`), which in a storefront
|
|
33
|
-
* is the shop's own address, because the store API ships no CORS headers and
|
|
34
|
-
* every storefront reaches it through a same-origin `/api/store/*` rewrite.
|
|
35
|
-
* So the mark is asked for under that same path, which is the one address a
|
|
36
|
-
* storefront can always reach; the platform answers it with the file it
|
|
37
|
-
* already serves. Pointing at the platform's own `/integrations/…` instead
|
|
38
|
-
* is what made every mark 404, since that path is not proxied and the shop's
|
|
39
|
-
* own domain has no such file (found on the workbench before release).
|
|
4
|
+
* The carrier's mark beside its checkout row. Identity and the same-origin
|
|
5
|
+
* mark path arrive resolved by the platform adapter; the package keeps no
|
|
6
|
+
* carrier list.
|
|
40
7
|
*/
|
|
41
|
-
export function
|
|
42
|
-
|
|
43
|
-
):
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
marks[optionId] = {
|
|
48
|
-
src: `${base}/api/store/integrations/marks/${mark.carrier}`,
|
|
49
|
-
alt: mark.alt,
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
return marks
|
|
8
|
+
export function carrierMark(
|
|
9
|
+
option: Pick<StoreShippingOption, "carrier">
|
|
10
|
+
): { src: string; alt: string } | null {
|
|
11
|
+
const carrier = option.carrier
|
|
12
|
+
if (!carrier?.mark_url) return null
|
|
13
|
+
return { src: carrier.mark_url, alt: carrier.name }
|
|
53
14
|
}
|
|
@@ -7,7 +7,6 @@ import type { Cart } from "../api/carts"
|
|
|
7
7
|
import type { StoreShippingOption } from "../api/checkout"
|
|
8
8
|
import type { StoreCustomer } from "../api/customers"
|
|
9
9
|
import { CheckoutAddressForm } from "./address-form"
|
|
10
|
-
import { carrierMarks } from "./carrier-marks"
|
|
11
10
|
import { DiscountSection } from "./discount-section"
|
|
12
11
|
import { MobileCheckoutBottomBar } from "./mobile-checkout-bottom-bar"
|
|
13
12
|
import { MobileCheckoutTopBar } from "./mobile-checkout-top-bar"
|
|
@@ -214,11 +213,7 @@ export function CheckoutClient({
|
|
|
214
213
|
userCity: o.formData["shipping_address.city"] ?? "",
|
|
215
214
|
userAddress: o.formData["shipping_address.address_1"] ?? "",
|
|
216
215
|
}}
|
|
217
|
-
|
|
218
|
-
// finger; a store that passes its own map keeps it.
|
|
219
|
-
logoByFulfillmentOptionId={
|
|
220
|
-
logoByFulfillmentOptionId ?? carrierMarks(client.origin)
|
|
221
|
-
}
|
|
216
|
+
logoByFulfillmentOptionId={logoByFulfillmentOptionId}
|
|
222
217
|
/>
|
|
223
218
|
|
|
224
219
|
<CheckoutPaymentMethodList
|
|
@@ -16,8 +16,10 @@ import type { StoreShippingOption } from "../api/checkout"
|
|
|
16
16
|
* because a cart carried over from that platform still speaks it.
|
|
17
17
|
*/
|
|
18
18
|
export function fulfillmentOptionId(
|
|
19
|
-
option: Pick<StoreShippingOption, "data"> | null | undefined
|
|
19
|
+
option: Pick<StoreShippingOption, "data" | "carrier"> | null | undefined
|
|
20
20
|
): string | null {
|
|
21
|
+
const resolved = option?.carrier?.fulfillment_option?.id
|
|
22
|
+
if (typeof resolved === "string" && resolved) return resolved
|
|
21
23
|
const data = option?.data as
|
|
22
24
|
| { fulfillment_option?: unknown; id?: unknown }
|
|
23
25
|
| null
|
package/src/checkout/index.ts
CHANGED
|
@@ -44,7 +44,7 @@ export {
|
|
|
44
44
|
type PickupPoint,
|
|
45
45
|
} from "./pickup-point-selector"
|
|
46
46
|
export { pickupOptionOf, type PickupOption } from "./pickup-option"
|
|
47
|
-
export {
|
|
47
|
+
export { carrierMark } from "./carrier-marks"
|
|
48
48
|
export { AddressSelect } from "./address-select"
|
|
49
49
|
export { CompanyDetails } from "./company-details"
|
|
50
50
|
export { DiscountSection } from "./discount-section"
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
+
import type { StoreShippingOption } from "../api/checkout"
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* Does this shipping option deliver to a POINT, and to whose?
|
|
3
5
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* of them, and a carrier connected tomorrow opens its picker with no line
|
|
8
|
-
* added anywhere.
|
|
6
|
+
* The platform resolves provider, mode and picker scope through the carrier
|
|
7
|
+
* adapter. The string form remains as rollout compatibility for older store
|
|
8
|
+
* API responses.
|
|
9
9
|
*
|
|
10
10
|
* `address` is the door: there is no point to choose, so it answers null.
|
|
11
11
|
*
|
|
@@ -17,19 +17,43 @@
|
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
19
|
export type PickupOption = {
|
|
20
|
-
/**
|
|
20
|
+
/** Carrier adapter id. */
|
|
21
21
|
provider: string
|
|
22
22
|
mode: "office" | "locker"
|
|
23
|
+
scope: "city" | "global"
|
|
23
24
|
}
|
|
24
25
|
|
|
25
26
|
export function pickupOptionOf(
|
|
26
|
-
|
|
27
|
+
input:
|
|
28
|
+
| string
|
|
29
|
+
| Partial<Pick<StoreShippingOption, "carrier" | "data">>
|
|
30
|
+
| null
|
|
31
|
+
| undefined
|
|
27
32
|
): PickupOption | null {
|
|
33
|
+
if (typeof input === "object" && input) {
|
|
34
|
+
const carrier = input.carrier
|
|
35
|
+
const mode = carrier?.fulfillment_option?.mode
|
|
36
|
+
if (carrier && (mode === "office" || mode === "locker")) {
|
|
37
|
+
return { provider: carrier.id, mode, scope: carrier.pickup_scope }
|
|
38
|
+
}
|
|
39
|
+
const data = input.data as
|
|
40
|
+
| { fulfillment_option?: unknown; id?: unknown }
|
|
41
|
+
| null
|
|
42
|
+
| undefined
|
|
43
|
+
const legacy =
|
|
44
|
+
typeof data?.fulfillment_option === "string"
|
|
45
|
+
? data.fulfillment_option
|
|
46
|
+
: typeof data?.id === "string"
|
|
47
|
+
? data.id
|
|
48
|
+
: null
|
|
49
|
+
return pickupOptionOf(legacy)
|
|
50
|
+
}
|
|
51
|
+
const fulfillmentOptionId = input
|
|
28
52
|
if (!fulfillmentOptionId) return null
|
|
29
53
|
const dash = fulfillmentOptionId.lastIndexOf("-")
|
|
30
54
|
if (dash <= 0) return null
|
|
31
55
|
const mode = fulfillmentOptionId.slice(dash + 1)
|
|
32
56
|
if (mode !== "office" && mode !== "locker") return null
|
|
33
57
|
const provider = fulfillmentOptionId.slice(0, dash)
|
|
34
|
-
return provider ? { provider, mode } : null
|
|
58
|
+
return provider ? { provider, mode, scope: "global" } : null
|
|
35
59
|
}
|
|
@@ -53,10 +53,12 @@ export type PickupPoint = StorePickupPoint
|
|
|
53
53
|
type PickupPointSelectorProps = {
|
|
54
54
|
/** The SDK transport — the points come from the store, never the carrier. */
|
|
55
55
|
client: StorefrontClient
|
|
56
|
-
/**
|
|
56
|
+
/** Carrier adapter id from the shipping option contract. */
|
|
57
57
|
provider: string
|
|
58
|
-
/** Which kind of point this option delivers to. */
|
|
59
|
-
mode: "office" | "locker"
|
|
58
|
+
/** Which kind of point this option delivers to. */
|
|
59
|
+
mode: "office" | "locker"
|
|
60
|
+
/** Adapter-declared catalogue scope. */
|
|
61
|
+
scope: "city" | "global"
|
|
60
62
|
/** The address the shopper has typed so far, for "nearest to me". */
|
|
61
63
|
userCity: string
|
|
62
64
|
userAddress: string
|
|
@@ -70,8 +72,9 @@ const MIN_SEARCH_CHARS = 2
|
|
|
70
72
|
|
|
71
73
|
export function PickupPointSelector({
|
|
72
74
|
client,
|
|
73
|
-
provider,
|
|
74
|
-
mode,
|
|
75
|
+
provider,
|
|
76
|
+
mode,
|
|
77
|
+
scope,
|
|
75
78
|
userCity,
|
|
76
79
|
userAddress,
|
|
77
80
|
selectedPoint,
|
|
@@ -120,10 +123,10 @@ export function PickupPointSelector({
|
|
|
120
123
|
return () => { alive = false; clearTimeout(timer) }
|
|
121
124
|
}, [userCity, userAddress])
|
|
122
125
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
126
|
+
const eligiblePoints = useMemo(
|
|
127
|
+
() => (scope === "city" ? pickupPointsInCity(points, userCity) : points),
|
|
128
|
+
[points, scope, userCity]
|
|
129
|
+
)
|
|
127
130
|
const nearest = useMemo(() => nearestPickupPoints(eligiblePoints, coords, userCity),
|
|
128
131
|
[eligiblePoints, coords, userCity])
|
|
129
132
|
const searchResults = useMemo(() => searchPickupPoints(eligiblePoints, search),
|
|
@@ -29,12 +29,11 @@ export function loadPickupPoints(
|
|
|
29
29
|
return promise
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
/**
|
|
32
|
+
/** A city-scoped adapter's eligible points, comparing both scripts. */
|
|
33
33
|
export function pickupPointsInCity(points: StorePickupPoint[], city: string): StorePickupPoint[] {
|
|
34
34
|
const needle = normalizeForMatch(city.trim())
|
|
35
35
|
if (!needle) return points
|
|
36
|
-
return points.filter((point) => normalizeForMatch(point.city).includes(needle)
|
|
37
|
-
(point.provider === "boxnow" && normalizeForMatch(point.address).includes(needle)))
|
|
36
|
+
return points.filter((point) => normalizeForMatch(point.city).includes(needle))
|
|
38
37
|
}
|
|
39
38
|
|
|
40
39
|
/** The reference's full catalogue → distance sort → nearest three, in that order. */
|
|
@@ -1,41 +1,33 @@
|
|
|
1
1
|
"use client"
|
|
2
2
|
|
|
3
|
-
import type { StorefrontClient } from "../api/http"
|
|
4
|
-
import type { StoreShippingOption } from "../api/checkout"
|
|
5
|
-
import
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
type EcontOffice,
|
|
12
|
-
} from "./econt-office-selector"
|
|
13
|
-
import { BoxNowLockerSelector } from "./boxnow-locker-selector"
|
|
14
|
-
import { PigeonOfficeSelector } from "./pigeon-office-selector"
|
|
15
|
-
import {
|
|
16
|
-
PickupPointSelector,
|
|
17
|
-
type PickupPoint,
|
|
3
|
+
import type { StorefrontClient } from "../api/http"
|
|
4
|
+
import type { StoreShippingOption } from "../api/checkout"
|
|
5
|
+
import { Price } from "../lib/price"
|
|
6
|
+
import { cn } from "../lib/utils"
|
|
7
|
+
import { useCheckoutLabels } from "./context"
|
|
8
|
+
import {
|
|
9
|
+
PickupPointSelector,
|
|
10
|
+
type PickupPoint,
|
|
18
11
|
} from "./pickup-point-selector"
|
|
19
12
|
import type { PickupPointKeys } from "../api/integrations"
|
|
20
|
-
import { pickupOptionOf, type PickupOption } from "./pickup-option"
|
|
21
|
-
import { fulfillmentOptionId } from "./fulfillment-option"
|
|
22
|
-
import {
|
|
13
|
+
import { pickupOptionOf, type PickupOption } from "./pickup-option"
|
|
14
|
+
import { fulfillmentOptionId } from "./fulfillment-option"
|
|
15
|
+
import { carrierMark } from "./carrier-marks"
|
|
16
|
+
import { ErrorMessage } from "./error-message"
|
|
23
17
|
|
|
24
18
|
/**
|
|
25
19
|
* CheckoutShippingMethodList — radio-style list of shipping options.
|
|
26
|
-
* When the user selects an
|
|
27
|
-
*
|
|
20
|
+
* When the user selects an adapter-declared office or locker option, the
|
|
21
|
+
* shared picker expands inline inside that row.
|
|
28
22
|
*
|
|
29
23
|
* Ported from `@1click/ui/src/checkout/shipping-method-list.tsx` (v2.3.1)
|
|
30
24
|
* with the Cartbase data seam: options are `StoreShippingOption` rows from
|
|
31
25
|
* `checkout.listShippingOptions(client, {cart_id})` — rule-filtered and
|
|
32
26
|
* display-ordered SERVER-SIDE (checkout rules + `checkout_method_order`,
|
|
33
27
|
* docs/storefront/checkout.md); this component only renders what arrives.
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
* Detection uses the stable fulfillment-option id set by the backend
|
|
38
|
-
* provider (`shipping_option.data.id`) — NOT the display name. Admins
|
|
28
|
+
* Pickup catalogues use the SDK transport through one shared picker.
|
|
29
|
+
* Detection uses the adapter-resolved fulfillment option returned by the
|
|
30
|
+
* backend, never the display name. Admins
|
|
39
31
|
* rename options freely, and Bulgarian labels overlap ("До точен адрес с
|
|
40
32
|
* ЕКОНТ" contains "еконт" but is address delivery, not office).
|
|
41
33
|
*
|
|
@@ -52,42 +44,10 @@ type CheckoutShippingMethodListProps = {
|
|
|
52
44
|
onSelect: (id: string) => void
|
|
53
45
|
addressReady: boolean
|
|
54
46
|
currencyCode: string
|
|
55
|
-
/** Optional: Econt office state + handler for inline-expand rows */
|
|
56
|
-
econt?: {
|
|
57
|
-
detect?: (option: StoreShippingOption) => boolean
|
|
58
|
-
selectedOffice: EcontOffice | null
|
|
59
|
-
onSelectOffice: (office: EcontOffice | null) => void
|
|
60
|
-
userCity: string
|
|
61
|
-
userAddress: string
|
|
62
|
-
}
|
|
63
|
-
/** Optional: BoxNow locker state + handler for inline-expand rows */
|
|
64
|
-
boxnow?: {
|
|
65
|
-
detect?: (option: StoreShippingOption) => boolean
|
|
66
|
-
/** SDK transport for the locker-directory fetch. */
|
|
67
|
-
client: StorefrontClient
|
|
68
|
-
selectedLocker: BoxNowLocker | null
|
|
69
|
-
onSelectLocker: (locker: BoxNowLocker | null) => void
|
|
70
|
-
userCity: string
|
|
71
|
-
userAddress: string
|
|
72
|
-
}
|
|
73
|
-
/** Optional: Pigeon Express office state + handler for inline-expand rows */
|
|
74
|
-
pigeon?: {
|
|
75
|
-
detect?: (option: StoreShippingOption) => boolean
|
|
76
|
-
/** SDK transport for the pickup-point directory (the carrier's own
|
|
77
|
-
* catalogue is credentialed, so it is served by the platform). */
|
|
78
|
-
client: StorefrontClient
|
|
79
|
-
selectedOffice: PigeonOffice | null
|
|
80
|
-
onSelectOffice: (office: PigeonOffice | null) => void
|
|
81
|
-
userCity: string
|
|
82
|
-
userAddress: string
|
|
83
|
-
}
|
|
84
47
|
/**
|
|
85
|
-
* The pickup picker
|
|
86
|
-
*
|
|
87
|
-
*
|
|
88
|
-
* through one door, so a carrier connected tomorrow opens its picker with
|
|
89
|
-
* nothing added here. The three above stay for stores that mount them;
|
|
90
|
-
* where both are given, the carrier-specific one wins for its own carrier.
|
|
48
|
+
* The pickup picker for every carrier. The option's resolved carrier
|
|
49
|
+
* contract says whether it is an office or locker, and the platform
|
|
50
|
+
* answers through one catalogue door.
|
|
91
51
|
*/
|
|
92
52
|
pickup?: {
|
|
93
53
|
/** SDK transport — the catalogue is the store's answer, not the
|
|
@@ -103,11 +63,8 @@ type CheckoutShippingMethodListProps = {
|
|
|
103
63
|
userAddress: string
|
|
104
64
|
}
|
|
105
65
|
/**
|
|
106
|
-
* Optional per-store carrier branding
|
|
107
|
-
* option id
|
|
108
|
-
* etc. If a match is found, a small logo renders between the radio and
|
|
109
|
-
* the option name. Stores that don't supply a map get the unbranded
|
|
110
|
-
* (radio + text only) layout.
|
|
66
|
+
* Optional per-store carrier branding override, keyed by the stable
|
|
67
|
+
* fulfillment option id. Without one, the adapter's mark renders.
|
|
111
68
|
*/
|
|
112
69
|
logoByFulfillmentOptionId?: Record<string, { src: string; alt: string }>
|
|
113
70
|
/** Show the shipping options as a read-only price PREVIEW before the
|
|
@@ -116,7 +73,7 @@ type CheckoutShippingMethodListProps = {
|
|
|
116
73
|
* non-selectable — a hint tells the shopper to enter their address to
|
|
117
74
|
* choose. For flat-priced stores this lets people see the cost up front
|
|
118
75
|
* (a common reason carts stall) without letting them pick a method that
|
|
119
|
-
* needs a city (
|
|
76
|
+
* needs a city (office or locker). Default `false` keeps the
|
|
120
77
|
* classic address-gated placeholder for every existing store. */
|
|
121
78
|
previewWhenAddressNotReady?: boolean
|
|
122
79
|
}
|
|
@@ -126,21 +83,9 @@ type CheckoutShippingMethodListProps = {
|
|
|
126
83
|
const getFulfillmentOptionId = (option: StoreShippingOption): string | null =>
|
|
127
84
|
fulfillmentOptionId(option)
|
|
128
85
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
const defaultBoxnowDetect = (option: StoreShippingOption): boolean => {
|
|
134
|
-
return getFulfillmentOptionId(option) === "boxnow-locker"
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
const defaultPigeonDetect = (option: StoreShippingOption): boolean => {
|
|
138
|
-
return getFulfillmentOptionId(option) === "pigeon-office"
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
/** Which carrier, and which kind of point, from the option's own id. */
|
|
142
|
-
const detectPickup = (option: StoreShippingOption): PickupOption | null =>
|
|
143
|
-
pickupOptionOf(getFulfillmentOptionId(option))
|
|
86
|
+
/** Which carrier, which point kind and scope, from its adapter contract. */
|
|
87
|
+
const detectPickup = (option: StoreShippingOption): PickupOption | null =>
|
|
88
|
+
pickupOptionOf(option)
|
|
144
89
|
|
|
145
90
|
export function CheckoutShippingMethodList({
|
|
146
91
|
shippingMethods,
|
|
@@ -152,18 +97,11 @@ export function CheckoutShippingMethodList({
|
|
|
152
97
|
onSelect,
|
|
153
98
|
addressReady,
|
|
154
99
|
currencyCode,
|
|
155
|
-
|
|
156
|
-
boxnow,
|
|
157
|
-
pigeon,
|
|
158
|
-
pickup,
|
|
100
|
+
pickup,
|
|
159
101
|
logoByFulfillmentOptionId,
|
|
160
102
|
previewWhenAddressNotReady = false,
|
|
161
103
|
}: CheckoutShippingMethodListProps) {
|
|
162
104
|
const labels = useCheckoutLabels()
|
|
163
|
-
const detectEcont = econt?.detect ?? defaultEcontDetect
|
|
164
|
-
const detectBoxnow = boxnow?.detect ?? defaultBoxnowDetect
|
|
165
|
-
const detectPigeon = pigeon?.detect ?? defaultPigeonDetect
|
|
166
|
-
|
|
167
105
|
// Preview mode: address not filled yet, but the store wants the options
|
|
168
106
|
// shown as a read-only price preview rather than a placeholder. Rows are
|
|
169
107
|
// rendered but locked (no selection) — see `previewWhenAddressNotReady`.
|
|
@@ -244,26 +182,14 @@ export function CheckoutShippingMethodList({
|
|
|
244
182
|
? option.amount
|
|
245
183
|
: calculatedPricesMap[option.id]
|
|
246
184
|
const isFree = price === 0
|
|
247
|
-
|
|
248
|
-
const
|
|
249
|
-
|
|
250
|
-
const
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
// the option's own id, so a carrier connected tomorrow opens its
|
|
256
|
-
// picker with no line added here. It yields to a legacy picker
|
|
257
|
-
// when the store still mounts one for that carrier.
|
|
258
|
-
const pickupOption = pickup ? detectPickup(option) : null
|
|
259
|
-
const isPickup =
|
|
260
|
-
!!pickupOption && !isEcontOffice && !isBoxnowLocker && !isPigeonOffice
|
|
261
|
-
const hasExpanded =
|
|
262
|
-
selected &&
|
|
263
|
-
(isEcontOffice || isBoxnowLocker || isPigeonOffice || isPickup)
|
|
264
|
-
const logo = logoByFulfillmentOptionId
|
|
265
|
-
? logoByFulfillmentOptionId[getFulfillmentOptionId(option) ?? ""]
|
|
266
|
-
: undefined
|
|
185
|
+
// Any option whose adapter resolves to a pickup destination.
|
|
186
|
+
const pickupOption = pickup ? detectPickup(option) : null
|
|
187
|
+
const isPickup = !!pickupOption
|
|
188
|
+
const hasExpanded = selected && isPickup
|
|
189
|
+
const logo =
|
|
190
|
+
logoByFulfillmentOptionId?.[getFulfillmentOptionId(option) ?? ""] ??
|
|
191
|
+
carrierMark(option) ??
|
|
192
|
+
undefined
|
|
267
193
|
|
|
268
194
|
return (
|
|
269
195
|
<div
|
|
@@ -312,22 +238,7 @@ export function CheckoutShippingMethodList({
|
|
|
312
238
|
<span className="text-sm font-medium text-foreground">
|
|
313
239
|
{option.name}
|
|
314
240
|
</span>
|
|
315
|
-
{selected &&
|
|
316
|
-
<p className="text-xs text-muted-foreground mt-0.5">
|
|
317
|
-
{econt.selectedOffice.name}
|
|
318
|
-
</p>
|
|
319
|
-
)}
|
|
320
|
-
{selected && isBoxnowLocker && boxnow?.selectedLocker && (
|
|
321
|
-
<p className="text-xs text-muted-foreground mt-0.5">
|
|
322
|
-
{boxnow.selectedLocker.title}
|
|
323
|
-
</p>
|
|
324
|
-
)}
|
|
325
|
-
{selected && isPigeonOffice && pigeon?.selectedOffice && (
|
|
326
|
-
<p className="text-xs text-muted-foreground mt-0.5">
|
|
327
|
-
{pigeon.selectedOffice.name}
|
|
328
|
-
</p>
|
|
329
|
-
)}
|
|
330
|
-
{selected && isPickup && pickup?.selectedPoint && (
|
|
241
|
+
{selected && isPickup && pickup?.selectedPoint && (
|
|
331
242
|
<p className="text-xs text-muted-foreground mt-0.5">
|
|
332
243
|
{pickup.selectedPoint.name}
|
|
333
244
|
</p>
|
|
@@ -371,40 +282,12 @@ export function CheckoutShippingMethodList({
|
|
|
371
282
|
</span>
|
|
372
283
|
</button>
|
|
373
284
|
|
|
374
|
-
{hasExpanded &&
|
|
375
|
-
<EcontOfficeSelector
|
|
376
|
-
userCity={econt.userCity}
|
|
377
|
-
userAddress={econt.userAddress}
|
|
378
|
-
selectedOffice={econt.selectedOffice}
|
|
379
|
-
onSelect={econt.onSelectOffice}
|
|
380
|
-
/>
|
|
381
|
-
)}
|
|
382
|
-
|
|
383
|
-
{hasExpanded && isBoxnowLocker && boxnow && (
|
|
384
|
-
<BoxNowLockerSelector
|
|
385
|
-
client={boxnow.client}
|
|
386
|
-
userCity={boxnow.userCity}
|
|
387
|
-
userAddress={boxnow.userAddress}
|
|
388
|
-
selectedLocker={boxnow.selectedLocker}
|
|
389
|
-
onSelect={boxnow.onSelectLocker}
|
|
390
|
-
/>
|
|
391
|
-
)}
|
|
392
|
-
|
|
393
|
-
{hasExpanded && isPigeonOffice && pigeon && (
|
|
394
|
-
<PigeonOfficeSelector
|
|
395
|
-
client={pigeon.client}
|
|
396
|
-
userCity={pigeon.userCity}
|
|
397
|
-
userAddress={pigeon.userAddress}
|
|
398
|
-
selectedOffice={pigeon.selectedOffice}
|
|
399
|
-
onSelect={pigeon.onSelectOffice}
|
|
400
|
-
/>
|
|
401
|
-
)}
|
|
402
|
-
|
|
403
|
-
{hasExpanded && isPickup && pickup && pickupOption && (
|
|
285
|
+
{hasExpanded && isPickup && pickup && pickupOption && (
|
|
404
286
|
<PickupPointSelector
|
|
405
287
|
client={pickup.client}
|
|
406
|
-
provider={pickupOption.provider}
|
|
407
|
-
mode={pickupOption.mode}
|
|
288
|
+
provider={pickupOption.provider}
|
|
289
|
+
mode={pickupOption.mode}
|
|
290
|
+
scope={pickupOption.scope}
|
|
408
291
|
userCity={pickup.userCity}
|
|
409
292
|
userAddress={pickup.userAddress}
|
|
410
293
|
selectedPoint={pickup.selectedPoint}
|
|
@@ -49,11 +49,9 @@ import { clearCartCookie } from "../lib/cookie-names"
|
|
|
49
49
|
import { useCartDrawer } from "../cart-drawer/context"
|
|
50
50
|
import compareAddresses from "./compare-addresses"
|
|
51
51
|
import { translateAddressError } from "./address-error-copy"
|
|
52
|
-
import { translatePaymentError } from "./payment-error-copy"
|
|
53
|
-
import { useCheckoutLabels, useOrderConfirmedPath } from "./context"
|
|
54
|
-
import
|
|
55
|
-
import type { BoxNowLocker, PigeonOffice } from "../api/integrations"
|
|
56
|
-
import { fulfillmentOptionId } from "./fulfillment-option"
|
|
52
|
+
import { translatePaymentError } from "./payment-error-copy"
|
|
53
|
+
import { useCheckoutLabels, useOrderConfirmedPath } from "./context"
|
|
54
|
+
import { fulfillmentOptionId } from "./fulfillment-option"
|
|
57
55
|
import { useCheckoutFunnel } from "./use-checkout-funnel"
|
|
58
56
|
import { amountDueAfterTender, isNothingToPay } from "./summary-math"
|
|
59
57
|
import { forgetFiredEvents } from "../tracking/once"
|
|
@@ -931,72 +929,7 @@ export function useCheckoutOrchestration({
|
|
|
931
929
|
// Server-side, prepare-checkout removes the PREVIOUS prepare's carrier
|
|
932
930
|
// keys before merging (the `_prepared_carrier_keys` marker), so
|
|
933
931
|
// switching carriers can never leak stale fields into the order.
|
|
934
|
-
|
|
935
|
-
useState<EcontOffice | null>(
|
|
936
|
-
cart?.metadata?.econt_office_code
|
|
937
|
-
? ({
|
|
938
|
-
code: cart.metadata.econt_office_code as string,
|
|
939
|
-
name: cart.metadata.econt_office_name as string,
|
|
940
|
-
} as EcontOffice)
|
|
941
|
-
: null
|
|
942
|
-
)
|
|
943
|
-
|
|
944
|
-
const handleSelectEcontOffice = useCallback(
|
|
945
|
-
(office: EcontOffice | null) => {
|
|
946
|
-
setSelectedEcontOffice(office)
|
|
947
|
-
},
|
|
948
|
-
[]
|
|
949
|
-
)
|
|
950
|
-
|
|
951
|
-
const [selectedBoxnowLocker, setSelectedBoxnowLocker] =
|
|
952
|
-
useState<BoxNowLocker | null>(
|
|
953
|
-
cart?.metadata?.boxnow_locker_id
|
|
954
|
-
? ({
|
|
955
|
-
id: cart.metadata.boxnow_locker_id as string,
|
|
956
|
-
title: (cart.metadata.boxnow_locker_title as string) ?? "",
|
|
957
|
-
addressLine1:
|
|
958
|
-
(cart.metadata.boxnow_locker_address as string) ?? "",
|
|
959
|
-
addressLine2: "",
|
|
960
|
-
postalCode: (cart.metadata.boxnow_locker_postal as string) ?? "",
|
|
961
|
-
country: "",
|
|
962
|
-
lat: null,
|
|
963
|
-
lng: null,
|
|
964
|
-
note: "",
|
|
965
|
-
} as BoxNowLocker)
|
|
966
|
-
: null
|
|
967
|
-
)
|
|
968
|
-
|
|
969
|
-
const handleSelectBoxnowLocker = useCallback(
|
|
970
|
-
(locker: BoxNowLocker | null) => {
|
|
971
|
-
setSelectedBoxnowLocker(locker)
|
|
972
|
-
},
|
|
973
|
-
[]
|
|
974
|
-
)
|
|
975
|
-
|
|
976
|
-
// Pigeon Express keeps offices and lockers in ONE catalogue, told apart by
|
|
977
|
-
// which id key the order carries (`pigeon_office_id` here; the platform's
|
|
978
|
-
// destination registry owns both names).
|
|
979
|
-
const [selectedPigeonOffice, setSelectedPigeonOffice] =
|
|
980
|
-
useState<PigeonOffice | null>(
|
|
981
|
-
cart?.metadata?.pigeon_office_id
|
|
982
|
-
? ({
|
|
983
|
-
id: cart.metadata.pigeon_office_id as string,
|
|
984
|
-
name: (cart.metadata.pigeon_point_name as string) ?? "",
|
|
985
|
-
type: "office",
|
|
986
|
-
city: (cart.metadata.pigeon_point_city as string) ?? "",
|
|
987
|
-
address: (cart.metadata.pigeon_point_address as string) ?? "",
|
|
988
|
-
} as PigeonOffice)
|
|
989
|
-
: null
|
|
990
|
-
)
|
|
991
|
-
|
|
992
|
-
const handleSelectPigeonOffice = useCallback(
|
|
993
|
-
(office: PigeonOffice | null) => {
|
|
994
|
-
setSelectedPigeonOffice(office)
|
|
995
|
-
},
|
|
996
|
-
[]
|
|
997
|
-
)
|
|
998
|
-
|
|
999
|
-
/**
|
|
932
|
+
/**
|
|
1000
933
|
* THE pickup point, whatever the carrier (2026-09-18).
|
|
1001
934
|
*
|
|
1002
935
|
* The three states above are one state per carrier, written into one
|
|
@@ -1172,9 +1105,7 @@ export function useCheckoutOrchestration({
|
|
|
1172
1105
|
// specific destination (e.g. picking direct address after BoxNow
|
|
1173
1106
|
// locker). The carrier's own keys are never eagerly written, so only
|
|
1174
1107
|
// our own note is cleared beside the state.
|
|
1175
|
-
|
|
1176
|
-
setSelectedEcontOffice(null)
|
|
1177
|
-
setSelectedPickupPoint(null)
|
|
1108
|
+
setSelectedPickupPoint(null)
|
|
1178
1109
|
setPickupKeys(null)
|
|
1179
1110
|
rememberChoice({
|
|
1180
1111
|
_checkout_shipping_option: id,
|
|
@@ -1207,9 +1138,6 @@ export function useCheckoutOrchestration({
|
|
|
1207
1138
|
const selectedFulfillmentOptionId = useMemo(() => {
|
|
1208
1139
|
return fulfillmentOptionId(selectedShippingOption)
|
|
1209
1140
|
}, [selectedShippingOption])
|
|
1210
|
-
const selectedIsBoxnow = selectedFulfillmentOptionId === "boxnow-locker"
|
|
1211
|
-
const selectedIsEcont = selectedFulfillmentOptionId === "econt-office"
|
|
1212
|
-
const selectedIsPigeon = selectedFulfillmentOptionId === "pigeon-office"
|
|
1213
1141
|
|
|
1214
1142
|
/**
|
|
1215
1143
|
* WHICH carrier and WHICH kind of point this option delivers to, read from
|
|
@@ -1218,38 +1146,28 @@ export function useCheckoutOrchestration({
|
|
|
1218
1146
|
* them that way, so a carrier connected tomorrow is understood here without
|
|
1219
1147
|
* a line being added. `address` is the door and has no point to pick.
|
|
1220
1148
|
*/
|
|
1221
|
-
const selectedPickup = useMemo(
|
|
1222
|
-
() => pickupOptionOf(
|
|
1223
|
-
[
|
|
1224
|
-
)
|
|
1225
|
-
|
|
1226
|
-
// Defensive: trust cart.metadata for locker/office IDs in addition to
|
|
1227
|
-
// local React state.
|
|
1228
|
-
//
|
|
1229
|
-
//
|
|
1230
|
-
// timing / hydration race). Without this fallback, the local-null kept
|
|
1149
|
+
const selectedPickup = useMemo(
|
|
1150
|
+
() => pickupOptionOf(selectedShippingOption),
|
|
1151
|
+
[selectedShippingOption]
|
|
1152
|
+
)
|
|
1153
|
+
|
|
1154
|
+
// Defensive: trust cart.metadata for locker/office IDs in addition to
|
|
1155
|
+
// local React state. A mobile picker can update cart.metadata while its
|
|
1156
|
+
// local point remains stale after a touch/hydration race. Without this
|
|
1157
|
+
// fallback, the local-null kept
|
|
1231
1158
|
// `deliveryReady` false and the entire payment section turned into a
|
|
1232
1159
|
// ghost — even though the cart server-side knew the locker was set.
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
// An option that delivers to a point is not ready until the point is
|
|
1238
|
-
// named, whatever the carrier. The three lines under it are the same rule
|
|
1239
|
-
// written per carrier, kept for stores still mounting the old components;
|
|
1240
|
-
// a store on the shared picker is answered by the first.
|
|
1160
|
+
|
|
1161
|
+
// An option that delivers to a point is not ready until the point is
|
|
1162
|
+
// named, whatever the carrier.
|
|
1241
1163
|
const pickupSatisfied =
|
|
1242
1164
|
!selectedPickup ||
|
|
1243
1165
|
(!!selectedPickupPoint && selectedPickupPoint.provider === selectedPickup.provider)
|
|
1244
1166
|
|
|
1245
|
-
const deliveryReady =
|
|
1246
|
-
(!!selectedShippingMethod ||
|
|
1247
|
-
(cart?.shipping_methods?.length ?? 0) > 0) &&
|
|
1248
|
-
|
|
1249
|
-
// The legacy pickers answer for their own carriers.
|
|
1250
|
-
(selectedIsBoxnow && (!!selectedBoxnowLocker || hasBoxnowLockerInCart)) ||
|
|
1251
|
-
(selectedIsEcont && (!!selectedEcontOffice || hasEcontOfficeInCart)) ||
|
|
1252
|
-
(selectedIsPigeon && (!!selectedPigeonOffice || hasPigeonOfficeInCart)))
|
|
1167
|
+
const deliveryReady =
|
|
1168
|
+
(!!selectedShippingMethod ||
|
|
1169
|
+
(cart?.shipping_methods?.length ?? 0) > 0) &&
|
|
1170
|
+
pickupSatisfied
|
|
1253
1171
|
|
|
1254
1172
|
// Reconcile paymentTab with currently-available methods. When the
|
|
1255
1173
|
// store's paymentMethodFilter strips a method in response to a shipping
|
|
@@ -1511,39 +1429,7 @@ export function useCheckoutOrchestration({
|
|
|
1511
1429
|
// state. Called by PaymentButton on click.
|
|
1512
1430
|
const buildPrepareCheckoutPayload =
|
|
1513
1431
|
useCallback((): PrepareCheckoutInput => {
|
|
1514
|
-
const carrierMetadata: Record<string, unknown> = {}
|
|
1515
|
-
if (selectedEcontOffice) {
|
|
1516
|
-
const addr = [
|
|
1517
|
-
selectedEcontOffice.address?.street,
|
|
1518
|
-
selectedEcontOffice.address?.num,
|
|
1519
|
-
]
|
|
1520
|
-
.filter(Boolean)
|
|
1521
|
-
.join(" ")
|
|
1522
|
-
carrierMetadata.econt_office_code = selectedEcontOffice.code
|
|
1523
|
-
carrierMetadata.econt_office_name = selectedEcontOffice.name
|
|
1524
|
-
carrierMetadata.econt_office_city =
|
|
1525
|
-
selectedEcontOffice.address?.city?.name || ""
|
|
1526
|
-
carrierMetadata.econt_office_address = addr
|
|
1527
|
-
carrierMetadata.econt_office_phone =
|
|
1528
|
-
selectedEcontOffice.phones?.[0] || ""
|
|
1529
|
-
}
|
|
1530
|
-
if (selectedBoxnowLocker) {
|
|
1531
|
-
carrierMetadata.boxnow_locker_id = selectedBoxnowLocker.id
|
|
1532
|
-
carrierMetadata.boxnow_locker_title = selectedBoxnowLocker.title
|
|
1533
|
-
carrierMetadata.boxnow_locker_address =
|
|
1534
|
-
selectedBoxnowLocker.addressLine1 ?? ""
|
|
1535
|
-
carrierMetadata.boxnow_locker_postal =
|
|
1536
|
-
selectedBoxnowLocker.postalCode ?? ""
|
|
1537
|
-
}
|
|
1538
|
-
if (selectedPigeonOffice) {
|
|
1539
|
-
// The platform's destination registry owns these names: the id key
|
|
1540
|
-
// says office or locker, and the three label keys are what the order
|
|
1541
|
-
// page and the waybill read back (src/lib/shipping/destinations.ts).
|
|
1542
|
-
carrierMetadata.pigeon_office_id = String(selectedPigeonOffice.id)
|
|
1543
|
-
carrierMetadata.pigeon_point_name = selectedPigeonOffice.name
|
|
1544
|
-
carrierMetadata.pigeon_point_city = selectedPigeonOffice.city ?? ""
|
|
1545
|
-
carrierMetadata.pigeon_point_address = selectedPigeonOffice.address ?? ""
|
|
1546
|
-
}
|
|
1432
|
+
const carrierMetadata: Record<string, unknown> = {}
|
|
1547
1433
|
// The carrier-agnostic pickup point (2026-09-18). The names come from
|
|
1548
1434
|
// the platform's destination registry with the point itself, so this
|
|
1549
1435
|
// writes what THIS carrier's booking reads without knowing which
|
|
@@ -1606,12 +1492,11 @@ export function useCheckoutOrchestration({
|
|
|
1606
1492
|
...tender,
|
|
1607
1493
|
}
|
|
1608
1494
|
}, [
|
|
1609
|
-
formData,
|
|
1610
|
-
selectedShippingMethod,
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
paymentTab,
|
|
1495
|
+
formData,
|
|
1496
|
+
selectedShippingMethod,
|
|
1497
|
+
selectedPickupPoint,
|
|
1498
|
+
pickupKeys,
|
|
1499
|
+
paymentTab,
|
|
1615
1500
|
cardId,
|
|
1616
1501
|
codMethodId,
|
|
1617
1502
|
nothingToPay,
|
|
@@ -1658,18 +1543,16 @@ export function useCheckoutOrchestration({
|
|
|
1658
1543
|
const stateSnapshot = {
|
|
1659
1544
|
cart_id: cart.id,
|
|
1660
1545
|
paymentTab,
|
|
1661
|
-
cardId,
|
|
1662
|
-
codMethodId,
|
|
1663
|
-
selectedShippingMethod,
|
|
1664
|
-
|
|
1665
|
-
? {
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
}
|
|
1672
|
-
: null,
|
|
1546
|
+
cardId,
|
|
1547
|
+
codMethodId,
|
|
1548
|
+
selectedShippingMethod,
|
|
1549
|
+
selectedPickupPoint: selectedPickupPoint
|
|
1550
|
+
? {
|
|
1551
|
+
provider: selectedPickupPoint.provider,
|
|
1552
|
+
id: selectedPickupPoint.id,
|
|
1553
|
+
name: selectedPickupPoint.name,
|
|
1554
|
+
}
|
|
1555
|
+
: null,
|
|
1673
1556
|
hasStripeBundle: !!stripeBundle,
|
|
1674
1557
|
}
|
|
1675
1558
|
dbg("[buy-click] STATE", stateSnapshot)
|
|
@@ -1853,11 +1736,9 @@ export function useCheckoutOrchestration({
|
|
|
1853
1736
|
cart.id,
|
|
1854
1737
|
paymentTab,
|
|
1855
1738
|
cardId,
|
|
1856
|
-
codMethodId,
|
|
1857
|
-
selectedShippingMethod,
|
|
1858
|
-
|
|
1859
|
-
selectedBoxnowLocker,
|
|
1860
|
-
selectedPigeonOffice,
|
|
1739
|
+
codMethodId,
|
|
1740
|
+
selectedShippingMethod,
|
|
1741
|
+
selectedPickupPoint,
|
|
1861
1742
|
flushAddressSave,
|
|
1862
1743
|
buildPrepareCheckoutPayload,
|
|
1863
1744
|
placeOrder,
|
|
@@ -1891,12 +1772,9 @@ export function useCheckoutOrchestration({
|
|
|
1891
1772
|
shippingMethods,
|
|
1892
1773
|
calculatedPricesMap,
|
|
1893
1774
|
isLoadingPrices,
|
|
1894
|
-
selectedShippingMethod,
|
|
1895
|
-
selectedShippingOption,
|
|
1896
|
-
selectedFulfillmentOptionId,
|
|
1897
|
-
selectedIsBoxnow,
|
|
1898
|
-
selectedIsEcont,
|
|
1899
|
-
selectedIsPigeon,
|
|
1775
|
+
selectedShippingMethod,
|
|
1776
|
+
selectedShippingOption,
|
|
1777
|
+
selectedFulfillmentOptionId,
|
|
1900
1778
|
shippingLoading,
|
|
1901
1779
|
shippingError,
|
|
1902
1780
|
optimisticShippingCost,
|
|
@@ -1904,13 +1782,7 @@ export function useCheckoutOrchestration({
|
|
|
1904
1782
|
handleSelectShipping,
|
|
1905
1783
|
|
|
1906
1784
|
// Carriers
|
|
1907
|
-
|
|
1908
|
-
handleSelectEcontOffice,
|
|
1909
|
-
selectedBoxnowLocker,
|
|
1910
|
-
handleSelectBoxnowLocker,
|
|
1911
|
-
selectedPigeonOffice,
|
|
1912
|
-
handleSelectPigeonOffice,
|
|
1913
|
-
/** The pickup point, whatever the carrier, and which carrier and kind of
|
|
1785
|
+
/** The pickup point, whatever the carrier, and which carrier and kind of
|
|
1914
1786
|
* point the chosen option asks for. Null on a door delivery. */
|
|
1915
1787
|
selectedPickupPoint,
|
|
1916
1788
|
handleSelectPickupPoint,
|