@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/dist/index.cjs CHANGED
@@ -21,8 +21,15 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
21
21
  var src_exports = {};
22
22
  __export(src_exports, {
23
23
  ALLOWED_YOUTUBE_HOSTS: () => ALLOWED_YOUTUBE_HOSTS,
24
+ AddToCartButton: () => AddToCartButton,
25
+ CartIcon: () => CartIcon,
26
+ CartLineList: () => CartLineList,
27
+ CartSummary: () => CartSummary,
28
+ CheckoutForm: () => CheckoutForm,
29
+ CheckoutLoginPrompt: () => CheckoutLoginPrompt,
24
30
  DEFAULT_SHOP_PRICE_BANDS: () => DEFAULT_SHOP_PRICE_BANDS,
25
31
  MarkdownRenderer: () => MarkdownRenderer,
32
+ OrderConfirmation: () => OrderConfirmation,
26
33
  ProductCard: () => ProductCard,
27
34
  ProfileCard: () => ProfileCard,
28
35
  SafeHtml: () => SafeHtml,
@@ -13456,7 +13463,10 @@ function toValueArray(value) {
13456
13463
  return Array.isArray(value) ? value : [value];
13457
13464
  }
13458
13465
  function hasAnyActiveFilter(active) {
13459
- return Object.keys(active.taxonomy ?? {}).length > 0 || Object.keys(active.attr ?? {}).length > 0 || active.priceMin !== void 0 || active.priceMax !== void 0 || Boolean(active.onSale);
13466
+ return Object.keys(active.taxonomy ?? {}).length > 0 || Object.keys(active.attr ?? {}).length > 0 || hasActivePriceFilter(active) || Boolean(active.onSale);
13467
+ }
13468
+ function hasActivePriceFilter(active) {
13469
+ return active.priceMin != null || active.priceMax != null;
13460
13470
  }
13461
13471
  function shouldRenderShopFilters(facets, active = {}) {
13462
13472
  const hasFacetContent = facets.taxonomies.some((axis) => axis.terms.length > 0) || facets.attributes.some((attr) => attr.options.length > 0) || facets.price.max > facets.price.min || facets.onSale.count > 0;
@@ -13514,6 +13524,7 @@ function ShopFilters({
13514
13524
  const visibleTaxonomies = facets.taxonomies.filter((axis) => axis.terms.length > 0);
13515
13525
  const visibleAttributes = facets.attributes.filter((attr) => attr.options.length > 0);
13516
13526
  const hasPriceSpread = facets.price.max > facets.price.min;
13527
+ const showPriceGroup = hasPriceSpread || hasActivePriceFilter(active);
13517
13528
  const hasOnSale = facets.onSale.count > 0;
13518
13529
  if (!shouldRenderShopFilters(facets, active)) {
13519
13530
  return null;
@@ -13577,7 +13588,7 @@ function ShopFilters({
13577
13588
  );
13578
13589
  }) }, attr.key);
13579
13590
  }),
13580
- hasPriceSpread && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(FilterFieldset, { legend: t.price, children: priceBands.map((band, i) => {
13591
+ showPriceGroup && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(FilterFieldset, { legend: t.price, children: priceBands.map((band, i) => {
13581
13592
  const isActive = active.priceMin === band.min && active.priceMax === band.max;
13582
13593
  const nextMin = isActive ? void 0 : band.min;
13583
13594
  const nextMax = isActive ? void 0 : band.max;
@@ -13615,11 +13626,617 @@ function ShopFilters({
13615
13626
  )
13616
13627
  ] });
13617
13628
  }
13629
+
13630
+ // src/components/add-to-cart-button.tsx
13631
+ var import_utils8 = require("@reblu/site-ui/utils");
13632
+ var import_jsx_runtime8 = require("react/jsx-runtime");
13633
+ var DEFAULT_LABELS2 = {
13634
+ add: "A\xF1adir al carrito",
13635
+ adding: "A\xF1adiendo\u2026",
13636
+ outOfStock: "Agotado"
13637
+ };
13638
+ function AddToCartButton({
13639
+ onAddToCart,
13640
+ disabled,
13641
+ outOfStock,
13642
+ pending,
13643
+ labels,
13644
+ className
13645
+ }) {
13646
+ const t = { ...DEFAULT_LABELS2, ...labels };
13647
+ const isDisabled = Boolean(disabled || outOfStock || pending);
13648
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
13649
+ "button",
13650
+ {
13651
+ type: "button",
13652
+ onClick: onAddToCart,
13653
+ disabled: isDisabled,
13654
+ "data-slot": "add-to-cart-button",
13655
+ className: (0, import_utils8.cn)(
13656
+ "inline-flex items-center justify-center gap-2 rounded-xl px-4 py-2.5 text-sm font-semibold transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
13657
+ isDisabled ? "cursor-not-allowed bg-muted text-muted-foreground" : "bg-primary text-primary-foreground hover:bg-primary/90",
13658
+ className
13659
+ ),
13660
+ children: outOfStock ? t.outOfStock : pending ? t.adding : t.add
13661
+ }
13662
+ );
13663
+ }
13664
+
13665
+ // src/components/cart-icon.tsx
13666
+ var import_utils9 = require("@reblu/site-ui/utils");
13667
+ var import_jsx_runtime9 = require("react/jsx-runtime");
13668
+ var DEFAULT_LABELS3 = {
13669
+ cart: "Carrito",
13670
+ itemsSuffix: (count) => `${count} art\xEDculo${count === 1 ? "" : "s"}`
13671
+ };
13672
+ function CartIcon({ itemCount, href, labels, className }) {
13673
+ const t = { ...DEFAULT_LABELS3, ...labels };
13674
+ const ariaLabel = itemCount > 0 ? `${t.cart} (${t.itemsSuffix(itemCount)})` : t.cart;
13675
+ const displayCount = itemCount > 99 ? "99+" : String(itemCount);
13676
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
13677
+ "a",
13678
+ {
13679
+ href,
13680
+ "aria-label": ariaLabel,
13681
+ "data-slot": "cart-icon",
13682
+ className: (0, import_utils9.cn)(
13683
+ "relative inline-flex items-center justify-center rounded-lg p-2 text-foreground/80 transition-colors hover:bg-muted/60 hover:text-foreground",
13684
+ className
13685
+ ),
13686
+ children: [
13687
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
13688
+ "svg",
13689
+ {
13690
+ viewBox: "0 0 24 24",
13691
+ fill: "none",
13692
+ stroke: "currentColor",
13693
+ strokeWidth: 2,
13694
+ strokeLinecap: "round",
13695
+ strokeLinejoin: "round",
13696
+ className: "h-5 w-5",
13697
+ "aria-hidden": "true",
13698
+ children: [
13699
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("circle", { cx: "9", cy: "21", r: "1" }),
13700
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("circle", { cx: "20", cy: "21", r: "1" }),
13701
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("path", { d: "M1 1h4l2.68 13.39a2 2 0 0 0 2 1.61h9.72a2 2 0 0 0 2-1.61L23 6H6" })
13702
+ ]
13703
+ }
13704
+ ),
13705
+ itemCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
13706
+ "span",
13707
+ {
13708
+ "aria-hidden": "true",
13709
+ className: "absolute -top-0.5 -right-0.5 flex h-[18px] min-w-[18px] items-center justify-center rounded-full bg-primary px-1 text-[10px] font-bold leading-none tabular-nums text-primary-foreground",
13710
+ children: displayCount
13711
+ }
13712
+ )
13713
+ ]
13714
+ }
13715
+ );
13716
+ }
13717
+
13718
+ // src/components/cart-line-list.tsx
13719
+ var import_utils10 = require("@reblu/site-ui/utils");
13720
+ var import_jsx_runtime10 = require("react/jsx-runtime");
13721
+ var DEFAULT_LABELS4 = {
13722
+ quantityLabel: "Cantidad",
13723
+ decrease: "Reducir cantidad",
13724
+ increase: "Aumentar cantidad",
13725
+ remove: "Eliminar",
13726
+ unitPriceSuffix: "/ ud."
13727
+ };
13728
+ function lineKey(productId, variantId) {
13729
+ return `${productId}-${variantId ?? "no-variant"}`;
13730
+ }
13731
+ function CartLineList({
13732
+ items,
13733
+ onQuantityChange,
13734
+ onRemove,
13735
+ liveMessage,
13736
+ labels,
13737
+ currencyCode,
13738
+ locale,
13739
+ className
13740
+ }) {
13741
+ const t = { ...DEFAULT_LABELS4, ...labels };
13742
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: (0, import_utils10.cn)("space-y-4", className), "data-slot": "cart-line-list", children: [
13743
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { "aria-live": "polite", className: "sr-only", children: liveMessage }),
13744
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("ul", { className: "space-y-4", children: items.map((item) => {
13745
+ const maxQty = item.maxStock ?? Infinity;
13746
+ const atMax = item.quantity >= maxQty;
13747
+ const atMin = item.quantity <= 1;
13748
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
13749
+ "li",
13750
+ {
13751
+ className: "flex gap-4 rounded-xl border border-border bg-card p-4",
13752
+ children: [
13753
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "h-20 w-20 flex-shrink-0 overflow-hidden rounded-lg bg-muted", children: item.imageUrl ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("img", { src: item.imageUrl, alt: item.name, className: "h-full w-full object-cover" }) : null }),
13754
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "min-w-0 flex-1", children: [
13755
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { className: "truncate text-sm font-semibold leading-tight", children: item.name }),
13756
+ item.variantName && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { className: "mt-0.5 text-xs text-muted-foreground", children: item.variantName }),
13757
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("p", { className: "mt-1 text-sm text-muted-foreground", children: [
13758
+ formatPrice(item.unitPrice, currencyCode, locale),
13759
+ " ",
13760
+ t.unitPriceSuffix
13761
+ ] }),
13762
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
13763
+ "div",
13764
+ {
13765
+ role: "group",
13766
+ "aria-label": t.quantityLabel,
13767
+ className: "mt-3 flex items-center gap-3",
13768
+ children: [
13769
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
13770
+ "button",
13771
+ {
13772
+ type: "button",
13773
+ "aria-label": t.decrease,
13774
+ disabled: !onQuantityChange || atMin,
13775
+ onClick: onQuantityChange ? () => onQuantityChange(item.productId, item.variantId, item.quantity - 1) : void 0,
13776
+ className: "flex h-7 w-7 items-center justify-center rounded border border-border transition hover:bg-muted disabled:cursor-not-allowed disabled:opacity-40",
13777
+ children: "\u2212"
13778
+ }
13779
+ ),
13780
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "w-8 text-center text-sm font-semibold tabular-nums", children: item.quantity }),
13781
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
13782
+ "button",
13783
+ {
13784
+ type: "button",
13785
+ "aria-label": t.increase,
13786
+ disabled: !onQuantityChange || atMax,
13787
+ onClick: onQuantityChange ? () => onQuantityChange(item.productId, item.variantId, item.quantity + 1) : void 0,
13788
+ className: "flex h-7 w-7 items-center justify-center rounded border border-border transition hover:bg-muted disabled:cursor-not-allowed disabled:opacity-40",
13789
+ children: "+"
13790
+ }
13791
+ ),
13792
+ onRemove && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
13793
+ "button",
13794
+ {
13795
+ type: "button",
13796
+ "aria-label": `${t.remove} ${item.name}`,
13797
+ onClick: () => onRemove(item.productId, item.variantId),
13798
+ className: "ml-2 text-muted-foreground transition-colors hover:text-destructive",
13799
+ children: t.remove
13800
+ }
13801
+ )
13802
+ ]
13803
+ }
13804
+ )
13805
+ ] }),
13806
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "flex-shrink-0 text-right", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { className: "text-base font-bold", children: formatPrice(item.unitPrice * item.quantity, currencyCode, locale) }) })
13807
+ ]
13808
+ },
13809
+ lineKey(item.productId, item.variantId)
13810
+ );
13811
+ }) })
13812
+ ] });
13813
+ }
13814
+
13815
+ // src/components/cart-summary.tsx
13816
+ var import_utils11 = require("@reblu/site-ui/utils");
13817
+ var import_jsx_runtime11 = require("react/jsx-runtime");
13818
+ var DEFAULT_LABELS5 = {
13819
+ subtotal: "Subtotal",
13820
+ discountPrefix: "Descuento",
13821
+ shipping: "Env\xEDo",
13822
+ freeShipping: "Env\xEDo gratis",
13823
+ shippingCalculated: "Se calcula en el pago",
13824
+ total: "Total"
13825
+ };
13826
+ function CartSummary({
13827
+ subtotal,
13828
+ discount,
13829
+ discountCode,
13830
+ shipping,
13831
+ shippingEnabled,
13832
+ total,
13833
+ labels,
13834
+ currencyCode,
13835
+ locale,
13836
+ className
13837
+ }) {
13838
+ const t = { ...DEFAULT_LABELS5, ...labels };
13839
+ const hasDiscount = Boolean(discount && discount > 0);
13840
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("dl", { className: (0, import_utils11.cn)("space-y-2 text-sm", className), "data-slot": "cart-summary", children: [
13841
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex justify-between text-muted-foreground", children: [
13842
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("dt", { children: t.subtotal }),
13843
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("dd", { children: formatPrice(subtotal, currencyCode, locale) })
13844
+ ] }),
13845
+ hasDiscount && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex justify-between text-green-600 dark:text-green-400", children: [
13846
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("dt", { children: [
13847
+ t.discountPrefix,
13848
+ discountCode ? ` (${discountCode})` : ""
13849
+ ] }),
13850
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("dd", { children: [
13851
+ "-",
13852
+ formatPrice(discount, currencyCode, locale)
13853
+ ] })
13854
+ ] }),
13855
+ shippingEnabled && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex justify-between text-muted-foreground", children: [
13856
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("dt", { children: t.shipping }),
13857
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("dd", { children: shipping == null || !Number.isFinite(shipping) ? t.shippingCalculated : shipping === 0 ? t.freeShipping : formatPrice(shipping, currencyCode, locale) })
13858
+ ] }),
13859
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex justify-between border-t border-border pt-2 text-base font-bold", children: [
13860
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("dt", { children: t.total }),
13861
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("dd", { children: formatPrice(total, currencyCode, locale) })
13862
+ ] })
13863
+ ] });
13864
+ }
13865
+
13866
+ // src/components/checkout-login-prompt.tsx
13867
+ var import_utils12 = require("@reblu/site-ui/utils");
13868
+ var import_jsx_runtime12 = require("react/jsx-runtime");
13869
+ var DEFAULT_LABELS6 = {
13870
+ title: "Identificaci\xF3n",
13871
+ description: "Introduce tu email para agilizar el proceso o contin\xFAa como invitado.",
13872
+ emailLabel: "Email",
13873
+ emailPlaceholder: "tu@email.com",
13874
+ continueWithEmail: "Continuar con este email",
13875
+ checking: "Verificando\u2026",
13876
+ continueAsGuest: "Continuar como invitado",
13877
+ close: "Cerrar"
13878
+ };
13879
+ var EMAIL_PATTERN = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
13880
+ function CheckoutLoginPrompt({
13881
+ open,
13882
+ email,
13883
+ onEmailChange,
13884
+ onClose,
13885
+ onSubmitEmail,
13886
+ onContinueAsGuest,
13887
+ checking,
13888
+ error,
13889
+ labels,
13890
+ className
13891
+ }) {
13892
+ if (!open) return null;
13893
+ const t = { ...DEFAULT_LABELS6, ...labels };
13894
+ const isValidEmail = EMAIL_PATTERN.test(email.trim());
13895
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_jsx_runtime12.Fragment, { children: [
13896
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "fixed inset-0 z-50 bg-black/50", onClick: onClose, "aria-hidden": "true" }),
13897
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
13898
+ "div",
13899
+ {
13900
+ role: "dialog",
13901
+ "aria-modal": "true",
13902
+ "aria-labelledby": "checkout-login-prompt-title",
13903
+ "data-slot": "checkout-login-prompt",
13904
+ className: (0, import_utils12.cn)(
13905
+ "fixed left-1/2 top-1/2 z-50 w-full max-w-md -translate-x-1/2 -translate-y-1/2 space-y-5 rounded-2xl border border-border bg-background p-6 shadow-2xl",
13906
+ className
13907
+ ),
13908
+ children: [
13909
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "flex items-start justify-between gap-3", children: [
13910
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { children: [
13911
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("h2", { id: "checkout-login-prompt-title", className: "text-xl font-bold", children: t.title }),
13912
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { className: "mt-1 text-sm text-muted-foreground", children: t.description })
13913
+ ] }),
13914
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
13915
+ "button",
13916
+ {
13917
+ type: "button",
13918
+ onClick: onClose,
13919
+ "aria-label": t.close,
13920
+ className: "flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-lg text-muted-foreground transition-colors hover:bg-muted hover:text-foreground",
13921
+ children: "\u2715"
13922
+ }
13923
+ )
13924
+ ] }),
13925
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "space-y-2", children: [
13926
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("label", { htmlFor: "checkout-login-prompt-email", className: "text-sm font-medium", children: t.emailLabel }),
13927
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
13928
+ "input",
13929
+ {
13930
+ id: "checkout-login-prompt-email",
13931
+ type: "email",
13932
+ autoComplete: "email",
13933
+ value: email,
13934
+ onChange: (e) => onEmailChange(e.target.value),
13935
+ placeholder: t.emailPlaceholder,
13936
+ className: (0, import_utils12.cn)(
13937
+ "w-full rounded-lg border bg-background px-3 py-2 text-sm transition focus:outline-none focus:ring-2 focus:ring-primary/20",
13938
+ error ? "border-destructive" : "border-border focus:border-primary"
13939
+ )
13940
+ }
13941
+ ),
13942
+ error && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { className: "text-xs text-destructive", children: error })
13943
+ ] }),
13944
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "space-y-2", children: [
13945
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
13946
+ "button",
13947
+ {
13948
+ type: "button",
13949
+ onClick: onSubmitEmail,
13950
+ disabled: checking || !isValidEmail,
13951
+ className: (0, import_utils12.cn)(
13952
+ "flex w-full items-center justify-center gap-2 rounded-xl px-4 py-2.5 text-sm font-semibold transition-all",
13953
+ checking || !isValidEmail ? "cursor-not-allowed bg-muted text-muted-foreground" : "bg-primary text-primary-foreground hover:bg-primary/90"
13954
+ ),
13955
+ children: checking ? t.checking : t.continueWithEmail
13956
+ }
13957
+ ),
13958
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
13959
+ "button",
13960
+ {
13961
+ type: "button",
13962
+ onClick: onContinueAsGuest,
13963
+ className: "flex w-full items-center justify-center gap-2 rounded-xl bg-muted px-4 py-2.5 text-sm font-semibold text-muted-foreground transition-colors hover:bg-muted/70",
13964
+ children: t.continueAsGuest
13965
+ }
13966
+ )
13967
+ ] })
13968
+ ]
13969
+ }
13970
+ )
13971
+ ] });
13972
+ }
13973
+
13974
+ // src/components/checkout-form.tsx
13975
+ var import_utils13 = require("@reblu/site-ui/utils");
13976
+ var import_jsx_runtime13 = require("react/jsx-runtime");
13977
+ var DEFAULT_LABELS7 = {
13978
+ contactTitle: "Datos de contacto",
13979
+ nameLabel: "Nombre completo",
13980
+ namePlaceholder: "Tu nombre",
13981
+ emailLabel: "Email",
13982
+ emailPlaceholder: "tu@email.com",
13983
+ phoneLabel: "Tel\xE9fono",
13984
+ discountTitle: "C\xF3digo de descuento",
13985
+ discountPlaceholder: "C\xD3DIGO10",
13986
+ applyDiscount: "Aplicar",
13987
+ applyingDiscount: "Aplicando\u2026",
13988
+ removeDiscount: "Quitar",
13989
+ discountAppliedSuffix: (amount) => `\u2014 ${amount} de descuento`,
13990
+ submit: "Confirmar y pagar",
13991
+ submitting: "Procesando\u2026"
13992
+ };
13993
+ function CheckoutForm({
13994
+ contact,
13995
+ onContactChange,
13996
+ contactErrors,
13997
+ discountCode,
13998
+ onDiscountCodeChange,
13999
+ onApplyDiscount,
14000
+ onRemoveDiscount,
14001
+ appliedDiscount,
14002
+ discountApplying,
14003
+ discountError,
14004
+ loginPrompt,
14005
+ children: children2,
14006
+ submitting,
14007
+ submitDisabled,
14008
+ onSubmit,
14009
+ labels,
14010
+ currencyCode,
14011
+ locale,
14012
+ className
14013
+ }) {
14014
+ const t = { ...DEFAULT_LABELS7, ...labels };
14015
+ function patchContact(patch3) {
14016
+ onContactChange({ ...contact, ...patch3 });
14017
+ }
14018
+ function handleDiscountCodeKeyDown(event) {
14019
+ if (event.key !== "Enter") return;
14020
+ if (event.nativeEvent.isComposing) return;
14021
+ event.preventDefault();
14022
+ if (discountCode.trim() && !discountApplying) onApplyDiscount();
14023
+ }
14024
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_jsx_runtime13.Fragment, { children: [
14025
+ loginPrompt,
14026
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
14027
+ "form",
14028
+ {
14029
+ "data-testid": "checkout-form",
14030
+ onSubmit,
14031
+ className: (0, import_utils13.cn)("space-y-8", className),
14032
+ "data-slot": "checkout-form",
14033
+ children: [
14034
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("section", { className: "space-y-4", children: [
14035
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("h2", { className: "border-b border-border pb-2 text-lg font-semibold", children: t.contactTitle }),
14036
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "grid grid-cols-1 gap-4 sm:grid-cols-2", children: [
14037
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "space-y-1", children: [
14038
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("label", { htmlFor: "checkout-form-name", className: "text-sm font-medium", children: t.nameLabel }),
14039
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
14040
+ "input",
14041
+ {
14042
+ id: "checkout-form-name",
14043
+ type: "text",
14044
+ autoComplete: "name",
14045
+ value: contact.name,
14046
+ onChange: (e) => patchContact({ name: e.target.value }),
14047
+ placeholder: t.namePlaceholder,
14048
+ className: (0, import_utils13.cn)(
14049
+ "w-full rounded-lg border bg-background px-3 py-2 text-sm transition focus:outline-none focus:ring-2 focus:ring-primary/20",
14050
+ contactErrors?.name ? "border-destructive" : "border-border focus:border-primary"
14051
+ )
14052
+ }
14053
+ ),
14054
+ contactErrors?.name && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("p", { className: "text-xs text-destructive", children: contactErrors.name })
14055
+ ] }),
14056
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "space-y-1", children: [
14057
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("label", { htmlFor: "checkout-form-email", className: "text-sm font-medium", children: t.emailLabel }),
14058
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
14059
+ "input",
14060
+ {
14061
+ id: "checkout-form-email",
14062
+ type: "email",
14063
+ autoComplete: "email",
14064
+ value: contact.email,
14065
+ onChange: (e) => patchContact({ email: e.target.value }),
14066
+ placeholder: t.emailPlaceholder,
14067
+ className: (0, import_utils13.cn)(
14068
+ "w-full rounded-lg border bg-background px-3 py-2 text-sm transition focus:outline-none focus:ring-2 focus:ring-primary/20",
14069
+ contactErrors?.email ? "border-destructive" : "border-border focus:border-primary"
14070
+ )
14071
+ }
14072
+ ),
14073
+ contactErrors?.email && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("p", { className: "text-xs text-destructive", children: contactErrors.email })
14074
+ ] }),
14075
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "space-y-1", children: [
14076
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("label", { htmlFor: "checkout-form-phone", className: "text-sm font-medium", children: t.phoneLabel }),
14077
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
14078
+ "input",
14079
+ {
14080
+ id: "checkout-form-phone",
14081
+ type: "tel",
14082
+ autoComplete: "tel",
14083
+ value: contact.phone ?? "",
14084
+ onChange: (e) => patchContact({ phone: e.target.value }),
14085
+ className: (0, import_utils13.cn)(
14086
+ "w-full rounded-lg border bg-background px-3 py-2 text-sm transition focus:outline-none focus:ring-2 focus:ring-primary/20",
14087
+ contactErrors?.phone ? "border-destructive" : "border-border focus:border-primary"
14088
+ )
14089
+ }
14090
+ ),
14091
+ contactErrors?.phone && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("p", { className: "text-xs text-destructive", children: contactErrors.phone })
14092
+ ] })
14093
+ ] })
14094
+ ] }),
14095
+ children2,
14096
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("section", { className: "space-y-3", children: [
14097
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("h2", { className: "border-b border-border pb-2 text-lg font-semibold", children: t.discountTitle }),
14098
+ appliedDiscount ? /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex items-center justify-between rounded-lg border border-green-200 bg-green-50 px-4 py-3 dark:border-green-800 dark:bg-green-900/20", children: [
14099
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex items-center gap-2 text-sm text-green-700 dark:text-green-400", children: [
14100
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "font-semibold", children: appliedDiscount.code }),
14101
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { children: t.discountAppliedSuffix(formatPrice(appliedDiscount.amount, currencyCode, locale)) })
14102
+ ] }),
14103
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
14104
+ "button",
14105
+ {
14106
+ type: "button",
14107
+ onClick: onRemoveDiscount,
14108
+ "aria-label": t.removeDiscount,
14109
+ className: "text-green-600 transition-colors hover:text-green-800 dark:text-green-400 dark:hover:text-green-200",
14110
+ children: t.removeDiscount
14111
+ }
14112
+ )
14113
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex gap-2", children: [
14114
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("label", { htmlFor: "checkout-form-discount-code", className: "sr-only", children: t.discountTitle }),
14115
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
14116
+ "input",
14117
+ {
14118
+ id: "checkout-form-discount-code",
14119
+ type: "text",
14120
+ value: discountCode,
14121
+ onChange: (e) => onDiscountCodeChange(e.target.value.toUpperCase()),
14122
+ onKeyDown: handleDiscountCodeKeyDown,
14123
+ placeholder: t.discountPlaceholder,
14124
+ className: "flex-1 rounded-lg border border-border bg-background px-3 py-2 text-sm uppercase transition focus:border-primary focus:outline-none focus:ring-2 focus:ring-primary/20"
14125
+ }
14126
+ ),
14127
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
14128
+ "button",
14129
+ {
14130
+ type: "button",
14131
+ onClick: onApplyDiscount,
14132
+ disabled: !discountCode.trim() || discountApplying,
14133
+ className: "rounded-lg bg-muted px-4 py-2 text-sm font-medium text-foreground transition hover:bg-muted/70 disabled:cursor-not-allowed disabled:opacity-50",
14134
+ children: discountApplying ? t.applyingDiscount : t.applyDiscount
14135
+ }
14136
+ )
14137
+ ] }),
14138
+ discountError && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("p", { className: "text-xs text-destructive", children: discountError })
14139
+ ] }),
14140
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
14141
+ "button",
14142
+ {
14143
+ type: "submit",
14144
+ disabled: submitting || submitDisabled,
14145
+ className: (0, import_utils13.cn)(
14146
+ "flex w-full items-center justify-center gap-2 rounded-xl px-4 py-3 font-semibold transition-all",
14147
+ submitting || submitDisabled ? "cursor-not-allowed bg-muted text-muted-foreground" : "bg-primary text-primary-foreground hover:bg-primary/90"
14148
+ ),
14149
+ children: submitting ? t.submitting : t.submit
14150
+ }
14151
+ )
14152
+ ]
14153
+ }
14154
+ )
14155
+ ] });
14156
+ }
14157
+
14158
+ // src/components/order-confirmation.tsx
14159
+ var import_utils14 = require("@reblu/site-ui/utils");
14160
+ var import_jsx_runtime14 = require("react/jsx-runtime");
14161
+ var DEFAULT_LABELS8 = {
14162
+ pollingTitle: "Confirmando tu pedido\u2026",
14163
+ pollingDescription: "Estamos procesando el pago, un momento\u2026",
14164
+ foundTitle: "\xA1Pedido confirmado!",
14165
+ foundDescription: "Recibir\xE1s un email de confirmaci\xF3n con todos los detalles.",
14166
+ timeoutTitle: "Pago recibido",
14167
+ timeoutDescription: "Tu pago se ha procesado. Recibir\xE1s un email de confirmaci\xF3n en breve.",
14168
+ orderNumberLabel: "Pedido confirmado",
14169
+ totalLabel: "Total",
14170
+ confirmationEmailPrefix: "Te hemos enviado un email de confirmaci\xF3n a",
14171
+ sessionReferenceLabel: "Referencia de sesi\xF3n"
14172
+ };
14173
+ function OrderConfirmation({
14174
+ status,
14175
+ order,
14176
+ sessionId,
14177
+ labels,
14178
+ currencyCode,
14179
+ locale,
14180
+ className
14181
+ }) {
14182
+ const t = { ...DEFAULT_LABELS8, ...labels };
14183
+ const title = status === "polling" ? t.pollingTitle : order ? t.foundTitle : t.timeoutTitle;
14184
+ const description = status === "polling" ? t.pollingDescription : order ? t.foundDescription : t.timeoutDescription;
14185
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: (0, import_utils14.cn)("space-y-6 text-center", className), "data-slot": "order-confirmation", children: [
14186
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("h1", { className: "text-3xl font-bold", children: title }),
14187
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("p", { className: "text-lg text-muted-foreground", children: description }),
14188
+ order && /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "space-y-6", children: [
14189
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "inline-block rounded-xl bg-muted px-6 py-4", children: [
14190
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("p", { className: "mb-1 text-xs uppercase tracking-widest text-muted-foreground", children: t.orderNumberLabel }),
14191
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("p", { className: "font-mono text-lg font-semibold", children: order.orderNumber })
14192
+ ] }),
14193
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "mx-auto max-w-sm space-y-3 rounded-xl border border-border bg-card p-5 text-left", children: [
14194
+ order.items.map((item, idx) => /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
14195
+ "div",
14196
+ {
14197
+ className: "flex justify-between gap-4 text-sm",
14198
+ children: [
14199
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("span", { className: "max-w-[60%] truncate text-muted-foreground", children: [
14200
+ item.product.name,
14201
+ item.variant ? ` (${item.variant.name})` : "",
14202
+ " x",
14203
+ item.quantity
14204
+ ] }),
14205
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { children: formatPrice(item.total, currencyCode, locale) })
14206
+ ]
14207
+ },
14208
+ idx
14209
+ )),
14210
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "space-y-1 border-t border-border pt-3 text-sm", children: /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex justify-between pt-1 text-base font-bold", children: [
14211
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { children: t.totalLabel }),
14212
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { children: formatPrice(order.total, currencyCode, locale) })
14213
+ ] }) })
14214
+ ] }),
14215
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("p", { className: "mx-auto max-w-sm text-sm text-muted-foreground", children: [
14216
+ t.confirmationEmailPrefix,
14217
+ " ",
14218
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "font-medium text-foreground", children: order.customerEmail }),
14219
+ "."
14220
+ ] })
14221
+ ] }),
14222
+ !order && status === "timeout" && sessionId && /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "inline-block rounded-xl bg-muted px-6 py-4", children: [
14223
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("p", { className: "mb-1 text-xs uppercase tracking-widest text-muted-foreground", children: t.sessionReferenceLabel }),
14224
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("p", { className: "break-all font-mono text-sm font-semibold", children: sessionId })
14225
+ ] })
14226
+ ] });
14227
+ }
13618
14228
  // Annotate the CommonJS export names for ESM import in node:
13619
14229
  0 && (module.exports = {
13620
14230
  ALLOWED_YOUTUBE_HOSTS,
14231
+ AddToCartButton,
14232
+ CartIcon,
14233
+ CartLineList,
14234
+ CartSummary,
14235
+ CheckoutForm,
14236
+ CheckoutLoginPrompt,
13621
14237
  DEFAULT_SHOP_PRICE_BANDS,
13622
14238
  MarkdownRenderer,
14239
+ OrderConfirmation,
13623
14240
  ProductCard,
13624
14241
  ProfileCard,
13625
14242
  SafeHtml,