{
  "name": "cart-summary",
  "title": "CartSummary",
  "description": "Cart line items with quantity controls and totals.",
  "type": "component",
  "registryDependencies": [
    "price",
    "quantity-selector"
  ],
  "files": [
    {
      "path": "cart-summary.tsx",
      "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 { currencyCode, type CurrencyCode } 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 type { InternalUseCartItem } from \"./hooks/use-cart-types\";\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  currency,\n  classNames,\n}: {\n  item: UseCartItem;\n  onRemove: (itemId: string) => void;\n  onQuantityChange: (itemId: string, qty: number) => void;\n  currency: CurrencyCode;\n  classNames?: CartSummaryClassNames;\n}): React.ReactElement {\n  const { unitPrice: locallyResolvedUnitPrice } = useProductPrice({\n    product: item.product,\n    variant: item.variant,\n    addOnOptions: item.addOnOptions,\n  });\n  const pinnedUnitPrice = (item as InternalUseCartItem)._quotedUnitPrice;\n  const unitPrice =\n    pinnedUnitPrice === undefined ? locallyResolvedUnitPrice : parsePrice(pinnedUnitPrice);\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 src={imageUrl} alt=\"\" className=\"w-full h-full object-cover\" loading=\"lazy\" />\n        ) : (\n          <div className=\"w-full h-full grid place-items-center text-muted-foreground/40\">\n            <svg\n              className=\"w-6 h-6\"\n              fill=\"none\"\n              stroke=\"currentColor\"\n              strokeWidth=\"1.5\"\n              viewBox=\"0 0 24 24\"\n              aria-hidden\n            >\n              <path\n                strokeLinecap=\"round\"\n                strokeLinejoin=\"round\"\n                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              />\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\n              className=\"w-3.5 h-3.5\"\n              fill=\"none\"\n              stroke=\"currentColor\"\n              strokeWidth=\"2\"\n              viewBox=\"0 0 24 24\"\n              aria-hidden\n            >\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 ||\n          (item.addOnOptions && item.addOnOptions.length > 0) ||\n          hasComposite ||\n          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\n            data-cimplify-cart-customer-inputs\n            className={cn(\"mt-1.5 text-xs space-y-0.5\", classNames?.customerInputs)}\n          >\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\">\n                  {input.field_name}:\n                </dt>\n                <dd data-cimplify-cart-input-value className=\"text-foreground m-0 truncate\">\n                  {input.field_type === INPUT_FIELD_TYPE.File ||\n                  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\") &&\n          item.scheduledStart && (\n            <div\n              data-cimplify-cart-service-info\n              className={cn(\n                \"mt-1.5 flex items-center gap-1.5 text-xs text-muted-foreground\",\n                classNames?.serviceInfo,\n              )}\n            >\n              <svg\n                className=\"w-3.5 h-3.5 shrink-0\"\n                fill=\"none\"\n                stroke=\"currentColor\"\n                strokeWidth=\"2\"\n                viewBox=\"0 0 24 24\"\n                aria-hidden\n              >\n                <path\n                  strokeLinecap=\"round\"\n                  strokeLinejoin=\"round\"\n                  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                />\n              </svg>\n              <span>\n                {new Date(item.scheduledStart).toLocaleDateString(undefined, {\n                  month: \"short\",\n                  day: \"numeric\",\n                })}{\" \"}\n                at{\" \"}\n                {new Date(item.scheduledStart).toLocaleTimeString([], {\n                  hour: \"2-digit\",\n                  minute: \"2-digit\",\n                })}\n              </span>\n              {item.serviceStatus && (\n                <span\n                  data-status={item.serviceStatus}\n                  className=\"ml-auto px-1.5 py-0.5 rounded-full bg-muted text-[10px] uppercase tracking-wider\"\n                >\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\n            data-cimplify-cart-digital-info\n            className={cn(\n              \"mt-1.5 flex items-center gap-1 text-xs text-muted-foreground\",\n              classNames?.digitalInfo,\n            )}\n          >\n            <svg\n              className=\"w-3.5 h-3.5 shrink-0\"\n              fill=\"none\"\n              stroke=\"currentColor\"\n              strokeWidth=\"2\"\n              viewBox=\"0 0 24 24\"\n              aria-hidden\n            >\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(\n              \"mt-1.5 pl-3 border-l border-border space-y-0.5 text-xs text-muted-foreground\",\n              item.compositeResolved ? classNames?.compositeBreakdown : classNames?.bundleBreakdown,\n            )}\n          >\n            {(\n              (item.bundleResolved?.selections ??\n                item.compositeResolved?.selections ??\n                []) as Array<{\n                component_id?: string;\n                product_name?: string;\n                component_name?: string;\n                quantity: number;\n                product_type?: string;\n              }>\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\n                  key={compId}\n                  data-product-type={sel.product_type}\n                  className=\"flex items-baseline gap-1.5 m-0\"\n                >\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            currency={currency}\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    taxRate,\n    taxInclusive,\n    taxJurisdictionLabel,\n    total,\n    isEmpty,\n    isOptimistic,\n    pendingOpCount,\n    currency,\n    removeItem,\n    updateQuantity,\n  } = useCart();\n  const displayCurrency = serverCart?.currency ?? currencyCode(currency);\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                currency={displayCurrency}\n                classNames={classNames}\n              />\n            ))}\n          </div>\n\n          {/* Totals */}\n          {showTotals && (\n            <div\n              data-cimplify-cart-totals\n              className={cn(\n                \"mt-6 pt-4 border-t border-border space-y-2 text-sm\",\n                classNames?.totals,\n              )}\n            >\n              <div\n                data-cimplify-cart-subtotal\n                className={cn(\"flex items-baseline justify-between\", classNames?.subtotal)}\n              >\n                <span className=\"text-muted-foreground\">\n                  Subtotal ({itemCount} {itemCount === 1 ? \"item\" : \"items\"})\n                </span>\n                <Price amount={subtotal} currency={displayCurrency} className=\"tabular-nums\" />\n              </div>\n\n              {totalDiscounts != null && parsePrice(totalDiscounts) > 0 && (\n                <div\n                  data-cimplify-cart-discount\n                  className={cn(\"flex items-baseline justify-between\", classNames?.discount)}\n                >\n                  <span className=\"text-muted-foreground\">Discount</span>\n                  <Price\n                    amount={totalDiscounts}\n                    currency={displayCurrency}\n                    prefix=\"-\"\n                    className=\"tabular-nums text-primary\"\n                  />\n                </div>\n              )}\n\n              {deliveryFee != null && (\n                <div\n                  data-cimplify-cart-delivery-fee\n                  className={cn(\"flex items-baseline justify-between\", classNames?.deliveryFee)}\n                >\n                  <span className=\"text-muted-foreground\">Delivery</span>\n                  {parsePrice(deliveryFee) > 0 ? (\n                    <Price\n                      amount={deliveryFee}\n                      currency={displayCurrency}\n                      className=\"tabular-nums\"\n                    />\n                  ) : (\n                    <span className=\"font-medium text-primary\">Free</span>\n                  )}\n                </div>\n              )}\n\n              {serviceCharge != null && parsePrice(serviceCharge) > 0 && (\n                <div\n                  data-cimplify-cart-service-charge\n                  className={cn(\"flex items-baseline justify-between\", classNames?.serviceCharge)}\n                >\n                  <span className=\"text-muted-foreground\">Service charge</span>\n                  <Price\n                    amount={serviceCharge}\n                    currency={displayCurrency}\n                    className=\"tabular-nums\"\n                  />\n                </div>\n              )}\n\n              <div\n                data-cimplify-cart-tax\n                className={cn(\"flex items-baseline justify-between\", classNames?.tax)}\n              >\n                <span className=\"flex items-baseline gap-1.5 text-muted-foreground\">\n                  {taxInclusive\n                    ? \"Tax, included in prices\"\n                    : taxJurisdictionLabel\n                      ? `Sales tax, ${taxJurisdictionLabel}`\n                      : \"Sales tax\"}\n                  {taxRate != null && taxRate > 0 && (\n                    <span className=\"rounded-full bg-muted px-1.5 py-0.5 text-[11px] font-medium\">\n                      {taxRate}%\n                    </span>\n                  )}\n                </span>\n                <Price amount={tax} currency={displayCurrency} className=\"tabular-nums\" />\n              </div>\n\n              <div\n                data-cimplify-cart-total\n                className={cn(\n                  \"flex items-baseline justify-between pt-3 mt-1 border-t border-border\",\n                  classNames?.total,\n                )}\n              >\n                <span className=\"font-semibold\">Total</span>\n                <Price\n                  amount={total}\n                  currency={displayCurrency}\n                  className=\"text-base font-bold tabular-nums\"\n                />\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"
    }
  ]
}
