@cimplify/sdk 0.20.1 → 0.21.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/react.mjs CHANGED
@@ -1,8 +1,8 @@
1
1
  "use client";
2
- import { getVariantDisplayName, createCimplifyClient } from './chunk-676BJ3EU.mjs';
3
- export { getVariantDisplayName } from './chunk-676BJ3EU.mjs';
4
- import { ELEMENT_TYPES, EVENT_TYPES } from './chunk-7KH2XJIW.mjs';
5
- import { isSupportedCurrency, formatPrice, parsePrice, isOnSale, getDiscountPercentage, getBasePrice } from './chunk-HG7GG3TT.mjs';
2
+ import { getVariantDisplayName, createCimplifyClient } from './chunk-W3DMONZR.mjs';
3
+ export { getVariantDisplayName } from './chunk-W3DMONZR.mjs';
4
+ import { ELEMENT_TYPES, EVENT_TYPES } from './chunk-R4EYMPSN.mjs';
5
+ import { isSupportedCurrency, formatPrice, parsePrice, isOnSale, getDiscountPercentage, getBasePrice } from './chunk-JFT63H34.mjs';
6
6
  import React3, { createContext, useContext, useState, useEffect, useRef, useMemo, useSyncExternalStore, useCallback, useId } from 'react';
7
7
  import { jsx, Fragment, jsxs } from 'react/jsx-runtime';
8
8
  import { NumberField } from '@base-ui/react/number-field';
@@ -374,7 +374,7 @@ function transformToCheckoutCart(cart, fx) {
374
374
  };
375
375
  }
376
376
  var CART_STORAGE_PREFIX = "cimplify:cart:v2";
377
- var DEFAULT_TAX_RATE = 0.08875;
377
+ var DEFAULT_TAX_RATE = 0;
378
378
  var cartStores = /* @__PURE__ */ new Map();
379
379
  function clearAllCartStores() {
380
380
  for (const [, store] of cartStores) {
@@ -887,6 +887,14 @@ function createCartStore(params) {
887
887
  await initializePromise;
888
888
  initializePromise = null;
889
889
  }
890
+ async function reorder(orderId) {
891
+ const result = await client.cart.reorderFromOrder(orderId);
892
+ if (!result.ok) {
893
+ throw result.error;
894
+ }
895
+ await sync();
896
+ return result.value;
897
+ }
890
898
  return {
891
899
  subscribe(listener) {
892
900
  listeners.add(listener);
@@ -902,7 +910,8 @@ function createCartStore(params) {
902
910
  removeItem,
903
911
  updateQuantity,
904
912
  clearCart,
905
- sync
913
+ sync,
914
+ reorder
906
915
  };
907
916
  }
908
917
  function getStoreKey(client, locationId, isDemoMode) {
@@ -983,6 +992,12 @@ function useCart(options = {}) {
983
992
  throw syncError;
984
993
  }
985
994
  }, [store]);
995
+ const reorder = useCallback(
996
+ async (orderId) => {
997
+ return store.reorder(orderId);
998
+ },
999
+ [store]
1000
+ );
986
1001
  const itemCount = useMemo(
987
1002
  () => snapshot.items.reduce((sum, item) => sum + item.quantity, 0),
988
1003
  [snapshot.items]
@@ -1000,7 +1015,8 @@ function useCart(options = {}) {
1000
1015
  removeItem,
1001
1016
  updateQuantity,
1002
1017
  clearCart,
1003
- sync
1018
+ sync,
1019
+ reorder
1004
1020
  };
1005
1021
  }
1006
1022
  var LOCATION_STORAGE_KEY = "cimplify_location_id";
@@ -1691,6 +1707,7 @@ function useProducts(options = {}) {
1691
1707
  const previousLocationIdRef = useRef(locationId);
1692
1708
  const requestIdRef = useRef(0);
1693
1709
  const tagsSignature = options.tags?.join(",") ?? "";
1710
+ const propertiesSignature = options.properties ? JSON.stringify(options.properties) : "";
1694
1711
  const queryOptions = useMemo(
1695
1712
  () => ({
1696
1713
  category: options.category,
@@ -1720,7 +1737,7 @@ function useProducts(options = {}) {
1720
1737
  options.min_price,
1721
1738
  options.max_price,
1722
1739
  options.page,
1723
- options.properties
1740
+ propertiesSignature
1724
1741
  ]
1725
1742
  );
1726
1743
  const cacheKey = useMemo(
@@ -2002,6 +2019,7 @@ function useCategories(options = {}) {
2002
2019
  }, [cacheKey, load]);
2003
2020
  return { categories, isLoading, error, refetch };
2004
2021
  }
2022
+ var TERMINAL_STATUSES = /* @__PURE__ */ new Set(["completed", "cancelled", "refunded"]);
2005
2023
  var orderCache = /* @__PURE__ */ new Map();
2006
2024
  var orderInflight = /* @__PURE__ */ new Map();
2007
2025
  function buildOrderCacheKey(client, orderId) {
@@ -2018,6 +2036,9 @@ function useOrder(orderId, options = {}) {
2018
2036
  const poll = options.poll ?? false;
2019
2037
  const pollInterval = options.pollInterval ?? 5e3;
2020
2038
  const requestIdRef = useRef(0);
2039
+ const previousStatusRef = useRef(null);
2040
+ const onStatusChangeRef = useRef(options.onStatusChange);
2041
+ onStatusChangeRef.current = options.onStatusChange;
2021
2042
  const cacheKey = useMemo(
2022
2043
  () => buildOrderCacheKey(client, normalizedOrderId),
2023
2044
  [client, normalizedOrderId]
@@ -2068,6 +2089,12 @@ function useOrder(orderId, options = {}) {
2068
2089
  if (nextRequestId === requestIdRef.current) {
2069
2090
  setOrder(value);
2070
2091
  setError(null);
2092
+ if (value && previousStatusRef.current !== null && previousStatusRef.current !== value.status) {
2093
+ onStatusChangeRef.current?.(previousStatusRef.current, value.status, value);
2094
+ }
2095
+ if (value) {
2096
+ previousStatusRef.current = value.status;
2097
+ }
2071
2098
  }
2072
2099
  } catch (loadError) {
2073
2100
  if (nextRequestId === requestIdRef.current) {
@@ -2084,8 +2111,9 @@ function useOrder(orderId, options = {}) {
2084
2111
  useEffect(() => {
2085
2112
  void load(false);
2086
2113
  }, [load]);
2114
+ const isTerminal = order ? TERMINAL_STATUSES.has(order.status) : false;
2087
2115
  useEffect(() => {
2088
- if (!poll || !enabled || normalizedOrderId.length === 0) {
2116
+ if (!poll || !enabled || normalizedOrderId.length === 0 || isTerminal) {
2089
2117
  return;
2090
2118
  }
2091
2119
  const timer = window.setInterval(() => {
@@ -2094,12 +2122,12 @@ function useOrder(orderId, options = {}) {
2094
2122
  return () => {
2095
2123
  window.clearInterval(timer);
2096
2124
  };
2097
- }, [enabled, load, normalizedOrderId, poll, pollInterval]);
2125
+ }, [enabled, isTerminal, load, normalizedOrderId, poll, pollInterval]);
2098
2126
  const refetch = useCallback(async () => {
2099
2127
  orderCache.delete(cacheKey);
2100
2128
  await load(true);
2101
2129
  }, [cacheKey, load]);
2102
- return { order, isLoading, error, refetch };
2130
+ return { order, isLoading, error, refetch, isTerminal };
2103
2131
  }
2104
2132
  var LOCATION_STORAGE_KEY2 = "cimplify_location_id";
2105
2133
  function readStoredLocationId() {
@@ -7517,12 +7545,15 @@ function OrderSummary({
7517
7545
  orderId,
7518
7546
  poll = false,
7519
7547
  renderLineItem,
7548
+ onReorder,
7549
+ onStatusChange,
7520
7550
  className,
7521
7551
  classNames
7522
7552
  }) {
7523
7553
  const { order: fetched, isLoading } = useOrder(orderProp ? null : orderId, {
7524
7554
  enabled: !orderProp && !!orderId,
7525
- poll
7555
+ poll,
7556
+ onStatusChange
7526
7557
  });
7527
7558
  const order = orderProp ?? fetched;
7528
7559
  if (isLoading && !order) {
@@ -7688,6 +7719,16 @@ function OrderSummary({
7688
7719
  className: classNames?.trackingLink,
7689
7720
  children: "Track your order"
7690
7721
  }
7722
+ ),
7723
+ onReorder && /* @__PURE__ */ jsx(
7724
+ "button",
7725
+ {
7726
+ type: "button",
7727
+ onClick: () => onReorder(order),
7728
+ "data-cimplify-order-reorder": true,
7729
+ className: classNames?.reorderButton,
7730
+ children: "Reorder"
7731
+ }
7691
7732
  )
7692
7733
  ] });
7693
7734
  }
@@ -7711,6 +7752,7 @@ function OrderHistory({
7711
7752
  limit,
7712
7753
  onOrderClick,
7713
7754
  renderOrder,
7755
+ onReorder,
7714
7756
  emptyMessage = "No orders yet",
7715
7757
  className,
7716
7758
  classNames
@@ -7782,7 +7824,20 @@ function OrderHistory({
7782
7824
  order.total_quantity === 1 ? "item" : "items"
7783
7825
  ] }),
7784
7826
  /* @__PURE__ */ jsx("span", { "data-cimplify-order-history-total": true, className: classNames?.total, children: /* @__PURE__ */ jsx(Price, { amount: order.total_price }) })
7785
- ] })
7827
+ ] }),
7828
+ onReorder && /* @__PURE__ */ jsx(
7829
+ "button",
7830
+ {
7831
+ type: "button",
7832
+ onClick: (e) => {
7833
+ e.stopPropagation();
7834
+ onReorder(order);
7835
+ },
7836
+ "data-cimplify-order-reorder": true,
7837
+ className: classNames?.reorderButton,
7838
+ children: "Reorder"
7839
+ }
7840
+ )
7786
7841
  ] })
7787
7842
  },
7788
7843
  order.id
@@ -8974,6 +9029,8 @@ function OrderDetailPage({
8974
9029
  poll = true,
8975
9030
  onBack,
8976
9031
  renderLineItem,
9032
+ onReorder,
9033
+ onStatusChange,
8977
9034
  backLabel = "Back to orders",
8978
9035
  className,
8979
9036
  classNames
@@ -8998,7 +9055,9 @@ function OrderDetailPage({
8998
9055
  order,
8999
9056
  orderId: order ? void 0 : orderId,
9000
9057
  poll,
9001
- renderLineItem
9058
+ renderLineItem,
9059
+ onReorder,
9060
+ onStatusChange
9002
9061
  }
9003
9062
  ) })
9004
9063
  ] });
@@ -9014,6 +9073,7 @@ function OrderHistoryPage({
9014
9073
  orders: ordersProp,
9015
9074
  limit = 20,
9016
9075
  onOrderNavigate,
9076
+ onReorder,
9017
9077
  showFilters = true,
9018
9078
  renderOrder,
9019
9079
  className,
@@ -9051,7 +9111,7 @@ function OrderHistoryPage({
9051
9111
  children: "Back to orders"
9052
9112
  }
9053
9113
  ),
9054
- /* @__PURE__ */ jsx(OrderSummary, { order: selectedOrder })
9114
+ /* @__PURE__ */ jsx(OrderSummary, { order: selectedOrder, onReorder })
9055
9115
  ] }) });
9056
9116
  }
9057
9117
  return /* @__PURE__ */ jsxs("div", { "data-cimplify-order-history-page": true, className: cn(className, classNames?.root), children: [
@@ -9074,7 +9134,8 @@ function OrderHistoryPage({
9074
9134
  status: statusFilter,
9075
9135
  limit,
9076
9136
  onOrderClick: handleOrderClick,
9077
- renderOrder
9137
+ renderOrder,
9138
+ onReorder
9078
9139
  }
9079
9140
  ) })
9080
9141
  ] });
package/dist/utils.js CHANGED
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  var chunkS6TU55KP_js = require('./chunk-S6TU55KP.js');
4
- var chunk2BSYTQAY_js = require('./chunk-2BSYTQAY.js');
4
+ var chunk64OCG2TJ_js = require('./chunk-64OCG2TJ.js');
5
5
 
6
6
 
7
7
 
@@ -19,109 +19,109 @@ Object.defineProperty(exports, "resolvePhoneCountryCode", {
19
19
  });
20
20
  Object.defineProperty(exports, "CURRENCY_SYMBOLS", {
21
21
  enumerable: true,
22
- get: function () { return chunk2BSYTQAY_js.CURRENCY_SYMBOLS; }
22
+ get: function () { return chunk64OCG2TJ_js.CURRENCY_SYMBOLS; }
23
23
  });
24
24
  Object.defineProperty(exports, "MOBILE_MONEY_PROVIDERS", {
25
25
  enumerable: true,
26
- get: function () { return chunk2BSYTQAY_js.MOBILE_MONEY_PROVIDERS; }
26
+ get: function () { return chunk64OCG2TJ_js.MOBILE_MONEY_PROVIDERS; }
27
27
  });
28
28
  Object.defineProperty(exports, "categorizePaymentError", {
29
29
  enumerable: true,
30
- get: function () { return chunk2BSYTQAY_js.categorizePaymentError; }
30
+ get: function () { return chunk64OCG2TJ_js.categorizePaymentError; }
31
31
  });
32
32
  Object.defineProperty(exports, "detectMobileMoneyProvider", {
33
33
  enumerable: true,
34
- get: function () { return chunk2BSYTQAY_js.detectMobileMoneyProvider; }
34
+ get: function () { return chunk64OCG2TJ_js.detectMobileMoneyProvider; }
35
35
  });
36
36
  Object.defineProperty(exports, "formatMoney", {
37
37
  enumerable: true,
38
- get: function () { return chunk2BSYTQAY_js.formatMoney; }
38
+ get: function () { return chunk64OCG2TJ_js.formatMoney; }
39
39
  });
40
40
  Object.defineProperty(exports, "formatNumberCompact", {
41
41
  enumerable: true,
42
- get: function () { return chunk2BSYTQAY_js.formatNumberCompact; }
42
+ get: function () { return chunk64OCG2TJ_js.formatNumberCompact; }
43
43
  });
44
44
  Object.defineProperty(exports, "formatPrice", {
45
45
  enumerable: true,
46
- get: function () { return chunk2BSYTQAY_js.formatPrice; }
46
+ get: function () { return chunk64OCG2TJ_js.formatPrice; }
47
47
  });
48
48
  Object.defineProperty(exports, "formatPriceAdjustment", {
49
49
  enumerable: true,
50
- get: function () { return chunk2BSYTQAY_js.formatPriceAdjustment; }
50
+ get: function () { return chunk64OCG2TJ_js.formatPriceAdjustment; }
51
51
  });
52
52
  Object.defineProperty(exports, "formatPriceCompact", {
53
53
  enumerable: true,
54
- get: function () { return chunk2BSYTQAY_js.formatPriceCompact; }
54
+ get: function () { return chunk64OCG2TJ_js.formatPriceCompact; }
55
55
  });
56
56
  Object.defineProperty(exports, "formatPriceWithTax", {
57
57
  enumerable: true,
58
- get: function () { return chunk2BSYTQAY_js.formatPriceWithTax; }
58
+ get: function () { return chunk64OCG2TJ_js.formatPriceWithTax; }
59
59
  });
60
60
  Object.defineProperty(exports, "formatProductPrice", {
61
61
  enumerable: true,
62
- get: function () { return chunk2BSYTQAY_js.formatProductPrice; }
62
+ get: function () { return chunk64OCG2TJ_js.formatProductPrice; }
63
63
  });
64
64
  Object.defineProperty(exports, "getBasePrice", {
65
65
  enumerable: true,
66
- get: function () { return chunk2BSYTQAY_js.getBasePrice; }
66
+ get: function () { return chunk64OCG2TJ_js.getBasePrice; }
67
67
  });
68
68
  Object.defineProperty(exports, "getCurrencySymbol", {
69
69
  enumerable: true,
70
- get: function () { return chunk2BSYTQAY_js.getCurrencySymbol; }
70
+ get: function () { return chunk64OCG2TJ_js.getCurrencySymbol; }
71
71
  });
72
72
  Object.defineProperty(exports, "getDiscountPercentage", {
73
73
  enumerable: true,
74
- get: function () { return chunk2BSYTQAY_js.getDiscountPercentage; }
74
+ get: function () { return chunk64OCG2TJ_js.getDiscountPercentage; }
75
75
  });
76
76
  Object.defineProperty(exports, "getDisplayPrice", {
77
77
  enumerable: true,
78
- get: function () { return chunk2BSYTQAY_js.getDisplayPrice; }
78
+ get: function () { return chunk64OCG2TJ_js.getDisplayPrice; }
79
79
  });
80
80
  Object.defineProperty(exports, "getMarkupPercentage", {
81
81
  enumerable: true,
82
- get: function () { return chunk2BSYTQAY_js.getMarkupPercentage; }
82
+ get: function () { return chunk64OCG2TJ_js.getMarkupPercentage; }
83
83
  });
84
84
  Object.defineProperty(exports, "getProductCurrency", {
85
85
  enumerable: true,
86
- get: function () { return chunk2BSYTQAY_js.getProductCurrency; }
86
+ get: function () { return chunk64OCG2TJ_js.getProductCurrency; }
87
87
  });
88
88
  Object.defineProperty(exports, "getTaxAmount", {
89
89
  enumerable: true,
90
- get: function () { return chunk2BSYTQAY_js.getTaxAmount; }
90
+ get: function () { return chunk64OCG2TJ_js.getTaxAmount; }
91
91
  });
92
92
  Object.defineProperty(exports, "hasTaxInfo", {
93
93
  enumerable: true,
94
- get: function () { return chunk2BSYTQAY_js.hasTaxInfo; }
94
+ get: function () { return chunk64OCG2TJ_js.hasTaxInfo; }
95
95
  });
96
96
  Object.defineProperty(exports, "isOnSale", {
97
97
  enumerable: true,
98
- get: function () { return chunk2BSYTQAY_js.isOnSale; }
98
+ get: function () { return chunk64OCG2TJ_js.isOnSale; }
99
99
  });
100
100
  Object.defineProperty(exports, "isPaymentStatusFailure", {
101
101
  enumerable: true,
102
- get: function () { return chunk2BSYTQAY_js.isPaymentStatusFailure; }
102
+ get: function () { return chunk64OCG2TJ_js.isPaymentStatusFailure; }
103
103
  });
104
104
  Object.defineProperty(exports, "isPaymentStatusRequiresAction", {
105
105
  enumerable: true,
106
- get: function () { return chunk2BSYTQAY_js.isPaymentStatusRequiresAction; }
106
+ get: function () { return chunk64OCG2TJ_js.isPaymentStatusRequiresAction; }
107
107
  });
108
108
  Object.defineProperty(exports, "isPaymentStatusSuccess", {
109
109
  enumerable: true,
110
- get: function () { return chunk2BSYTQAY_js.isPaymentStatusSuccess; }
110
+ get: function () { return chunk64OCG2TJ_js.isPaymentStatusSuccess; }
111
111
  });
112
112
  Object.defineProperty(exports, "isTaxInclusive", {
113
113
  enumerable: true,
114
- get: function () { return chunk2BSYTQAY_js.isTaxInclusive; }
114
+ get: function () { return chunk64OCG2TJ_js.isTaxInclusive; }
115
115
  });
116
116
  Object.defineProperty(exports, "normalizePaymentResponse", {
117
117
  enumerable: true,
118
- get: function () { return chunk2BSYTQAY_js.normalizePaymentResponse; }
118
+ get: function () { return chunk64OCG2TJ_js.normalizePaymentResponse; }
119
119
  });
120
120
  Object.defineProperty(exports, "normalizeStatusResponse", {
121
121
  enumerable: true,
122
- get: function () { return chunk2BSYTQAY_js.normalizeStatusResponse; }
122
+ get: function () { return chunk64OCG2TJ_js.normalizeStatusResponse; }
123
123
  });
124
124
  Object.defineProperty(exports, "parsePrice", {
125
125
  enumerable: true,
126
- get: function () { return chunk2BSYTQAY_js.parsePrice; }
126
+ get: function () { return chunk64OCG2TJ_js.parsePrice; }
127
127
  });
package/dist/utils.mjs CHANGED
@@ -1,2 +1,2 @@
1
1
  export { isValidPhoneForContext, normalizePhoneToE164, resolvePhoneCountryCode } from './chunk-WDIMISRC.mjs';
2
- export { CURRENCY_SYMBOLS, MOBILE_MONEY_PROVIDERS, categorizePaymentError, detectMobileMoneyProvider, formatMoney, formatNumberCompact, formatPrice, formatPriceAdjustment, formatPriceCompact, formatPriceWithTax, formatProductPrice, getBasePrice, getCurrencySymbol, getDiscountPercentage, getDisplayPrice, getMarkupPercentage, getProductCurrency, getTaxAmount, hasTaxInfo, isOnSale, isPaymentStatusFailure, isPaymentStatusRequiresAction, isPaymentStatusSuccess, isTaxInclusive, normalizePaymentResponse, normalizeStatusResponse, parsePrice } from './chunk-HG7GG3TT.mjs';
2
+ export { CURRENCY_SYMBOLS, MOBILE_MONEY_PROVIDERS, categorizePaymentError, detectMobileMoneyProvider, formatMoney, formatNumberCompact, formatPrice, formatPriceAdjustment, formatPriceCompact, formatPriceWithTax, formatProductPrice, getBasePrice, getCurrencySymbol, getDiscountPercentage, getDisplayPrice, getMarkupPercentage, getProductCurrency, getTaxAmount, hasTaxInfo, isOnSale, isPaymentStatusFailure, isPaymentStatusRequiresAction, isPaymentStatusSuccess, isTaxInclusive, normalizePaymentResponse, normalizeStatusResponse, parsePrice } from './chunk-JFT63H34.mjs';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cimplify/sdk",
3
- "version": "0.20.1",
3
+ "version": "0.21.0",
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": "order-detail-page.tsx",
13
- "content": "\"use client\";\n\nimport React from \"react\";\nimport type { Order, LineItem } from \"@cimplify/sdk\";\nimport { OrderSummary } from \"@cimplify/sdk/react\";\nimport { cn } from \"@cimplify/sdk/react\";\n\nexport interface OrderDetailPageClassNames {\n root?: string;\n header?: string;\n title?: string;\n backButton?: string;\n summary?: string;\n}\n\nexport interface OrderDetailPageProps {\n /** Order ID to display. */\n orderId: string;\n /** Pre-fetched order for SSR. */\n order?: Order;\n /** Poll for status updates. Default: true. */\n poll?: boolean;\n /** Called when back button is clicked. */\n onBack?: () => void;\n /** Custom line item renderer. */\n renderLineItem?: (item: LineItem) => React.ReactNode;\n /** Back button label. */\n backLabel?: string;\n className?: string;\n classNames?: OrderDetailPageClassNames;\n}\n\n/**\n * OrderDetailPage — single order detail view with live status polling.\n *\n * SSR-friendly: pass `order` prop for server rendering.\n */\nexport function OrderDetailPage({\n orderId,\n order,\n poll = true,\n onBack,\n renderLineItem,\n backLabel = \"Back to orders\",\n className,\n classNames,\n}: OrderDetailPageProps): React.ReactElement {\n return (\n <div data-cimplify-order-detail-page className={cn(className, classNames?.root)}>\n {/* Header */}\n <div data-cimplify-order-detail-header className={classNames?.header}>\n {onBack && (\n <button\n type=\"button\"\n onClick={onBack}\n data-cimplify-order-detail-back\n className={classNames?.backButton}\n >\n {backLabel}\n </button>\n )}\n <h1 data-cimplify-order-detail-title className={classNames?.title}>\n Order Details\n </h1>\n </div>\n\n {/* Order summary */}\n <div data-cimplify-order-detail-content className={classNames?.summary}>\n <OrderSummary\n order={order}\n orderId={order ? undefined : orderId}\n poll={poll}\n renderLineItem={renderLineItem}\n />\n </div>\n </div>\n );\n}\n"
13
+ "content": "\"use client\";\n\nimport React from \"react\";\nimport type { Order, LineItem, OrderStatus } from \"@cimplify/sdk\";\nimport { OrderSummary } from \"@cimplify/sdk/react\";\nimport { cn } from \"@cimplify/sdk/react\";\n\nexport interface OrderDetailPageClassNames {\n root?: string;\n header?: string;\n title?: string;\n backButton?: string;\n summary?: string;\n}\n\nexport interface OrderDetailPageProps {\n /** Order ID to display. */\n orderId: string;\n /** Pre-fetched order for SSR. */\n order?: Order;\n /** Poll for status updates. Default: true. */\n poll?: boolean;\n /** Called when back button is clicked. */\n onBack?: () => void;\n /** Custom line item renderer. */\n renderLineItem?: (item: LineItem) => React.ReactNode;\n /** Called when the reorder button is clicked. */\n onReorder?: (order: Order) => void;\n /** Called when the order status changes during polling. */\n onStatusChange?: (previousStatus: OrderStatus, newStatus: OrderStatus, order: Order) => void;\n /** Back button label. */\n backLabel?: string;\n className?: string;\n classNames?: OrderDetailPageClassNames;\n}\n\n/**\n * OrderDetailPage — single order detail view with live status polling.\n *\n * SSR-friendly: pass `order` prop for server rendering.\n */\nexport function OrderDetailPage({\n orderId,\n order,\n poll = true,\n onBack,\n renderLineItem,\n onReorder,\n onStatusChange,\n backLabel = \"Back to orders\",\n className,\n classNames,\n}: OrderDetailPageProps): React.ReactElement {\n return (\n <div data-cimplify-order-detail-page className={cn(className, classNames?.root)}>\n {/* Header */}\n <div data-cimplify-order-detail-header className={classNames?.header}>\n {onBack && (\n <button\n type=\"button\"\n onClick={onBack}\n data-cimplify-order-detail-back\n className={classNames?.backButton}\n >\n {backLabel}\n </button>\n )}\n <h1 data-cimplify-order-detail-title className={classNames?.title}>\n Order Details\n </h1>\n </div>\n\n {/* Order summary */}\n <div data-cimplify-order-detail-content className={classNames?.summary}>\n <OrderSummary\n order={order}\n orderId={order ? undefined : orderId}\n poll={poll}\n renderLineItem={renderLineItem}\n onReorder={onReorder}\n onStatusChange={onStatusChange}\n />\n </div>\n </div>\n );\n}\n"
14
14
  }
15
15
  ]
16
16
  }
@@ -11,7 +11,7 @@
11
11
  "files": [
12
12
  {
13
13
  "path": "order-history-page.tsx",
14
- "content": "\"use client\";\n\nimport React, { useState, useCallback } from \"react\";\nimport { Tabs } from \"@base-ui/react/tabs\";\nimport type { Order, OrderStatus } from \"@cimplify/sdk\";\nimport { OrderHistory } from \"@cimplify/sdk/react\";\nimport { OrderSummary } from \"@cimplify/sdk/react\";\nimport { cn } from \"@cimplify/sdk/react\";\n\nexport interface OrderHistoryPageClassNames {\n root?: string;\n header?: string;\n title?: string;\n filters?: string;\n filterButton?: string;\n list?: string;\n detail?: string;\n backButton?: string;\n}\n\nexport interface OrderHistoryPageProps {\n /** Page title. */\n title?: string;\n /** Pre-fetched orders for SSR. */\n orders?: Order[];\n /** Max orders to show per page. */\n limit?: number;\n /** Called when navigating to an order detail (e.g. for routing). */\n onOrderNavigate?: (order: Order) => void;\n /** Show status filter tabs. Default: true. */\n showFilters?: boolean;\n /** Custom order row renderer. */\n renderOrder?: (order: Order) => React.ReactNode;\n className?: string;\n classNames?: OrderHistoryPageClassNames;\n}\n\nconst STATUS_FILTERS: { label: string; value: OrderStatus | undefined; tabValue: string }[] = [\n { label: \"All\", value: undefined, tabValue: \"all\" },\n { label: \"Active\", value: \"confirmed\", tabValue: \"confirmed\" },\n { label: \"Completed\", value: \"completed\", tabValue: \"completed\" },\n { label: \"Cancelled\", value: \"cancelled\", tabValue: \"cancelled\" },\n];\n\n/**\n * OrderHistoryPage — order list with inline detail view and status filtering.\n *\n * SSR-friendly: pass `orders` prop for server rendering.\n * Supports both inline detail view and external navigation via `onOrderNavigate`.\n */\nexport function OrderHistoryPage({\n title = \"Order History\",\n orders: ordersProp,\n limit = 20,\n onOrderNavigate,\n showFilters = true,\n renderOrder,\n className,\n classNames,\n}: OrderHistoryPageProps): React.ReactElement {\n const [statusFilter, setStatusFilter] = useState<OrderStatus | undefined>(undefined);\n const [selectedOrder, setSelectedOrder] = useState<Order | null>(null);\n\n const handleTabChange = useCallback((value: string | number | null) => {\n const filter = STATUS_FILTERS.find((f) => f.tabValue === value);\n setStatusFilter(filter?.value);\n }, []);\n\n const handleOrderClick = useCallback(\n (order: Order) => {\n if (onOrderNavigate) {\n onOrderNavigate(order);\n } else {\n setSelectedOrder(order);\n }\n },\n [onOrderNavigate],\n );\n\n const handleBack = useCallback(() => {\n setSelectedOrder(null);\n }, []);\n\n const activeTabValue = STATUS_FILTERS.find((f) => f.value === statusFilter)?.tabValue ?? \"all\";\n\n // Inline detail view\n if (selectedOrder && !onOrderNavigate) {\n return (\n <div data-cimplify-order-history-page className={cn(className, classNames?.root)}>\n <div data-cimplify-order-history-detail className={classNames?.detail}>\n <button\n type=\"button\"\n onClick={handleBack}\n data-cimplify-order-history-back\n className={classNames?.backButton}\n >\n Back to orders\n </button>\n <OrderSummary order={selectedOrder} />\n </div>\n </div>\n );\n }\n\n return (\n <div data-cimplify-order-history-page className={cn(className, classNames?.root)}>\n {/* Header */}\n <div data-cimplify-order-history-header className={classNames?.header}>\n <h1 data-cimplify-order-history-title className={classNames?.title}>\n {title}\n </h1>\n </div>\n\n {/* Status filter */}\n {showFilters && (\n <Tabs.Root value={activeTabValue} onValueChange={handleTabChange}>\n <Tabs.List data-cimplify-order-history-filters className={classNames?.filters}>\n {STATUS_FILTERS.map((filter) => (\n <Tabs.Tab\n key={filter.tabValue}\n value={filter.tabValue}\n data-cimplify-order-filter\n data-selected={statusFilter === filter.value || undefined}\n className={classNames?.filterButton}\n >\n {filter.label}\n </Tabs.Tab>\n ))}\n </Tabs.List>\n </Tabs.Root>\n )}\n\n {/* Order list */}\n <div data-cimplify-order-history-list className={classNames?.list}>\n <OrderHistory\n orders={ordersProp}\n status={statusFilter}\n limit={limit}\n onOrderClick={handleOrderClick}\n renderOrder={renderOrder}\n />\n </div>\n </div>\n );\n}\n"
14
+ "content": "\"use client\";\n\nimport React, { useState, useCallback } from \"react\";\nimport { Tabs } from \"@base-ui/react/tabs\";\nimport type { Order, OrderStatus } from \"@cimplify/sdk\";\nimport { OrderHistory } from \"@cimplify/sdk/react\";\nimport { OrderSummary } from \"@cimplify/sdk/react\";\nimport { cn } from \"@cimplify/sdk/react\";\n\nexport interface OrderHistoryPageClassNames {\n root?: string;\n header?: string;\n title?: string;\n filters?: string;\n filterButton?: string;\n list?: string;\n detail?: string;\n backButton?: string;\n}\n\nexport interface OrderHistoryPageProps {\n /** Page title. */\n title?: string;\n /** Pre-fetched orders for SSR. */\n orders?: Order[];\n /** Max orders to show per page. */\n limit?: number;\n /** Called when navigating to an order detail (e.g. for routing). */\n onOrderNavigate?: (order: Order) => void;\n /** Called when the reorder button is clicked. */\n onReorder?: (order: Order) => void;\n /** Show status filter tabs. Default: true. */\n showFilters?: boolean;\n /** Custom order row renderer. */\n renderOrder?: (order: Order) => React.ReactNode;\n className?: string;\n classNames?: OrderHistoryPageClassNames;\n}\n\nconst STATUS_FILTERS: { label: string; value: OrderStatus | undefined; tabValue: string }[] = [\n { label: \"All\", value: undefined, tabValue: \"all\" },\n { label: \"Active\", value: \"confirmed\", tabValue: \"confirmed\" },\n { label: \"Completed\", value: \"completed\", tabValue: \"completed\" },\n { label: \"Cancelled\", value: \"cancelled\", tabValue: \"cancelled\" },\n];\n\n/**\n * OrderHistoryPage — order list with inline detail view and status filtering.\n *\n * SSR-friendly: pass `orders` prop for server rendering.\n * Supports both inline detail view and external navigation via `onOrderNavigate`.\n */\nexport function OrderHistoryPage({\n title = \"Order History\",\n orders: ordersProp,\n limit = 20,\n onOrderNavigate,\n onReorder,\n showFilters = true,\n renderOrder,\n className,\n classNames,\n}: OrderHistoryPageProps): React.ReactElement {\n const [statusFilter, setStatusFilter] = useState<OrderStatus | undefined>(undefined);\n const [selectedOrder, setSelectedOrder] = useState<Order | null>(null);\n\n const handleTabChange = useCallback((value: string | number | null) => {\n const filter = STATUS_FILTERS.find((f) => f.tabValue === value);\n setStatusFilter(filter?.value);\n }, []);\n\n const handleOrderClick = useCallback(\n (order: Order) => {\n if (onOrderNavigate) {\n onOrderNavigate(order);\n } else {\n setSelectedOrder(order);\n }\n },\n [onOrderNavigate],\n );\n\n const handleBack = useCallback(() => {\n setSelectedOrder(null);\n }, []);\n\n const activeTabValue = STATUS_FILTERS.find((f) => f.value === statusFilter)?.tabValue ?? \"all\";\n\n // Inline detail view\n if (selectedOrder && !onOrderNavigate) {\n return (\n <div data-cimplify-order-history-page className={cn(className, classNames?.root)}>\n <div data-cimplify-order-history-detail className={classNames?.detail}>\n <button\n type=\"button\"\n onClick={handleBack}\n data-cimplify-order-history-back\n className={classNames?.backButton}\n >\n Back to orders\n </button>\n <OrderSummary order={selectedOrder} onReorder={onReorder} />\n </div>\n </div>\n );\n }\n\n return (\n <div data-cimplify-order-history-page className={cn(className, classNames?.root)}>\n {/* Header */}\n <div data-cimplify-order-history-header className={classNames?.header}>\n <h1 data-cimplify-order-history-title className={classNames?.title}>\n {title}\n </h1>\n </div>\n\n {/* Status filter */}\n {showFilters && (\n <Tabs.Root value={activeTabValue} onValueChange={handleTabChange}>\n <Tabs.List data-cimplify-order-history-filters className={classNames?.filters}>\n {STATUS_FILTERS.map((filter) => (\n <Tabs.Tab\n key={filter.tabValue}\n value={filter.tabValue}\n data-cimplify-order-filter\n data-selected={statusFilter === filter.value || undefined}\n className={classNames?.filterButton}\n >\n {filter.label}\n </Tabs.Tab>\n ))}\n </Tabs.List>\n </Tabs.Root>\n )}\n\n {/* Order list */}\n <div data-cimplify-order-history-list className={classNames?.list}>\n <OrderHistory\n orders={ordersProp}\n status={statusFilter}\n limit={limit}\n onOrderClick={handleOrderClick}\n renderOrder={renderOrder}\n onReorder={onReorder}\n />\n </div>\n </div>\n );\n}\n"
15
15
  }
16
16
  ]
17
17
  }
@@ -10,7 +10,7 @@
10
10
  "files": [
11
11
  {
12
12
  "path": "order-history.tsx",
13
- "content": "\"use client\";\n\nimport React from \"react\";\nimport type { Order, OrderStatus } from \"@cimplify/sdk\";\nimport { useOrders } from \"@cimplify/sdk/react\";\nimport { Price } from \"@cimplify/sdk/react\";\nimport { cn } from \"@cimplify/sdk/react\";\n\nexport interface OrderHistoryClassNames {\n root?: string;\n item?: string;\n orderId?: string;\n status?: string;\n date?: string;\n total?: string;\n empty?: string;\n loading?: string;\n}\n\nexport interface OrderHistoryProps {\n /** Override orders (skips fetch). For SSR, pass pre-fetched orders. */\n orders?: Order[];\n /** Filter by status. */\n status?: OrderStatus;\n /** Max orders to display. */\n limit?: number;\n /** Called when an order row is clicked. */\n onOrderClick?: (order: Order) => void;\n /** Custom order row renderer. */\n renderOrder?: (order: Order) => React.ReactNode;\n /** Text shown when empty. */\n emptyMessage?: string;\n className?: string;\n classNames?: OrderHistoryClassNames;\n}\n\nconst STATUS_LABELS: Record<OrderStatus, string> = {\n pending: \"Pending\",\n created: \"Created\",\n confirmed: \"Confirmed\",\n in_preparation: \"In Preparation\",\n ready_to_serve: \"Ready\",\n partially_served: \"Partially Served\",\n served: \"Served\",\n delivered: \"Delivered\",\n picked_up: \"Picked Up\",\n completed: \"Completed\",\n cancelled: \"Cancelled\",\n refunded: \"Refunded\",\n};\n\n/**\n * OrderHistory — list of past orders with status and totals.\n *\n * Fetches via `useOrders` unless pre-loaded orders are passed.\n */\nexport function OrderHistory({\n orders: ordersProp,\n status,\n limit,\n onOrderClick,\n renderOrder,\n emptyMessage = \"No orders yet\",\n className,\n classNames,\n}: OrderHistoryProps): React.ReactElement {\n const { orders: fetched, isLoading } = useOrders({\n status,\n limit,\n enabled: ordersProp === undefined,\n });\n\n const orders = ordersProp ?? fetched;\n\n if (isLoading && orders.length === 0) {\n return (\n <div\n data-cimplify-order-history\n aria-busy=\"true\"\n className={cn(className, classNames?.root, classNames?.loading)}\n />\n );\n }\n\n if (orders.length === 0) {\n return (\n <div\n data-cimplify-order-history\n data-empty\n className={cn(className, classNames?.root, classNames?.empty)}\n >\n <p>{emptyMessage}</p>\n </div>\n );\n }\n\n return (\n <div data-cimplify-order-history className={cn(className, classNames?.root)}>\n {orders.map((order) => (\n <button\n key={order.id}\n type=\"button\"\n onClick={() => onOrderClick?.(order)}\n data-cimplify-order-history-item\n data-status={order.status}\n className={classNames?.item}\n >\n {renderOrder ? (\n renderOrder(order)\n ) : (\n <>\n <div data-cimplify-order-history-main>\n <span data-cimplify-order-history-id className={classNames?.orderId}>\n #{order.user_friendly_id}\n </span>\n <span\n data-cimplify-order-history-status\n data-status={order.status}\n className={classNames?.status}\n >\n {STATUS_LABELS[order.status] ?? order.status}\n </span>\n </div>\n <div data-cimplify-order-history-details>\n <time\n data-cimplify-order-history-date\n dateTime={order.created_at}\n className={classNames?.date}\n >\n {new Date(order.created_at).toLocaleDateString()}\n </time>\n <span data-cimplify-order-history-items>\n {order.total_quantity} {order.total_quantity === 1 ? \"item\" : \"items\"}\n </span>\n <span data-cimplify-order-history-total className={classNames?.total}>\n <Price amount={order.total_price} />\n </span>\n </div>\n </>\n )}\n </button>\n ))}\n </div>\n );\n}\n"
13
+ "content": "\"use client\";\n\nimport React from \"react\";\nimport type { Order, OrderStatus } from \"@cimplify/sdk\";\nimport { useOrders } from \"@cimplify/sdk/react\";\nimport { Price } from \"@cimplify/sdk/react\";\nimport { cn } from \"@cimplify/sdk/react\";\n\nexport interface OrderHistoryClassNames {\n root?: string;\n item?: string;\n orderId?: string;\n status?: string;\n date?: string;\n total?: string;\n empty?: string;\n loading?: string;\n reorderButton?: string;\n}\n\nexport interface OrderHistoryProps {\n /** Override orders (skips fetch). For SSR, pass pre-fetched orders. */\n orders?: Order[];\n /** Filter by status. */\n status?: OrderStatus;\n /** Max orders to display. */\n limit?: number;\n /** Called when an order row is clicked. */\n onOrderClick?: (order: Order) => void;\n /** Custom order row renderer. */\n renderOrder?: (order: Order) => React.ReactNode;\n /** Called when the reorder button is clicked. If provided, a reorder button is rendered. */\n onReorder?: (order: Order) => void;\n /** Text shown when empty. */\n emptyMessage?: string;\n className?: string;\n classNames?: OrderHistoryClassNames;\n}\n\nconst STATUS_LABELS: Record<OrderStatus, string> = {\n pending: \"Pending\",\n created: \"Created\",\n confirmed: \"Confirmed\",\n in_preparation: \"In Preparation\",\n ready_to_serve: \"Ready\",\n partially_served: \"Partially Served\",\n served: \"Served\",\n delivered: \"Delivered\",\n picked_up: \"Picked Up\",\n completed: \"Completed\",\n cancelled: \"Cancelled\",\n refunded: \"Refunded\",\n};\n\n/**\n * OrderHistory — list of past orders with status and totals.\n *\n * Fetches via `useOrders` unless pre-loaded orders are passed.\n */\nexport function OrderHistory({\n orders: ordersProp,\n status,\n limit,\n onOrderClick,\n renderOrder,\n onReorder,\n emptyMessage = \"No orders yet\",\n className,\n classNames,\n}: OrderHistoryProps): React.ReactElement {\n const { orders: fetched, isLoading } = useOrders({\n status,\n limit,\n enabled: ordersProp === undefined,\n });\n\n const orders = ordersProp ?? fetched;\n\n if (isLoading && orders.length === 0) {\n return (\n <div\n data-cimplify-order-history\n aria-busy=\"true\"\n className={cn(className, classNames?.root, classNames?.loading)}\n />\n );\n }\n\n if (orders.length === 0) {\n return (\n <div\n data-cimplify-order-history\n data-empty\n className={cn(className, classNames?.root, classNames?.empty)}\n >\n <p>{emptyMessage}</p>\n </div>\n );\n }\n\n return (\n <div data-cimplify-order-history className={cn(className, classNames?.root)}>\n {orders.map((order) => (\n <button\n key={order.id}\n type=\"button\"\n onClick={() => onOrderClick?.(order)}\n data-cimplify-order-history-item\n data-status={order.status}\n className={classNames?.item}\n >\n {renderOrder ? (\n renderOrder(order)\n ) : (\n <>\n <div data-cimplify-order-history-main>\n <span data-cimplify-order-history-id className={classNames?.orderId}>\n #{order.user_friendly_id}\n </span>\n <span\n data-cimplify-order-history-status\n data-status={order.status}\n className={classNames?.status}\n >\n {STATUS_LABELS[order.status] ?? order.status}\n </span>\n </div>\n <div data-cimplify-order-history-details>\n <time\n data-cimplify-order-history-date\n dateTime={order.created_at}\n className={classNames?.date}\n >\n {new Date(order.created_at).toLocaleDateString()}\n </time>\n <span data-cimplify-order-history-items>\n {order.total_quantity} {order.total_quantity === 1 ? \"item\" : \"items\"}\n </span>\n <span data-cimplify-order-history-total className={classNames?.total}>\n <Price amount={order.total_price} />\n </span>\n </div>\n {onReorder && (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n onReorder(order);\n }}\n data-cimplify-order-reorder\n className={classNames?.reorderButton}\n >\n Reorder\n </button>\n )}\n </>\n )}\n </button>\n ))}\n </div>\n );\n}\n"
14
14
  }
15
15
  ]
16
16
  }
@@ -10,7 +10,7 @@
10
10
  "files": [
11
11
  {
12
12
  "path": "order-summary.tsx",
13
- "content": "\"use client\";\n\nimport React from \"react\";\nimport type { Order, LineItem, OrderStatus, PaymentState } from \"@cimplify/sdk\";\nimport { useOrder } from \"@cimplify/sdk/react\";\nimport { Price } from \"@cimplify/sdk/react\";\nimport { parsePrice } from \"@cimplify/sdk\";\nimport { cn } from \"@cimplify/sdk/react\";\n\nexport interface OrderSummaryClassNames {\n root?: string;\n header?: string;\n orderId?: string;\n status?: string;\n paymentState?: string;\n fulfillmentDetails?: string;\n deliveryAddress?: string;\n items?: string;\n lineItem?: string;\n notes?: string;\n totals?: string;\n customer?: string;\n loading?: string;\n serviceInfo?: string;\n digitalInfo?: string;\n bundleBreakdown?: string;\n compositeBreakdown?: string;\n trackingLink?: string;\n}\n\nexport interface OrderSummaryProps {\n /** Pass an Order object directly (skips fetch). */\n order?: Order;\n /** Or pass an order ID to fetch via useOrder. */\n orderId?: string;\n /** Poll for status updates. */\n poll?: boolean;\n /** Custom line item renderer. */\n renderLineItem?: (item: LineItem) => React.ReactNode;\n className?: string;\n classNames?: OrderSummaryClassNames;\n}\n\nconst STATUS_LABELS: Record<OrderStatus, string> = {\n pending: \"Pending\",\n created: \"Created\",\n confirmed: \"Confirmed\",\n in_preparation: \"In Preparation\",\n ready_to_serve: \"Ready\",\n partially_served: \"Partially Served\",\n served: \"Served\",\n delivered: \"Delivered\",\n picked_up: \"Picked Up\",\n completed: \"Completed\",\n cancelled: \"Cancelled\",\n refunded: \"Refunded\",\n};\n\nconst PAYMENT_STATE_LABELS: Record<PaymentState, string> = {\n not_paid: \"Not Paid\",\n paid: \"Paid\",\n partially_refunded: \"Partially Refunded\",\n refunded: \"Refunded\",\n};\n\n/**\n * OrderSummary — displays a single order's details, line items, and totals.\n *\n * Accepts either a pre-loaded `order` object or an `orderId` to fetch.\n * Supports polling for live status updates.\n */\nexport function OrderSummary({\n order: orderProp,\n orderId,\n poll = false,\n renderLineItem,\n className,\n classNames,\n}: OrderSummaryProps): React.ReactElement {\n const { order: fetched, isLoading } = useOrder(orderProp ? null : orderId, {\n enabled: !orderProp && !!orderId,\n poll,\n });\n\n const order = orderProp ?? fetched;\n\n if (isLoading && !order) {\n return (\n <div\n data-cimplify-order-summary\n aria-busy=\"true\"\n className={cn(className, classNames?.root, classNames?.loading)}\n >\n <div data-cimplify-order-summary-skeleton />\n </div>\n );\n }\n\n if (!order) {\n return (\n <div data-cimplify-order-summary className={cn(className, classNames?.root)}>\n <p>Order not found.</p>\n </div>\n );\n }\n\n return (\n <div data-cimplify-order-summary className={cn(className, classNames?.root)}>\n {/* Header */}\n <div data-cimplify-order-header className={classNames?.header}>\n <span data-cimplify-order-id className={classNames?.orderId}>\n Order #{order.user_friendly_id}\n </span>\n <span\n data-cimplify-order-status\n data-status={order.status}\n className={classNames?.status}\n >\n {STATUS_LABELS[order.status] ?? order.status}\n </span>\n {order.payment_state && (\n <span\n data-cimplify-order-payment-state\n data-payment-state={order.payment_state}\n className={classNames?.paymentState}\n >\n {PAYMENT_STATE_LABELS[order.payment_state] ?? order.payment_state}\n </span>\n )}\n </div>\n\n {/* Fulfillment details */}\n {order.order_type === \"pickup\" && order.pickup_time && (\n <div data-cimplify-order-fulfillment className={classNames?.fulfillmentDetails}>\n <span>Pickup time: {new Date(order.pickup_time).toLocaleString()}</span>\n </div>\n )}\n {order.order_type === \"dine-in\" && order.table_number && (\n <div data-cimplify-order-fulfillment className={classNames?.fulfillmentDetails}>\n <span>Table: {order.table_number}</span>\n </div>\n )}\n\n {/* Date */}\n <time data-cimplify-order-date dateTime={order.created_at}>\n {new Date(order.created_at).toLocaleDateString(undefined, {\n year: \"numeric\",\n month: \"long\",\n day: \"numeric\",\n hour: \"2-digit\",\n minute: \"2-digit\",\n })}\n </time>\n\n {/* Customer info */}\n {(order.customer_name || order.customer_email) && (\n <div data-cimplify-order-customer className={classNames?.customer}>\n {order.customer_name && (\n <span data-cimplify-order-customer-name>{order.customer_name}</span>\n )}\n {order.customer_email && (\n <span data-cimplify-order-customer-email>{order.customer_email}</span>\n )}\n </div>\n )}\n\n {/* Delivery address */}\n {order.delivery_address && (\n <div data-cimplify-order-delivery-address className={classNames?.deliveryAddress}>\n <span>Delivery address</span>\n <p>{order.delivery_address}</p>\n </div>\n )}\n\n {/* Line items */}\n <div data-cimplify-order-items className={classNames?.items}>\n {order.items.map((item) =>\n renderLineItem ? (\n <React.Fragment key={item.id}>{renderLineItem(item)}</React.Fragment>\n ) : (\n <div key={item.id} data-cimplify-order-line-item className={classNames?.lineItem}>\n <div data-cimplify-order-line-info>\n <span data-cimplify-order-line-qty>{item.quantity}&times;</span>\n <span data-cimplify-order-line-key>{item.line_key}</span>\n </div>\n <Price amount={item.price} />\n {item.fulfillment_type === \"digital\" && item.fulfillment_id && (\n <a\n href={item.fulfillment_id}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n data-cimplify-order-line-download\n className=\"text-sm text-primary underline\"\n >\n Download\n </a>\n )}\n\n {/* Service items: show scheduling and confirmation details */}\n {item.configuration.type === \"service\" && (\n <div data-cimplify-order-service-info className={classNames?.serviceInfo}>\n {item.configuration.scheduled_start && (\n <span>{\"\\u{1F4C5}\"} {new Date(item.configuration.scheduled_start).toLocaleDateString()} at {new Date(item.configuration.scheduled_start).toLocaleTimeString([], { hour: \"2-digit\", minute: \"2-digit\" })}</span>\n )}\n {item.configuration.confirmation_code && (\n <span>Confirmation: {item.configuration.confirmation_code}</span>\n )}\n {item.configuration.service_status && (\n <span data-status={item.configuration.service_status}>{item.configuration.service_status}</span>\n )}\n </div>\n )}\n\n {/* Digital items: instant delivery badge */}\n {item.configuration.type === \"digital\" && (\n <div data-cimplify-order-digital-info className={classNames?.digitalInfo}>\n <span>{\"\\u26A1\"} Instant digital delivery</span>\n </div>\n )}\n\n {/* Bundle items: component breakdown */}\n {item.configuration.type === \"bundle\" && item.configuration.resolved && (\n <div data-cimplify-order-bundle-breakdown className={classNames?.bundleBreakdown}>\n {item.configuration.resolved.selections.map((sel) => (\n <div key={sel.component_id} data-product-type={sel.product_type}>\n <span>{sel.quantity}&times; {sel.product_name}</span>\n {sel.product_type === \"service\" && sel.scheduling?.scheduled_start && (\n <span> &mdash; {new Date(sel.scheduling.scheduled_start).toLocaleDateString()}</span>\n )}\n {sel.product_type === \"digital\" && <span> &mdash; Digital</span>}\n </div>\n ))}\n </div>\n )}\n\n {/* Composite items: component breakdown with pricing */}\n {item.configuration.type === \"composite\" && item.configuration.resolved && (\n <div data-cimplify-order-composite-breakdown className={classNames?.compositeBreakdown}>\n {item.configuration.resolved.selections.map((sel) => (\n <div key={sel.component_id} data-product-type={sel.product_type}>\n <span>{sel.quantity}&times; {sel.component_name}</span>\n {sel.product_type === \"service\" && sel.scheduling?.scheduled_start && (\n <span> &mdash; {new Date(sel.scheduling.scheduled_start).toLocaleDateString()}</span>\n )}\n <Price amount={sel.unit_price} />\n </div>\n ))}\n </div>\n )}\n </div>\n ),\n )}\n </div>\n\n {/* Customer notes / special instructions */}\n {order.customer_notes && order.customer_notes.length > 0 && (\n <div data-cimplify-order-notes className={classNames?.notes}>\n <span>Notes</span>\n {order.customer_notes.map((note, index) => (\n <p key={index} data-cimplify-order-note>{note}</p>\n ))}\n </div>\n )}\n\n {/* Totals */}\n <div data-cimplify-order-totals className={classNames?.totals}>\n {order.total_discount != null && parsePrice(order.total_discount) !== 0 && (\n <div data-cimplify-order-discount>\n <span>Discount</span>\n <Price amount={order.total_discount} prefix=\"-\" />\n </div>\n )}\n {order.delivery_fee != null && parsePrice(order.delivery_fee) > 0 && (\n <div data-cimplify-order-delivery-fee>\n <span>Delivery fee</span>\n <Price amount={order.delivery_fee} />\n </div>\n )}\n {order.service_charge != null && parsePrice(order.service_charge) !== 0 && (\n <div data-cimplify-order-service-charge>\n <span>Service charge</span>\n <Price amount={order.service_charge} />\n </div>\n )}\n {order.tax != null && parsePrice(order.tax) !== 0 && (\n <div data-cimplify-order-tax>\n <span>Tax</span>\n <Price amount={order.tax} />\n </div>\n )}\n <div data-cimplify-order-total>\n <span>Total</span>\n <Price amount={order.total_price} />\n </div>\n </div>\n\n {/* Tracking */}\n {order.tracking_link && (\n <a\n href={order.tracking_link}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n data-cimplify-order-tracking\n className={classNames?.trackingLink}\n >\n Track your order\n </a>\n )}\n </div>\n );\n}\n"
13
+ "content": "\"use client\";\n\nimport React from \"react\";\nimport type { Order, LineItem, OrderStatus, PaymentState } from \"@cimplify/sdk\";\nimport { useOrder } from \"@cimplify/sdk/react\";\nimport { Price } from \"@cimplify/sdk/react\";\nimport { parsePrice } from \"@cimplify/sdk\";\nimport { cn } from \"@cimplify/sdk/react\";\n\nexport interface OrderSummaryClassNames {\n root?: string;\n header?: string;\n orderId?: string;\n status?: string;\n paymentState?: string;\n fulfillmentDetails?: string;\n deliveryAddress?: string;\n items?: string;\n lineItem?: string;\n notes?: string;\n totals?: string;\n customer?: string;\n loading?: string;\n serviceInfo?: string;\n digitalInfo?: string;\n bundleBreakdown?: string;\n compositeBreakdown?: string;\n trackingLink?: string;\n reorderButton?: string;\n}\n\nexport interface OrderSummaryProps {\n /** Pass an Order object directly (skips fetch). */\n order?: Order;\n /** Or pass an order ID to fetch via useOrder. */\n orderId?: string;\n /** Poll for status updates. */\n poll?: boolean;\n /** Custom line item renderer. */\n renderLineItem?: (item: LineItem) => React.ReactNode;\n /** Called when the reorder button is clicked. */\n onReorder?: (order: Order) => void;\n /** Called when the order status changes during polling. */\n onStatusChange?: (previousStatus: OrderStatus, newStatus: OrderStatus, order: Order) => void;\n className?: string;\n classNames?: OrderSummaryClassNames;\n}\n\nconst STATUS_LABELS: Record<OrderStatus, string> = {\n pending: \"Pending\",\n created: \"Created\",\n confirmed: \"Confirmed\",\n in_preparation: \"In Preparation\",\n ready_to_serve: \"Ready\",\n partially_served: \"Partially Served\",\n served: \"Served\",\n delivered: \"Delivered\",\n picked_up: \"Picked Up\",\n completed: \"Completed\",\n cancelled: \"Cancelled\",\n refunded: \"Refunded\",\n};\n\nconst PAYMENT_STATE_LABELS: Record<PaymentState, string> = {\n not_paid: \"Not Paid\",\n paid: \"Paid\",\n partially_refunded: \"Partially Refunded\",\n refunded: \"Refunded\",\n};\n\n/**\n * OrderSummary — displays a single order's details, line items, and totals.\n *\n * Accepts either a pre-loaded `order` object or an `orderId` to fetch.\n * Supports polling for live status updates.\n */\nexport function OrderSummary({\n order: orderProp,\n orderId,\n poll = false,\n renderLineItem,\n onReorder,\n onStatusChange,\n className,\n classNames,\n}: OrderSummaryProps): React.ReactElement {\n const { order: fetched, isLoading } = useOrder(orderProp ? null : orderId, {\n enabled: !orderProp && !!orderId,\n poll,\n onStatusChange,\n });\n\n const order = orderProp ?? fetched;\n\n if (isLoading && !order) {\n return (\n <div\n data-cimplify-order-summary\n aria-busy=\"true\"\n className={cn(className, classNames?.root, classNames?.loading)}\n >\n <div data-cimplify-order-summary-skeleton />\n </div>\n );\n }\n\n if (!order) {\n return (\n <div data-cimplify-order-summary className={cn(className, classNames?.root)}>\n <p>Order not found.</p>\n </div>\n );\n }\n\n return (\n <div data-cimplify-order-summary className={cn(className, classNames?.root)}>\n {/* Header */}\n <div data-cimplify-order-header className={classNames?.header}>\n <span data-cimplify-order-id className={classNames?.orderId}>\n Order #{order.user_friendly_id}\n </span>\n <span\n data-cimplify-order-status\n data-status={order.status}\n className={classNames?.status}\n >\n {STATUS_LABELS[order.status] ?? order.status}\n </span>\n {order.payment_state && (\n <span\n data-cimplify-order-payment-state\n data-payment-state={order.payment_state}\n className={classNames?.paymentState}\n >\n {PAYMENT_STATE_LABELS[order.payment_state] ?? order.payment_state}\n </span>\n )}\n </div>\n\n {/* Fulfillment details */}\n {order.order_type === \"pickup\" && order.pickup_time && (\n <div data-cimplify-order-fulfillment className={classNames?.fulfillmentDetails}>\n <span>Pickup time: {new Date(order.pickup_time).toLocaleString()}</span>\n </div>\n )}\n {order.order_type === \"dine-in\" && order.table_number && (\n <div data-cimplify-order-fulfillment className={classNames?.fulfillmentDetails}>\n <span>Table: {order.table_number}</span>\n </div>\n )}\n\n {/* Date */}\n <time data-cimplify-order-date dateTime={order.created_at}>\n {new Date(order.created_at).toLocaleDateString(undefined, {\n year: \"numeric\",\n month: \"long\",\n day: \"numeric\",\n hour: \"2-digit\",\n minute: \"2-digit\",\n })}\n </time>\n\n {/* Customer info */}\n {(order.customer_name || order.customer_email) && (\n <div data-cimplify-order-customer className={classNames?.customer}>\n {order.customer_name && (\n <span data-cimplify-order-customer-name>{order.customer_name}</span>\n )}\n {order.customer_email && (\n <span data-cimplify-order-customer-email>{order.customer_email}</span>\n )}\n </div>\n )}\n\n {/* Delivery address */}\n {order.delivery_address && (\n <div data-cimplify-order-delivery-address className={classNames?.deliveryAddress}>\n <span>Delivery address</span>\n <p>{order.delivery_address}</p>\n </div>\n )}\n\n {/* Line items */}\n <div data-cimplify-order-items className={classNames?.items}>\n {order.items.map((item) =>\n renderLineItem ? (\n <React.Fragment key={item.id}>{renderLineItem(item)}</React.Fragment>\n ) : (\n <div key={item.id} data-cimplify-order-line-item className={classNames?.lineItem}>\n <div data-cimplify-order-line-info>\n <span data-cimplify-order-line-qty>{item.quantity}&times;</span>\n <span data-cimplify-order-line-key>{item.line_key}</span>\n </div>\n <Price amount={item.price} />\n {item.fulfillment_type === \"digital\" && item.fulfillment_id && (\n <a\n href={item.fulfillment_id}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n data-cimplify-order-line-download\n className=\"text-sm text-primary underline\"\n >\n Download\n </a>\n )}\n\n {/* Service items: show scheduling and confirmation details */}\n {item.configuration.type === \"service\" && (\n <div data-cimplify-order-service-info className={classNames?.serviceInfo}>\n {item.configuration.scheduled_start && (\n <span>{\"\\u{1F4C5}\"} {new Date(item.configuration.scheduled_start).toLocaleDateString()} at {new Date(item.configuration.scheduled_start).toLocaleTimeString([], { hour: \"2-digit\", minute: \"2-digit\" })}</span>\n )}\n {item.configuration.confirmation_code && (\n <span>Confirmation: {item.configuration.confirmation_code}</span>\n )}\n {item.configuration.service_status && (\n <span data-status={item.configuration.service_status}>{item.configuration.service_status}</span>\n )}\n </div>\n )}\n\n {/* Digital items: instant delivery badge */}\n {item.configuration.type === \"digital\" && (\n <div data-cimplify-order-digital-info className={classNames?.digitalInfo}>\n <span>{\"\\u26A1\"} Instant digital delivery</span>\n </div>\n )}\n\n {/* Bundle items: component breakdown */}\n {item.configuration.type === \"bundle\" && item.configuration.resolved && (\n <div data-cimplify-order-bundle-breakdown className={classNames?.bundleBreakdown}>\n {item.configuration.resolved.selections.map((sel) => (\n <div key={sel.component_id} data-product-type={sel.product_type}>\n <span>{sel.quantity}&times; {sel.product_name}</span>\n {sel.product_type === \"service\" && sel.scheduling?.scheduled_start && (\n <span> &mdash; {new Date(sel.scheduling.scheduled_start).toLocaleDateString()}</span>\n )}\n {sel.product_type === \"digital\" && <span> &mdash; Digital</span>}\n </div>\n ))}\n </div>\n )}\n\n {/* Composite items: component breakdown with pricing */}\n {item.configuration.type === \"composite\" && item.configuration.resolved && (\n <div data-cimplify-order-composite-breakdown className={classNames?.compositeBreakdown}>\n {item.configuration.resolved.selections.map((sel) => (\n <div key={sel.component_id} data-product-type={sel.product_type}>\n <span>{sel.quantity}&times; {sel.component_name}</span>\n {sel.product_type === \"service\" && sel.scheduling?.scheduled_start && (\n <span> &mdash; {new Date(sel.scheduling.scheduled_start).toLocaleDateString()}</span>\n )}\n <Price amount={sel.unit_price} />\n </div>\n ))}\n </div>\n )}\n </div>\n ),\n )}\n </div>\n\n {/* Customer notes / special instructions */}\n {order.customer_notes && order.customer_notes.length > 0 && (\n <div data-cimplify-order-notes className={classNames?.notes}>\n <span>Notes</span>\n {order.customer_notes.map((note, index) => (\n <p key={index} data-cimplify-order-note>{note}</p>\n ))}\n </div>\n )}\n\n {/* Totals */}\n <div data-cimplify-order-totals className={classNames?.totals}>\n {order.total_discount != null && parsePrice(order.total_discount) !== 0 && (\n <div data-cimplify-order-discount>\n <span>Discount</span>\n <Price amount={order.total_discount} prefix=\"-\" />\n </div>\n )}\n {order.delivery_fee != null && parsePrice(order.delivery_fee) > 0 && (\n <div data-cimplify-order-delivery-fee>\n <span>Delivery fee</span>\n <Price amount={order.delivery_fee} />\n </div>\n )}\n {order.service_charge != null && parsePrice(order.service_charge) !== 0 && (\n <div data-cimplify-order-service-charge>\n <span>Service charge</span>\n <Price amount={order.service_charge} />\n </div>\n )}\n {order.tax != null && parsePrice(order.tax) !== 0 && (\n <div data-cimplify-order-tax>\n <span>Tax</span>\n <Price amount={order.tax} />\n </div>\n )}\n <div data-cimplify-order-total>\n <span>Total</span>\n <Price amount={order.total_price} />\n </div>\n </div>\n\n {/* Tracking */}\n {order.tracking_link && (\n <a\n href={order.tracking_link}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n data-cimplify-order-tracking\n className={classNames?.trackingLink}\n >\n Track your order\n </a>\n )}\n\n {onReorder && (\n <button\n type=\"button\"\n onClick={() => onReorder(order)}\n data-cimplify-order-reorder\n className={classNames?.reorderButton}\n >\n Reorder\n </button>\n )}\n </div>\n );\n}\n"
14
14
  }
15
15
  ]
16
16
  }