@mohasinac/appkit 4.14.0 → 4.16.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.
Files changed (206) hide show
  1. package/dist/_internal/server/features/auctions/actions.js +2 -2
  2. package/dist/_internal/server/features/cart/service.d.ts +3 -3
  3. package/dist/_internal/server/features/checkout/actions.js +19 -0
  4. package/dist/_internal/server/features/checkout/source-context.d.ts +5 -0
  5. package/dist/_internal/server/features/checkout/source-context.js +157 -0
  6. package/dist/_internal/server/features/offers/adapters.d.ts +103 -0
  7. package/dist/_internal/server/features/offers/adapters.js +88 -0
  8. package/dist/_internal/server/features/offers/index.d.ts +2 -0
  9. package/dist/_internal/server/features/offers/index.js +1 -0
  10. package/dist/_internal/server/features/orders/adapters.js +24 -0
  11. package/dist/_internal/server/jobs/core/offerExpiry.js +10 -10
  12. package/dist/_internal/shared/actions/action-registry.d.ts +9 -2
  13. package/dist/_internal/shared/actions/action-registry.js +458 -7
  14. package/dist/_internal/shared/features/auctions/schema.d.ts +14 -2
  15. package/dist/_internal/shared/features/auctions/schema.js +13 -1
  16. package/dist/_internal/shared/features/blog/schema.d.ts +4 -4
  17. package/dist/_internal/shared/features/cart/schema.d.ts +25 -20
  18. package/dist/_internal/shared/features/cart/schema.js +15 -0
  19. package/dist/_internal/shared/features/categories/bundle-schemas.d.ts +22 -22
  20. package/dist/_internal/shared/features/orders/schema.d.ts +4 -4
  21. package/dist/_internal/shared/features/products/schema.d.ts +27 -27
  22. package/dist/_internal/shared/features/promotions/schema.d.ts +4 -4
  23. package/dist/_internal/shared/features/reviews/schema.d.ts +2 -2
  24. package/dist/_internal/shared/history/history.d.ts +22 -0
  25. package/dist/_internal/shared/history/history.js +41 -1
  26. package/dist/client.d.ts +13 -2
  27. package/dist/client.js +20 -2
  28. package/dist/constants/field-names.d.ts +8 -6
  29. package/dist/constants/field-names.js +48 -6
  30. package/dist/contracts/extend.d.ts +29 -0
  31. package/dist/contracts/extend.js +20 -0
  32. package/dist/contracts/index.d.ts +2 -1
  33. package/dist/contracts/index.js +2 -0
  34. package/dist/features/account/components/UserBidsView.js +12 -4
  35. package/dist/features/account/components/UserReturnsView.d.ts +3 -0
  36. package/dist/features/account/components/UserReturnsView.js +17 -1
  37. package/dist/features/admin/components/AdminAuditLogView.js +1 -0
  38. package/dist/features/admin/components/AdminBidsView.js +15 -2
  39. package/dist/features/admin/components/AdminBlogEditorView.js +65 -33
  40. package/dist/features/admin/components/AdminFeatureEditorView.js +35 -6
  41. package/dist/features/admin/components/AdminNavEditorView.js +15 -5
  42. package/dist/features/admin/components/AdminNewsletterView.js +18 -2
  43. package/dist/features/admin/components/AdminOffersView.js +62 -18
  44. package/dist/features/admin/components/AdminPaymentMethodsView.js +24 -4
  45. package/dist/features/admin/components/AdminScammerEditorView.d.ts +2 -1
  46. package/dist/features/admin/components/AdminScammerEditorView.js +3 -13
  47. package/dist/features/admin/components/AdminScammersView.js +3 -8
  48. package/dist/features/admin/components/AdminSectionsView.js +32 -23
  49. package/dist/features/admin/components/AdminSessionsView.js +17 -2
  50. package/dist/features/admin/components/AdminViewCards.js +24 -17
  51. package/dist/features/admin/components/DataListingView.js +15 -1
  52. package/dist/features/admin/components/DataTable.js +7 -17
  53. package/dist/features/admin/components/ViewAuditLogEntryModal.js +1 -0
  54. package/dist/features/admin/components/sections/adminSectionsTypes.d.ts +4 -2
  55. package/dist/features/admin/components/sections/adminSectionsTypes.js +45 -27
  56. package/dist/features/admin/constants/filter-tabs.d.ts +3 -0
  57. package/dist/features/admin/constants/filter-tabs.js +1 -0
  58. package/dist/features/admin/schemas/homepage-section-form.d.ts +22 -0
  59. package/dist/features/admin/schemas/homepage-section-form.js +68 -0
  60. package/dist/features/admin/schemas/nav-item-form.d.ts +74 -0
  61. package/dist/features/admin/schemas/nav-item-form.js +72 -0
  62. package/dist/features/auctions/components/AuctionBidsTable.d.ts +10 -1
  63. package/dist/features/auctions/components/AuctionBidsTable.js +22 -4
  64. package/dist/features/auctions/components/bid-detail-fields.d.ts +29 -0
  65. package/dist/features/auctions/components/bid-detail-fields.js +103 -0
  66. package/dist/features/auctions/schemas/index.d.ts +14 -14
  67. package/dist/features/audit-log/schemas/firestore.d.ts +14 -0
  68. package/dist/features/audit-log/schemas/firestore.js +7 -0
  69. package/dist/features/auth/schemas/index.d.ts +2 -2
  70. package/dist/features/blog/actions/blog-actions.d.ts +7 -7
  71. package/dist/features/blog/schemas/blog-form.d.ts +80 -0
  72. package/dist/features/blog/schemas/blog-form.js +100 -0
  73. package/dist/features/blog/schemas/index.d.ts +10 -10
  74. package/dist/features/cart/schemas/index.d.ts +3 -3
  75. package/dist/features/catalogue/schemas/validation.d.ts +12 -12
  76. package/dist/features/categories/schemas/index.d.ts +16 -16
  77. package/dist/features/collections/schemas/index.d.ts +8 -8
  78. package/dist/features/events/components/AdminEventEditorView.js +32 -54
  79. package/dist/features/events/components/AdminEventEntriesView.js +22 -3
  80. package/dist/features/events/schemas/event-form.d.ts +113 -0
  81. package/dist/features/events/schemas/event-form.js +82 -0
  82. package/dist/features/events/schemas/index.d.ts +2 -2
  83. package/dist/features/faq/actions/faq-actions.d.ts +6 -6
  84. package/dist/features/faq/schemas/index.d.ts +6 -6
  85. package/dist/features/grouped/schemas/grouped-listing-form.d.ts +101 -0
  86. package/dist/features/grouped/schemas/grouped-listing-form.js +89 -0
  87. package/dist/features/grouped/schemas/index.d.ts +4 -4
  88. package/dist/features/homepage/actions/homepage-section-actions.d.ts +11 -3
  89. package/dist/features/homepage/actions/homepage-section-actions.js +33 -1
  90. package/dist/features/homepage/schemas/index.d.ts +71 -40
  91. package/dist/features/homepage/schemas/index.js +24 -0
  92. package/dist/features/messages/schemas/index.d.ts +2 -2
  93. package/dist/features/orders/components/OrderStatusTimeline.d.ts +5 -1
  94. package/dist/features/orders/components/OrderStatusTimeline.js +39 -20
  95. package/dist/features/orders/repository/orders.repository.js +5 -0
  96. package/dist/features/orders/schemas/firestore.d.ts +36 -1
  97. package/dist/features/orders/schemas/index.d.ts +24 -24
  98. package/dist/features/orders/types/index.d.ts +35 -7
  99. package/dist/features/pre-orders/schemas/index.d.ts +4 -4
  100. package/dist/features/products/components/GroupSettingsPanel.js +5 -1
  101. package/dist/features/products/schemas/index.d.ts +38 -38
  102. package/dist/features/products/schemas/product-features.validators.d.ts +75 -2
  103. package/dist/features/products/schemas/product-features.validators.js +63 -0
  104. package/dist/features/promotions/schemas/index.d.ts +14 -14
  105. package/dist/features/reviews/schemas/index.d.ts +14 -14
  106. package/dist/features/scams/constants/scammer-status-display.d.ts +28 -0
  107. package/dist/features/scams/constants/scammer-status-display.js +79 -0
  108. package/dist/features/scams/schemas/index.d.ts +2 -2
  109. package/dist/features/search/schemas/index.d.ts +2 -2
  110. package/dist/features/seller/actions/offer-actions.js +32 -2
  111. package/dist/features/seller/components/OfferPhaseTimeline.d.ts +59 -0
  112. package/dist/features/seller/components/OfferPhaseTimeline.js +107 -0
  113. package/dist/features/seller/components/SellerBidsView.js +11 -4
  114. package/dist/features/seller/components/SellerCouponEditorView.js +17 -17
  115. package/dist/features/seller/components/SellerOffersView.js +2 -1
  116. package/dist/features/seller/components/SellerPayoutSettingsView.js +38 -14
  117. package/dist/features/seller/repository/offer.repository.d.ts +69 -8
  118. package/dist/features/seller/repository/offer.repository.js +127 -23
  119. package/dist/features/seller/schemas/coupon-form.d.ts +72 -0
  120. package/dist/features/seller/schemas/coupon-form.js +116 -0
  121. package/dist/features/seller/schemas/firestore.d.ts +56 -2
  122. package/dist/features/seller/schemas/firestore.js +27 -0
  123. package/dist/features/seller/schemas/index.d.ts +2 -2
  124. package/dist/features/seller/schemas/offer-forms.d.ts +21 -0
  125. package/dist/features/seller/schemas/offer-forms.js +21 -0
  126. package/dist/features/shell/build-sections.d.ts +63 -0
  127. package/dist/features/shell/build-sections.js +207 -0
  128. package/dist/features/shell/field-ui-meta.d.ts +115 -0
  129. package/dist/features/shell/field-ui-meta.js +324 -0
  130. package/dist/features/shell/index.d.ts +4 -0
  131. package/dist/features/shell/index.js +2 -0
  132. package/dist/features/shipments/schemas/validation.d.ts +22 -22
  133. package/dist/features/status-history/components/StatusTimeline.d.ts +74 -0
  134. package/dist/features/status-history/components/StatusTimeline.js +46 -0
  135. package/dist/features/status-history/index.d.ts +11 -0
  136. package/dist/features/status-history/index.js +10 -0
  137. package/dist/features/store-extensions/components/ReviewDecisionModal.d.ts +27 -0
  138. package/dist/features/store-extensions/components/ReviewDecisionModal.js +82 -0
  139. package/dist/features/store-extensions/index.d.ts +12 -0
  140. package/dist/features/store-extensions/index.js +12 -0
  141. package/dist/features/store-extensions/schemas/admin-notification-form.d.ts +36 -0
  142. package/dist/features/store-extensions/schemas/admin-notification-form.js +114 -0
  143. package/dist/features/store-extensions/schemas/analytics-forms.d.ts +57 -0
  144. package/dist/features/store-extensions/schemas/analytics-forms.js +158 -0
  145. package/dist/features/store-extensions/schemas/custom-role-form.d.ts +85 -0
  146. package/dist/features/store-extensions/schemas/custom-role-form.js +118 -0
  147. package/dist/features/store-extensions/schemas/firestore.d.ts +21 -1
  148. package/dist/features/store-extensions/schemas/firestore.js +60 -0
  149. package/dist/features/store-extensions/schemas/google-config-form.d.ts +53 -0
  150. package/dist/features/store-extensions/schemas/google-config-form.js +105 -0
  151. package/dist/features/store-extensions/schemas/item-request-create-form.d.ts +28 -0
  152. package/dist/features/store-extensions/schemas/item-request-create-form.js +80 -0
  153. package/dist/features/store-extensions/schemas/listing-template-form.d.ts +78 -0
  154. package/dist/features/store-extensions/schemas/listing-template-form.js +87 -0
  155. package/dist/features/store-extensions/schemas/moderation-review-form.d.ts +49 -0
  156. package/dist/features/store-extensions/schemas/moderation-review-form.js +101 -0
  157. package/dist/features/store-extensions/schemas/payout-method-form.d.ts +153 -0
  158. package/dist/features/store-extensions/schemas/payout-method-form.js +155 -0
  159. package/dist/features/store-extensions/schemas/report-create-form.d.ts +25 -0
  160. package/dist/features/store-extensions/schemas/report-create-form.js +79 -0
  161. package/dist/features/store-extensions/schemas/report-review-form.d.ts +47 -0
  162. package/dist/features/store-extensions/schemas/report-review-form.js +111 -0
  163. package/dist/features/store-extensions/schemas/shipping-config-form.d.ts +148 -0
  164. package/dist/features/store-extensions/schemas/shipping-config-form.js +104 -0
  165. package/dist/features/store-extensions/schemas/store-category-form.d.ts +54 -0
  166. package/dist/features/store-extensions/schemas/store-category-form.js +73 -0
  167. package/dist/features/stores/schemas/index.d.ts +4 -4
  168. package/dist/features/support/schemas/index.d.ts +4 -4
  169. package/dist/features/tester/seed-data/tester-checklist-seed-data.js +350 -0
  170. package/dist/features/wishlist/hooks/useWishlistWithGuest.d.ts +1 -1
  171. package/dist/index.d.ts +12 -2
  172. package/dist/index.js +12 -2
  173. package/dist/next/routing/route-map.d.ts +16 -2
  174. package/dist/next/routing/route-map.js +9 -2
  175. package/dist/schemas/firestore-value.d.ts +8 -0
  176. package/dist/schemas/firestore-value.js +44 -0
  177. package/dist/schemas/index.d.ts +1 -1
  178. package/dist/schemas/index.js +1 -1
  179. package/dist/schemas/registry.d.ts +734 -101
  180. package/dist/schemas/registry.js +64 -1
  181. package/dist/seed/bids-seed-data.js +30 -0
  182. package/dist/seed/offers-seed-data.js +212 -7
  183. package/dist/seed/orders-seed-data.js +336 -1
  184. package/dist/seed/products-auctions-seed-data.js +41 -0
  185. package/dist/server.d.ts +2 -0
  186. package/dist/server.js +5 -0
  187. package/dist/styles.css +1 -1
  188. package/dist/tailwind-utilities.css +1 -1
  189. package/dist/ui/DataTable.js +4 -2
  190. package/dist/ui/columns/build-columns.d.ts +6 -1
  191. package/dist/ui/columns/build-columns.js +53 -0
  192. package/dist/ui/columns/cell-renderers.d.ts +68 -0
  193. package/dist/ui/columns/cell-renderers.js +111 -0
  194. package/dist/ui/columns/column-renderers.d.ts +6 -1
  195. package/dist/ui/columns/column-renderers.js +6 -1
  196. package/dist/ui/columns/index.d.ts +2 -1
  197. package/dist/ui/columns/index.js +5 -1
  198. package/dist/ui/components/Button.js +27 -2
  199. package/dist/ui/components/RecordDetailModal.d.ts +10 -1
  200. package/dist/ui/components/RecordDetailModal.js +2 -2
  201. package/dist/ui/icons/icon-registry.d.ts +65 -0
  202. package/dist/ui/icons/icon-registry.js +115 -0
  203. package/package.json +2 -1
  204. package/scripts/node-esm-loader.mjs +65 -0
  205. package/scripts/seed-cli-loader.mjs +6 -49
  206. package/scripts/seed-cli.mjs +1 -1
@@ -1,16 +1,25 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  /**
4
- * OrderStatusTimeline — a genuine (not fabricated/estimated) status timeline
5
- * built from the real per-status timestamps already written by the
6
- * order-status-update path (`orderDate`/`shippingDate`/`deliveryDate`/
7
- * `cancellationDate`). No live courier-API integration — `trackingUrl` is
8
- * just the passthrough link a seller/admin entered, rendered as an external
9
- * link that opens in a new tab. Statuses with no dedicated date field
10
- * (confirmed/processing/return_requested/returned/refunded) render as a step
11
- * without a timestamp rather than showing a made-up date.
4
+ * OrderStatusTimeline — the order-shaped wrapper around `<StatusTimeline>`.
5
+ *
6
+ * Two branches, and the fallback matters as much as the primary path:
7
+ *
8
+ * **Branch A — real history.** When the order carries `timeline` entries
9
+ * (written by the repository since W2), each status change becomes a step
10
+ * dated by when it actually happened, stamped with who did it. This finally
11
+ * makes transitions with no dedicated date field visible at all — a payment
12
+ * review, a re-upload request, a partial refund.
13
+ *
14
+ * **Branch B — legacy orders.** Every order written before `statusHistory`
15
+ * existed has none, and back-filling one would mean inventing timestamps. So
16
+ * the original derivation from the four scalar dates is kept verbatim for
17
+ * those. Statuses with no dedicated date field render without one.
18
+ *
19
+ * No live courier API: `trackingUrl` is the passthrough link a seller entered.
12
20
  */
13
- import { Anchor, Badge, Div, Heading, Row, Stack, Text } from "../../../ui";
21
+ import { Anchor, Badge, Row, Stack, Text } from "../../../ui";
22
+ import { StatusTimeline, stepsFromEntries, } from "../../status-history/components/StatusTimeline";
14
23
  const STAGE_SEQUENCE = ["pending", "confirmed", "processing", "shipped", "delivered"];
15
24
  const STAGE_LABELS = {
16
25
  pending: "Order placed",
@@ -23,10 +32,16 @@ const STAGE_LABELS = {
23
32
  returned: "Returned",
24
33
  refunded: "Refunded",
25
34
  };
26
- function buildSteps(props) {
35
+ const NEGATIVE_STATUSES = new Set(["cancelled"]);
36
+ const labelFor = (status) => STAGE_LABELS[status] ?? status;
37
+ const isNegative = (status) => NEGATIVE_STATUSES.has(status);
38
+ /** Branch B — derive from the four scalar dates, for orders with no history. */
39
+ function buildLegacySteps(props) {
27
40
  const status = (props.orderStatus ?? "").toLowerCase();
28
41
  const currentIndex = STAGE_SEQUENCE.indexOf(status);
29
- const steps = [{ key: "pending", label: STAGE_LABELS.pending, date: props.orderDate }];
42
+ const steps = [
43
+ { key: "pending", label: STAGE_LABELS.pending, date: props.orderDate },
44
+ ];
30
45
  if ((status === "confirmed" || status === "processing") && !props.shippingDate) {
31
46
  steps.push({ key: status, label: STAGE_LABELS[status] });
32
47
  }
@@ -37,19 +52,23 @@ function buildSteps(props) {
37
52
  steps.push({ key: "delivered", label: STAGE_LABELS.delivered, date: props.deliveryDate });
38
53
  }
39
54
  if (status === "cancelled" || props.cancellationDate) {
40
- steps.push({ key: "cancelled", label: STAGE_LABELS.cancelled, date: props.cancellationDate });
55
+ steps.push({
56
+ key: "cancelled",
57
+ label: STAGE_LABELS.cancelled,
58
+ date: props.cancellationDate,
59
+ negative: true,
60
+ });
41
61
  }
42
62
  else if (status === "return_requested" || status === "returned" || status === "refunded") {
43
63
  steps.push({ key: status, label: STAGE_LABELS[status] ?? status });
44
64
  }
45
65
  return steps;
46
66
  }
47
- export function OrderStatusTimeline({ orderStatus, orderDate, shippingDate, deliveryDate, cancellationDate, trackingNumber, shippingCarrier, trackingUrl, className = "", }) {
48
- const steps = buildSteps({ orderStatus, orderDate, shippingDate, deliveryDate, cancellationDate });
49
- const isTerminalNegative = steps[steps.length - 1]?.key === "cancelled";
50
- return (_jsxs(Div, { className: className, rounded: "xl", padding: "md", surface: "muted", border: "default", children: [_jsx(Heading, { level: 3, size: "sm", weight: "semibold", color: "muted", className: "mb-3", children: "Tracking" }), _jsx(Stack, { gap: "none", children: steps.map((step, i) => {
51
- const isLast = i === steps.length - 1;
52
- const isNegative = step.key === "cancelled";
53
- return (_jsxs(Row, { gap: "sm", padding: isLast ? "none" : "b-md", children: [_jsxs(Stack, { gap: "none", align: "center", className: "shrink-0", children: [_jsx(Div, { className: `h-2.5 w-2.5 ${isNegative ? "" : "bg-[var(--appkit-color-primary)]"}`, rounded: "full", surface: isNegative ? "danger-surface" : "none" }), !isLast && _jsx(Div, { className: "w-px flex-1 min-h-[24px]", surface: "subtle" })] }), _jsxs(Stack, { gap: "none", className: "min-w-0 flex-1", children: [_jsx(Text, { size: "sm", weight: "medium", className: isNegative ? "text-error" : undefined, children: step.label }), _jsx(Text, { size: "xs", color: "muted", children: step.date ? new Date(step.date).toLocaleString() : "—" })] })] }, step.key));
54
- }) }), (trackingNumber || shippingCarrier || trackingUrl) && !isTerminalNegative && (_jsx(Div, { className: "mt-3 border-t border-[var(--appkit-color-border)]", padding: "t-sm", children: _jsxs(Stack, { gap: "xs", children: [shippingCarrier && (_jsxs(Row, { justify: "between", children: [_jsx(Text, { size: "sm", color: "muted", children: "Carrier" }), _jsx(Text, { size: "sm", weight: "medium", children: shippingCarrier })] })), trackingNumber && (_jsxs(Row, { justify: "between", children: [_jsx(Text, { size: "sm", color: "muted", children: "Tracking number" }), _jsx(Text, { size: "sm", weight: "medium", children: trackingNumber })] })), trackingUrl && (_jsx(Row, { justify: "end", children: _jsx(Anchor, { href: trackingUrl, tone: "brand", weight: "semibold", size: "sm", children: "Track with carrier \u2192" }) }))] }) })), !trackingNumber && !trackingUrl && !isTerminalNegative && (_jsx(Badge, { variant: "default", className: "mt-3", children: "No tracking details yet" }))] }));
67
+ export function OrderStatusTimeline({ orderStatus, timeline, timelineTruncated, orderDate, shippingDate, deliveryDate, cancellationDate, trackingNumber, shippingCarrier, trackingUrl, className = "", }) {
68
+ const fromHistory = stepsFromEntries(timeline, labelFor, isNegative);
69
+ const steps = fromHistory.length
70
+ ? fromHistory
71
+ : buildLegacySteps({ orderStatus, orderDate, shippingDate, deliveryDate, cancellationDate });
72
+ const hasTracking = Boolean(trackingNumber || shippingCarrier || trackingUrl);
73
+ return (_jsx(StatusTimeline, { title: "Tracking", steps: steps, truncatedCount: timelineTruncated, className: className, footer: hasTracking ? (_jsxs(Stack, { gap: "xs", children: [shippingCarrier && (_jsxs(Row, { justify: "between", children: [_jsx(Text, { size: "sm", color: "muted", children: "Carrier" }), _jsx(Text, { size: "sm", weight: "medium", children: shippingCarrier })] })), trackingNumber && (_jsxs(Row, { justify: "between", children: [_jsx(Text, { size: "sm", color: "muted", children: "Tracking number" }), _jsx(Text, { size: "sm", weight: "medium", children: trackingNumber })] })), trackingUrl && (_jsx(Row, { justify: "end", children: _jsx(Anchor, { href: trackingUrl, tone: "brand", weight: "semibold", size: "sm", children: "Track with carrier \u2192" }) }))] })) : (_jsx(Badge, { variant: "default", children: "No tracking details yet" })) }));
55
74
  }
@@ -95,6 +95,11 @@ class OrderRepository extends BaseRepository {
95
95
  reason: ctx?.reason,
96
96
  note: ctx?.note,
97
97
  extraChanges: ctx?.extraChanges,
98
+ // `encryptPiiFields` is a flat top-level loop and never descends into
99
+ // arrays, so anything PII-named reaching `changes` would persist in
100
+ // PLAINTEXT inside statusHistory. None of ORDER_TRACKED_FIELDS is PII
101
+ // today — this is defence for the next field somebody adds.
102
+ piiFields: ORDER_PII_FIELDS,
98
103
  historyField: "statusHistory",
99
104
  truncatedField: "statusHistoryTruncated",
100
105
  });
@@ -24,12 +24,39 @@ export interface OrderSourceAuctionWon {
24
24
  /** Second-highest bid, when there was one. */
25
25
  runnerUpAmount?: number;
26
26
  }
27
+ /**
28
+ * A buyout is a REAL BID, not a bid-free purchase.
29
+ *
30
+ * An earlier draft of this type asserted "zero bids BY DEFINITION, because
31
+ * buy-now is only valid while `!bidsHaveStarted`". That gate no longer
32
+ * exists: `buyNowAuction` places an actual `BidDocument` (`isBuyout: true`)
33
+ * plus a 1h locked cart line and leaves the auction **live**, and Buy Now is
34
+ * offered whenever `buyNowPrice` still beats the standing bid. Recording
35
+ * `bidCount: 0` would bake a falsehood into an immutable audit record.
36
+ *
37
+ * `standingBidAtBuyout` is the highest NON-buyout bid the buyout beat — read
38
+ * from the bids rather than `product.currentBid`, because a pending buyout
39
+ * deliberately never writes to that mirror.
40
+ *
41
+ * An unpaid buyout lapses to **`cancelled`**, not `forfeited`, and the seller
42
+ * is deliberately not notified: nothing was ever sold. That makes its
43
+ * timeline materially different from a lapsed auction win.
44
+ */
27
45
  export interface OrderSourceAuctionBuyNow {
28
46
  path: "auction-buy-now";
29
47
  auctionId: string;
48
+ /** The buyout bid itself — a buyout IS a bid. */
49
+ bidId: string;
30
50
  buyNowPrice: number;
31
51
  listPrice: number;
52
+ /** Highest non-buyout bid at the moment of buyout. 0 when nobody had bid. */
53
+ standingBidAtBuyout: number;
54
+ /** Active non-buyout bids at buyout time. NOT always zero. */
55
+ bidCount: number;
32
56
  boughtAt: Date;
57
+ /** `min(now + AUCTION_BUYOUT_WINDOW_MS, auctionEndDate)`. */
58
+ checkoutDeadline: Date;
59
+ auctionEndDate?: Date;
33
60
  }
34
61
  export interface OrderSourceOfferAccepted {
35
62
  path: "offer-accepted";
@@ -38,7 +65,15 @@ export interface OrderSourceOfferAccepted {
38
65
  /** The asking price at the moment the offer was made. */
39
66
  listPriceAtOffer: number;
40
67
  counterAmount?: number;
41
- /** How many counter rounds before agreement. 0 = accepted outright. */
68
+ /**
69
+ * How many counter rounds before agreement. 1 = accepted outright.
70
+ *
71
+ * Read straight off the accepted offer's `counterRound`, which W2 made a
72
+ * persisted field. Before that this number was **unknowable**: each counter
73
+ * created an unlinked document and `previousOfferId` lived only inside a
74
+ * `serverLogger.info` call, so a three-round negotiation left no trace of
75
+ * having been one.
76
+ */
42
77
  counterRounds: number;
43
78
  acceptedBy: string;
44
79
  acceptedAt: Date;
@@ -91,17 +91,17 @@ export declare const orderDocumentItemSchema: z.ZodObject<{
91
91
  listingType: z.ZodOptional<z.ZodEnum<["standard", "auction", "pre-order", "prize-draw", "classified", "digital-code", "live"]>>;
92
92
  prizeRevealStatus: z.ZodOptional<z.ZodEnum<["pending", "open", "closed", "revealed"]>>;
93
93
  }, "strip", z.ZodTypeAny, {
94
+ quantity: number;
94
95
  productId: string;
95
96
  productTitle: string;
96
- quantity: number;
97
97
  unitPrice: number;
98
98
  totalPrice: number;
99
99
  listingType?: "classified" | "standard" | "auction" | "pre-order" | "prize-draw" | "digital-code" | "live" | undefined;
100
100
  prizeRevealStatus?: "closed" | "open" | "pending" | "revealed" | undefined;
101
101
  }, {
102
+ quantity: number;
102
103
  productId: string;
103
104
  productTitle: string;
104
- quantity: number;
105
105
  unitPrice: number;
106
106
  totalPrice: number;
107
107
  listingType?: "classified" | "standard" | "auction" | "pre-order" | "prize-draw" | "digital-code" | "live" | undefined;
@@ -168,14 +168,14 @@ export declare const appliedOrderDiscountSchema: z.ZodObject<{
168
168
  code: string;
169
169
  type: "auto" | "coupon" | "deal";
170
170
  discountAmount: number;
171
- scope?: "seller" | "admin" | undefined;
171
+ scope?: "admin" | "seller" | undefined;
172
172
  storeId?: string | undefined;
173
173
  couponId?: string | undefined;
174
174
  }, {
175
175
  code: string;
176
176
  type: "auto" | "coupon" | "deal";
177
177
  discountAmount: number;
178
- scope?: "seller" | "admin" | undefined;
178
+ scope?: "admin" | "seller" | undefined;
179
179
  storeId?: string | undefined;
180
180
  couponId?: string | undefined;
181
181
  }>;
@@ -250,17 +250,17 @@ export declare const orderFirestoreSchema: z.ZodObject<{
250
250
  listingType: z.ZodOptional<z.ZodEnum<["standard", "auction", "pre-order", "prize-draw", "classified", "digital-code", "live"]>>;
251
251
  prizeRevealStatus: z.ZodOptional<z.ZodEnum<["pending", "open", "closed", "revealed"]>>;
252
252
  }, "strip", z.ZodTypeAny, {
253
+ quantity: number;
253
254
  productId: string;
254
255
  productTitle: string;
255
- quantity: number;
256
256
  unitPrice: number;
257
257
  totalPrice: number;
258
258
  listingType?: "classified" | "standard" | "auction" | "pre-order" | "prize-draw" | "digital-code" | "live" | undefined;
259
259
  prizeRevealStatus?: "closed" | "open" | "pending" | "revealed" | undefined;
260
260
  }, {
261
+ quantity: number;
261
262
  productId: string;
262
263
  productTitle: string;
263
- quantity: number;
264
264
  unitPrice: number;
265
265
  totalPrice: number;
266
266
  listingType?: "classified" | "standard" | "auction" | "pre-order" | "prize-draw" | "digital-code" | "live" | undefined;
@@ -326,14 +326,14 @@ export declare const orderFirestoreSchema: z.ZodObject<{
326
326
  code: string;
327
327
  type: "auto" | "coupon" | "deal";
328
328
  discountAmount: number;
329
- scope?: "seller" | "admin" | undefined;
329
+ scope?: "admin" | "seller" | undefined;
330
330
  storeId?: string | undefined;
331
331
  couponId?: string | undefined;
332
332
  }, {
333
333
  code: string;
334
334
  type: "auto" | "coupon" | "deal";
335
335
  discountAmount: number;
336
- scope?: "seller" | "admin" | undefined;
336
+ scope?: "admin" | "seller" | undefined;
337
337
  storeId?: string | undefined;
338
338
  couponId?: string | undefined;
339
339
  }>, "many">>;
@@ -547,11 +547,11 @@ export declare const orderFirestoreSchema: z.ZodObject<{
547
547
  toDate: z.ZodFunction<z.ZodTuple<[], z.ZodUnknown>, z.ZodDate>;
548
548
  }, z.ZodTypeAny, "passthrough">;
549
549
  userId: string;
550
+ quantity: number;
550
551
  productId: string;
551
552
  productTitle: string;
552
553
  userName: string;
553
554
  userEmail: string;
554
- quantity: number;
555
555
  unitPrice: number;
556
556
  totalPrice: number;
557
557
  paymentStatus: "paid" | "failed" | "pending" | "processing" | "refunded" | "partial_refund";
@@ -562,6 +562,7 @@ export declare const orderFirestoreSchema: z.ZodObject<{
562
562
  platformFee?: number | undefined;
563
563
  paymentMethod?: string | undefined;
564
564
  notes?: string | undefined;
565
+ storeName?: string | undefined;
565
566
  prizeRevealMode?: "scheduled" | "instant" | undefined;
566
567
  paymentId?: string | undefined;
567
568
  shippingAddress?: string | undefined;
@@ -587,9 +588,9 @@ export declare const orderFirestoreSchema: z.ZodObject<{
587
588
  shippingCarrier?: string | undefined;
588
589
  trackingUrl?: string | undefined;
589
590
  items?: {
591
+ quantity: number;
590
592
  productId: string;
591
593
  productTitle: string;
592
- quantity: number;
593
594
  unitPrice: number;
594
595
  totalPrice: number;
595
596
  listingType?: "classified" | "standard" | "auction" | "pre-order" | "prize-draw" | "digital-code" | "live" | undefined;
@@ -605,7 +606,6 @@ export declare const orderFirestoreSchema: z.ZodObject<{
605
606
  }, z.ZodTypeAny, "passthrough">;
606
607
  } | undefined;
607
608
  prizeDrawProductId?: string | undefined;
608
- storeName?: string | undefined;
609
609
  couponCode?: string | undefined;
610
610
  physicalLocation?: {
611
611
  zone: string;
@@ -622,7 +622,7 @@ export declare const orderFirestoreSchema: z.ZodObject<{
622
622
  code: string;
623
623
  type: "auto" | "coupon" | "deal";
624
624
  discountAmount: number;
625
- scope?: "seller" | "admin" | undefined;
625
+ scope?: "admin" | "seller" | undefined;
626
626
  storeId?: string | undefined;
627
627
  couponId?: string | undefined;
628
628
  }[] | undefined;
@@ -695,11 +695,11 @@ export declare const orderFirestoreSchema: z.ZodObject<{
695
695
  toDate: z.ZodFunction<z.ZodTuple<[], z.ZodUnknown>, z.ZodDate>;
696
696
  }, z.ZodTypeAny, "passthrough">;
697
697
  userId: string;
698
+ quantity: number;
698
699
  productId: string;
699
700
  productTitle: string;
700
701
  userName: string;
701
702
  userEmail: string;
702
- quantity: number;
703
703
  unitPrice: number;
704
704
  totalPrice: number;
705
705
  paymentStatus: "paid" | "failed" | "pending" | "processing" | "refunded" | "partial_refund";
@@ -710,6 +710,7 @@ export declare const orderFirestoreSchema: z.ZodObject<{
710
710
  platformFee?: number | undefined;
711
711
  paymentMethod?: string | undefined;
712
712
  notes?: string | undefined;
713
+ storeName?: string | undefined;
713
714
  prizeRevealMode?: "scheduled" | "instant" | undefined;
714
715
  paymentId?: string | undefined;
715
716
  shippingAddress?: string | undefined;
@@ -735,9 +736,9 @@ export declare const orderFirestoreSchema: z.ZodObject<{
735
736
  shippingCarrier?: string | undefined;
736
737
  trackingUrl?: string | undefined;
737
738
  items?: {
739
+ quantity: number;
738
740
  productId: string;
739
741
  productTitle: string;
740
- quantity: number;
741
742
  unitPrice: number;
742
743
  totalPrice: number;
743
744
  listingType?: "classified" | "standard" | "auction" | "pre-order" | "prize-draw" | "digital-code" | "live" | undefined;
@@ -753,7 +754,6 @@ export declare const orderFirestoreSchema: z.ZodObject<{
753
754
  }, z.ZodTypeAny, "passthrough">;
754
755
  } | undefined;
755
756
  prizeDrawProductId?: string | undefined;
756
- storeName?: string | undefined;
757
757
  couponCode?: string | undefined;
758
758
  physicalLocation?: {
759
759
  zone: string;
@@ -770,7 +770,7 @@ export declare const orderFirestoreSchema: z.ZodObject<{
770
770
  code: string;
771
771
  type: "auto" | "coupon" | "deal";
772
772
  discountAmount: number;
773
- scope?: "seller" | "admin" | undefined;
773
+ scope?: "admin" | "seller" | undefined;
774
774
  storeId?: string | undefined;
775
775
  couponId?: string | undefined;
776
776
  }[] | undefined;
@@ -847,8 +847,8 @@ export declare const orderItemSchema: z.ZodObject<{
847
847
  }, "strip", z.ZodTypeAny, {
848
848
  title: string;
849
849
  price: number;
850
- productId: string;
851
850
  quantity: number;
851
+ productId: string;
852
852
  currency?: string | undefined;
853
853
  image?: string | undefined;
854
854
  storeId?: string | undefined;
@@ -856,8 +856,8 @@ export declare const orderItemSchema: z.ZodObject<{
856
856
  }, {
857
857
  title: string;
858
858
  price: number;
859
- productId: string;
860
859
  quantity: number;
860
+ productId: string;
861
861
  currency?: string | undefined;
862
862
  image?: string | undefined;
863
863
  storeId?: string | undefined;
@@ -907,8 +907,8 @@ export declare const orderSchema: z.ZodObject<{
907
907
  }, "strip", z.ZodTypeAny, {
908
908
  title: string;
909
909
  price: number;
910
- productId: string;
911
910
  quantity: number;
911
+ productId: string;
912
912
  currency?: string | undefined;
913
913
  image?: string | undefined;
914
914
  storeId?: string | undefined;
@@ -916,8 +916,8 @@ export declare const orderSchema: z.ZodObject<{
916
916
  }, {
917
917
  title: string;
918
918
  price: number;
919
- productId: string;
920
919
  quantity: number;
920
+ productId: string;
921
921
  currency?: string | undefined;
922
922
  image?: string | undefined;
923
923
  storeId?: string | undefined;
@@ -962,18 +962,18 @@ export declare const orderSchema: z.ZodObject<{
962
962
  [k: string]: unknown;
963
963
  };
964
964
  userId: string;
965
+ total: number;
965
966
  paymentStatus: string;
966
967
  items: {
967
968
  title: string;
968
969
  price: number;
969
- productId: string;
970
970
  quantity: number;
971
+ productId: string;
971
972
  currency?: string | undefined;
972
973
  image?: string | undefined;
973
974
  storeId?: string | undefined;
974
975
  attributes?: Record<string, string> | undefined;
975
976
  }[];
976
- total: number;
977
977
  subtotal: number;
978
978
  orderStatus: "pending" | "cancelled" | "processing" | "refunded" | "delivered" | "confirmed" | "shipped" | "return_requested" | "returned";
979
979
  createdAt?: string | undefined;
@@ -999,18 +999,18 @@ export declare const orderSchema: z.ZodObject<{
999
999
  [k: string]: unknown;
1000
1000
  };
1001
1001
  userId: string;
1002
+ total: number;
1002
1003
  paymentStatus: string;
1003
1004
  items: {
1004
1005
  title: string;
1005
1006
  price: number;
1006
- productId: string;
1007
1007
  quantity: number;
1008
+ productId: string;
1008
1009
  currency?: string | undefined;
1009
1010
  image?: string | undefined;
1010
1011
  storeId?: string | undefined;
1011
1012
  attributes?: Record<string, string> | undefined;
1012
1013
  }[];
1013
- total: number;
1014
1014
  subtotal: number;
1015
1015
  orderStatus: "pending" | "cancelled" | "processing" | "refunded" | "delivered" | "confirmed" | "shipped" | "return_requested" | "returned";
1016
1016
  createdAt?: string | undefined;
@@ -2,7 +2,8 @@ import { type PaymentGateway, PaymentGatewayValues } from "../../payments/schema
2
2
  export { type PaymentGateway, PaymentGatewayValues };
3
3
  import type { ListingType } from "../../products/types/index";
4
4
  import type { OrderType } from "../utils/order-splitter";
5
- import type { AppliedOrderDiscount } from "../schemas/firestore";
5
+ import type { AppliedOrderDiscount, OrderSourceContext } from "../schemas/firestore";
6
+ import type { FieldChange } from "../../../_internal/shared/history/index";
6
7
  export type PaymentStatus = "pending" | "processing" | "paid" | "failed" | "refunded" | "partial_refund";
7
8
  export interface UserAddress {
8
9
  id: string;
@@ -46,12 +47,34 @@ export interface OrderItem {
46
47
  /** Snapshot of bundle.bundleProductIds at order-creation time. */
47
48
  bundleProductIds?: string[];
48
49
  }
49
- export interface OrderTimeline {
50
- status: OrderStatus;
51
- message?: string;
52
- timestamp: string;
53
- actor?: string;
50
+ /**
51
+ * One entry on an order's timeline — the client-facing shape of
52
+ * `StatusChangeEntry`, with dates as ISO strings.
53
+ *
54
+ * The previous declaration here was `{status, message?, timestamp, actor?}`
55
+ * and had **zero writers** in the entire codebase since the day it was
56
+ * added, so nothing depended on it. `OrderStatusTimeline` synthesised its
57
+ * steps from four scalar date fields instead, which meant any transition
58
+ * without a dedicated date field — payment reviewed, proof re-upload asked
59
+ * for, a partial refund posted — could not appear at all.
60
+ */
61
+ export interface OrderTimelineEntry {
62
+ at: string;
63
+ actorUid?: string;
64
+ actorRole: "buyer" | "seller" | "admin" | "system";
65
+ /**
66
+ * Delta only: `{ status: { from: "confirmed", to: "shipped" } }`.
67
+ * Reuses the primitive's `FieldChange` rather than restating the shape, so
68
+ * the client type cannot drift from what the repository writes.
69
+ */
70
+ changes: Record<string, FieldChange>;
71
+ reason?: string;
72
+ note?: string;
73
+ /** The function or job that caused it — what the timeline renders by. */
74
+ trigger: string;
54
75
  }
76
+ /** @deprecated Use {@link OrderTimelineEntry}. Kept only to avoid breaking a stale import. */
77
+ export type OrderTimeline = OrderTimelineEntry;
55
78
  export interface Order {
56
79
  id: string;
57
80
  userId: string;
@@ -108,7 +131,12 @@ export interface Order {
108
131
  /** Carrier tracking page URL — rendered as an external link that opens in a new tab. No live-tracking API integration; this is just the passthrough URL a seller/admin entered. */
109
132
  trackingUrl?: string;
110
133
  notes?: string;
111
- timeline?: OrderTimeline[];
134
+ /** Real recorded transitions — see {@link OrderTimelineEntry}. */
135
+ timeline?: OrderTimelineEntry[];
136
+ /** Entries trimmed off the front of the capped history, if any. */
137
+ timelineTruncated?: number;
138
+ /** How the buyer acquired the right to buy. Written once, at creation. */
139
+ sourceContext?: OrderSourceContext;
112
140
  physicalLocation?: {
113
141
  zone: string;
114
142
  shelf: string;
@@ -50,14 +50,13 @@ export declare const preorderItemSchema: z.ZodObject<{
50
50
  }, "strip", z.ZodTypeAny, {
51
51
  id: string;
52
52
  name: string;
53
- active: boolean;
54
53
  slug: string;
54
+ active: boolean;
55
55
  isFeatured: boolean;
56
56
  salePrice: number;
57
57
  regularPrice: number;
58
58
  brand?: string | undefined;
59
59
  createdAt?: string | undefined;
60
- images?: string[] | undefined;
61
60
  media?: {
62
61
  type: "video" | "image" | "file";
63
62
  url: string;
@@ -67,6 +66,7 @@ export declare const preorderItemSchema: z.ZodObject<{
67
66
  youtubeId?: string | undefined;
68
67
  duration?: number | undefined;
69
68
  }[] | undefined;
69
+ images?: string[] | undefined;
70
70
  franchise?: string | undefined;
71
71
  preorderShipDate?: string | undefined;
72
72
  }, {
@@ -78,8 +78,6 @@ export declare const preorderItemSchema: z.ZodObject<{
78
78
  brand?: string | undefined;
79
79
  createdAt?: string | undefined;
80
80
  active?: boolean | undefined;
81
- images?: string[] | undefined;
82
- isFeatured?: boolean | undefined;
83
81
  media?: {
84
82
  type: "video" | "image" | "file";
85
83
  url: string;
@@ -89,6 +87,8 @@ export declare const preorderItemSchema: z.ZodObject<{
89
87
  youtubeId?: string | undefined;
90
88
  duration?: number | undefined;
91
89
  }[] | undefined;
90
+ images?: string[] | undefined;
91
+ isFeatured?: boolean | undefined;
92
92
  franchise?: string | undefined;
93
93
  preorderShipDate?: string | undefined;
94
94
  }>;
@@ -235,7 +235,11 @@ export function GroupSettingsPanel({ productId, productSlug, groupId, isGroupPar
235
235
  setOpen(next);
236
236
  if (next && isGroupParent && children === null)
237
237
  loadChildren();
238
- }, paddingX: "none", paddingY: "xs", justify: "between", className: "w-full text-left group", "aria-expanded": open, children: [_jsx(Heading, { level: 3, size: "sm", weight: "semibold", color: "muted", children: "Group Settings" }), _jsx(Span, { size: "xs", className: "group-hover:text-[var(--appkit-color-text-muted)] dark:group-hover:text-[var(--appkit-color-text-faint)] transition-colors", color: "faint", children: open ? "▲" : "▼" })] }), open && (_jsxs(Stack, { gap: "sm", className: `mt-3 ${__P.p4}`, surface: "muted", rounded: "lg", border: "default", children: [!groupId && !groupParentSlug && (_jsxs(Stack, { gap: "xs", children: [_jsx(Text, { size: "xs", color: "muted", children: "Group related listings together \u2014 e.g. a set, bundle, or multi-part item. Parts can be sold individually but shown together." }), _jsx(Button, { type: "button", variant: "secondary", size: "sm", onClick: startGroup, isLoading: loading, children: "Start a group" })] })), isGroupParent && groupId && (_jsxs(Stack, { gap: "md", children: [_jsxs(FormShellContext.Provider, { value: titleShellCtx, children: [_jsxs(Row, { align: "start", gap: "sm", wrap: true, children: [_jsx(Div, { className: "flex-1 min-w-[200px]", children: _jsx(FormField, { name: "groupTitle", label: "Group title", type: "text", value: editTitle, onChange: setEditTitle, placeholder: "e.g. Human Toy Complete Set" }) }), _jsx(Div, { padding: "t-lg", children: _jsx(Button, { type: "button", variant: "secondary", size: "sm", onClick: saveTitle, isLoading: loading, children: "Save title" }) })] }), _jsx(FormErrorSummary, {})] }), _jsxs(Div, { children: [_jsxs(Text, { className: "mb-2", color: "muted", size: "xs", weight: "semibold", children: ["Members (", childSlugsCount + 1, " \u2014 including this listing)"] }), loading && !children ? (_jsx(Text, { size: "xs", color: "faint", children: "Loading\u2026" })) : (_jsxs(Div, { className: "divide-y divide-zinc-100 divide-[var(--appkit-color-border-subtle)]", children: [_jsxs(Row, { align: "center", gap: "sm", padding: "y-xs", children: [_jsx(Span, { padding: "pill-2xs", weight: "semibold", className: "bg-[var(--appkit-color-primary)]/10 text-[var(--appkit-color-primary)] text-[10px]", rounded: "default", children: "Parent" }), _jsx(Text, { className: "flex-1", color: "primary", size: "sm", children: productSlug })] }), (children ?? []).filter((c) => c.id !== productId).map((child) => (_jsxs(Row, { align: "center", gap: "sm", padding: "y-xs", children: [child.images?.[0] ? (_jsx(Div, { className: "relative w-8 h-8 flex-shrink-0 border border-[var(--appkit-color-border)]", rounded: "full", overflow: "hidden", children: _jsx(MediaImage, { src: child.images[0], alt: child.title, size: "thumbnail" }) })) : (_jsx(Div, { className: "w-8 h-8", surface: "subtle", rounded: "full" })), _jsx(Text, { className: "flex-1 truncate", color: "primary", size: "sm", children: child.title }), _jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => requestUnlinkChild(child.id), isLoading: loading, className: `${CLS_DELETE_LINK} text-[length:var(--appkit-text-xs)]`, children: "Unlink" })] }, child.id)))] }))] }), _jsxs(Row, { gap: "sm", wrap: true, children: [_jsx(Button, { type: "button", variant: "secondary", size: "sm", onClick: () => setShowAddModal(true), children: "Add child listing" }), _jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: requestDissolveGroup, isLoading: loading, className: CLS_DELETE_LINK, children: "Dissolve group" })] })] })), !isGroupParent && groupParentSlug && (_jsxs(Stack, { gap: "xs", children: [_jsxs(Text, { size: "sm", color: "muted", children: ["Part of:", " ", _jsx(Span, { weight: "medium", color: "primary", children: groupTitle ?? groupParentSlug })] }), _jsxs(Text, { size: "xs", color: "muted", children: ["Parent listing: ", _jsx(Code, { className: "font-mono", children: groupParentSlug })] }), _jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: requestLeaveGroup, isLoading: loading, className: `${CLS_DELETE_LINK} w-fit`, children: "Leave group" })] }))] })), (groupChildSlugs?.length ?? 0) >= 4 ? (_jsx(SideDrawer, { isOpen: showAddModal, onClose: () => setShowAddModal(false), title: "Add child listing", children: _jsx(AddChildContent, { addTab: addTab, setAddTab: setAddTab, createForm: createForm, setCreateForm: setCreateForm, productSlug: productSlug, storeProductsEndpoint: storeProductsEndpoint, linkTargets: linkTargets, setLinkTargets: setLinkTargets, onAddCreate: addCreateChild, onAddLink: addLinkChild, loading: loading }) })) : (_jsx(Modal, { open: showAddModal, onClose: () => setShowAddModal(false), title: "Add child listing", size: "lg", children: _jsx(AddChildContent, { addTab: addTab, setAddTab: setAddTab, createForm: createForm, setCreateForm: setCreateForm, productSlug: productSlug, storeProductsEndpoint: storeProductsEndpoint, linkTargets: linkTargets, setLinkTargets: setLinkTargets, onAddCreate: addCreateChild, onAddLink: addLinkChild, loading: loading }) })), confirmAction && (_jsx(ConfirmDeleteModal, { isOpen: true, title: confirmAction.title, message: confirmAction.message, onConfirm: confirmAction.onConfirm, onClose: () => setConfirmAction(null), isDeleting: loading }))] }));
238
+ }, paddingX: "none", paddingY: "xs", justify: "between", className: "w-full text-left group", "aria-expanded": open, children: [_jsx(Heading, { level: 3, size: "sm", weight: "semibold", color: "muted", children: "Group Settings" }), _jsx(Span, { size: "xs", className: "group-hover:text-[var(--appkit-color-text-muted)] dark:group-hover:text-[var(--appkit-color-text-faint)] transition-colors", color: "faint", children: open ? "▲" : "▼" })] }), open && (_jsxs(Stack, { gap: "sm", className: `mt-3 ${__P.p4}`, surface: "muted", rounded: "lg", border: "default", children: [!groupId && !groupParentSlug && (_jsxs(Stack, { gap: "xs", children: [_jsx(Text, { size: "xs", color: "muted", children: "Group related listings together \u2014 e.g. a set, bundle, or multi-part item. Parts can be sold individually but shown together." }), _jsx(Button, { type: "button", variant: "secondary", size: "sm", onClick: startGroup, isLoading: loading, children: "Start a group" })] })), isGroupParent && groupId && (_jsxs(Stack, { gap: "md", children: [_jsxs(FormShellContext.Provider, { value: titleShellCtx, children: [_jsxs(Row, { align: "start", gap: "sm", wrap: true, children: [_jsx(Div, { className: "flex-1 min-w-[200px]", children: _jsx(FormField, { name: "groupTitle", label: "Group title", type: "text", value: editTitle, onChange: setEditTitle, placeholder: "e.g. Human Toy Complete Set" }) }), _jsx(Div, { padding: "t-lg", children: _jsx(Button, { type: "button", variant: "secondary", size: "sm", onClick: saveTitle, isLoading: loading, children: "Save title" }) })] }), _jsx(FormErrorSummary, {})] }), _jsxs(Div, { children: [_jsxs(Text, { className: "mb-2", color: "muted", size: "xs", weight: "semibold", children: ["Members (", childSlugsCount + 1, " \u2014 including this listing)"] }), loading && !children ? (_jsx(Text, { size: "xs", color: "faint", children: "Loading\u2026" })) : (_jsxs(Div, { className: "divide-y divide-zinc-100 divide-[var(--appkit-color-border-subtle)]", children: [_jsxs(Row, { align: "center", gap: "sm", padding: "y-xs", children: [_jsx(Span, { padding: "pill-2xs", weight: "semibold", className: "bg-[var(--appkit-color-primary)]/10 text-[var(--appkit-color-primary)] text-[10px]", rounded: "default", children: "Parent" }), _jsx(Text, { className: "flex-1", color: "primary", size: "sm", children: productSlug })] }), (children ?? []).filter((c) => c.id !== productId).map((child) => (_jsxs(Row, { align: "center", gap: "sm", padding: "y-xs", children: [child.images?.[0] ? (_jsx(Div, { className: "relative w-8 h-8 flex-shrink-0 border border-[var(--appkit-color-border)]", rounded: "full", overflow: "hidden", children: _jsx(MediaImage, { src: child.images[0], alt: child.title, size: "thumbnail" }) })) : (_jsx(Div, { className: "w-8 h-8", surface: "subtle", rounded: "full" })), _jsx(Text, { className: "flex-1 truncate", color: "primary", size: "sm", children: child.title }), _jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => requestUnlinkChild(child.id), isLoading: loading, className: `${CLS_DELETE_LINK} text-[length:var(--appkit-text-xs)]`, children: "Unlink" })] }, child.id)))] }))] }), _jsxs(Row, { gap: "sm", wrap: true, children: [_jsx(Button, { type: "button", variant: "secondary", size: "sm", onClick: () => setShowAddModal(true), children: "Add child listing" }), _jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: requestDissolveGroup, isLoading: loading, className: CLS_DELETE_LINK, children: "Dissolve group" })] })] })), !isGroupParent && groupParentSlug && (_jsxs(Stack, { gap: "xs", children: [_jsxs(Text, { size: "sm", color: "muted", children: ["Part of:", " ", _jsx(Span, { weight: "medium", color: "primary", children: groupTitle ?? groupParentSlug })] }), _jsxs(Text, { size: "xs", color: "muted", children: ["Parent listing: ", _jsx(Code, { className: "font-mono", children: groupParentSlug })] }), _jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: requestLeaveGroup, isLoading: loading, className: `${CLS_DELETE_LINK} w-fit`, children: "Leave group" })] }))] })), (() => {
239
+ const content = (_jsx(AddChildContent, { addTab: addTab, setAddTab: setAddTab, createForm: createForm, setCreateForm: setCreateForm, productSlug: productSlug, storeProductsEndpoint: storeProductsEndpoint, linkTargets: linkTargets, setLinkTargets: setLinkTargets, onAddCreate: addCreateChild, onAddLink: addLinkChild, loading: loading }));
240
+ const close = () => setShowAddModal(false);
241
+ return (groupChildSlugs?.length ?? 0) >= 4 ? (_jsx(SideDrawer, { isOpen: showAddModal, onClose: close, title: "Add child listing", children: content })) : (_jsx(Modal, { open: showAddModal, onClose: close, title: "Add child listing", size: "lg", children: content }));
242
+ })(), confirmAction && (_jsx(ConfirmDeleteModal, { isOpen: true, title: confirmAction.title, message: confirmAction.message, onConfirm: confirmAction.onConfirm, onClose: () => setConfirmAction(null), isDeleting: loading }))] }));
239
243
  }
240
244
  function AddChildContent({ addTab, setAddTab, createForm, setCreateForm, productSlug, storeProductsEndpoint, linkTargets, setLinkTargets, onAddCreate, onAddLink, loading, }) {
241
245
  const { shellCtx, validate } = useFormShellState(createChildSchema);