@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
package/src/api/checkout.ts
CHANGED
|
@@ -6,10 +6,10 @@
|
|
|
6
6
|
* 1. **Orchestrated (recommended)** — `prepareCheckout()` is the atomic
|
|
7
7
|
* Buy-click: ONE call writes address (+billing mirror), shipping method
|
|
8
8
|
* (+ carrier metadata), payment collection and the payment session at
|
|
9
|
-
* the FINAL amount (real Stripe PaymentIntent
|
|
10
|
-
*
|
|
11
|
-
* drift/repair between the two:
|
|
12
|
-
* `refreshPaymentIfTerminal()`.
|
|
9
|
+
* the FINAL amount (real Stripe PaymentIntent for a processor, or a
|
|
10
|
+
* provider-less row for a merchant method). Then `completeCart()`
|
|
11
|
+
* (see `./carts`). Amount drift/repair between the two:
|
|
12
|
+
* `syncPaymentAmount()` and `refreshPaymentIfTerminal()`.
|
|
13
13
|
*
|
|
14
14
|
* 2. **Manual (Medusa-style)** — `listShippingOptions()` →
|
|
15
15
|
* `addShippingMethod()` → `createPaymentCollection()` →
|
|
@@ -137,28 +137,54 @@ export interface ListPaymentProvidersQuery {
|
|
|
137
137
|
cart_id?: string
|
|
138
138
|
}
|
|
139
139
|
|
|
140
|
-
/**
|
|
141
|
-
export interface
|
|
142
|
-
/** e.g. `pp_stripe
|
|
140
|
+
/** A connected PROCESSOR entry of GET /api/store/payment-providers. */
|
|
141
|
+
export interface StorePaymentProcessorEntry {
|
|
142
|
+
/** e.g. `pp_stripe`. `pp_giftcard` is internal and never listed. */
|
|
143
143
|
id: string
|
|
144
144
|
is_enabled: boolean
|
|
145
145
|
created_at?: string
|
|
146
146
|
}
|
|
147
147
|
|
|
148
|
+
/** A merchant PAYMENT METHOD entry (pp_* kill, 20260811250000): methods
|
|
149
|
+
* stand alone — no provider id anywhere. Initiate sessions with
|
|
150
|
+
* `payment_method_id`; `kind === "cod"` marks the pre-created
|
|
151
|
+
* collection-on-delivery method (courier cash collection); `instructions`
|
|
152
|
+
* is the merchant's checkout note, rendered under the method. */
|
|
153
|
+
export interface StorePaymentMethodEntry {
|
|
154
|
+
payment_method_id: string
|
|
155
|
+
name: string
|
|
156
|
+
kind: "manual" | "cod"
|
|
157
|
+
instructions: string | null
|
|
158
|
+
/** The method's fee (any method may carry one) — predict
|
|
159
|
+
* `payment_method_fee_total` from it before a session exists. */
|
|
160
|
+
fee_amount: number | null
|
|
161
|
+
fee_label: string | null
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/** Row of GET /api/store/payment-providers — a processor or a method. */
|
|
165
|
+
export type StorePaymentEntry = StorePaymentProcessorEntry | StorePaymentMethodEntry
|
|
166
|
+
|
|
167
|
+
/** True when the entry is a merchant payment method (vs a processor). */
|
|
168
|
+
export const isMethodEntry = (
|
|
169
|
+
entry: StorePaymentEntry
|
|
170
|
+
): entry is StorePaymentMethodEntry => "payment_method_id" in entry
|
|
171
|
+
|
|
148
172
|
export interface PaymentProviderListResponse extends ListEnvelope {
|
|
149
|
-
payment_providers:
|
|
173
|
+
payment_providers: StorePaymentEntry[]
|
|
150
174
|
}
|
|
151
175
|
|
|
152
176
|
/**
|
|
153
|
-
* List
|
|
154
|
-
*
|
|
155
|
-
*
|
|
156
|
-
*
|
|
157
|
-
*
|
|
177
|
+
* List everything payable (rule-filtered, display-ordered): connected
|
|
178
|
+
* processors + every ENABLED merchant method. → 200 list envelope;
|
|
179
|
+
* `count`/`limit` = full filtered list (no pagination). An empty list is
|
|
180
|
+
* honest: a fresh store cannot take money until the merchant connects a
|
|
181
|
+
* processor, flips the COD switch, or creates a manual method
|
|
182
|
+
* (no-payment-method-by-default).
|
|
158
183
|
* Auth: anon `x-client-id`.
|
|
159
|
-
* Settings:
|
|
160
|
-
*
|
|
161
|
-
*
|
|
184
|
+
* Settings: processor enablement (admin integrations), the Payments
|
|
185
|
+
* settings methods manager, checkout rules (`target_type=payment_method`,
|
|
186
|
+
* methods participate under their `payment_method_id`) +
|
|
187
|
+
* `checkout_method_order`.
|
|
162
188
|
*/
|
|
163
189
|
export async function listPaymentProviders(
|
|
164
190
|
client: StorefrontClient,
|
|
@@ -213,12 +239,18 @@ export async function createPaymentCollection(
|
|
|
213
239
|
}
|
|
214
240
|
|
|
215
241
|
export interface InitiatePaymentSessionInput {
|
|
216
|
-
/**
|
|
217
|
-
|
|
242
|
+
/** A connected processor (`pp_stripe`) — NEVER `pp_giftcard` (→ 400
|
|
243
|
+
* `invalid_provider`). Exactly ONE of `provider_id` /
|
|
244
|
+
* `payment_method_id` (→ 400 `invalid_data` otherwise). */
|
|
245
|
+
provider_id?: string
|
|
246
|
+
/** A merchant payment method (`payment_method_id` from the listing's
|
|
247
|
+
* method entries) — the session lands with `provider_id` NULL and the
|
|
248
|
+
* method snapshot (`payment_method_id`/`_name`/`_kind`) in `data`. */
|
|
249
|
+
payment_method_id?: string
|
|
218
250
|
/**
|
|
219
|
-
* Merged into the session's `data` blob.
|
|
251
|
+
* Merged into the session's `data` blob. Server-owned keys
|
|
220
252
|
* (`payment_intent_id`, `client_secret`, `status`, `stripe_customer_id`,
|
|
221
|
-
* `setup_future_usage
|
|
253
|
+
* `setup_future_usage`, and the method snapshot keys) are stripped.
|
|
222
254
|
*/
|
|
223
255
|
data?: Record<string, unknown>
|
|
224
256
|
/**
|
|
@@ -233,16 +265,19 @@ export interface InitiatePaymentSessionInput {
|
|
|
233
265
|
|
|
234
266
|
export interface StorePaymentSession {
|
|
235
267
|
id: string
|
|
236
|
-
|
|
237
|
-
|
|
268
|
+
/** The processor, or NULL for a merchant-method session (the method
|
|
269
|
+
* snapshot lives in `data`). */
|
|
270
|
+
provider_id: string | null
|
|
271
|
+
/** `collection.amount − gift_card_total` — the remainder this tender charges. */
|
|
238
272
|
amount: MajorUnitAmount
|
|
239
273
|
currency_code: string
|
|
240
274
|
/** `pending` until authorized at complete. */
|
|
241
275
|
status: string
|
|
242
276
|
authorized_at: string | null
|
|
243
277
|
/**
|
|
244
|
-
*
|
|
245
|
-
* (mount Stripe Elements with it), `status`.
|
|
278
|
+
* Tender blob. For Stripe: `payment_intent_id`, `client_secret`
|
|
279
|
+
* (mount Stripe Elements with it), `status`. For methods: the snapshot
|
|
280
|
+
* `payment_method_id` / `payment_method_name` / `payment_method_kind`.
|
|
246
281
|
*/
|
|
247
282
|
data: Record<string, unknown> | null
|
|
248
283
|
[key: string]: unknown
|
|
@@ -260,10 +295,11 @@ export interface InitiatePaymentSessionResponse {
|
|
|
260
295
|
* in place; terminal PIs self-heal by rotation.
|
|
261
296
|
* Auth: anon `x-client-id`.
|
|
262
297
|
* Errors: 404 `payment_collection_not_found`, 400 `invalid_provider`
|
|
263
|
-
* (pp_giftcard) | `
|
|
298
|
+
* (pp_giftcard / unknown processor) | `invalid_data` (tender XOR violated,
|
|
299
|
+
* unknown/disabled method) | `stripe_not_configured` | `validation_failed`.
|
|
264
300
|
* Settings: Stripe credentials (admin integrations); gift-card tender
|
|
265
|
-
* shrinks the session amount; COD fee appears on the cart the moment a
|
|
266
|
-
*
|
|
301
|
+
* shrinks the session amount; the COD fee appears on the cart the moment a
|
|
302
|
+
* live COD-method session exists.
|
|
267
303
|
*/
|
|
268
304
|
export async function initiatePaymentSession(
|
|
269
305
|
client: StorefrontClient,
|
|
@@ -312,28 +348,37 @@ export interface PrepareCheckoutInput {
|
|
|
312
348
|
* switching carriers never leaks stale fields into the order.
|
|
313
349
|
*/
|
|
314
350
|
carrier_metadata?: Record<string, unknown>
|
|
315
|
-
/**
|
|
316
|
-
|
|
351
|
+
/** A connected processor (`pp_stripe`) — never `pp_giftcard`. Exactly ONE
|
|
352
|
+
* of `payment_provider` / `payment_method_id`. */
|
|
353
|
+
payment_provider?: string
|
|
354
|
+
/** A merchant payment method — the session lands provider-less with the
|
|
355
|
+
* method snapshot (pp_* kill, 20260811250000). */
|
|
356
|
+
payment_method_id?: string
|
|
317
357
|
}
|
|
318
358
|
|
|
319
359
|
/** Response of prepare-checkout (verbatim src/lib/checkout-orchestration/prepare.ts). */
|
|
320
360
|
export interface PrepareCheckoutResult {
|
|
321
361
|
cart_id: string
|
|
322
362
|
payment_collection_id: string | null
|
|
323
|
-
/** Stripe Elements secret; null for
|
|
363
|
+
/** Stripe Elements secret; null for method sessions and zero-remainder carts. */
|
|
324
364
|
client_secret: string | null
|
|
325
|
-
/** The
|
|
365
|
+
/** The processor whose session was minted; null for method sessions and
|
|
366
|
+
* when zero-remainder skipped the session. */
|
|
326
367
|
provider_id: string | null
|
|
368
|
+
/** The method whose session was minted; null for processor sessions and
|
|
369
|
+
* when zero-remainder skipped the session. */
|
|
370
|
+
payment_method_id: string | null
|
|
327
371
|
}
|
|
328
372
|
|
|
329
373
|
/**
|
|
330
374
|
* Atomic Buy-click step 1 of 2. ONE call writes address (+billing mirror) +
|
|
331
375
|
* carrier metadata → shipping method (priced against the new destination) →
|
|
332
376
|
* payment collection at the shipped total → payment session at the FINAL
|
|
333
|
-
* amount. For
|
|
334
|
-
* amounts are re-synced after it. Zero-remainder carts (gift
|
|
335
|
-
* the total) skip the
|
|
336
|
-
* come back null and the cart
|
|
377
|
+
* amount. For the COD method the native fee applies once the session
|
|
378
|
+
* exists, so amounts are re-synced after it. Zero-remainder carts (gift
|
|
379
|
+
* cards cover the total) skip the tender session — `client_secret`,
|
|
380
|
+
* `provider_id` and `payment_method_id` come back null and the cart
|
|
381
|
+
* completes on the gift session.
|
|
337
382
|
*
|
|
338
383
|
* Fully compensated (wire-pattern): any failure rolls back session →
|
|
339
384
|
* collection → shipping method → addresses/metadata to the pre-call
|
|
@@ -341,13 +386,14 @@ export interface PrepareCheckoutResult {
|
|
|
341
386
|
* `checkout_error_logs` (step `prepare-checkout`).
|
|
342
387
|
*
|
|
343
388
|
* After prepare: Stripe → `stripe.confirmPayment(client_secret)` →
|
|
344
|
-
* `completeCart()`;
|
|
389
|
+
* `completeCart()`; merchant methods → `completeCart()` directly.
|
|
345
390
|
*
|
|
346
391
|
* Auth: anon `x-client-id`.
|
|
347
392
|
* Errors: 404 `cart_not_found` | `shipping_option_not_found`, 409
|
|
348
393
|
* `cart_completed`, 400 `validation_failed` | `invalid_provider` |
|
|
349
|
-
* `
|
|
350
|
-
*
|
|
394
|
+
* `invalid_data` (tender XOR violated) | `shipping_price_missing` |
|
|
395
|
+
* `stripe_not_configured`.
|
|
396
|
+
* Settings: Stripe credentials, the COD method's fee, gift-card tender,
|
|
351
397
|
* checkout rules (enforced at listings + complete, not here).
|
|
352
398
|
*/
|
|
353
399
|
export async function prepareCheckout(
|
|
@@ -358,10 +404,14 @@ export async function prepareCheckout(
|
|
|
358
404
|
return client.post(`/api/store/carts/${cartId}/prepare-checkout`, input)
|
|
359
405
|
}
|
|
360
406
|
|
|
361
|
-
/** Body of POST /api/store/carts/:id/sync-payment-amount (`.strict()`).
|
|
407
|
+
/** Body of POST /api/store/carts/:id/sync-payment-amount (`.strict()`).
|
|
408
|
+
* At most ONE of the two — the requested tender; omit both to sync
|
|
409
|
+
* whatever session is pending. */
|
|
362
410
|
export interface SyncPaymentAmountInput {
|
|
363
|
-
/** Rotate to this
|
|
411
|
+
/** Rotate to this processor when the pending session's tender differs. */
|
|
364
412
|
provider_id?: string
|
|
413
|
+
/** Rotate to this merchant method when the pending session's tender differs. */
|
|
414
|
+
payment_method_id?: string
|
|
365
415
|
}
|
|
366
416
|
|
|
367
417
|
/** Response matrix (verbatim src/lib/checkout-orchestration/sync.ts). */
|
|
@@ -371,6 +421,7 @@ export interface SyncPaymentAmountResult {
|
|
|
371
421
|
rotated?: boolean
|
|
372
422
|
client_secret?: string | null
|
|
373
423
|
provider_id?: string | null
|
|
424
|
+
payment_method_id?: string | null
|
|
374
425
|
/** No-op reasons: `cart-completed` | `no_payment_collection` | `no_pending_session`. */
|
|
375
426
|
reason?: string
|
|
376
427
|
}
|
package/src/api/consent.ts
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* fetch — resolve the config server-side (RSC) and inline it. Render the
|
|
11
11
|
* built-in banner only when `enabled && mode === "builtin"`. In `external`
|
|
12
12
|
* mode the merchant's CMP owns the UI and must write the same `_1c_consent`
|
|
13
|
-
* cookie (or call `setConsent()`) — all
|
|
13
|
+
* cookie (or call `setConsent()`) — all Cartbase-side tag gating works off
|
|
14
14
|
* that one seam. Choices persist 12 months.
|
|
15
15
|
*/
|
|
16
16
|
|
|
@@ -45,7 +45,7 @@ export interface ConsentCopy {
|
|
|
45
45
|
*/
|
|
46
46
|
export interface ConsentConfig {
|
|
47
47
|
enabled: boolean
|
|
48
|
-
/** `builtin` =
|
|
48
|
+
/** `builtin` = Cartbase renders the banner; `external` = merchant CMP owns UI. */
|
|
49
49
|
mode: "builtin" | "external"
|
|
50
50
|
/** `modal` = blocking centered modal; `banner-bottom` = non-blocking bar. */
|
|
51
51
|
layout: "modal" | "banner-bottom"
|
package/src/api/integrations.ts
CHANGED
|
@@ -7,9 +7,10 @@
|
|
|
7
7
|
* src/lib/tracking/store-config-block.ts.
|
|
8
8
|
*
|
|
9
9
|
* The config payload is COMPOSED from an ordered block registry — each
|
|
10
|
-
* block owns distinct top-level keys (`carriers`, `
|
|
11
|
-
*
|
|
12
|
-
*
|
|
10
|
+
* block owns distinct top-level keys (`carriers`, `tracking` today; future
|
|
11
|
+
* blocks append). NOT wrapped in an envelope: the blocks ARE the top-level
|
|
12
|
+
* keys. (The `cod` block died with the 'cod' integration, 2026-08-11 —
|
|
13
|
+
* method fees now ride the payment listing entries themselves.)
|
|
13
14
|
*
|
|
14
15
|
* SECURITY LAW: every block is an explicit allowlist — credentials
|
|
15
16
|
* (carrier API keys, CAPI access_token, GA4 api_secret, Klaviyo
|
|
@@ -33,19 +34,6 @@ export interface PublicCarrierConfig {
|
|
|
33
34
|
lockers_url?: string
|
|
34
35
|
}
|
|
35
36
|
|
|
36
|
-
/** COD checkout block — null unless enabled AND fee_amount > 0 (the exact
|
|
37
|
-
* gate the totals engine applies, so the optimistic fee row never disagrees
|
|
38
|
-
* with the charged total). */
|
|
39
|
-
export interface PublicCodConfig {
|
|
40
|
-
enabled: true
|
|
41
|
-
/** EUR major units. */
|
|
42
|
-
fee_amount: number
|
|
43
|
-
fee_currency: "eur"
|
|
44
|
-
fee_label: string
|
|
45
|
-
/** Admin's checkout note, or null. */
|
|
46
|
-
description: string | null
|
|
47
|
-
}
|
|
48
|
-
|
|
49
37
|
/** Public tag config — only ENABLED providers with a public id appear.
|
|
50
38
|
* Secrets can never appear here (explicit allowlist). */
|
|
51
39
|
export interface StoreTrackingBlock {
|
|
@@ -64,7 +52,6 @@ export interface StoreIntegrationsConfig {
|
|
|
64
52
|
/** Keyed by provider slug (e.g. `boxnow`, `econt`). Disabled carriers are
|
|
65
53
|
* ABSENT, never `enabled: false`. */
|
|
66
54
|
carriers: Record<string, PublicCarrierConfig>
|
|
67
|
-
cod: PublicCodConfig | null
|
|
68
55
|
tracking: StoreTrackingBlock
|
|
69
56
|
}
|
|
70
57
|
|
package/src/api/regions.ts
CHANGED
|
@@ -10,7 +10,7 @@ import type { StorefrontClient } from "./http"
|
|
|
10
10
|
import type { IsoDateString, ListEnvelope, PaginationQuery } from "./types"
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
|
-
* A store region (`barter_commerce.regions` row). NOTE:
|
|
13
|
+
* A store region (`barter_commerce.regions` row). NOTE: Cartbase regions carry
|
|
14
14
|
* NO embedded `countries` array on the store surface (divergence from
|
|
15
15
|
* Medusa's Store API — tax scoping is by `tax_regions` server-side).
|
|
16
16
|
*/
|
|
@@ -43,7 +43,7 @@ import { defaultCartDrawerLabels, type CartDrawerLabels } from "./labels"
|
|
|
43
43
|
* snapshot never updated past the failed mutation. See
|
|
44
44
|
* https://react.dev/reference/react/useOptimistic.
|
|
45
45
|
*
|
|
46
|
-
* Ported from `@1click/ui/src/cart-drawer/context.tsx` (v2.3.1).
|
|
46
|
+
* Ported from `@1click/ui/src/cart-drawer/context.tsx` (v2.3.1). Cartbase
|
|
47
47
|
* data seam: the Medusa server actions (`../data/cart`) are replaced by
|
|
48
48
|
* `@cartbase/storefront/api/carts` calls made through a `StorefrontClient`
|
|
49
49
|
* passed to the provider. Every mutation returns the FULL decorated cart
|
|
@@ -54,7 +54,7 @@ import { defaultCartDrawerLabels, type CartDrawerLabels } from "./labels"
|
|
|
54
54
|
* is unit-agnostic and unchanged).
|
|
55
55
|
*
|
|
56
56
|
* The source's `logEvent` ops funnel (backend error log endpoint) has no
|
|
57
|
-
*
|
|
57
|
+
* Cartbase SDK equivalent yet — the same single-funnel discipline is kept
|
|
58
58
|
* via the `onOptimisticError` prop: every failed optimistic mutation
|
|
59
59
|
* (every PDP add, cross-sell add, drawer remove, drawer quantity change)
|
|
60
60
|
* flows through it with per-action context. Wire it to the store's ops
|
|
@@ -158,7 +158,7 @@ type CartDrawerContextValue = {
|
|
|
158
158
|
quantity?: number,
|
|
159
159
|
display?: OptimisticAddDisplay
|
|
160
160
|
) => Promise<boolean>
|
|
161
|
-
/** POST line-items/:id with `{quantity}` —
|
|
161
|
+
/** POST line-items/:id with `{quantity}` — Cartbase contract: 0 deletes. */
|
|
162
162
|
updateQuantity: (lineId: string, quantity: number) => Promise<boolean>
|
|
163
163
|
removeItem: (lineId: string) => Promise<boolean>
|
|
164
164
|
/** Re-read the decorated cart (totals/gift-card tender are live per read). */
|
|
@@ -503,7 +503,7 @@ export function CartDrawerProvider({
|
|
|
503
503
|
return runOptimistic(
|
|
504
504
|
{ type: "update_quantity", lineId, quantity },
|
|
505
505
|
async () => {
|
|
506
|
-
//
|
|
506
|
+
// Cartbase contract: body is `{quantity}` only (metadata is NOT
|
|
507
507
|
// accepted on update, only on add); quantity 0 deletes the line.
|
|
508
508
|
const res = await sdkUpdateLineItem(client, id, lineId, { quantity })
|
|
509
509
|
confirm(res.cart)
|
|
@@ -14,7 +14,7 @@ import { useCartDrawer } from "./context"
|
|
|
14
14
|
* cross-sell zone below the line items on desktop.
|
|
15
15
|
*
|
|
16
16
|
* Ported from `@1click/ui/src/cart-drawer/cross-sell-carousel.tsx` (v2.3.1).
|
|
17
|
-
*
|
|
17
|
+
* Cartbase data seam: same as the sidebar — feed from
|
|
18
18
|
* `@cartbase/storefront/api/products` `listProducts` or `api/search`
|
|
19
19
|
* `listRelatedProducts` via `toCrossSellProduct`
|
|
20
20
|
* (`./cross-sell-sidebar`; the card shapes are identical), wire `onAdd`
|
|
@@ -12,7 +12,7 @@ import { useCartDrawer } from "./context"
|
|
|
12
12
|
* drawer's left sidebar.
|
|
13
13
|
*
|
|
14
14
|
* Ported from `@1click/ui/src/cart-drawer/cross-sell-sidebar.tsx` (v2.3.1).
|
|
15
|
-
*
|
|
15
|
+
* Cartbase data seam: source stores fed this from Medusa product fetches —
|
|
16
16
|
* feed it from `@cartbase/storefront/api/products` `listProducts` (curated
|
|
17
17
|
* collection/tag) or `@cartbase/storefront/api/search` `listRelatedProducts`
|
|
18
18
|
* (anchor-product complements), mapped through `toCrossSellProduct` below.
|
|
@@ -6,7 +6,7 @@ import { useCartDrawer } from "./context"
|
|
|
6
6
|
/**
|
|
7
7
|
* Cart drawer header — store title, item count badge, close button.
|
|
8
8
|
*
|
|
9
|
-
* Ported from `@1click/ui/src/cart-drawer/header.tsx` (v2.3.1).
|
|
9
|
+
* Ported from `@1click/ui/src/cart-drawer/header.tsx` (v2.3.1). Cartbase
|
|
10
10
|
* adjustment: the badge counts PRODUCT lines via the shared
|
|
11
11
|
* `productItemCount` predicate (the source summed all lines) so a
|
|
12
12
|
* backend-injected COD-fee line can never inflate the item count —
|
|
@@ -15,13 +15,13 @@ import { CartItemVariant } from "./variant"
|
|
|
15
15
|
* CartItem — a single cart line row: thumbnail, title, variant, quantity
|
|
16
16
|
* stepper, price (with dual-currency + optional strikethrough), remove button.
|
|
17
17
|
*
|
|
18
|
-
* Ported from `@1click/ui/src/cart-drawer/item/index.tsx` (v2.3.1).
|
|
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`
|
|
20
20
|
* (`@cartbase/storefront/api/carts`) — per-line `total`/`original_total` are
|
|
21
21
|
* server-computed EUR major units. Remove goes through the provider's
|
|
22
22
|
* SDK-wired `removeItem` (DELETE line-items/:id, returns the full decorated
|
|
23
23
|
* cart) instead of the Medusa server action. The variant row renders the
|
|
24
|
-
* line's flat `variant_title` —
|
|
24
|
+
* line's flat `variant_title` — Cartbase cart lines carry the variant title
|
|
25
25
|
* string, not an embedded variant object.
|
|
26
26
|
*
|
|
27
27
|
* `children` is rendered below the main row — intended for per-item upsell
|
|
@@ -15,9 +15,9 @@ import { useCartDrawer } from "../context"
|
|
|
15
15
|
* automatically (confirmed snapshot unchanged).
|
|
16
16
|
*
|
|
17
17
|
* Ported from `@1click/ui/src/cart-drawer/item/quantity.tsx` (v2.3.1).
|
|
18
|
-
*
|
|
18
|
+
* Cartbase data seam: `updateQuantity` wraps
|
|
19
19
|
* `POST /api/store/carts/:id/line-items/:lineId` with body `{quantity}` —
|
|
20
|
-
* the
|
|
20
|
+
* the Cartbase contract accepts ONLY `quantity` (no metadata on update) and
|
|
21
21
|
* quantity 0 deletes the line (unreachable here: the stepper floor is 1;
|
|
22
22
|
* removal is the dedicated remove button).
|
|
23
23
|
*/
|
|
@@ -12,12 +12,12 @@ import { useCartDrawer } from "../context"
|
|
|
12
12
|
* onAdd handler.
|
|
13
13
|
*
|
|
14
14
|
* Ported from `@1click/ui/src/cart-drawer/item/upsell.tsx` (v2.3.1).
|
|
15
|
-
*
|
|
15
|
+
* Cartbase data seam: source stores fed this from Medusa product fetches —
|
|
16
16
|
* feed it from `@cartbase/storefront/api/search` `listRelatedProducts`
|
|
17
17
|
* (per-anchor complements: manual admin picks + deterministic fallback)
|
|
18
18
|
* mapped via `toCrossSellProduct` (`../cross-sell-sidebar`). `variantId`
|
|
19
19
|
* was added to `UpsellProduct` and to the `onAdd` signature so the handler
|
|
20
|
-
* can call the provider's `addItem(variantId)` directly —
|
|
20
|
+
* can call the provider's `addItem(variantId)` directly — Cartbase carts add
|
|
21
21
|
* by variant id, not product id. Prices are EUR major units.
|
|
22
22
|
*/
|
|
23
23
|
|
|
@@ -28,7 +28,7 @@ export type UpsellProduct = {
|
|
|
28
28
|
thumbnail: string | null
|
|
29
29
|
price: number
|
|
30
30
|
currencyCode: string
|
|
31
|
-
/** Variant to add (
|
|
31
|
+
/** Variant to add (Cartbase add-to-cart is by variant id). */
|
|
32
32
|
variantId?: string
|
|
33
33
|
}
|
|
34
34
|
|
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
* under the product title in a cart line.
|
|
6
6
|
*
|
|
7
7
|
* Ported from `@1click/ui/src/cart-drawer/item/variant.tsx` (v2.3.1).
|
|
8
|
-
*
|
|
9
|
-
* embedded Medusa variant object;
|
|
8
|
+
* Cartbase data seam: the source rendered `variant.options[].value` from an
|
|
9
|
+
* embedded Medusa variant object; Cartbase cart lines carry the flat
|
|
10
10
|
* `variant_title` string (SDK `CartLineItem.variant_title`) instead, so
|
|
11
11
|
* that is the primary prop. Callers that fetched the full variant (PDP
|
|
12
12
|
* context) can still pass structural `options` and get the original
|
|
@@ -6,7 +6,7 @@ import type { CartDrawerLabels } from "./labels"
|
|
|
6
6
|
* Ships alongside the English defaults so Bulgarian stores get the
|
|
7
7
|
* translated copy with a single import instead of hand-writing every
|
|
8
8
|
* string. Strings match the originals from mindpages-storefront, plus the
|
|
9
|
-
*
|
|
9
|
+
* Cartbase summary-breakdown row additions.
|
|
10
10
|
*
|
|
11
11
|
* Usage in a store's layout:
|
|
12
12
|
*
|
|
@@ -62,7 +62,7 @@ export const bulgarianCartDrawerLabels: CartDrawerLabels = {
|
|
|
62
62
|
|
|
63
63
|
discount: "Отстъпка",
|
|
64
64
|
tax: "ДДС",
|
|
65
|
-
|
|
65
|
+
paymentMethodFee: "Такса за плащане",
|
|
66
66
|
giftCard: "Подаръчна карта",
|
|
67
67
|
remainingToPay: "Остава за плащане",
|
|
68
68
|
|