@cartbase/storefront 0.1.2 → 0.3.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/README.md +3 -3
- package/package.json +231 -226
- package/src/api/auth.ts +1 -1
- package/src/api/carts.ts +5 -3
- package/src/api/checkout.ts +91 -40
- package/src/api/consent.ts +2 -2
- package/src/api/integrations.ts +4 -17
- package/src/api/regions.ts +1 -1
- package/src/cart-drawer/context.tsx +4 -4
- package/src/cart-drawer/cross-sell-carousel.tsx +1 -1
- package/src/cart-drawer/cross-sell-sidebar.tsx +1 -1
- package/src/cart-drawer/header.tsx +1 -1
- package/src/cart-drawer/item/index.tsx +2 -2
- package/src/cart-drawer/item/quantity.tsx +2 -2
- package/src/cart-drawer/item/upsell.tsx +3 -3
- package/src/cart-drawer/item/variant.tsx +2 -2
- package/src/cart-drawer/labels-bg.ts +2 -2
- package/src/cart-drawer/labels.ts +119 -119
- package/src/cart-drawer/summary-breakdown.tsx +197 -196
- package/src/cart-drawer/template.tsx +2 -2
- package/src/cart-drawer/tiered-progress.tsx +1 -1
- package/src/checkout/address-error-copy.ts +3 -3
- package/src/checkout/address-form.tsx +2 -2
- package/src/checkout/address-select.tsx +1 -1
- package/src/checkout/boxnow-locker-selector.tsx +3 -3
- package/src/checkout/checkout-client.tsx +15 -12
- package/src/checkout/company-details.tsx +1 -1
- package/src/checkout/compare-addresses.ts +1 -1
- package/src/checkout/discount-section.tsx +5 -5
- package/src/checkout/econt-office-selector.tsx +2 -2
- package/src/checkout/gift-card-section.tsx +2 -2
- package/src/checkout/index.ts +0 -1
- package/src/checkout/labels-bg.ts +2 -2
- package/src/checkout/labels.ts +263 -263
- package/src/checkout/line-item-card.tsx +2 -2
- package/src/checkout/order-summary.tsx +48 -48
- package/src/checkout/payment-button.tsx +4 -4
- package/src/checkout/payment-error-copy.ts +6 -6
- package/src/checkout/payment-method-list.tsx +93 -33
- package/src/checkout/payment-wrapper.tsx +8 -11
- package/src/checkout/promotion-error-copy.ts +2 -2
- package/src/checkout/shipping-method-list.tsx +2 -2
- package/src/checkout/stripe-wrapper.tsx +1 -1
- package/src/checkout/use-checkout-orchestration.ts +108 -98
- package/src/common/cart-button-client.tsx +1 -1
- package/src/common/cart-button.tsx +1 -1
- package/src/common/country-select.tsx +2 -2
- package/src/common/delete-button.tsx +1 -1
- package/src/common/index.ts +1 -1
- package/src/common/language-select.tsx +2 -2
- package/src/common/localized-link.tsx +1 -1
- package/src/lib/cart-helpers.ts +2 -2
- package/src/lib/cookie-names.ts +41 -0
- package/src/lib/get-product-price.ts +3 -3
- package/src/lib/payment-constants.ts +53 -66
- package/src/lib/store-api-error.ts +1 -1
- package/src/order/index.ts +1 -1
- package/src/order/labels-bg.ts +39 -39
- package/src/order/labels.ts +79 -79
- package/src/order/order-address-card.tsx +1 -1
- package/src/order/order-completed-template.tsx +20 -12
- package/src/order/order-confirmation-header.tsx +1 -1
- package/src/order/order-delivery-card.tsx +3 -3
- package/src/order/order-item.tsx +2 -2
- package/src/order/order-items-list.tsx +3 -3
- package/src/order/order-payment-card.tsx +27 -36
- package/src/order/order-timeline.tsx +2 -2
- package/src/order/order-totals.tsx +250 -245
- package/src/platform/identity.ts +18 -0
- package/src/platform/index.ts +8 -0
- package/src/platform/metadata.ts +26 -0
- package/src/platform/platform-init.tsx +46 -0
- package/src/products/image-gallery.tsx +1 -1
- package/src/products/mobile-actions.tsx +1 -1
- package/src/products/option-select.tsx +1 -1
- package/src/products/preview-price.tsx +1 -1
- package/src/products/product-actions.tsx +6 -6
- package/src/products/product-info.tsx +1 -1
- package/src/products/product-preview.tsx +1 -1
- package/src/products/product-price.tsx +2 -2
- package/src/products/product-tabs.tsx +1 -1
- package/src/products/purchase-options.tsx +1 -1
- package/src/products/related-products.tsx +3 -3
- package/src/products/variant-matching.ts +4 -4
- package/src/reviews-ui/labels.ts +1 -1
- package/src/reviews-ui/photo-upload.tsx +1 -1
- package/src/reviews-ui/review-list.tsx +1 -1
- package/src/reviews-ui/review-widget.tsx +1 -1
- package/src/reviews-ui/review-wizard.tsx +1 -1
- package/src/reviews-ui/wizard-state.ts +1 -1
- package/src/store/category-template.tsx +2 -2
- package/src/store/collection-template.tsx +2 -2
- package/src/store/labels.ts +2 -2
- package/src/store/paginated-products.tsx +1 -1
- package/src/store/search-params.ts +1 -1
- package/src/store/search-template.tsx +1 -1
- package/src/tracking/consent-banner.tsx +1 -1
- package/src/tracking/consent-init.tsx +1 -1
- package/src/tracking/consent.ts +6 -6
- package/src/tracking/fbq.ts +2 -2
- package/src/tracking/get-tracking-attribution.ts +1 -1
- package/src/tracking/get-tracking-config.ts +2 -2
- package/src/tracking/index.ts +2 -2
- package/src/tracking/meta-pixel.tsx +1 -1
- package/src/tracking/rybbit.tsx +1 -1
- package/src/tracking/types.ts +6 -6
|
@@ -1,119 +1,119 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Cart drawer labels — default English copy.
|
|
3
|
-
*
|
|
4
|
-
* Every user-facing string in the cart drawer comes from a `CartDrawerLabels`
|
|
5
|
-
* object. Stores override strings by passing a partial labels object to
|
|
6
|
-
* `CartDrawerProvider`. English is the baseline; stores wanting Bulgarian,
|
|
7
|
-
* German, etc. pass their own translations.
|
|
8
|
-
*
|
|
9
|
-
* Ported from `@1click/ui/src/cart-drawer/labels.ts` (v2.3.1).
|
|
10
|
-
* additions: the summary-breakdown row labels (`discount`, `tax`, `
|
|
11
|
-
* `giftCard`, `remainingToPay`) — the decorated cart carries
|
|
12
|
-
* discount/tax/COD-fee/gift-card tender fields that the breakdown renders,
|
|
13
|
-
* so the copy for those rows lives here too.
|
|
14
|
-
*/
|
|
15
|
-
|
|
16
|
-
export type CartDrawerLabels = {
|
|
17
|
-
// Header
|
|
18
|
-
yourCart: string
|
|
19
|
-
item: string
|
|
20
|
-
items: string
|
|
21
|
-
closeCart: string
|
|
22
|
-
|
|
23
|
-
// Empty state
|
|
24
|
-
emptyCartTitle: string
|
|
25
|
-
emptyCartMessage: string
|
|
26
|
-
browseProducts: string
|
|
27
|
-
|
|
28
|
-
// Tiered progress
|
|
29
|
-
awayFromFreeShipping: string
|
|
30
|
-
allRewardsUnlocked: string
|
|
31
|
-
|
|
32
|
-
// Gift wrap / notes
|
|
33
|
-
addGiftWrap: string
|
|
34
|
-
giftNote: string
|
|
35
|
-
orderNote: string
|
|
36
|
-
giftNotePlaceholder: string
|
|
37
|
-
orderNotePlaceholder: string
|
|
38
|
-
|
|
39
|
-
// Rewards
|
|
40
|
-
estimatedRewardPoints: string
|
|
41
|
-
|
|
42
|
-
// Cross-sell
|
|
43
|
-
pairsWellWith: string
|
|
44
|
-
youllLoveThis: string
|
|
45
|
-
youMightAlsoLike: string
|
|
46
|
-
addToCart: string
|
|
47
|
-
|
|
48
|
-
// Summary / footer
|
|
49
|
-
subtotal: string
|
|
50
|
-
taxAndShipping: string
|
|
51
|
-
shipping: string
|
|
52
|
-
free: string
|
|
53
|
-
calculatedAtCheckout: string
|
|
54
|
-
total: string
|
|
55
|
-
secureCheckout: string
|
|
56
|
-
shippingAndTaxCalculatedAtCheckout: string
|
|
57
|
-
|
|
58
|
-
// Summary breakdown rows (
|
|
59
|
-
discount: string
|
|
60
|
-
tax: string
|
|
61
|
-
/** Fallback only — the server's `
|
|
62
|
-
|
|
63
|
-
/** Prefix for an applied gift-card row; rendered as `<giftCard> ••1234`. */
|
|
64
|
-
giftCard: string
|
|
65
|
-
/** Label for `gift_card_remainder` — what the remainder provider charges. */
|
|
66
|
-
remainingToPay: string
|
|
67
|
-
|
|
68
|
-
// Continue shopping
|
|
69
|
-
continueShopping: string
|
|
70
|
-
|
|
71
|
-
// A11y / buttons
|
|
72
|
-
remove: string
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
export const defaultCartDrawerLabels: CartDrawerLabels = {
|
|
76
|
-
yourCart: "Your cart",
|
|
77
|
-
item: "item",
|
|
78
|
-
items: "items",
|
|
79
|
-
closeCart: "Close cart",
|
|
80
|
-
|
|
81
|
-
emptyCartTitle: "Your cart is empty",
|
|
82
|
-
emptyCartMessage: "You haven't added any products yet.",
|
|
83
|
-
browseProducts: "Browse products",
|
|
84
|
-
|
|
85
|
-
awayFromFreeShipping: "away from free shipping",
|
|
86
|
-
allRewardsUnlocked: "All rewards unlocked",
|
|
87
|
-
|
|
88
|
-
addGiftWrap: "Add gift wrapping",
|
|
89
|
-
giftNote: "Gift note",
|
|
90
|
-
orderNote: "Order note",
|
|
91
|
-
giftNotePlaceholder: "Add a personal message...",
|
|
92
|
-
orderNotePlaceholder: "Special instructions for this order...",
|
|
93
|
-
|
|
94
|
-
estimatedRewardPoints: "Estimated reward points",
|
|
95
|
-
|
|
96
|
-
pairsWellWith: "Pairs well with",
|
|
97
|
-
youllLoveThis: "You'll love this",
|
|
98
|
-
youMightAlsoLike: "You might also like",
|
|
99
|
-
addToCart: "Add",
|
|
100
|
-
|
|
101
|
-
subtotal: "Subtotal",
|
|
102
|
-
taxAndShipping: "VAT included. Shipping calculated at checkout.",
|
|
103
|
-
shipping: "Shipping",
|
|
104
|
-
free: "FREE",
|
|
105
|
-
calculatedAtCheckout: "Calculated at checkout",
|
|
106
|
-
total: "Total",
|
|
107
|
-
secureCheckout: "Checkout",
|
|
108
|
-
shippingAndTaxCalculatedAtCheckout: "Shipping and tax calculated at checkout",
|
|
109
|
-
|
|
110
|
-
discount: "Discount",
|
|
111
|
-
tax: "VAT",
|
|
112
|
-
|
|
113
|
-
giftCard: "Gift card",
|
|
114
|
-
remainingToPay: "Remaining to pay",
|
|
115
|
-
|
|
116
|
-
continueShopping: "Continue shopping",
|
|
117
|
-
|
|
118
|
-
remove: "Remove",
|
|
119
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Cart drawer labels — default English copy.
|
|
3
|
+
*
|
|
4
|
+
* Every user-facing string in the cart drawer comes from a `CartDrawerLabels`
|
|
5
|
+
* object. Stores override strings by passing a partial labels object to
|
|
6
|
+
* `CartDrawerProvider`. English is the baseline; stores wanting Bulgarian,
|
|
7
|
+
* German, etc. pass their own translations.
|
|
8
|
+
*
|
|
9
|
+
* Ported from `@1click/ui/src/cart-drawer/labels.ts` (v2.3.1). Cartbase
|
|
10
|
+
* additions: the summary-breakdown row labels (`discount`, `tax`, `paymentMethodFee`,
|
|
11
|
+
* `giftCard`, `remainingToPay`) — the decorated cart carries
|
|
12
|
+
* discount/tax/COD-fee/gift-card tender fields that the breakdown renders,
|
|
13
|
+
* so the copy for those rows lives here too.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
export type CartDrawerLabels = {
|
|
17
|
+
// Header
|
|
18
|
+
yourCart: string
|
|
19
|
+
item: string
|
|
20
|
+
items: string
|
|
21
|
+
closeCart: string
|
|
22
|
+
|
|
23
|
+
// Empty state
|
|
24
|
+
emptyCartTitle: string
|
|
25
|
+
emptyCartMessage: string
|
|
26
|
+
browseProducts: string
|
|
27
|
+
|
|
28
|
+
// Tiered progress
|
|
29
|
+
awayFromFreeShipping: string
|
|
30
|
+
allRewardsUnlocked: string
|
|
31
|
+
|
|
32
|
+
// Gift wrap / notes
|
|
33
|
+
addGiftWrap: string
|
|
34
|
+
giftNote: string
|
|
35
|
+
orderNote: string
|
|
36
|
+
giftNotePlaceholder: string
|
|
37
|
+
orderNotePlaceholder: string
|
|
38
|
+
|
|
39
|
+
// Rewards
|
|
40
|
+
estimatedRewardPoints: string
|
|
41
|
+
|
|
42
|
+
// Cross-sell
|
|
43
|
+
pairsWellWith: string
|
|
44
|
+
youllLoveThis: string
|
|
45
|
+
youMightAlsoLike: string
|
|
46
|
+
addToCart: string
|
|
47
|
+
|
|
48
|
+
// Summary / footer
|
|
49
|
+
subtotal: string
|
|
50
|
+
taxAndShipping: string
|
|
51
|
+
shipping: string
|
|
52
|
+
free: string
|
|
53
|
+
calculatedAtCheckout: string
|
|
54
|
+
total: string
|
|
55
|
+
secureCheckout: string
|
|
56
|
+
shippingAndTaxCalculatedAtCheckout: string
|
|
57
|
+
|
|
58
|
+
// Summary breakdown rows (Cartbase decorated-cart fields)
|
|
59
|
+
discount: string
|
|
60
|
+
tax: string
|
|
61
|
+
/** Fallback only — the server's `payment_method_fee_label` wins when present. */
|
|
62
|
+
paymentMethodFee: string
|
|
63
|
+
/** Prefix for an applied gift-card row; rendered as `<giftCard> ••1234`. */
|
|
64
|
+
giftCard: string
|
|
65
|
+
/** Label for `gift_card_remainder` — what the remainder provider charges. */
|
|
66
|
+
remainingToPay: string
|
|
67
|
+
|
|
68
|
+
// Continue shopping
|
|
69
|
+
continueShopping: string
|
|
70
|
+
|
|
71
|
+
// A11y / buttons
|
|
72
|
+
remove: string
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export const defaultCartDrawerLabels: CartDrawerLabels = {
|
|
76
|
+
yourCart: "Your cart",
|
|
77
|
+
item: "item",
|
|
78
|
+
items: "items",
|
|
79
|
+
closeCart: "Close cart",
|
|
80
|
+
|
|
81
|
+
emptyCartTitle: "Your cart is empty",
|
|
82
|
+
emptyCartMessage: "You haven't added any products yet.",
|
|
83
|
+
browseProducts: "Browse products",
|
|
84
|
+
|
|
85
|
+
awayFromFreeShipping: "away from free shipping",
|
|
86
|
+
allRewardsUnlocked: "All rewards unlocked",
|
|
87
|
+
|
|
88
|
+
addGiftWrap: "Add gift wrapping",
|
|
89
|
+
giftNote: "Gift note",
|
|
90
|
+
orderNote: "Order note",
|
|
91
|
+
giftNotePlaceholder: "Add a personal message...",
|
|
92
|
+
orderNotePlaceholder: "Special instructions for this order...",
|
|
93
|
+
|
|
94
|
+
estimatedRewardPoints: "Estimated reward points",
|
|
95
|
+
|
|
96
|
+
pairsWellWith: "Pairs well with",
|
|
97
|
+
youllLoveThis: "You'll love this",
|
|
98
|
+
youMightAlsoLike: "You might also like",
|
|
99
|
+
addToCart: "Add",
|
|
100
|
+
|
|
101
|
+
subtotal: "Subtotal",
|
|
102
|
+
taxAndShipping: "VAT included. Shipping calculated at checkout.",
|
|
103
|
+
shipping: "Shipping",
|
|
104
|
+
free: "FREE",
|
|
105
|
+
calculatedAtCheckout: "Calculated at checkout",
|
|
106
|
+
total: "Total",
|
|
107
|
+
secureCheckout: "Checkout",
|
|
108
|
+
shippingAndTaxCalculatedAtCheckout: "Shipping and tax calculated at checkout",
|
|
109
|
+
|
|
110
|
+
discount: "Discount",
|
|
111
|
+
tax: "VAT",
|
|
112
|
+
paymentMethodFee: "Payment method fee",
|
|
113
|
+
giftCard: "Gift card",
|
|
114
|
+
remainingToPay: "Remaining to pay",
|
|
115
|
+
|
|
116
|
+
continueShopping: "Continue shopping",
|
|
117
|
+
|
|
118
|
+
remove: "Remove",
|
|
119
|
+
}
|
|
@@ -1,196 +1,197 @@
|
|
|
1
|
-
"use client"
|
|
2
|
-
|
|
3
|
-
import type { AppliedGiftCard } from "../api/carts"
|
|
4
|
-
import { convertToLocale } from "../lib/money"
|
|
5
|
-
import { useCartDrawer } from "./context"
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* CartSummaryBreakdown — full totals breakdown for the drawer (or a cart
|
|
9
|
-
* page): subtotal, discount, shipping, tax, COD fee, total, applied
|
|
10
|
-
* gift-card tender and the remainder. Alternative to the compact
|
|
11
|
-
* `CartStickyFooter` (which deliberately shows a product-only subtotal in
|
|
12
|
-
* pre-checkout context).
|
|
13
|
-
*
|
|
14
|
-
* Ported from `@1click/ui/src/cart-drawer/summary-breakdown.tsx` (v2.3.1)
|
|
15
|
-
* and EXPANDED for
|
|
16
|
-
* subtotal only;
|
|
17
|
-
* every read (`subtotal`, `discount_total`, `shipping_total`, `tax_total`,
|
|
18
|
-
* `
|
|
19
|
-
* `gift_card_remainder`, `total` — all EUR major units), so this component
|
|
20
|
-
* renders EXACTLY what the server returns and computes no money. The only
|
|
21
|
-
* client-side transformation is the display sign on deductions.
|
|
22
|
-
*
|
|
23
|
-
* Row rules (see `selectSummaryRows`):
|
|
24
|
-
* - discount / tax /
|
|
25
|
-
* -
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
| { kind: "
|
|
52
|
-
| { kind: "
|
|
53
|
-
| { kind: "
|
|
54
|
-
| { kind: "
|
|
55
|
-
| { kind: "
|
|
56
|
-
| { kind: "
|
|
57
|
-
| { kind: "
|
|
58
|
-
| { kind: "
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
*
|
|
63
|
-
*
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
rows
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
//
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
const
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
<span
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
<span
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
<span
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
<span>{labels.
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
<span
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
<span
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
<span
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import type { AppliedGiftCard } from "../api/carts"
|
|
4
|
+
import { convertToLocale } from "../lib/money"
|
|
5
|
+
import { useCartDrawer } from "./context"
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* CartSummaryBreakdown — full totals breakdown for the drawer (or a cart
|
|
9
|
+
* page): subtotal, discount, shipping, tax, COD fee, total, applied
|
|
10
|
+
* gift-card tender and the remainder. Alternative to the compact
|
|
11
|
+
* `CartStickyFooter` (which deliberately shows a product-only subtotal in
|
|
12
|
+
* pre-checkout context).
|
|
13
|
+
*
|
|
14
|
+
* Ported from `@1click/ui/src/cart-drawer/summary-breakdown.tsx` (v2.3.1)
|
|
15
|
+
* and EXPANDED for Cartbase: the source rendered a client-summed product
|
|
16
|
+
* subtotal only; Cartbase's decorated cart carries server-computed totals on
|
|
17
|
+
* every read (`subtotal`, `discount_total`, `shipping_total`, `tax_total`,
|
|
18
|
+
* `payment_method_fee_total`/`payment_method_fee_label`, `gift_cards[]`, `gift_card_total`,
|
|
19
|
+
* `gift_card_remainder`, `total` — all EUR major units), so this component
|
|
20
|
+
* renders EXACTLY what the server returns and computes no money. The only
|
|
21
|
+
* client-side transformation is the display sign on deductions.
|
|
22
|
+
*
|
|
23
|
+
* Row rules (see `selectSummaryRows`):
|
|
24
|
+
* - discount / tax / method-fee rows appear only when their total is > 0.
|
|
25
|
+
* - the method fee renders the server's `payment_method_fee_label`
|
|
26
|
+
* (labels.paymentMethodFee fallback).
|
|
27
|
+
* - shipping renders the amount once a shipping method is set (0 → FREE);
|
|
28
|
+
* before that it shows "calculated at checkout".
|
|
29
|
+
* - gift-card tender renders one row per applied card (masked `last4`,
|
|
30
|
+
* negative display) plus the `gift_card_remainder` row — the amount the
|
|
31
|
+
* remainder provider actually charges. A depleted card stays listed at 0
|
|
32
|
+
* (server keeps it in `gift_cards` at amount 0).
|
|
33
|
+
*/
|
|
34
|
+
|
|
35
|
+
/** The decorated-cart fields the breakdown consumes (structural subset). */
|
|
36
|
+
export type SummaryCartTotals = {
|
|
37
|
+
subtotal: number
|
|
38
|
+
discount_total: number
|
|
39
|
+
shipping_total: number
|
|
40
|
+
tax_total: number
|
|
41
|
+
total: number
|
|
42
|
+
payment_method_fee_total?: number
|
|
43
|
+
payment_method_fee_label?: string | null
|
|
44
|
+
gift_cards?: AppliedGiftCard[]
|
|
45
|
+
gift_card_total?: number
|
|
46
|
+
gift_card_remainder?: number
|
|
47
|
+
shipping_methods?: ReadonlyArray<unknown>
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export type SummaryRow =
|
|
51
|
+
| { kind: "subtotal"; amount: number }
|
|
52
|
+
| { kind: "discount"; amount: number }
|
|
53
|
+
| { kind: "shipping"; amount: number }
|
|
54
|
+
| { kind: "shipping_pending" }
|
|
55
|
+
| { kind: "tax"; amount: number }
|
|
56
|
+
| { kind: "payment_method_fee"; amount: number; label: string | null }
|
|
57
|
+
| { kind: "total"; amount: number }
|
|
58
|
+
| { kind: "gift_card"; last4: string; amount: number }
|
|
59
|
+
| { kind: "remainder"; amount: number }
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Pure row selection over the decorated-cart totals — exported for unit
|
|
63
|
+
* tests. Amounts pass through verbatim (server truth); deduction rows
|
|
64
|
+
* (discount, gift cards) carry a negated amount for display only.
|
|
65
|
+
*/
|
|
66
|
+
export function selectSummaryRows(cart: SummaryCartTotals): SummaryRow[] {
|
|
67
|
+
const rows: SummaryRow[] = []
|
|
68
|
+
rows.push({ kind: "subtotal", amount: cart.subtotal })
|
|
69
|
+
|
|
70
|
+
if ((cart.discount_total ?? 0) > 0) {
|
|
71
|
+
rows.push({ kind: "discount", amount: -cart.discount_total })
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (cart.shipping_methods && cart.shipping_methods.length > 0) {
|
|
75
|
+
rows.push({ kind: "shipping", amount: cart.shipping_total })
|
|
76
|
+
} else {
|
|
77
|
+
rows.push({ kind: "shipping_pending" })
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if ((cart.tax_total ?? 0) > 0) {
|
|
81
|
+
rows.push({ kind: "tax", amount: cart.tax_total })
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if ((cart.payment_method_fee_total ?? 0) > 0) {
|
|
85
|
+
rows.push({
|
|
86
|
+
kind: "payment_method_fee",
|
|
87
|
+
amount: cart.payment_method_fee_total!,
|
|
88
|
+
label: cart.payment_method_fee_label ?? null,
|
|
89
|
+
})
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
rows.push({ kind: "total", amount: cart.total })
|
|
93
|
+
|
|
94
|
+
if (cart.gift_cards && cart.gift_cards.length > 0) {
|
|
95
|
+
for (const card of cart.gift_cards) {
|
|
96
|
+
// `|| 0` normalizes -0 (a depleted card at amount 0) so Intl never
|
|
97
|
+
// renders a stray "-€0.00".
|
|
98
|
+
rows.push({ kind: "gift_card", last4: card.last4, amount: -card.amount || 0 })
|
|
99
|
+
}
|
|
100
|
+
rows.push({ kind: "remainder", amount: cart.gift_card_remainder ?? 0 })
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return rows
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export function CartSummaryBreakdown() {
|
|
107
|
+
const { cart, labels } = useCartDrawer()
|
|
108
|
+
if (!cart) return null
|
|
109
|
+
|
|
110
|
+
const currencyCode = cart.currency_code
|
|
111
|
+
const money = (amount: number) =>
|
|
112
|
+
convertToLocale({ amount, currency_code: currencyCode })
|
|
113
|
+
const rows = selectSummaryRows(cart)
|
|
114
|
+
|
|
115
|
+
return (
|
|
116
|
+
<div className="px-6 py-5 space-y-2">
|
|
117
|
+
{rows.map((row, i) => {
|
|
118
|
+
switch (row.kind) {
|
|
119
|
+
case "subtotal":
|
|
120
|
+
return (
|
|
121
|
+
<div key={i} className="flex justify-between items-center text-[13px] text-muted-foreground">
|
|
122
|
+
<span>{labels.subtotal}</span>
|
|
123
|
+
<span className="tabular-nums">{money(row.amount)}</span>
|
|
124
|
+
</div>
|
|
125
|
+
)
|
|
126
|
+
case "discount":
|
|
127
|
+
return (
|
|
128
|
+
<div key={i} className="flex justify-between items-center text-[13px] text-success">
|
|
129
|
+
<span>{labels.discount}</span>
|
|
130
|
+
<span className="tabular-nums">{money(row.amount)}</span>
|
|
131
|
+
</div>
|
|
132
|
+
)
|
|
133
|
+
case "shipping":
|
|
134
|
+
return (
|
|
135
|
+
<div key={i} className="flex justify-between items-center text-[13px] text-muted-foreground">
|
|
136
|
+
<span>{labels.shipping}</span>
|
|
137
|
+
<span className="tabular-nums">
|
|
138
|
+
{row.amount === 0 ? labels.free : money(row.amount)}
|
|
139
|
+
</span>
|
|
140
|
+
</div>
|
|
141
|
+
)
|
|
142
|
+
case "shipping_pending":
|
|
143
|
+
return (
|
|
144
|
+
<div key={i} className="flex justify-between items-center text-[13px] text-muted-foreground">
|
|
145
|
+
<span>{labels.shipping}</span>
|
|
146
|
+
<span>{labels.calculatedAtCheckout}</span>
|
|
147
|
+
</div>
|
|
148
|
+
)
|
|
149
|
+
case "tax":
|
|
150
|
+
return (
|
|
151
|
+
<div key={i} className="flex justify-between items-center text-[13px] text-muted-foreground">
|
|
152
|
+
<span>{labels.tax}</span>
|
|
153
|
+
<span className="tabular-nums">{money(row.amount)}</span>
|
|
154
|
+
</div>
|
|
155
|
+
)
|
|
156
|
+
case "payment_method_fee":
|
|
157
|
+
return (
|
|
158
|
+
<div key={i} className="flex justify-between items-center text-[13px] text-muted-foreground">
|
|
159
|
+
<span>{row.label ?? labels.paymentMethodFee}</span>
|
|
160
|
+
<span className="tabular-nums">{money(row.amount)}</span>
|
|
161
|
+
</div>
|
|
162
|
+
)
|
|
163
|
+
case "total":
|
|
164
|
+
return (
|
|
165
|
+
<div
|
|
166
|
+
key={i}
|
|
167
|
+
className="flex justify-between items-center pt-2 mt-1 border-t border-border"
|
|
168
|
+
>
|
|
169
|
+
<span className="text-[15px] font-bold text-foreground">
|
|
170
|
+
{labels.total}
|
|
171
|
+
</span>
|
|
172
|
+
<span className="text-[17px] font-bold text-foreground tracking-tight tabular-nums">
|
|
173
|
+
{money(row.amount)}
|
|
174
|
+
</span>
|
|
175
|
+
</div>
|
|
176
|
+
)
|
|
177
|
+
case "gift_card":
|
|
178
|
+
return (
|
|
179
|
+
<div key={i} className="flex justify-between items-center text-[13px] text-success">
|
|
180
|
+
<span>
|
|
181
|
+
{labels.giftCard} ••{row.last4}
|
|
182
|
+
</span>
|
|
183
|
+
<span className="tabular-nums">{money(row.amount)}</span>
|
|
184
|
+
</div>
|
|
185
|
+
)
|
|
186
|
+
case "remainder":
|
|
187
|
+
return (
|
|
188
|
+
<div key={i} className="flex justify-between items-center text-[13px] font-semibold text-foreground">
|
|
189
|
+
<span>{labels.remainingToPay}</span>
|
|
190
|
+
<span className="tabular-nums">{money(row.amount)}</span>
|
|
191
|
+
</div>
|
|
192
|
+
)
|
|
193
|
+
}
|
|
194
|
+
})}
|
|
195
|
+
</div>
|
|
196
|
+
)
|
|
197
|
+
}
|