@cartbase/storefront 0.8.0 → 0.10.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 (50) hide show
  1. package/package.json +7 -8
  2. package/src/cart-drawer/cross-sell-carousel.tsx +2 -3
  3. package/src/cart-drawer/gift-wrap.tsx +82 -83
  4. package/src/cart-drawer/index.ts +0 -1
  5. package/src/cart-drawer/item/index.tsx +4 -6
  6. package/src/cart-drawer/sticky-footer.tsx +73 -73
  7. package/src/checkout/address-error-copy.ts +117 -119
  8. package/src/checkout/discount-section.tsx +218 -218
  9. package/src/checkout/error-copy-codes.ts +63 -0
  10. package/src/checkout/gift-card-section.tsx +4 -4
  11. package/src/checkout/index.ts +4 -5
  12. package/src/checkout/labels.ts +218 -0
  13. package/src/checkout/line-item-card.tsx +3 -3
  14. package/src/checkout/order-summary.tsx +11 -12
  15. package/src/checkout/payment-button.tsx +372 -373
  16. package/src/checkout/payment-error-copy.ts +154 -269
  17. package/src/checkout/promotion-error-copy.ts +91 -124
  18. package/src/checkout/shipping-method-list.tsx +2 -2
  19. package/src/checkout/use-checkout-orchestration.ts +10 -8
  20. package/src/lib/money.ts +1 -1
  21. package/src/lib/price.tsx +39 -0
  22. package/src/locales/bg.ts +468 -0
  23. package/src/locales/en.ts +26 -0
  24. package/src/locales/es.ts +467 -0
  25. package/src/locales/index.ts +19 -0
  26. package/src/locales/provider.tsx +63 -0
  27. package/src/locales/types.ts +49 -0
  28. package/src/order/index.ts +0 -1
  29. package/src/order/order-delivery-card.tsx +2 -2
  30. package/src/order/order-item.tsx +3 -3
  31. package/src/order/order-totals.tsx +4 -4
  32. package/src/products/index.ts +0 -1
  33. package/src/reviews-ui/index.ts +0 -1
  34. package/src/store/index.ts +0 -1
  35. package/src/tracking/attribution.ts +13 -0
  36. package/src/tracking/chatgpt-pixel.tsx +92 -0
  37. package/src/tracking/consent-banner.tsx +5 -1
  38. package/src/tracking/consent.ts +12 -0
  39. package/src/tracking/events.ts +56 -2
  40. package/src/tracking/index.ts +12 -0
  41. package/src/tracking/oaiq.ts +206 -0
  42. package/src/tracking/storefront-tags.tsx +2 -0
  43. package/src/tracking/types.ts +4 -0
  44. package/src/cart-drawer/labels-bg.ts +0 -72
  45. package/src/checkout/labels-bg.ts +0 -128
  46. package/src/lib/dual-price.tsx +0 -73
  47. package/src/order/labels-bg.ts +0 -39
  48. package/src/products/labels-bg.ts +0 -35
  49. package/src/reviews-ui/labels-bg.ts +0 -91
  50. package/src/store/labels-bg.ts +0 -22
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cartbase/storefront",
3
- "version": "0.8.0",
3
+ "version": "0.10.0",
4
4
  "description": "Storefront SDK + UI component library for Cartbase stores: typed API client, checkout orchestration, cart drawer, product/catalog components, tracking. Source-shipped TypeScript — add it to transpilePackages.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -49,7 +49,7 @@
49
49
  "./lib/get-percentage-diff": "./src/lib/get-percentage-diff.ts",
50
50
  "./lib/product": "./src/lib/product.ts",
51
51
  "./lib/sort-products": "./src/lib/sort-products.ts",
52
- "./lib/dual-price": "./src/lib/dual-price.tsx",
52
+ "./lib/price": "./src/lib/price.tsx",
53
53
  "./lib/payment-constants": "./src/lib/payment-constants.ts",
54
54
  "./lib/store-api-error": "./src/lib/store-api-error.ts",
55
55
  "./lib/hooks/use-intersection": "./src/lib/hooks/use-intersection.ts",
@@ -103,7 +103,6 @@
103
103
  "./checkout": "./src/checkout/index.ts",
104
104
  "./checkout/context": "./src/checkout/context.tsx",
105
105
  "./checkout/labels": "./src/checkout/labels.ts",
106
- "./checkout/labels-bg": "./src/checkout/labels-bg.ts",
107
106
  "./checkout/error-message": "./src/checkout/error-message.tsx",
108
107
  "./checkout/stripe-wrapper": "./src/checkout/stripe-wrapper.tsx",
109
108
  "./checkout/payment-wrapper": "./src/checkout/payment-wrapper.tsx",
@@ -127,7 +126,6 @@
127
126
  "./cart-drawer": "./src/cart-drawer/index.ts",
128
127
  "./cart-drawer/context": "./src/cart-drawer/context.tsx",
129
128
  "./cart-drawer/labels": "./src/cart-drawer/labels.ts",
130
- "./cart-drawer/labels-bg": "./src/cart-drawer/labels-bg.ts",
131
129
  "./cart-drawer/cart-drawer": "./src/cart-drawer/cart-drawer.tsx",
132
130
  "./cart-drawer/header": "./src/cart-drawer/header.tsx",
133
131
  "./cart-drawer/promo-banner": "./src/cart-drawer/promo-banner.tsx",
@@ -151,7 +149,6 @@
151
149
  "./products": "./src/products/index.ts",
152
150
  "./products/context": "./src/products/context.tsx",
153
151
  "./products/labels": "./src/products/labels.ts",
154
- "./products/labels-bg": "./src/products/labels-bg.ts",
155
152
  "./products/thumbnail": "./src/products/thumbnail.tsx",
156
153
  "./products/preview-price": "./src/products/preview-price.tsx",
157
154
  "./products/product-price": "./src/products/product-price.tsx",
@@ -167,7 +164,6 @@
167
164
  "./products/product-template": "./src/products/product-template.tsx",
168
165
  "./store": "./src/store/index.ts",
169
166
  "./store/labels": "./src/store/labels.ts",
170
- "./store/labels-bg": "./src/store/labels-bg.ts",
171
167
  "./store/pagination": "./src/store/pagination.tsx",
172
168
  "./store/sort-select": "./src/store/sort-select.tsx",
173
169
  "./store/paginated-products": "./src/store/paginated-products.tsx",
@@ -179,7 +175,6 @@
179
175
  "./order": "./src/order/index.ts",
180
176
  "./order/context": "./src/order/context.tsx",
181
177
  "./order/labels": "./src/order/labels.ts",
182
- "./order/labels-bg": "./src/order/labels-bg.ts",
183
178
  "./order/order-confirmation-header": "./src/order/order-confirmation-header.tsx",
184
179
  "./order/order-item": "./src/order/order-item.tsx",
185
180
  "./order/order-items-list": "./src/order/order-items-list.tsx",
@@ -198,7 +193,11 @@
198
193
  "./common/country-select": "./src/common/country-select.tsx",
199
194
  "./common/language-select": "./src/common/language-select.tsx",
200
195
  "./common/skeleton": "./src/common/skeleton.tsx",
201
- "./reviews-ui": "./src/reviews-ui/index.ts"
196
+ "./reviews-ui": "./src/reviews-ui/index.ts",
197
+ "./locales": "./src/locales/index.ts",
198
+ "./locales/en": "./src/locales/en.ts",
199
+ "./locales/es": "./src/locales/es.ts",
200
+ "./locales/bg": "./src/locales/bg.ts"
202
201
  },
203
202
  "dependencies": {
204
203
  "@stripe/react-stripe-js": "^6.1.0",
@@ -4,7 +4,7 @@ import Image from "next/image"
4
4
  import Link from "next/link"
5
5
  import { useRef, useState } from "react"
6
6
 
7
- import { DualPrice } from "../lib/dual-price"
7
+ import { Price } from "../lib/price"
8
8
  import { cn } from "../lib/utils"
9
9
  import { useCartDrawer } from "./context"
10
10
 
@@ -178,11 +178,10 @@ export function CartCrossSellCarousel({
178
178
  >
179
179
  {product.title}
180
180
  </Link>
181
- <DualPrice
181
+ <Price
182
182
  amount={product.price}
183
183
  currencyCode={product.currencyCode}
184
184
  className="text-[13px] font-bold text-foreground block mt-0.5"
185
- bgnClassName="text-muted-foreground text-[10px] ml-1"
186
185
  />
187
186
  </div>
188
187
 
@@ -1,83 +1,82 @@
1
- "use client"
2
-
3
- import { useState } from "react"
4
-
5
- import { DualPrice } from "../lib/dual-price"
6
- import { cn } from "../lib/utils"
7
- import { useCartDrawer } from "./context"
8
-
9
- /**
10
- * CartGiftWrap — inline toggle row for adding gift wrapping to the cart.
11
- *
12
- * Ported from `@1click/ui/src/cart-drawer/gift-wrap.tsx` (v2.3.1);
13
- * originally extracted from mindpages-storefront
14
- * src/modules/cart-drawer/cart-gift-wrap.tsx. `price` is EUR major units.
15
- * The store owns the effect: wire `onToggle` to add/remove the store's
16
- * gift-wrap variant via the provider's `addItem` / `removeItem`.
17
- */
18
-
19
- type CartGiftWrapProps = {
20
- price: number
21
- currencyCode: string
22
- enabled?: boolean
23
- onToggle?: (enabled: boolean) => void
24
- }
25
-
26
- export function CartGiftWrap({
27
- price,
28
- currencyCode,
29
- enabled = false,
30
- onToggle,
31
- }: CartGiftWrapProps) {
32
- const { labels } = useCartDrawer()
33
- const [checked, setChecked] = useState(enabled)
34
-
35
- const handleToggle = () => {
36
- const next = !checked
37
- setChecked(next)
38
- onToggle?.(next)
39
- }
40
-
41
- return (
42
- <div className="px-5 sm:px-6 py-2">
43
- <button
44
- type="button"
45
- onClick={handleToggle}
46
- className="flex items-center gap-3 w-full text-left py-2 min-h-[44px] group"
47
- >
48
- <div
49
- className={cn(
50
- "w-5 h-5 rounded border-[1.5px] flex items-center justify-center transition-all flex-shrink-0",
51
- checked
52
- ? "bg-foreground border-text-base"
53
- : "border-border group-hover:border-text-muted group-active:border-text-muted"
54
- )}
55
- >
56
- {checked && (
57
- <svg
58
- width="10"
59
- height="10"
60
- viewBox="0 0 10 10"
61
- fill="none"
62
- stroke="white"
63
- strokeWidth="2"
64
- strokeLinecap="round"
65
- strokeLinejoin="round"
66
- >
67
- <path d="M2 5l2 2 4-4" />
68
- </svg>
69
- )}
70
- </div>
71
- <span className="text-[13px] text-foreground flex-1">
72
- {labels.addGiftWrap}
73
- </span>
74
- <DualPrice
75
- amount={price}
76
- currencyCode={currencyCode}
77
- className="text-[13px] text-muted-foreground font-medium"
78
- bgnClassName="text-muted-foreground text-[10px] ml-1"
79
- />
80
- </button>
81
- </div>
82
- )
83
- }
1
+ "use client"
2
+
3
+ import { useState } from "react"
4
+
5
+ import { Price } from "../lib/price"
6
+ import { cn } from "../lib/utils"
7
+ import { useCartDrawer } from "./context"
8
+
9
+ /**
10
+ * CartGiftWrap — inline toggle row for adding gift wrapping to the cart.
11
+ *
12
+ * Ported from `@1click/ui/src/cart-drawer/gift-wrap.tsx` (v2.3.1);
13
+ * originally extracted from mindpages-storefront
14
+ * src/modules/cart-drawer/cart-gift-wrap.tsx. `price` is EUR major units.
15
+ * The store owns the effect: wire `onToggle` to add/remove the store's
16
+ * gift-wrap variant via the provider's `addItem` / `removeItem`.
17
+ */
18
+
19
+ type CartGiftWrapProps = {
20
+ price: number
21
+ currencyCode: string
22
+ enabled?: boolean
23
+ onToggle?: (enabled: boolean) => void
24
+ }
25
+
26
+ export function CartGiftWrap({
27
+ price,
28
+ currencyCode,
29
+ enabled = false,
30
+ onToggle,
31
+ }: CartGiftWrapProps) {
32
+ const { labels } = useCartDrawer()
33
+ const [checked, setChecked] = useState(enabled)
34
+
35
+ const handleToggle = () => {
36
+ const next = !checked
37
+ setChecked(next)
38
+ onToggle?.(next)
39
+ }
40
+
41
+ return (
42
+ <div className="px-5 sm:px-6 py-2">
43
+ <button
44
+ type="button"
45
+ onClick={handleToggle}
46
+ className="flex items-center gap-3 w-full text-left py-2 min-h-[44px] group"
47
+ >
48
+ <div
49
+ className={cn(
50
+ "w-5 h-5 rounded border-[1.5px] flex items-center justify-center transition-all flex-shrink-0",
51
+ checked
52
+ ? "bg-foreground border-text-base"
53
+ : "border-border group-hover:border-text-muted group-active:border-text-muted"
54
+ )}
55
+ >
56
+ {checked && (
57
+ <svg
58
+ width="10"
59
+ height="10"
60
+ viewBox="0 0 10 10"
61
+ fill="none"
62
+ stroke="white"
63
+ strokeWidth="2"
64
+ strokeLinecap="round"
65
+ strokeLinejoin="round"
66
+ >
67
+ <path d="M2 5l2 2 4-4" />
68
+ </svg>
69
+ )}
70
+ </div>
71
+ <span className="text-[13px] text-foreground flex-1">
72
+ {labels.addGiftWrap}
73
+ </span>
74
+ <Price
75
+ amount={price}
76
+ currencyCode={currencyCode}
77
+ className="text-[13px] text-muted-foreground font-medium"
78
+ />
79
+ </button>
80
+ </div>
81
+ )
82
+ }
@@ -28,7 +28,6 @@ export {
28
28
  defaultCartDrawerLabels,
29
29
  type CartDrawerLabels,
30
30
  } from "./labels"
31
- export { bulgarianCartDrawerLabels } from "./labels-bg"
32
31
 
33
32
  export { CartDrawer } from "./cart-drawer"
34
33
  export { CartDrawerHeader } from "./header"
@@ -5,7 +5,7 @@ import Link from "next/link"
5
5
  import { type ReactNode } from "react"
6
6
 
7
7
  import type { CartLineItem } from "../../api/carts"
8
- import { DualPrice } from "../../lib/dual-price"
8
+ import { Price } from "../../lib/price"
9
9
  import { cn } from "../../lib/utils"
10
10
  import { useCartDrawer } from "../context"
11
11
  import { CartItemQuantity } from "./quantity"
@@ -13,7 +13,7 @@ import { CartItemVariant } from "./variant"
13
13
 
14
14
  /**
15
15
  * CartItem — a single cart line row: thumbnail, title, variant, quantity
16
- * stepper, price (with dual-currency + optional strikethrough), remove button.
16
+ * stepper, price (with optional strikethrough), remove button.
17
17
  *
18
18
  * Ported from `@1click/ui/src/cart-drawer/item/index.tsx` (v2.3.1). Cartbase
19
19
  * data seam: `item` is the SDK's decorated `CartLineItem`
@@ -111,22 +111,20 @@ export function CartItem({ item, currencyCode, children }: CartItemProps) {
111
111
  <div className="text-right flex-shrink-0">
112
112
  {hasDiscount && (
113
113
  <span className="text-xs text-muted-foreground line-through block leading-none mb-0.5">
114
- <DualPrice
114
+ <Price
115
115
  amount={originalTotal}
116
116
  currencyCode={currencyCode}
117
117
  className="text-xs text-muted-foreground"
118
- bgnClassName="hidden"
119
118
  />
120
119
  </span>
121
120
  )}
122
- <DualPrice
121
+ <Price
123
122
  amount={total}
124
123
  currencyCode={currencyCode}
125
124
  className={cn(
126
125
  "text-sm font-bold",
127
126
  hasDiscount ? "text-success" : "text-foreground"
128
127
  )}
129
- bgnClassName="text-muted-foreground text-[10px] ml-1"
130
128
  />
131
129
  </div>
132
130
  </div>
@@ -1,73 +1,73 @@
1
- "use client"
2
-
3
- import Link from "next/link"
4
-
5
- import { DualPrice } from "../lib/dual-price"
6
- import { productTotal } from "../lib/cart-helpers"
7
- import { useCartDrawer } from "./context"
8
-
9
- /**
10
- * CartStickyFooter — bottom-of-drawer subtotal + checkout CTA with
11
- * iPhone home-indicator safe-area padding.
12
- *
13
- * Ported from `@1click/ui/src/cart-drawer/sticky-footer.tsx` (v2.3.1);
14
- * originally extracted from mindpages-storefront
15
- * src/modules/cart-drawer/cart-sticky-footer.tsx. Amounts EUR major units.
16
- */
17
-
18
- export function CartStickyFooter() {
19
- const { cart, close, labels, hrefs } = useCartDrawer()
20
- if (!cart || !cart.items?.length) return null
21
-
22
- // Drawer is pre-checkout context — never read `cart.total` here.
23
- // `cart.total` includes shipping/tax/COD fee, all of which are
24
- // checkout-context state that must not leak into the shopping
25
- // drawer. Sum of product line totals only.
26
- const total = productTotal(cart.items)
27
- const currencyCode = cart.currency_code
28
-
29
- return (
30
- <div
31
- className="flex-shrink-0 bg-card px-5 sm:px-6 pt-4 sm:pt-5 relative z-10"
32
- style={{
33
- boxShadow: "0 -4px 12px rgba(0,0,0,0.04)",
34
- paddingBottom: "max(1.25rem, env(safe-area-inset-bottom, 1.25rem))",
35
- }}
36
- >
37
- <div className="flex items-baseline justify-between mb-1">
38
- <span className="text-[15px] font-bold text-foreground">
39
- {labels.subtotal}
40
- </span>
41
- <DualPrice
42
- amount={total}
43
- currencyCode={currencyCode}
44
- className="text-xl font-bold text-foreground tracking-tight"
45
- />
46
- </div>
47
- <p className="text-[11px] text-muted-foreground mb-3.5 sm:mb-4">
48
- {labels.taxAndShipping}
49
- </p>
50
- <Link href={hrefs.checkout} onClick={close}>
51
- <button
52
- type="button"
53
- className="w-full h-[52px] bg-foreground hover:bg-foreground/90 active:bg-foreground/95 text-card text-[15px] sm:text-sm font-semibold rounded-[2px] flex items-center justify-center gap-2.5 transition-colors active:scale-[0.98]"
54
- >
55
- <svg
56
- width="16"
57
- height="16"
58
- viewBox="0 0 16 16"
59
- fill="none"
60
- stroke="currentColor"
61
- strokeWidth="1.5"
62
- strokeLinecap="round"
63
- strokeLinejoin="round"
64
- >
65
- <rect x="3" y="6" width="10" height="8" rx="1.5" />
66
- <path d="M5.5 6V4.5a2.5 2.5 0 015 0V6" />
67
- </svg>
68
- {labels.secureCheckout}
69
- </button>
70
- </Link>
71
- </div>
72
- )
73
- }
1
+ "use client"
2
+
3
+ import Link from "next/link"
4
+
5
+ import { Price } from "../lib/price"
6
+ import { productTotal } from "../lib/cart-helpers"
7
+ import { useCartDrawer } from "./context"
8
+
9
+ /**
10
+ * CartStickyFooter — bottom-of-drawer subtotal + checkout CTA with
11
+ * iPhone home-indicator safe-area padding.
12
+ *
13
+ * Ported from `@1click/ui/src/cart-drawer/sticky-footer.tsx` (v2.3.1);
14
+ * originally extracted from mindpages-storefront
15
+ * src/modules/cart-drawer/cart-sticky-footer.tsx. Amounts EUR major units.
16
+ */
17
+
18
+ export function CartStickyFooter() {
19
+ const { cart, close, labels, hrefs } = useCartDrawer()
20
+ if (!cart || !cart.items?.length) return null
21
+
22
+ // Drawer is pre-checkout context — never read `cart.total` here.
23
+ // `cart.total` includes shipping/tax/COD fee, all of which are
24
+ // checkout-context state that must not leak into the shopping
25
+ // drawer. Sum of product line totals only.
26
+ const total = productTotal(cart.items)
27
+ const currencyCode = cart.currency_code
28
+
29
+ return (
30
+ <div
31
+ className="flex-shrink-0 bg-card px-5 sm:px-6 pt-4 sm:pt-5 relative z-10"
32
+ style={{
33
+ boxShadow: "0 -4px 12px rgba(0,0,0,0.04)",
34
+ paddingBottom: "max(1.25rem, env(safe-area-inset-bottom, 1.25rem))",
35
+ }}
36
+ >
37
+ <div className="flex items-baseline justify-between mb-1">
38
+ <span className="text-[15px] font-bold text-foreground">
39
+ {labels.subtotal}
40
+ </span>
41
+ <Price
42
+ amount={total}
43
+ currencyCode={currencyCode}
44
+ className="text-xl font-bold text-foreground tracking-tight"
45
+ />
46
+ </div>
47
+ <p className="text-[11px] text-muted-foreground mb-3.5 sm:mb-4">
48
+ {labels.taxAndShipping}
49
+ </p>
50
+ <Link href={hrefs.checkout} onClick={close}>
51
+ <button
52
+ type="button"
53
+ className="w-full h-[52px] bg-foreground hover:bg-foreground/90 active:bg-foreground/95 text-card text-[15px] sm:text-sm font-semibold rounded-[2px] flex items-center justify-center gap-2.5 transition-colors active:scale-[0.98]"
54
+ >
55
+ <svg
56
+ width="16"
57
+ height="16"
58
+ viewBox="0 0 16 16"
59
+ fill="none"
60
+ stroke="currentColor"
61
+ strokeWidth="1.5"
62
+ strokeLinecap="round"
63
+ strokeLinejoin="round"
64
+ >
65
+ <rect x="3" y="6" width="10" height="8" rx="1.5" />
66
+ <path d="M5.5 6V4.5a2.5 2.5 0 015 0V6" />
67
+ </svg>
68
+ {labels.secureCheckout}
69
+ </button>
70
+ </Link>
71
+ </div>
72
+ )
73
+ }