@mohasinac/appkit 4.14.0 → 4.15.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 (157) 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.js +3 -1
  13. package/dist/_internal/shared/features/auctions/schema.d.ts +14 -2
  14. package/dist/_internal/shared/features/auctions/schema.js +13 -1
  15. package/dist/_internal/shared/features/cart/schema.d.ts +14 -9
  16. package/dist/_internal/shared/features/cart/schema.js +15 -0
  17. package/dist/_internal/shared/features/products/schema.d.ts +2 -2
  18. package/dist/_internal/shared/history/history.d.ts +22 -0
  19. package/dist/_internal/shared/history/history.js +41 -1
  20. package/dist/client.d.ts +13 -2
  21. package/dist/client.js +20 -2
  22. package/dist/constants/field-names.d.ts +8 -6
  23. package/dist/constants/field-names.js +48 -6
  24. package/dist/features/account/components/UserBidsView.js +12 -4
  25. package/dist/features/account/components/UserReturnsView.d.ts +3 -0
  26. package/dist/features/account/components/UserReturnsView.js +17 -1
  27. package/dist/features/admin/components/AdminAuditLogView.js +1 -0
  28. package/dist/features/admin/components/AdminBidsView.js +15 -2
  29. package/dist/features/admin/components/AdminBlogEditorView.js +65 -33
  30. package/dist/features/admin/components/AdminFeatureEditorView.js +35 -6
  31. package/dist/features/admin/components/AdminNavEditorView.js +15 -5
  32. package/dist/features/admin/components/AdminNewsletterView.js +18 -2
  33. package/dist/features/admin/components/AdminOffersView.js +62 -18
  34. package/dist/features/admin/components/AdminPaymentMethodsView.js +24 -4
  35. package/dist/features/admin/components/AdminScammerEditorView.d.ts +2 -1
  36. package/dist/features/admin/components/AdminScammerEditorView.js +3 -13
  37. package/dist/features/admin/components/AdminScammersView.js +3 -8
  38. package/dist/features/admin/components/AdminSectionsView.js +32 -23
  39. package/dist/features/admin/components/AdminSessionsView.js +17 -2
  40. package/dist/features/admin/components/ViewAuditLogEntryModal.js +1 -0
  41. package/dist/features/admin/components/sections/adminSectionsTypes.d.ts +4 -2
  42. package/dist/features/admin/components/sections/adminSectionsTypes.js +45 -27
  43. package/dist/features/admin/constants/filter-tabs.d.ts +3 -0
  44. package/dist/features/admin/constants/filter-tabs.js +1 -0
  45. package/dist/features/admin/schemas/homepage-section-form.d.ts +22 -0
  46. package/dist/features/admin/schemas/homepage-section-form.js +68 -0
  47. package/dist/features/admin/schemas/nav-item-form.d.ts +74 -0
  48. package/dist/features/admin/schemas/nav-item-form.js +72 -0
  49. package/dist/features/auctions/components/AuctionBidsTable.d.ts +10 -1
  50. package/dist/features/auctions/components/AuctionBidsTable.js +22 -4
  51. package/dist/features/auctions/components/bid-detail-fields.d.ts +29 -0
  52. package/dist/features/auctions/components/bid-detail-fields.js +103 -0
  53. package/dist/features/audit-log/schemas/firestore.d.ts +14 -0
  54. package/dist/features/audit-log/schemas/firestore.js +7 -0
  55. package/dist/features/blog/schemas/blog-form.d.ts +80 -0
  56. package/dist/features/blog/schemas/blog-form.js +100 -0
  57. package/dist/features/events/components/AdminEventEditorView.js +32 -54
  58. package/dist/features/events/components/AdminEventEntriesView.js +22 -3
  59. package/dist/features/events/schemas/event-form.d.ts +113 -0
  60. package/dist/features/events/schemas/event-form.js +82 -0
  61. package/dist/features/grouped/schemas/grouped-listing-form.d.ts +101 -0
  62. package/dist/features/grouped/schemas/grouped-listing-form.js +89 -0
  63. package/dist/features/homepage/actions/homepage-section-actions.d.ts +11 -3
  64. package/dist/features/homepage/actions/homepage-section-actions.js +33 -1
  65. package/dist/features/homepage/schemas/index.d.ts +31 -0
  66. package/dist/features/homepage/schemas/index.js +24 -0
  67. package/dist/features/orders/components/OrderStatusTimeline.d.ts +5 -1
  68. package/dist/features/orders/components/OrderStatusTimeline.js +39 -20
  69. package/dist/features/orders/repository/orders.repository.js +5 -0
  70. package/dist/features/orders/schemas/firestore.d.ts +36 -1
  71. package/dist/features/orders/types/index.d.ts +35 -7
  72. package/dist/features/products/components/GroupSettingsPanel.js +5 -1
  73. package/dist/features/products/schemas/index.d.ts +2 -2
  74. package/dist/features/products/schemas/product-features.validators.d.ts +73 -0
  75. package/dist/features/products/schemas/product-features.validators.js +63 -0
  76. package/dist/features/scams/constants/scammer-status-display.d.ts +28 -0
  77. package/dist/features/scams/constants/scammer-status-display.js +79 -0
  78. package/dist/features/seller/actions/offer-actions.js +32 -2
  79. package/dist/features/seller/components/OfferPhaseTimeline.d.ts +59 -0
  80. package/dist/features/seller/components/OfferPhaseTimeline.js +107 -0
  81. package/dist/features/seller/components/SellerBidsView.js +11 -4
  82. package/dist/features/seller/components/SellerCouponEditorView.js +17 -17
  83. package/dist/features/seller/components/SellerOffersView.js +2 -1
  84. package/dist/features/seller/components/SellerPayoutSettingsView.js +38 -14
  85. package/dist/features/seller/repository/offer.repository.d.ts +69 -8
  86. package/dist/features/seller/repository/offer.repository.js +127 -23
  87. package/dist/features/seller/schemas/coupon-form.d.ts +72 -0
  88. package/dist/features/seller/schemas/coupon-form.js +116 -0
  89. package/dist/features/seller/schemas/firestore.d.ts +56 -2
  90. package/dist/features/seller/schemas/firestore.js +27 -0
  91. package/dist/features/seller/schemas/offer-forms.d.ts +21 -0
  92. package/dist/features/seller/schemas/offer-forms.js +21 -0
  93. package/dist/features/shell/build-sections.d.ts +63 -0
  94. package/dist/features/shell/build-sections.js +207 -0
  95. package/dist/features/shell/field-ui-meta.d.ts +115 -0
  96. package/dist/features/shell/field-ui-meta.js +324 -0
  97. package/dist/features/shell/index.d.ts +4 -0
  98. package/dist/features/shell/index.js +2 -0
  99. package/dist/features/status-history/components/StatusTimeline.d.ts +74 -0
  100. package/dist/features/status-history/components/StatusTimeline.js +46 -0
  101. package/dist/features/status-history/index.d.ts +11 -0
  102. package/dist/features/status-history/index.js +10 -0
  103. package/dist/features/store-extensions/components/ReviewDecisionModal.d.ts +27 -0
  104. package/dist/features/store-extensions/components/ReviewDecisionModal.js +82 -0
  105. package/dist/features/store-extensions/index.d.ts +12 -0
  106. package/dist/features/store-extensions/index.js +12 -0
  107. package/dist/features/store-extensions/schemas/admin-notification-form.d.ts +36 -0
  108. package/dist/features/store-extensions/schemas/admin-notification-form.js +114 -0
  109. package/dist/features/store-extensions/schemas/analytics-forms.d.ts +57 -0
  110. package/dist/features/store-extensions/schemas/analytics-forms.js +158 -0
  111. package/dist/features/store-extensions/schemas/custom-role-form.d.ts +85 -0
  112. package/dist/features/store-extensions/schemas/custom-role-form.js +118 -0
  113. package/dist/features/store-extensions/schemas/firestore.d.ts +21 -1
  114. package/dist/features/store-extensions/schemas/firestore.js +60 -0
  115. package/dist/features/store-extensions/schemas/google-config-form.d.ts +53 -0
  116. package/dist/features/store-extensions/schemas/google-config-form.js +105 -0
  117. package/dist/features/store-extensions/schemas/item-request-create-form.d.ts +28 -0
  118. package/dist/features/store-extensions/schemas/item-request-create-form.js +80 -0
  119. package/dist/features/store-extensions/schemas/listing-template-form.d.ts +78 -0
  120. package/dist/features/store-extensions/schemas/listing-template-form.js +87 -0
  121. package/dist/features/store-extensions/schemas/moderation-review-form.d.ts +49 -0
  122. package/dist/features/store-extensions/schemas/moderation-review-form.js +101 -0
  123. package/dist/features/store-extensions/schemas/payout-method-form.d.ts +153 -0
  124. package/dist/features/store-extensions/schemas/payout-method-form.js +155 -0
  125. package/dist/features/store-extensions/schemas/report-create-form.d.ts +25 -0
  126. package/dist/features/store-extensions/schemas/report-create-form.js +79 -0
  127. package/dist/features/store-extensions/schemas/report-review-form.d.ts +47 -0
  128. package/dist/features/store-extensions/schemas/report-review-form.js +111 -0
  129. package/dist/features/store-extensions/schemas/shipping-config-form.d.ts +148 -0
  130. package/dist/features/store-extensions/schemas/shipping-config-form.js +104 -0
  131. package/dist/features/store-extensions/schemas/store-category-form.d.ts +54 -0
  132. package/dist/features/store-extensions/schemas/store-category-form.js +73 -0
  133. package/dist/features/tester/seed-data/tester-checklist-seed-data.js +326 -0
  134. package/dist/index.d.ts +12 -2
  135. package/dist/index.js +12 -2
  136. package/dist/next/routing/route-map.d.ts +16 -2
  137. package/dist/next/routing/route-map.js +9 -2
  138. package/dist/schemas/firestore-value.d.ts +8 -0
  139. package/dist/schemas/firestore-value.js +44 -0
  140. package/dist/schemas/index.d.ts +1 -1
  141. package/dist/schemas/index.js +1 -1
  142. package/dist/schemas/registry.d.ts +634 -1
  143. package/dist/schemas/registry.js +64 -1
  144. package/dist/seed/bids-seed-data.js +30 -0
  145. package/dist/seed/offers-seed-data.js +212 -7
  146. package/dist/seed/orders-seed-data.js +336 -1
  147. package/dist/seed/products-auctions-seed-data.js +41 -0
  148. package/dist/server.d.ts +2 -0
  149. package/dist/server.js +5 -0
  150. package/dist/styles.css +1 -1
  151. package/dist/tailwind-utilities.css +1 -1
  152. package/dist/ui/components/RecordDetailModal.d.ts +10 -1
  153. package/dist/ui/components/RecordDetailModal.js +2 -2
  154. package/package.json +2 -1
  155. package/scripts/node-esm-loader.mjs +65 -0
  156. package/scripts/seed-cli-loader.mjs +6 -49
  157. package/scripts/seed-cli.mjs +1 -1
@@ -0,0 +1,113 @@
1
+ import { z } from "zod";
2
+ /**
3
+ * ## `.passthrough()` STAYS here, deliberately
4
+ *
5
+ * `EventDraft` carries ~40 fields; this schema names seven. The blog form's
6
+ * equivalent was safe to tighten because its draft had fourteen fields and all
7
+ * fourteen could be named in one sitting. This one cannot: most of its fields
8
+ * are per-event-type (poll options, survey fields, spin prizes, raffle config)
9
+ * and only apply under a `type` discriminator, so naming them correctly means
10
+ * modelling the discriminated union rather than listing keys.
11
+ *
12
+ * Tightening it blind is the single most dangerous edit available in this
13
+ * codebase — `z.object()` strips unknown keys, so every field this schema
14
+ * failed to name would vanish from the draft the moment passthrough came off.
15
+ * That is exactly how `productBaseSchema` came to eat every `classified*` /
16
+ * `digitalCode*` / `liveItem*` / `prize*` field.
17
+ *
18
+ * The wire payload is NOT at risk either way: it is built explicitly below
19
+ * (`type`, `title`, `slug`, … plus `buildEventTypeConfig` /
20
+ * `buildEventRaffleFields`), never by spreading the draft.
21
+ *
22
+ * Prerequisite for removing it: a FULL-collection `roundtrip-diff --entity
23
+ * event --full`, not a sample — per the data-integrity protocol.
24
+ *
25
+ * 🛑 `annotate()` must be the OUTERMOST call on each field — it keys a WeakMap
26
+ * by schema instance and every zod wrapper returns a new one.
27
+ */
28
+ export declare const eventDraftSchema: z.ZodEffects<z.ZodObject<{
29
+ type: z.ZodString;
30
+ title: z.ZodString;
31
+ startsAt: z.ZodString;
32
+ endsAt: z.ZodString;
33
+ couponId: z.ZodString;
34
+ displayCode: z.ZodString;
35
+ pollOptions: z.ZodArray<z.ZodObject<{
36
+ id: z.ZodString;
37
+ label: z.ZodString;
38
+ }, "strip", z.ZodTypeAny, {
39
+ id: string;
40
+ label: string;
41
+ }, {
42
+ id: string;
43
+ label: string;
44
+ }>, "many">;
45
+ }, "passthrough", z.ZodTypeAny, z.objectOutputType<{
46
+ type: z.ZodString;
47
+ title: z.ZodString;
48
+ startsAt: z.ZodString;
49
+ endsAt: z.ZodString;
50
+ couponId: z.ZodString;
51
+ displayCode: z.ZodString;
52
+ pollOptions: z.ZodArray<z.ZodObject<{
53
+ id: z.ZodString;
54
+ label: z.ZodString;
55
+ }, "strip", z.ZodTypeAny, {
56
+ id: string;
57
+ label: string;
58
+ }, {
59
+ id: string;
60
+ label: string;
61
+ }>, "many">;
62
+ }, z.ZodTypeAny, "passthrough">, z.objectInputType<{
63
+ type: z.ZodString;
64
+ title: z.ZodString;
65
+ startsAt: z.ZodString;
66
+ endsAt: z.ZodString;
67
+ couponId: z.ZodString;
68
+ displayCode: z.ZodString;
69
+ pollOptions: z.ZodArray<z.ZodObject<{
70
+ id: z.ZodString;
71
+ label: z.ZodString;
72
+ }, "strip", z.ZodTypeAny, {
73
+ id: string;
74
+ label: string;
75
+ }, {
76
+ id: string;
77
+ label: string;
78
+ }>, "many">;
79
+ }, z.ZodTypeAny, "passthrough">>, z.objectOutputType<{
80
+ type: z.ZodString;
81
+ title: z.ZodString;
82
+ startsAt: z.ZodString;
83
+ endsAt: z.ZodString;
84
+ couponId: z.ZodString;
85
+ displayCode: z.ZodString;
86
+ pollOptions: z.ZodArray<z.ZodObject<{
87
+ id: z.ZodString;
88
+ label: z.ZodString;
89
+ }, "strip", z.ZodTypeAny, {
90
+ id: string;
91
+ label: string;
92
+ }, {
93
+ id: string;
94
+ label: string;
95
+ }>, "many">;
96
+ }, z.ZodTypeAny, "passthrough">, z.objectInputType<{
97
+ type: z.ZodString;
98
+ title: z.ZodString;
99
+ startsAt: z.ZodString;
100
+ endsAt: z.ZodString;
101
+ couponId: z.ZodString;
102
+ displayCode: z.ZodString;
103
+ pollOptions: z.ZodArray<z.ZodObject<{
104
+ id: z.ZodString;
105
+ label: z.ZodString;
106
+ }, "strip", z.ZodTypeAny, {
107
+ id: string;
108
+ label: string;
109
+ }, {
110
+ id: string;
111
+ label: string;
112
+ }>, "many">;
113
+ }, z.ZodTypeAny, "passthrough">>;
@@ -0,0 +1,82 @@
1
+ /*
2
+ * WHY: Same reason as the blog form schema — keeping it in the `.tsx` view
3
+ * would have dragged a React component tree into every API route through
4
+ * `SCHEMAS` -> `routeHandler`.
5
+ * WHAT: The event editor's form schema, with its UI metadata.
6
+ *
7
+ * EXPORTS:
8
+ * eventDraftSchema
9
+ *
10
+ * @tag domain:events
11
+ * @tag layer:schema
12
+ * @tag pattern:none
13
+ * @tag access:isomorphic
14
+ * @tag consumers:AdminEventEditorView,SCHEMAS.forms
15
+ * @tag sideEffects:none
16
+ */
17
+ import { z } from "zod";
18
+ import { annotate } from "../../shell/field-ui-meta";
19
+ /**
20
+ * ## `.passthrough()` STAYS here, deliberately
21
+ *
22
+ * `EventDraft` carries ~40 fields; this schema names seven. The blog form's
23
+ * equivalent was safe to tighten because its draft had fourteen fields and all
24
+ * fourteen could be named in one sitting. This one cannot: most of its fields
25
+ * are per-event-type (poll options, survey fields, spin prizes, raffle config)
26
+ * and only apply under a `type` discriminator, so naming them correctly means
27
+ * modelling the discriminated union rather than listing keys.
28
+ *
29
+ * Tightening it blind is the single most dangerous edit available in this
30
+ * codebase — `z.object()` strips unknown keys, so every field this schema
31
+ * failed to name would vanish from the draft the moment passthrough came off.
32
+ * That is exactly how `productBaseSchema` came to eat every `classified*` /
33
+ * `digitalCode*` / `liveItem*` / `prize*` field.
34
+ *
35
+ * The wire payload is NOT at risk either way: it is built explicitly below
36
+ * (`type`, `title`, `slug`, … plus `buildEventTypeConfig` /
37
+ * `buildEventRaffleFields`), never by spreading the draft.
38
+ *
39
+ * Prerequisite for removing it: a FULL-collection `roundtrip-diff --entity
40
+ * event --full`, not a sample — per the data-integrity protocol.
41
+ *
42
+ * 🛑 `annotate()` must be the OUTERMOST call on each field — it keys a WeakMap
43
+ * by schema instance and every zod wrapper returns a new one.
44
+ */
45
+ export const eventDraftSchema = z.object({
46
+ type: annotate(z.string(), {
47
+ section: "details", sectionLabel: "Details", sectionRequired: true,
48
+ quick: true, order: 1, row: "pair", kind: "select",
49
+ }),
50
+ title: annotate(z.string().min(1, "Title is required"), {
51
+ section: "details", quick: true, order: 2, row: "full",
52
+ }),
53
+ startsAt: annotate(z.string().min(1, "Start date is required"), {
54
+ section: "details", quick: true, order: 3, row: "pair", kind: "date",
55
+ }),
56
+ endsAt: annotate(z.string().min(1, "End date is required"), {
57
+ section: "details", quick: true, order: 4, row: "pair", kind: "date",
58
+ }),
59
+ couponId: annotate(z.string(), {
60
+ section: "settings", sectionLabel: "Settings", order: 1, row: "pair",
61
+ help: "Required for offer events.",
62
+ }),
63
+ displayCode: annotate(z.string(), {
64
+ section: "settings", order: 2, row: "pair",
65
+ help: "Required for offer events.",
66
+ }),
67
+ pollOptions: annotate(z.array(z.object({ id: z.string(), label: z.string() })), {
68
+ section: "settings", order: 3, row: "full", kind: "list",
69
+ }),
70
+ }).passthrough().superRefine((v, ctx) => {
71
+ if (v.type === "offer") {
72
+ if (!v.couponId.trim()) {
73
+ ctx.addIssue({ code: z.ZodIssueCode.custom, path: ["couponId"], message: "Coupon is required for offer events" });
74
+ }
75
+ if (!v.displayCode.trim()) {
76
+ ctx.addIssue({ code: z.ZodIssueCode.custom, path: ["displayCode"], message: "Display code is required for offer events" });
77
+ }
78
+ }
79
+ if (v.type === "poll" && v.pollOptions.filter((o) => o.label.trim()).length < 2) {
80
+ ctx.addIssue({ code: z.ZodIssueCode.custom, path: ["pollOptions"], message: "Poll events require at least 2 options" });
81
+ }
82
+ });
@@ -0,0 +1,101 @@
1
+ import { z } from "zod";
2
+ /**
3
+ * 🛑 `annotate()` must be the OUTERMOST call on each field — it keys a WeakMap
4
+ * by schema instance and every zod wrapper returns a new one.
5
+ */
6
+ export declare const groupedListingFormSchema: z.ZodObject<{
7
+ title: z.ZodString;
8
+ description: z.ZodOptional<z.ZodString>;
9
+ groupTheme: z.ZodEnum<["related", "character", "lineage", "set", "generic"]>;
10
+ productIds: z.ZodArray<z.ZodString, "many">;
11
+ minActiveMembers: z.ZodNumber;
12
+ coverImage: z.ZodOptional<z.ZodString>;
13
+ isActive: z.ZodBoolean;
14
+ isFeatured: z.ZodBoolean;
15
+ }, "strip", z.ZodTypeAny, {
16
+ title: string;
17
+ isActive: boolean;
18
+ isFeatured: boolean;
19
+ productIds: string[];
20
+ groupTheme: "set" | "related" | "character" | "lineage" | "generic";
21
+ minActiveMembers: number;
22
+ description?: string | undefined;
23
+ coverImage?: string | undefined;
24
+ }, {
25
+ title: string;
26
+ isActive: boolean;
27
+ isFeatured: boolean;
28
+ productIds: string[];
29
+ groupTheme: "set" | "related" | "character" | "lineage" | "generic";
30
+ minActiveMembers: number;
31
+ description?: string | undefined;
32
+ coverImage?: string | undefined;
33
+ }>;
34
+ export type GroupedListingFormValues = z.infer<typeof groupedListingFormSchema>;
35
+ /**
36
+ * Create contract. `storeId` is absent — the store route sets it from the
37
+ * session, so a seller cannot file a group under another store.
38
+ */
39
+ export declare const groupedListingCreateSchema: z.ZodObject<{
40
+ title: z.ZodString;
41
+ description: z.ZodOptional<z.ZodOptional<z.ZodString>>;
42
+ isActive: z.ZodBoolean;
43
+ isFeatured: z.ZodOptional<z.ZodBoolean>;
44
+ coverImage: z.ZodOptional<z.ZodOptional<z.ZodString>>;
45
+ productIds: z.ZodArray<z.ZodString, "many">;
46
+ groupTheme: z.ZodEnum<["related", "character", "lineage", "set", "generic"]>;
47
+ minActiveMembers: z.ZodOptional<z.ZodNumber>;
48
+ }, "strip", z.ZodTypeAny, {
49
+ title: string;
50
+ isActive: boolean;
51
+ productIds: string[];
52
+ groupTheme: "set" | "related" | "character" | "lineage" | "generic";
53
+ description?: string | undefined;
54
+ isFeatured?: boolean | undefined;
55
+ coverImage?: string | undefined;
56
+ minActiveMembers?: number | undefined;
57
+ }, {
58
+ title: string;
59
+ isActive: boolean;
60
+ productIds: string[];
61
+ groupTheme: "set" | "related" | "character" | "lineage" | "generic";
62
+ description?: string | undefined;
63
+ isFeatured?: boolean | undefined;
64
+ coverImage?: string | undefined;
65
+ minActiveMembers?: number | undefined;
66
+ }>;
67
+ /** Update contract — partial, `.strict()` so an unknown key is a 400. */
68
+ export declare const groupedListingUpdateSchema: z.ZodObject<{
69
+ title: z.ZodOptional<z.ZodString>;
70
+ description: z.ZodOptional<z.ZodString>;
71
+ groupTheme: z.ZodOptional<z.ZodEnum<["related", "character", "lineage", "set", "generic"]>>;
72
+ productIds: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
73
+ minActiveMembers: z.ZodOptional<z.ZodNumber>;
74
+ coverImage: z.ZodOptional<z.ZodString>;
75
+ isActive: z.ZodOptional<z.ZodBoolean>;
76
+ isFeatured: z.ZodOptional<z.ZodBoolean>;
77
+ brandSlug: z.ZodOptional<z.ZodString>;
78
+ categorySlug: z.ZodOptional<z.ZodString>;
79
+ }, "strict", z.ZodTypeAny, {
80
+ title?: string | undefined;
81
+ description?: string | undefined;
82
+ categorySlug?: string | undefined;
83
+ brandSlug?: string | undefined;
84
+ isActive?: boolean | undefined;
85
+ isFeatured?: boolean | undefined;
86
+ coverImage?: string | undefined;
87
+ productIds?: string[] | undefined;
88
+ groupTheme?: "set" | "related" | "character" | "lineage" | "generic" | undefined;
89
+ minActiveMembers?: number | undefined;
90
+ }, {
91
+ title?: string | undefined;
92
+ description?: string | undefined;
93
+ categorySlug?: string | undefined;
94
+ brandSlug?: string | undefined;
95
+ isActive?: boolean | undefined;
96
+ isFeatured?: boolean | undefined;
97
+ coverImage?: string | undefined;
98
+ productIds?: string[] | undefined;
99
+ groupTheme?: "set" | "related" | "character" | "lineage" | "generic" | undefined;
100
+ minActiveMembers?: number | undefined;
101
+ }>;
@@ -0,0 +1,89 @@
1
+ /*
2
+ * WHY: `POST /api/store/grouped-listings` spread the raw request body into
3
+ * `.create()` and its PATCH built `{...body}` — no Zod on either. The two
4
+ * pages behind them validated one field between them (`new` checked
5
+ * `title` was non-empty and toasted; `[id]/edit` checked nothing at all).
6
+ * WHAT: One schema for the pages AND the store routes.
7
+ *
8
+ * ## `activeMemberCount` is NOT in this schema
9
+ *
10
+ * It is derived — both routes already recompute it from `productIds.length`,
11
+ * which is correct and was verified rather than assumed. Accepting it from the
12
+ * body would let a caller set a count that disagrees with the array, and the
13
+ * count is what the public visibility check reads.
14
+ *
15
+ * `slug`, `visibilityStatus` and `createdBy` are absent for the same reason:
16
+ * derived or session-owned, never typed.
17
+ *
18
+ * EXPORTS:
19
+ * groupedListingFormSchema, groupedListingCreateSchema,
20
+ * groupedListingUpdateSchema, type GroupedListingFormValues
21
+ *
22
+ * @tag domain:grouped
23
+ * @tag layer:schema
24
+ * @tag pattern:none
25
+ * @tag access:isomorphic
26
+ * @tag consumers:store/grouped-listings pages,/api/store/grouped-listings
27
+ * @tag sideEffects:none
28
+ */
29
+ import { z } from "zod";
30
+ import { annotate } from "../../shell/field-ui-meta";
31
+ /** Mirrors `GroupTheme` in ./firestore. */
32
+ const groupThemeSchema = z.enum(["related", "character", "lineage", "set", "generic"]);
33
+ /**
34
+ * 🛑 `annotate()` must be the OUTERMOST call on each field — it keys a WeakMap
35
+ * by schema instance and every zod wrapper returns a new one.
36
+ */
37
+ export const groupedListingFormSchema = z.object({
38
+ title: annotate(z.string().min(1, "Title is required").max(120, "Keep the title under 120 characters."), {
39
+ section: "basics", sectionLabel: "Group", sectionRequired: true,
40
+ quick: true, order: 1, row: "full",
41
+ }),
42
+ description: annotate(z.string().max(500, "Keep the description under 500 characters.").optional(), {
43
+ section: "basics", order: 2, row: "full",
44
+ }),
45
+ groupTheme: annotate(groupThemeSchema, {
46
+ section: "basics", quick: true, order: 3, row: "pair",
47
+ }),
48
+ productIds: annotate(z.array(z.string()), {
49
+ section: "members", sectionLabel: "Members", order: 1, row: "full", kind: "list",
50
+ }),
51
+ minActiveMembers: annotate(z.coerce.number().int("Must be a whole number.").min(1, "A group needs at least one member to show."), {
52
+ section: "members", order: 2, row: "pair",
53
+ help: "Below this many in-stock members, the group hides itself.",
54
+ }),
55
+ coverImage: annotate(z.string().optional(), {
56
+ section: "media", sectionLabel: "Media", order: 1, row: "full", kind: "media",
57
+ }),
58
+ isActive: annotate(z.boolean(), {
59
+ section: "visibility", sectionLabel: "Visibility", order: 1, row: "quarter",
60
+ }),
61
+ isFeatured: annotate(z.boolean(), {
62
+ section: "visibility", order: 2, row: "quarter",
63
+ }),
64
+ });
65
+ /**
66
+ * Create contract. `storeId` is absent — the store route sets it from the
67
+ * session, so a seller cannot file a group under another store.
68
+ */
69
+ export const groupedListingCreateSchema = groupedListingFormSchema.partial({
70
+ description: true,
71
+ coverImage: true,
72
+ isFeatured: true,
73
+ minActiveMembers: true,
74
+ });
75
+ /** Update contract — partial, `.strict()` so an unknown key is a 400. */
76
+ export const groupedListingUpdateSchema = z
77
+ .object({
78
+ title: z.string().min(1).max(120).optional(),
79
+ description: z.string().max(500).optional(),
80
+ groupTheme: groupThemeSchema.optional(),
81
+ productIds: z.array(z.string()).optional(),
82
+ minActiveMembers: z.coerce.number().int().min(1).optional(),
83
+ coverImage: z.string().optional(),
84
+ isActive: z.boolean().optional(),
85
+ isFeatured: z.boolean().optional(),
86
+ brandSlug: z.string().optional(),
87
+ categorySlug: z.string().optional(),
88
+ })
89
+ .strict();
@@ -2,21 +2,29 @@ import { z } from "zod";
2
2
  import type { HomepageSectionDocument } from "../schemas";
3
3
  import type { FirebaseSieveResult } from "../../../providers/db-firebase";
4
4
  export declare const createSectionSchema: z.ZodObject<{
5
- type: z.ZodString;
5
+ type: z.ZodEnum<["welcome", "carousel", "stats", "trust-indicators", "categories", "brands", "products", "pre-orders", "auctions", "banner", "features", "reviews", "whatsapp-community", "faq", "blog-articles", "newsletter", "stores", "events", "social-feed", "custom-cards", "google-reviews", "featured-bundles", "prize-draws", "event-raffles", "collection-cards"]>;
6
6
  enabled: z.ZodDefault<z.ZodBoolean>;
7
7
  order: z.ZodDefault<z.ZodNumber>;
8
8
  config: z.ZodOptional<z.ZodObject<{}, "passthrough", z.ZodTypeAny, z.objectOutputType<{}, z.ZodTypeAny, "passthrough">, z.objectInputType<{}, z.ZodTypeAny, "passthrough">>>;
9
9
  }, "strip", z.ZodTypeAny, {
10
- type: string;
10
+ type: "banner" | "stores" | "products" | "pre-orders" | "prize-draws" | "auctions" | "reviews" | "events" | "categories" | "brands" | "carousel" | "newsletter" | "features" | "stats" | "welcome" | "trust-indicators" | "whatsapp-community" | "faq" | "blog-articles" | "social-feed" | "custom-cards" | "google-reviews" | "featured-bundles" | "event-raffles" | "collection-cards";
11
11
  enabled: boolean;
12
12
  order: number;
13
13
  config?: z.objectOutputType<{}, z.ZodTypeAny, "passthrough"> | undefined;
14
14
  }, {
15
- type: string;
15
+ type: "banner" | "stores" | "products" | "pre-orders" | "prize-draws" | "auctions" | "reviews" | "events" | "categories" | "brands" | "carousel" | "newsletter" | "features" | "stats" | "welcome" | "trust-indicators" | "whatsapp-community" | "faq" | "blog-articles" | "social-feed" | "custom-cards" | "google-reviews" | "featured-bundles" | "event-raffles" | "collection-cards";
16
16
  enabled?: boolean | undefined;
17
17
  order?: number | undefined;
18
18
  config?: z.objectInputType<{}, z.ZodTypeAny, "passthrough"> | undefined;
19
19
  }>;
20
+ /**
21
+ * The next free `order` value.
22
+ *
23
+ * `HomepageSectionCreateInput.order` is REQUIRED, and both API create routes
24
+ * need the same answer for "the caller did not choose one". Two copies of this
25
+ * query is how they would come to disagree about where a new section lands.
26
+ */
27
+ export declare function resolveNextSectionOrder(): Promise<number>;
20
28
  export declare const updateSectionSchema: z.ZodObject<{
21
29
  order: z.ZodOptional<z.ZodNumber>;
22
30
  enabled: z.ZodOptional<z.ZodBoolean>;
@@ -1,13 +1,45 @@
1
1
  import { z } from "zod";
2
2
  import { serverLogger } from "../../../monitoring";
3
+ import { sortBy } from "../../../constants/sort";
4
+ import { HOMEPAGE_SECTION_FIELDS } from "../../../constants/field-names";
3
5
  import { homepageSectionsRepository } from "../repository/homepage-sections.repository";
6
+ import { homepageSectionTypeSchema } from "../schemas";
4
7
  // --- Schemas --------------------------------------------------------------
8
+ /*
9
+ * 🛑 `type` was `z.string().min(1)` — ANY non-empty string.
10
+ *
11
+ * This is the third of three create paths for `homepageSections`
12
+ * (`POST /api/admin/sections`, `POST /api/homepage-sections`, and this server
13
+ * action via `createHomepageSectionAction`), and it was the loosest: a caller
14
+ * could store a section whose `type` matches no renderer, which the homepage
15
+ * then skips in silence — a row that exists, occupies an order slot, and draws
16
+ * nothing.
17
+ *
18
+ * All three now agree on `homepageSectionTypeSchema`, which is derived from
19
+ * the `SectionType` union.
20
+ */
5
21
  export const createSectionSchema = z.object({
6
- type: z.string().min(1),
22
+ type: homepageSectionTypeSchema,
7
23
  enabled: z.boolean().default(true),
8
24
  order: z.number().int().default(0),
9
25
  config: z.object({}).passthrough().optional(),
10
26
  });
27
+ /**
28
+ * The next free `order` value.
29
+ *
30
+ * `HomepageSectionCreateInput.order` is REQUIRED, and both API create routes
31
+ * need the same answer for "the caller did not choose one". Two copies of this
32
+ * query is how they would come to disagree about where a new section lands.
33
+ */
34
+ export async function resolveNextSectionOrder() {
35
+ const latest = await homepageSectionsRepository.list({
36
+ sorts: sortBy(HOMEPAGE_SECTION_FIELDS.ORDER),
37
+ page: "1",
38
+ pageSize: "1",
39
+ });
40
+ const top = latest.items[0]?.order;
41
+ return typeof top === "number" ? top + 1 : 1;
42
+ }
11
43
  export const updateSectionSchema = z.object({
12
44
  order: z.number().int().optional(),
13
45
  enabled: z.boolean().optional(),
@@ -47,6 +47,37 @@ export declare const homepageSectionFirestoreSchema: z.ZodObject<{
47
47
  order: number;
48
48
  config: Record<string, JsonValue>;
49
49
  }>;
50
+ /**
51
+ * The create contract for a homepage section, shared by BOTH routes that can
52
+ * create one — `POST /api/admin/sections` (what the editor calls) and
53
+ * `POST /api/homepage-sections` (reachable by URL only).
54
+ *
55
+ * It exists because those two had drifted into being one validated path and
56
+ * one raw `{...body}` spread, and because the validated one's hand-written
57
+ * type list had fallen four values behind `SectionType` — so an admin could
58
+ * configure a Featured Bundles / Prize Draws / Event Raffles / Collection
59
+ * Cards section in full and get a 400 on save.
60
+ *
61
+ * `type` comes from `homepageSectionTypeSchema`, which is derived from the
62
+ * union, so that gap cannot reopen.
63
+ */
64
+ export declare const homepageSectionCreateSchema: z.ZodObject<{
65
+ type: z.ZodEnum<["welcome", "carousel", "stats", "trust-indicators", "categories", "brands", "products", "pre-orders", "auctions", "banner", "features", "reviews", "whatsapp-community", "faq", "blog-articles", "newsletter", "stores", "events", "social-feed", "custom-cards", "google-reviews", "featured-bundles", "prize-draws", "event-raffles", "collection-cards"]>;
66
+ enabled: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
67
+ order: z.ZodOptional<z.ZodNumber>;
68
+ config: z.ZodRecord<z.ZodString, z.ZodType<JsonValue, z.ZodTypeDef, JsonValue>>;
69
+ }, "strip", z.ZodTypeAny, {
70
+ type: "banner" | "stores" | "products" | "pre-orders" | "prize-draws" | "auctions" | "reviews" | "events" | "categories" | "brands" | "carousel" | "newsletter" | "features" | "stats" | "welcome" | "trust-indicators" | "whatsapp-community" | "faq" | "blog-articles" | "social-feed" | "custom-cards" | "google-reviews" | "featured-bundles" | "event-raffles" | "collection-cards";
71
+ enabled: boolean;
72
+ config: Record<string, JsonValue>;
73
+ order?: number | undefined;
74
+ }, {
75
+ type: "banner" | "stores" | "products" | "pre-orders" | "prize-draws" | "auctions" | "reviews" | "events" | "categories" | "brands" | "carousel" | "newsletter" | "features" | "stats" | "welcome" | "trust-indicators" | "whatsapp-community" | "faq" | "blog-articles" | "social-feed" | "custom-cards" | "google-reviews" | "featured-bundles" | "event-raffles" | "collection-cards";
76
+ config: Record<string, JsonValue>;
77
+ enabled?: boolean | undefined;
78
+ order?: number | undefined;
79
+ }>;
80
+ export type HomepageSectionCreateValues = z.infer<typeof homepageSectionCreateSchema>;
50
81
  export declare const carouselBackgroundSchema: z.ZodObject<{
51
82
  type: z.ZodEnum<["image", "video", "color", "gradient"]>;
52
83
  url: z.ZodOptional<z.ZodString>;
@@ -56,6 +56,30 @@ export const homepageSectionFirestoreSchema = z.object({
56
56
  config: z.record(z.string(), jsonValueSchema),
57
57
  ...auditTimestampsShape,
58
58
  });
59
+ /**
60
+ * The create contract for a homepage section, shared by BOTH routes that can
61
+ * create one — `POST /api/admin/sections` (what the editor calls) and
62
+ * `POST /api/homepage-sections` (reachable by URL only).
63
+ *
64
+ * It exists because those two had drifted into being one validated path and
65
+ * one raw `{...body}` spread, and because the validated one's hand-written
66
+ * type list had fallen four values behind `SectionType` — so an admin could
67
+ * configure a Featured Bundles / Prize Draws / Event Raffles / Collection
68
+ * Cards section in full and get a 400 on save.
69
+ *
70
+ * `type` comes from `homepageSectionTypeSchema`, which is derived from the
71
+ * union, so that gap cannot reopen.
72
+ */
73
+ export const homepageSectionCreateSchema = z.object({
74
+ type: homepageSectionTypeSchema,
75
+ enabled: z.boolean().optional().default(true),
76
+ order: z.number().int().optional(),
77
+ // Deliberately open. Every section type carries a different config shape,
78
+ // and a closed schema here would silently STRIP the builder's output for any
79
+ // type it did not enumerate — the `productBaseSchema` failure, one domain
80
+ // over.
81
+ config: z.record(z.string(), jsonValueSchema),
82
+ });
59
83
  export const carouselBackgroundSchema = z.object({
60
84
  type: z.enum(["image", "video", "color", "gradient"]),
61
85
  url: z.string().optional(),
@@ -1,5 +1,9 @@
1
+ import { type TimelineEntry } from "../../status-history/components/StatusTimeline";
1
2
  export interface OrderStatusTimelineProps {
2
3
  orderStatus: string;
4
+ /** Recorded history. When present it wins over the scalar-date fallback. */
5
+ timeline?: TimelineEntry[];
6
+ timelineTruncated?: number;
3
7
  orderDate?: string;
4
8
  shippingDate?: string;
5
9
  deliveryDate?: string;
@@ -9,4 +13,4 @@ export interface OrderStatusTimelineProps {
9
13
  trackingUrl?: string;
10
14
  className?: string;
11
15
  }
12
- export declare function OrderStatusTimeline({ orderStatus, orderDate, shippingDate, deliveryDate, cancellationDate, trackingNumber, shippingCarrier, trackingUrl, className, }: OrderStatusTimelineProps): import("react").JSX.Element;
16
+ export declare function OrderStatusTimeline({ orderStatus, timeline, timelineTruncated, orderDate, shippingDate, deliveryDate, cancellationDate, trackingNumber, shippingCarrier, trackingUrl, className, }: OrderStatusTimelineProps): import("react").JSX.Element;
@@ -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
  }