@cimplify/sdk 0.44.10 → 0.44.11

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/react.d.mts CHANGED
@@ -1025,6 +1025,10 @@ interface CartSummaryProps {
1025
1025
  onItemRemove?: (itemId: string) => void;
1026
1026
  onQuantityChange?: (itemId: string, quantity: number) => void;
1027
1027
  emptyMessage?: string;
1028
+ /** Render the totals block (subtotal/tax/total) below the items. Default true. Hosts that show their own totals (drawer, page sidebar) should pass false. */
1029
+ showTotals?: boolean;
1030
+ /** Render the "Proceed to Checkout" button. Defaults to true if `onCheckout` is provided. */
1031
+ showCheckoutButton?: boolean;
1028
1032
  className?: string;
1029
1033
  classNames?: CartSummaryClassNames;
1030
1034
  }
@@ -1034,7 +1038,7 @@ interface CartSummaryProps {
1034
1038
  * NOT a drawer or modal — just the cart content. Templates wrap this in
1035
1039
  * their own drawer/modal shell with animations.
1036
1040
  */
1037
- declare function CartSummary({ cart: serverCart, onCheckout, onItemRemove, onQuantityChange, emptyMessage, className, classNames, }: CartSummaryProps): React$1.ReactElement;
1041
+ declare function CartSummary({ cart: serverCart, onCheckout, onItemRemove, onQuantityChange, emptyMessage, showTotals, showCheckoutButton, className, classNames, }: CartSummaryProps): React$1.ReactElement;
1038
1042
 
1039
1043
  interface AvailabilityBadgeClassNames {
1040
1044
  root?: string;
package/dist/react.d.ts CHANGED
@@ -1025,6 +1025,10 @@ interface CartSummaryProps {
1025
1025
  onItemRemove?: (itemId: string) => void;
1026
1026
  onQuantityChange?: (itemId: string, quantity: number) => void;
1027
1027
  emptyMessage?: string;
1028
+ /** Render the totals block (subtotal/tax/total) below the items. Default true. Hosts that show their own totals (drawer, page sidebar) should pass false. */
1029
+ showTotals?: boolean;
1030
+ /** Render the "Proceed to Checkout" button. Defaults to true if `onCheckout` is provided. */
1031
+ showCheckoutButton?: boolean;
1028
1032
  className?: string;
1029
1033
  classNames?: CartSummaryClassNames;
1030
1034
  }
@@ -1034,7 +1038,7 @@ interface CartSummaryProps {
1034
1038
  * NOT a drawer or modal — just the cart content. Templates wrap this in
1035
1039
  * their own drawer/modal shell with animations.
1036
1040
  */
1037
- declare function CartSummary({ cart: serverCart, onCheckout, onItemRemove, onQuantityChange, emptyMessage, className, classNames, }: CartSummaryProps): React$1.ReactElement;
1041
+ declare function CartSummary({ cart: serverCart, onCheckout, onItemRemove, onQuantityChange, emptyMessage, showTotals, showCheckoutButton, className, classNames, }: CartSummaryProps): React$1.ReactElement;
1038
1042
 
1039
1043
  interface AvailabilityBadgeClassNames {
1040
1044
  root?: string;
package/dist/react.js CHANGED
@@ -6613,6 +6613,8 @@ function CartSummary({
6613
6613
  onItemRemove,
6614
6614
  onQuantityChange,
6615
6615
  emptyMessage = "Your cart is empty",
6616
+ showTotals = true,
6617
+ showCheckoutButton,
6616
6618
  className,
6617
6619
  classNames
6618
6620
  }) {
@@ -6663,45 +6665,55 @@ function CartSummary({
6663
6665
  },
6664
6666
  item.id
6665
6667
  )) }),
6666
- /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-cimplify-cart-totals": true, className: classNames?.totals, children: [
6667
- /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-cimplify-cart-subtotal": true, className: classNames?.subtotal, children: [
6668
- /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
6669
- "Subtotal (",
6670
- itemCount,
6671
- " ",
6672
- itemCount === 1 ? "item" : "items",
6673
- ")"
6674
- ] }),
6675
- /* @__PURE__ */ jsxRuntime.jsx(Price, { amount: subtotal })
6676
- ] }),
6677
- totalDiscounts != null && chunkV2O5AO7I_js.parsePrice(totalDiscounts) > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-cimplify-cart-discount": true, className: classNames?.discount, children: [
6678
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Discount" }),
6679
- /* @__PURE__ */ jsxRuntime.jsx(Price, { amount: totalDiscounts, prefix: "-" })
6680
- ] }),
6681
- deliveryFee != null && /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-cimplify-cart-delivery-fee": true, className: classNames?.deliveryFee, children: [
6682
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Delivery fee" }),
6683
- chunkV2O5AO7I_js.parsePrice(deliveryFee) > 0 ? /* @__PURE__ */ jsxRuntime.jsx(Price, { amount: deliveryFee }) : /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Free delivery" })
6684
- ] }),
6685
- serviceCharge != null && chunkV2O5AO7I_js.parsePrice(serviceCharge) > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-cimplify-cart-service-charge": true, className: classNames?.serviceCharge, children: [
6686
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Service charge" }),
6687
- /* @__PURE__ */ jsxRuntime.jsx(Price, { amount: serviceCharge })
6688
- ] }),
6689
- /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-cimplify-cart-tax": true, className: classNames?.tax, children: [
6690
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Tax" }),
6691
- /* @__PURE__ */ jsxRuntime.jsx(Price, { amount: tax })
6692
- ] }),
6693
- /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-cimplify-cart-total": true, className: classNames?.total, children: [
6694
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Total" }),
6695
- /* @__PURE__ */ jsxRuntime.jsx(Price, { amount: total })
6696
- ] })
6697
- ] }),
6698
- onCheckout && /* @__PURE__ */ jsxRuntime.jsx(
6668
+ showTotals && /* @__PURE__ */ jsxRuntime.jsxs(
6669
+ "div",
6670
+ {
6671
+ "data-cimplify-cart-totals": true,
6672
+ className: cn("mt-6 pt-4 border-t border-border space-y-2 text-sm", classNames?.totals),
6673
+ children: [
6674
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-cimplify-cart-subtotal": true, className: cn("flex items-baseline justify-between", classNames?.subtotal), children: [
6675
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-muted-foreground", children: [
6676
+ "Subtotal (",
6677
+ itemCount,
6678
+ " ",
6679
+ itemCount === 1 ? "item" : "items",
6680
+ ")"
6681
+ ] }),
6682
+ /* @__PURE__ */ jsxRuntime.jsx(Price, { amount: subtotal, className: "tabular-nums" })
6683
+ ] }),
6684
+ totalDiscounts != null && chunkV2O5AO7I_js.parsePrice(totalDiscounts) > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-cimplify-cart-discount": true, className: cn("flex items-baseline justify-between", classNames?.discount), children: [
6685
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: "Discount" }),
6686
+ /* @__PURE__ */ jsxRuntime.jsx(Price, { amount: totalDiscounts, prefix: "-", className: "tabular-nums text-primary" })
6687
+ ] }),
6688
+ deliveryFee != null && /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-cimplify-cart-delivery-fee": true, className: cn("flex items-baseline justify-between", classNames?.deliveryFee), children: [
6689
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: "Delivery" }),
6690
+ chunkV2O5AO7I_js.parsePrice(deliveryFee) > 0 ? /* @__PURE__ */ jsxRuntime.jsx(Price, { amount: deliveryFee, className: "tabular-nums" }) : /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium text-primary", children: "Free" })
6691
+ ] }),
6692
+ serviceCharge != null && chunkV2O5AO7I_js.parsePrice(serviceCharge) > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-cimplify-cart-service-charge": true, className: cn("flex items-baseline justify-between", classNames?.serviceCharge), children: [
6693
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: "Service charge" }),
6694
+ /* @__PURE__ */ jsxRuntime.jsx(Price, { amount: serviceCharge, className: "tabular-nums" })
6695
+ ] }),
6696
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-cimplify-cart-tax": true, className: cn("flex items-baseline justify-between", classNames?.tax), children: [
6697
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: "Tax" }),
6698
+ /* @__PURE__ */ jsxRuntime.jsx(Price, { amount: tax, className: "tabular-nums" })
6699
+ ] }),
6700
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-cimplify-cart-total": true, className: cn("flex items-baseline justify-between pt-3 mt-1 border-t border-border", classNames?.total), children: [
6701
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-semibold", children: "Total" }),
6702
+ /* @__PURE__ */ jsxRuntime.jsx(Price, { amount: total, className: "text-base font-bold tabular-nums" })
6703
+ ] })
6704
+ ]
6705
+ }
6706
+ ),
6707
+ (showCheckoutButton ?? Boolean(onCheckout)) && onCheckout && /* @__PURE__ */ jsxRuntime.jsx(
6699
6708
  "button",
6700
6709
  {
6701
6710
  type: "button",
6702
6711
  onClick: onCheckout,
6703
6712
  "data-cimplify-cart-checkout": true,
6704
- className: classNames?.checkout,
6713
+ className: cn(
6714
+ "mt-4 w-full h-12 rounded-full bg-foreground text-background font-semibold text-sm hover:bg-foreground/90 active:scale-[0.99] transition-all",
6715
+ classNames?.checkout
6716
+ ),
6705
6717
  children: "Proceed to Checkout"
6706
6718
  }
6707
6719
  )
@@ -10780,7 +10792,7 @@ function DefaultCartLayout({
10780
10792
  ] }),
10781
10793
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid lg:grid-cols-[1fr_360px] gap-10", children: [
10782
10794
  /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
10783
- /* @__PURE__ */ jsxRuntime.jsx(CartSummary, {}),
10795
+ /* @__PURE__ */ jsxRuntime.jsx(CartSummary, { showTotals: false, showCheckoutButton: false }),
10784
10796
  onContinueShopping && /* @__PURE__ */ jsxRuntime.jsxs(
10785
10797
  "button",
10786
10798
  {
@@ -11072,7 +11084,7 @@ function CartDrawer({
11072
11084
  }
11073
11085
  ) })
11074
11086
  ] }),
11075
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 overflow-y-auto px-6", children: cart.isEmpty ? /* @__PURE__ */ jsxRuntime.jsx(EmptyState, { onShop: handleShop }) : /* @__PURE__ */ jsxRuntime.jsx(CartSummary, {}) }),
11087
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 overflow-y-auto px-6", children: cart.isEmpty ? /* @__PURE__ */ jsxRuntime.jsx(EmptyState, { onShop: handleShop }) : /* @__PURE__ */ jsxRuntime.jsx(CartSummary, { showTotals: false, showCheckoutButton: false }) }),
11076
11088
  !cart.isEmpty && /* @__PURE__ */ jsxRuntime.jsxs("footer", { className: "border-t border-border px-6 py-5 shrink-0 space-y-3 bg-background", children: [
11077
11089
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-baseline justify-between", children: [
11078
11090
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-muted-foreground", children: "Subtotal" }),
package/dist/react.mjs CHANGED
@@ -6608,6 +6608,8 @@ function CartSummary({
6608
6608
  onItemRemove,
6609
6609
  onQuantityChange,
6610
6610
  emptyMessage = "Your cart is empty",
6611
+ showTotals = true,
6612
+ showCheckoutButton,
6611
6613
  className,
6612
6614
  classNames
6613
6615
  }) {
@@ -6658,45 +6660,55 @@ function CartSummary({
6658
6660
  },
6659
6661
  item.id
6660
6662
  )) }),
6661
- /* @__PURE__ */ jsxs("div", { "data-cimplify-cart-totals": true, className: classNames?.totals, children: [
6662
- /* @__PURE__ */ jsxs("div", { "data-cimplify-cart-subtotal": true, className: classNames?.subtotal, children: [
6663
- /* @__PURE__ */ jsxs("span", { children: [
6664
- "Subtotal (",
6665
- itemCount,
6666
- " ",
6667
- itemCount === 1 ? "item" : "items",
6668
- ")"
6669
- ] }),
6670
- /* @__PURE__ */ jsx(Price, { amount: subtotal })
6671
- ] }),
6672
- totalDiscounts != null && parsePrice(totalDiscounts) > 0 && /* @__PURE__ */ jsxs("div", { "data-cimplify-cart-discount": true, className: classNames?.discount, children: [
6673
- /* @__PURE__ */ jsx("span", { children: "Discount" }),
6674
- /* @__PURE__ */ jsx(Price, { amount: totalDiscounts, prefix: "-" })
6675
- ] }),
6676
- deliveryFee != null && /* @__PURE__ */ jsxs("div", { "data-cimplify-cart-delivery-fee": true, className: classNames?.deliveryFee, children: [
6677
- /* @__PURE__ */ jsx("span", { children: "Delivery fee" }),
6678
- parsePrice(deliveryFee) > 0 ? /* @__PURE__ */ jsx(Price, { amount: deliveryFee }) : /* @__PURE__ */ jsx("span", { children: "Free delivery" })
6679
- ] }),
6680
- serviceCharge != null && parsePrice(serviceCharge) > 0 && /* @__PURE__ */ jsxs("div", { "data-cimplify-cart-service-charge": true, className: classNames?.serviceCharge, children: [
6681
- /* @__PURE__ */ jsx("span", { children: "Service charge" }),
6682
- /* @__PURE__ */ jsx(Price, { amount: serviceCharge })
6683
- ] }),
6684
- /* @__PURE__ */ jsxs("div", { "data-cimplify-cart-tax": true, className: classNames?.tax, children: [
6685
- /* @__PURE__ */ jsx("span", { children: "Tax" }),
6686
- /* @__PURE__ */ jsx(Price, { amount: tax })
6687
- ] }),
6688
- /* @__PURE__ */ jsxs("div", { "data-cimplify-cart-total": true, className: classNames?.total, children: [
6689
- /* @__PURE__ */ jsx("span", { children: "Total" }),
6690
- /* @__PURE__ */ jsx(Price, { amount: total })
6691
- ] })
6692
- ] }),
6693
- onCheckout && /* @__PURE__ */ jsx(
6663
+ showTotals && /* @__PURE__ */ jsxs(
6664
+ "div",
6665
+ {
6666
+ "data-cimplify-cart-totals": true,
6667
+ className: cn("mt-6 pt-4 border-t border-border space-y-2 text-sm", classNames?.totals),
6668
+ children: [
6669
+ /* @__PURE__ */ jsxs("div", { "data-cimplify-cart-subtotal": true, className: cn("flex items-baseline justify-between", classNames?.subtotal), children: [
6670
+ /* @__PURE__ */ jsxs("span", { className: "text-muted-foreground", children: [
6671
+ "Subtotal (",
6672
+ itemCount,
6673
+ " ",
6674
+ itemCount === 1 ? "item" : "items",
6675
+ ")"
6676
+ ] }),
6677
+ /* @__PURE__ */ jsx(Price, { amount: subtotal, className: "tabular-nums" })
6678
+ ] }),
6679
+ totalDiscounts != null && parsePrice(totalDiscounts) > 0 && /* @__PURE__ */ jsxs("div", { "data-cimplify-cart-discount": true, className: cn("flex items-baseline justify-between", classNames?.discount), children: [
6680
+ /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "Discount" }),
6681
+ /* @__PURE__ */ jsx(Price, { amount: totalDiscounts, prefix: "-", className: "tabular-nums text-primary" })
6682
+ ] }),
6683
+ deliveryFee != null && /* @__PURE__ */ jsxs("div", { "data-cimplify-cart-delivery-fee": true, className: cn("flex items-baseline justify-between", classNames?.deliveryFee), children: [
6684
+ /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "Delivery" }),
6685
+ parsePrice(deliveryFee) > 0 ? /* @__PURE__ */ jsx(Price, { amount: deliveryFee, className: "tabular-nums" }) : /* @__PURE__ */ jsx("span", { className: "font-medium text-primary", children: "Free" })
6686
+ ] }),
6687
+ serviceCharge != null && parsePrice(serviceCharge) > 0 && /* @__PURE__ */ jsxs("div", { "data-cimplify-cart-service-charge": true, className: cn("flex items-baseline justify-between", classNames?.serviceCharge), children: [
6688
+ /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "Service charge" }),
6689
+ /* @__PURE__ */ jsx(Price, { amount: serviceCharge, className: "tabular-nums" })
6690
+ ] }),
6691
+ /* @__PURE__ */ jsxs("div", { "data-cimplify-cart-tax": true, className: cn("flex items-baseline justify-between", classNames?.tax), children: [
6692
+ /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "Tax" }),
6693
+ /* @__PURE__ */ jsx(Price, { amount: tax, className: "tabular-nums" })
6694
+ ] }),
6695
+ /* @__PURE__ */ jsxs("div", { "data-cimplify-cart-total": true, className: cn("flex items-baseline justify-between pt-3 mt-1 border-t border-border", classNames?.total), children: [
6696
+ /* @__PURE__ */ jsx("span", { className: "font-semibold", children: "Total" }),
6697
+ /* @__PURE__ */ jsx(Price, { amount: total, className: "text-base font-bold tabular-nums" })
6698
+ ] })
6699
+ ]
6700
+ }
6701
+ ),
6702
+ (showCheckoutButton ?? Boolean(onCheckout)) && onCheckout && /* @__PURE__ */ jsx(
6694
6703
  "button",
6695
6704
  {
6696
6705
  type: "button",
6697
6706
  onClick: onCheckout,
6698
6707
  "data-cimplify-cart-checkout": true,
6699
- className: classNames?.checkout,
6708
+ className: cn(
6709
+ "mt-4 w-full h-12 rounded-full bg-foreground text-background font-semibold text-sm hover:bg-foreground/90 active:scale-[0.99] transition-all",
6710
+ classNames?.checkout
6711
+ ),
6700
6712
  children: "Proceed to Checkout"
6701
6713
  }
6702
6714
  )
@@ -10775,7 +10787,7 @@ function DefaultCartLayout({
10775
10787
  ] }),
10776
10788
  /* @__PURE__ */ jsxs("div", { className: "grid lg:grid-cols-[1fr_360px] gap-10", children: [
10777
10789
  /* @__PURE__ */ jsxs("div", { children: [
10778
- /* @__PURE__ */ jsx(CartSummary, {}),
10790
+ /* @__PURE__ */ jsx(CartSummary, { showTotals: false, showCheckoutButton: false }),
10779
10791
  onContinueShopping && /* @__PURE__ */ jsxs(
10780
10792
  "button",
10781
10793
  {
@@ -11067,7 +11079,7 @@ function CartDrawer({
11067
11079
  }
11068
11080
  ) })
11069
11081
  ] }),
11070
- /* @__PURE__ */ jsx("div", { className: "flex-1 overflow-y-auto px-6", children: cart.isEmpty ? /* @__PURE__ */ jsx(EmptyState, { onShop: handleShop }) : /* @__PURE__ */ jsx(CartSummary, {}) }),
11082
+ /* @__PURE__ */ jsx("div", { className: "flex-1 overflow-y-auto px-6", children: cart.isEmpty ? /* @__PURE__ */ jsx(EmptyState, { onShop: handleShop }) : /* @__PURE__ */ jsx(CartSummary, { showTotals: false, showCheckoutButton: false }) }),
11071
11083
  !cart.isEmpty && /* @__PURE__ */ jsxs("footer", { className: "border-t border-border px-6 py-5 shrink-0 space-y-3 bg-background", children: [
11072
11084
  /* @__PURE__ */ jsxs("div", { className: "flex items-baseline justify-between", children: [
11073
11085
  /* @__PURE__ */ jsx("span", { className: "text-sm text-muted-foreground", children: "Subtotal" }),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cimplify/sdk",
3
- "version": "0.44.10",
3
+ "version": "0.44.11",
4
4
  "description": "Cimplify Commerce SDK for storefronts",
5
5
  "keywords": [
6
6
  "cimplify",
@@ -10,7 +10,7 @@
10
10
  "files": [
11
11
  {
12
12
  "path": "cart-summary.tsx",
13
- "content": "\"use client\";\n\nimport React from \"react\";\nimport type { UseCartItem } from \"@cimplify/sdk/react\";\nimport type { DisplayCart } from \"@cimplify/sdk\";\nimport { useCart } from \"@cimplify/sdk/react\";\nimport { getVariantDisplayName } from \"@cimplify/sdk\";\nimport { INPUT_FIELD_TYPE } from \"@cimplify/sdk\";\nimport { parsePrice } from \"@cimplify/sdk\";\nimport { Price } from \"@cimplify/sdk/react\";\nimport { QuantitySelector } from \"@cimplify/sdk/react\";\nimport { useProductPrice } from \"@cimplify/sdk/react\";\nimport { cn } from \"@cimplify/sdk/react\";\n\nexport interface CartSummaryClassNames {\n root?: string;\n item?: string;\n itemName?: string;\n itemPrice?: string;\n quantity?: string;\n removeButton?: string;\n totals?: string;\n subtotal?: string;\n tax?: string;\n deliveryFee?: string;\n serviceCharge?: string;\n discount?: string;\n total?: string;\n checkout?: string;\n empty?: string;\n serviceInfo?: string;\n digitalInfo?: string;\n bundleBreakdown?: string;\n compositeBreakdown?: string;\n customerInputs?: string;\n}\n\nexport interface CartSummaryProps {\n /** Optional server cart for extended pricing details (delivery fee, service charge, discounts). */\n cart?: DisplayCart;\n onCheckout?: () => void;\n onItemRemove?: (itemId: string) => void;\n onQuantityChange?: (itemId: string, quantity: number) => void;\n emptyMessage?: string;\n className?: string;\n classNames?: CartSummaryClassNames;\n}\n\nfunction CartLineItemRow({\n item,\n onRemove,\n onQuantityChange,\n classNames,\n}: {\n item: UseCartItem;\n onRemove: (itemId: string) => void;\n onQuantityChange: (itemId: string, qty: number) => void;\n classNames?: CartSummaryClassNames;\n}): React.ReactElement {\n const { unitPrice } = useProductPrice({\n product: item.product,\n variant: item.variant,\n addOnOptions: item.addOnOptions,\n });\n const hasComposite = item.compositeSelections && item.compositeSelections.length > 0;\n const hasBundle = item.bundleSelections && item.bundleSelections.length > 0;\n\n const isOptimistic = (item as { isOptimistic?: boolean }).isOptimistic === true;\n\n const lineTotal = parsePrice(unitPrice) * item.quantity;\n const imageUrl = item.product.image_url;\n const variantLabel = item.variant ? getVariantDisplayName(item.variant, item.product.name) : null;\n\n return (\n <div\n data-cimplify-cart-item\n data-optimistic={isOptimistic ? \"true\" : undefined}\n className={cn(\n \"group relative flex gap-3 py-4 border-b border-border last:border-b-0 transition-opacity\",\n isOptimistic && \"opacity-60\",\n classNames?.item,\n )}\n >\n {/* Thumbnail */}\n <div\n data-cimplify-cart-item-image\n className=\"relative w-20 h-20 shrink-0 rounded-md overflow-hidden bg-muted\"\n >\n {imageUrl ? (\n <img\n src={imageUrl}\n alt=\"\"\n className=\"w-full h-full object-cover\"\n loading=\"lazy\"\n />\n ) : (\n <div className=\"w-full h-full grid place-items-center text-muted-foreground/40\">\n <svg className=\"w-6 h-6\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.5\" viewBox=\"0 0 24 24\" aria-hidden>\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M2.25 15.75l5.159-5.159a2.25 2.25 0 013.182 0l5.159 5.159m-1.5-1.5l1.409-1.409a2.25 2.25 0 013.182 0l2.909 2.909m-18 3.75h16.5a1.5 1.5 0 001.5-1.5V6a1.5 1.5 0 00-1.5-1.5H3.75A1.5 1.5 0 002.25 6v12a1.5 1.5 0 001.5 1.5zm10.5-11.25h.008v.008h-.008V8.25zm.375 0a.375.375 0 11-.75 0 .375.375 0 01.75 0z\" />\n </svg>\n </div>\n )}\n </div>\n\n {/* Info column */}\n <div data-cimplify-cart-item-info className=\"flex-1 min-w-0 flex flex-col\">\n <div className=\"flex items-start justify-between gap-3\">\n <h3\n data-cimplify-cart-item-name\n className={cn(\"text-sm font-medium leading-tight m-0 truncate\", classNames?.itemName)}\n >\n {item.product.name}\n </h3>\n <button\n type=\"button\"\n onClick={() => onRemove(item.id)}\n data-cimplify-cart-item-remove\n className={cn(\n \"shrink-0 -mr-1 -mt-1 grid place-items-center w-7 h-7 rounded-full text-muted-foreground/60 hover:text-foreground hover:bg-muted transition-colors\",\n classNames?.removeButton,\n )}\n aria-label={`Remove ${item.product.name}`}\n >\n <svg className=\"w-3.5 h-3.5\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" viewBox=\"0 0 24 24\" aria-hidden>\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M6 18L18 6M6 6l12 12\" />\n </svg>\n </button>\n </div>\n\n {/* Pills row: variant, add-ons, badges */}\n {(variantLabel || (item.addOnOptions && item.addOnOptions.length > 0) || hasComposite || hasBundle) && (\n <div data-cimplify-cart-item-pills className=\"flex flex-wrap gap-1.5 mt-1.5\">\n {variantLabel && (\n <span\n data-cimplify-cart-item-variant\n className=\"inline-flex items-center px-2 py-0.5 rounded-full bg-muted text-[11px] font-medium text-foreground\"\n >\n {variantLabel}\n </span>\n )}\n {item.addOnOptions?.map((opt) => (\n <span\n key={opt.id}\n data-cimplify-cart-item-addon\n className=\"inline-flex items-center px-2 py-0.5 rounded-full bg-muted text-[11px] font-medium text-muted-foreground\"\n >\n + {opt.name}\n </span>\n ))}\n {(hasComposite || hasBundle) && (\n <span\n data-cimplify-cart-item-badge\n className=\"inline-flex items-center px-2 py-0.5 rounded-full bg-primary/10 text-[11px] font-semibold uppercase tracking-wider text-primary\"\n >\n {hasComposite ? \"Custom\" : \"Bundle\"}\n </span>\n )}\n </div>\n )}\n\n {/* Special instructions */}\n {item.specialInstructions && (\n <p\n data-cimplify-cart-item-instructions\n className=\"mt-1.5 text-xs italic text-muted-foreground line-clamp-2\"\n >\n \\u201C{item.specialInstructions}\\u201D\n </p>\n )}\n\n {/* Customer input values */}\n {item.customerInputs && item.customerInputs.length > 0 && (\n <dl data-cimplify-cart-customer-inputs className={cn(\"mt-1.5 text-xs space-y-0.5\", classNames?.customerInputs)}>\n {item.customerInputs.map((input) => (\n <div key={input.field_id} data-cimplify-cart-input className=\"flex gap-1.5\">\n <dt data-cimplify-cart-input-label className=\"text-muted-foreground\">{input.field_name}:</dt>\n <dd data-cimplify-cart-input-value className=\"text-foreground m-0 truncate\">\n {input.field_type === INPUT_FIELD_TYPE.File || input.field_type === INPUT_FIELD_TYPE.Image\n ? String(input.value).split(\"/\").pop() || \"Uploaded file\"\n : String(input.value)}\n </dd>\n </div>\n ))}\n </dl>\n )}\n\n {/* Service: scheduled details */}\n {(item.lineType === \"service\" || item.product.type === \"service\") && item.scheduledStart && (\n <div data-cimplify-cart-service-info className={cn(\"mt-1.5 flex items-center gap-1.5 text-xs text-muted-foreground\", classNames?.serviceInfo)}>\n <svg className=\"w-3.5 h-3.5 shrink-0\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" viewBox=\"0 0 24 24\" aria-hidden>\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z\" />\n </svg>\n <span>\n {new Date(item.scheduledStart).toLocaleDateString(undefined, { month: \"short\", day: \"numeric\" })} at{\" \"}\n {new Date(item.scheduledStart).toLocaleTimeString([], { hour: \"2-digit\", minute: \"2-digit\" })}\n </span>\n {item.serviceStatus && (\n <span data-status={item.serviceStatus} className=\"ml-auto px-1.5 py-0.5 rounded-full bg-muted text-[10px] uppercase tracking-wider\">\n {item.serviceStatus}\n </span>\n )}\n </div>\n )}\n\n {/* Digital: instant-delivery badge */}\n {(item.lineType === \"digital\" || item.product.type === \"digital\") && (\n <div data-cimplify-cart-digital-info className={cn(\"mt-1.5 flex items-center gap-1 text-xs text-muted-foreground\", classNames?.digitalInfo)}>\n <svg className=\"w-3.5 h-3.5 shrink-0\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" viewBox=\"0 0 24 24\" aria-hidden>\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M13 10V3L4 14h7v7l9-11h-7z\" />\n </svg>\n <span>Instant digital delivery</span>\n </div>\n )}\n\n {/* Bundle / composite component breakdown */}\n {(item.bundleResolved || item.compositeResolved) && (\n <ul\n data-cimplify-cart-composite-breakdown={item.compositeResolved ? \"\" : undefined}\n data-cimplify-cart-bundle-breakdown={item.bundleResolved ? \"\" : undefined}\n className={cn(\"mt-1.5 pl-3 border-l border-border space-y-0.5 text-xs text-muted-foreground\", item.compositeResolved ? classNames?.compositeBreakdown : classNames?.bundleBreakdown)}\n >\n {((item.bundleResolved?.selections ?? item.compositeResolved?.selections ?? []) as Array<{\n component_id?: string;\n product_name?: string;\n component_name?: string;\n quantity: number;\n product_type?: string;\n }>).map((sel) => {\n const name = sel.product_name ?? sel.component_name ?? \"\";\n const compId = sel.component_id ?? `${name}-${sel.quantity}`;\n return (\n <li key={compId} data-product-type={sel.product_type} className=\"flex items-baseline gap-1.5 m-0\">\n <span className=\"font-medium text-foreground/80\">{sel.quantity}\\u00D7</span>\n <span className=\"truncate\">{name}</span>\n </li>\n );\n })}\n </ul>\n )}\n\n {/* Bottom row: line price + qty stepper */}\n <div className=\"mt-3 flex items-center justify-between gap-3\">\n <Price\n amount={lineTotal}\n className={cn(\"text-sm font-semibold tabular-nums\", classNames?.itemPrice)}\n />\n <div data-cimplify-cart-item-controls className={classNames?.quantity}>\n <QuantitySelector\n value={item.quantity}\n onChange={(qty) => onQuantityChange(item.id, qty)}\n min={0}\n />\n </div>\n </div>\n </div>\n </div>\n );\n}\n\n/**\n * CartSummary — renders cart line items + totals.\n *\n * NOT a drawer or modal — just the cart content. Templates wrap this in\n * their own drawer/modal shell with animations.\n */\nexport function CartSummary({\n cart: serverCart,\n onCheckout,\n onItemRemove,\n onQuantityChange,\n emptyMessage = \"Your cart is empty\",\n className,\n classNames,\n}: CartSummaryProps): React.ReactElement {\n const {\n items,\n itemCount,\n subtotal,\n tax,\n total,\n isEmpty,\n isOptimistic,\n pendingOpCount,\n removeItem,\n updateQuantity,\n } = useCart();\n\n const deliveryFee = serverCart?.delivery_fee;\n const serviceCharge = serverCart?.service_charge;\n const totalDiscounts = serverCart?.total_discounts;\n\n const handleRemove = (itemId: string) => {\n if (onItemRemove) {\n onItemRemove(itemId);\n } else {\n void removeItem(itemId);\n }\n };\n\n const handleQuantityChange = (itemId: string, qty: number) => {\n if (onQuantityChange) {\n onQuantityChange(itemId, qty);\n } else {\n void updateQuantity(itemId, qty);\n }\n };\n\n return (\n <div\n data-cimplify-cart-summary\n data-optimistic={isOptimistic ? \"true\" : undefined}\n data-pending-ops={pendingOpCount > 0 ? pendingOpCount : undefined}\n className={cn(className, classNames?.root)}\n >\n {isEmpty ? (\n <div data-cimplify-cart-empty className={classNames?.empty}>\n <p>{emptyMessage}</p>\n </div>\n ) : (\n <>\n {/* Line items */}\n <div data-cimplify-cart-items>\n {items.map((item) => (\n <CartLineItemRow\n key={item.id}\n item={item}\n onRemove={handleRemove}\n onQuantityChange={handleQuantityChange}\n classNames={classNames}\n />\n ))}\n </div>\n\n {/* Totals */}\n <div data-cimplify-cart-totals className={classNames?.totals}>\n <div data-cimplify-cart-subtotal className={classNames?.subtotal}>\n <span>Subtotal ({itemCount} {itemCount === 1 ? \"item\" : \"items\"})</span>\n <Price amount={subtotal} />\n </div>\n\n {totalDiscounts != null && parsePrice(totalDiscounts) > 0 && (\n <div data-cimplify-cart-discount className={classNames?.discount}>\n <span>Discount</span>\n <Price amount={totalDiscounts} prefix=\"-\" />\n </div>\n )}\n\n {deliveryFee != null && (\n <div data-cimplify-cart-delivery-fee className={classNames?.deliveryFee}>\n <span>Delivery fee</span>\n {parsePrice(deliveryFee) > 0 ? (\n <Price amount={deliveryFee} />\n ) : (\n <span>Free delivery</span>\n )}\n </div>\n )}\n\n {serviceCharge != null && parsePrice(serviceCharge) > 0 && (\n <div data-cimplify-cart-service-charge className={classNames?.serviceCharge}>\n <span>Service charge</span>\n <Price amount={serviceCharge} />\n </div>\n )}\n\n <div data-cimplify-cart-tax className={classNames?.tax}>\n <span>Tax</span>\n <Price amount={tax} />\n </div>\n <div data-cimplify-cart-total className={classNames?.total}>\n <span>Total</span>\n <Price amount={total} />\n </div>\n </div>\n\n {onCheckout && (\n <button\n type=\"button\"\n onClick={onCheckout}\n data-cimplify-cart-checkout\n className={classNames?.checkout}\n >\n Proceed to Checkout\n </button>\n )}\n </>\n )}\n </div>\n );\n}\n"
13
+ "content": "\"use client\";\n\nimport React from \"react\";\nimport type { UseCartItem } from \"@cimplify/sdk/react\";\nimport type { DisplayCart } from \"@cimplify/sdk\";\nimport { useCart } from \"@cimplify/sdk/react\";\nimport { getVariantDisplayName } from \"@cimplify/sdk\";\nimport { INPUT_FIELD_TYPE } from \"@cimplify/sdk\";\nimport { parsePrice } from \"@cimplify/sdk\";\nimport { Price } from \"@cimplify/sdk/react\";\nimport { QuantitySelector } from \"@cimplify/sdk/react\";\nimport { useProductPrice } from \"@cimplify/sdk/react\";\nimport { cn } from \"@cimplify/sdk/react\";\n\nexport interface CartSummaryClassNames {\n root?: string;\n item?: string;\n itemName?: string;\n itemPrice?: string;\n quantity?: string;\n removeButton?: string;\n totals?: string;\n subtotal?: string;\n tax?: string;\n deliveryFee?: string;\n serviceCharge?: string;\n discount?: string;\n total?: string;\n checkout?: string;\n empty?: string;\n serviceInfo?: string;\n digitalInfo?: string;\n bundleBreakdown?: string;\n compositeBreakdown?: string;\n customerInputs?: string;\n}\n\nexport interface CartSummaryProps {\n /** Optional server cart for extended pricing details (delivery fee, service charge, discounts). */\n cart?: DisplayCart;\n onCheckout?: () => void;\n onItemRemove?: (itemId: string) => void;\n onQuantityChange?: (itemId: string, quantity: number) => void;\n emptyMessage?: string;\n /** Render the totals block (subtotal/tax/total) below the items. Default true. Hosts that show their own totals (drawer, page sidebar) should pass false. */\n showTotals?: boolean;\n /** Render the \"Proceed to Checkout\" button. Defaults to true if `onCheckout` is provided. */\n showCheckoutButton?: boolean;\n className?: string;\n classNames?: CartSummaryClassNames;\n}\n\nfunction CartLineItemRow({\n item,\n onRemove,\n onQuantityChange,\n classNames,\n}: {\n item: UseCartItem;\n onRemove: (itemId: string) => void;\n onQuantityChange: (itemId: string, qty: number) => void;\n classNames?: CartSummaryClassNames;\n}): React.ReactElement {\n const { unitPrice } = useProductPrice({\n product: item.product,\n variant: item.variant,\n addOnOptions: item.addOnOptions,\n });\n const hasComposite = item.compositeSelections && item.compositeSelections.length > 0;\n const hasBundle = item.bundleSelections && item.bundleSelections.length > 0;\n\n const isOptimistic = (item as { isOptimistic?: boolean }).isOptimistic === true;\n\n const lineTotal = parsePrice(unitPrice) * item.quantity;\n const imageUrl = item.product.image_url;\n const variantLabel = item.variant ? getVariantDisplayName(item.variant, item.product.name) : null;\n\n return (\n <div\n data-cimplify-cart-item\n data-optimistic={isOptimistic ? \"true\" : undefined}\n className={cn(\n \"group relative flex gap-3 py-4 border-b border-border last:border-b-0 transition-opacity\",\n isOptimistic && \"opacity-60\",\n classNames?.item,\n )}\n >\n {/* Thumbnail */}\n <div\n data-cimplify-cart-item-image\n className=\"relative w-20 h-20 shrink-0 rounded-md overflow-hidden bg-muted\"\n >\n {imageUrl ? (\n <img\n src={imageUrl}\n alt=\"\"\n className=\"w-full h-full object-cover\"\n loading=\"lazy\"\n />\n ) : (\n <div className=\"w-full h-full grid place-items-center text-muted-foreground/40\">\n <svg className=\"w-6 h-6\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.5\" viewBox=\"0 0 24 24\" aria-hidden>\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M2.25 15.75l5.159-5.159a2.25 2.25 0 013.182 0l5.159 5.159m-1.5-1.5l1.409-1.409a2.25 2.25 0 013.182 0l2.909 2.909m-18 3.75h16.5a1.5 1.5 0 001.5-1.5V6a1.5 1.5 0 00-1.5-1.5H3.75A1.5 1.5 0 002.25 6v12a1.5 1.5 0 001.5 1.5zm10.5-11.25h.008v.008h-.008V8.25zm.375 0a.375.375 0 11-.75 0 .375.375 0 01.75 0z\" />\n </svg>\n </div>\n )}\n </div>\n\n {/* Info column */}\n <div data-cimplify-cart-item-info className=\"flex-1 min-w-0 flex flex-col\">\n <div className=\"flex items-start justify-between gap-3\">\n <h3\n data-cimplify-cart-item-name\n className={cn(\"text-sm font-medium leading-tight m-0 truncate\", classNames?.itemName)}\n >\n {item.product.name}\n </h3>\n <button\n type=\"button\"\n onClick={() => onRemove(item.id)}\n data-cimplify-cart-item-remove\n className={cn(\n \"shrink-0 -mr-1 -mt-1 grid place-items-center w-7 h-7 rounded-full text-muted-foreground/60 hover:text-foreground hover:bg-muted transition-colors\",\n classNames?.removeButton,\n )}\n aria-label={`Remove ${item.product.name}`}\n >\n <svg className=\"w-3.5 h-3.5\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" viewBox=\"0 0 24 24\" aria-hidden>\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M6 18L18 6M6 6l12 12\" />\n </svg>\n </button>\n </div>\n\n {/* Pills row: variant, add-ons, badges */}\n {(variantLabel || (item.addOnOptions && item.addOnOptions.length > 0) || hasComposite || hasBundle) && (\n <div data-cimplify-cart-item-pills className=\"flex flex-wrap gap-1.5 mt-1.5\">\n {variantLabel && (\n <span\n data-cimplify-cart-item-variant\n className=\"inline-flex items-center px-2 py-0.5 rounded-full bg-muted text-[11px] font-medium text-foreground\"\n >\n {variantLabel}\n </span>\n )}\n {item.addOnOptions?.map((opt) => (\n <span\n key={opt.id}\n data-cimplify-cart-item-addon\n className=\"inline-flex items-center px-2 py-0.5 rounded-full bg-muted text-[11px] font-medium text-muted-foreground\"\n >\n + {opt.name}\n </span>\n ))}\n {(hasComposite || hasBundle) && (\n <span\n data-cimplify-cart-item-badge\n className=\"inline-flex items-center px-2 py-0.5 rounded-full bg-primary/10 text-[11px] font-semibold uppercase tracking-wider text-primary\"\n >\n {hasComposite ? \"Custom\" : \"Bundle\"}\n </span>\n )}\n </div>\n )}\n\n {/* Special instructions */}\n {item.specialInstructions && (\n <p\n data-cimplify-cart-item-instructions\n className=\"mt-1.5 text-xs italic text-muted-foreground line-clamp-2\"\n >\n \\u201C{item.specialInstructions}\\u201D\n </p>\n )}\n\n {/* Customer input values */}\n {item.customerInputs && item.customerInputs.length > 0 && (\n <dl data-cimplify-cart-customer-inputs className={cn(\"mt-1.5 text-xs space-y-0.5\", classNames?.customerInputs)}>\n {item.customerInputs.map((input) => (\n <div key={input.field_id} data-cimplify-cart-input className=\"flex gap-1.5\">\n <dt data-cimplify-cart-input-label className=\"text-muted-foreground\">{input.field_name}:</dt>\n <dd data-cimplify-cart-input-value className=\"text-foreground m-0 truncate\">\n {input.field_type === INPUT_FIELD_TYPE.File || input.field_type === INPUT_FIELD_TYPE.Image\n ? String(input.value).split(\"/\").pop() || \"Uploaded file\"\n : String(input.value)}\n </dd>\n </div>\n ))}\n </dl>\n )}\n\n {/* Service: scheduled details */}\n {(item.lineType === \"service\" || item.product.type === \"service\") && item.scheduledStart && (\n <div data-cimplify-cart-service-info className={cn(\"mt-1.5 flex items-center gap-1.5 text-xs text-muted-foreground\", classNames?.serviceInfo)}>\n <svg className=\"w-3.5 h-3.5 shrink-0\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" viewBox=\"0 0 24 24\" aria-hidden>\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z\" />\n </svg>\n <span>\n {new Date(item.scheduledStart).toLocaleDateString(undefined, { month: \"short\", day: \"numeric\" })} at{\" \"}\n {new Date(item.scheduledStart).toLocaleTimeString([], { hour: \"2-digit\", minute: \"2-digit\" })}\n </span>\n {item.serviceStatus && (\n <span data-status={item.serviceStatus} className=\"ml-auto px-1.5 py-0.5 rounded-full bg-muted text-[10px] uppercase tracking-wider\">\n {item.serviceStatus}\n </span>\n )}\n </div>\n )}\n\n {/* Digital: instant-delivery badge */}\n {(item.lineType === \"digital\" || item.product.type === \"digital\") && (\n <div data-cimplify-cart-digital-info className={cn(\"mt-1.5 flex items-center gap-1 text-xs text-muted-foreground\", classNames?.digitalInfo)}>\n <svg className=\"w-3.5 h-3.5 shrink-0\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" viewBox=\"0 0 24 24\" aria-hidden>\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M13 10V3L4 14h7v7l9-11h-7z\" />\n </svg>\n <span>Instant digital delivery</span>\n </div>\n )}\n\n {/* Bundle / composite component breakdown */}\n {(item.bundleResolved || item.compositeResolved) && (\n <ul\n data-cimplify-cart-composite-breakdown={item.compositeResolved ? \"\" : undefined}\n data-cimplify-cart-bundle-breakdown={item.bundleResolved ? \"\" : undefined}\n className={cn(\"mt-1.5 pl-3 border-l border-border space-y-0.5 text-xs text-muted-foreground\", item.compositeResolved ? classNames?.compositeBreakdown : classNames?.bundleBreakdown)}\n >\n {((item.bundleResolved?.selections ?? item.compositeResolved?.selections ?? []) as Array<{\n component_id?: string;\n product_name?: string;\n component_name?: string;\n quantity: number;\n product_type?: string;\n }>).map((sel) => {\n const name = sel.product_name ?? sel.component_name ?? \"\";\n const compId = sel.component_id ?? `${name}-${sel.quantity}`;\n return (\n <li key={compId} data-product-type={sel.product_type} className=\"flex items-baseline gap-1.5 m-0\">\n <span className=\"font-medium text-foreground/80\">{sel.quantity}\\u00D7</span>\n <span className=\"truncate\">{name}</span>\n </li>\n );\n })}\n </ul>\n )}\n\n {/* Bottom row: line price + qty stepper */}\n <div className=\"mt-3 flex items-center justify-between gap-3\">\n <Price\n amount={lineTotal}\n className={cn(\"text-sm font-semibold tabular-nums\", classNames?.itemPrice)}\n />\n <div data-cimplify-cart-item-controls className={classNames?.quantity}>\n <QuantitySelector\n value={item.quantity}\n onChange={(qty) => onQuantityChange(item.id, qty)}\n min={0}\n />\n </div>\n </div>\n </div>\n </div>\n );\n}\n\n/**\n * CartSummary — renders cart line items + totals.\n *\n * NOT a drawer or modal — just the cart content. Templates wrap this in\n * their own drawer/modal shell with animations.\n */\nexport function CartSummary({\n cart: serverCart,\n onCheckout,\n onItemRemove,\n onQuantityChange,\n emptyMessage = \"Your cart is empty\",\n showTotals = true,\n showCheckoutButton,\n className,\n classNames,\n}: CartSummaryProps): React.ReactElement {\n const {\n items,\n itemCount,\n subtotal,\n tax,\n total,\n isEmpty,\n isOptimistic,\n pendingOpCount,\n removeItem,\n updateQuantity,\n } = useCart();\n\n const deliveryFee = serverCart?.delivery_fee;\n const serviceCharge = serverCart?.service_charge;\n const totalDiscounts = serverCart?.total_discounts;\n\n const handleRemove = (itemId: string) => {\n if (onItemRemove) {\n onItemRemove(itemId);\n } else {\n void removeItem(itemId);\n }\n };\n\n const handleQuantityChange = (itemId: string, qty: number) => {\n if (onQuantityChange) {\n onQuantityChange(itemId, qty);\n } else {\n void updateQuantity(itemId, qty);\n }\n };\n\n return (\n <div\n data-cimplify-cart-summary\n data-optimistic={isOptimistic ? \"true\" : undefined}\n data-pending-ops={pendingOpCount > 0 ? pendingOpCount : undefined}\n className={cn(className, classNames?.root)}\n >\n {isEmpty ? (\n <div data-cimplify-cart-empty className={classNames?.empty}>\n <p>{emptyMessage}</p>\n </div>\n ) : (\n <>\n {/* Line items */}\n <div data-cimplify-cart-items>\n {items.map((item) => (\n <CartLineItemRow\n key={item.id}\n item={item}\n onRemove={handleRemove}\n onQuantityChange={handleQuantityChange}\n classNames={classNames}\n />\n ))}\n </div>\n\n {/* Totals */}\n {showTotals && (\n <div\n data-cimplify-cart-totals\n className={cn(\"mt-6 pt-4 border-t border-border space-y-2 text-sm\", classNames?.totals)}\n >\n <div data-cimplify-cart-subtotal className={cn(\"flex items-baseline justify-between\", classNames?.subtotal)}>\n <span className=\"text-muted-foreground\">\n Subtotal ({itemCount} {itemCount === 1 ? \"item\" : \"items\"})\n </span>\n <Price amount={subtotal} className=\"tabular-nums\" />\n </div>\n\n {totalDiscounts != null && parsePrice(totalDiscounts) > 0 && (\n <div data-cimplify-cart-discount className={cn(\"flex items-baseline justify-between\", classNames?.discount)}>\n <span className=\"text-muted-foreground\">Discount</span>\n <Price amount={totalDiscounts} prefix=\"-\" className=\"tabular-nums text-primary\" />\n </div>\n )}\n\n {deliveryFee != null && (\n <div data-cimplify-cart-delivery-fee className={cn(\"flex items-baseline justify-between\", classNames?.deliveryFee)}>\n <span className=\"text-muted-foreground\">Delivery</span>\n {parsePrice(deliveryFee) > 0 ? (\n <Price amount={deliveryFee} className=\"tabular-nums\" />\n ) : (\n <span className=\"font-medium text-primary\">Free</span>\n )}\n </div>\n )}\n\n {serviceCharge != null && parsePrice(serviceCharge) > 0 && (\n <div data-cimplify-cart-service-charge className={cn(\"flex items-baseline justify-between\", classNames?.serviceCharge)}>\n <span className=\"text-muted-foreground\">Service charge</span>\n <Price amount={serviceCharge} className=\"tabular-nums\" />\n </div>\n )}\n\n <div data-cimplify-cart-tax className={cn(\"flex items-baseline justify-between\", classNames?.tax)}>\n <span className=\"text-muted-foreground\">Tax</span>\n <Price amount={tax} className=\"tabular-nums\" />\n </div>\n\n <div data-cimplify-cart-total className={cn(\"flex items-baseline justify-between pt-3 mt-1 border-t border-border\", classNames?.total)}>\n <span className=\"font-semibold\">Total</span>\n <Price amount={total} className=\"text-base font-bold tabular-nums\" />\n </div>\n </div>\n )}\n\n {(showCheckoutButton ?? Boolean(onCheckout)) && onCheckout && (\n <button\n type=\"button\"\n onClick={onCheckout}\n data-cimplify-cart-checkout\n className={cn(\n \"mt-4 w-full h-12 rounded-full bg-foreground text-background font-semibold text-sm hover:bg-foreground/90 active:scale-[0.99] transition-all\",\n classNames?.checkout,\n )}\n >\n Proceed to Checkout\n </button>\n )}\n </>\n )}\n </div>\n );\n}\n"
14
14
  }
15
15
  ]
16
16
  }