@mohasinac/appkit 4.1.1 → 4.1.3

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 (187) hide show
  1. package/dist/_internal/client/features/maintenance/index.d.ts +2 -0
  2. package/dist/_internal/client/features/maintenance/index.js +1 -0
  3. package/dist/_internal/client/features/maintenance/views/CloudLogsListView.d.ts +9 -0
  4. package/dist/_internal/client/features/maintenance/views/CloudLogsListView.js +55 -0
  5. package/dist/_internal/server/features/auctions/index.d.ts +1 -1
  6. package/dist/_internal/server/features/auctions/index.js +1 -1
  7. package/dist/_internal/server/features/auctions/service.js +2 -2
  8. package/dist/_internal/server/features/maintenance/cloud-logs-data.d.ts +50 -0
  9. package/dist/_internal/server/features/maintenance/cloud-logs-data.js +152 -0
  10. package/dist/_internal/server/jobs/core/onOrderCreate.d.ts +4 -12
  11. package/dist/_internal/server/jobs/core/onOrderCreate.js +3 -3
  12. package/dist/_internal/server/jobs/core/onProductWrite.js +14 -2
  13. package/dist/_internal/shared/features/auctions/config.d.ts +1 -1
  14. package/dist/_internal/shared/features/auctions/config.js +1 -1
  15. package/dist/_internal/shared/features/auctions/schema.js +1 -1
  16. package/dist/_internal/shared/features/cart/schema.js +2 -2
  17. package/dist/_internal/shared/features/events/schema.js +1 -1
  18. package/dist/_internal/shared/features/orders/schema.js +1 -1
  19. package/dist/_internal/shared/features/products/schema.js +3 -3
  20. package/dist/_internal/shared/features/promotions/schema.js +3 -3
  21. package/dist/_internal/shared/fees/calculator.d.ts +6 -7
  22. package/dist/client.d.ts +4 -4
  23. package/dist/client.js +4 -2
  24. package/dist/constants/api-endpoints.d.ts +9 -6
  25. package/dist/constants/api-endpoints.js +3 -2
  26. package/dist/contracts/client-payment-gateway.d.ts +1 -2
  27. package/dist/features/about/components/AboutView.d.ts +6 -16
  28. package/dist/features/about/components/AboutView.js +3 -3
  29. package/dist/features/about/index.d.ts +2 -1
  30. package/dist/features/about/schemas/firestore.d.ts +47 -0
  31. package/dist/features/about/schemas/firestore.js +6 -0
  32. package/dist/features/about/schemas/index.d.ts +210 -0
  33. package/dist/features/about/schemas/index.js +45 -0
  34. package/dist/features/account/hooks/useProfile.js +3 -0
  35. package/dist/features/admin/components/AdminCarouselGroupEditorView.d.ts +6 -0
  36. package/dist/features/admin/components/AdminCarouselGroupEditorView.js +50 -0
  37. package/dist/features/admin/components/AdminCartsView.js +16 -3
  38. package/dist/features/admin/components/AdminCouponEditorView.js +2 -2
  39. package/dist/features/admin/components/AdminCouponsView.js +14 -4
  40. package/dist/features/admin/components/AdminEmployeeEditorView.js +1 -42
  41. package/dist/features/admin/components/AdminMediaView.js +1 -1
  42. package/dist/features/admin/components/AdminNotificationsView.js +31 -19
  43. package/dist/features/admin/components/AdminOrdersView.js +7 -0
  44. package/dist/features/admin/components/AdminPayoutsView.js +18 -3
  45. package/dist/features/admin/components/AdminProductsView.js +17 -2
  46. package/dist/features/admin/components/AdminReviewsView.js +16 -2
  47. package/dist/features/admin/components/AdminScammersView.js +7 -0
  48. package/dist/features/admin/components/AdminSectionsView.js +4 -0
  49. package/dist/features/admin/components/AdminSiteConfigGuideView.js +1 -1
  50. package/dist/features/admin/components/AdminSiteSettingsView.js +44 -2
  51. package/dist/features/admin/components/AdminStoreEditorView.d.ts +2 -1
  52. package/dist/features/admin/components/AdminStoreEditorView.js +4 -4
  53. package/dist/features/admin/components/AdminStoresView.js +1 -1
  54. package/dist/features/admin/components/AdminSupportTicketDetailView.js +23 -5
  55. package/dist/features/admin/components/AdminSupportTicketsView.js +20 -2
  56. package/dist/features/admin/components/AdminTesterChecklistItemEditorView.js +4 -1
  57. package/dist/features/admin/components/AdminTesterChecklistView.js +8 -0
  58. package/dist/features/admin/components/index.d.ts +2 -0
  59. package/dist/features/admin/components/index.js +1 -0
  60. package/dist/features/admin/constants/filter-tabs.d.ts +55 -41
  61. package/dist/features/admin/constants/filter-tabs.js +55 -27
  62. package/dist/features/admin/schemas/firestore.d.ts +5 -0
  63. package/dist/features/auctions/actions/bid-actions.js +1 -1
  64. package/dist/features/auctions/components/AuctionDetailPageView.js +2 -2
  65. package/dist/features/auctions/components/AuctionsListView.js +8 -1
  66. package/dist/features/auth/components/LoginForm.js +2 -2
  67. package/dist/features/auth/components/RegisterForm.js +2 -2
  68. package/dist/features/auth/permissions/constants.d.ts +16 -0
  69. package/dist/features/auth/permissions/constants.js +53 -0
  70. package/dist/features/auth/schemas/firestore.js +1 -0
  71. package/dist/features/blog/components/BlogFilters.js +1 -1
  72. package/dist/features/cart/schemas/firestore.d.ts +1 -1
  73. package/dist/features/categories/schemas/firestore.d.ts +1 -1
  74. package/dist/features/consultation/components/ConsultationForm.js +1 -1
  75. package/dist/features/events/components/EventFilters.js +2 -0
  76. package/dist/features/homepage/components/WelcomeSection.js +2 -2
  77. package/dist/features/layout/TitleBarLayout.d.ts +2 -2
  78. package/dist/features/layout/TitleBarLayout.js +4 -4
  79. package/dist/features/media/AvatarUpload.js +7 -3
  80. package/dist/features/media/MediaVideo.js +1 -1
  81. package/dist/features/media/finalize.d.ts +9 -13
  82. package/dist/features/media/finalize.js +23 -74
  83. package/dist/features/media/server.d.ts +1 -1
  84. package/dist/features/media/server.js +1 -1
  85. package/dist/features/orders/hooks/useOrders.js +16 -4
  86. package/dist/features/orders/schemas/firestore.d.ts +5 -5
  87. package/dist/features/payments/schemas/firestore.d.ts +2 -2
  88. package/dist/features/pre-orders/components/PreOrdersListView.js +7 -0
  89. package/dist/features/products/components/ArtStickersListView.js +5 -0
  90. package/dist/features/products/components/ProductDetailPageView.d.ts +7 -1
  91. package/dist/features/products/components/ProductForm.js +14 -1
  92. package/dist/features/products/components/ProductsIndexPageView.js +7 -0
  93. package/dist/features/products/constants/action-defs.d.ts +1 -0
  94. package/dist/features/products/constants/action-defs.js +1 -0
  95. package/dist/features/products/schemas/firestore.d.ts +1 -1
  96. package/dist/features/products/schemas/product-templates.d.ts +1 -1
  97. package/dist/features/products/types/index.d.ts +4 -0
  98. package/dist/features/scams/schemas/index.js +2 -2
  99. package/dist/features/search/components/Search.js +3 -3
  100. package/dist/features/seller/components/SellerAuctionsView.js +27 -2
  101. package/dist/features/seller/components/SellerOrdersView.js +1 -1
  102. package/dist/features/seller/components/SellerPayoutsView.js +24 -10
  103. package/dist/features/seller/components/SellerPreOrdersView.js +12 -4
  104. package/dist/features/seller/components/SellerPrizeDrawsView.js +23 -5
  105. package/dist/features/seller/components/SellerProductShell.d.ts +2 -0
  106. package/dist/features/seller/components/SellerProductShell.js +1 -1
  107. package/dist/features/seller/components/SellerProductsView.js +8 -6
  108. package/dist/features/seller/components/SellerReviewsView.js +27 -6
  109. package/dist/features/seller/components/index.d.ts +0 -2
  110. package/dist/features/seller/components/index.js +0 -1
  111. package/dist/features/stores/components/StoreListingsGuideView.js +2 -1
  112. package/dist/features/stores/components/StoreProductsView.js +1 -1
  113. package/dist/features/stores/schemas/firestore.d.ts +11 -0
  114. package/dist/features/stores/schemas/firestore.js +5 -0
  115. package/dist/features/stores/schemas/index.d.ts +2 -2
  116. package/dist/features/tester/actions/checklist-item-actions.d.ts +6 -0
  117. package/dist/features/tester/actions/checklist-item-actions.js +2 -0
  118. package/dist/features/tester/components/AdminTesterFeedbackIssuesView.js +13 -2
  119. package/dist/features/tester/components/AdminTesterFeedbackListView.js +2 -1
  120. package/dist/features/tester/components/AdminTesterFeedbackReportView.js +9 -5
  121. package/dist/features/tester/components/TesterHubView.js +34 -21
  122. package/dist/features/tester/repository/tester-checklist-item.repository.js +1 -0
  123. package/dist/features/tester/repository/tester-checklist-response.repository.d.ts +18 -4
  124. package/dist/features/tester/repository/tester-checklist-response.repository.js +43 -8
  125. package/dist/features/tester/schemas/firestore.d.ts +8 -3
  126. package/dist/features/tester/schemas/firestore.js +6 -0
  127. package/dist/features/tester/seed-data/tester-checklist-seed-data.js +21 -9
  128. package/dist/features/tester/utils/phases.d.ts +31 -0
  129. package/dist/features/tester/utils/phases.js +46 -0
  130. package/dist/features/whatsapp-bot/components/SellerWhatsAppSettingsView.js +1 -1
  131. package/dist/features/whatsapp-bot/types/index.d.ts +1 -1
  132. package/dist/features/wishlist/types/index.d.ts +3 -0
  133. package/dist/index.d.ts +11 -4
  134. package/dist/index.js +15 -7
  135. package/dist/next/components/NotFoundView.js +2 -1
  136. package/dist/next/routing/route-map.d.ts +4 -12
  137. package/dist/next/routing/route-map.js +2 -4
  138. package/dist/providers/db-firebase/admin.d.ts +1 -0
  139. package/dist/providers/db-firebase/admin.js +1 -1
  140. package/dist/schemas/registry.d.ts +2 -2
  141. package/dist/seed/addresses-seed-data.js +18 -18
  142. package/dist/seed/bids-seed-data.js +4 -4
  143. package/dist/seed/blog-posts-seed-data.js +82 -82
  144. package/dist/seed/carousel-slides-seed-data.js +14 -10
  145. package/dist/seed/carousels-seed-data.js +3 -3
  146. package/dist/seed/cart-seed-data.js +92 -92
  147. package/dist/seed/claimed-coupons-seed-data.js +10 -10
  148. package/dist/seed/conversations-seed-data.js +75 -75
  149. package/dist/seed/coupon-usage-seed-data.js +12 -12
  150. package/dist/seed/coupons-seed-data.js +41 -41
  151. package/dist/seed/events-seed-data.js +105 -105
  152. package/dist/seed/grouped-listings-seed-data.d.ts +2 -137
  153. package/dist/seed/grouped-listings-seed-data.js +79 -143
  154. package/dist/seed/history-seed-data.js +38 -38
  155. package/dist/seed/homepage-sections-seed-data.js +9 -9
  156. package/dist/seed/notifications-seed-data.js +56 -56
  157. package/dist/seed/offers-seed-data.js +91 -91
  158. package/dist/seed/orders-seed-data.js +121 -106
  159. package/dist/seed/payouts-seed-data.js +42 -42
  160. package/dist/seed/products-auctions-seed-data.js +10 -2
  161. package/dist/seed/products-standard-seed-data.js +40 -8
  162. package/dist/seed/reviews-seed-data.js +42 -23
  163. package/dist/seed/scammers-seed-data.js +22 -22
  164. package/dist/seed/sessions-seed-data.js +7 -7
  165. package/dist/seed/shipments-seed-data.js +10 -10
  166. package/dist/seed/site-settings-seed-data.js +82 -0
  167. package/dist/seed/store-addresses-seed-data.js +2 -2
  168. package/dist/seed/store-extensions-seed-data.js +93 -93
  169. package/dist/seed/stores-seed-data.js +2 -0
  170. package/dist/seed/support-tickets-seed-data.js +47 -47
  171. package/dist/seed/users-seed-data.js +55 -89
  172. package/dist/seed/wishlists-seed-data.js +22 -22
  173. package/dist/server.d.ts +2 -1
  174. package/dist/server.js +5 -5
  175. package/dist/styles.css +1 -1
  176. package/dist/tailwind-utilities.css +1 -1
  177. package/dist/ui/components/PageLoader.js +8 -3
  178. package/dist/ui/components/Select.d.ts +8 -1
  179. package/dist/ui/components/Select.js +2 -2
  180. package/dist/ui/components/SiteMark.d.ts +23 -0
  181. package/dist/ui/components/SiteMark.js +76 -0
  182. package/dist/ui/index.d.ts +2 -0
  183. package/dist/ui/index.js +1 -0
  184. package/dist/validation/schemas.d.ts +0 -2
  185. package/dist/validation/schemas.js +0 -2
  186. package/package.json +1 -1
  187. package/scripts/seed-cli.mjs +17 -15
@@ -2,7 +2,7 @@
2
2
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
3
  import { Code, useApiMutation } from "@mohasinac/appkit/client";
4
4
  import React from "react";
5
- import { useQueryClient } from "@tanstack/react-query";
5
+ import { useQuery, useQueryClient } from "@tanstack/react-query";
6
6
  import { Button, Div, FormActions, HorizontalRule, Input, Label, Row, Select, SideDrawer, Span, Stack, Text, Textarea, Toggle, useToast } from "../../../ui";
7
7
  import { apiClient } from "../../../http";
8
8
  import { ADMIN_ENDPOINTS, SUPPORT_ENDPOINTS } from "../../../constants/api-endpoints";
@@ -109,6 +109,24 @@ export function AdminSupportTicketDetailView({ open, onClose, ticketId, subject,
109
109
  const [storeIsFeatured, setStoreIsFeatured] = React.useState(false);
110
110
  const linkedStoreId = parties.storeId?.trim();
111
111
  const isStoreChangeRequest = category === "store_change_request";
112
+ // Seed the panel from the store's REAL current values, not hardcoded
113
+ // defaults — applyStoreChange below sends all three fields unconditionally,
114
+ // so an unseeded default would silently reset a real verified/featured
115
+ // store back to false/"active" on the very first unrelated save.
116
+ const storeQuery = useQuery({
117
+ queryKey: ["admin", "support-ticket-store-change", linkedStoreId],
118
+ queryFn: async () => {
119
+ const res = await apiClient.get(ADMIN_ENDPOINTS.STORE_BY_ID(linkedStoreId));
120
+ return (res?.data ?? res);
121
+ },
122
+ enabled: open && isStoreChangeRequest && !!linkedStoreId,
123
+ select: (data) => {
124
+ setStoreStatus(data.status ?? "active");
125
+ setStoreIsVerified(Boolean(data.isVerified));
126
+ setStoreIsFeatured(Boolean(data.isFeatured));
127
+ return data;
128
+ },
129
+ });
112
130
  const applyStoreChange = useApiMutation({
113
131
  mutationFn: async () => {
114
132
  if (!linkedStoreId)
@@ -247,7 +265,7 @@ export function AdminSupportTicketDetailView({ open, onClose, ticketId, subject,
247
265
  ["orderId", "Order ID"],
248
266
  ["productId", "Product slug"],
249
267
  ["bidId", "Bid ID"],
250
- ].map(([key, label]) => (_jsx(Input, { label: label, type: "text", value: parties[key] ?? "", onChange: (e) => setParties((p) => ({ ...p, [key]: e.target.value })), placeholder: label }, key)))] }), isOrderModificationRequest && (_jsx(OrderItemsPanel, { linkedOrderId: linkedOrderId, orderItemsOpen: orderItemsOpen, setOrderItemsOpen: setOrderItemsOpen, orderItems: orderItems, setOrderItems: setOrderItems, loadOrderItems: loadOrderItems, applyOrderItems: applyOrderItems })), isStoreChangeRequest && (_jsx(StoreChangePanel, { linkedStoreId: linkedStoreId, storeStatus: storeStatus, setStoreStatus: setStoreStatus, storeIsVerified: storeIsVerified, setStoreIsVerified: setStoreIsVerified, storeIsFeatured: storeIsFeatured, setStoreIsFeatured: setStoreIsFeatured, applyStoreChange: applyStoreChange })), isUnbanRequest && (_jsx(UnbanRequestPanel, { linkedUserId: linkedUserId, liftHardBan: liftHardBan, liftSoftBanTickets: liftSoftBanTickets })), _jsxs(FormActions, { align: "right", children: [_jsx(Button, { type: "button", variant: "secondary", onClick: onClose, children: "Cancel" }), _jsx(Button, { type: "button", isLoading: updateMutation.isPending, disabled: !ticketId || updateMutation.isPending, onClick: () => updateMutation.mutate(), children: "Save changes" })] })] }) }));
268
+ ].map(([key, label]) => (_jsx(Input, { label: label, type: "text", value: parties[key] ?? "", onChange: (e) => setParties((p) => ({ ...p, [key]: e.target.value })), placeholder: label }, key)))] }), isOrderModificationRequest && (_jsx(OrderItemsPanel, { linkedOrderId: linkedOrderId, orderItemsOpen: orderItemsOpen, setOrderItemsOpen: setOrderItemsOpen, orderItems: orderItems, setOrderItems: setOrderItems, loadOrderItems: loadOrderItems, applyOrderItems: applyOrderItems })), isStoreChangeRequest && (_jsx(StoreChangePanel, { linkedStoreId: linkedStoreId, storeStatus: storeStatus, setStoreStatus: setStoreStatus, storeIsVerified: storeIsVerified, setStoreIsVerified: setStoreIsVerified, storeIsFeatured: storeIsFeatured, setStoreIsFeatured: setStoreIsFeatured, applyStoreChange: applyStoreChange, isLoadingStore: storeQuery.isLoading })), isUnbanRequest && (_jsx(UnbanRequestPanel, { linkedUserId: linkedUserId, liftHardBan: liftHardBan, liftSoftBanTickets: liftSoftBanTickets })), _jsxs(FormActions, { align: "right", children: [_jsx(Button, { type: "button", variant: "secondary", onClick: onClose, children: "Cancel" }), _jsx(Button, { type: "button", isLoading: updateMutation.isPending, disabled: !ticketId || updateMutation.isPending, onClick: () => updateMutation.mutate(), children: "Save changes" })] })] }) }));
251
269
  }
252
270
  function OrderItemsPanel(props) {
253
271
  const { linkedOrderId, orderItemsOpen, setOrderItemsOpen, orderItems, setOrderItems, loadOrderItems, applyOrderItems, } = props;
@@ -258,13 +276,13 @@ function OrderItemsPanel(props) {
258
276
  return (_jsxs(Stack, { padding: "sm", className: "border border-info/40 bg-[var(--appkit-color-info-surface)]/40", gap: "sm", rounded: "lg", children: [_jsx(Text, { className: "text-info tracking-wide", size: "xs", weight: "semibold", transform: "uppercase", children: "Modify order items" }), linkedOrderId ? (_jsxs(_Fragment, { children: [_jsxs(Text, { size: "xs", color: "muted", children: ["Editing order: ", _jsx(Code, { className: "font-mono", children: linkedOrderId })] }), !orderItemsOpen ? (_jsx(Button, { type: "button", variant: "secondary", size: "sm", isLoading: loadOrderItems.isPending, disabled: loadOrderItems.isPending, onClick: () => loadOrderItems.mutate(), children: "Load current items" })) : (_jsxs(_Fragment, { children: [orderItems.length === 0 ? (_jsx(Text, { size: "xs", color: "muted", children: "Order has no items." })) : (_jsx(Stack, { gap: "sm", children: orderItems.map((it, idx) => (_jsxs(Row, { paddingY: "y-2xs", padding: "x-xs", rounded: "default", align: "center", gap: "sm", border: "default", children: [_jsxs(Div, { className: "flex-1 min-w-0", children: [_jsx(Text, { className: "truncate", size: "xs", weight: "medium", children: it.productTitle }), _jsxs(Text, { size: "xs", color: "muted", children: [it.productId, " \u00B7 \u20B9", it.unitPrice.toFixed(2), "/ea"] })] }), _jsx(Input, { bare: true, type: "number", min: "0", value: it.quantity, onChange: (e) => updateQty(idx, e.target.value), className: "w-16 rounded border border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)] px-[var(--appkit-space-2)] py-[var(--appkit-space-1)] text-[length:var(--appkit-text-xs)]" })] }, `${it.productId}-${idx}`))) })), _jsxs(Row, { gap: "sm", children: [_jsx(Button, { type: "button", variant: "secondary", size: "sm", onClick: () => setOrderItemsOpen(false), children: "Cancel" }), _jsx(Button, { type: "button", variant: "primary", size: "sm", isLoading: applyOrderItems.isPending, disabled: applyOrderItems.isPending, onClick: () => applyOrderItems.mutate(), children: "Apply changes" })] })] }))] })) : (_jsxs(Text, { size: "xs", color: "muted", children: ["Set the", " ", _jsx(Text, { as: "span", weight: "semibold", children: "Order ID" }), " ", "field in the Linked parties panel above to enable this action."] }))] }));
259
277
  }
260
278
  function StoreChangePanel(props) {
261
- const { linkedStoreId, storeStatus, setStoreStatus, storeIsVerified, setStoreIsVerified, storeIsFeatured, setStoreIsFeatured, applyStoreChange, } = props;
262
- return (_jsxs(Stack, { padding: "sm", className: "border border-warning/40 bg-[var(--appkit-color-warning-surface)]/40", gap: "sm", rounded: "lg", children: [_jsx(Text, { className: "text-warning tracking-wide", size: "xs", weight: "semibold", transform: "uppercase", children: "Apply store change" }), linkedStoreId ? (_jsxs(_Fragment, { children: [_jsxs(Text, { size: "xs", color: "muted", children: ["Editing store: ", _jsx(Code, { className: "font-mono", children: linkedStoreId })] }), _jsx(Select, { label: "Store status", options: [
279
+ const { linkedStoreId, storeStatus, setStoreStatus, storeIsVerified, setStoreIsVerified, storeIsFeatured, setStoreIsFeatured, applyStoreChange, isLoadingStore, } = props;
280
+ return (_jsxs(Stack, { padding: "sm", className: "border border-warning/40 bg-[var(--appkit-color-warning-surface)]/40", gap: "sm", rounded: "lg", children: [_jsx(Text, { className: "text-warning tracking-wide", size: "xs", weight: "semibold", transform: "uppercase", children: "Apply store change" }), linkedStoreId ? (_jsxs(_Fragment, { children: [_jsxs(Text, { size: "xs", color: "muted", children: ["Editing store: ", _jsx(Code, { className: "font-mono", children: linkedStoreId })] }), isLoadingStore && (_jsx(Text, { size: "xs", color: "muted", children: "Loading current store state\u2026" })), _jsx(Select, { label: "Store status", options: [
263
281
  { label: "Active", value: "active" },
264
282
  { label: "Pending", value: "pending" },
265
283
  { label: "Suspended", value: "suspended" },
266
284
  { label: "Rejected", value: "rejected" },
267
- ], value: storeStatus, onValueChange: setStoreStatus }), _jsx(Toggle, { label: "Verified badge", checked: storeIsVerified, onChange: setStoreIsVerified }), _jsx(Toggle, { label: "Featured store", checked: storeIsFeatured, onChange: setStoreIsFeatured }), _jsx(Button, { type: "button", variant: "primary", size: "sm", isLoading: applyStoreChange.isPending, disabled: applyStoreChange.isPending, onClick: () => applyStoreChange.mutate(), children: "Apply store change" })] })) : (_jsxs(Text, { size: "xs", color: "muted", children: ["Set the", " ", _jsx(Text, { as: "span", weight: "semibold", children: "Store slug" }), " ", "field in the Linked parties panel above to enable this action."] }))] }));
285
+ ], value: storeStatus, onValueChange: setStoreStatus, disabled: isLoadingStore }), _jsx(Toggle, { label: "Verified badge", checked: storeIsVerified, onChange: setStoreIsVerified, disabled: isLoadingStore }), _jsx(Toggle, { label: "Featured store", checked: storeIsFeatured, onChange: setStoreIsFeatured, disabled: isLoadingStore }), _jsx(Button, { type: "button", variant: "primary", size: "sm", isLoading: applyStoreChange.isPending, disabled: applyStoreChange.isPending || isLoadingStore, onClick: () => applyStoreChange.mutate(), children: "Apply store change" })] })) : (_jsxs(Text, { size: "xs", color: "muted", children: ["Set the", " ", _jsx(Text, { as: "span", weight: "semibold", children: "Store slug" }), " ", "field in the Linked parties panel above to enable this action."] }))] }));
268
286
  }
269
287
  function UnbanRequestPanel(props) {
270
288
  const { linkedUserId, liftHardBan, liftSoftBanTickets } = props;
@@ -3,9 +3,11 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
3
3
  import { SIEVE_OP, Stack, sieveFilter } from "@mohasinac/appkit";
4
4
  import { sortBy } from "@mohasinac/appkit";
5
5
  import React, { useState } from "react";
6
+ import { useRouter } from "next/navigation";
6
7
  import { FilterChipGroup, ListingLayout, RowActionMenu, Span, Text } from "../../../ui";
7
8
  import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
8
9
  import { ROW_ACTION_META, ROW_ACTION_ID } from "../../../features/products/constants/action-defs";
10
+ import { ROUTES } from "../../../next/routing/route-map";
9
11
  import { ADMIN_SUPPORT_TICKET_STATUS_TABS, ADMIN_SUPPORT_TICKET_PRIORITY_TABS, } from "../constants/filter-tabs";
10
12
  import { toRecordArray, toRelativeDate, toStringValue, } from "../hooks/useAdminListingData";
11
13
  import { DataListingView } from "./DataListingView";
@@ -46,6 +48,7 @@ const TICKET_COLUMNS = [
46
48
  },
47
49
  ];
48
50
  export function AdminSupportTicketsView({ children, ...props }) {
51
+ const router = useRouter();
49
52
  const [drawerOpen, setDrawerOpen] = useState(false);
50
53
  const [selectedRow, setSelectedRow] = useState(null);
51
54
  if (React.Children.count(children) > 0) {
@@ -56,7 +59,7 @@ export function AdminSupportTicketsView({ children, ...props }) {
56
59
  title: "Support Tickets",
57
60
  searchPlaceholder: "Search by subject",
58
61
  emptyLabel: "No support tickets found",
59
- filterKeys: ["status", "priority"],
62
+ filterKeys: ["status", "priority", "showResolved"],
60
63
  defaultSort: sortBy("createdAt", "DESC"),
61
64
  queryKey: ["admin", "support-tickets", "listing"],
62
65
  endpoint: ADMIN_ENDPOINTS.SUPPORT_TICKETS,
@@ -91,8 +94,19 @@ export function AdminSupportTicketsView({ children, ...props }) {
91
94
  },
92
95
  buildFilters: (f) => {
93
96
  const parts = [];
94
- if (f.status && f.status !== "All")
97
+ if (f.status && f.status !== "All") {
95
98
  parts.push(sieveFilter("status", SIEVE_OP.EQ, f.status));
99
+ }
100
+ else if (f.showResolved !== "true") {
101
+ // Hide resolved and closed tickets by default (open-only quick
102
+ // view). Pipe-joined EQ, not NEQ: sievejs parses a same-field pipe
103
+ // as an OR group, upgraded by the Firestore adapter to an IN query.
104
+ // Unlike NEQ, IN has no orderBy-must-match-filtered-field
105
+ // restriction, so this stays compatible with the default createdAt
106
+ // sort. See sieve.ts for the documented Sieve/Firestore
107
+ // incompatibility this avoids.
108
+ parts.push(sieveFilter("status", SIEVE_OP.EQ, "open|in_progress|waiting_on_user"));
109
+ }
96
110
  if (f.priority && f.priority !== "All")
97
111
  parts.push(sieveFilter("priority", SIEVE_OP.EQ, f.priority));
98
112
  return parts.join(",") || undefined;
@@ -105,6 +119,10 @@ export function AdminSupportTicketsView({ children, ...props }) {
105
119
  setDrawerOpen(true);
106
120
  },
107
121
  },
122
+ {
123
+ label: "Open full page",
124
+ onClick: () => router.push(String(ROUTES.ADMIN.SUPPORT_TICKET_BY_ID(row.id))),
125
+ },
108
126
  ] })),
109
127
  renderFilterPanel: ({ pendingFilters, setPendingFilters }) => (_jsxs(_Fragment, { children: [_jsx(FilterChipGroup, { label: "Status", tabs: ADMIN_SUPPORT_TICKET_STATUS_TABS, value: pendingFilters.status ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, status: id })) }), _jsx(FilterChipGroup, { label: "Priority", tabs: ADMIN_SUPPORT_TICKET_PRIORITY_TABS, value: pendingFilters.priority ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, priority: id })) })] })),
110
128
  };
@@ -39,6 +39,7 @@ export function AdminTesterChecklistItemEditorView({ itemId, onSaved, onDeleted,
39
39
  const [description, setDescription] = React.useState("");
40
40
  const [href, setHref] = React.useState("");
41
41
  const [order, setOrder] = React.useState(0);
42
+ const [phase, setPhase] = React.useState(1);
42
43
  const [isActive, setIsActive] = React.useState(true);
43
44
  const [adminOnly, setAdminOnly] = React.useState(false);
44
45
  const [deleteConfirmOpen, setDeleteConfirmOpen] = React.useState(false);
@@ -63,6 +64,7 @@ export function AdminTesterChecklistItemEditorView({ itemId, onSaved, onDeleted,
63
64
  setDescription(item.description ?? "");
64
65
  setHref(item.href ?? "");
65
66
  setOrder(typeof item.order === "number" ? item.order : 0);
67
+ setPhase(typeof item.phase === "number" ? item.phase : 1);
66
68
  setIsActive(item.isActive ?? true);
67
69
  setAdminOnly(item.adminOnly ?? false);
68
70
  }, [itemQuery.data]);
@@ -87,6 +89,7 @@ export function AdminTesterChecklistItemEditorView({ itemId, onSaved, onDeleted,
87
89
  description: description || "",
88
90
  href: href || "",
89
91
  order,
92
+ phase,
90
93
  isActive,
91
94
  adminOnly,
92
95
  };
@@ -116,7 +119,7 @@ export function AdminTesterChecklistItemEditorView({ itemId, onSaved, onDeleted,
116
119
  });
117
120
  const isSubmitting = saveMutation.isPending || itemQuery.isLoading;
118
121
  const canSave = Boolean(groupLabel.trim()) && Boolean(pageLabel.trim()) && Boolean(label.trim());
119
- const formSection = (_jsxs(_Fragment, { children: [_jsx(Form, { schema: checklistItemFormSchema, onSubmit: (e) => e.preventDefault(), spacing: "md", children: ({ setFieldError, clearErrors }) => (_jsxs(_Fragment, { children: [_jsxs(Div, { layout: "grid", gap: "4", className: "grid-cols-2", children: [_jsx(FieldInput, { name: "groupLabel", label: "Group", value: groupLabel, onChange: handleGroupLabelChange, required: true, placeholder: "e.g. Buying", hint: "Top-level accordion section on the Tester Hub." }), _jsx(Input, { label: "Group key", value: groupKey, onChange: (e) => setGroupKey(e.target.value), placeholder: "buying", helperText: "Auto-generated from the group name." })] }), _jsxs(Div, { layout: "grid", gap: "4", className: "grid-cols-2", children: [_jsx(FieldInput, { name: "pageLabel", label: "Page", value: pageLabel, onChange: handlePageLabelChange, required: true, placeholder: "e.g. Checkout", hint: "Sub-accordion within the group." }), _jsx(Input, { label: "Page key", value: pageKey, onChange: (e) => setPageKey(e.target.value), placeholder: "checkout", helperText: "Auto-generated from the page name." })] }), _jsx(FieldInput, { name: "label", label: "Test case (Yes/No question)", value: label, onChange: setLabel, required: true, placeholder: "e.g. Coupon discount is correctly reflected in the order total" }), _jsx(FieldTextarea, { name: "description", label: "Description (optional)", value: description, onChange: setDescription, placeholder: "Extra context for the tester." }), _jsx(FieldInput, { name: "href", label: "Deep link (optional)", value: href, onChange: setHref, placeholder: "/cart", hint: "Jumps the tester straight to the feature being tested." }), _jsx(Input, { label: "Display order", value: String(order), onChange: (e) => setOrder(parseInt(e.target.value, 10) || 0), type: "number", min: 0, helperText: "Lower = shown first within the page." }), _jsxs(Stack, { className: `${__P.p4}`, gap: "3", rounded: "lg", border: "default", children: [_jsx(Text, { size: "sm", weight: "medium", color: "muted", children: "Visibility" }), _jsx(Toggle, { label: "Active (visible to testers)", checked: isActive, onChange: setIsActive }), _jsx(Toggle, { label: "Admin-only (requires canTestAdmin)", checked: adminOnly, onChange: setAdminOnly })] }), _jsxs(Row, { gap: "3", padding: "t-xs", children: [_jsx(Button, { type: "submit", isLoading: isSubmitting, disabled: !canSave || isSubmitting, onClick: () => {
122
+ const formSection = (_jsxs(_Fragment, { children: [_jsx(Form, { schema: checklistItemFormSchema, onSubmit: (e) => e.preventDefault(), spacing: "md", children: ({ setFieldError, clearErrors }) => (_jsxs(_Fragment, { children: [_jsxs(Div, { layout: "grid", gap: "4", className: "grid-cols-2", children: [_jsx(FieldInput, { name: "groupLabel", label: "Group", value: groupLabel, onChange: handleGroupLabelChange, required: true, placeholder: "e.g. Buying", hint: "Top-level accordion section on the Tester Hub." }), _jsx(Input, { label: "Group key", value: groupKey, onChange: (e) => setGroupKey(e.target.value), placeholder: "buying", helperText: "Auto-generated from the group name." })] }), _jsxs(Div, { layout: "grid", gap: "4", className: "grid-cols-2", children: [_jsx(FieldInput, { name: "pageLabel", label: "Page", value: pageLabel, onChange: handlePageLabelChange, required: true, placeholder: "e.g. Checkout", hint: "Sub-accordion within the group." }), _jsx(Input, { label: "Page key", value: pageKey, onChange: (e) => setPageKey(e.target.value), placeholder: "checkout", helperText: "Auto-generated from the page name." })] }), _jsx(FieldInput, { name: "label", label: "Test case (Yes/No question)", value: label, onChange: setLabel, required: true, placeholder: "e.g. Coupon discount is correctly reflected in the order total" }), _jsx(FieldTextarea, { name: "description", label: "Description (optional)", value: description, onChange: setDescription, placeholder: "Extra context for the tester." }), _jsx(FieldInput, { name: "href", label: "Deep link (optional)", value: href, onChange: setHref, placeholder: "/cart", hint: "Jumps the tester straight to the feature being tested." }), _jsxs(Div, { layout: "grid", gap: "4", className: "grid-cols-2", children: [_jsx(Input, { label: "Display order", value: String(order), onChange: (e) => setOrder(parseInt(e.target.value, 10) || 0), type: "number", min: 0, helperText: "Lower = shown first within the page." }), _jsx(Input, { label: "Phase", value: String(phase), onChange: (e) => setPhase(Math.max(1, parseInt(e.target.value, 10) || 1)), type: "number", min: 1, helperText: "Test batch this case belongs to \u2014 testers work through one phase at a time." })] }), _jsxs(Stack, { className: `${__P.p4}`, gap: "3", rounded: "lg", border: "default", children: [_jsx(Text, { size: "sm", weight: "medium", color: "muted", children: "Visibility" }), _jsx(Toggle, { label: "Active (visible to testers)", checked: isActive, onChange: setIsActive }), _jsx(Toggle, { label: "Admin-only (requires canTestAdmin)", checked: adminOnly, onChange: setAdminOnly })] }), _jsxs(Row, { gap: "3", padding: "t-xs", children: [_jsx(Button, { type: "submit", isLoading: isSubmitting, disabled: !canSave || isSubmitting, onClick: () => {
120
123
  clearErrors();
121
124
  if (!label.trim()) {
122
125
  setFieldError("label", "Test case label is required");
@@ -16,6 +16,12 @@ const COLUMNS = [
16
16
  sortable: true,
17
17
  render: (row) => (_jsxs(Stack, { gap: "xs", children: [_jsx(Text, { weight: "semibold", color: "primary", children: row.primary }), _jsx(Text, { size: "xs", color: "muted", children: row.secondary })] })),
18
18
  },
19
+ {
20
+ key: "phase",
21
+ header: "Phase",
22
+ className: "w-20",
23
+ render: (row) => _jsx(Text, { size: "sm", color: "muted", children: row.phase }),
24
+ },
19
25
  {
20
26
  key: "status",
21
27
  header: "Status",
@@ -41,6 +47,7 @@ export function AdminTesterChecklistView({ children, onBulkDelete, ...props }) {
41
47
  endpoint: ADMIN_ENDPOINTS.TESTER_CHECKLIST_ITEMS,
42
48
  sortOptions: [
43
49
  { value: "order", label: "Order" },
50
+ { value: "phase", label: "Phase" },
44
51
  { value: sortBy("createdAt", "DESC"), label: "Newest" },
45
52
  { value: "label", label: "Test case A–Z" },
46
53
  ],
@@ -49,6 +56,7 @@ export function AdminTesterChecklistView({ children, onBulkDelete, ...props }) {
49
56
  id: toStringValue(item.id, `checklist-${index}`),
50
57
  primary: toStringValue(item.label, "Untitled test case"),
51
58
  secondary: `${toStringValue(item.groupLabel, "Uncategorized")} / ${toStringValue(item.pageLabel, "")}`,
59
+ phase: typeof item.phase === "number" ? item.phase : 1,
52
60
  status: toStringValue(typeof item.isActive === "boolean"
53
61
  ? item.isActive
54
62
  ? "Active"
@@ -46,6 +46,8 @@ export { AdminStickersView } from "./AdminStickersView";
46
46
  export type { AdminStickersViewProps } from "./AdminStickersView";
47
47
  export { AdminGroupedListingsView } from "./AdminGroupedListingsView";
48
48
  export type { AdminGroupedListingsViewProps } from "./AdminGroupedListingsView";
49
+ export { AdminCarouselGroupEditorView } from "./AdminCarouselGroupEditorView";
50
+ export type { AdminCarouselGroupEditorViewProps } from "./AdminCarouselGroupEditorView";
49
51
  export { AdminBundlesView } from "./AdminBundlesView";
50
52
  export type { AdminBundlesViewProps } from "./AdminBundlesView";
51
53
  export { AdminBundleEditorView } from "./AdminBundleEditorView";
@@ -26,6 +26,7 @@ export { AdminLiveView } from "./AdminLiveView";
26
26
  export { AdminArtView } from "./AdminArtView";
27
27
  export { AdminStickersView } from "./AdminStickersView";
28
28
  export { AdminGroupedListingsView } from "./AdminGroupedListingsView";
29
+ export { AdminCarouselGroupEditorView } from "./AdminCarouselGroupEditorView";
29
30
  // S-SBUNI-4 2026-05-13 — admin bundle CRUD views.
30
31
  export { AdminBundlesView } from "./AdminBundlesView";
31
32
  export { AdminBundleEditorView } from "./AdminBundleEditorView";
@@ -19,12 +19,14 @@ export declare const ALL_TAB: {
19
19
  readonly id: "All";
20
20
  readonly label: "All";
21
21
  };
22
- /** Admin > Products — listing status filter chip set. */
22
+ /** Admin > Products — listing status filter chip set. Real `ProductStatus`
23
+ * is draft|published|in_review|archived — "pending" fixed to "in_review"
24
+ * (its real meaning); the previous id never matched any document. */
23
25
  export declare const ADMIN_PRODUCT_STATUS_TABS: readonly [{
24
26
  readonly id: "All";
25
27
  readonly label: "All";
26
28
  }, {
27
- readonly id: "pending";
29
+ readonly id: "in_review";
28
30
  readonly label: "Pending";
29
31
  }, {
30
32
  readonly id: "published";
@@ -264,13 +266,15 @@ export declare const ADMIN_EVENT_ENTRY_STATUS_TABS: readonly [{
264
266
  readonly id: "CANCELLED";
265
267
  readonly label: "Cancelled";
266
268
  }];
267
- /** Admin > Events — event-state filter chip set. */
269
+ /** Admin > Events — event-state filter chip set. Real `EventStatus` is
270
+ * draft|active|paused|ended|cancelled — there is no "published" value
271
+ * (field-names.ts's copy of this enum is stale; verified against the
272
+ * feature's own events/types/index.ts). A "Published" chip previously
273
+ * returned zero rows every time — removed rather than aliased since
274
+ * "Active" already covers the intended meaning. */
268
275
  export declare const ADMIN_EVENT_STATUS_TABS: readonly [{
269
276
  readonly id: "All";
270
277
  readonly label: "All";
271
- }, {
272
- readonly id: "published";
273
- readonly label: "Published";
274
278
  }, {
275
279
  readonly id: "draft";
276
280
  readonly label: "Draft";
@@ -371,12 +375,18 @@ export declare const EMPTY_TAB: {
371
375
  readonly id: "";
372
376
  readonly label: "All";
373
377
  };
374
- /** Seller > Products — listing-state filter chip set. */
378
+ /** Seller > Products — listing-state filter chip set. Real `ProductStatus`
379
+ * is draft|published|in_review|archived — there is no "active" or "sold"
380
+ * value on the `status` field. "Active" was fixed to "published" (its real
381
+ * meaning); "Sold" was removed since sold-filtering already has its own
382
+ * correct, working mechanism (the `isSold`-driven "Show sold" toolbar
383
+ * toggle in SellerProductsView.tsx) — the status chip duplicated it with
384
+ * a broken `status=="sold"` query that always returned zero rows. */
375
385
  export declare const SELLER_PRODUCT_STATUS_TABS: readonly [{
376
386
  readonly id: "All";
377
387
  readonly label: "All";
378
388
  }, {
379
- readonly id: "active";
389
+ readonly id: "published";
380
390
  readonly label: "Active";
381
391
  }, {
382
392
  readonly id: "draft";
@@ -384,33 +394,36 @@ export declare const SELLER_PRODUCT_STATUS_TABS: readonly [{
384
394
  }, {
385
395
  readonly id: "archived";
386
396
  readonly label: "Archived";
387
- }, {
388
- readonly id: "sold";
389
- readonly label: "Sold";
390
397
  }];
391
- /** Seller > Auctions — auction-state filter chip set. */
398
+ /** Seller > Auctions — auction-state filter chip set. Real `ProductStatus`
399
+ * has no "active"/"ended"/"cancelled" values. "Active" fixed to
400
+ * "published". "Ended" and "Cancelled" removed — "ended" is a derived
401
+ * state (auctionEndDate vs now), not a stored status, and is now handled
402
+ * by SellerAuctionsView's "Show ended" toolbar toggle; there is no stored
403
+ * "cancelled" state for auctions at all (an auction that expires with no
404
+ * bids becomes `archived`, indistinguishable in storage from a listing the
405
+ * seller archived for any other reason — aliasing "Cancelled" to
406
+ * `status==archived` would be misleading, so it's dropped rather than
407
+ * reintroduced under a wrong mapping). */
392
408
  export declare const SELLER_AUCTION_STATUS_TABS: readonly [{
393
409
  readonly id: "All";
394
410
  readonly label: "All";
395
411
  }, {
396
- readonly id: "active";
412
+ readonly id: "published";
397
413
  readonly label: "Active";
398
414
  }, {
399
415
  readonly id: "draft";
400
416
  readonly label: "Draft";
401
- }, {
402
- readonly id: "ended";
403
- readonly label: "Ended";
404
- }, {
405
- readonly id: "cancelled";
406
- readonly label: "Cancelled";
407
417
  }];
408
- /** Seller > Pre-orders — pre-order-state filter chip set. */
418
+ /** Seller > Pre-orders — pre-order-state filter chip set. Same root cause
419
+ * and fix as SELLER_AUCTION_STATUS_TABS above — no "active"/"cancelled"
420
+ * status values exist; pre-order cancellation, if tracked, lives in the
421
+ * separate `preOrderProductionStatus` field, not `status`. */
409
422
  export declare const SELLER_PRE_ORDER_STATUS_TABS: readonly [{
410
423
  readonly id: "All";
411
424
  readonly label: "All";
412
425
  }, {
413
- readonly id: "active";
426
+ readonly id: "published";
414
427
  readonly label: "Active";
415
428
  }, {
416
429
  readonly id: "draft";
@@ -418,53 +431,54 @@ export declare const SELLER_PRE_ORDER_STATUS_TABS: readonly [{
418
431
  }, {
419
432
  readonly id: "archived";
420
433
  readonly label: "Archived";
421
- }, {
422
- readonly id: "cancelled";
423
- readonly label: "Cancelled";
424
434
  }];
425
- /** Seller > Prize Draws — draw-state filter chip set. */
435
+ /** Seller > Prize Draws — draw-state filter chip set. Same root cause and
436
+ * fix as SELLER_AUCTION_STATUS_TABS — "ended" is derived from
437
+ * `prizeRevealWindowEnd` vs now (handled by the "Show closed" toolbar
438
+ * toggle), no stored "cancelled" state exists. */
426
439
  export declare const SELLER_PRIZE_DRAW_STATUS_TABS: readonly [{
427
440
  readonly id: "All";
428
441
  readonly label: "All";
429
442
  }, {
430
- readonly id: "active";
443
+ readonly id: "published";
431
444
  readonly label: "Active";
432
445
  }, {
433
446
  readonly id: "draft";
434
447
  readonly label: "Draft";
435
- }, {
436
- readonly id: "ended";
437
- readonly label: "Ended";
438
- }, {
439
- readonly id: "cancelled";
440
- readonly label: "Cancelled";
441
448
  }];
442
449
  /** Seller > Orders — order-state filter chip set. Subset of
443
450
  * `ADMIN_ORDER_STATUS_TABS` — sellers don't see `RETURN_REQUESTED` until
444
- * the buyer initiates one through support. */
451
+ * the buyer initiates one through support. IDs are lowercase to match the
452
+ * real stored `OrderStatusValues` (Firestore `==` is byte-exact — the
453
+ * previous uppercase ids never matched any document, every chip here
454
+ * silently returned zero rows). */
445
455
  export declare const SELLER_ORDER_STATUS_TABS: readonly [{
446
456
  readonly id: "All";
447
457
  readonly label: "All";
448
458
  }, {
449
- readonly id: "PENDING";
459
+ readonly id: "pending";
450
460
  readonly label: "Pending";
451
461
  }, {
452
- readonly id: "PROCESSING";
462
+ readonly id: "processing";
453
463
  readonly label: "Processing";
454
464
  }, {
455
- readonly id: "SHIPPED";
465
+ readonly id: "shipped";
456
466
  readonly label: "Shipped";
457
467
  }, {
458
- readonly id: "DELIVERED";
468
+ readonly id: "delivered";
459
469
  readonly label: "Delivered";
460
470
  }, {
461
- readonly id: "CANCELLED";
471
+ readonly id: "cancelled";
462
472
  readonly label: "Cancelled";
463
473
  }, {
464
- readonly id: "REFUNDED";
474
+ readonly id: "refunded";
465
475
  readonly label: "Refunded";
466
476
  }];
467
- /** Seller > Offers — offer-state filter chip set. */
477
+ /** Seller > Offers — offer-state filter chip set. Real `OfferStatus` is
478
+ * pending|accepted|declined|countered|expired|withdrawn|paid — there is no
479
+ * "rejected" value, the real one is "declined" (the id below was fixed;
480
+ * the previous "rejected" id always returned zero rows). Label kept as
481
+ * "Rejected" for continuity with existing seller-facing wording. */
468
482
  export declare const SELLER_OFFER_STATUS_TABS: readonly [{
469
483
  readonly id: "All";
470
484
  readonly label: "All";
@@ -475,7 +489,7 @@ export declare const SELLER_OFFER_STATUS_TABS: readonly [{
475
489
  readonly id: "accepted";
476
490
  readonly label: "Accepted";
477
491
  }, {
478
- readonly id: "rejected";
492
+ readonly id: "declined";
479
493
  readonly label: "Rejected";
480
494
  }, {
481
495
  readonly id: "expired";
@@ -10,10 +10,12 @@
10
10
  * primitive and stay in sync when statuses are added.
11
11
  */
12
12
  export const ALL_TAB = { id: "All", label: "All" };
13
- /** Admin > Products — listing status filter chip set. */
13
+ /** Admin > Products — listing status filter chip set. Real `ProductStatus`
14
+ * is draft|published|in_review|archived — "pending" fixed to "in_review"
15
+ * (its real meaning); the previous id never matched any document. */
14
16
  export const ADMIN_PRODUCT_STATUS_TABS = [
15
17
  ALL_TAB,
16
- { id: "pending", label: "Pending" },
18
+ { id: "in_review", label: "Pending" },
17
19
  { id: "published", label: "Published" },
18
20
  { id: "draft", label: "Draft" },
19
21
  { id: "archived", label: "Archived" },
@@ -129,10 +131,14 @@ export const ADMIN_EVENT_ENTRY_STATUS_TABS = [
129
131
  { id: "WAITLISTED", label: "Waitlisted" },
130
132
  { id: "CANCELLED", label: "Cancelled" },
131
133
  ];
132
- /** Admin > Events — event-state filter chip set. */
134
+ /** Admin > Events — event-state filter chip set. Real `EventStatus` is
135
+ * draft|active|paused|ended|cancelled — there is no "published" value
136
+ * (field-names.ts's copy of this enum is stale; verified against the
137
+ * feature's own events/types/index.ts). A "Published" chip previously
138
+ * returned zero rows every time — removed rather than aliased since
139
+ * "Active" already covers the intended meaning. */
133
140
  export const ADMIN_EVENT_STATUS_TABS = [
134
141
  ALL_TAB,
135
- { id: "published", label: "Published" },
136
142
  { id: "draft", label: "Draft" },
137
143
  { id: "active", label: "Active" },
138
144
  { id: "ended", label: "Ended" },
@@ -184,56 +190,78 @@ export const ADMIN_COUPON_TYPE_TABS = [
184
190
  * filter URL with `""` instead of `"All"` so the `"" == no filter` invariant
185
191
  * stays a single shape across queries. */
186
192
  export const EMPTY_TAB = { id: "", label: "All" };
187
- /** Seller > Products — listing-state filter chip set. */
193
+ /** Seller > Products — listing-state filter chip set. Real `ProductStatus`
194
+ * is draft|published|in_review|archived — there is no "active" or "sold"
195
+ * value on the `status` field. "Active" was fixed to "published" (its real
196
+ * meaning); "Sold" was removed since sold-filtering already has its own
197
+ * correct, working mechanism (the `isSold`-driven "Show sold" toolbar
198
+ * toggle in SellerProductsView.tsx) — the status chip duplicated it with
199
+ * a broken `status=="sold"` query that always returned zero rows. */
188
200
  export const SELLER_PRODUCT_STATUS_TABS = [
189
201
  ALL_TAB,
190
- { id: "active", label: "Active" },
202
+ { id: "published", label: "Active" },
191
203
  { id: "draft", label: "Draft" },
192
204
  { id: "archived", label: "Archived" },
193
- { id: "sold", label: "Sold" },
194
205
  ];
195
- /** Seller > Auctions — auction-state filter chip set. */
206
+ /** Seller > Auctions — auction-state filter chip set. Real `ProductStatus`
207
+ * has no "active"/"ended"/"cancelled" values. "Active" fixed to
208
+ * "published". "Ended" and "Cancelled" removed — "ended" is a derived
209
+ * state (auctionEndDate vs now), not a stored status, and is now handled
210
+ * by SellerAuctionsView's "Show ended" toolbar toggle; there is no stored
211
+ * "cancelled" state for auctions at all (an auction that expires with no
212
+ * bids becomes `archived`, indistinguishable in storage from a listing the
213
+ * seller archived for any other reason — aliasing "Cancelled" to
214
+ * `status==archived` would be misleading, so it's dropped rather than
215
+ * reintroduced under a wrong mapping). */
196
216
  export const SELLER_AUCTION_STATUS_TABS = [
197
217
  ALL_TAB,
198
- { id: "active", label: "Active" },
218
+ { id: "published", label: "Active" },
199
219
  { id: "draft", label: "Draft" },
200
- { id: "ended", label: "Ended" },
201
- { id: "cancelled", label: "Cancelled" },
202
220
  ];
203
- /** Seller > Pre-orders — pre-order-state filter chip set. */
221
+ /** Seller > Pre-orders — pre-order-state filter chip set. Same root cause
222
+ * and fix as SELLER_AUCTION_STATUS_TABS above — no "active"/"cancelled"
223
+ * status values exist; pre-order cancellation, if tracked, lives in the
224
+ * separate `preOrderProductionStatus` field, not `status`. */
204
225
  export const SELLER_PRE_ORDER_STATUS_TABS = [
205
226
  ALL_TAB,
206
- { id: "active", label: "Active" },
227
+ { id: "published", label: "Active" },
207
228
  { id: "draft", label: "Draft" },
208
229
  { id: "archived", label: "Archived" },
209
- { id: "cancelled", label: "Cancelled" },
210
230
  ];
211
- /** Seller > Prize Draws — draw-state filter chip set. */
231
+ /** Seller > Prize Draws — draw-state filter chip set. Same root cause and
232
+ * fix as SELLER_AUCTION_STATUS_TABS — "ended" is derived from
233
+ * `prizeRevealWindowEnd` vs now (handled by the "Show closed" toolbar
234
+ * toggle), no stored "cancelled" state exists. */
212
235
  export const SELLER_PRIZE_DRAW_STATUS_TABS = [
213
236
  ALL_TAB,
214
- { id: "active", label: "Active" },
237
+ { id: "published", label: "Active" },
215
238
  { id: "draft", label: "Draft" },
216
- { id: "ended", label: "Ended" },
217
- { id: "cancelled", label: "Cancelled" },
218
239
  ];
219
240
  /** Seller > Orders — order-state filter chip set. Subset of
220
241
  * `ADMIN_ORDER_STATUS_TABS` — sellers don't see `RETURN_REQUESTED` until
221
- * the buyer initiates one through support. */
242
+ * the buyer initiates one through support. IDs are lowercase to match the
243
+ * real stored `OrderStatusValues` (Firestore `==` is byte-exact — the
244
+ * previous uppercase ids never matched any document, every chip here
245
+ * silently returned zero rows). */
222
246
  export const SELLER_ORDER_STATUS_TABS = [
223
247
  ALL_TAB,
224
- { id: "PENDING", label: "Pending" },
225
- { id: "PROCESSING", label: "Processing" },
226
- { id: "SHIPPED", label: "Shipped" },
227
- { id: "DELIVERED", label: "Delivered" },
228
- { id: "CANCELLED", label: "Cancelled" },
229
- { id: "REFUNDED", label: "Refunded" },
248
+ { id: "pending", label: "Pending" },
249
+ { id: "processing", label: "Processing" },
250
+ { id: "shipped", label: "Shipped" },
251
+ { id: "delivered", label: "Delivered" },
252
+ { id: "cancelled", label: "Cancelled" },
253
+ { id: "refunded", label: "Refunded" },
230
254
  ];
231
- /** Seller > Offers — offer-state filter chip set. */
255
+ /** Seller > Offers — offer-state filter chip set. Real `OfferStatus` is
256
+ * pending|accepted|declined|countered|expired|withdrawn|paid — there is no
257
+ * "rejected" value, the real one is "declined" (the id below was fixed;
258
+ * the previous "rejected" id always returned zero rows). Label kept as
259
+ * "Rejected" for continuity with existing seller-facing wording. */
232
260
  export const SELLER_OFFER_STATUS_TABS = [
233
261
  ALL_TAB,
234
262
  { id: "pending", label: "Pending" },
235
263
  { id: "accepted", label: "Accepted" },
236
- { id: "rejected", label: "Rejected" },
264
+ { id: "declined", label: "Rejected" },
237
265
  { id: "expired", label: "Expired" },
238
266
  ];
239
267
  /** Seller > Bids — bid-state filter chip set. Uses the empty-sentinel
@@ -2,6 +2,7 @@
2
2
  * Admin Feature Firestore Document Types & Constants
3
3
  * Covers: notifications, chat rooms, site settings
4
4
  */
5
+ import type { AboutContentDocument } from "../../about/schemas/firestore";
5
6
  export declare const ADMIN_CHECKOUT_BYPASS_FLAG_KEY: "adminCheckoutBypass";
6
7
  export type NotificationType = "order_placed" | "order_confirmed" | "order_shipped" | "order_delivered" | "order_cancelled" | "bid_placed" | "bid_outbid" | "bid_won" | "bid_lost" | "review_approved" | "review_replied" | "product_available" | "promotion" | "system" | "welcome" | "account_action" | "offer_received" | "offer_responded" | "offer_expired" | "offer_counter_accepted" | "refund_initiated" | "prize_reveal_ready" | "prize_reveal_expired" | "prize_reveal_reminder" | "emi_installment_due_soon" | "emi_installment_overdue" | "payment_review";
7
8
  import type { BaseDocument } from "../../../_internal/shared/types/base-document";
@@ -426,6 +427,10 @@ export interface SiteSettingsDocument extends BaseDocument {
426
427
  codDeposit: number;
427
428
  };
428
429
  };
430
+ /** About Us page content, admin-editable via Site Settings. Optional so
431
+ * pre-existing docs without it still type-check; `about/page.tsx` falls
432
+ * back to i18n defaults when absent. */
433
+ aboutContent?: AboutContentDocument;
429
434
  navbarConfig?: {
430
435
  hiddenNavItems?: string[];
431
436
  };
@@ -47,7 +47,7 @@ export async function placeBid(userId, userEmail, input) {
47
47
  const baseBid = (product.currentBid ?? 0) > 0
48
48
  ? product.currentBid
49
49
  : (product.startingBid ?? product.price);
50
- const minIncrement = product.minBidIncrement ?? 100;
50
+ const minIncrement = product.minBidIncrement ?? 1;
51
51
  // Proxy-bid semantics (eBay style): the buyer's `bidAmount` is treated as
52
52
  // their **maximum** they're willing to pay; the visible price only steps up
53
53
  // in `minIncrement` units as needed to stay ahead of the previous high bid.
@@ -125,9 +125,9 @@ export async function AuctionDetailPageView({ id, initialAuction, onPlaceBid, on
125
125
  price: typeof p.price === "number" ? p.price : undefined,
126
126
  storeId: typeof p.storeId === "string" ? p.storeId : undefined,
127
127
  storeName: typeof p.storeName === "string" ? p.storeName : undefined,
128
- } }), _jsxs(Container, { size: "xl", padding: "y-lg", children: [_jsxs(Row, { className: "mb-4", align: "center", justify: "between", gap: "sm", wrap: true, children: [_jsxs(Nav, { "aria-label": "Breadcrumb", layout: "flex-wrap", gap: "2xs", textSize: "xs", color: "muted", children: [_jsx(Link, { href: String(ROUTES.HOME), className: CLS_BREADCRUMB_LINK, children: "Home" }), _jsx(Span, { "aria-hidden": true, children: "/" }), _jsx(Link, { href: String(ROUTES.PUBLIC.AUCTIONS), className: CLS_BREADCRUMB_LINK, children: "Auctions" }), category && (_jsxs(_Fragment, { children: [_jsx(Span, { "aria-hidden": true, children: "/" }), _jsx(Link, { href: String(ROUTES.PUBLIC.CATEGORY_DETAIL(category)), className: CLS_BREADCRUMB_LINK, children: categoryName || category })] })), _jsx(Span, { "aria-hidden": true, children: "/" }), _jsx(Span, { className: "truncate max-w-[200px]", color: "muted", children: title })] }), _jsx(ShareButton, { title: title })] }), _jsx(AuctionDetailView, { renderGallery: () => (_jsx(ProductGalleryClient, { images: images, productName: title })), renderInfo: () => renderAuctionInfoPanel({ title, currentBid, currency, bidCount, isEnded, endDate, buyNowPrice, featured, freeShipping, condition, category, categoryName, brand, brandSlug, productFeatures, features, descriptionHtml, safeSeller, storeHref }), renderBidForm: () => onPlaceBid ? (_jsx(Stack, { id: "auction-bid-form", gap: "3", children: _jsxs(Stack, { className: `${__P.p5}`, border: "subtle", gap: "3", rounded: "xl", surface: "muted", children: [_jsxs(Row, { justify: "between", align: "baseline", children: [_jsx(Span, { size: "xl", weight: "bold", className: "text-primary-600 dark:text-primary-400", children: formatCurrency(currentBid, currency) }), _jsxs(Span, { size: "xs", color: "muted", children: [bidCount, " ", bidCount === 1 ? "bid" : "bids", " \u00B7 min increment ", formatCurrency(minBidIncrement, currency)] })] }), _jsx(PlaceBidModalButton, { productId: String(product.id), currentBid: currentBid, startingBid: startingBid, minBidIncrement: minBidIncrement, currency: currency, isEnded: isEnded, buyNowPrice: buyNowPrice, bidsHaveStarted: bidsHaveStarted, bidCount: bidCount, tags: tags, onPlaceBid: onPlaceBid, onBuyNow: onBuyNow, triggerClassName: "w-full" })] }) })) : (
128
+ } }), _jsxs(Container, { size: "xl", padding: "y-lg", children: [_jsxs(Row, { className: "mb-4", align: "center", justify: "between", gap: "sm", wrap: true, children: [_jsxs(Nav, { "aria-label": "Breadcrumb", layout: "flex-wrap", gap: "2xs", textSize: "xs", color: "muted", children: [_jsx(Link, { href: String(ROUTES.HOME), className: CLS_BREADCRUMB_LINK, children: "Home" }), _jsx(Span, { "aria-hidden": true, children: "/" }), _jsx(Link, { href: String(ROUTES.PUBLIC.AUCTIONS), className: CLS_BREADCRUMB_LINK, children: "Auctions" }), category && (_jsxs(_Fragment, { children: [_jsx(Span, { "aria-hidden": true, children: "/" }), _jsx(Link, { href: String(ROUTES.PUBLIC.CATEGORY_DETAIL(category)), className: CLS_BREADCRUMB_LINK, children: categoryName || category })] })), _jsx(Span, { "aria-hidden": true, children: "/" }), _jsx(Span, { className: "truncate max-w-[200px]", color: "muted", children: title })] }), _jsx(ShareButton, { title: title })] }), _jsx(AuctionDetailView, { renderGallery: () => (_jsx(ProductGalleryClient, { images: images, productName: title })), renderInfo: () => renderAuctionInfoPanel({ title, currentBid, currency, bidCount, isEnded, endDate, buyNowPrice, featured, freeShipping, condition, category, categoryName, brand, brandSlug, productFeatures, features, descriptionHtml, safeSeller, storeHref }), renderBidForm: () => onPlaceBid ? (_jsx(Stack, { id: "auction-bid-form", gap: "3", children: _jsxs(Stack, { className: `${__P.p5}`, border: "subtle", gap: "3", rounded: "xl", surface: "muted", children: [_jsxs(Row, { justify: "between", align: "baseline", children: [_jsx(Span, { size: "xl", weight: "bold", className: "text-primary-600 dark:text-primary-400", children: formatCurrency(currentBid, currency) }), _jsxs(Span, { size: "xs", color: "muted", children: [bidCount, " ", bidCount === 1 ? "bid" : "bids", !isEnded && _jsxs(_Fragment, { children: [" \u00B7 min increment ", formatCurrency(minBidIncrement, currency)] })] })] }), _jsx(PlaceBidModalButton, { productId: String(product.id), currentBid: currentBid, startingBid: startingBid, minBidIncrement: minBidIncrement, currency: currency, isEnded: isEnded, buyNowPrice: buyNowPrice, bidsHaveStarted: bidsHaveStarted, bidCount: bidCount, tags: tags, onPlaceBid: onPlaceBid, onBuyNow: onBuyNow, triggerClassName: "w-full" })] }) })) : (
129
129
  /* Read-only bid panel — shown when no bid action is wired (preview/demo) */
130
- _jsxs(Stack, { className: `${__P.p5}`, border: "subtle", gap: "md", rounded: "xl", surface: "muted", children: [_jsx(Stack, { gap: "xs", children: _jsxs(Text, { size: "xs", color: "muted", children: ["Starting bid: ", formatCurrency(startingBid, currency), " \u00B7 min increment ", formatCurrency(minBidIncrement, currency)] }) }), _jsxs(Stack, { gap: "sm", children: [_jsx(Input, { type: "number", placeholder: `At least ${formatCurrency(currentBid + minBidIncrement, currency)}`, min: currentBid + minBidIncrement, "aria-label": "Your bid amount", disabled: isEnded }), _jsx(Button, { variant: "primary", size: "md", className: "w-full", disabled: isEnded, children: isEnded ? "Auction Ended" : "Place Bid" }), buyNowPrice !== null && !isEnded && !bidsHaveStarted && (_jsxs(Button, { variant: "secondary", size: "md", className: "w-full", disabled: true, children: ["Buy Now \u2014 ", formatCurrency(buyNowPrice, currency)] }))] }), tags.length > 0 && (_jsx(Div, { border: "default", className: "border-t", padding: "t-md", children: _jsx(Row, { wrap: true, gap: "xs", children: tags.map((tag) => (_jsx(Span, { size: "xs", rounded: "full", padding: "pill-sm-tall", surface: "subtle", color: "muted", children: tag }, tag))) }) }))] })), renderMobileBidForm: () => !isEnded && onPlaceBid ? (_jsxs(Stack, { className: `lg:hidden ${__P.p4}`, border: "subtle", gap: "3", rounded: "xl", surface: "muted", children: [_jsxs(Row, { align: "center", gap: "sm", children: [_jsx(Span, { size: "base", weight: "bold", className: "text-primary-600 dark:text-primary-400", children: formatCurrency(currentBid, currency) }), _jsxs(Span, { size: "xs", color: "muted", children: [bidCount, " bids"] })] }), _jsx(PlaceBidModalButton, { productId: String(product.id), currentBid: currentBid, startingBid: startingBid, minBidIncrement: minBidIncrement, currency: currency, isEnded: isEnded, buyNowPrice: buyNowPrice, bidsHaveStarted: bidsHaveStarted, bidCount: bidCount, tags: tags, onPlaceBid: onPlaceBid, onBuyNow: onBuyNow, triggerClassName: "w-full" })] })) : !isEnded ? (_jsxs(Div, { className: `${__P.p4} lg:hidden`, border: "subtle", rounded: "xl", surface: "muted", children: [_jsxs(Row, { align: "center", gap: "sm", className: "mb-3", children: [_jsx(Span, { size: "base", weight: "bold", className: "text-primary-600 dark:text-primary-400", children: formatCurrency(currentBid, currency) }), _jsxs(Span, { size: "xs", color: "muted", children: [bidCount, " bids"] })] }), _jsx(Button, { variant: "primary", size: "md", className: "w-full", children: "Place Bid" })] })) : null, renderSublistingSection: sublistingCategoryId
130
+ _jsxs(Stack, { className: `${__P.p5}`, border: "subtle", gap: "md", rounded: "xl", surface: "muted", children: [_jsx(Stack, { gap: "xs", children: _jsxs(Text, { size: "xs", color: "muted", children: ["Starting bid: ", formatCurrency(startingBid, currency), !isEnded && _jsxs(_Fragment, { children: [" \u00B7 min increment ", formatCurrency(minBidIncrement, currency)] })] }) }), _jsxs(Stack, { gap: "sm", children: [_jsx(Input, { type: "number", placeholder: `At least ${formatCurrency(currentBid + minBidIncrement, currency)}`, min: currentBid + minBidIncrement, "aria-label": "Your bid amount", disabled: isEnded }), _jsx(Button, { variant: "primary", size: "md", className: "w-full", disabled: isEnded, children: isEnded ? "Auction Ended" : "Place Bid" }), buyNowPrice !== null && !isEnded && !bidsHaveStarted && (_jsxs(Button, { variant: "secondary", size: "md", className: "w-full", disabled: true, children: ["Buy Now \u2014 ", formatCurrency(buyNowPrice, currency)] }))] }), tags.length > 0 && (_jsx(Div, { border: "default", className: "border-t", padding: "t-md", children: _jsx(Row, { wrap: true, gap: "xs", children: tags.map((tag) => (_jsx(Span, { size: "xs", rounded: "full", padding: "pill-sm-tall", surface: "subtle", color: "muted", children: tag }, tag))) }) }))] })), renderMobileBidForm: () => !isEnded && onPlaceBid ? (_jsxs(Stack, { className: `lg:hidden ${__P.p4}`, border: "subtle", gap: "3", rounded: "xl", surface: "muted", children: [_jsxs(Row, { align: "center", gap: "sm", children: [_jsx(Span, { size: "base", weight: "bold", className: "text-primary-600 dark:text-primary-400", children: formatCurrency(currentBid, currency) }), _jsxs(Span, { size: "xs", color: "muted", children: [bidCount, " bids"] })] }), _jsx(PlaceBidModalButton, { productId: String(product.id), currentBid: currentBid, startingBid: startingBid, minBidIncrement: minBidIncrement, currency: currency, isEnded: isEnded, buyNowPrice: buyNowPrice, bidsHaveStarted: bidsHaveStarted, bidCount: bidCount, tags: tags, onPlaceBid: onPlaceBid, onBuyNow: onBuyNow, triggerClassName: "w-full" })] })) : !isEnded ? (_jsxs(Div, { className: `${__P.p4} lg:hidden`, border: "subtle", rounded: "xl", surface: "muted", children: [_jsxs(Row, { align: "center", gap: "sm", className: "mb-3", children: [_jsx(Span, { size: "base", weight: "bold", className: "text-primary-600 dark:text-primary-400", children: formatCurrency(currentBid, currency) }), _jsxs(Span, { size: "xs", color: "muted", children: [bidCount, " bids"] })] }), _jsx(Button, { variant: "primary", size: "md", className: "w-full", children: "Place Bid" })] })) : null, renderSublistingSection: sublistingCategoryId
131
131
  ? () => (_jsx(SublistingCarouselSection, { sublistingCategoryId: sublistingCategoryId, currentListingId: String(product.id) }))
132
132
  : undefined, renderTabs: () => (_jsx(ProductTabsShell, { descriptionContent: descriptionHtml ? (_jsx(RichText, { html: descriptionHtml, proseClass: "prose prose-sm sm:prose max-w-none dark:prose-invert", className: "text-[var(--appkit-color-text-muted)]" })) : undefined, specsContent: specs.length > 0 ? (_jsx(Dl, { divide: "subtle", rounded: "xl", border: "subtle", className: "overflow-hidden", children: specs.map((s, i) => (_jsxs(Row, { gap: "md", oddEven: "zebra", surface: "default", padding: "inline", children: [_jsx(Dt, { className: "w-36 flex-shrink-0", color: "primary", weight: "medium", children: s.name }), _jsxs(Dd, { className: "flex-1", color: "muted", children: [s.value, s.unit ? ` ${s.unit}` : ""] })] }, i))) })) : undefined, customTabs: customSections.map((s) => ({
133
133
  id: s.id,