@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.
- package/package.json +226 -0
- package/src/api/auth.ts +108 -0
- package/src/api/carts.ts +506 -0
- package/src/api/categories.ts +184 -0
- package/src/api/checkout.ts +440 -0
- package/src/api/collections.ts +130 -0
- package/src/api/consent.ts +75 -0
- package/src/api/content.ts +125 -0
- package/src/api/customers.ts +307 -0
- package/src/api/gift-cards.ts +112 -0
- package/src/api/http.ts +122 -0
- package/src/api/index.ts +29 -0
- package/src/api/integrations.ts +130 -0
- package/src/api/menus.ts +77 -0
- package/src/api/metaobjects.ts +136 -0
- package/src/api/orders.ts +290 -0
- package/src/api/products.ts +303 -0
- package/src/api/redirects.ts +37 -0
- package/src/api/regions.ts +141 -0
- package/src/api/reviews.ts +259 -0
- package/src/api/search.ts +133 -0
- package/src/api/types.ts +91 -0
- package/src/cart-drawer/cart-drawer.tsx +86 -0
- package/src/cart-drawer/context.tsx +569 -0
- package/src/cart-drawer/continue-shopping.tsx +28 -0
- package/src/cart-drawer/cross-sell-carousel.tsx +212 -0
- package/src/cart-drawer/cross-sell-sidebar.tsx +158 -0
- package/src/cart-drawer/empty.tsx +52 -0
- package/src/cart-drawer/free-gift.tsx +71 -0
- package/src/cart-drawer/gift-wrap.tsx +83 -0
- package/src/cart-drawer/header.tsx +52 -0
- package/src/cart-drawer/index.ts +69 -0
- package/src/cart-drawer/item/index.tsx +164 -0
- package/src/cart-drawer/item/quantity.tsx +100 -0
- package/src/cart-drawer/item/upsell.tsx +110 -0
- package/src/cart-drawer/item/variant.tsx +46 -0
- package/src/cart-drawer/labels-bg.ts +72 -0
- package/src/cart-drawer/labels.ts +119 -0
- package/src/cart-drawer/notes.tsx +131 -0
- package/src/cart-drawer/payment-badges.tsx +96 -0
- package/src/cart-drawer/promo-banner.tsx +43 -0
- package/src/cart-drawer/rewards-points.tsx +78 -0
- package/src/cart-drawer/sticky-footer.tsx +73 -0
- package/src/cart-drawer/summary-breakdown.tsx +196 -0
- package/src/cart-drawer/template.tsx +225 -0
- package/src/cart-drawer/tiered-progress.tsx +168 -0
- package/src/checkout/address-error-copy.ts +119 -0
- package/src/checkout/address-form.tsx +224 -0
- package/src/checkout/address-select.tsx +79 -0
- package/src/checkout/boxnow-locker-selector.tsx +410 -0
- package/src/checkout/checkout-client.tsx +222 -0
- package/src/checkout/company-details.tsx +94 -0
- package/src/checkout/compare-addresses.ts +40 -0
- package/src/checkout/context.tsx +76 -0
- package/src/checkout/discount-section.tsx +218 -0
- package/src/checkout/econt-office-selector.tsx +332 -0
- package/src/checkout/error-message.tsx +25 -0
- package/src/checkout/geocode.ts +154 -0
- package/src/checkout/gift-card-section.tsx +224 -0
- package/src/checkout/index.ts +74 -0
- package/src/checkout/labels-bg.ts +128 -0
- package/src/checkout/labels.ts +263 -0
- package/src/checkout/line-item-card.tsx +152 -0
- package/src/checkout/order-summary.tsx +524 -0
- package/src/checkout/payment-button.tsx +373 -0
- package/src/checkout/payment-error-copy.ts +269 -0
- package/src/checkout/payment-method-list.tsx +365 -0
- package/src/checkout/payment-wrapper.tsx +102 -0
- package/src/checkout/promotion-error-copy.ts +124 -0
- package/src/checkout/shipping-method-list.tsx +335 -0
- package/src/checkout/stripe-wrapper.tsx +165 -0
- package/src/checkout/use-checkout-orchestration.ts +1504 -0
- package/src/common/cart-button-client.tsx +39 -0
- package/src/common/cart-button.tsx +28 -0
- package/src/common/country-select.tsx +65 -0
- package/src/common/delete-button.tsx +66 -0
- package/src/common/index.ts +17 -0
- package/src/common/language-select.tsx +78 -0
- package/src/common/localized-link.tsx +45 -0
- package/src/common/skeleton.tsx +29 -0
- package/src/index.ts +12 -0
- package/src/lib/cart-helpers.ts +113 -0
- package/src/lib/dual-price.tsx +73 -0
- package/src/lib/get-percentage-diff.ts +5 -0
- package/src/lib/get-product-price.ts +133 -0
- package/src/lib/hooks/use-intersection.ts +30 -0
- package/src/lib/hooks/use-toggle-state.ts +25 -0
- package/src/lib/money.ts +73 -0
- package/src/lib/payment-constants.ts +66 -0
- package/src/lib/product.ts +22 -0
- package/src/lib/sort-products.ts +63 -0
- package/src/lib/store-api-error.ts +36 -0
- package/src/lib/utils.ts +16 -0
- package/src/order/context.tsx +32 -0
- package/src/order/index.ts +63 -0
- package/src/order/labels-bg.ts +39 -0
- package/src/order/labels.ts +79 -0
- package/src/order/order-address-card.tsx +47 -0
- package/src/order/order-completed-template.tsx +165 -0
- package/src/order/order-confirmation-header.tsx +65 -0
- package/src/order/order-delivery-card.tsx +258 -0
- package/src/order/order-help-section.tsx +47 -0
- package/src/order/order-item.tsx +201 -0
- package/src/order/order-items-list.tsx +52 -0
- package/src/order/order-payment-card.tsx +95 -0
- package/src/order/order-timeline.tsx +141 -0
- package/src/order/order-totals.tsx +245 -0
- package/src/primitives/field.tsx +125 -0
- package/src/primitives/select-field.tsx +77 -0
- package/src/primitives/ui/accordion.tsx +61 -0
- package/src/primitives/ui/button.tsx +68 -0
- package/src/primitives/ui/collapsible.tsx +16 -0
- package/src/primitives/ui/dialog.tsx +112 -0
- package/src/primitives/ui/input.tsx +30 -0
- package/src/primitives/ui/label.tsx +31 -0
- package/src/primitives/ui/popover.tsx +38 -0
- package/src/primitives/ui/select.tsx +163 -0
- package/src/primitives/ui/sheet.tsx +131 -0
- package/src/primitives/ui/tabs.tsx +62 -0
- package/src/products/context.tsx +34 -0
- package/src/products/image-gallery.tsx +43 -0
- package/src/products/index.ts +44 -0
- package/src/products/labels-bg.ts +35 -0
- package/src/products/labels.ts +57 -0
- package/src/products/mobile-actions.tsx +180 -0
- package/src/products/option-select.tsx +67 -0
- package/src/products/preview-price.tsx +36 -0
- package/src/products/product-actions-wrapper.tsx +58 -0
- package/src/products/product-actions.tsx +217 -0
- package/src/products/product-info.tsx +43 -0
- package/src/products/product-preview.tsx +49 -0
- package/src/products/product-price.tsx +69 -0
- package/src/products/product-tabs.tsx +169 -0
- package/src/products/product-template.tsx +114 -0
- package/src/products/purchase-options.tsx +130 -0
- package/src/products/related-products.tsx +86 -0
- package/src/products/thumbnail.tsx +71 -0
- package/src/products/variant-matching.ts +71 -0
- package/src/reviews-ui/helpers.ts +174 -0
- package/src/reviews-ui/index.ts +74 -0
- package/src/reviews-ui/labels-bg.ts +91 -0
- package/src/reviews-ui/labels.ts +199 -0
- package/src/reviews-ui/photo-upload.tsx +345 -0
- package/src/reviews-ui/review-list.tsx +249 -0
- package/src/reviews-ui/review-widget.tsx +224 -0
- package/src/reviews-ui/review-wizard.tsx +560 -0
- package/src/reviews-ui/star-badge.tsx +104 -0
- package/src/reviews-ui/wizard-state.ts +81 -0
- package/src/store/category-template.tsx +129 -0
- package/src/store/collection-template.tsx +139 -0
- package/src/store/index.ts +41 -0
- package/src/store/labels-bg.ts +22 -0
- package/src/store/labels.ts +52 -0
- package/src/store/paginated-products.tsx +116 -0
- package/src/store/pagination.tsx +103 -0
- package/src/store/search-params.ts +256 -0
- package/src/store/search-template.tsx +249 -0
- package/src/store/skeleton-product-grid.tsx +26 -0
- package/src/store/sort-select.tsx +81 -0
- package/src/store/store-template.tsx +65 -0
- package/src/tracking/attribution.ts +418 -0
- package/src/tracking/consent-banner.tsx +355 -0
- package/src/tracking/consent-init.tsx +44 -0
- package/src/tracking/consent.ts +243 -0
- package/src/tracking/fbq.ts +168 -0
- package/src/tracking/ga4.tsx +49 -0
- package/src/tracking/get-tracking-attribution.ts +224 -0
- package/src/tracking/get-tracking-config.ts +50 -0
- package/src/tracking/gtag.ts +200 -0
- package/src/tracking/index.ts +133 -0
- package/src/tracking/meta-pixel.tsx +166 -0
- package/src/tracking/rybbit-events.ts +242 -0
- package/src/tracking/rybbit.tsx +40 -0
- package/src/tracking/types.ts +185 -0
- package/src/tracking/use-engagement-time.ts +58 -0
- 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
|
+
}
|