@cartbase/storefront 0.1.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.
Files changed (176) hide show
  1. package/package.json +226 -0
  2. package/src/api/auth.ts +108 -0
  3. package/src/api/carts.ts +506 -0
  4. package/src/api/categories.ts +184 -0
  5. package/src/api/checkout.ts +440 -0
  6. package/src/api/collections.ts +130 -0
  7. package/src/api/consent.ts +75 -0
  8. package/src/api/content.ts +125 -0
  9. package/src/api/customers.ts +307 -0
  10. package/src/api/gift-cards.ts +112 -0
  11. package/src/api/http.ts +122 -0
  12. package/src/api/index.ts +29 -0
  13. package/src/api/integrations.ts +130 -0
  14. package/src/api/menus.ts +77 -0
  15. package/src/api/metaobjects.ts +136 -0
  16. package/src/api/orders.ts +290 -0
  17. package/src/api/products.ts +303 -0
  18. package/src/api/redirects.ts +37 -0
  19. package/src/api/regions.ts +141 -0
  20. package/src/api/reviews.ts +259 -0
  21. package/src/api/search.ts +133 -0
  22. package/src/api/types.ts +91 -0
  23. package/src/cart-drawer/cart-drawer.tsx +86 -0
  24. package/src/cart-drawer/context.tsx +569 -0
  25. package/src/cart-drawer/continue-shopping.tsx +28 -0
  26. package/src/cart-drawer/cross-sell-carousel.tsx +212 -0
  27. package/src/cart-drawer/cross-sell-sidebar.tsx +158 -0
  28. package/src/cart-drawer/empty.tsx +52 -0
  29. package/src/cart-drawer/free-gift.tsx +71 -0
  30. package/src/cart-drawer/gift-wrap.tsx +83 -0
  31. package/src/cart-drawer/header.tsx +52 -0
  32. package/src/cart-drawer/index.ts +69 -0
  33. package/src/cart-drawer/item/index.tsx +164 -0
  34. package/src/cart-drawer/item/quantity.tsx +100 -0
  35. package/src/cart-drawer/item/upsell.tsx +110 -0
  36. package/src/cart-drawer/item/variant.tsx +46 -0
  37. package/src/cart-drawer/labels-bg.ts +72 -0
  38. package/src/cart-drawer/labels.ts +119 -0
  39. package/src/cart-drawer/notes.tsx +131 -0
  40. package/src/cart-drawer/payment-badges.tsx +96 -0
  41. package/src/cart-drawer/promo-banner.tsx +43 -0
  42. package/src/cart-drawer/rewards-points.tsx +78 -0
  43. package/src/cart-drawer/sticky-footer.tsx +73 -0
  44. package/src/cart-drawer/summary-breakdown.tsx +196 -0
  45. package/src/cart-drawer/template.tsx +225 -0
  46. package/src/cart-drawer/tiered-progress.tsx +168 -0
  47. package/src/checkout/address-error-copy.ts +119 -0
  48. package/src/checkout/address-form.tsx +224 -0
  49. package/src/checkout/address-select.tsx +79 -0
  50. package/src/checkout/boxnow-locker-selector.tsx +410 -0
  51. package/src/checkout/checkout-client.tsx +222 -0
  52. package/src/checkout/company-details.tsx +94 -0
  53. package/src/checkout/compare-addresses.ts +40 -0
  54. package/src/checkout/context.tsx +76 -0
  55. package/src/checkout/discount-section.tsx +218 -0
  56. package/src/checkout/econt-office-selector.tsx +332 -0
  57. package/src/checkout/error-message.tsx +25 -0
  58. package/src/checkout/geocode.ts +154 -0
  59. package/src/checkout/gift-card-section.tsx +224 -0
  60. package/src/checkout/index.ts +74 -0
  61. package/src/checkout/labels-bg.ts +128 -0
  62. package/src/checkout/labels.ts +263 -0
  63. package/src/checkout/line-item-card.tsx +152 -0
  64. package/src/checkout/order-summary.tsx +524 -0
  65. package/src/checkout/payment-button.tsx +373 -0
  66. package/src/checkout/payment-error-copy.ts +269 -0
  67. package/src/checkout/payment-method-list.tsx +365 -0
  68. package/src/checkout/payment-wrapper.tsx +102 -0
  69. package/src/checkout/promotion-error-copy.ts +124 -0
  70. package/src/checkout/shipping-method-list.tsx +335 -0
  71. package/src/checkout/stripe-wrapper.tsx +165 -0
  72. package/src/checkout/use-checkout-orchestration.ts +1504 -0
  73. package/src/common/cart-button-client.tsx +39 -0
  74. package/src/common/cart-button.tsx +28 -0
  75. package/src/common/country-select.tsx +65 -0
  76. package/src/common/delete-button.tsx +66 -0
  77. package/src/common/index.ts +17 -0
  78. package/src/common/language-select.tsx +78 -0
  79. package/src/common/localized-link.tsx +45 -0
  80. package/src/common/skeleton.tsx +29 -0
  81. package/src/index.ts +12 -0
  82. package/src/lib/cart-helpers.ts +113 -0
  83. package/src/lib/dual-price.tsx +73 -0
  84. package/src/lib/get-percentage-diff.ts +5 -0
  85. package/src/lib/get-product-price.ts +133 -0
  86. package/src/lib/hooks/use-intersection.ts +30 -0
  87. package/src/lib/hooks/use-toggle-state.ts +25 -0
  88. package/src/lib/money.ts +73 -0
  89. package/src/lib/payment-constants.ts +66 -0
  90. package/src/lib/product.ts +22 -0
  91. package/src/lib/sort-products.ts +63 -0
  92. package/src/lib/store-api-error.ts +36 -0
  93. package/src/lib/utils.ts +16 -0
  94. package/src/order/context.tsx +32 -0
  95. package/src/order/index.ts +63 -0
  96. package/src/order/labels-bg.ts +39 -0
  97. package/src/order/labels.ts +79 -0
  98. package/src/order/order-address-card.tsx +47 -0
  99. package/src/order/order-completed-template.tsx +165 -0
  100. package/src/order/order-confirmation-header.tsx +65 -0
  101. package/src/order/order-delivery-card.tsx +258 -0
  102. package/src/order/order-help-section.tsx +47 -0
  103. package/src/order/order-item.tsx +201 -0
  104. package/src/order/order-items-list.tsx +52 -0
  105. package/src/order/order-payment-card.tsx +95 -0
  106. package/src/order/order-timeline.tsx +141 -0
  107. package/src/order/order-totals.tsx +245 -0
  108. package/src/primitives/field.tsx +125 -0
  109. package/src/primitives/select-field.tsx +77 -0
  110. package/src/primitives/ui/accordion.tsx +61 -0
  111. package/src/primitives/ui/button.tsx +68 -0
  112. package/src/primitives/ui/collapsible.tsx +16 -0
  113. package/src/primitives/ui/dialog.tsx +112 -0
  114. package/src/primitives/ui/input.tsx +30 -0
  115. package/src/primitives/ui/label.tsx +31 -0
  116. package/src/primitives/ui/popover.tsx +38 -0
  117. package/src/primitives/ui/select.tsx +163 -0
  118. package/src/primitives/ui/sheet.tsx +131 -0
  119. package/src/primitives/ui/tabs.tsx +62 -0
  120. package/src/products/context.tsx +34 -0
  121. package/src/products/image-gallery.tsx +43 -0
  122. package/src/products/index.ts +44 -0
  123. package/src/products/labels-bg.ts +35 -0
  124. package/src/products/labels.ts +57 -0
  125. package/src/products/mobile-actions.tsx +180 -0
  126. package/src/products/option-select.tsx +67 -0
  127. package/src/products/preview-price.tsx +36 -0
  128. package/src/products/product-actions-wrapper.tsx +58 -0
  129. package/src/products/product-actions.tsx +217 -0
  130. package/src/products/product-info.tsx +43 -0
  131. package/src/products/product-preview.tsx +49 -0
  132. package/src/products/product-price.tsx +69 -0
  133. package/src/products/product-tabs.tsx +169 -0
  134. package/src/products/product-template.tsx +114 -0
  135. package/src/products/purchase-options.tsx +130 -0
  136. package/src/products/related-products.tsx +86 -0
  137. package/src/products/thumbnail.tsx +71 -0
  138. package/src/products/variant-matching.ts +71 -0
  139. package/src/reviews-ui/helpers.ts +174 -0
  140. package/src/reviews-ui/index.ts +74 -0
  141. package/src/reviews-ui/labels-bg.ts +91 -0
  142. package/src/reviews-ui/labels.ts +199 -0
  143. package/src/reviews-ui/photo-upload.tsx +345 -0
  144. package/src/reviews-ui/review-list.tsx +249 -0
  145. package/src/reviews-ui/review-widget.tsx +224 -0
  146. package/src/reviews-ui/review-wizard.tsx +560 -0
  147. package/src/reviews-ui/star-badge.tsx +104 -0
  148. package/src/reviews-ui/wizard-state.ts +81 -0
  149. package/src/store/category-template.tsx +129 -0
  150. package/src/store/collection-template.tsx +139 -0
  151. package/src/store/index.ts +41 -0
  152. package/src/store/labels-bg.ts +22 -0
  153. package/src/store/labels.ts +52 -0
  154. package/src/store/paginated-products.tsx +116 -0
  155. package/src/store/pagination.tsx +103 -0
  156. package/src/store/search-params.ts +256 -0
  157. package/src/store/search-template.tsx +249 -0
  158. package/src/store/skeleton-product-grid.tsx +26 -0
  159. package/src/store/sort-select.tsx +81 -0
  160. package/src/store/store-template.tsx +65 -0
  161. package/src/tracking/attribution.ts +418 -0
  162. package/src/tracking/consent-banner.tsx +355 -0
  163. package/src/tracking/consent-init.tsx +44 -0
  164. package/src/tracking/consent.ts +243 -0
  165. package/src/tracking/fbq.ts +168 -0
  166. package/src/tracking/ga4.tsx +49 -0
  167. package/src/tracking/get-tracking-attribution.ts +224 -0
  168. package/src/tracking/get-tracking-config.ts +50 -0
  169. package/src/tracking/gtag.ts +200 -0
  170. package/src/tracking/index.ts +133 -0
  171. package/src/tracking/meta-pixel.tsx +166 -0
  172. package/src/tracking/rybbit-events.ts +242 -0
  173. package/src/tracking/rybbit.tsx +40 -0
  174. package/src/tracking/types.ts +185 -0
  175. package/src/tracking/use-engagement-time.ts +58 -0
  176. package/tailwind-preset.cjs +72 -0
@@ -0,0 +1,224 @@
1
+ "use client"
2
+
3
+ import { useState } from "react"
4
+
5
+ import type { StorefrontClient } from "../api/http"
6
+ import type { Cart } from "../api/carts"
7
+ import { applyGiftCard, removeGiftCard } from "../api/gift-cards"
8
+ import { DualPrice } from "../lib/dual-price"
9
+ import { cn } from "../lib/utils"
10
+ import { useCheckoutLabels } from "./context"
11
+ import { translateGiftCardError } from "./payment-error-copy"
12
+
13
+ /**
14
+ * GiftCardSection — collapsible gift-card input shown inside the order
15
+ * summary card, plus the applied-cards chip list (masked `last4`, per-card
16
+ * coverage, remove).
17
+ *
18
+ * NEW in the barter port (no @1click equivalent — gift-card tender is a
19
+ * barter feature, docs/storefront/gift-cards.md). Built in the
20
+ * DiscountSection idiom so the two summary widgets read as one family.
21
+ *
22
+ * Contract discipline baked in:
23
+ * - Redemption is TENDER, never a discount: totals/VAT don't move. This
24
+ * component renders `cart.gift_cards[]` / `gift_card_total` /
25
+ * `gift_card_remainder` straight off the cart decoration — SERVER
26
+ * truth, zero client math.
27
+ * - Apply/remove via `@cartbase/storefront/api/gift-cards`
28
+ * (POST/DELETE /api/store/carts/:id/gift-cards). Both return the
29
+ * decorated cart — handed to `onCartChange`.
30
+ * - The API deliberately answers unknown/disabled/expired/depleted codes
31
+ * with ONE generic `invalid_gift_card` (no code-existence oracle) and
32
+ * rate-limits attempts (`rate_limited`). The error copy mirrors that:
33
+ * one honest generic message per code — never branch on failure reasons
34
+ * the API hides (`translateGiftCardError`).
35
+ * - The confirmation is MASKED — only `last4` renders; the code is shown
36
+ * nowhere after submit.
37
+ *
38
+ * After apply/remove, the checkout must re-sync any pending provider
39
+ * session (`syncPaymentAmount`) — the orchestration hook exposes it; wire
40
+ * `onCartChange` to call it (see CheckoutClient).
41
+ */
42
+
43
+ type GiftCardSectionProps = {
44
+ /** The SDK transport — apply/remove go through it. */
45
+ client: StorefrontClient
46
+ cart: Cart
47
+ /** Receives the decorated cart returned by a successful apply/remove. */
48
+ onCartChange?: (cart: Cart) => void
49
+ }
50
+
51
+ export function GiftCardSection({
52
+ client,
53
+ cart,
54
+ onCartChange,
55
+ }: GiftCardSectionProps) {
56
+ const labels = useCheckoutLabels()
57
+ const [code, setCode] = useState("")
58
+ const [loading, setLoading] = useState(false)
59
+ const [removingId, setRemovingId] = useState<string | null>(null)
60
+ const [error, setError] = useState("")
61
+ const [open, setOpen] = useState(false)
62
+
63
+ const appliedCards = cart.gift_cards ?? []
64
+
65
+ const handleApply = async () => {
66
+ if (!code.trim()) return
67
+ setLoading(true)
68
+ setError("")
69
+ try {
70
+ const { cart: updated } = await applyGiftCard(client, cart.id, {
71
+ code: code.trim(),
72
+ })
73
+ onCartChange?.(updated)
74
+ setCode("")
75
+ } catch (e: unknown) {
76
+ // Generic-oracle discipline: one clean message per code family.
77
+ setError(translateGiftCardError(e))
78
+ } finally {
79
+ setLoading(false)
80
+ }
81
+ }
82
+
83
+ const handleRemove = async (giftCardId: string) => {
84
+ setRemovingId(giftCardId)
85
+ setError("")
86
+ try {
87
+ const { cart: updated } = await removeGiftCard(client, cart.id, {
88
+ gift_card_id: giftCardId,
89
+ })
90
+ onCartChange?.(updated)
91
+ } catch (e: unknown) {
92
+ setError(translateGiftCardError(e))
93
+ } finally {
94
+ setRemovingId(null)
95
+ }
96
+ }
97
+
98
+ return (
99
+ <div>
100
+ <button
101
+ type="button"
102
+ onClick={() => setOpen(!open)}
103
+ className="flex items-center gap-3 w-full p-4 rounded-xl border border-border bg-muted/50 hover:bg-muted transition-colors"
104
+ >
105
+ <div className="w-10 h-10 rounded-full bg-gradient-to-br from-primary to-primary/70 flex items-center justify-center flex-shrink-0">
106
+ {/* Gift icon */}
107
+ <svg
108
+ className="w-5 h-5 text-card"
109
+ fill="none"
110
+ viewBox="0 0 24 24"
111
+ stroke="currentColor"
112
+ strokeWidth={2}
113
+ >
114
+ <path
115
+ strokeLinecap="round"
116
+ strokeLinejoin="round"
117
+ d="M21 11.25v8.25a1.5 1.5 0 01-1.5 1.5H4.5a1.5 1.5 0 01-1.5-1.5v-8.25M12 4.875A2.625 2.625 0 109.375 7.5H12m0-2.625V7.5m0-2.625A2.625 2.625 0 1114.625 7.5H12m0 0V21m-8.625-9.75h18c.621 0 1.125-.504 1.125-1.125v-1.5c0-.621-.504-1.125-1.125-1.125h-18c-.621 0-1.125.504-1.125 1.125v1.5c0 .621.504 1.125 1.125 1.125z"
118
+ />
119
+ </svg>
120
+ </div>
121
+ <div className="flex-1 text-left">
122
+ <p className="text-sm font-semibold text-foreground">
123
+ {labels.giftCard}
124
+ </p>
125
+ <p className="text-xs text-muted-foreground">{labels.giftCardSub}</p>
126
+ </div>
127
+ <span className="text-sm font-medium text-foreground flex items-center gap-1.5">
128
+ {labels.addCode}
129
+ <svg
130
+ className={cn(
131
+ "w-4 h-4 text-muted-foreground transition-transform duration-200",
132
+ open && "rotate-180"
133
+ )}
134
+ fill="none"
135
+ viewBox="0 0 24 24"
136
+ stroke="currentColor"
137
+ strokeWidth={2}
138
+ >
139
+ <path
140
+ strokeLinecap="round"
141
+ strokeLinejoin="round"
142
+ d="M19.5 8.25l-7.5 7.5-7.5-7.5"
143
+ />
144
+ </svg>
145
+ </span>
146
+ </button>
147
+
148
+ {open && (
149
+ <div className="mt-3 flex gap-2">
150
+ <input
151
+ type="text"
152
+ value={code}
153
+ onChange={(e) => setCode(e.target.value.toUpperCase())}
154
+ placeholder={labels.giftCardPlaceholder}
155
+ className="flex-1 h-11 px-4 text-sm bg-card border border-border rounded-xl focus:outline-none focus:border-primary focus:ring-2 focus:ring-primary/20 transition-all"
156
+ />
157
+ <button
158
+ type="button"
159
+ onClick={handleApply}
160
+ disabled={loading || !code.trim()}
161
+ className="px-5 h-11 text-sm font-semibold bg-foreground text-card rounded-xl hover:bg-foreground/90 transition-all disabled:opacity-40"
162
+ >
163
+ {labels.addCode}
164
+ </button>
165
+ </div>
166
+ )}
167
+
168
+ {error && <p className="text-xs text-destructive mt-2">{error}</p>}
169
+
170
+ {appliedCards.length > 0 && (
171
+ <div className="mt-3 space-y-1.5">
172
+ {appliedCards.map((gc) => (
173
+ <div
174
+ key={gc.id}
175
+ className={cn(
176
+ "flex items-center justify-between px-4 py-2.5 bg-success/10 border border-success/20 rounded-lg transition-opacity",
177
+ removingId === gc.id && "opacity-40 pointer-events-none"
178
+ )}
179
+ >
180
+ <div className="flex items-center gap-2 min-w-0">
181
+ <svg
182
+ className="w-4 h-4 text-success flex-shrink-0"
183
+ fill="none"
184
+ viewBox="0 0 24 24"
185
+ stroke="currentColor"
186
+ strokeWidth={2}
187
+ >
188
+ <path
189
+ strokeLinecap="round"
190
+ strokeLinejoin="round"
191
+ d="M9 12.75L11.25 15 15 9.75M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
192
+ />
193
+ </svg>
194
+ <span className="text-sm font-semibold text-success truncate">
195
+ {labels.giftCardEnding} ••••{gc.last4}
196
+ </span>
197
+ </div>
198
+ <div className="flex items-center gap-3 flex-shrink-0">
199
+ {/* Per-card coverage — server-derived; a depleted/disabled
200
+ card stays listed at 0 so the shopper sees WHY the
201
+ remainder didn't shrink. */}
202
+ <span className="text-sm font-medium text-success">
203
+ -
204
+ <DualPrice
205
+ amount={gc.amount}
206
+ currencyCode={cart.currency_code}
207
+ />
208
+ </span>
209
+ <button
210
+ type="button"
211
+ onClick={() => handleRemove(gc.id)}
212
+ disabled={removingId !== null}
213
+ className="text-xs text-muted-foreground hover:text-destructive hover:underline transition-colors"
214
+ >
215
+ {labels.remove}
216
+ </button>
217
+ </div>
218
+ </div>
219
+ ))}
220
+ </div>
221
+ )}
222
+ </div>
223
+ )
224
+ }
@@ -0,0 +1,74 @@
1
+ // @cartbase/storefront/checkout — barrel export.
2
+ //
3
+ // Ported from `@1click/ui/src/checkout/index.ts` (v2.3.1). Not ported in
4
+ // this batch: CheckoutLineItem's standalone file (its flat row lives inside
5
+ // order-summary.tsx and is re-exported below) and the Mobile* bars (out of
6
+ // the pre-declared file list; stores compose their own from the hook).
7
+ // Extra modules beyond the pre-declared export list (geocode,
8
+ // compare-addresses) are exported from here only.
9
+
10
+ export {
11
+ CheckoutProvider,
12
+ useCheckoutLabels,
13
+ useOrderConfirmedPath,
14
+ } from "./context"
15
+ export { defaultCheckoutLabels, type CheckoutLabels } from "./labels"
16
+ export { bulgarianCheckoutLabels } from "./labels-bg"
17
+ export { ErrorMessage } from "./error-message"
18
+ export {
19
+ StripeContext,
20
+ StripeScopeProvider,
21
+ StripeElementsScope,
22
+ useStripeScope,
23
+ } from "./stripe-wrapper"
24
+ export { PaymentWrapper } from "./payment-wrapper"
25
+ export { PaymentButton } from "./payment-button"
26
+ export {
27
+ EcontOfficeSelector,
28
+ type EcontOffice,
29
+ } from "./econt-office-selector"
30
+ export {
31
+ BoxNowLockerSelector,
32
+ type BoxNowLocker,
33
+ } from "./boxnow-locker-selector"
34
+ export { AddressSelect } from "./address-select"
35
+ export { CompanyDetails } from "./company-details"
36
+ export { DiscountSection } from "./discount-section"
37
+ export { GiftCardSection } from "./gift-card-section"
38
+ export { LineItemCard } from "./line-item-card"
39
+ export { OrderSummary, CheckoutLineItem } from "./order-summary"
40
+ export { CheckoutAddressForm } from "./address-form"
41
+ export { CheckoutShippingMethodList } from "./shipping-method-list"
42
+ export { CheckoutPaymentMethodList } from "./payment-method-list"
43
+ export { CheckoutClient } from "./checkout-client"
44
+ export {
45
+ useCheckoutOrchestration,
46
+ resolveOrderConfirmedPath,
47
+ type UseCheckoutOrchestrationOptions,
48
+ type CheckoutCodConfig,
49
+ type CheckoutLogError,
50
+ type PaymentProviderLike,
51
+ } from "./use-checkout-orchestration"
52
+ export {
53
+ translatePaymentError,
54
+ translateGiftCardError,
55
+ PAYMENT_ERROR_CODE_COPY,
56
+ GIFT_CARD_ERROR_CODE_COPY,
57
+ } from "./payment-error-copy"
58
+ export {
59
+ translateAddressError,
60
+ ADDRESS_ERROR_CODE_COPY,
61
+ } from "./address-error-copy"
62
+ export {
63
+ translatePromotionError,
64
+ PROMOTION_ERROR_CODE_COPY,
65
+ } from "./promotion-error-copy"
66
+ export { default as compareAddresses } from "./compare-addresses"
67
+ export {
68
+ transliterateBgToLatin,
69
+ normalizeForMatch,
70
+ distanceMeters,
71
+ formatDistance,
72
+ cleanAddress,
73
+ geocodeAddress,
74
+ } from "./geocode"
@@ -0,0 +1,128 @@
1
+ import type { CheckoutLabels } from "./labels"
2
+
3
+ /**
4
+ * Bulgarian translations for the checkout.
5
+ *
6
+ * Ported from `@1click/ui/src/checkout/labels-bg.ts` (v2.3.1) + the barter
7
+ * gift-card keys. Strings match the originals from mindpages-storefront's
8
+ * checkout-client monolith.
9
+ *
10
+ * Usage in a store's checkout page:
11
+ *
12
+ * import {
13
+ * CheckoutProvider,
14
+ * bulgarianCheckoutLabels,
15
+ * } from "@cartbase/storefront/checkout"
16
+ *
17
+ * <CheckoutProvider labels={bulgarianCheckoutLabels}>
18
+ * <CheckoutClient ... />
19
+ * </CheckoutProvider>
20
+ */
21
+
22
+ export const bulgarianCheckoutLabels: CheckoutLabels = {
23
+ deliveryInfo: "Информация за доставка",
24
+ shippingServices: "Начин на доставка",
25
+ paymentMethod: "Начин на плащане",
26
+ orderSummary: "Обобщение на поръчката",
27
+
28
+ email: "Имейл",
29
+ country: "Държава",
30
+ singleCountryName: "България",
31
+ firstName: "Собствено име",
32
+ lastName: "Фамилно име",
33
+ address: "Адрес",
34
+ apartment: "Етаж, апартамент и т.н. (незадължително)",
35
+ city: "Град",
36
+ postalCode: "Пощенски код",
37
+ phone: "Телефон",
38
+ province: "Област",
39
+
40
+ saveAddress: "Запази и продължи",
41
+ updateAddress: "Обнови адреса",
42
+ addToOrder: "+ Добави към поръчката",
43
+ addCode: "Приложи",
44
+
45
+ deliveryDisabled:
46
+ "Въведете адреса си за доставка, за да видите наличните варианти за доставяне.",
47
+ noShippingOptions: "Няма налични опции за доставка за вашия адрес.",
48
+ paymentDisabled: "Изберете начин на доставка, за да продължите.",
49
+ deliveryPreviewHint: "Въведете адрес, за да изберете доставка.",
50
+ buyNeedsAddress: "Въведете адрес за доставка",
51
+ buyNeedsDelivery: "Изберете начин на доставка",
52
+
53
+ codNote: "Плащане при доставка. Възможна е допълнителна такса.",
54
+ payOnline: "Плащане онлайн",
55
+ cashOnDelivery: "Наложен платеж",
56
+ termsText:
57
+ "С поръчката си приемате нашите Общи условия и Политика за поверителност.",
58
+
59
+ useSavedAddress: "Използвайте запазен адрес?",
60
+ saveInfo: "Запазване на тази информация за следващия път",
61
+
62
+ chooseAddress: "Изберете адрес",
63
+
64
+ items: "артикула",
65
+ qty: "Кол.",
66
+ subtotal: "Междинна сума",
67
+ shipping: "Доставка",
68
+ shippingCalc: "Ще бъде изчислена",
69
+ shippingFree: "Безплатна",
70
+ tax: "Вкл. ДДС",
71
+ taxTooltip: "ДДС 20% е включен в цените",
72
+ total: "Обща сума",
73
+ discount: "Отстъпка",
74
+ discountCode: "Код за отстъпка",
75
+ discountSub: "Спестете с промо код",
76
+ codFee: "Такса наложен платеж",
77
+ recommended: "Препоръчано за вас",
78
+ recommendedSoon: "Скоро тук",
79
+ recommendedSoonSub: "Персонализирани предложения за вас",
80
+ remove: "Премахни",
81
+ secureCheckout: "Защитено плащане",
82
+
83
+ giftCard: "Карта за подарък",
84
+ giftCardSub: "Използвайте карта за подарък",
85
+ giftCardPlaceholder: "GIFT-XXXX-XXXX",
86
+ giftCardApplied: "Карти за подарък",
87
+ giftCardRemaining: "Остава за плащане",
88
+ giftCardEnding: "Карта, завършваща на",
89
+
90
+ companyInvoice: "Фактура за фирма",
91
+ companyName: "Име на фирма",
92
+ companyVat: "ЕИК / Булстат",
93
+ companyMol: "МОЛ (материално отговорно лице)",
94
+ companyAddress: "Адрес на фирма",
95
+
96
+ placeOrder: "Поръчай",
97
+ selectPaymentMethod: "Изберете начин на плащане",
98
+
99
+ processingCard: [
100
+ "Свързваме се с банката...",
101
+ "Потвърждаваме плащането...",
102
+ "Записваме поръчката...",
103
+ "Внимаваме за всеки детайл...",
104
+ ],
105
+ processingCod: [
106
+ "Резервираме доставката...",
107
+ "Подготвяме поръчката...",
108
+ "Внимаваме за всеки детайл...",
109
+ ],
110
+
111
+ econtLoadingOffices: "Зареждане на офиси...",
112
+ econtNearestOffices: "Най-близки офиси",
113
+ econtSearchAnother: "Търси друг офис",
114
+ econtSearchPlaceholder: "Търси по име, град или адрес...",
115
+ econtNoResults: "Няма намерени офиси за",
116
+ econtChange: "Промени",
117
+
118
+ boxnowLoadingLockers: "Зареждане на автомати...",
119
+ boxnowNearestLockers: "Най-близки автомати",
120
+ boxnowSearchAnother: "Търси друг автомат",
121
+ boxnowSearchPlaceholder: "Търси по име, адрес или пощенски код...",
122
+ boxnowNoResults: "Няма намерени автомати за",
123
+ boxnowChange: "Промени",
124
+ boxnowUnavailable:
125
+ "BoxNow временно е недостъпен, моля изберете друг метод за доставка.",
126
+ boxnowNoLockersInCity:
127
+ "Няма BoxNow автомати във вашия град. Моля изберете друг метод за доставка.",
128
+ }
@@ -0,0 +1,263 @@
1
+ /**
2
+ * Checkout labels — default English copy.
3
+ *
4
+ * Ported from `@1click/ui/src/checkout/labels.ts` (v2.3.1) with the
5
+ * barter additions: gift-card tender labels (`giftCard*`) for the new
6
+ * GiftCardSection (gift cards are a barter 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
+ taxTooltip: string
85
+ total: string
86
+ discount: string
87
+ discountCode: string
88
+ discountSub: string
89
+ /** Default label for the cash-on-delivery fee row. Used as the third-
90
+ * level fallback when neither the cart's `cod_fee_label` (barter
91
+ * server truth) nor a per-store override is available. */
92
+ codFee: string
93
+ recommended: string
94
+ recommendedSoon: string
95
+ recommendedSoonSub: string
96
+ remove: string
97
+ secureCheckout: string
98
+
99
+ // Gift cards (barter gift-card tender — docs/storefront/gift-cards.md)
100
+ giftCard: string
101
+ giftCardSub: string
102
+ giftCardPlaceholder: string
103
+ /** Row label for the summary's gift-card tender line ("Gift cards −X"). */
104
+ giftCardApplied: string
105
+ /** Label for the remainder row ("Remaining to pay"). Rendered only when
106
+ * gift cards cover part of the total (`gift_card_total > 0`). */
107
+ giftCardRemaining: string
108
+ /** Prefix for an applied card chip, followed by the masked last4
109
+ * (e.g. "Gift card ••••1234"). */
110
+ giftCardEnding: string
111
+
112
+ // Company invoice
113
+ companyInvoice: string
114
+ companyName: string
115
+ companyVat: string
116
+ companyMol: string
117
+ companyAddress: string
118
+
119
+ // Place order button
120
+ placeOrder: string
121
+ selectPaymentMethod: string
122
+
123
+ /**
124
+ * Processing-state messages shown beneath the spinning Buy button
125
+ * while `performBuyClick` is in flight. Cycle through with a fade
126
+ * transition every ~1.8s; on long flows (3DS challenges, slow
127
+ * networks) the cycle loops back to index 0.
128
+ *
129
+ * Two arrays so card and COD show different micro-narratives — card
130
+ * mentions the bank handshake (the slow part), COD jumps straight
131
+ * to delivery + order. The closing entry should be a brand-flavored
132
+ * line that's still honest to the "processing" moment (don't say
133
+ * "packing" — nothing is being packed yet).
134
+ */
135
+ processingCard: string[]
136
+ processingCod: string[]
137
+
138
+ // Econt office selector
139
+ econtLoadingOffices: string
140
+ econtNearestOffices: string
141
+ econtSearchAnother: string
142
+ econtSearchPlaceholder: string
143
+ econtNoResults: string
144
+ econtChange: string
145
+
146
+ // BoxNow locker selector
147
+ boxnowLoadingLockers: string
148
+ boxnowNearestLockers: string
149
+ boxnowSearchAnother: string
150
+ boxnowSearchPlaceholder: string
151
+ boxnowNoResults: string
152
+ boxnowChange: string
153
+ boxnowUnavailable: string
154
+ boxnowNoLockersInCity: string
155
+ }
156
+
157
+ export const defaultCheckoutLabels: CheckoutLabels = {
158
+ deliveryInfo: "Delivery information",
159
+ shippingServices: "Shipping method",
160
+ paymentMethod: "Payment method",
161
+ orderSummary: "Order summary",
162
+
163
+ email: "Email",
164
+ country: "Country",
165
+ singleCountryName: undefined,
166
+ firstName: "First name",
167
+ lastName: "Last name",
168
+ address: "Address",
169
+ apartment: "Apartment, suite, etc. (optional)",
170
+ city: "City",
171
+ postalCode: "Postal code",
172
+ phone: "Phone",
173
+ province: "State / province",
174
+
175
+ saveAddress: "Save and continue",
176
+ updateAddress: "Update address",
177
+ addToOrder: "+ Add to order",
178
+ addCode: "Apply",
179
+
180
+ deliveryDisabled:
181
+ "Enter your delivery address to see available shipping options.",
182
+ noShippingOptions: "No shipping options available for your address.",
183
+ paymentDisabled: "Select a shipping method to continue.",
184
+ deliveryPreviewHint: "Enter your address to choose a delivery method.",
185
+ buyNeedsAddress: "Enter your delivery address",
186
+ buyNeedsDelivery: "Choose a delivery method",
187
+
188
+ codNote: "Cash on delivery. Additional fees may apply.",
189
+ payOnline: "Pay online",
190
+ cashOnDelivery: "Cash on delivery",
191
+ termsText:
192
+ "By placing your order you accept our Terms and Privacy Policy.",
193
+
194
+ useSavedAddress: "Use a saved address?",
195
+ saveInfo: "Save this information for next time",
196
+
197
+ chooseAddress: "Choose an address",
198
+
199
+ items: "items",
200
+ qty: "Qty",
201
+ subtotal: "Subtotal",
202
+ shipping: "Shipping",
203
+ shippingCalc: "Calculated at checkout",
204
+ shippingFree: "Free",
205
+ tax: "VAT incl.",
206
+ taxTooltip: "20% VAT is included in prices",
207
+ total: "Total",
208
+ discount: "Discount",
209
+ discountCode: "Discount code",
210
+ discountSub: "Save with a promo code",
211
+ codFee: "Cash on delivery fee",
212
+ recommended: "Recommended for you",
213
+ recommendedSoon: "Coming soon",
214
+ recommendedSoonSub: "Personalized recommendations",
215
+ remove: "Remove",
216
+ secureCheckout: "Secure checkout",
217
+
218
+ giftCard: "Gift card",
219
+ giftCardSub: "Redeem a gift card",
220
+ giftCardPlaceholder: "GIFT-XXXX-XXXX",
221
+ giftCardApplied: "Gift cards",
222
+ giftCardRemaining: "Remaining to pay",
223
+ giftCardEnding: "Gift card ending in",
224
+
225
+ companyInvoice: "Company invoice",
226
+ companyName: "Company name",
227
+ companyVat: "VAT / Company number",
228
+ companyMol: "Responsible person",
229
+ companyAddress: "Company address",
230
+
231
+ placeOrder: "Place order",
232
+ selectPaymentMethod: "Select a payment method",
233
+
234
+ processingCard: [
235
+ "Connecting to the bank...",
236
+ "Confirming the payment...",
237
+ "Recording the order...",
238
+ "Attending to every detail...",
239
+ ],
240
+ processingCod: [
241
+ "Reserving the delivery...",
242
+ "Preparing the order...",
243
+ "Attending to every detail...",
244
+ ],
245
+
246
+ econtLoadingOffices: "Loading offices...",
247
+ econtNearestOffices: "Nearest offices",
248
+ econtSearchAnother: "Search for another office",
249
+ econtSearchPlaceholder: "Search by name, city, or address...",
250
+ econtNoResults: "No offices found for",
251
+ econtChange: "Change",
252
+
253
+ boxnowLoadingLockers: "Loading lockers...",
254
+ boxnowNearestLockers: "Nearest lockers",
255
+ boxnowSearchAnother: "Search for another locker",
256
+ boxnowSearchPlaceholder: "Search by name, address, or postal code...",
257
+ boxnowNoResults: "No lockers found for",
258
+ boxnowChange: "Change",
259
+ boxnowUnavailable:
260
+ "BoxNow is temporarily unavailable, please choose a different shipping method.",
261
+ boxnowNoLockersInCity:
262
+ "No BoxNow lockers found in your city. Please choose a different shipping method.",
263
+ }