@reblu/site-blocks 0.5.0 → 0.6.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/CHANGELOG.md +36 -0
- package/README.md +43 -0
- package/dist/index.cjs +619 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +274 -2
- package/dist/index.d.ts +274 -2
- package/dist/index.js +612 -2
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.d.cts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
-
import { PageDetail, PageListItem, ShopProductListItem, ShopFacets } from '@reblu/site-contracts';
|
|
2
|
+
import { PageDetail, PageListItem, ShopProductListItem, ShopFacets, ShopOrder } from '@reblu/site-contracts';
|
|
3
|
+
import { ReactNode, FormEvent } from 'react';
|
|
3
4
|
|
|
4
5
|
declare function readingTime(content: string | null | undefined): number | null;
|
|
5
6
|
|
|
@@ -383,4 +384,275 @@ interface ShopFiltersProps {
|
|
|
383
384
|
declare function shouldRenderShopFilters(facets: ShopFacets, active?: ShopFiltersActive): boolean;
|
|
384
385
|
declare function ShopFilters({ facets, hrefs, active, onChange, priceBands, labels, currencyCode, locale, className, }: ShopFiltersProps): react_jsx_runtime.JSX.Element | null;
|
|
385
386
|
|
|
386
|
-
|
|
387
|
+
/**
|
|
388
|
+
* `AddToCartButton` — presentational add-to-cart CTA (REB-829). RSC-safe by
|
|
389
|
+
* construction (same contract as `ShopFilters`'s `OptionLink`): passing
|
|
390
|
+
* `onAddToCart` only makes sense from a Client Component, but the component
|
|
391
|
+
* itself carries no `'use client'` directive, so a Server Component may
|
|
392
|
+
* still render it WITHOUT the prop (e.g. a disabled preview state).
|
|
393
|
+
*/
|
|
394
|
+
interface AddToCartButtonLabels {
|
|
395
|
+
add: string;
|
|
396
|
+
adding: string;
|
|
397
|
+
outOfStock: string;
|
|
398
|
+
}
|
|
399
|
+
interface AddToCartButtonProps {
|
|
400
|
+
onAddToCart?: () => void;
|
|
401
|
+
disabled?: boolean;
|
|
402
|
+
outOfStock?: boolean;
|
|
403
|
+
/** True while the add-to-cart call (or a navigation it triggers) is in flight. */
|
|
404
|
+
pending?: boolean;
|
|
405
|
+
labels?: Partial<AddToCartButtonLabels>;
|
|
406
|
+
className?: string;
|
|
407
|
+
}
|
|
408
|
+
declare function AddToCartButton({ onAddToCart, disabled, outOfStock, pending, labels, className, }: AddToCartButtonProps): react_jsx_runtime.JSX.Element;
|
|
409
|
+
|
|
410
|
+
/**
|
|
411
|
+
* `CartIcon` — presentational cart badge/link (REB-829). RSC-safe (plain
|
|
412
|
+
* `<a href>`, no hooks) — a Server Component can render it directly with a
|
|
413
|
+
* value already computed server-side; a Client Component drives `itemCount`
|
|
414
|
+
* from `@reblu/site-next/cart`'s `useCart().itemCount`.
|
|
415
|
+
*/
|
|
416
|
+
interface CartIconLabels {
|
|
417
|
+
cart: string;
|
|
418
|
+
itemsSuffix: (count: number) => string;
|
|
419
|
+
}
|
|
420
|
+
interface CartIconProps {
|
|
421
|
+
itemCount: number;
|
|
422
|
+
href: string;
|
|
423
|
+
labels?: Partial<CartIconLabels>;
|
|
424
|
+
className?: string;
|
|
425
|
+
}
|
|
426
|
+
declare function CartIcon({ itemCount, href, labels, className }: CartIconProps): react_jsx_runtime.JSX.Element;
|
|
427
|
+
|
|
428
|
+
/**
|
|
429
|
+
* `CartLineList` — presentational cart lines (REB-829), container/
|
|
430
|
+
* presentational split: it takes `items` + optional `onQuantityChange`/
|
|
431
|
+
* `onRemove` callbacks, never fetches or owns cart state itself. The
|
|
432
|
+
* consuming SITE wires these to `@reblu/site-next/cart`'s `useCart()`.
|
|
433
|
+
*
|
|
434
|
+
* RSC-safe by construction (same `OptionLink` contract as `ShopFilters`):
|
|
435
|
+
* the quantity stepper/remove buttons only do anything when `onQuantityChange`/
|
|
436
|
+
* `onRemove` are supplied from a Client Component — omit them from a Server
|
|
437
|
+
* Component to render a read-only summary instead.
|
|
438
|
+
*/
|
|
439
|
+
interface CartLineItemView {
|
|
440
|
+
productId: string;
|
|
441
|
+
variantId?: string | null;
|
|
442
|
+
name: string;
|
|
443
|
+
variantName?: string;
|
|
444
|
+
/** Integer cents. */
|
|
445
|
+
unitPrice: number;
|
|
446
|
+
quantity: number;
|
|
447
|
+
imageUrl?: string;
|
|
448
|
+
/** `null`/`undefined` = untracked stock — never clamps the stepper's max. */
|
|
449
|
+
maxStock?: number | null;
|
|
450
|
+
}
|
|
451
|
+
interface CartLineListLabels {
|
|
452
|
+
quantityLabel: string;
|
|
453
|
+
decrease: string;
|
|
454
|
+
increase: string;
|
|
455
|
+
remove: string;
|
|
456
|
+
unitPriceSuffix: string;
|
|
457
|
+
}
|
|
458
|
+
interface CartLineListProps {
|
|
459
|
+
items: CartLineItemView[];
|
|
460
|
+
onQuantityChange?: (productId: string, variantId: string | null | undefined, quantity: number) => void;
|
|
461
|
+
onRemove?: (productId: string, variantId: string | null | undefined) => void;
|
|
462
|
+
/**
|
|
463
|
+
* A visually-hidden `aria-live="polite"` announcement (e.g. "Cantidad
|
|
464
|
+
* actualizada", "Producto eliminado del carrito") — the consuming SITE
|
|
465
|
+
* decides WHEN to set it (after a successful mutation), this component
|
|
466
|
+
* only renders the live region.
|
|
467
|
+
*/
|
|
468
|
+
liveMessage?: string;
|
|
469
|
+
labels?: Partial<CartLineListLabels>;
|
|
470
|
+
currencyCode?: string;
|
|
471
|
+
locale?: string;
|
|
472
|
+
className?: string;
|
|
473
|
+
}
|
|
474
|
+
declare function CartLineList({ items, onQuantityChange, onRemove, liveMessage, labels, currencyCode, locale, className, }: CartLineListProps): react_jsx_runtime.JSX.Element;
|
|
475
|
+
|
|
476
|
+
/**
|
|
477
|
+
* `CartSummary` — presentational price breakdown (REB-829): subtotal,
|
|
478
|
+
* optional discount, optional shipping (sourced from `ShopConfig` by the
|
|
479
|
+
* consuming SITE — this component only renders what it's given), and total.
|
|
480
|
+
* Pure data in, RSC-safe (no hooks, no callbacks).
|
|
481
|
+
*/
|
|
482
|
+
interface CartSummaryLabels {
|
|
483
|
+
subtotal: string;
|
|
484
|
+
discountPrefix: string;
|
|
485
|
+
shipping: string;
|
|
486
|
+
freeShipping: string;
|
|
487
|
+
/**
|
|
488
|
+
* Shown when `shipping` is `null`/`undefined` (unknown yet) — REB-829
|
|
489
|
+
* review finding: this is NOT the same state as `0` (confirmed free).
|
|
490
|
+
* Claiming "free shipping" for an amount the SITE simply hasn't computed
|
|
491
|
+
* yet understates the real total and is reversed at checkout.
|
|
492
|
+
*/
|
|
493
|
+
shippingCalculated: string;
|
|
494
|
+
total: string;
|
|
495
|
+
}
|
|
496
|
+
interface CartSummaryProps {
|
|
497
|
+
/** Integer cents. */
|
|
498
|
+
subtotal: number;
|
|
499
|
+
/** Integer cents, positive. Omit (or `0`) to hide the discount line. */
|
|
500
|
+
discount?: number;
|
|
501
|
+
/** The applied discount code, shown next to the discount amount. */
|
|
502
|
+
discountCode?: string;
|
|
503
|
+
/** Integer cents. Only rendered when `shippingEnabled` is true. */
|
|
504
|
+
shipping?: number | null;
|
|
505
|
+
/** Whether this tenant's shop even has shipping cost — hides the line entirely when false/omitted. */
|
|
506
|
+
shippingEnabled?: boolean;
|
|
507
|
+
/** Integer cents — the final charged amount. */
|
|
508
|
+
total: number;
|
|
509
|
+
labels?: Partial<CartSummaryLabels>;
|
|
510
|
+
currencyCode?: string;
|
|
511
|
+
locale?: string;
|
|
512
|
+
className?: string;
|
|
513
|
+
}
|
|
514
|
+
declare function CartSummary({ subtotal, discount, discountCode, shipping, shippingEnabled, total, labels, currencyCode, locale, className, }: CartSummaryProps): react_jsx_runtime.JSX.Element;
|
|
515
|
+
|
|
516
|
+
/**
|
|
517
|
+
* `CheckoutLoginPrompt` — the "login prompt hook" the checkout flow needs
|
|
518
|
+
* (REB-829): a guest-checkout identification dialog offering "continue with
|
|
519
|
+
* this email" (so the SITE can check for an existing account via
|
|
520
|
+
* `client.shop.checkEmail`) or "continue as guest" outright. Fully
|
|
521
|
+
* controlled/presentational — it performs NO fetch itself (unlike
|
|
522
|
+
* demo-site's original `CheckoutLoginModal`, which called
|
|
523
|
+
* `/api/shop/check-email` internally); the consuming SITE owns `email`
|
|
524
|
+
* state and the `checkEmail` call, and drives `checking`/`error` from its
|
|
525
|
+
* own result.
|
|
526
|
+
*/
|
|
527
|
+
interface CheckoutLoginPromptLabels {
|
|
528
|
+
title: string;
|
|
529
|
+
description: string;
|
|
530
|
+
emailLabel: string;
|
|
531
|
+
emailPlaceholder: string;
|
|
532
|
+
continueWithEmail: string;
|
|
533
|
+
checking: string;
|
|
534
|
+
continueAsGuest: string;
|
|
535
|
+
close: string;
|
|
536
|
+
}
|
|
537
|
+
interface CheckoutLoginPromptProps {
|
|
538
|
+
open: boolean;
|
|
539
|
+
email: string;
|
|
540
|
+
onEmailChange: (value: string) => void;
|
|
541
|
+
onClose: () => void;
|
|
542
|
+
/** Called when the visitor confirms a (valid-looking) email. */
|
|
543
|
+
onSubmitEmail: () => void;
|
|
544
|
+
onContinueAsGuest: () => void;
|
|
545
|
+
checking?: boolean;
|
|
546
|
+
error?: string | null;
|
|
547
|
+
labels?: Partial<CheckoutLoginPromptLabels>;
|
|
548
|
+
className?: string;
|
|
549
|
+
}
|
|
550
|
+
declare function CheckoutLoginPrompt({ open, email, onEmailChange, onClose, onSubmitEmail, onContinueAsGuest, checking, error, labels, className, }: CheckoutLoginPromptProps): react_jsx_runtime.JSX.Element | null;
|
|
551
|
+
|
|
552
|
+
/**
|
|
553
|
+
* `CheckoutForm` — presentational guest-checkout form (REB-829): contact
|
|
554
|
+
* fields, a discount-code section, an optional `loginPrompt` slot, and the
|
|
555
|
+
* submit CTA. Fully controlled — `values`/callbacks only, no internal state,
|
|
556
|
+
* no fetch, no react-hook-form/zod dependency (keeping this package's
|
|
557
|
+
* dependency surface minimal); the consuming SITE owns validation and wires
|
|
558
|
+
* its own `onSubmit`.
|
|
559
|
+
*
|
|
560
|
+
* Scope note: shipping/billing address collection is intentionally OUT of
|
|
561
|
+
* this component — those field sets vary by tenant shop config (shipping
|
|
562
|
+
* enabled/disabled, invoicing requirements) far more than the guest-email +
|
|
563
|
+
* discount-code flow the issue named. A SITE that needs them renders its own
|
|
564
|
+
* fields via the `children` slot, rendered between the contact and discount
|
|
565
|
+
* sections.
|
|
566
|
+
*/
|
|
567
|
+
interface CheckoutContactValues {
|
|
568
|
+
name: string;
|
|
569
|
+
email: string;
|
|
570
|
+
phone?: string;
|
|
571
|
+
}
|
|
572
|
+
type CheckoutContactErrors = Partial<Record<keyof CheckoutContactValues, string>>;
|
|
573
|
+
interface CheckoutAppliedDiscount {
|
|
574
|
+
code: string;
|
|
575
|
+
amount: number;
|
|
576
|
+
}
|
|
577
|
+
interface CheckoutFormLabels {
|
|
578
|
+
contactTitle: string;
|
|
579
|
+
nameLabel: string;
|
|
580
|
+
namePlaceholder: string;
|
|
581
|
+
emailLabel: string;
|
|
582
|
+
emailPlaceholder: string;
|
|
583
|
+
phoneLabel: string;
|
|
584
|
+
discountTitle: string;
|
|
585
|
+
discountPlaceholder: string;
|
|
586
|
+
applyDiscount: string;
|
|
587
|
+
applyingDiscount: string;
|
|
588
|
+
removeDiscount: string;
|
|
589
|
+
discountAppliedSuffix: (formattedAmount: string) => string;
|
|
590
|
+
submit: string;
|
|
591
|
+
submitting: string;
|
|
592
|
+
}
|
|
593
|
+
interface CheckoutFormProps {
|
|
594
|
+
contact: CheckoutContactValues;
|
|
595
|
+
onContactChange: (values: CheckoutContactValues) => void;
|
|
596
|
+
contactErrors?: CheckoutContactErrors;
|
|
597
|
+
discountCode: string;
|
|
598
|
+
onDiscountCodeChange: (value: string) => void;
|
|
599
|
+
onApplyDiscount: () => void;
|
|
600
|
+
onRemoveDiscount: () => void;
|
|
601
|
+
appliedDiscount?: CheckoutAppliedDiscount | null;
|
|
602
|
+
discountApplying?: boolean;
|
|
603
|
+
discountError?: string | null;
|
|
604
|
+
/**
|
|
605
|
+
* Rendered OUTSIDE the `<form>`, immediately before it — e.g. the
|
|
606
|
+
* `CheckoutLoginPrompt` dialog. It must never render inside the `<form>`:
|
|
607
|
+
* an Enter keypress in one of ITS inputs would otherwise submit checkout
|
|
608
|
+
* (REB-829 review finding — see `handleDiscountCodeKeyDown` below for the
|
|
609
|
+
* same defect class).
|
|
610
|
+
*/
|
|
611
|
+
loginPrompt?: ReactNode;
|
|
612
|
+
/** Rendered between the contact and discount sections — e.g. shipping/billing fields. */
|
|
613
|
+
children?: ReactNode;
|
|
614
|
+
submitting?: boolean;
|
|
615
|
+
submitDisabled?: boolean;
|
|
616
|
+
onSubmit: (event: FormEvent<HTMLFormElement>) => void;
|
|
617
|
+
labels?: Partial<CheckoutFormLabels>;
|
|
618
|
+
currencyCode?: string;
|
|
619
|
+
locale?: string;
|
|
620
|
+
className?: string;
|
|
621
|
+
}
|
|
622
|
+
declare function CheckoutForm({ contact, onContactChange, contactErrors, discountCode, onDiscountCodeChange, onApplyDiscount, onRemoveDiscount, appliedDiscount, discountApplying, discountError, loginPrompt, children, submitting, submitDisabled, onSubmit, labels, currencyCode, locale, className, }: CheckoutFormProps): react_jsx_runtime.JSX.Element;
|
|
623
|
+
|
|
624
|
+
/**
|
|
625
|
+
* `OrderConfirmation` — presentational order-confirmation view (REB-829).
|
|
626
|
+
* Pure data + status in, RSC-safe (no hooks). The consuming SITE owns the
|
|
627
|
+
* actual lookup/polling (`client.shop.order(sessionId)` via a Route Handler
|
|
628
|
+
* built from `createShopOrderRoute`, retried while `'polling'` — the Stripe
|
|
629
|
+
* webhook that creates the order may not have landed yet) and passes the
|
|
630
|
+
* resulting `status`/`order` in.
|
|
631
|
+
*/
|
|
632
|
+
type OrderConfirmationOrder = ShopOrder;
|
|
633
|
+
type OrderConfirmationStatus = 'polling' | 'found' | 'timeout';
|
|
634
|
+
interface OrderConfirmationLabels {
|
|
635
|
+
pollingTitle: string;
|
|
636
|
+
pollingDescription: string;
|
|
637
|
+
foundTitle: string;
|
|
638
|
+
foundDescription: string;
|
|
639
|
+
timeoutTitle: string;
|
|
640
|
+
timeoutDescription: string;
|
|
641
|
+
orderNumberLabel: string;
|
|
642
|
+
totalLabel: string;
|
|
643
|
+
confirmationEmailPrefix: string;
|
|
644
|
+
sessionReferenceLabel: string;
|
|
645
|
+
}
|
|
646
|
+
interface OrderConfirmationProps {
|
|
647
|
+
status: OrderConfirmationStatus;
|
|
648
|
+
order: OrderConfirmationOrder | null;
|
|
649
|
+
/** Shown as a fallback reference when `status === 'timeout'` and no order was found. */
|
|
650
|
+
sessionId?: string;
|
|
651
|
+
labels?: Partial<OrderConfirmationLabels>;
|
|
652
|
+
currencyCode?: string;
|
|
653
|
+
locale?: string;
|
|
654
|
+
className?: string;
|
|
655
|
+
}
|
|
656
|
+
declare function OrderConfirmation({ status, order, sessionId, labels, currencyCode, locale, className, }: OrderConfirmationProps): react_jsx_runtime.JSX.Element;
|
|
657
|
+
|
|
658
|
+
export { ALLOWED_YOUTUBE_HOSTS, AddToCartButton, type AddToCartButtonLabels, type AddToCartButtonProps, CartIcon, type CartIconLabels, type CartIconProps, type CartLineItemView, CartLineList, type CartLineListLabels, type CartLineListProps, CartSummary, type CartSummaryLabels, type CartSummaryProps, type CheckoutAppliedDiscount, type CheckoutContactErrors, type CheckoutContactValues, CheckoutForm, type CheckoutFormLabels, type CheckoutFormProps, CheckoutLoginPrompt, type CheckoutLoginPromptLabels, type CheckoutLoginPromptProps, DEFAULT_SHOP_PRICE_BANDS, MarkdownRenderer, type MarkdownRendererProps, OrderConfirmation, type OrderConfirmationLabels, type OrderConfirmationOrder, type OrderConfirmationProps, type OrderConfirmationStatus, ProductCard, type ProductCardProps, ProfileCard, type ProfileCardProps, SafeHtml, type SafeHtmlProps, type ShareUrls, type ShopFilterPriceBand, ShopFilters, type ShopFiltersActive, type ShopFiltersHrefs, type ShopFiltersLabels, type ShopFiltersProps, Specialty, type SpecialtyProps, Treatment, type TreatmentProps, buildShareUrls, formatPrice, isAllowedYouTubeHost, markdownToSafeHtml, readingTime, sanitizeHtml, shouldRenderShopFilters };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
-
import { PageDetail, PageListItem, ShopProductListItem, ShopFacets } from '@reblu/site-contracts';
|
|
2
|
+
import { PageDetail, PageListItem, ShopProductListItem, ShopFacets, ShopOrder } from '@reblu/site-contracts';
|
|
3
|
+
import { ReactNode, FormEvent } from 'react';
|
|
3
4
|
|
|
4
5
|
declare function readingTime(content: string | null | undefined): number | null;
|
|
5
6
|
|
|
@@ -383,4 +384,275 @@ interface ShopFiltersProps {
|
|
|
383
384
|
declare function shouldRenderShopFilters(facets: ShopFacets, active?: ShopFiltersActive): boolean;
|
|
384
385
|
declare function ShopFilters({ facets, hrefs, active, onChange, priceBands, labels, currencyCode, locale, className, }: ShopFiltersProps): react_jsx_runtime.JSX.Element | null;
|
|
385
386
|
|
|
386
|
-
|
|
387
|
+
/**
|
|
388
|
+
* `AddToCartButton` — presentational add-to-cart CTA (REB-829). RSC-safe by
|
|
389
|
+
* construction (same contract as `ShopFilters`'s `OptionLink`): passing
|
|
390
|
+
* `onAddToCart` only makes sense from a Client Component, but the component
|
|
391
|
+
* itself carries no `'use client'` directive, so a Server Component may
|
|
392
|
+
* still render it WITHOUT the prop (e.g. a disabled preview state).
|
|
393
|
+
*/
|
|
394
|
+
interface AddToCartButtonLabels {
|
|
395
|
+
add: string;
|
|
396
|
+
adding: string;
|
|
397
|
+
outOfStock: string;
|
|
398
|
+
}
|
|
399
|
+
interface AddToCartButtonProps {
|
|
400
|
+
onAddToCart?: () => void;
|
|
401
|
+
disabled?: boolean;
|
|
402
|
+
outOfStock?: boolean;
|
|
403
|
+
/** True while the add-to-cart call (or a navigation it triggers) is in flight. */
|
|
404
|
+
pending?: boolean;
|
|
405
|
+
labels?: Partial<AddToCartButtonLabels>;
|
|
406
|
+
className?: string;
|
|
407
|
+
}
|
|
408
|
+
declare function AddToCartButton({ onAddToCart, disabled, outOfStock, pending, labels, className, }: AddToCartButtonProps): react_jsx_runtime.JSX.Element;
|
|
409
|
+
|
|
410
|
+
/**
|
|
411
|
+
* `CartIcon` — presentational cart badge/link (REB-829). RSC-safe (plain
|
|
412
|
+
* `<a href>`, no hooks) — a Server Component can render it directly with a
|
|
413
|
+
* value already computed server-side; a Client Component drives `itemCount`
|
|
414
|
+
* from `@reblu/site-next/cart`'s `useCart().itemCount`.
|
|
415
|
+
*/
|
|
416
|
+
interface CartIconLabels {
|
|
417
|
+
cart: string;
|
|
418
|
+
itemsSuffix: (count: number) => string;
|
|
419
|
+
}
|
|
420
|
+
interface CartIconProps {
|
|
421
|
+
itemCount: number;
|
|
422
|
+
href: string;
|
|
423
|
+
labels?: Partial<CartIconLabels>;
|
|
424
|
+
className?: string;
|
|
425
|
+
}
|
|
426
|
+
declare function CartIcon({ itemCount, href, labels, className }: CartIconProps): react_jsx_runtime.JSX.Element;
|
|
427
|
+
|
|
428
|
+
/**
|
|
429
|
+
* `CartLineList` — presentational cart lines (REB-829), container/
|
|
430
|
+
* presentational split: it takes `items` + optional `onQuantityChange`/
|
|
431
|
+
* `onRemove` callbacks, never fetches or owns cart state itself. The
|
|
432
|
+
* consuming SITE wires these to `@reblu/site-next/cart`'s `useCart()`.
|
|
433
|
+
*
|
|
434
|
+
* RSC-safe by construction (same `OptionLink` contract as `ShopFilters`):
|
|
435
|
+
* the quantity stepper/remove buttons only do anything when `onQuantityChange`/
|
|
436
|
+
* `onRemove` are supplied from a Client Component — omit them from a Server
|
|
437
|
+
* Component to render a read-only summary instead.
|
|
438
|
+
*/
|
|
439
|
+
interface CartLineItemView {
|
|
440
|
+
productId: string;
|
|
441
|
+
variantId?: string | null;
|
|
442
|
+
name: string;
|
|
443
|
+
variantName?: string;
|
|
444
|
+
/** Integer cents. */
|
|
445
|
+
unitPrice: number;
|
|
446
|
+
quantity: number;
|
|
447
|
+
imageUrl?: string;
|
|
448
|
+
/** `null`/`undefined` = untracked stock — never clamps the stepper's max. */
|
|
449
|
+
maxStock?: number | null;
|
|
450
|
+
}
|
|
451
|
+
interface CartLineListLabels {
|
|
452
|
+
quantityLabel: string;
|
|
453
|
+
decrease: string;
|
|
454
|
+
increase: string;
|
|
455
|
+
remove: string;
|
|
456
|
+
unitPriceSuffix: string;
|
|
457
|
+
}
|
|
458
|
+
interface CartLineListProps {
|
|
459
|
+
items: CartLineItemView[];
|
|
460
|
+
onQuantityChange?: (productId: string, variantId: string | null | undefined, quantity: number) => void;
|
|
461
|
+
onRemove?: (productId: string, variantId: string | null | undefined) => void;
|
|
462
|
+
/**
|
|
463
|
+
* A visually-hidden `aria-live="polite"` announcement (e.g. "Cantidad
|
|
464
|
+
* actualizada", "Producto eliminado del carrito") — the consuming SITE
|
|
465
|
+
* decides WHEN to set it (after a successful mutation), this component
|
|
466
|
+
* only renders the live region.
|
|
467
|
+
*/
|
|
468
|
+
liveMessage?: string;
|
|
469
|
+
labels?: Partial<CartLineListLabels>;
|
|
470
|
+
currencyCode?: string;
|
|
471
|
+
locale?: string;
|
|
472
|
+
className?: string;
|
|
473
|
+
}
|
|
474
|
+
declare function CartLineList({ items, onQuantityChange, onRemove, liveMessage, labels, currencyCode, locale, className, }: CartLineListProps): react_jsx_runtime.JSX.Element;
|
|
475
|
+
|
|
476
|
+
/**
|
|
477
|
+
* `CartSummary` — presentational price breakdown (REB-829): subtotal,
|
|
478
|
+
* optional discount, optional shipping (sourced from `ShopConfig` by the
|
|
479
|
+
* consuming SITE — this component only renders what it's given), and total.
|
|
480
|
+
* Pure data in, RSC-safe (no hooks, no callbacks).
|
|
481
|
+
*/
|
|
482
|
+
interface CartSummaryLabels {
|
|
483
|
+
subtotal: string;
|
|
484
|
+
discountPrefix: string;
|
|
485
|
+
shipping: string;
|
|
486
|
+
freeShipping: string;
|
|
487
|
+
/**
|
|
488
|
+
* Shown when `shipping` is `null`/`undefined` (unknown yet) — REB-829
|
|
489
|
+
* review finding: this is NOT the same state as `0` (confirmed free).
|
|
490
|
+
* Claiming "free shipping" for an amount the SITE simply hasn't computed
|
|
491
|
+
* yet understates the real total and is reversed at checkout.
|
|
492
|
+
*/
|
|
493
|
+
shippingCalculated: string;
|
|
494
|
+
total: string;
|
|
495
|
+
}
|
|
496
|
+
interface CartSummaryProps {
|
|
497
|
+
/** Integer cents. */
|
|
498
|
+
subtotal: number;
|
|
499
|
+
/** Integer cents, positive. Omit (or `0`) to hide the discount line. */
|
|
500
|
+
discount?: number;
|
|
501
|
+
/** The applied discount code, shown next to the discount amount. */
|
|
502
|
+
discountCode?: string;
|
|
503
|
+
/** Integer cents. Only rendered when `shippingEnabled` is true. */
|
|
504
|
+
shipping?: number | null;
|
|
505
|
+
/** Whether this tenant's shop even has shipping cost — hides the line entirely when false/omitted. */
|
|
506
|
+
shippingEnabled?: boolean;
|
|
507
|
+
/** Integer cents — the final charged amount. */
|
|
508
|
+
total: number;
|
|
509
|
+
labels?: Partial<CartSummaryLabels>;
|
|
510
|
+
currencyCode?: string;
|
|
511
|
+
locale?: string;
|
|
512
|
+
className?: string;
|
|
513
|
+
}
|
|
514
|
+
declare function CartSummary({ subtotal, discount, discountCode, shipping, shippingEnabled, total, labels, currencyCode, locale, className, }: CartSummaryProps): react_jsx_runtime.JSX.Element;
|
|
515
|
+
|
|
516
|
+
/**
|
|
517
|
+
* `CheckoutLoginPrompt` — the "login prompt hook" the checkout flow needs
|
|
518
|
+
* (REB-829): a guest-checkout identification dialog offering "continue with
|
|
519
|
+
* this email" (so the SITE can check for an existing account via
|
|
520
|
+
* `client.shop.checkEmail`) or "continue as guest" outright. Fully
|
|
521
|
+
* controlled/presentational — it performs NO fetch itself (unlike
|
|
522
|
+
* demo-site's original `CheckoutLoginModal`, which called
|
|
523
|
+
* `/api/shop/check-email` internally); the consuming SITE owns `email`
|
|
524
|
+
* state and the `checkEmail` call, and drives `checking`/`error` from its
|
|
525
|
+
* own result.
|
|
526
|
+
*/
|
|
527
|
+
interface CheckoutLoginPromptLabels {
|
|
528
|
+
title: string;
|
|
529
|
+
description: string;
|
|
530
|
+
emailLabel: string;
|
|
531
|
+
emailPlaceholder: string;
|
|
532
|
+
continueWithEmail: string;
|
|
533
|
+
checking: string;
|
|
534
|
+
continueAsGuest: string;
|
|
535
|
+
close: string;
|
|
536
|
+
}
|
|
537
|
+
interface CheckoutLoginPromptProps {
|
|
538
|
+
open: boolean;
|
|
539
|
+
email: string;
|
|
540
|
+
onEmailChange: (value: string) => void;
|
|
541
|
+
onClose: () => void;
|
|
542
|
+
/** Called when the visitor confirms a (valid-looking) email. */
|
|
543
|
+
onSubmitEmail: () => void;
|
|
544
|
+
onContinueAsGuest: () => void;
|
|
545
|
+
checking?: boolean;
|
|
546
|
+
error?: string | null;
|
|
547
|
+
labels?: Partial<CheckoutLoginPromptLabels>;
|
|
548
|
+
className?: string;
|
|
549
|
+
}
|
|
550
|
+
declare function CheckoutLoginPrompt({ open, email, onEmailChange, onClose, onSubmitEmail, onContinueAsGuest, checking, error, labels, className, }: CheckoutLoginPromptProps): react_jsx_runtime.JSX.Element | null;
|
|
551
|
+
|
|
552
|
+
/**
|
|
553
|
+
* `CheckoutForm` — presentational guest-checkout form (REB-829): contact
|
|
554
|
+
* fields, a discount-code section, an optional `loginPrompt` slot, and the
|
|
555
|
+
* submit CTA. Fully controlled — `values`/callbacks only, no internal state,
|
|
556
|
+
* no fetch, no react-hook-form/zod dependency (keeping this package's
|
|
557
|
+
* dependency surface minimal); the consuming SITE owns validation and wires
|
|
558
|
+
* its own `onSubmit`.
|
|
559
|
+
*
|
|
560
|
+
* Scope note: shipping/billing address collection is intentionally OUT of
|
|
561
|
+
* this component — those field sets vary by tenant shop config (shipping
|
|
562
|
+
* enabled/disabled, invoicing requirements) far more than the guest-email +
|
|
563
|
+
* discount-code flow the issue named. A SITE that needs them renders its own
|
|
564
|
+
* fields via the `children` slot, rendered between the contact and discount
|
|
565
|
+
* sections.
|
|
566
|
+
*/
|
|
567
|
+
interface CheckoutContactValues {
|
|
568
|
+
name: string;
|
|
569
|
+
email: string;
|
|
570
|
+
phone?: string;
|
|
571
|
+
}
|
|
572
|
+
type CheckoutContactErrors = Partial<Record<keyof CheckoutContactValues, string>>;
|
|
573
|
+
interface CheckoutAppliedDiscount {
|
|
574
|
+
code: string;
|
|
575
|
+
amount: number;
|
|
576
|
+
}
|
|
577
|
+
interface CheckoutFormLabels {
|
|
578
|
+
contactTitle: string;
|
|
579
|
+
nameLabel: string;
|
|
580
|
+
namePlaceholder: string;
|
|
581
|
+
emailLabel: string;
|
|
582
|
+
emailPlaceholder: string;
|
|
583
|
+
phoneLabel: string;
|
|
584
|
+
discountTitle: string;
|
|
585
|
+
discountPlaceholder: string;
|
|
586
|
+
applyDiscount: string;
|
|
587
|
+
applyingDiscount: string;
|
|
588
|
+
removeDiscount: string;
|
|
589
|
+
discountAppliedSuffix: (formattedAmount: string) => string;
|
|
590
|
+
submit: string;
|
|
591
|
+
submitting: string;
|
|
592
|
+
}
|
|
593
|
+
interface CheckoutFormProps {
|
|
594
|
+
contact: CheckoutContactValues;
|
|
595
|
+
onContactChange: (values: CheckoutContactValues) => void;
|
|
596
|
+
contactErrors?: CheckoutContactErrors;
|
|
597
|
+
discountCode: string;
|
|
598
|
+
onDiscountCodeChange: (value: string) => void;
|
|
599
|
+
onApplyDiscount: () => void;
|
|
600
|
+
onRemoveDiscount: () => void;
|
|
601
|
+
appliedDiscount?: CheckoutAppliedDiscount | null;
|
|
602
|
+
discountApplying?: boolean;
|
|
603
|
+
discountError?: string | null;
|
|
604
|
+
/**
|
|
605
|
+
* Rendered OUTSIDE the `<form>`, immediately before it — e.g. the
|
|
606
|
+
* `CheckoutLoginPrompt` dialog. It must never render inside the `<form>`:
|
|
607
|
+
* an Enter keypress in one of ITS inputs would otherwise submit checkout
|
|
608
|
+
* (REB-829 review finding — see `handleDiscountCodeKeyDown` below for the
|
|
609
|
+
* same defect class).
|
|
610
|
+
*/
|
|
611
|
+
loginPrompt?: ReactNode;
|
|
612
|
+
/** Rendered between the contact and discount sections — e.g. shipping/billing fields. */
|
|
613
|
+
children?: ReactNode;
|
|
614
|
+
submitting?: boolean;
|
|
615
|
+
submitDisabled?: boolean;
|
|
616
|
+
onSubmit: (event: FormEvent<HTMLFormElement>) => void;
|
|
617
|
+
labels?: Partial<CheckoutFormLabels>;
|
|
618
|
+
currencyCode?: string;
|
|
619
|
+
locale?: string;
|
|
620
|
+
className?: string;
|
|
621
|
+
}
|
|
622
|
+
declare function CheckoutForm({ contact, onContactChange, contactErrors, discountCode, onDiscountCodeChange, onApplyDiscount, onRemoveDiscount, appliedDiscount, discountApplying, discountError, loginPrompt, children, submitting, submitDisabled, onSubmit, labels, currencyCode, locale, className, }: CheckoutFormProps): react_jsx_runtime.JSX.Element;
|
|
623
|
+
|
|
624
|
+
/**
|
|
625
|
+
* `OrderConfirmation` — presentational order-confirmation view (REB-829).
|
|
626
|
+
* Pure data + status in, RSC-safe (no hooks). The consuming SITE owns the
|
|
627
|
+
* actual lookup/polling (`client.shop.order(sessionId)` via a Route Handler
|
|
628
|
+
* built from `createShopOrderRoute`, retried while `'polling'` — the Stripe
|
|
629
|
+
* webhook that creates the order may not have landed yet) and passes the
|
|
630
|
+
* resulting `status`/`order` in.
|
|
631
|
+
*/
|
|
632
|
+
type OrderConfirmationOrder = ShopOrder;
|
|
633
|
+
type OrderConfirmationStatus = 'polling' | 'found' | 'timeout';
|
|
634
|
+
interface OrderConfirmationLabels {
|
|
635
|
+
pollingTitle: string;
|
|
636
|
+
pollingDescription: string;
|
|
637
|
+
foundTitle: string;
|
|
638
|
+
foundDescription: string;
|
|
639
|
+
timeoutTitle: string;
|
|
640
|
+
timeoutDescription: string;
|
|
641
|
+
orderNumberLabel: string;
|
|
642
|
+
totalLabel: string;
|
|
643
|
+
confirmationEmailPrefix: string;
|
|
644
|
+
sessionReferenceLabel: string;
|
|
645
|
+
}
|
|
646
|
+
interface OrderConfirmationProps {
|
|
647
|
+
status: OrderConfirmationStatus;
|
|
648
|
+
order: OrderConfirmationOrder | null;
|
|
649
|
+
/** Shown as a fallback reference when `status === 'timeout'` and no order was found. */
|
|
650
|
+
sessionId?: string;
|
|
651
|
+
labels?: Partial<OrderConfirmationLabels>;
|
|
652
|
+
currencyCode?: string;
|
|
653
|
+
locale?: string;
|
|
654
|
+
className?: string;
|
|
655
|
+
}
|
|
656
|
+
declare function OrderConfirmation({ status, order, sessionId, labels, currencyCode, locale, className, }: OrderConfirmationProps): react_jsx_runtime.JSX.Element;
|
|
657
|
+
|
|
658
|
+
export { ALLOWED_YOUTUBE_HOSTS, AddToCartButton, type AddToCartButtonLabels, type AddToCartButtonProps, CartIcon, type CartIconLabels, type CartIconProps, type CartLineItemView, CartLineList, type CartLineListLabels, type CartLineListProps, CartSummary, type CartSummaryLabels, type CartSummaryProps, type CheckoutAppliedDiscount, type CheckoutContactErrors, type CheckoutContactValues, CheckoutForm, type CheckoutFormLabels, type CheckoutFormProps, CheckoutLoginPrompt, type CheckoutLoginPromptLabels, type CheckoutLoginPromptProps, DEFAULT_SHOP_PRICE_BANDS, MarkdownRenderer, type MarkdownRendererProps, OrderConfirmation, type OrderConfirmationLabels, type OrderConfirmationOrder, type OrderConfirmationProps, type OrderConfirmationStatus, ProductCard, type ProductCardProps, ProfileCard, type ProfileCardProps, SafeHtml, type SafeHtmlProps, type ShareUrls, type ShopFilterPriceBand, ShopFilters, type ShopFiltersActive, type ShopFiltersHrefs, type ShopFiltersLabels, type ShopFiltersProps, Specialty, type SpecialtyProps, Treatment, type TreatmentProps, buildShareUrls, formatPrice, isAllowedYouTubeHost, markdownToSafeHtml, readingTime, sanitizeHtml, shouldRenderShopFilters };
|