@mohasinac/appkit 4.10.0 → 4.11.1

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 (324) hide show
  1. package/dist/_internal/client/features/layout/DashboardLayoutClient.d.ts +6 -1
  2. package/dist/_internal/client/features/layout/DashboardLayoutClient.js +4 -1
  3. package/dist/_internal/client/features/lottery/LotteryPullForm.d.ts +1 -1
  4. package/dist/_internal/client/features/lottery/LotteryPullForm.js +2 -2
  5. package/dist/_internal/server/features/admin/daily-digest-actions.d.ts +18 -0
  6. package/dist/_internal/server/features/admin/daily-digest-actions.js +37 -0
  7. package/dist/_internal/server/features/audit-log/actions.d.ts +25 -0
  8. package/dist/_internal/server/features/audit-log/actions.js +38 -0
  9. package/dist/_internal/server/features/brands/actions.js +0 -2
  10. package/dist/_internal/server/features/brands/og.d.ts +4 -1
  11. package/dist/_internal/server/features/brands/og.js +1 -1
  12. package/dist/_internal/server/features/grouped/data.d.ts +4 -0
  13. package/dist/_internal/server/features/grouped/data.js +24 -0
  14. package/dist/_internal/server/features/grouped/index.d.ts +1 -1
  15. package/dist/_internal/server/features/grouped/index.js +1 -1
  16. package/dist/_internal/server/features/orders/actions.js +9 -0
  17. package/dist/_internal/server/features/orders/adapters.js +25 -0
  18. package/dist/_internal/server/features/products/data.d.ts +2 -2
  19. package/dist/_internal/server/features/products/data.js +5 -23
  20. package/dist/_internal/server/functions/scheduled.d.ts +3 -1
  21. package/dist/_internal/server/functions/scheduled.js +17 -1
  22. package/dist/_internal/server/jobs/core/dailyStatusDigest.d.ts +40 -0
  23. package/dist/_internal/server/jobs/core/dailyStatusDigest.js +164 -0
  24. package/dist/_internal/server/jobs/core/hardBanCascade.js +11 -0
  25. package/dist/_internal/server/jobs/core/testerSandboxCleanup.d.ts +1 -0
  26. package/dist/_internal/server/jobs/core/testerSandboxCleanup.js +4 -1
  27. package/dist/_internal/server/jobs/core/testerSandboxRefresh.d.ts +12 -0
  28. package/dist/_internal/server/jobs/core/testerSandboxRefresh.js +85 -0
  29. package/dist/_internal/server/jobs/core/whatsappNotify.js +16 -0
  30. package/dist/_internal/server/jobs/handlers/dailyStatusDigest.d.ts +2 -0
  31. package/dist/_internal/server/jobs/handlers/dailyStatusDigest.js +4 -0
  32. package/dist/_internal/server/jobs/handlers/index.d.ts +2 -0
  33. package/dist/_internal/server/jobs/handlers/index.js +3 -0
  34. package/dist/_internal/server/jobs/handlers/testerSandboxRefresh.d.ts +2 -0
  35. package/dist/_internal/server/jobs/handlers/testerSandboxRefresh.js +2 -0
  36. package/dist/_internal/server/notifications/channel-health.d.ts +27 -0
  37. package/dist/_internal/server/notifications/channel-health.js +101 -0
  38. package/dist/_internal/shared/features/brands/schema.d.ts +0 -6
  39. package/dist/_internal/shared/features/brands/schema.js +0 -1
  40. package/dist/_internal/shared/features/categories/bundle-schemas.d.ts +7 -0
  41. package/dist/_internal/shared/features/categories/bundle-schemas.js +3 -0
  42. package/dist/_internal/shared/features/orders/schema.d.ts +8 -8
  43. package/dist/_internal/shared/features/products/to-product-item.d.ts +25 -0
  44. package/dist/_internal/shared/features/products/to-product-item.js +45 -0
  45. package/dist/client.d.ts +21 -6
  46. package/dist/client.js +13 -3
  47. package/dist/constants/api-endpoints.d.ts +6 -9
  48. package/dist/constants/api-endpoints.js +2 -3
  49. package/dist/contracts/client-auth.d.ts +3 -7
  50. package/dist/contracts/email.d.ts +1 -0
  51. package/dist/features/account/components/AddressForm.js +24 -2
  52. package/dist/features/account/components/BecomeSellerView.d.ts +1 -2
  53. package/dist/features/account/components/BecomeSellerView.js +1 -1
  54. package/dist/features/account/components/NotificationBell.js +1 -1
  55. package/dist/features/account/components/UserBidsView.d.ts +1 -0
  56. package/dist/features/account/components/UserBidsView.js +47 -0
  57. package/dist/features/account/components/UserOrdersView.d.ts +4 -11
  58. package/dist/features/account/components/UserOrdersView.js +54 -4
  59. package/dist/features/account/components/UserReturnsView.d.ts +4 -9
  60. package/dist/features/account/components/UserReturnsView.js +37 -3
  61. package/dist/features/account/components/index.d.ts +3 -2
  62. package/dist/features/account/components/index.js +1 -0
  63. package/dist/features/admin/actions/admin-actions.js +22 -0
  64. package/dist/features/admin/actions/admin-coupon-actions.js +10 -0
  65. package/dist/features/admin/actions/notification-actions.js +4 -3
  66. package/dist/features/admin/components/AdminAdsView.d.ts +2 -3
  67. package/dist/features/admin/components/AdminAdsView.js +86 -89
  68. package/dist/features/admin/components/AdminAllEventEntriesView.js +18 -3
  69. package/dist/features/admin/components/AdminAuditLogView.d.ts +11 -0
  70. package/dist/features/admin/components/AdminAuditLogView.js +95 -0
  71. package/dist/features/admin/components/AdminBlogEditorView.js +37 -3
  72. package/dist/features/admin/components/AdminBrandEditorView.js +10 -6
  73. package/dist/features/admin/components/AdminBundleEditorView.js +18 -2
  74. package/dist/features/admin/components/AdminCarouselEditorView.js +2 -2
  75. package/dist/features/admin/components/AdminCarouselGroupEditorView.js +2 -2
  76. package/dist/features/admin/components/AdminCartsView.js +34 -4
  77. package/dist/features/admin/components/AdminCategoryEditorView.js +5 -4
  78. package/dist/features/admin/components/AdminCouponEditorView.js +2 -2
  79. package/dist/features/admin/components/AdminFaqEditorView.js +2 -2
  80. package/dist/features/admin/components/AdminHistoryView.js +11 -1
  81. package/dist/features/admin/components/AdminNotificationsView.js +27 -1
  82. package/dist/features/admin/components/AdminOrderEditorView.d.ts +10 -1
  83. package/dist/features/admin/components/AdminOrderEditorView.js +4 -3
  84. package/dist/features/admin/components/AdminOrdersView.js +92 -24
  85. package/dist/features/admin/components/AdminPayoutMarkPaidModal.d.ts +7 -0
  86. package/dist/features/admin/components/AdminPayoutMarkPaidModal.js +47 -0
  87. package/dist/features/admin/components/AdminPayoutsView.js +15 -31
  88. package/dist/features/admin/components/AdminProductEditorView.js +26 -3
  89. package/dist/features/admin/components/AdminSiteSettingsView.js +20 -1
  90. package/dist/features/admin/components/AdminStoreAddressesView.js +22 -2
  91. package/dist/features/admin/components/AdminStoresView.js +7 -0
  92. package/dist/features/admin/components/AdminSublistingCategoryEditorView.js +2 -2
  93. package/dist/features/admin/components/AdminTesterChecklistItemEditorView.js +2 -2
  94. package/dist/features/admin/components/AdminUserEditorView.d.ts +2 -2
  95. package/dist/features/admin/components/AdminUserEditorView.js +4 -2
  96. package/dist/features/admin/components/AdminUsersView.js +1 -1
  97. package/dist/features/admin/components/AdminWishlistsView.js +14 -1
  98. package/dist/features/admin/components/DataListingView.d.ts +2 -2
  99. package/dist/features/admin/components/ViewAuditLogEntryModal.d.ts +19 -0
  100. package/dist/features/admin/components/ViewAuditLogEntryModal.js +22 -0
  101. package/dist/features/admin/components/ViewNotificationModal.d.ts +21 -0
  102. package/dist/features/admin/components/ViewNotificationModal.js +15 -0
  103. package/dist/features/admin/components/analytics/AdminPageViewsReportView.d.ts +4 -7
  104. package/dist/features/admin/components/analytics/AdminPageViewsReportView.js +56 -36
  105. package/dist/features/admin/components/index.d.ts +8 -1
  106. package/dist/features/admin/components/index.js +4 -0
  107. package/dist/features/admin/constants/filter-tabs.d.ts +49 -0
  108. package/dist/features/admin/constants/filter-tabs.js +27 -0
  109. package/dist/features/admin/hooks/useAdminListing.d.ts +6 -0
  110. package/dist/features/admin/hooks/useAdminListing.js +3 -1
  111. package/dist/features/admin/hooks/useAdminListingData.d.ts +9 -1
  112. package/dist/features/admin/hooks/useAdminListingData.js +9 -2
  113. package/dist/features/admin/repository/site-settings.repository.d.ts +5 -0
  114. package/dist/features/admin/schemas/firestore.d.ts +12 -0
  115. package/dist/features/analytics/types.d.ts +1 -1
  116. package/dist/features/analytics/types.js +8 -0
  117. package/dist/features/auctions/schemas/index.d.ts +5 -0
  118. package/dist/features/audit-log/repository/audit-log.repository.d.ts +45 -0
  119. package/dist/features/audit-log/repository/audit-log.repository.js +30 -0
  120. package/dist/features/audit-log/schemas/firestore.d.ts +59 -0
  121. package/dist/features/audit-log/schemas/firestore.js +35 -0
  122. package/dist/features/auth/actions/index.d.ts +0 -1
  123. package/dist/features/auth/actions/index.js +0 -1
  124. package/dist/features/auth/hooks/useAuth.d.ts +8 -35
  125. package/dist/features/auth/hooks/useAuth.js +21 -37
  126. package/dist/features/auth/permissions/constants.d.ts +1 -1
  127. package/dist/features/auth/permissions/constants.js +2 -0
  128. package/dist/features/blog/schemas/index.d.ts +32 -0
  129. package/dist/features/catalogue/components/AdminCatalogueApprovalsView.js +31 -3
  130. package/dist/features/catalogue/components/CatalogueItemEditorView.js +2 -1
  131. package/dist/features/categories/components/BrandDetailPageView.js +14 -12
  132. package/dist/features/categories/components/CategoryDetailPageView.js +7 -3
  133. package/dist/features/categories/components/CategoryHighlightsAndFaqSection.d.ts +14 -0
  134. package/dist/features/categories/components/CategoryHighlightsAndFaqSection.js +22 -0
  135. package/dist/features/categories/components/CategoryProductsView.d.ts +1 -5
  136. package/dist/features/categories/components/CategoryProductsView.js +1 -1
  137. package/dist/features/categories/components/CategorySelectorCreate.js +16 -2
  138. package/dist/features/categories/schemas/firestore.d.ts +19 -2
  139. package/dist/features/categories/schemas/index.d.ts +24 -3
  140. package/dist/features/categories/schemas/index.js +3 -1
  141. package/dist/features/categories/types/index.d.ts +7 -0
  142. package/dist/features/checkout/actions/checkout-value-otp-actions.d.ts +17 -3
  143. package/dist/features/checkout/actions/checkout-value-otp-actions.js +101 -22
  144. package/dist/features/contact/components/ContactForm.js +17 -4
  145. package/dist/features/contact/email.d.ts +1 -0
  146. package/dist/features/contact/email.js +3 -0
  147. package/dist/features/events/components/AdminEventEditorView.js +54 -2
  148. package/dist/features/events/components/AdminEventEntriesView.d.ts +2 -16
  149. package/dist/features/events/components/AdminEventEntriesView.js +56 -89
  150. package/dist/features/events/components/EventFormDrawer.js +5 -1
  151. package/dist/features/events/components/EventRaffleEntryForm.d.ts +1 -3
  152. package/dist/features/events/components/EventRaffleEntryForm.js +1 -1
  153. package/dist/features/events/schemas/index.d.ts +22 -0
  154. package/dist/features/grouped/components/GroupedListingsCarousel.js +4 -1
  155. package/dist/features/grouped/repository/grouped-listings.repository.d.ts +4 -0
  156. package/dist/features/grouped/repository/grouped-listings.repository.js +18 -0
  157. package/dist/features/homepage/components/CharacterHotspotForm.js +202 -187
  158. package/dist/features/homepage/components/SectionCarousel.d.ts +1 -7
  159. package/dist/features/homepage/components/SectionCarousel.js +1 -1
  160. package/dist/features/layout/TitleBarLayout.d.ts +1 -1
  161. package/dist/features/layout/TitleBarLayout.js +3 -3
  162. package/dist/features/media/MediaImage.js +39 -3
  163. package/dist/features/media/types/index.d.ts +10 -0
  164. package/dist/features/media/types/index.js +1 -0
  165. package/dist/features/media/upload/MediaUploadField.d.ts +7 -1
  166. package/dist/features/media/upload/MediaUploadField.js +11 -7
  167. package/dist/features/media/upload/video-poster.d.ts +2 -0
  168. package/dist/features/media/upload/video-poster.js +16 -1
  169. package/dist/features/media/upload/video-poster.test.js +1 -1
  170. package/dist/features/orders/components/OrderStatusTimeline.d.ts +12 -0
  171. package/dist/features/orders/components/OrderStatusTimeline.js +55 -0
  172. package/dist/features/orders/components/index.d.ts +2 -0
  173. package/dist/features/orders/components/index.js +1 -0
  174. package/dist/features/orders/constants/payment-window.d.ts +25 -0
  175. package/dist/features/orders/constants/payment-window.js +25 -0
  176. package/dist/features/orders/repository/orders.repository.d.ts +32 -0
  177. package/dist/features/orders/repository/orders.repository.js +56 -0
  178. package/dist/features/orders/schemas/index.d.ts +24 -24
  179. package/dist/features/orders/types/index.d.ts +33 -0
  180. package/dist/features/pre-orders/schemas/index.d.ts +5 -0
  181. package/dist/features/products/components/BidHistory.js +9 -1
  182. package/dist/features/products/components/GroupSettingsPanel.js +25 -3
  183. package/dist/features/products/components/PrizeDrawDetailPageView.js +1 -1
  184. package/dist/features/products/components/PrizeDrawEntryActions.d.ts +1 -4
  185. package/dist/features/products/components/PrizeDrawEntryActions.js +1 -1
  186. package/dist/features/products/components/ProductDetailActions.d.ts +1 -2
  187. package/dist/features/products/components/ProductDetailActions.js +1 -1
  188. package/dist/features/products/components/ProductGrid.js +15 -1
  189. package/dist/features/products/constants/action-defs.d.ts +1 -0
  190. package/dist/features/products/constants/action-defs.js +1 -0
  191. package/dist/features/products/schemas/index.d.ts +5 -0
  192. package/dist/features/promotions/components/CouponsIndexListing.d.ts +1 -3
  193. package/dist/features/promotions/components/CouponsIndexListing.js +1 -1
  194. package/dist/features/search/components/SearchView.d.ts +1 -3
  195. package/dist/features/search/components/SearchView.js +1 -1
  196. package/dist/features/seller/components/QuickProductForm.js +2 -1
  197. package/dist/features/seller/components/SellerBidsView.js +66 -107
  198. package/dist/features/seller/components/SellerGoogleReviewsView.js +17 -1
  199. package/dist/features/seller/components/SellerOrdersView.d.ts +15 -3
  200. package/dist/features/seller/components/SellerOrdersView.js +134 -163
  201. package/dist/features/seller/components/SellerPayoutMethodsView.js +60 -48
  202. package/dist/features/seller/components/SellerPayoutSettingsView.js +69 -4
  203. package/dist/features/seller/components/SellerPayoutsView.d.ts +12 -0
  204. package/dist/features/seller/components/SellerPayoutsView.js +25 -5
  205. package/dist/features/seller/components/SellerProductShell.d.ts +10 -3
  206. package/dist/features/seller/components/SellerProductShell.js +89 -36
  207. package/dist/features/seller/components/SellerProductsFilterDrawer.d.ts +2 -7
  208. package/dist/features/seller/components/SellerProductsFilterDrawer.js +4 -4
  209. package/dist/features/seller/components/SellerProductsView.d.ts +2 -4
  210. package/dist/features/seller/components/SellerProductsView.js +131 -189
  211. package/dist/features/seller/components/SellerReviewsView.js +95 -133
  212. package/dist/features/seller/components/SellerShippingConfigsView.js +47 -67
  213. package/dist/features/seller/components/SellerShippingView.js +41 -4
  214. package/dist/features/seller/components/SellerStoreCategoriesView.js +51 -59
  215. package/dist/features/seller/components/SellerStorefrontView.d.ts +6 -1
  216. package/dist/features/seller/components/SellerStorefrontView.js +42 -5
  217. package/dist/features/seller/components/index.d.ts +2 -2
  218. package/dist/features/seller/components/index.js +2 -2
  219. package/dist/features/shell/FormShell.d.ts +24 -4
  220. package/dist/features/shell/FormShell.js +12 -6
  221. package/dist/features/shell/QuickFormDrawer.js +67 -12
  222. package/dist/features/shell/StepForm.d.ts +30 -1
  223. package/dist/features/shell/StepForm.js +47 -4
  224. package/dist/features/shell/index.d.ts +1 -1
  225. package/dist/features/shell/index.js +1 -1
  226. package/dist/features/shipments/components/AdminShipmentEditorView.js +2 -2
  227. package/dist/features/shipments/schemas/validation.d.ts +14 -4
  228. package/dist/features/tester/components/AdminTesterFeedbackListView.js +23 -2
  229. package/dist/features/tester/components/TesterChecklistStepRow.js +1 -1
  230. package/dist/features/tester/components/TesterHubView.js +3 -2
  231. package/dist/features/tester/seed-data/orders-tester-seed-data.js +9 -1
  232. package/dist/features/tester/seed-data/products-tester-seed-data.js +43 -32
  233. package/dist/features/tester/seed-data/tester-checklist-seed-data.js +404 -15
  234. package/dist/index.d.ts +15 -3
  235. package/dist/index.js +22 -5
  236. package/dist/next/routing/route-map.d.ts +11 -0
  237. package/dist/next/routing/route-map.js +5 -0
  238. package/dist/providers/email-resend/provider.js +1 -0
  239. package/dist/providers/firebase-client/auth.d.ts +1 -0
  240. package/dist/providers/firebase-client/auth.js +7 -1
  241. package/dist/repositories/index.d.ts +1 -0
  242. package/dist/repositories/index.js +1 -0
  243. package/dist/schemas/registry.d.ts +61 -25
  244. package/dist/security/pii-mask.js +1 -1
  245. package/dist/seed/categories-seed-data.js +69 -0
  246. package/dist/seed/products-auctions-seed-data.js +11 -0
  247. package/dist/seed/products-standard-seed-data.js +93 -4
  248. package/dist/seed/site-settings-seed-data.js +13 -1
  249. package/dist/seed/store-extensions-seed-data.js +1 -1
  250. package/dist/server-entry.d.ts +2 -1
  251. package/dist/server-entry.js +20 -2
  252. package/dist/server.d.ts +7 -2
  253. package/dist/server.js +8 -3
  254. package/dist/styles.css +1 -1
  255. package/dist/tailwind-utilities.css +1 -1
  256. package/dist/ui/components/Form.d.ts +11 -2
  257. package/dist/ui/components/Form.js +4 -3
  258. package/dist/ui/components/FormField.d.ts +3 -1
  259. package/dist/ui/components/FormField.js +22 -4
  260. package/dist/ui/components/ListingLayout.d.ts +1 -2
  261. package/dist/ui/components/ListingLayout.js +1 -1
  262. package/dist/ui/components/RecordDetailModal.d.ts +68 -0
  263. package/dist/ui/components/RecordDetailModal.js +8 -0
  264. package/dist/ui/components/SlottedListingView.js +9 -1
  265. package/dist/ui/forms/FormErrorSummary.d.ts +25 -0
  266. package/dist/ui/forms/FormErrorSummary.js +37 -0
  267. package/dist/ui/forms/FormShell.d.ts +37 -2
  268. package/dist/ui/forms/FormShell.js +37 -7
  269. package/dist/ui/forms/FormShell.style.css +17 -0
  270. package/dist/ui/forms/index.d.ts +2 -0
  271. package/dist/ui/forms/index.js +1 -0
  272. package/dist/ui/index.d.ts +4 -0
  273. package/dist/ui/index.js +5 -0
  274. package/dist/utils/media-url.d.ts +3 -1
  275. package/dist/utils/media-url.js +20 -1
  276. package/dist/validation/schemas.d.ts +4 -4
  277. package/package.json +1 -1
  278. package/dist/_internal/client/features/classified/ClassifiedDetailView.d.ts +0 -9
  279. package/dist/_internal/client/features/classified/ClassifiedDetailView.js +0 -55
  280. package/dist/_internal/client/features/digital-code/DigitalCodeDetailView.d.ts +0 -18
  281. package/dist/_internal/client/features/digital-code/DigitalCodeDetailView.js +0 -21
  282. package/dist/_internal/client/features/live/LiveItemDetailView.d.ts +0 -9
  283. package/dist/_internal/client/features/live/LiveItemDetailView.js +0 -25
  284. package/dist/_internal/server/jobs/core/prizeRevealClose.d.ts +0 -2
  285. package/dist/_internal/server/jobs/core/prizeRevealClose.js +0 -28
  286. package/dist/_internal/server/jobs/core/prizeRevealExpiry.d.ts +0 -2
  287. package/dist/_internal/server/jobs/core/prizeRevealExpiry.js +0 -61
  288. package/dist/_internal/server/jobs/core/prizeRevealOpen.d.ts +0 -2
  289. package/dist/_internal/server/jobs/core/prizeRevealOpen.js +0 -68
  290. package/dist/_internal/server/jobs/core/prizeRevealReminder.d.ts +0 -2
  291. package/dist/_internal/server/jobs/core/prizeRevealReminder.js +0 -49
  292. package/dist/_internal/server/jobs/handlers/prizeRevealClose.d.ts +0 -2
  293. package/dist/_internal/server/jobs/handlers/prizeRevealClose.js +0 -2
  294. package/dist/_internal/server/jobs/handlers/prizeRevealExpiry.d.ts +0 -2
  295. package/dist/_internal/server/jobs/handlers/prizeRevealExpiry.js +0 -2
  296. package/dist/_internal/server/jobs/handlers/prizeRevealOpen.d.ts +0 -2
  297. package/dist/_internal/server/jobs/handlers/prizeRevealOpen.js +0 -2
  298. package/dist/_internal/server/jobs/handlers/prizeRevealReminder.d.ts +0 -2
  299. package/dist/_internal/server/jobs/handlers/prizeRevealReminder.js +0 -2
  300. package/dist/features/auth/consent-otp.d.ts +0 -57
  301. package/dist/features/auth/consent-otp.js +0 -127
  302. package/dist/features/cart/components/CheckoutOtpModal.d.ts +0 -15
  303. package/dist/features/cart/components/CheckoutOtpModal.js +0 -12
  304. package/dist/features/checkout/actions/checkout-actions.d.ts +0 -31
  305. package/dist/features/checkout/actions/checkout-actions.js +0 -124
  306. package/dist/features/classified/components/ClassifiedIndexListing.d.ts +0 -5
  307. package/dist/features/classified/components/ClassifiedIndexListing.js +0 -123
  308. package/dist/features/classified/components/ClassifiedListView.d.ts +0 -6
  309. package/dist/features/classified/components/ClassifiedListView.js +0 -44
  310. package/dist/features/digital-codes/components/DigitalCodesIndexListing.d.ts +0 -5
  311. package/dist/features/digital-codes/components/DigitalCodesIndexListing.js +0 -114
  312. package/dist/features/digital-codes/components/DigitalCodesListView.d.ts +0 -6
  313. package/dist/features/digital-codes/components/DigitalCodesListView.js +0 -38
  314. package/dist/features/live/components/LiveItemsIndexListing.d.ts +0 -5
  315. package/dist/features/live/components/LiveItemsIndexListing.js +0 -115
  316. package/dist/features/live/components/LiveItemsListView.d.ts +0 -6
  317. package/dist/features/live/components/LiveItemsListView.js +0 -44
  318. package/dist/features/seller/components/SellerTemplatesView.d.ts +0 -12
  319. package/dist/features/seller/components/SellerTemplatesView.js +0 -276
  320. package/dist/react/hooks/useVisibleItems.d.ts +0 -37
  321. package/dist/react/hooks/useVisibleItems.js +0 -59
  322. package/dist/ui/components/TabStrip.d.ts +0 -34
  323. package/dist/ui/components/TabStrip.js +0 -56
  324. package/dist/ui/components/TabStrip.style.css +0 -97
@@ -28,6 +28,7 @@ export declare const blogPostFirestoreSchema: z.ZodObject<{
28
28
  thumbnailUrl: z.ZodOptional<z.ZodString>;
29
29
  source: z.ZodOptional<z.ZodEnum<["upload", "youtube", "external"]>>;
30
30
  youtubeId: z.ZodOptional<z.ZodString>;
31
+ duration: z.ZodOptional<z.ZodNumber>;
31
32
  }, "strip", z.ZodTypeAny, {
32
33
  type: "video" | "image" | "file";
33
34
  url: string;
@@ -35,6 +36,7 @@ export declare const blogPostFirestoreSchema: z.ZodObject<{
35
36
  alt?: string | undefined;
36
37
  thumbnailUrl?: string | undefined;
37
38
  youtubeId?: string | undefined;
39
+ duration?: number | undefined;
38
40
  }, {
39
41
  type: "video" | "image" | "file";
40
42
  url: string;
@@ -42,6 +44,7 @@ export declare const blogPostFirestoreSchema: z.ZodObject<{
42
44
  alt?: string | undefined;
43
45
  thumbnailUrl?: string | undefined;
44
46
  youtubeId?: string | undefined;
47
+ duration?: number | undefined;
45
48
  }>>;
46
49
  contentImages: z.ZodOptional<z.ZodArray<z.ZodObject<{
47
50
  url: z.ZodString;
@@ -50,6 +53,7 @@ export declare const blogPostFirestoreSchema: z.ZodObject<{
50
53
  thumbnailUrl: z.ZodOptional<z.ZodString>;
51
54
  source: z.ZodOptional<z.ZodEnum<["upload", "youtube", "external"]>>;
52
55
  youtubeId: z.ZodOptional<z.ZodString>;
56
+ duration: z.ZodOptional<z.ZodNumber>;
53
57
  }, "strip", z.ZodTypeAny, {
54
58
  type: "video" | "image" | "file";
55
59
  url: string;
@@ -57,6 +61,7 @@ export declare const blogPostFirestoreSchema: z.ZodObject<{
57
61
  alt?: string | undefined;
58
62
  thumbnailUrl?: string | undefined;
59
63
  youtubeId?: string | undefined;
64
+ duration?: number | undefined;
60
65
  }, {
61
66
  type: "video" | "image" | "file";
62
67
  url: string;
@@ -64,6 +69,7 @@ export declare const blogPostFirestoreSchema: z.ZodObject<{
64
69
  alt?: string | undefined;
65
70
  thumbnailUrl?: string | undefined;
66
71
  youtubeId?: string | undefined;
72
+ duration?: number | undefined;
67
73
  }>, "many">>;
68
74
  additionalImages: z.ZodOptional<z.ZodArray<z.ZodObject<{
69
75
  url: z.ZodString;
@@ -72,6 +78,7 @@ export declare const blogPostFirestoreSchema: z.ZodObject<{
72
78
  thumbnailUrl: z.ZodOptional<z.ZodString>;
73
79
  source: z.ZodOptional<z.ZodEnum<["upload", "youtube", "external"]>>;
74
80
  youtubeId: z.ZodOptional<z.ZodString>;
81
+ duration: z.ZodOptional<z.ZodNumber>;
75
82
  }, "strip", z.ZodTypeAny, {
76
83
  type: "video" | "image" | "file";
77
84
  url: string;
@@ -79,6 +86,7 @@ export declare const blogPostFirestoreSchema: z.ZodObject<{
79
86
  alt?: string | undefined;
80
87
  thumbnailUrl?: string | undefined;
81
88
  youtubeId?: string | undefined;
89
+ duration?: number | undefined;
82
90
  }, {
83
91
  type: "video" | "image" | "file";
84
92
  url: string;
@@ -86,6 +94,7 @@ export declare const blogPostFirestoreSchema: z.ZodObject<{
86
94
  alt?: string | undefined;
87
95
  thumbnailUrl?: string | undefined;
88
96
  youtubeId?: string | undefined;
97
+ duration?: number | undefined;
89
98
  }>, "many">>;
90
99
  category: z.ZodString;
91
100
  tags: z.ZodArray<z.ZodString, "many">;
@@ -135,6 +144,7 @@ export declare const blogPostFirestoreSchema: z.ZodObject<{
135
144
  alt?: string | undefined;
136
145
  thumbnailUrl?: string | undefined;
137
146
  youtubeId?: string | undefined;
147
+ duration?: number | undefined;
138
148
  } | undefined;
139
149
  additionalImages?: {
140
150
  type: "video" | "image" | "file";
@@ -143,6 +153,7 @@ export declare const blogPostFirestoreSchema: z.ZodObject<{
143
153
  alt?: string | undefined;
144
154
  thumbnailUrl?: string | undefined;
145
155
  youtubeId?: string | undefined;
156
+ duration?: number | undefined;
146
157
  }[] | undefined;
147
158
  metaTitle?: string | undefined;
148
159
  metaDescription?: string | undefined;
@@ -153,6 +164,7 @@ export declare const blogPostFirestoreSchema: z.ZodObject<{
153
164
  alt?: string | undefined;
154
165
  thumbnailUrl?: string | undefined;
155
166
  youtubeId?: string | undefined;
167
+ duration?: number | undefined;
156
168
  }[] | undefined;
157
169
  authorAvatar?: string | undefined;
158
170
  }, {
@@ -185,6 +197,7 @@ export declare const blogPostFirestoreSchema: z.ZodObject<{
185
197
  alt?: string | undefined;
186
198
  thumbnailUrl?: string | undefined;
187
199
  youtubeId?: string | undefined;
200
+ duration?: number | undefined;
188
201
  } | undefined;
189
202
  additionalImages?: {
190
203
  type: "video" | "image" | "file";
@@ -193,6 +206,7 @@ export declare const blogPostFirestoreSchema: z.ZodObject<{
193
206
  alt?: string | undefined;
194
207
  thumbnailUrl?: string | undefined;
195
208
  youtubeId?: string | undefined;
209
+ duration?: number | undefined;
196
210
  }[] | undefined;
197
211
  metaTitle?: string | undefined;
198
212
  metaDescription?: string | undefined;
@@ -203,6 +217,7 @@ export declare const blogPostFirestoreSchema: z.ZodObject<{
203
217
  alt?: string | undefined;
204
218
  thumbnailUrl?: string | undefined;
205
219
  youtubeId?: string | undefined;
220
+ duration?: number | undefined;
206
221
  }[] | undefined;
207
222
  authorAvatar?: string | undefined;
208
223
  }>;
@@ -235,6 +250,7 @@ export declare const blogPostSchema: z.ZodObject<{
235
250
  thumbnailUrl: z.ZodOptional<z.ZodString>;
236
251
  source: z.ZodOptional<z.ZodEnum<["upload", "youtube", "external"]>>;
237
252
  youtubeId: z.ZodOptional<z.ZodString>;
253
+ duration: z.ZodOptional<z.ZodNumber>;
238
254
  }, "strip", z.ZodTypeAny, {
239
255
  type: "video" | "image" | "file";
240
256
  url: string;
@@ -242,6 +258,7 @@ export declare const blogPostSchema: z.ZodObject<{
242
258
  alt?: string | undefined;
243
259
  thumbnailUrl?: string | undefined;
244
260
  youtubeId?: string | undefined;
261
+ duration?: number | undefined;
245
262
  }, {
246
263
  type: "video" | "image" | "file";
247
264
  url: string;
@@ -249,6 +266,7 @@ export declare const blogPostSchema: z.ZodObject<{
249
266
  alt?: string | undefined;
250
267
  thumbnailUrl?: string | undefined;
251
268
  youtubeId?: string | undefined;
269
+ duration?: number | undefined;
252
270
  }>, z.ZodEffects<z.ZodString, {
253
271
  url: string;
254
272
  type: "image";
@@ -259,6 +277,7 @@ export declare const blogPostSchema: z.ZodObject<{
259
277
  alt?: string | undefined;
260
278
  thumbnailUrl?: string | undefined;
261
279
  youtubeId?: string | undefined;
280
+ duration?: number | undefined;
262
281
  } | {
263
282
  url: string;
264
283
  type: "image";
@@ -269,6 +288,7 @@ export declare const blogPostSchema: z.ZodObject<{
269
288
  alt?: string | undefined;
270
289
  thumbnailUrl?: string | undefined;
271
290
  youtubeId?: string | undefined;
291
+ duration?: number | undefined;
272
292
  } | null | undefined>;
273
293
  contentImages: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodObject<{
274
294
  url: z.ZodString;
@@ -277,6 +297,7 @@ export declare const blogPostSchema: z.ZodObject<{
277
297
  thumbnailUrl: z.ZodOptional<z.ZodString>;
278
298
  source: z.ZodOptional<z.ZodEnum<["upload", "youtube", "external"]>>;
279
299
  youtubeId: z.ZodOptional<z.ZodString>;
300
+ duration: z.ZodOptional<z.ZodNumber>;
280
301
  }, "strip", z.ZodTypeAny, {
281
302
  type: "video" | "image" | "file";
282
303
  url: string;
@@ -284,6 +305,7 @@ export declare const blogPostSchema: z.ZodObject<{
284
305
  alt?: string | undefined;
285
306
  thumbnailUrl?: string | undefined;
286
307
  youtubeId?: string | undefined;
308
+ duration?: number | undefined;
287
309
  }, {
288
310
  type: "video" | "image" | "file";
289
311
  url: string;
@@ -291,6 +313,7 @@ export declare const blogPostSchema: z.ZodObject<{
291
313
  alt?: string | undefined;
292
314
  thumbnailUrl?: string | undefined;
293
315
  youtubeId?: string | undefined;
316
+ duration?: number | undefined;
294
317
  }>, "many">>>;
295
318
  additionalImages: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodObject<{
296
319
  url: z.ZodString;
@@ -299,6 +322,7 @@ export declare const blogPostSchema: z.ZodObject<{
299
322
  thumbnailUrl: z.ZodOptional<z.ZodString>;
300
323
  source: z.ZodOptional<z.ZodEnum<["upload", "youtube", "external"]>>;
301
324
  youtubeId: z.ZodOptional<z.ZodString>;
325
+ duration: z.ZodOptional<z.ZodNumber>;
302
326
  }, "strip", z.ZodTypeAny, {
303
327
  type: "video" | "image" | "file";
304
328
  url: string;
@@ -306,6 +330,7 @@ export declare const blogPostSchema: z.ZodObject<{
306
330
  alt?: string | undefined;
307
331
  thumbnailUrl?: string | undefined;
308
332
  youtubeId?: string | undefined;
333
+ duration?: number | undefined;
309
334
  }, {
310
335
  type: "video" | "image" | "file";
311
336
  url: string;
@@ -313,6 +338,7 @@ export declare const blogPostSchema: z.ZodObject<{
313
338
  alt?: string | undefined;
314
339
  thumbnailUrl?: string | undefined;
315
340
  youtubeId?: string | undefined;
341
+ duration?: number | undefined;
316
342
  }>, "many">>>;
317
343
  category: z.ZodEnum<["news", "tips", "guides", "updates", "community"]>;
318
344
  tags: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
@@ -341,6 +367,7 @@ export declare const blogPostSchema: z.ZodObject<{
341
367
  alt?: string | undefined;
342
368
  thumbnailUrl?: string | undefined;
343
369
  youtubeId?: string | undefined;
370
+ duration?: number | undefined;
344
371
  } | {
345
372
  url: string;
346
373
  type: "image";
@@ -352,6 +379,7 @@ export declare const blogPostSchema: z.ZodObject<{
352
379
  alt?: string | undefined;
353
380
  thumbnailUrl?: string | undefined;
354
381
  youtubeId?: string | undefined;
382
+ duration?: number | undefined;
355
383
  }[];
356
384
  contentImages: {
357
385
  type: "video" | "image" | "file";
@@ -360,6 +388,7 @@ export declare const blogPostSchema: z.ZodObject<{
360
388
  alt?: string | undefined;
361
389
  thumbnailUrl?: string | undefined;
362
390
  youtubeId?: string | undefined;
391
+ duration?: number | undefined;
363
392
  }[];
364
393
  content?: string | undefined;
365
394
  createdAt?: string | undefined;
@@ -395,6 +424,7 @@ export declare const blogPostSchema: z.ZodObject<{
395
424
  alt?: string | undefined;
396
425
  thumbnailUrl?: string | undefined;
397
426
  youtubeId?: string | undefined;
427
+ duration?: number | undefined;
398
428
  } | null | undefined;
399
429
  authorId?: string | undefined;
400
430
  authorName?: string | undefined;
@@ -407,6 +437,7 @@ export declare const blogPostSchema: z.ZodObject<{
407
437
  alt?: string | undefined;
408
438
  thumbnailUrl?: string | undefined;
409
439
  youtubeId?: string | undefined;
440
+ duration?: number | undefined;
410
441
  }[] | undefined;
411
442
  metaTitle?: string | undefined;
412
443
  metaDescription?: string | undefined;
@@ -417,6 +448,7 @@ export declare const blogPostSchema: z.ZodObject<{
417
448
  alt?: string | undefined;
418
449
  thumbnailUrl?: string | undefined;
419
450
  youtubeId?: string | undefined;
451
+ duration?: number | undefined;
420
452
  }[] | undefined;
421
453
  authorAvatar?: string | undefined;
422
454
  }>;
@@ -4,15 +4,24 @@ import React from "react";
4
4
  import { sortBy } from "@mohasinac/appkit/client";
5
5
  import { useQueryClient } from "@tanstack/react-query";
6
6
  import { useApiMutation } from "@mohasinac/appkit/client";
7
- import { Button, Modal, Row, Stack, Text, Textarea } from "../../../ui";
7
+ import { Button, Modal, RecordDetailModal, Row, Stack, Text, Textarea } from "../../../ui";
8
+ import { formatCurrency } from "../../../utils/number.formatter";
8
9
  import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
9
10
  import { apiClient } from "../../../http";
10
11
  import { toRecordArray, toRelativeDate, toStringValue, } from "../../admin/hooks/useAdminListingData";
11
12
  import { DataListingView } from "../../admin/components/DataListingView";
13
+ /** The submitted item's photos — the whole point of a visual approval. */
14
+ function toPhotoEntries(raw) {
15
+ const images = Array.isArray(raw.images) ? raw.images : [];
16
+ return images
17
+ .filter((i) => typeof i === "string")
18
+ .map((url, i) => ({ image: url, title: `Photo ${i + 1}` }));
19
+ }
12
20
  export function AdminCatalogueApprovalsView() {
13
21
  const queryClient = useQueryClient();
14
22
  const [rejectTarget, setRejectTarget] = React.useState(null);
15
23
  const [rejectReason, setRejectReason] = React.useState("");
24
+ const [selected, setSelected] = React.useState(null);
16
25
  const invalidate = () => queryClient.invalidateQueries({ queryKey: ["admin", "catalogue-approvals"] });
17
26
  const approveMutation = useApiMutation({
18
27
  successMessage: "Approved and listed",
@@ -44,10 +53,29 @@ export function AdminCatalogueApprovalsView() {
44
53
  secondary: toStringValue(item.ownerId, ""),
45
54
  status: toStringValue(item.listingStatus, "pending_admin_approval"),
46
55
  updatedAt: toRelativeDate(item.submittedForApprovalAt ?? item.createdAt),
56
+ _raw: item,
47
57
  })),
48
58
  getTotal: (response, mappedRows) => (typeof response.meta?.total === "number" ? response.meta.total : mappedRows.length),
49
59
  buildFilters: () => undefined,
50
- renderRowActions: (row) => (_jsxs(Row, { gap: "sm", children: [_jsx(Button, { size: "sm", isLoading: approveMutation.isPending, onClick: () => approveMutation.mutate(row.id), children: "Approve" }), _jsx(Button, { size: "sm", variant: "danger", onClick: () => setRejectTarget(row.id), children: "Reject" })] })),
60
+ // Approve/Reject used to be the ONLY row affordance an admin was asked
61
+ // to accept or refuse a submission whose photos, description and price
62
+ // were never rendered anywhere. "View" opens those first.
63
+ onRowClick: (row) => setSelected(row),
64
+ renderRowActions: (row) => (_jsxs(Row, { gap: "sm", children: [_jsx(Button, { size: "sm", variant: "outline", onClick: () => setSelected(row), children: "View" }), _jsx(Button, { size: "sm", isLoading: approveMutation.isPending, onClick: () => approveMutation.mutate(row.id), children: "Approve" }), _jsx(Button, { size: "sm", variant: "danger", onClick: () => setRejectTarget(row.id), children: "Reject" })] })),
51
65
  };
52
- return (_jsxs(_Fragment, { children: [_jsx(DataListingView, { config: config }), _jsx(Modal, { isOpen: !!rejectTarget, onClose: () => setRejectTarget(null), title: "Reject this catalogue listing?", size: "sm", children: _jsxs(Stack, { gap: "md", children: [_jsx(Text, { variant: "secondary", children: "The owner will see this reason." }), _jsx(Textarea, { value: rejectReason, onChange: (e) => setRejectReason(e.target.value), rows: 3, placeholder: "Reason for rejection" }), _jsx(Button, { isLoading: rejectMutation.isPending, disabled: !rejectReason.trim(), variant: "danger", onClick: () => rejectMutation.mutate(), children: "Reject" })] }) })] }));
66
+ const raw = selected?._raw ?? {};
67
+ const price = typeof raw.price === "number" ? raw.price : undefined;
68
+ return (_jsxs(_Fragment, { children: [_jsx(DataListingView, { config: config }), _jsx(RecordDetailModal, { isOpen: Boolean(selected), onClose: () => setSelected(null), title: selected?.primary ?? "Catalogue Submission", badges: [{ label: selected?.status ?? "pending", variant: "warning" }], description: toStringValue(raw.description, "") || undefined, fields: [
69
+ { label: "Submitted by", value: toStringValue(raw.ownerId, "—") },
70
+ { label: "Estimated price", value: price !== undefined ? formatCurrency(price) : "—" },
71
+ { label: "Quantity", value: String(raw.quantity ?? 1) },
72
+ { label: "Condition", value: toStringValue(raw.condition, "—") },
73
+ { label: "Submitted", value: selected?.updatedAt ?? "—" },
74
+ ], items: { heading: "Photos", entries: toPhotoEntries(raw) }, footer: selected ? (_jsxs(_Fragment, { children: [_jsx(Button, { variant: "danger", onClick: () => {
75
+ setRejectTarget(selected.id);
76
+ setSelected(null);
77
+ }, children: "Reject" }), _jsx(Button, { isLoading: approveMutation.isPending, onClick: () => {
78
+ approveMutation.mutate(selected.id);
79
+ setSelected(null);
80
+ }, children: "Approve" })] })) : null }), _jsx(Modal, { isOpen: !!rejectTarget, onClose: () => setRejectTarget(null), title: "Reject this catalogue listing?", size: "sm", children: _jsxs(Stack, { gap: "md", children: [_jsx(Text, { variant: "secondary", children: "The owner will see this reason." }), _jsx(Textarea, { value: rejectReason, onChange: (e) => setRejectReason(e.target.value), rows: 3, placeholder: "Reason for rejection" }), _jsx(Button, { isLoading: rejectMutation.isPending, disabled: !rejectReason.trim(), variant: "danger", onClick: () => rejectMutation.mutate(), children: "Reject" })] }) })] }));
53
81
  }
@@ -4,6 +4,7 @@ import React from "react";
4
4
  import { useQueryClient } from "@tanstack/react-query";
5
5
  import { useApiMutation } from "@mohasinac/appkit/client";
6
6
  import { Alert, Button, FieldInput, FieldSelect, FieldTextarea, Form, Grid, Row, Span, Stack, Toggle } from "../../../ui";
7
+ import { FormErrorSummary } from "../../../ui/forms";
7
8
  import { MediaUploadList } from "../../media/upload/MediaUploadList";
8
9
  import { useMediaUpload } from "../../media";
9
10
  import { apiClient } from "../../../http";
@@ -61,7 +62,7 @@ export function CatalogueItemEditorView({ item, onSaved }) {
61
62
  onSaved?.(saved.id);
62
63
  },
63
64
  });
64
- return (_jsx(Form, { schema: createCatalogueItemSchema, spacing: "md", children: ({ setFieldError }) => (_jsxs(Stack, { gap: "md", children: [item?.listingStatus && item.listingStatus !== "not_listed" && (_jsxs(Alert, { variant: "info", children: ["This item is ", item.listingStatus.replace(/_/g, " "), item.listingStatus === "rejected" && item.rejectionReason ? `: ${item.rejectionReason}` : "."] })), _jsxs(Grid, { cols: 2, gap: "md", children: [_jsx(FieldInput, { name: "title", label: "Title", required: true, value: title, onChange: setTitle }), _jsx(FieldSelect, { name: "condition", label: "Condition", value: condition, onChange: setCondition, options: CONDITION_OPTIONS }), _jsx(FieldInput, { name: "price", label: "Estimated resale price (\u20B9)", type: "number", min: "0", value: price, onChange: setPrice }), _jsx(FieldInput, { name: "quantity", label: "Quantity", type: "number", min: "1", value: quantity, onChange: setQuantity })] }), _jsx(FieldTextarea, { name: "description", label: "Description", value: description, onChange: setDescription, rows: 3 }), _jsx(MediaUploadList, { label: "Photos", value: galleryFields, onChange: (fields) => setImages(fields.map((f) => f.url)), onUpload: handleUpload, accept: "image/*", maxItems: 8, helperText: "Photos older than 30 days must be refreshed before this item can be listed." }), _jsxs(Row, { gap: "sm", align: "center", children: [_jsx(Toggle, { checked: visibility === "public", onChange: (checked) => setVisibility(checked ? "public" : "private"), size: "sm" }), _jsx(Span, { children: visibility === "public" ? "Public — visible on your profile" : "Private — only you can see it" })] }), _jsx(Button, { type: "button", isLoading: saveMutation.isPending, onClick: () => {
65
+ return (_jsx(Form, { schema: createCatalogueItemSchema, spacing: "md", children: ({ setFieldError }) => (_jsxs(Stack, { gap: "md", children: [item?.listingStatus && item.listingStatus !== "not_listed" && (_jsxs(Alert, { variant: "info", children: ["This item is ", item.listingStatus.replace(/_/g, " "), item.listingStatus === "rejected" && item.rejectionReason ? `: ${item.rejectionReason}` : "."] })), _jsxs(Grid, { cols: 2, gap: "md", children: [_jsx(FieldInput, { name: "title", label: "Title", required: true, value: title, onChange: setTitle }), _jsx(FieldSelect, { name: "condition", label: "Condition", value: condition, onChange: setCondition, options: CONDITION_OPTIONS }), _jsx(FieldInput, { name: "price", label: "Estimated resale price (\u20B9)", type: "number", min: "0", value: price, onChange: setPrice }), _jsx(FieldInput, { name: "quantity", label: "Quantity", type: "number", min: "1", value: quantity, onChange: setQuantity })] }), _jsx(FieldTextarea, { name: "description", label: "Description", value: description, onChange: setDescription, rows: 3 }), _jsx(MediaUploadList, { label: "Photos", value: galleryFields, onChange: (fields) => setImages(fields.map((f) => f.url)), onUpload: handleUpload, accept: "image/*", maxItems: 8, helperText: "Photos older than 30 days must be refreshed before this item can be listed." }), _jsxs(Row, { gap: "sm", align: "center", children: [_jsx(Toggle, { checked: visibility === "public", onChange: (checked) => setVisibility(checked ? "public" : "private"), size: "sm" }), _jsx(Span, { children: visibility === "public" ? "Public — visible on your profile" : "Private — only you can see it" })] }), _jsx(FormErrorSummary, {}), _jsx(Button, { type: "button", isLoading: saveMutation.isPending, onClick: () => {
65
66
  const parsed = createCatalogueItemSchema.safeParse({
66
67
  title,
67
68
  images,
@@ -4,11 +4,14 @@ import { sortBy } from "@mohasinac/appkit";
4
4
  import Link from "next/link";
5
5
  import { categoriesRepository, productRepository, } from "../../../repositories";
6
6
  import { ROUTES } from "../../../next";
7
- import { Container, Div, Heading, Main, Nav, Section, Span, Text } from "../../../ui";
7
+ import { Anchor, Container, Div, Dl, Dt, Dd, Heading, Main, Nav, Section, Span, Stack, Text } from "../../../ui";
8
8
  import { DynamicBgDiv } from "../../../ui/components/DynamicBgDiv";
9
9
  import { MediaImage } from "../../media/MediaImage";
10
10
  import { BrandDetailTabs } from "./BrandDetailTabs";
11
11
  import { CategoryGrid } from "./CategoryGrid";
12
+ import { CategoryHighlightsAndFaqSection } from "./CategoryHighlightsAndFaqSection";
13
+ import { GroupedListingsCarousel } from "../../grouped/components/GroupedListingsCarousel";
14
+ import { getGroupsForBrand } from "../../../_internal/server/features/grouped/data";
12
15
  const __O = {
13
16
  hidden: "overflow-hidden",
14
17
  };
@@ -17,7 +20,7 @@ export async function BrandDetailPageView({ slug, initialBrand }) {
17
20
  .getCategoryBySlug(slug)
18
21
  .catch(() => undefined)));
19
22
  const brandName = brand?.name;
20
- const [productsResult, auctionsResult, preOrdersResult, prizeDrawsResult, allBundles, activeBrands] = await Promise.all([
23
+ const [productsResult, auctionsResult, preOrdersResult, prizeDrawsResult, allBundles, activeBrands, groupedListings] = await Promise.all([
21
24
  brandName
22
25
  ? productRepository
23
26
  .list({
@@ -59,22 +62,21 @@ export async function BrandDetailPageView({ slug, initialBrand }) {
59
62
  .catch(() => null)
60
63
  : Promise.resolve(null),
61
64
  // SB-UNI-D — bundles are categoryType:"bundle" rows on the categories
62
- // collection. We pull all bundle categories and filter client-side by
63
- // brand affinity until the bundle storage carries an explicit brandSlug.
64
- brandName
65
+ // collection, tagged to a brand via the real brandSlug field (added
66
+ // 2026-08-21 previously a fragile seo.keywords string-match heuristic).
67
+ // We pull all bundle categories and filter client-side by brandSlug —
68
+ // no dedicated repository query needed at this catalog's bundle volume.
69
+ brand?.slug
65
70
  ? categoriesRepository
66
71
  .listByType("bundle", { activeOnly: true, limit: 50 })
67
72
  .catch(() => [])
68
73
  : Promise.resolve([]),
69
74
  // Related brands — every other active brand row, excluding this one.
70
75
  categoriesRepository.findActiveBrands().catch(() => []),
76
+ brand?.slug ? getGroupsForBrand(brand.slug).catch(() => []) : Promise.resolve([]),
71
77
  ]);
72
- const brandLower = brandName?.toLowerCase();
73
- const brandBundles = brandLower
74
- ? allBundles.filter((b) => {
75
- const seo = b.seo?.keywords?.map((k) => k.toLowerCase()) ?? [];
76
- return seo.includes(brandLower);
77
- })
78
+ const brandBundles = brand?.slug
79
+ ? allBundles.filter((b) => b.brandSlug === brand.slug)
78
80
  : [];
79
81
  const relatedBrands = activeBrands.filter((b) => b.id !== brand?.id);
80
82
  const coverImage = brand?.display?.coverImage;
@@ -95,5 +97,5 @@ export async function BrandDetailPageView({ slug, initialBrand }) {
95
97
  counts.preOrders +
96
98
  counts.prizeDraws +
97
99
  counts.bundles;
98
- return (_jsxs(Main, { children: [_jsxs(Section, { className: `relative ${__O.hidden} ${hasCover ? "min-h-[220px] md:min-h-[280px]" : "bg-[var(--appkit-color-bg)]"}`, children: [hasCover && (_jsxs(_Fragment, { children: [_jsx(Div, { className: "absolute inset-0", children: _jsx(MediaImage, { src: coverImage, alt: "", size: "banner" }) }), _jsx(Div, { surface: "overlay-md", className: "absolute inset-0" })] })), !hasCover && (_jsx(DynamicBgDiv, { color: brandColor, className: "absolute inset-0 opacity-10" })), _jsxs(Div, { className: "relative z-10 max-w-7xl mx-auto", paddingY: hasCover ? "y-3xl" : "y-xl", padding: "x-md", children: [_jsxs(Nav, { layout: "flex", gap: "2xs", textSize: "sm", className: "mb-4", "aria-label": "Breadcrumb", children: [_jsx(Link, { href: String(ROUTES.HOME), className: hasCover ? "text-white/70 hover:text-white transition-colors" : "text-[var(--appkit-color-text-muted)] hover:text-primary-600 transition-colors", children: "Home" }), _jsx(Span, { className: hasCover ? "text-white/40" : "text-zinc-400", children: "/" }), _jsx(Link, { href: String(ROUTES.PUBLIC.BRANDS), className: hasCover ? "text-white/70 hover:text-white transition-colors" : "text-[var(--appkit-color-text-muted)] hover:text-primary-600 transition-colors", children: "Brands" }), _jsx(Span, { className: hasCover ? "text-white/40" : "text-zinc-400", children: "/" }), _jsx(Span, { weight: "medium", className: hasCover ? "text-white" : "text-[var(--appkit-color-text)]", children: brand?.name ?? slug })] }), _jsxs(Row, { className: "mb-3", align: "center", gap: "md", children: [brand?.display?.icon && (_jsx(Span, { className: "leading-none", size: "5xl", children: brand.display.icon })), _jsxs(_Fragment, { children: [_jsx(Heading, { color: "inverse", level: 1, className: `${hasCover ? "" : "text-[var(--appkit-color-text)]"}`, size: "3xl", mdSize: "4xl", weight: "bold", children: brand?.name ?? slug }), brand?.description && typeof brand.description === "string" && !brand.description.startsWith("{") && (_jsx(Text, { color: hasCover ? "inverse" : "muted", className: `max-w-2xl mt-1 ${hasCover ? "/80" : ""}`, size: "base", children: brand.description }))] })] }), _jsxs(Row, { gap: "sm", wrap: true, className: "mt-3", children: [counts.products > 0 && (_jsxs(Span, { layout: "inline-flex", gap: "xs", color: "inverse", size: "xs", weight: "medium", className: `${hasCover ? "bg-[rgba(255,255,255,0.2)] backdrop-blur-sm" : "bg-primary/10 text-primary-700 dark:text-primary-400"}`, rounded: "full", padding: "pill-sm-tall", children: [counts.products.toLocaleString(), " ", counts.products === 1 ? "product" : "products"] })), counts.auctions > 0 && (_jsxs(Span, { layout: "inline-flex", gap: "xs", color: hasCover ? "inverse" : "warning", surface: hasCover ? undefined : "warning-surface", size: "xs", weight: "medium", className: hasCover ? "bg-[rgba(255,255,255,0.2)] backdrop-blur-sm" : "", rounded: "full", padding: "pill-sm-tall", children: [counts.auctions.toLocaleString(), " ", counts.auctions === 1 ? "auction" : "auctions"] })), counts.preOrders > 0 && (_jsxs(Span, { layout: "inline-flex", gap: "xs", color: hasCover ? "inverse" : "info", surface: hasCover ? undefined : "info-surface", size: "xs", weight: "medium", className: hasCover ? "bg-[rgba(255,255,255,0.2)] backdrop-blur-sm" : "", rounded: "full", padding: "pill-sm-tall", children: [counts.preOrders.toLocaleString(), " ", counts.preOrders === 1 ? "pre-order" : "pre-orders"] })), totalItems === 0 && (_jsx(Span, { size: "sm", className: hasCover ? "text-white/60" : "text-zinc-400", children: "No items listed yet" }))] })] })] }), _jsx(Section, { padding: "y-lg", children: _jsx(Container, { size: "xl", children: brandName ? (_jsx(BrandDetailTabs, { brandName: brandName, initialProductsData: productsResult ?? undefined, initialBundles: brandBundles, counts: counts })) : (_jsx(Text, { paddingY: "3xl", color: "muted", size: "sm", align: "start", children: "Brand not found." })) }) }), relatedBrands.length > 0 && (_jsx(Section, { border: "subtle", surface: "default", className: "border-t", padding: "y-lg", children: _jsxs(Container, { size: "xl", children: [_jsx(Heading, { level: 2, className: "mb-4", size: "xl", weight: "semibold", children: "Related Brands" }), _jsx(CategoryGrid, { categories: relatedBrands, getHref: (b) => String(ROUTES.PUBLIC.BRAND_DETAIL(b.slug)) })] }) }))] }));
100
+ return (_jsxs(Main, { children: [_jsxs(Section, { className: `relative ${__O.hidden} ${hasCover ? "min-h-[220px] md:min-h-[280px]" : "bg-[var(--appkit-color-bg)]"}`, children: [hasCover && (_jsxs(_Fragment, { children: [_jsx(Div, { className: "absolute inset-0", children: _jsx(MediaImage, { src: coverImage, alt: "", size: "banner" }) }), _jsx(Div, { surface: "overlay-md", className: "absolute inset-0" })] })), !hasCover && (_jsx(DynamicBgDiv, { color: brandColor, className: "absolute inset-0 opacity-10" })), _jsxs(Div, { className: "relative z-10 max-w-7xl mx-auto", paddingY: hasCover ? "y-3xl" : "y-xl", padding: "x-md", children: [_jsxs(Nav, { layout: "flex", gap: "2xs", textSize: "sm", className: "mb-4", "aria-label": "Breadcrumb", children: [_jsx(Link, { href: String(ROUTES.HOME), className: hasCover ? "text-white/70 hover:text-white transition-colors" : "text-[var(--appkit-color-text-muted)] hover:text-primary-600 transition-colors", children: "Home" }), _jsx(Span, { className: hasCover ? "text-white/40" : "text-zinc-400", children: "/" }), _jsx(Link, { href: String(ROUTES.PUBLIC.BRANDS), className: hasCover ? "text-white/70 hover:text-white transition-colors" : "text-[var(--appkit-color-text-muted)] hover:text-primary-600 transition-colors", children: "Brands" }), _jsx(Span, { className: hasCover ? "text-white/40" : "text-zinc-400", children: "/" }), _jsx(Span, { weight: "medium", className: hasCover ? "text-white" : "text-[var(--appkit-color-text)]", children: brand?.name ?? slug })] }), _jsxs(Row, { className: "mb-3", align: "center", gap: "md", children: [brand?.display?.icon && (_jsx(Span, { className: "leading-none", size: "5xl", children: brand.display.icon })), _jsxs(_Fragment, { children: [_jsx(Heading, { color: "inverse", level: 1, className: `${hasCover ? "" : "text-[var(--appkit-color-text)]"}`, size: "3xl", mdSize: "4xl", weight: "bold", children: brand?.name ?? slug }), brand?.description && typeof brand.description === "string" && !brand.description.startsWith("{") && (_jsx(Text, { color: hasCover ? "inverse" : "muted", className: `max-w-2xl mt-1 ${hasCover ? "/80" : ""}`, size: "base", children: brand.description }))] })] }), _jsxs(Row, { gap: "sm", wrap: true, className: "mt-3", children: [counts.products > 0 && (_jsxs(Span, { layout: "inline-flex", gap: "xs", color: "inverse", size: "xs", weight: "medium", className: `${hasCover ? "bg-[rgba(255,255,255,0.2)] backdrop-blur-sm" : "bg-primary/10 text-primary-700 dark:text-primary-400"}`, rounded: "full", padding: "pill-sm-tall", children: [counts.products.toLocaleString(), " ", counts.products === 1 ? "product" : "products"] })), counts.auctions > 0 && (_jsxs(Span, { layout: "inline-flex", gap: "xs", color: hasCover ? "inverse" : "warning", surface: hasCover ? undefined : "warning-surface", size: "xs", weight: "medium", className: hasCover ? "bg-[rgba(255,255,255,0.2)] backdrop-blur-sm" : "", rounded: "full", padding: "pill-sm-tall", children: [counts.auctions.toLocaleString(), " ", counts.auctions === 1 ? "auction" : "auctions"] })), counts.preOrders > 0 && (_jsxs(Span, { layout: "inline-flex", gap: "xs", color: hasCover ? "inverse" : "info", surface: hasCover ? undefined : "info-surface", size: "xs", weight: "medium", className: hasCover ? "bg-[rgba(255,255,255,0.2)] backdrop-blur-sm" : "", rounded: "full", padding: "pill-sm-tall", children: [counts.preOrders.toLocaleString(), " ", counts.preOrders === 1 ? "pre-order" : "pre-orders"] })), totalItems === 0 && (_jsx(Span, { size: "sm", className: hasCover ? "text-white/60" : "text-zinc-400", children: "No items listed yet" }))] })] })] }), (brand?.brandWebsite || brand?.brandCountry || brand?.brandFounded) && (_jsx(Section, { border: "subtle", surface: "default", className: "border-b", padding: "y-md", children: _jsx(Container, { size: "xl", children: _jsxs(Stack, { gap: "sm", children: [_jsx(Heading, { level: 2, size: "sm", weight: "semibold", color: "muted", transform: "uppercase", children: "About this brand" }), _jsxs(Dl, { divide: "subtle", rounded: "xl", border: "subtle", className: "overflow-hidden max-w-lg", children: [brand?.brandWebsite && (_jsxs(Row, { gap: "md", oddEven: "zebra", surface: "default", padding: "inline", children: [_jsx(Dt, { className: "w-32 flex-shrink-0", color: "primary", weight: "medium", children: "Website" }), _jsx(Dd, { className: "flex-1", children: _jsx(Anchor, { href: brand.brandWebsite, tone: "brand", underline: "hover", children: brand.brandWebsite }) })] })), brand?.brandCountry && (_jsxs(Row, { gap: "md", oddEven: "zebra", surface: "default", padding: "inline", children: [_jsx(Dt, { className: "w-32 flex-shrink-0", color: "primary", weight: "medium", children: "Country" }), _jsx(Dd, { className: "flex-1", color: "muted", children: brand.brandCountry })] })), brand?.brandFounded && (_jsxs(Row, { gap: "md", oddEven: "zebra", surface: "default", padding: "inline", children: [_jsx(Dt, { className: "w-32 flex-shrink-0", color: "primary", weight: "medium", children: "Founded" }), _jsx(Dd, { className: "flex-1", color: "muted", children: brand.brandFounded })] }))] })] }) }) })), _jsx(CategoryHighlightsAndFaqSection, { highlights: brand?.highlights, faqs: brand?.faqs }), _jsx(Section, { padding: "y-lg", children: _jsx(Container, { size: "xl", children: brandName ? (_jsx(BrandDetailTabs, { brandName: brandName, initialProductsData: productsResult ?? undefined, initialBundles: brandBundles, counts: counts })) : (_jsx(Text, { paddingY: "3xl", color: "muted", size: "sm", align: "start", children: "Brand not found." })) }) }), groupedListings.length > 0 && (_jsx(Section, { padding: "y-lg", children: _jsx(Container, { size: "xl", children: _jsx(GroupedListingsCarousel, { groups: groupedListings }) }) })), relatedBrands.length > 0 && (_jsx(Section, { border: "subtle", surface: "default", className: "border-t", padding: "y-lg", children: _jsxs(Container, { size: "xl", children: [_jsx(Heading, { level: 2, className: "mb-4", size: "xl", weight: "semibold", children: "Related Brands" }), _jsx(CategoryGrid, { categories: relatedBrands, getHref: (b) => String(ROUTES.PUBLIC.BRAND_DETAIL(b.slug)) })] }) }))] }));
99
101
  }
@@ -8,6 +8,9 @@ import { Container, Div, Heading, Main, Nav, Section, Span, Text } from "../../.
8
8
  import { MediaImage } from "../../media/MediaImage";
9
9
  import { CategoryDetailTabs } from "./CategoryDetailTabs";
10
10
  import { CategoryGrid } from "./CategoryGrid";
11
+ import { CategoryHighlightsAndFaqSection } from "./CategoryHighlightsAndFaqSection";
12
+ import { GroupedListingsCarousel } from "../../grouped/components/GroupedListingsCarousel";
13
+ import { getGroupsForCategory } from "../../../_internal/server/features/grouped/data";
11
14
  const __O = {
12
15
  hidden: "overflow-hidden",
13
16
  xAuto: "overflow-x-auto",
@@ -25,7 +28,7 @@ export async function CategoryDetailPageView({ slug }) {
25
28
  ? await categoriesRepository.getDescendantIds(category.id).catch(() => [])
26
29
  : [];
27
30
  const categoriesIn = category?.id ? [category.id, ...descendantIds] : null;
28
- const [productsResult, auctionsCountResult, preOrdersCountResult, prizeDrawsCountResult, bundlesResult, childCategories, rootSiblingCategories] = await Promise.all([
31
+ const [productsResult, auctionsCountResult, preOrdersCountResult, prizeDrawsCountResult, bundlesResult, childCategories, rootSiblingCategories, groupedListings] = await Promise.all([
29
32
  categoriesIn
30
33
  ? productRepository
31
34
  .list({
@@ -82,6 +85,7 @@ export async function CategoryDetailPageView({ slug }) {
82
85
  category?.rootId
83
86
  ? categoriesRepository.getCategoriesByRootId(category.rootId).catch(() => [])
84
87
  : Promise.resolve([]),
88
+ category?.slug ? getGroupsForCategory(category.slug).catch(() => []) : Promise.resolve([]),
85
89
  ]);
86
90
  const relatedCategories = rootSiblingCategories.filter((c) => c.id !== category?.id && (!c.categoryType || c.categoryType === "category"));
87
91
  // Stores tab — query stores whose storeCategory matches this category or any child
@@ -126,7 +130,7 @@ export async function CategoryDetailPageView({ slug }) {
126
130
  const storeCount = categoryStores.length;
127
131
  const coverImage = category?.display?.coverImage;
128
132
  const hasCover = Boolean(coverImage);
129
- return (_jsxs(Main, { children: [_jsxs(Section, { className: `relative ${__O.hidden} ${hasCover ? "min-h-[220px] md:min-h-[280px]" : "bg-[var(--appkit-color-bg)]"}`, children: [hasCover && (_jsxs(_Fragment, { children: [_jsx(Div, { className: "absolute inset-0", children: _jsx(MediaImage, { src: coverImage, alt: "", size: "banner" }) }), _jsx(Div, { surface: "overlay-md", className: "absolute inset-0" })] })), _jsxs(Div, { className: "relative z-10 max-w-7xl mx-auto", paddingY: hasCover ? "y-3xl" : "y-xl", padding: "x-md", children: [_jsxs(Nav, { layout: "flex", gap: "2xs", textSize: "sm", className: "mb-4", "aria-label": "Breadcrumb", children: [_jsx(Link, { href: String(ROUTES.HOME), className: hasCover ? "text-white/70 hover:text-white transition-colors" : "text-[var(--appkit-color-text-muted)] hover:text-primary-600 transition-colors", children: "Home" }), _jsx(Span, { className: hasCover ? "text-white/40" : "text-zinc-400", children: "/" }), _jsx(Link, { href: String(ROUTES.PUBLIC.CATEGORIES), className: hasCover ? "text-white/70 hover:text-white transition-colors" : "text-[var(--appkit-color-text-muted)] hover:text-primary-600 transition-colors", children: "Categories" }), _jsx(Span, { className: hasCover ? "text-white/40" : "text-zinc-400", children: "/" }), _jsx(Span, { weight: "medium", className: hasCover ? "text-white" : "text-[var(--appkit-color-text)]", children: category?.name ?? slug })] }), _jsx(Heading, { color: "inverse", level: 1, className: `mb-2 ${hasCover ? "" : "text-[var(--appkit-color-text)]"}`, size: "3xl", mdSize: "4xl", weight: "bold", children: category?.name ?? slug }), category?.description && typeof category.description === "string" && !category.description.startsWith("{") && (_jsx(Text, { color: hasCover ? "inverse" : "muted", className: `max-w-2xl mb-4 ${hasCover ? "/80" : ""}`, size: "base", children: category.description })), _jsxs(Row, { wrap: true, gap: "sm", children: [productCount > 0 && (_jsxs(Span, { layout: "inline-flex", gap: "xs", color: "inverse", size: "xs", weight: "medium", className: hasCover ? "bg-[rgba(255,255,255,0.2)] backdrop-blur-sm" : "bg-primary/10 text-primary-700 dark:text-primary-400", rounded: "full", padding: "pill-sm-tall", children: [productCount.toLocaleString(), " ", productCount === 1 ? "product" : "products"] })), auctionCount > 0 && (_jsxs(Span, { layout: "inline-flex", gap: "xs", color: hasCover ? "inverse" : "warning", surface: hasCover ? undefined : "warning-surface", size: "xs", weight: "medium", className: hasCover ? "bg-[rgba(255,255,255,0.2)] backdrop-blur-sm" : "", rounded: "full", padding: "pill-sm-tall", children: [auctionCount.toLocaleString(), " ", auctionCount === 1 ? "auction" : "auctions"] })), preOrderCount > 0 && (_jsxs(Span, { layout: "inline-flex", gap: "xs", color: hasCover ? "inverse" : "info", surface: hasCover ? undefined : "info-surface", size: "xs", weight: "medium", className: hasCover ? "bg-[rgba(255,255,255,0.2)] backdrop-blur-sm" : "", rounded: "full", padding: "pill-sm-tall", children: [preOrderCount.toLocaleString(), " ", preOrderCount === 1 ? "pre-order" : "pre-orders"] })), storeCount > 0 && (_jsxs(Span, { layout: "inline-flex", gap: "xs", color: hasCover ? "inverse" : "success", surface: hasCover ? undefined : "success-surface", size: "xs", weight: "medium", className: hasCover ? "bg-[rgba(255,255,255,0.2)] backdrop-blur-sm" : "", rounded: "full", padding: "pill-sm-tall", children: [storeCount.toLocaleString(), " ", storeCount === 1 ? "store" : "stores"] }))] })] })] }), childCategories.length > 0 && (_jsx(Section, { border: "subtle", surface: "default", className: "border-b", children: _jsx(Div, { className: "max-w-7xl mx-auto", padding: "inline", children: _jsx(Div, { layout: "flex", gap: "2", className: `.5 ${__O.xAuto} [scrollbar-width:none]`, padding: "b-2xs", children: childCategories.map((child) => (_jsxs(Link, { href: String(ROUTES.PUBLIC.CATEGORY_DETAIL(child.slug)),
133
+ return (_jsxs(Main, { children: [_jsxs(Section, { className: `relative ${__O.hidden} ${hasCover ? "min-h-[220px] md:min-h-[280px]" : "bg-[var(--appkit-color-bg)]"}`, children: [hasCover && (_jsxs(_Fragment, { children: [_jsx(Div, { className: "absolute inset-0", children: _jsx(MediaImage, { src: coverImage, alt: "", size: "banner" }) }), _jsx(Div, { surface: "overlay-md", className: "absolute inset-0" })] })), _jsxs(Div, { className: "relative z-10 max-w-7xl mx-auto", paddingY: hasCover ? "y-3xl" : "y-xl", padding: "x-md", children: [_jsxs(Nav, { layout: "flex", gap: "2xs", textSize: "sm", className: "mb-4", "aria-label": "Breadcrumb", children: [_jsx(Link, { href: String(ROUTES.HOME), className: hasCover ? "text-white/70 hover:text-white transition-colors" : "text-[var(--appkit-color-text-muted)] hover:text-primary-600 transition-colors", children: "Home" }), _jsx(Span, { className: hasCover ? "text-white/40" : "text-zinc-400", children: "/" }), _jsx(Link, { href: String(ROUTES.PUBLIC.CATEGORIES), className: hasCover ? "text-white/70 hover:text-white transition-colors" : "text-[var(--appkit-color-text-muted)] hover:text-primary-600 transition-colors", children: "Categories" }), _jsx(Span, { className: hasCover ? "text-white/40" : "text-zinc-400", children: "/" }), _jsx(Span, { weight: "medium", className: hasCover ? "text-white" : "text-[var(--appkit-color-text)]", children: category?.name ?? slug })] }), _jsx(Heading, { color: "inverse", level: 1, className: `mb-2 ${hasCover ? "" : "text-[var(--appkit-color-text)]"}`, size: "3xl", mdSize: "4xl", weight: "bold", children: category?.name ?? slug }), category?.description && typeof category.description === "string" && !category.description.startsWith("{") && (_jsx(Text, { color: hasCover ? "inverse" : "muted", className: `max-w-2xl mb-4 ${hasCover ? "/80" : ""}`, size: "base", children: category.description })), _jsxs(Row, { wrap: true, gap: "sm", children: [productCount > 0 && (_jsxs(Span, { layout: "inline-flex", gap: "xs", color: "inverse", size: "xs", weight: "medium", className: hasCover ? "bg-[rgba(255,255,255,0.2)] backdrop-blur-sm" : "bg-primary/10 text-primary-700 dark:text-primary-400", rounded: "full", padding: "pill-sm-tall", children: [productCount.toLocaleString(), " ", productCount === 1 ? "product" : "products"] })), auctionCount > 0 && (_jsxs(Span, { layout: "inline-flex", gap: "xs", color: hasCover ? "inverse" : "warning", surface: hasCover ? undefined : "warning-surface", size: "xs", weight: "medium", className: hasCover ? "bg-[rgba(255,255,255,0.2)] backdrop-blur-sm" : "", rounded: "full", padding: "pill-sm-tall", children: [auctionCount.toLocaleString(), " ", auctionCount === 1 ? "auction" : "auctions"] })), preOrderCount > 0 && (_jsxs(Span, { layout: "inline-flex", gap: "xs", color: hasCover ? "inverse" : "info", surface: hasCover ? undefined : "info-surface", size: "xs", weight: "medium", className: hasCover ? "bg-[rgba(255,255,255,0.2)] backdrop-blur-sm" : "", rounded: "full", padding: "pill-sm-tall", children: [preOrderCount.toLocaleString(), " ", preOrderCount === 1 ? "pre-order" : "pre-orders"] })), storeCount > 0 && (_jsxs(Span, { layout: "inline-flex", gap: "xs", color: hasCover ? "inverse" : "success", surface: hasCover ? undefined : "success-surface", size: "xs", weight: "medium", className: hasCover ? "bg-[rgba(255,255,255,0.2)] backdrop-blur-sm" : "", rounded: "full", padding: "pill-sm-tall", children: [storeCount.toLocaleString(), " ", storeCount === 1 ? "store" : "stores"] }))] })] })] }), _jsx(CategoryHighlightsAndFaqSection, { highlights: category?.highlights, faqs: category?.faqs }), childCategories.length > 0 && (_jsx(Section, { border: "subtle", surface: "default", className: "border-b", children: _jsx(Div, { className: "max-w-7xl mx-auto", padding: "inline", children: _jsx(Div, { layout: "flex", gap: "2", className: `.5 ${__O.xAuto} [scrollbar-width:none]`, padding: "b-2xs", children: childCategories.map((child) => (_jsxs(Link, { href: String(ROUTES.PUBLIC.CATEGORY_DETAIL(child.slug)),
130
134
  // audit-responsive-wrap-ok: deliberate horizontal-scroll chip row (parent has overflow-x-auto) — each chip staying single-line is the intended design, not hidden content
131
135
  className: "flex-shrink-0 flex items-center gap-[var(--appkit-space-1-5)] rounded-full border border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface-elevated)] px-[var(--appkit-space-4)] py-[var(--appkit-space-1-5)] text-[length:var(--appkit-text-sm)] font-medium text-[var(--appkit-color-text-muted)] hover:border-primary hover:text-primary transition-colors whitespace-nowrap", children: [child.display?.icon && (_jsx(Span, { className: "leading-none", children: child.display.icon })), child.name, (child.metrics?.productCount ?? 0) > 0 && (_jsx(Span, { size: "xs", color: "muted", children: (child.metrics?.productCount ?? 0).toLocaleString() }))] }, child.id))) }) }) })), _jsx(Section, { padding: "y-lg", children: _jsx(Container, { size: "xl", children: _jsx(CategoryDetailTabs, { categorySlug: slug, categoryId: category?.id, initialProductsData: productsResult ?? undefined, initialBundles: bundlesResult ?? [], initialStores: categoryStores, counts: {
132
136
  products: productCount,
@@ -135,5 +139,5 @@ export async function CategoryDetailPageView({ slug }) {
135
139
  prizeDraws: prizeDrawCount,
136
140
  bundles: bundleCount,
137
141
  stores: storeCount,
138
- } }) }) }), relatedCategories.length > 0 && (_jsx(Section, { border: "subtle", surface: "default", className: "border-t", padding: "y-lg", children: _jsxs(Container, { size: "xl", children: [_jsx(Heading, { level: 2, className: "mb-4", size: "xl", weight: "semibold", children: "Related Categories" }), _jsx(CategoryGrid, { categories: relatedCategories, getHref: (c) => String(ROUTES.PUBLIC.CATEGORY_DETAIL(c.slug)) })] }) }))] }));
142
+ } }) }) }), groupedListings.length > 0 && (_jsx(Section, { padding: "y-lg", children: _jsx(Container, { size: "xl", children: _jsx(GroupedListingsCarousel, { groups: groupedListings }) }) })), relatedCategories.length > 0 && (_jsx(Section, { border: "subtle", surface: "default", className: "border-t", padding: "y-lg", children: _jsxs(Container, { size: "xl", children: [_jsx(Heading, { level: 2, className: "mb-4", size: "xl", weight: "semibold", children: "Related Categories" }), _jsx(CategoryGrid, { categories: relatedCategories, getHref: (c) => String(ROUTES.PUBLIC.CATEGORY_DETAIL(c.slug)) })] }) }))] }));
139
143
  }
@@ -0,0 +1,14 @@
1
+ export interface CategoryHighlightsAndFaqSectionProps {
2
+ highlights?: string[];
3
+ faqs?: {
4
+ question: string;
5
+ answer: string;
6
+ }[];
7
+ }
8
+ /**
9
+ * Shared "why shop here" highlights + FAQ accordion, rendered on both
10
+ * category and brand detail pages — the only editorial content either page
11
+ * has beyond a one-line description (see CategoryDisplay's field list).
12
+ * Renders nothing when both arrays are empty/absent.
13
+ */
14
+ export declare function CategoryHighlightsAndFaqSection({ highlights, faqs }: CategoryHighlightsAndFaqSectionProps): import("react").JSX.Element | null;
@@ -0,0 +1,22 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Container, Heading, Li, Section, Span, Stack, Ul } from "../../../ui";
3
+ import { FAQAccordion } from "../../faq/components/FAQAccordion";
4
+ /**
5
+ * Shared "why shop here" highlights + FAQ accordion, rendered on both
6
+ * category and brand detail pages — the only editorial content either page
7
+ * has beyond a one-line description (see CategoryDisplay's field list).
8
+ * Renders nothing when both arrays are empty/absent.
9
+ */
10
+ export function CategoryHighlightsAndFaqSection({ highlights, faqs }) {
11
+ const hasHighlights = Boolean(highlights && highlights.length > 0);
12
+ const hasFaqs = Boolean(faqs && faqs.length > 0);
13
+ if (!hasHighlights && !hasFaqs)
14
+ return null;
15
+ const faqItems = (faqs ?? []).map((f, i) => ({
16
+ id: `faq-${i}`,
17
+ question: f.question,
18
+ answer: { text: f.answer, format: "plain" },
19
+ category: "general",
20
+ }));
21
+ return (_jsx(Section, { padding: "y-lg", border: "subtle", className: "border-t", children: _jsx(Container, { size: "xl", children: _jsxs(Stack, { gap: "xl", children: [hasHighlights && (_jsxs(Stack, { gap: "sm", children: [_jsx(Heading, { level: 2, size: "lg", weight: "semibold", children: "Why shop here" }), _jsx(Ul, { spacing: "comfortable", size: "sm", color: "primary", children: highlights.map((h, i) => (_jsxs(Li, { layout: "flex-start", gap: "2", children: [_jsx(Span, { className: "mt-0.5 flex-shrink-0 text-primary-500", children: "\u2022" }), h] }, i))) })] })), hasFaqs && (_jsxs(Stack, { gap: "sm", children: [_jsx(Heading, { level: 2, size: "lg", weight: "semibold", children: "Frequently Asked Questions" }), _jsx(FAQAccordion, { faqs: faqItems })] }))] }) }) }));
22
+ }
@@ -9,10 +9,6 @@ export interface CategoryProductsViewProps {
9
9
  initialCategory?: CategoryItem;
10
10
  /** Initial SSR child categories */
11
11
  initialChildren?: CategoryItem[];
12
- labels?: {
13
- emptyTitle?: string;
14
- emptyDescription?: string;
15
- };
16
12
  /** Optional UrlTable-compatible state source for shared filter/sort controls */
17
13
  table?: UrlTable;
18
14
  /** Filter/sort preset for admin/seller/public listing behavior */
@@ -46,4 +42,4 @@ export interface CategoryProductsViewProps {
46
42
  isLoading?: boolean;
47
43
  className?: string;
48
44
  }
49
- export declare function CategoryProductsView({ labels: _labels, table, filterVariant, sortValue, onSortChange, renderBreadcrumbs, renderChildCategories, renderFilters, renderActiveFilters, renderSearch, renderSort, renderViewToggle, renderProducts, renderPagination, category, children: childCategories, total, isLoading, className, }: CategoryProductsViewProps): React.JSX.Element;
45
+ export declare function CategoryProductsView({ table, filterVariant, sortValue, onSortChange, renderBreadcrumbs, renderChildCategories, renderFilters, renderActiveFilters, renderSearch, renderSort, renderViewToggle, renderProducts, renderPagination, category, children: childCategories, total, isLoading, className, }: CategoryProductsViewProps): React.JSX.Element;
@@ -4,7 +4,7 @@ import React from "react";
4
4
  import { Div, Heading, Input, SlottedListingView, Stack, Text } from "../../../ui";
5
5
  import { CategoryFilters } from "./CategoryFilters";
6
6
  import { CategorySortSelect } from "./CategorySortSelect";
7
- export function CategoryProductsView({ labels: _labels = {}, table, filterVariant = "public", sortValue, onSortChange, renderBreadcrumbs, renderChildCategories, renderFilters, renderActiveFilters, renderSearch, renderSort, renderViewToggle, renderProducts, renderPagination, category = null, children: childCategories = [], total = 0, isLoading = false, className = "", }) {
7
+ export function CategoryProductsView({ table, filterVariant = "public", sortValue, onSortChange, renderBreadcrumbs, renderChildCategories, renderFilters, renderActiveFilters, renderSearch, renderSort, renderViewToggle, renderProducts, renderPagination, category = null, children: childCategories = [], total = 0, isLoading = false, className = "", }) {
8
8
  const [search, setSearch] = React.useState("");
9
9
  const [sort, setSort] = React.useState("-createdAt");
10
10
  const [viewMode, setViewMode] = React.useState("grid");
@@ -1,12 +1,21 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
3
  import { Button, Div, Label, Row, Select, SideDrawer } from "../../../ui";
4
- import { useState, useCallback } from "react";
4
+ import { useState, useCallback, useEffect } from "react";
5
+ import { z } from "zod";
5
6
  import { useCategories, useCreateCategory } from "../hooks/useCategorySelector";
6
7
  import { useMessage } from "../../../react";
7
8
  import { flattenCategories } from "../types";
8
9
  import { DrawerFormFooter } from "../../admin/components/DrawerFormFooter";
9
10
  import { CategoryForm } from "./CategoryForm";
11
+ import { useFormShellState, FormShellContext, FormErrorSummary } from "../../../ui/forms";
12
+ // Form-input subset — `categoryFirestoreSchema` (categories schemas) models
13
+ // the full stored document (id, timestamps, computed metrics) which this
14
+ // create-draft never has.
15
+ const categoryDraftSchema = z.object({
16
+ name: z.string().min(1, "Name is required"),
17
+ slug: z.string().optional(),
18
+ });
10
19
  const DEFAULT_LABELS = {
11
20
  selectPlaceholder: "Select category…",
12
21
  addCategory: "+ Add category",
@@ -21,6 +30,11 @@ function CreateCategoryContent({ allCategories, onSuccess, onCancel, labels, sta
21
30
  parentId: null,
22
31
  order: 0,
23
32
  });
33
+ const { shellCtx, validate } = useFormShellState(categoryDraftSchema);
34
+ useEffect(() => {
35
+ validate(draft);
36
+ // eslint-disable-next-line react-hooks/exhaustive-deps
37
+ }, [draft, validate]);
24
38
  const { mutate, isPending: isLoading } = useCreateCategory({
25
39
  onSuccess: (res) => {
26
40
  showSuccess(labels.successCreated);
@@ -33,7 +47,7 @@ function CreateCategoryContent({ allCategories, onSuccess, onCancel, labels, sta
33
47
  return;
34
48
  mutate(draft);
35
49
  }, [draft, mutate]);
36
- return (_jsxs(Div, { className: stackClassName, children: [_jsx(CategoryForm, { category: draft, allCategories: allCategories, onChange: setDraft, labels: labels.form, stackClassName: stackClassName }), _jsx(DrawerFormFooter, { variant: "inline", isLoading: isLoading, onSubmit: handleSave, onCancel: onCancel, isSubmitDisabled: !draft.name })] }));
50
+ return (_jsx(FormShellContext.Provider, { value: shellCtx, children: _jsxs(Div, { className: stackClassName, children: [_jsx(CategoryForm, { category: draft, allCategories: allCategories, onChange: setDraft, labels: labels.form, stackClassName: stackClassName }), _jsx(FormErrorSummary, {}), _jsx(DrawerFormFooter, { variant: "inline", isLoading: isLoading, onSubmit: handleSave, onCancel: onCancel, isSubmitDisabled: !draft.name })] }) }));
37
51
  }
38
52
  export function CategorySelectorCreate({ value, onChange, disabled = false, label, labels = {}, stackClassName = "space-y-4", }) {
39
53
  const [drawerOpen, setDrawerOpen] = useState(false);