@cartbase/storefront 0.19.0 → 0.20.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.
Files changed (105) hide show
  1. package/LICENSE +21 -21
  2. package/package.json +258 -258
  3. package/src/api/auth.ts +108 -108
  4. package/src/api/carts.ts +523 -523
  5. package/src/api/categories.ts +184 -184
  6. package/src/api/checkout.ts +526 -526
  7. package/src/api/collections.ts +130 -130
  8. package/src/api/consent.ts +75 -75
  9. package/src/api/content.ts +125 -125
  10. package/src/api/customers.ts +303 -303
  11. package/src/api/gift-cards.ts +112 -112
  12. package/src/api/http.ts +180 -180
  13. package/src/api/index.ts +30 -30
  14. package/src/api/menus.ts +77 -77
  15. package/src/api/metaobjects.ts +136 -136
  16. package/src/api/orders.ts +290 -290
  17. package/src/api/redirects.ts +37 -37
  18. package/src/api/regions.ts +200 -200
  19. package/src/api/reviews.ts +22 -1
  20. package/src/api/search.ts +163 -163
  21. package/src/api/store.ts +35 -35
  22. package/src/api/types.ts +91 -91
  23. package/src/cart-drawer/context.tsx +778 -778
  24. package/src/cart-drawer/cross-sell-carousel.tsx +211 -211
  25. package/src/cart-drawer/cross-sell-sidebar.tsx +158 -158
  26. package/src/cart-drawer/gift-wrap.tsx +82 -82
  27. package/src/cart-drawer/item/index.tsx +162 -162
  28. package/src/cart-drawer/item/upsell.tsx +110 -110
  29. package/src/cart-drawer/labels.ts +123 -123
  30. package/src/cart-drawer/mutation-queue.ts +78 -78
  31. package/src/cart-drawer/notes.tsx +131 -131
  32. package/src/cart-drawer/sticky-footer.tsx +73 -73
  33. package/src/cart-drawer/summary-breakdown.tsx +197 -197
  34. package/src/checkout/address-error-copy.ts +117 -117
  35. package/src/checkout/boxnow-locker-selector.tsx +410 -410
  36. package/src/checkout/compare-addresses.ts +40 -40
  37. package/src/checkout/discount-section.tsx +218 -218
  38. package/src/checkout/error-copy-codes.ts +63 -63
  39. package/src/checkout/geocode.ts +154 -154
  40. package/src/checkout/gift-card-section.tsx +224 -224
  41. package/src/checkout/index.ts +72 -72
  42. package/src/checkout/labels.ts +495 -495
  43. package/src/checkout/payment-button.tsx +372 -372
  44. package/src/checkout/payment-error-copy.ts +154 -154
  45. package/src/checkout/promotion-error-copy.ts +91 -91
  46. package/src/common/country-flag.tsx +52 -52
  47. package/src/common/country-select.tsx +11 -11
  48. package/src/common/index.ts +20 -20
  49. package/src/common/market-select.tsx +57 -57
  50. package/src/index.ts +12 -12
  51. package/src/lib/country-name.ts +59 -59
  52. package/src/lib/get-product-price.ts +133 -133
  53. package/src/lib/media-image.tsx +39 -39
  54. package/src/lib/payment-constants.ts +53 -53
  55. package/src/lib/platform.ts +13 -13
  56. package/src/lib/price.tsx +39 -39
  57. package/src/lib/store-api-error.ts +36 -36
  58. package/src/lib/variant-caption.ts +32 -32
  59. package/src/locales/bg.ts +3 -2
  60. package/src/locales/context.ts +37 -37
  61. package/src/locales/en.ts +26 -26
  62. package/src/locales/es.ts +3 -2
  63. package/src/locales/index.ts +19 -19
  64. package/src/locales/provider.tsx +59 -59
  65. package/src/locales/types.ts +77 -77
  66. package/src/order/index.ts +62 -62
  67. package/src/order/labels.ts +79 -79
  68. package/src/order/order-totals.tsx +250 -250
  69. package/src/primitives/select-field.tsx +93 -93
  70. package/src/products/image-gallery.tsx +43 -43
  71. package/src/products/option-select.tsx +67 -67
  72. package/src/products/product-info.tsx +5 -5
  73. package/src/products/product-price.tsx +69 -69
  74. package/src/products/product-promises.tsx +61 -61
  75. package/src/products/product-specs.tsx +65 -65
  76. package/src/products/product-tabs.tsx +123 -123
  77. package/src/products/purchase-options.tsx +130 -130
  78. package/src/products/sets.ts +110 -110
  79. package/src/products/variant-matching.ts +71 -71
  80. package/src/products/variant-url.ts +74 -74
  81. package/src/reviews-ui/index.ts +12 -0
  82. package/src/reviews-ui/labels.ts +5 -2
  83. package/src/reviews-ui/lightbox-state.ts +46 -46
  84. package/src/reviews-ui/review-header.tsx +166 -0
  85. package/src/reviews-ui/review-lightbox.tsx +271 -271
  86. package/src/reviews-ui/review-list.tsx +193 -191
  87. package/src/reviews-ui/review-widget.tsx +63 -71
  88. package/src/reviews-ui/star-badge.tsx +6 -4
  89. package/src/reviews-ui/widget-options.ts +55 -0
  90. package/src/store/category-template.tsx +136 -136
  91. package/src/store/index.ts +40 -40
  92. package/src/tracking/chatgpt-pixel.tsx +99 -99
  93. package/src/tracking/consent-init.tsx +62 -62
  94. package/src/tracking/events.ts +348 -348
  95. package/src/tracking/ga4.tsx +93 -93
  96. package/src/tracking/google-ads.ts +84 -84
  97. package/src/tracking/gtm.tsx +60 -60
  98. package/src/tracking/inline-script.ts +49 -49
  99. package/src/tracking/oaiq.ts +206 -206
  100. package/src/tracking/tiktok-pixel.tsx +91 -91
  101. package/src/tracking/track-init.tsx +56 -56
  102. package/src/tracking/track-order-purchase.tsx +122 -122
  103. package/src/tracking/ttq.ts +180 -180
  104. package/src/tracking/use-tracking-config.ts +54 -54
  105. package/theme/index.css +25 -25
@@ -1,495 +1,495 @@
1
- /**
2
- * Checkout labels — default English copy.
3
- *
4
- * Ported from `@1click/ui/src/checkout/labels.ts` (v2.3.1) with the
5
- * Cartbase additions: gift-card tender labels (`giftCard*`) for the new
6
- * GiftCardSection (gift cards are a Cartbase feature with no @1click
7
- * equivalent — docs/storefront/gift-cards.md).
8
- *
9
- * Every user-facing string in the checkout module comes from a
10
- * `CheckoutLabels` object. Stores override strings by passing a partial
11
- * labels object to `CheckoutProvider`. English is the baseline; stores
12
- * wanting Bulgarian, German, etc. pass their own translations.
13
- */
14
-
15
- export type CheckoutLabels = {
16
- // Section headings
17
- deliveryInfo: string
18
- shippingServices: string
19
- paymentMethod: string
20
- orderSummary: string
21
-
22
- // Form fields
23
- email: string
24
- country: string
25
- /**
26
- * Localized name of the single-region country (e.g. "България"). When
27
- * the checkout renders with exactly one country, the country select
28
- * collapses to a readonly field showing this name instead of the
29
- * dropdown's English `display_name`. Optional — falls back to the
30
- * country's `display_name` when omitted.
31
- */
32
- singleCountryName?: string
33
- firstName: string
34
- lastName: string
35
- address: string
36
- apartment: string
37
- city: string
38
- postalCode: string
39
- phone: string
40
- province: string
41
-
42
- // Buttons
43
- saveAddress: string
44
- updateAddress: string
45
- addToOrder: string
46
- addCode: string
47
-
48
- // States / empty / disabled
49
- deliveryDisabled: string
50
- noShippingOptions: string
51
- paymentDisabled: string
52
- /** Shown above the shipping options when they render as a read-only
53
- * price preview before the address is entered (stores that opt into
54
- * `previewWhenAddressNotReady`). */
55
- deliveryPreviewHint: string
56
- /** Hint under the disabled Buy button explaining what's still missing. */
57
- buyNeedsAddress: string
58
- buyNeedsDelivery: string
59
-
60
- // Payment tabs
61
- codNote: string
62
- /** Online-payment radio label. Covers every Stripe-enabled method
63
- * (card + Apple Pay + Google Pay + Link + whatever else is turned on
64
- * in the Stripe Dashboard). Replaces the old `payByCard`. */
65
- payOnline: string
66
- cashOnDelivery: string
67
- termsText: string
68
-
69
- // Address reuse
70
- useSavedAddress: string
71
- saveInfo: string
72
-
73
- // Saved address picker
74
- chooseAddress: string
75
-
76
- // Order summary
77
- items: string
78
- qty: string
79
- subtotal: string
80
- shipping: string
81
- shippingCalc: string
82
- shippingFree: string
83
- tax: string
84
- /**
85
- * What the tax is, in the store's words. OPTIONAL and undefined by
86
- * default: a rate ("20% VAT") or a regime ("included in prices") is the
87
- * store's fact, and the tooltip is simply absent when the store says
88
- * nothing.
89
- */
90
- taxTooltip?: string
91
- total: string
92
- discount: string
93
- discountCode: string
94
- discountSub: string
95
- /** Default label for the cash-on-delivery fee row. Used as the third-
96
- * level fallback when neither the cart's `payment_method_fee_label` (Cartbase
97
- * server truth) nor a per-store override is available. */
98
- paymentMethodFee: string
99
- recommended: string
100
- recommendedSoon: string
101
- recommendedSoonSub: string
102
- remove: string
103
- secureCheckout: string
104
-
105
- // Gift cards (Cartbase gift-card tender — docs/storefront/gift-cards.md)
106
- giftCard: string
107
- giftCardSub: string
108
- giftCardPlaceholder: string
109
- /** Row label for the summary's gift-card tender line ("Gift cards −X"). */
110
- giftCardApplied: string
111
- /** Label for the remainder row ("Remaining to pay"). Rendered only when
112
- * gift cards cover part of the total (`gift_card_total > 0`). */
113
- giftCardRemaining: string
114
- /** Prefix for an applied card chip, followed by the masked last4
115
- * (e.g. "Gift card ••••1234"). */
116
- giftCardEnding: string
117
-
118
- // Company invoice
119
- companyInvoice: string
120
- companyName: string
121
- companyVat: string
122
- companyMol: string
123
- companyAddress: string
124
-
125
- // Place order button
126
- placeOrder: string
127
- selectPaymentMethod: string
128
-
129
- /**
130
- * Processing-state messages shown beneath the spinning Buy button
131
- * while `performBuyClick` is in flight. Cycle through with a fade
132
- * transition every ~1.8s; on long flows (3DS challenges, slow
133
- * networks) the cycle loops back to index 0.
134
- *
135
- * Two arrays so card and COD show different micro-narratives — card
136
- * mentions the bank handshake (the slow part), COD jumps straight
137
- * to delivery + order. The closing entry should be a brand-flavored
138
- * line that's still honest to the "processing" moment (don't say
139
- * "packing" — nothing is being packed yet).
140
- */
141
- processingCard: string[]
142
- processingCod: string[]
143
-
144
- // Econt office selector
145
- econtLoadingOffices: string
146
- econtNearestOffices: string
147
- econtSearchAnother: string
148
- econtSearchPlaceholder: string
149
- econtNoResults: string
150
- econtChange: string
151
-
152
- // BoxNow locker selector
153
- boxnowLoadingLockers: string
154
- boxnowNearestLockers: string
155
- boxnowSearchAnother: string
156
- boxnowSearchPlaceholder: string
157
- boxnowNoResults: string
158
- boxnowChange: string
159
- boxnowUnavailable: string
160
- boxnowNoLockersInCity: string
161
- /**
162
- * What a shopper reads when saving the address fails. It used to be
163
- * hardcoded BULGARIAN inside `address-error-copy.ts` with no way to
164
- * override it, so a merchant anywhere on earth showed Bulgarian on the
165
- * one screen where a shopper is about to pay (found 2026-09-13). It is
166
- * copy, so it belongs in the label pack like all other copy.
167
- *
168
- * The PATTERNS that recognize each failure stay in code: they match the
169
- * API's own English error text, which is not translated and must not be.
170
- */
171
- addressErrors: AddressErrorCopy
172
- /**
173
- * The rest of the checkout's failure copy, lifted out of
174
- * `payment-error-copy.ts` and `promotion-error-copy.ts` on 2026-09-13
175
- * for the same reason as `addressErrors`: they held 54 hardcoded
176
- * Bulgarian sentences that every merchant shipped and none could
177
- * override. The recognition stays in those modules; only the words
178
- * live here.
179
- */
180
- paymentErrors: PaymentErrorCopy
181
- giftCardErrors: GiftCardErrorCopy
182
- promotionErrors: PromotionErrorCopy
183
- }
184
-
185
- /** The eleven things that can go wrong saving a checkout address. */
186
- export type AddressErrorCopy = {
187
- /** Anything unrecognized. Never leaks the technical message. */
188
- generic: string
189
- network: string
190
- validationFailed: string
191
- invalidRegion: string
192
- regionRequired: string
193
- cartCompleted: string
194
- cartNotFound: string
195
- invalidEmail: string
196
- invalidPostalCode: string
197
- invalidPhone: string
198
- invalidCountry: string
199
- }
200
-
201
- export type PaymentErrorCopy = {
202
- genericCard: string
203
- genericCod: string
204
- cartEmailRequired: string
205
- cartEmpty: string
206
- shippingAddressRequired: string
207
- shippingMethodRequired: string
208
- paymentCollectionRequired: string
209
- paymentSessionRequired: string
210
- insufficientInventory: string
211
- checkoutMethodHidden: string
212
- requiresAction: string
213
- paymentNotAuthorized: string
214
- paymentNotInitiated: string
215
- paymentIncomplete: string
216
- giftCardInsufficientBalance: string
217
- giftCardNotRedeemable: string
218
- accountRequired: string
219
- cartLocked: string
220
- cartCompleted: string
221
- cartNotFound: string
222
- shippingOptionNotFound: string
223
- invalidProvider: string
224
- shippingPriceMissing: string
225
- stripeNotConfigured: string
226
- validationFailed: string
227
- cardDeclined: string
228
- insufficientFunds: string
229
- expiredCard: string
230
- incorrectCvc: string
231
- incorrectCardNumber: string
232
- invalidExpiry: string
233
- authenticationRequired: string
234
- processingError: string
235
- rateLimited: string
236
- paymentExpired: string
237
- amountMismatch: string
238
- networkError: string
239
- cartAlreadyCompleted: string
240
- outOfStock: string
241
- invalidRegion: string
242
- shippingMethodMissing: string
243
- }
244
-
245
- export type GiftCardErrorCopy = {
246
- invalidGiftCard: string
247
- rateLimited: string
248
- cartCompleted: string
249
- cartNotFound: string
250
- validationFailed: string
251
- generic: string
252
- }
253
-
254
- export type PromotionErrorCopy = {
255
- generic: string
256
- needsEmail: string
257
- exhausted: string
258
- promotionNotFound: string
259
- promotionInactive: string
260
- promotionMisconfigured: string
261
- promotionUnsupportedType: string
262
- cartCompleted: string
263
- cartNotFound: string
264
- validationFailed: string
265
- }
266
-
267
- export const defaultCheckoutLabels: CheckoutLabels = {
268
- deliveryInfo: "Delivery information",
269
- shippingServices: "Shipping method",
270
- paymentMethod: "Payment method",
271
- orderSummary: "Order summary",
272
-
273
- email: "Email",
274
- country: "Country",
275
- singleCountryName: undefined,
276
- firstName: "First name",
277
- lastName: "Last name",
278
- address: "Address",
279
- apartment: "Apartment, suite, etc. (optional)",
280
- city: "City",
281
- postalCode: "Postal code",
282
- phone: "Phone",
283
- province: "State / province",
284
-
285
- saveAddress: "Save and continue",
286
- updateAddress: "Update address",
287
- addToOrder: "+ Add to order",
288
- addCode: "Apply",
289
-
290
- deliveryDisabled:
291
- "Enter your delivery address to see available shipping options.",
292
- noShippingOptions: "No shipping options available for your address.",
293
- paymentDisabled: "Select a shipping method to continue.",
294
- deliveryPreviewHint: "Enter your address to choose a delivery method.",
295
- buyNeedsAddress: "Enter your delivery address",
296
- buyNeedsDelivery: "Choose a delivery method",
297
-
298
- codNote: "Cash on delivery. Additional fees may apply.",
299
- payOnline: "Pay online",
300
- cashOnDelivery: "Cash on delivery",
301
- termsText:
302
- "By placing your order you accept our Terms and Privacy Policy.",
303
-
304
- useSavedAddress: "Use a saved address?",
305
- saveInfo: "Save this information for next time",
306
-
307
- chooseAddress: "Choose an address",
308
-
309
- items: "items",
310
- qty: "Qty",
311
- subtotal: "Subtotal",
312
- shipping: "Shipping",
313
- shippingCalc: "Calculated at checkout",
314
- shippingFree: "Free",
315
- // The row NAME, neutral on purpose: it sits beside a number, so it must
316
- // say something, but "VAT incl." names one tax regime and asserts that
317
- // prices include it. That is Bulgaria's answer, not Germany's, and not any
318
- // US state's. A store that wants its own word sets it in its pack.
319
- tax: "Tax",
320
- // NO DEFAULT. The explanation used to read "20% VAT is included in
321
- // prices", a rate and a regime the library cannot know, translated into
322
- // three languages. A store writes it or the tooltip does not appear at
323
- // all, which costs a shopper nothing.
324
- taxTooltip: undefined,
325
- total: "Total",
326
- discount: "Discount",
327
- discountCode: "Discount code",
328
- discountSub: "Save with a promo code",
329
- paymentMethodFee: "Payment method fee",
330
- recommended: "Recommended for you",
331
- recommendedSoon: "Coming soon",
332
- recommendedSoonSub: "Personalized recommendations",
333
- remove: "Remove",
334
- secureCheckout: "Secure checkout",
335
-
336
- giftCard: "Gift card",
337
- giftCardSub: "Redeem a gift card",
338
- giftCardPlaceholder: "GIFT-XXXX-XXXX",
339
- giftCardApplied: "Gift cards",
340
- giftCardRemaining: "Remaining to pay",
341
- giftCardEnding: "Gift card ending in",
342
-
343
- companyInvoice: "Company invoice",
344
- companyName: "Company name",
345
- companyVat: "VAT / Company number",
346
- companyMol: "Responsible person",
347
- companyAddress: "Company address",
348
-
349
- placeOrder: "Place order",
350
- selectPaymentMethod: "Select a payment method",
351
-
352
- processingCard: [
353
- "Connecting to the bank...",
354
- "Confirming the payment...",
355
- "Recording the order...",
356
- "Attending to every detail...",
357
- ],
358
- processingCod: [
359
- "Reserving the delivery...",
360
- "Preparing the order...",
361
- "Attending to every detail...",
362
- ],
363
-
364
- econtLoadingOffices: "Loading offices...",
365
- econtNearestOffices: "Nearest offices",
366
- econtSearchAnother: "Search for another office",
367
- econtSearchPlaceholder: "Search by name, city, or address...",
368
- econtNoResults: "No offices found for",
369
- econtChange: "Change",
370
-
371
- boxnowLoadingLockers: "Loading lockers...",
372
- boxnowNearestLockers: "Nearest lockers",
373
- boxnowSearchAnother: "Search for another locker",
374
- boxnowSearchPlaceholder: "Search by name, address, or postal code...",
375
- boxnowNoResults: "No lockers found for",
376
- boxnowChange: "Change",
377
- boxnowUnavailable:
378
- "BoxNow is temporarily unavailable, please choose a different shipping method.",
379
- boxnowNoLockersInCity:
380
- "No BoxNow lockers found in your city. Please choose a different shipping method.",
381
- addressErrors: {
382
- generic:
383
- "We could not save your address. Please check the details and try again.",
384
- network:
385
- "We cannot reach the server. Please check your connection and try again.",
386
- validationFailed:
387
- "One of the fields is not valid. Please check the details and try again.",
388
- invalidRegion: "That country is not valid. Please reload the page.",
389
- regionRequired: "The delivery region is not valid. Please reload the page.",
390
- cartCompleted:
391
- "This order is already placed. Please check your email for the confirmation.",
392
- cartNotFound: "Your cart session expired. Please reload the page.",
393
- invalidEmail: "That email address is not valid. Please check the spelling.",
394
- // NOT "four digits": the postcode length is the country's business, and
395
- // the Bulgarian original said four, which is wrong everywhere else.
396
- invalidPostalCode: "That postal code is not valid. Please check it.",
397
- invalidPhone: "That phone number is not valid. Please check the digits.",
398
- invalidCountry: "That country is not valid. Please reload the page.",
399
- },
400
- paymentErrors: {
401
- genericCard:
402
- "The payment could not be processed right now. Try again or choose another payment method.",
403
- genericCod: "The order could not be placed right now. Try again in a moment.",
404
- cartEmailRequired:
405
- "An email address is needed to place the order. Add one to continue.",
406
- cartEmpty: "Your cart is empty. Add a product to continue.",
407
- shippingAddressRequired:
408
- "A delivery address is needed to place the order. Add one to continue.",
409
- shippingMethodRequired: "Choose a delivery method to continue.",
410
- paymentCollectionRequired: "The payment is not ready yet. Try again.",
411
- paymentSessionRequired: "The payment is not ready yet. Try again.",
412
- insufficientInventory:
413
- "One of the products is no longer available in the quantity you chose. Update your cart to continue.",
414
- checkoutMethodHidden:
415
- "The method you chose is no longer available for this order. Choose another one and try again.",
416
- requiresAction:
417
- "Your bank needs to confirm this payment. Follow the steps it shows to continue.",
418
- paymentNotAuthorized:
419
- "The payment was not approved. Try again or choose another payment method.",
420
- paymentNotInitiated:
421
- "The payment expired before it went through. Reload the page and try again.",
422
- paymentIncomplete:
423
- "Your gift cards no longer cover the full amount. Choose a payment method for the rest.",
424
- giftCardInsufficientBalance:
425
- "The balance on that gift card no longer covers the amount. Remove it or choose another payment method.",
426
- giftCardNotRedeemable:
427
- "That gift card can no longer be used. Remove it and try again.",
428
- accountRequired: "This order needs you to be signed in. Sign in to continue.",
429
- cartLocked:
430
- "This order is being processed right now. Wait a few seconds before trying again.",
431
- cartCompleted:
432
- "This order has already gone through. Check your email for the confirmation.",
433
- cartNotFound: "Your cart is no longer available. Reload the page to start again.",
434
- shippingOptionNotFound:
435
- "The delivery method you chose is no longer available. Choose another one.",
436
- invalidProvider: "The payment method you chose cannot be used. Choose another one.",
437
- shippingPriceMissing:
438
- "The delivery cost could not be worked out. Choose another delivery method.",
439
- stripeNotConfigured:
440
- "Card payments are unavailable at the moment. Choose another payment method.",
441
- validationFailed: "Some order details are missing. Check the form and try again.",
442
- cardDeclined:
443
- "That card was declined by the bank. Try another card or contact your bank.",
444
- insufficientFunds: "That card does not have enough funds. Try another card.",
445
- expiredCard: "That card has expired. Try another card.",
446
- incorrectCvc:
447
- "The security code does not match. Check the security code on your card and try again.",
448
- incorrectCardNumber:
449
- "That card number does not look right. Check the digits and try again.",
450
- invalidExpiry: "The expiry date does not look right. Check the month and the year.",
451
- authenticationRequired:
452
- "Your bank needs to confirm this payment. Follow the steps it shows to continue.",
453
- processingError: "The card could not be processed. Try again in a moment.",
454
- rateLimited:
455
- "There have been too many payment attempts. Wait a few minutes and try again.",
456
- paymentExpired:
457
- "The payment expired before it went through. Reload the page and try again.",
458
- amountMismatch: "The order total changed. Reload the page and try again.",
459
- networkError:
460
- "The connection was lost. Check your internet connection and try again.",
461
- cartAlreadyCompleted:
462
- "This order has already gone through. Check your email for the confirmation.",
463
- outOfStock:
464
- "One of the products is no longer available in the quantity you chose. Update your cart to continue.",
465
- invalidRegion:
466
- "This delivery destination is not available. Reload the page and try again.",
467
- shippingMethodMissing: "Choose a delivery method to continue.",
468
- },
469
- giftCardErrors: {
470
- invalidGiftCard: "That gift card code is not valid. Check the code and try again.",
471
- rateLimited: "Too many attempts. Wait a few minutes and try again.",
472
- cartCompleted:
473
- "This order is already placed. Check your email for the confirmation.",
474
- cartNotFound: "Your cart timed out. Reload the page and try again.",
475
- validationFailed: "That code is not valid. Check it and try again.",
476
- generic: "The gift card could not be applied right now. Try again in a moment.",
477
- },
478
- promotionErrors: {
479
- generic:
480
- "This discount code can't be applied right now. Check the code and try again.",
481
- needsEmail:
482
- "This discount code needs an email address. Add yours above and try again.",
483
- exhausted: "This promotion has been fully used and is no longer available.",
484
- promotionNotFound: "This discount code isn't valid. Check it and try again.",
485
- promotionInactive: "This discount code is no longer active.",
486
- promotionMisconfigured:
487
- "This discount code can't be applied right now. Check the code and try again.",
488
- promotionUnsupportedType:
489
- "This discount code can't be applied right now. Check the code and try again.",
490
- cartCompleted:
491
- "This order has already been placed. Check your email for the confirmation.",
492
- cartNotFound: "We couldn't find your cart. Reload the page and try again.",
493
- validationFailed: "This discount code isn't valid. Check it and try again.",
494
- },
495
- }
1
+ /**
2
+ * Checkout labels — default English copy.
3
+ *
4
+ * Ported from `@1click/ui/src/checkout/labels.ts` (v2.3.1) with the
5
+ * Cartbase additions: gift-card tender labels (`giftCard*`) for the new
6
+ * GiftCardSection (gift cards are a Cartbase feature with no @1click
7
+ * equivalent — docs/storefront/gift-cards.md).
8
+ *
9
+ * Every user-facing string in the checkout module comes from a
10
+ * `CheckoutLabels` object. Stores override strings by passing a partial
11
+ * labels object to `CheckoutProvider`. English is the baseline; stores
12
+ * wanting Bulgarian, German, etc. pass their own translations.
13
+ */
14
+
15
+ export type CheckoutLabels = {
16
+ // Section headings
17
+ deliveryInfo: string
18
+ shippingServices: string
19
+ paymentMethod: string
20
+ orderSummary: string
21
+
22
+ // Form fields
23
+ email: string
24
+ country: string
25
+ /**
26
+ * Localized name of the single-region country (e.g. "България"). When
27
+ * the checkout renders with exactly one country, the country select
28
+ * collapses to a readonly field showing this name instead of the
29
+ * dropdown's English `display_name`. Optional — falls back to the
30
+ * country's `display_name` when omitted.
31
+ */
32
+ singleCountryName?: string
33
+ firstName: string
34
+ lastName: string
35
+ address: string
36
+ apartment: string
37
+ city: string
38
+ postalCode: string
39
+ phone: string
40
+ province: string
41
+
42
+ // Buttons
43
+ saveAddress: string
44
+ updateAddress: string
45
+ addToOrder: string
46
+ addCode: string
47
+
48
+ // States / empty / disabled
49
+ deliveryDisabled: string
50
+ noShippingOptions: string
51
+ paymentDisabled: string
52
+ /** Shown above the shipping options when they render as a read-only
53
+ * price preview before the address is entered (stores that opt into
54
+ * `previewWhenAddressNotReady`). */
55
+ deliveryPreviewHint: string
56
+ /** Hint under the disabled Buy button explaining what's still missing. */
57
+ buyNeedsAddress: string
58
+ buyNeedsDelivery: string
59
+
60
+ // Payment tabs
61
+ codNote: string
62
+ /** Online-payment radio label. Covers every Stripe-enabled method
63
+ * (card + Apple Pay + Google Pay + Link + whatever else is turned on
64
+ * in the Stripe Dashboard). Replaces the old `payByCard`. */
65
+ payOnline: string
66
+ cashOnDelivery: string
67
+ termsText: string
68
+
69
+ // Address reuse
70
+ useSavedAddress: string
71
+ saveInfo: string
72
+
73
+ // Saved address picker
74
+ chooseAddress: string
75
+
76
+ // Order summary
77
+ items: string
78
+ qty: string
79
+ subtotal: string
80
+ shipping: string
81
+ shippingCalc: string
82
+ shippingFree: string
83
+ tax: string
84
+ /**
85
+ * What the tax is, in the store's words. OPTIONAL and undefined by
86
+ * default: a rate ("20% VAT") or a regime ("included in prices") is the
87
+ * store's fact, and the tooltip is simply absent when the store says
88
+ * nothing.
89
+ */
90
+ taxTooltip?: string
91
+ total: string
92
+ discount: string
93
+ discountCode: string
94
+ discountSub: string
95
+ /** Default label for the cash-on-delivery fee row. Used as the third-
96
+ * level fallback when neither the cart's `payment_method_fee_label` (Cartbase
97
+ * server truth) nor a per-store override is available. */
98
+ paymentMethodFee: string
99
+ recommended: string
100
+ recommendedSoon: string
101
+ recommendedSoonSub: string
102
+ remove: string
103
+ secureCheckout: string
104
+
105
+ // Gift cards (Cartbase gift-card tender — docs/storefront/gift-cards.md)
106
+ giftCard: string
107
+ giftCardSub: string
108
+ giftCardPlaceholder: string
109
+ /** Row label for the summary's gift-card tender line ("Gift cards −X"). */
110
+ giftCardApplied: string
111
+ /** Label for the remainder row ("Remaining to pay"). Rendered only when
112
+ * gift cards cover part of the total (`gift_card_total > 0`). */
113
+ giftCardRemaining: string
114
+ /** Prefix for an applied card chip, followed by the masked last4
115
+ * (e.g. "Gift card ••••1234"). */
116
+ giftCardEnding: string
117
+
118
+ // Company invoice
119
+ companyInvoice: string
120
+ companyName: string
121
+ companyVat: string
122
+ companyMol: string
123
+ companyAddress: string
124
+
125
+ // Place order button
126
+ placeOrder: string
127
+ selectPaymentMethod: string
128
+
129
+ /**
130
+ * Processing-state messages shown beneath the spinning Buy button
131
+ * while `performBuyClick` is in flight. Cycle through with a fade
132
+ * transition every ~1.8s; on long flows (3DS challenges, slow
133
+ * networks) the cycle loops back to index 0.
134
+ *
135
+ * Two arrays so card and COD show different micro-narratives — card
136
+ * mentions the bank handshake (the slow part), COD jumps straight
137
+ * to delivery + order. The closing entry should be a brand-flavored
138
+ * line that's still honest to the "processing" moment (don't say
139
+ * "packing" — nothing is being packed yet).
140
+ */
141
+ processingCard: string[]
142
+ processingCod: string[]
143
+
144
+ // Econt office selector
145
+ econtLoadingOffices: string
146
+ econtNearestOffices: string
147
+ econtSearchAnother: string
148
+ econtSearchPlaceholder: string
149
+ econtNoResults: string
150
+ econtChange: string
151
+
152
+ // BoxNow locker selector
153
+ boxnowLoadingLockers: string
154
+ boxnowNearestLockers: string
155
+ boxnowSearchAnother: string
156
+ boxnowSearchPlaceholder: string
157
+ boxnowNoResults: string
158
+ boxnowChange: string
159
+ boxnowUnavailable: string
160
+ boxnowNoLockersInCity: string
161
+ /**
162
+ * What a shopper reads when saving the address fails. It used to be
163
+ * hardcoded BULGARIAN inside `address-error-copy.ts` with no way to
164
+ * override it, so a merchant anywhere on earth showed Bulgarian on the
165
+ * one screen where a shopper is about to pay (found 2026-09-13). It is
166
+ * copy, so it belongs in the label pack like all other copy.
167
+ *
168
+ * The PATTERNS that recognize each failure stay in code: they match the
169
+ * API's own English error text, which is not translated and must not be.
170
+ */
171
+ addressErrors: AddressErrorCopy
172
+ /**
173
+ * The rest of the checkout's failure copy, lifted out of
174
+ * `payment-error-copy.ts` and `promotion-error-copy.ts` on 2026-09-13
175
+ * for the same reason as `addressErrors`: they held 54 hardcoded
176
+ * Bulgarian sentences that every merchant shipped and none could
177
+ * override. The recognition stays in those modules; only the words
178
+ * live here.
179
+ */
180
+ paymentErrors: PaymentErrorCopy
181
+ giftCardErrors: GiftCardErrorCopy
182
+ promotionErrors: PromotionErrorCopy
183
+ }
184
+
185
+ /** The eleven things that can go wrong saving a checkout address. */
186
+ export type AddressErrorCopy = {
187
+ /** Anything unrecognized. Never leaks the technical message. */
188
+ generic: string
189
+ network: string
190
+ validationFailed: string
191
+ invalidRegion: string
192
+ regionRequired: string
193
+ cartCompleted: string
194
+ cartNotFound: string
195
+ invalidEmail: string
196
+ invalidPostalCode: string
197
+ invalidPhone: string
198
+ invalidCountry: string
199
+ }
200
+
201
+ export type PaymentErrorCopy = {
202
+ genericCard: string
203
+ genericCod: string
204
+ cartEmailRequired: string
205
+ cartEmpty: string
206
+ shippingAddressRequired: string
207
+ shippingMethodRequired: string
208
+ paymentCollectionRequired: string
209
+ paymentSessionRequired: string
210
+ insufficientInventory: string
211
+ checkoutMethodHidden: string
212
+ requiresAction: string
213
+ paymentNotAuthorized: string
214
+ paymentNotInitiated: string
215
+ paymentIncomplete: string
216
+ giftCardInsufficientBalance: string
217
+ giftCardNotRedeemable: string
218
+ accountRequired: string
219
+ cartLocked: string
220
+ cartCompleted: string
221
+ cartNotFound: string
222
+ shippingOptionNotFound: string
223
+ invalidProvider: string
224
+ shippingPriceMissing: string
225
+ stripeNotConfigured: string
226
+ validationFailed: string
227
+ cardDeclined: string
228
+ insufficientFunds: string
229
+ expiredCard: string
230
+ incorrectCvc: string
231
+ incorrectCardNumber: string
232
+ invalidExpiry: string
233
+ authenticationRequired: string
234
+ processingError: string
235
+ rateLimited: string
236
+ paymentExpired: string
237
+ amountMismatch: string
238
+ networkError: string
239
+ cartAlreadyCompleted: string
240
+ outOfStock: string
241
+ invalidRegion: string
242
+ shippingMethodMissing: string
243
+ }
244
+
245
+ export type GiftCardErrorCopy = {
246
+ invalidGiftCard: string
247
+ rateLimited: string
248
+ cartCompleted: string
249
+ cartNotFound: string
250
+ validationFailed: string
251
+ generic: string
252
+ }
253
+
254
+ export type PromotionErrorCopy = {
255
+ generic: string
256
+ needsEmail: string
257
+ exhausted: string
258
+ promotionNotFound: string
259
+ promotionInactive: string
260
+ promotionMisconfigured: string
261
+ promotionUnsupportedType: string
262
+ cartCompleted: string
263
+ cartNotFound: string
264
+ validationFailed: string
265
+ }
266
+
267
+ export const defaultCheckoutLabels: CheckoutLabels = {
268
+ deliveryInfo: "Delivery information",
269
+ shippingServices: "Shipping method",
270
+ paymentMethod: "Payment method",
271
+ orderSummary: "Order summary",
272
+
273
+ email: "Email",
274
+ country: "Country",
275
+ singleCountryName: undefined,
276
+ firstName: "First name",
277
+ lastName: "Last name",
278
+ address: "Address",
279
+ apartment: "Apartment, suite, etc. (optional)",
280
+ city: "City",
281
+ postalCode: "Postal code",
282
+ phone: "Phone",
283
+ province: "State / province",
284
+
285
+ saveAddress: "Save and continue",
286
+ updateAddress: "Update address",
287
+ addToOrder: "+ Add to order",
288
+ addCode: "Apply",
289
+
290
+ deliveryDisabled:
291
+ "Enter your delivery address to see available shipping options.",
292
+ noShippingOptions: "No shipping options available for your address.",
293
+ paymentDisabled: "Select a shipping method to continue.",
294
+ deliveryPreviewHint: "Enter your address to choose a delivery method.",
295
+ buyNeedsAddress: "Enter your delivery address",
296
+ buyNeedsDelivery: "Choose a delivery method",
297
+
298
+ codNote: "Cash on delivery. Additional fees may apply.",
299
+ payOnline: "Pay online",
300
+ cashOnDelivery: "Cash on delivery",
301
+ termsText:
302
+ "By placing your order you accept our Terms and Privacy Policy.",
303
+
304
+ useSavedAddress: "Use a saved address?",
305
+ saveInfo: "Save this information for next time",
306
+
307
+ chooseAddress: "Choose an address",
308
+
309
+ items: "items",
310
+ qty: "Qty",
311
+ subtotal: "Subtotal",
312
+ shipping: "Shipping",
313
+ shippingCalc: "Calculated at checkout",
314
+ shippingFree: "Free",
315
+ // The row NAME, neutral on purpose: it sits beside a number, so it must
316
+ // say something, but "VAT incl." names one tax regime and asserts that
317
+ // prices include it. That is Bulgaria's answer, not Germany's, and not any
318
+ // US state's. A store that wants its own word sets it in its pack.
319
+ tax: "Tax",
320
+ // NO DEFAULT. The explanation used to read "20% VAT is included in
321
+ // prices", a rate and a regime the library cannot know, translated into
322
+ // three languages. A store writes it or the tooltip does not appear at
323
+ // all, which costs a shopper nothing.
324
+ taxTooltip: undefined,
325
+ total: "Total",
326
+ discount: "Discount",
327
+ discountCode: "Discount code",
328
+ discountSub: "Save with a promo code",
329
+ paymentMethodFee: "Payment method fee",
330
+ recommended: "Recommended for you",
331
+ recommendedSoon: "Coming soon",
332
+ recommendedSoonSub: "Personalized recommendations",
333
+ remove: "Remove",
334
+ secureCheckout: "Secure checkout",
335
+
336
+ giftCard: "Gift card",
337
+ giftCardSub: "Redeem a gift card",
338
+ giftCardPlaceholder: "GIFT-XXXX-XXXX",
339
+ giftCardApplied: "Gift cards",
340
+ giftCardRemaining: "Remaining to pay",
341
+ giftCardEnding: "Gift card ending in",
342
+
343
+ companyInvoice: "Company invoice",
344
+ companyName: "Company name",
345
+ companyVat: "VAT / Company number",
346
+ companyMol: "Responsible person",
347
+ companyAddress: "Company address",
348
+
349
+ placeOrder: "Place order",
350
+ selectPaymentMethod: "Select a payment method",
351
+
352
+ processingCard: [
353
+ "Connecting to the bank...",
354
+ "Confirming the payment...",
355
+ "Recording the order...",
356
+ "Attending to every detail...",
357
+ ],
358
+ processingCod: [
359
+ "Reserving the delivery...",
360
+ "Preparing the order...",
361
+ "Attending to every detail...",
362
+ ],
363
+
364
+ econtLoadingOffices: "Loading offices...",
365
+ econtNearestOffices: "Nearest offices",
366
+ econtSearchAnother: "Search for another office",
367
+ econtSearchPlaceholder: "Search by name, city, or address...",
368
+ econtNoResults: "No offices found for",
369
+ econtChange: "Change",
370
+
371
+ boxnowLoadingLockers: "Loading lockers...",
372
+ boxnowNearestLockers: "Nearest lockers",
373
+ boxnowSearchAnother: "Search for another locker",
374
+ boxnowSearchPlaceholder: "Search by name, address, or postal code...",
375
+ boxnowNoResults: "No lockers found for",
376
+ boxnowChange: "Change",
377
+ boxnowUnavailable:
378
+ "BoxNow is temporarily unavailable, please choose a different shipping method.",
379
+ boxnowNoLockersInCity:
380
+ "No BoxNow lockers found in your city. Please choose a different shipping method.",
381
+ addressErrors: {
382
+ generic:
383
+ "We could not save your address. Please check the details and try again.",
384
+ network:
385
+ "We cannot reach the server. Please check your connection and try again.",
386
+ validationFailed:
387
+ "One of the fields is not valid. Please check the details and try again.",
388
+ invalidRegion: "That country is not valid. Please reload the page.",
389
+ regionRequired: "The delivery region is not valid. Please reload the page.",
390
+ cartCompleted:
391
+ "This order is already placed. Please check your email for the confirmation.",
392
+ cartNotFound: "Your cart session expired. Please reload the page.",
393
+ invalidEmail: "That email address is not valid. Please check the spelling.",
394
+ // NOT "four digits": the postcode length is the country's business, and
395
+ // the Bulgarian original said four, which is wrong everywhere else.
396
+ invalidPostalCode: "That postal code is not valid. Please check it.",
397
+ invalidPhone: "That phone number is not valid. Please check the digits.",
398
+ invalidCountry: "That country is not valid. Please reload the page.",
399
+ },
400
+ paymentErrors: {
401
+ genericCard:
402
+ "The payment could not be processed right now. Try again or choose another payment method.",
403
+ genericCod: "The order could not be placed right now. Try again in a moment.",
404
+ cartEmailRequired:
405
+ "An email address is needed to place the order. Add one to continue.",
406
+ cartEmpty: "Your cart is empty. Add a product to continue.",
407
+ shippingAddressRequired:
408
+ "A delivery address is needed to place the order. Add one to continue.",
409
+ shippingMethodRequired: "Choose a delivery method to continue.",
410
+ paymentCollectionRequired: "The payment is not ready yet. Try again.",
411
+ paymentSessionRequired: "The payment is not ready yet. Try again.",
412
+ insufficientInventory:
413
+ "One of the products is no longer available in the quantity you chose. Update your cart to continue.",
414
+ checkoutMethodHidden:
415
+ "The method you chose is no longer available for this order. Choose another one and try again.",
416
+ requiresAction:
417
+ "Your bank needs to confirm this payment. Follow the steps it shows to continue.",
418
+ paymentNotAuthorized:
419
+ "The payment was not approved. Try again or choose another payment method.",
420
+ paymentNotInitiated:
421
+ "The payment expired before it went through. Reload the page and try again.",
422
+ paymentIncomplete:
423
+ "Your gift cards no longer cover the full amount. Choose a payment method for the rest.",
424
+ giftCardInsufficientBalance:
425
+ "The balance on that gift card no longer covers the amount. Remove it or choose another payment method.",
426
+ giftCardNotRedeemable:
427
+ "That gift card can no longer be used. Remove it and try again.",
428
+ accountRequired: "This order needs you to be signed in. Sign in to continue.",
429
+ cartLocked:
430
+ "This order is being processed right now. Wait a few seconds before trying again.",
431
+ cartCompleted:
432
+ "This order has already gone through. Check your email for the confirmation.",
433
+ cartNotFound: "Your cart is no longer available. Reload the page to start again.",
434
+ shippingOptionNotFound:
435
+ "The delivery method you chose is no longer available. Choose another one.",
436
+ invalidProvider: "The payment method you chose cannot be used. Choose another one.",
437
+ shippingPriceMissing:
438
+ "The delivery cost could not be worked out. Choose another delivery method.",
439
+ stripeNotConfigured:
440
+ "Card payments are unavailable at the moment. Choose another payment method.",
441
+ validationFailed: "Some order details are missing. Check the form and try again.",
442
+ cardDeclined:
443
+ "That card was declined by the bank. Try another card or contact your bank.",
444
+ insufficientFunds: "That card does not have enough funds. Try another card.",
445
+ expiredCard: "That card has expired. Try another card.",
446
+ incorrectCvc:
447
+ "The security code does not match. Check the security code on your card and try again.",
448
+ incorrectCardNumber:
449
+ "That card number does not look right. Check the digits and try again.",
450
+ invalidExpiry: "The expiry date does not look right. Check the month and the year.",
451
+ authenticationRequired:
452
+ "Your bank needs to confirm this payment. Follow the steps it shows to continue.",
453
+ processingError: "The card could not be processed. Try again in a moment.",
454
+ rateLimited:
455
+ "There have been too many payment attempts. Wait a few minutes and try again.",
456
+ paymentExpired:
457
+ "The payment expired before it went through. Reload the page and try again.",
458
+ amountMismatch: "The order total changed. Reload the page and try again.",
459
+ networkError:
460
+ "The connection was lost. Check your internet connection and try again.",
461
+ cartAlreadyCompleted:
462
+ "This order has already gone through. Check your email for the confirmation.",
463
+ outOfStock:
464
+ "One of the products is no longer available in the quantity you chose. Update your cart to continue.",
465
+ invalidRegion:
466
+ "This delivery destination is not available. Reload the page and try again.",
467
+ shippingMethodMissing: "Choose a delivery method to continue.",
468
+ },
469
+ giftCardErrors: {
470
+ invalidGiftCard: "That gift card code is not valid. Check the code and try again.",
471
+ rateLimited: "Too many attempts. Wait a few minutes and try again.",
472
+ cartCompleted:
473
+ "This order is already placed. Check your email for the confirmation.",
474
+ cartNotFound: "Your cart timed out. Reload the page and try again.",
475
+ validationFailed: "That code is not valid. Check it and try again.",
476
+ generic: "The gift card could not be applied right now. Try again in a moment.",
477
+ },
478
+ promotionErrors: {
479
+ generic:
480
+ "This discount code can't be applied right now. Check the code and try again.",
481
+ needsEmail:
482
+ "This discount code needs an email address. Add yours above and try again.",
483
+ exhausted: "This promotion has been fully used and is no longer available.",
484
+ promotionNotFound: "This discount code isn't valid. Check it and try again.",
485
+ promotionInactive: "This discount code is no longer active.",
486
+ promotionMisconfigured:
487
+ "This discount code can't be applied right now. Check the code and try again.",
488
+ promotionUnsupportedType:
489
+ "This discount code can't be applied right now. Check the code and try again.",
490
+ cartCompleted:
491
+ "This order has already been placed. Check your email for the confirmation.",
492
+ cartNotFound: "We couldn't find your cart. Reload the page and try again.",
493
+ validationFailed: "This discount code isn't valid. Check it and try again.",
494
+ },
495
+ }