@cartbase/storefront 0.17.0 → 0.18.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.
- package/LICENSE +21 -21
- package/package.json +3 -1
- package/src/api/auth.ts +108 -108
- package/src/api/carts.ts +523 -523
- package/src/api/categories.ts +184 -184
- package/src/api/checkout.ts +526 -526
- package/src/api/collections.ts +130 -130
- package/src/api/consent.ts +75 -75
- package/src/api/content.ts +125 -125
- package/src/api/customers.ts +303 -303
- package/src/api/gift-cards.ts +112 -112
- package/src/api/http.ts +180 -180
- package/src/api/index.ts +30 -30
- package/src/api/menus.ts +77 -77
- package/src/api/metaobjects.ts +136 -136
- package/src/api/orders.ts +290 -290
- package/src/api/products.ts +43 -1
- package/src/api/redirects.ts +37 -37
- package/src/api/regions.ts +200 -200
- package/src/api/reviews.ts +259 -259
- package/src/api/search.ts +163 -163
- package/src/api/store.ts +35 -35
- package/src/api/types.ts +91 -91
- package/src/cart-drawer/context.tsx +367 -168
- package/src/cart-drawer/cross-sell-carousel.tsx +211 -211
- package/src/cart-drawer/cross-sell-sidebar.tsx +158 -158
- package/src/cart-drawer/gift-wrap.tsx +82 -82
- package/src/cart-drawer/index.ts +73 -68
- package/src/cart-drawer/item/index.tsx +162 -162
- package/src/cart-drawer/item/quantity.tsx +8 -14
- package/src/cart-drawer/item/upsell.tsx +110 -110
- package/src/cart-drawer/labels.ts +123 -123
- package/src/cart-drawer/mutation-queue.ts +78 -0
- package/src/cart-drawer/notes.tsx +131 -131
- package/src/cart-drawer/sticky-footer.tsx +73 -73
- package/src/cart-drawer/summary-breakdown.tsx +197 -197
- package/src/checkout/address-error-copy.ts +117 -117
- package/src/checkout/boxnow-locker-selector.tsx +410 -410
- package/src/checkout/compare-addresses.ts +40 -40
- package/src/checkout/discount-section.tsx +218 -218
- package/src/checkout/error-copy-codes.ts +63 -63
- package/src/checkout/geocode.ts +154 -154
- package/src/checkout/gift-card-section.tsx +224 -224
- package/src/checkout/index.ts +72 -72
- package/src/checkout/labels.ts +495 -495
- package/src/checkout/payment-button.tsx +372 -372
- package/src/checkout/payment-error-copy.ts +154 -154
- package/src/checkout/promotion-error-copy.ts +91 -91
- package/src/checkout/use-checkout-orchestration.ts +1604 -1592
- package/src/common/country-flag.tsx +52 -52
- package/src/common/country-select.tsx +11 -11
- package/src/common/index.ts +20 -20
- package/src/common/market-select.tsx +57 -57
- package/src/index.ts +12 -12
- package/src/lib/cookie-names.ts +14 -0
- package/src/lib/country-name.ts +59 -59
- package/src/lib/get-product-price.ts +133 -133
- package/src/lib/media-image.tsx +39 -39
- package/src/lib/payment-constants.ts +53 -53
- package/src/lib/platform.ts +13 -13
- package/src/lib/price.tsx +39 -39
- package/src/lib/store-api-error.ts +36 -36
- package/src/lib/variant-caption.ts +32 -32
- package/src/locales/bg.ts +467 -467
- package/src/locales/context.ts +37 -37
- package/src/locales/en.ts +26 -26
- package/src/locales/es.ts +466 -466
- package/src/locales/index.ts +19 -19
- package/src/locales/provider.tsx +59 -59
- package/src/locales/types.ts +77 -77
- package/src/order/index.ts +62 -62
- package/src/order/labels.ts +79 -79
- package/src/order/order-totals.tsx +250 -250
- package/src/primitives/select-field.tsx +93 -93
- package/src/products/image-gallery.tsx +43 -43
- package/src/products/index.ts +68 -59
- package/src/products/option-select.tsx +67 -67
- package/src/products/product-actions-wrapper.tsx +63 -62
- package/src/products/product-actions.tsx +151 -150
- package/src/products/product-price.tsx +69 -69
- package/src/products/product-promises.tsx +61 -61
- package/src/products/product-specs.tsx +65 -65
- package/src/products/product-tabs.tsx +123 -123
- package/src/products/product-template.tsx +163 -162
- package/src/products/purchase-options.tsx +130 -130
- package/src/products/sets.ts +110 -0
- package/src/products/use-product-actions.ts +199 -164
- package/src/products/variant-matching.ts +71 -71
- package/src/products/variant-url.ts +74 -74
- package/src/reviews-ui/index.ts +73 -73
- package/src/store/category-template.tsx +136 -136
- package/src/store/index.ts +40 -40
- package/src/tracking/chatgpt-pixel.tsx +99 -99
- package/src/tracking/consent-init.tsx +62 -62
- package/src/tracking/events.ts +348 -348
- package/src/tracking/ga4.tsx +93 -93
- package/src/tracking/google-ads.ts +84 -84
- package/src/tracking/gtm.tsx +60 -60
- package/src/tracking/inline-script.ts +49 -49
- package/src/tracking/oaiq.ts +206 -206
- package/src/tracking/tiktok-pixel.tsx +91 -91
- package/src/tracking/track-init.tsx +56 -56
- package/src/tracking/track-order-purchase.tsx +122 -122
- package/src/tracking/ttq.ts +180 -180
- package/src/tracking/use-tracking-config.ts +54 -54
- package/theme/index.css +25 -25
- package/theme/theme.css +150 -150
- package/theme/tokens.css +106 -106
|
@@ -1,117 +1,117 @@
|
|
|
1
|
-
import type { AddressErrorCopy } from "./labels"
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Recognizes an address-save failure and picks the copy for it.
|
|
5
|
-
*
|
|
6
|
-
* THE COPY IS NOT HERE. Until 2026-09-13 this file held thirteen hardcoded
|
|
7
|
-
* BULGARIAN sentences, so every merchant who installed the package showed
|
|
8
|
-
* Bulgarian on the one screen where a shopper is about to pay, with no way
|
|
9
|
-
* to override it. The copy now lives in the checkout label pack like all
|
|
10
|
-
* other copy, which means it is English by default and translated by
|
|
11
|
-
* whichever locale the store mounts.
|
|
12
|
-
*
|
|
13
|
-
* WHAT STAYS HERE IS THE RECOGNITION, and it stays on purpose: the codes
|
|
14
|
-
* and the patterns match the API's own error text, which is English wire
|
|
15
|
-
* format, not user-facing copy. Translating the matcher would break it.
|
|
16
|
-
*
|
|
17
|
-
* Ported from `@1click/ui/src/checkout/address-error-copy.ts` (v2.3.1) and
|
|
18
|
-
* adapted to the Cartbase error envelope: `StoreApiError` carries a
|
|
19
|
-
* structured `code` (POST /api/store/carts/:id, docs/storefront/carts.md),
|
|
20
|
-
* so recognition is code-first and the substring patterns are the fallback
|
|
21
|
-
* for network failures and any legacy wire.
|
|
22
|
-
*
|
|
23
|
-
* The mapping is deliberately narrow: only failures we recognize get their
|
|
24
|
-
* own sentence, and everything else gets the generic one rather than
|
|
25
|
-
* half-translated technical text.
|
|
26
|
-
*/
|
|
27
|
-
|
|
28
|
-
/** Cartbase cart-update error codes, mapped to a copy key. Code-first. */
|
|
29
|
-
export const ADDRESS_CODE_KEYS: Record<string, keyof AddressErrorCopy> = {
|
|
30
|
-
validation_failed: "validationFailed",
|
|
31
|
-
invalid_region: "invalidRegion",
|
|
32
|
-
region_required: "regionRequired",
|
|
33
|
-
cart_completed: "cartCompleted",
|
|
34
|
-
cart_not_found: "cartNotFound",
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Patterns matched in order, first match wins: most specific first,
|
|
39
|
-
* generic last. Case-insensitive. Kept verbatim from the production port.
|
|
40
|
-
*/
|
|
41
|
-
const PATTERNS: Array<{ match: RegExp; key: keyof AddressErrorCopy }> = [
|
|
42
|
-
// Email validation — confirmed (in @1click production) as the most common
|
|
43
|
-
// address-save failure shape.
|
|
44
|
-
{
|
|
45
|
-
match: /invalid[\s_]?(?:request:?\s*)?invalid[\s_]?email|invalid[\s_]?email/i,
|
|
46
|
-
key: "invalidEmail",
|
|
47
|
-
},
|
|
48
|
-
{
|
|
49
|
-
match:
|
|
50
|
-
/invalid[\s_]?postal|invalid[\s_]?post(?:code|al[\s_]?code)|postal[\s_]?code[\s_]?(?:is[\s_]?)?invalid/i,
|
|
51
|
-
key: "invalidPostalCode",
|
|
52
|
-
},
|
|
53
|
-
{
|
|
54
|
-
match: /invalid[\s_]?phone|phone[\s_]?(?:number[\s_]?)?(?:is[\s_]?)?invalid/i,
|
|
55
|
-
key: "invalidPhone",
|
|
56
|
-
},
|
|
57
|
-
{
|
|
58
|
-
match:
|
|
59
|
-
/invalid[\s_]?country|country[\s_]?(?:code[\s_]?)?(?:is[\s_]?)?invalid|no[\s_]?region|invalid[\s_]?region/i,
|
|
60
|
-
key: "invalidCountry",
|
|
61
|
-
},
|
|
62
|
-
// Cart already completed — race with order placement.
|
|
63
|
-
{
|
|
64
|
-
match:
|
|
65
|
-
/cart[\s_]?(?:is[\s_]?)?already[\s_]?completed|already[\s_]?an[\s_]?order|completed[\s_]?cart/i,
|
|
66
|
-
key: "cartCompleted",
|
|
67
|
-
},
|
|
68
|
-
{
|
|
69
|
-
match: /cart[\s_]?(?:not[\s_]?found|does[\s_]?not[\s_]?exist|expired)/i,
|
|
70
|
-
key: "cartNotFound",
|
|
71
|
-
},
|
|
72
|
-
{
|
|
73
|
-
match: /failed[\s_]?to[\s_]?fetch|network[\s_]?error|networkerror|econnreset|etimedout/i,
|
|
74
|
-
key: "network",
|
|
75
|
-
},
|
|
76
|
-
// The Next.js canned Server Components production message — surface a
|
|
77
|
-
// clean fallback instead of the English placeholder. (The Cartbase SDK
|
|
78
|
-
// throws from the browser, so this only fires for stores that proxy the
|
|
79
|
-
// save through a server action.)
|
|
80
|
-
{
|
|
81
|
-
match: /server[\s_]?components[\s_]?render|digest property is included/i,
|
|
82
|
-
key: "generic",
|
|
83
|
-
},
|
|
84
|
-
]
|
|
85
|
-
|
|
86
|
-
/** Which copy an error asks for, independent of language. */
|
|
87
|
-
export function addressErrorKey(err: unknown): keyof AddressErrorCopy {
|
|
88
|
-
if (err && typeof err === "object") {
|
|
89
|
-
const code = (err as { code?: unknown }).code
|
|
90
|
-
if (typeof code === "string" && ADDRESS_CODE_KEYS[code]) return ADDRESS_CODE_KEYS[code]
|
|
91
|
-
}
|
|
92
|
-
const raw = extractMessage(err)
|
|
93
|
-
if (!raw) return "generic"
|
|
94
|
-
for (const { match, key } of PATTERNS) {
|
|
95
|
-
if (match.test(raw)) return key
|
|
96
|
-
}
|
|
97
|
-
return "generic"
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
/**
|
|
101
|
-
* Turn any thrown or returned address-save error into the sentence a
|
|
102
|
-
* shopper reads, in the store's own language.
|
|
103
|
-
*/
|
|
104
|
-
export function translateAddressError(err: unknown, copy: AddressErrorCopy): string {
|
|
105
|
-
return copy[addressErrorKey(err)]
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
function extractMessage(err: unknown): string {
|
|
109
|
-
if (!err) return ""
|
|
110
|
-
if (typeof err === "string") return err
|
|
111
|
-
if (err instanceof Error) return err.message
|
|
112
|
-
if (typeof err === "object" && err !== null) {
|
|
113
|
-
const e = err as { message?: string; code?: string; type?: string }
|
|
114
|
-
return [e.message, e.code, e.type].filter(Boolean).join(" ")
|
|
115
|
-
}
|
|
116
|
-
return String(err)
|
|
117
|
-
}
|
|
1
|
+
import type { AddressErrorCopy } from "./labels"
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Recognizes an address-save failure and picks the copy for it.
|
|
5
|
+
*
|
|
6
|
+
* THE COPY IS NOT HERE. Until 2026-09-13 this file held thirteen hardcoded
|
|
7
|
+
* BULGARIAN sentences, so every merchant who installed the package showed
|
|
8
|
+
* Bulgarian on the one screen where a shopper is about to pay, with no way
|
|
9
|
+
* to override it. The copy now lives in the checkout label pack like all
|
|
10
|
+
* other copy, which means it is English by default and translated by
|
|
11
|
+
* whichever locale the store mounts.
|
|
12
|
+
*
|
|
13
|
+
* WHAT STAYS HERE IS THE RECOGNITION, and it stays on purpose: the codes
|
|
14
|
+
* and the patterns match the API's own error text, which is English wire
|
|
15
|
+
* format, not user-facing copy. Translating the matcher would break it.
|
|
16
|
+
*
|
|
17
|
+
* Ported from `@1click/ui/src/checkout/address-error-copy.ts` (v2.3.1) and
|
|
18
|
+
* adapted to the Cartbase error envelope: `StoreApiError` carries a
|
|
19
|
+
* structured `code` (POST /api/store/carts/:id, docs/storefront/carts.md),
|
|
20
|
+
* so recognition is code-first and the substring patterns are the fallback
|
|
21
|
+
* for network failures and any legacy wire.
|
|
22
|
+
*
|
|
23
|
+
* The mapping is deliberately narrow: only failures we recognize get their
|
|
24
|
+
* own sentence, and everything else gets the generic one rather than
|
|
25
|
+
* half-translated technical text.
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
/** Cartbase cart-update error codes, mapped to a copy key. Code-first. */
|
|
29
|
+
export const ADDRESS_CODE_KEYS: Record<string, keyof AddressErrorCopy> = {
|
|
30
|
+
validation_failed: "validationFailed",
|
|
31
|
+
invalid_region: "invalidRegion",
|
|
32
|
+
region_required: "regionRequired",
|
|
33
|
+
cart_completed: "cartCompleted",
|
|
34
|
+
cart_not_found: "cartNotFound",
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Patterns matched in order, first match wins: most specific first,
|
|
39
|
+
* generic last. Case-insensitive. Kept verbatim from the production port.
|
|
40
|
+
*/
|
|
41
|
+
const PATTERNS: Array<{ match: RegExp; key: keyof AddressErrorCopy }> = [
|
|
42
|
+
// Email validation — confirmed (in @1click production) as the most common
|
|
43
|
+
// address-save failure shape.
|
|
44
|
+
{
|
|
45
|
+
match: /invalid[\s_]?(?:request:?\s*)?invalid[\s_]?email|invalid[\s_]?email/i,
|
|
46
|
+
key: "invalidEmail",
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
match:
|
|
50
|
+
/invalid[\s_]?postal|invalid[\s_]?post(?:code|al[\s_]?code)|postal[\s_]?code[\s_]?(?:is[\s_]?)?invalid/i,
|
|
51
|
+
key: "invalidPostalCode",
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
match: /invalid[\s_]?phone|phone[\s_]?(?:number[\s_]?)?(?:is[\s_]?)?invalid/i,
|
|
55
|
+
key: "invalidPhone",
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
match:
|
|
59
|
+
/invalid[\s_]?country|country[\s_]?(?:code[\s_]?)?(?:is[\s_]?)?invalid|no[\s_]?region|invalid[\s_]?region/i,
|
|
60
|
+
key: "invalidCountry",
|
|
61
|
+
},
|
|
62
|
+
// Cart already completed — race with order placement.
|
|
63
|
+
{
|
|
64
|
+
match:
|
|
65
|
+
/cart[\s_]?(?:is[\s_]?)?already[\s_]?completed|already[\s_]?an[\s_]?order|completed[\s_]?cart/i,
|
|
66
|
+
key: "cartCompleted",
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
match: /cart[\s_]?(?:not[\s_]?found|does[\s_]?not[\s_]?exist|expired)/i,
|
|
70
|
+
key: "cartNotFound",
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
match: /failed[\s_]?to[\s_]?fetch|network[\s_]?error|networkerror|econnreset|etimedout/i,
|
|
74
|
+
key: "network",
|
|
75
|
+
},
|
|
76
|
+
// The Next.js canned Server Components production message — surface a
|
|
77
|
+
// clean fallback instead of the English placeholder. (The Cartbase SDK
|
|
78
|
+
// throws from the browser, so this only fires for stores that proxy the
|
|
79
|
+
// save through a server action.)
|
|
80
|
+
{
|
|
81
|
+
match: /server[\s_]?components[\s_]?render|digest property is included/i,
|
|
82
|
+
key: "generic",
|
|
83
|
+
},
|
|
84
|
+
]
|
|
85
|
+
|
|
86
|
+
/** Which copy an error asks for, independent of language. */
|
|
87
|
+
export function addressErrorKey(err: unknown): keyof AddressErrorCopy {
|
|
88
|
+
if (err && typeof err === "object") {
|
|
89
|
+
const code = (err as { code?: unknown }).code
|
|
90
|
+
if (typeof code === "string" && ADDRESS_CODE_KEYS[code]) return ADDRESS_CODE_KEYS[code]
|
|
91
|
+
}
|
|
92
|
+
const raw = extractMessage(err)
|
|
93
|
+
if (!raw) return "generic"
|
|
94
|
+
for (const { match, key } of PATTERNS) {
|
|
95
|
+
if (match.test(raw)) return key
|
|
96
|
+
}
|
|
97
|
+
return "generic"
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Turn any thrown or returned address-save error into the sentence a
|
|
102
|
+
* shopper reads, in the store's own language.
|
|
103
|
+
*/
|
|
104
|
+
export function translateAddressError(err: unknown, copy: AddressErrorCopy): string {
|
|
105
|
+
return copy[addressErrorKey(err)]
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function extractMessage(err: unknown): string {
|
|
109
|
+
if (!err) return ""
|
|
110
|
+
if (typeof err === "string") return err
|
|
111
|
+
if (err instanceof Error) return err.message
|
|
112
|
+
if (typeof err === "object" && err !== null) {
|
|
113
|
+
const e = err as { message?: string; code?: string; type?: string }
|
|
114
|
+
return [e.message, e.code, e.type].filter(Boolean).join(" ")
|
|
115
|
+
}
|
|
116
|
+
return String(err)
|
|
117
|
+
}
|