@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
@@ -1,31 +0,0 @@
1
- /**
2
- * Checkout Domain Actions (appkit)
3
- *
4
- * OTP-based checkout consent flow — delegates entirely to already-appkit-owned
5
- * helpers (consent-otp, auth repository, address repository).
6
- * Auth, rate-limiting, and Next.js specifics are handled by the consumer.
7
- */
8
- import { userRepository } from "../../auth/repository/user.repository";
9
- /** Returns masked email so the UI can confirm which inbox to check. */
10
- export declare function sendCheckoutConsentOtp(userId: string, userEmail: string, addressId: string): Promise<{
11
- maskedEmail: string;
12
- }>;
13
- /** Verifies the 6-digit consent OTP and marks the Firestore record verified. */
14
- export declare function verifyCheckoutConsentOtp(userId: string, addressId: string, code: string): Promise<void>;
15
- /**
16
- * Grants consent via SMS when the shipping address phone matches the buyer's
17
- * registered phone number.
18
- */
19
- export declare function grantCheckoutConsentViaSms(userId: string, userPhone: string | undefined, addressId: string): Promise<void>;
20
- /**
21
- * Grants checkout consent for an admin test order, bypassing the normal OTP flow.
22
- *
23
- * The resulting Firestore doc is identical to a verified consent OTP so that
24
- * `createCheckoutOrderAction` can proceed without modification. The
25
- * `verifiedVia: "admin_bypass"` marker prevents bypass-credit grants.
26
- *
27
- * Security: callers MUST verify the requesting user is an admin AND that
28
- * sieveFilter("siteSettings.featureFlags.adminCheckoutBypass", SIEVE_OP.EQ, "= true") before calling this.
29
- */
30
- export declare function grantAdminCheckoutBypass(userId: string, addressId: string, bypassingAdminUid: string): Promise<void>;
31
- export { userRepository };
@@ -1,124 +0,0 @@
1
- /**
2
- * Checkout Domain Actions (appkit)
3
- *
4
- * OTP-based checkout consent flow — delegates entirely to already-appkit-owned
5
- * helpers (consent-otp, auth repository, address repository).
6
- * Auth, rate-limiting, and Next.js specifics are handled by the consumer.
7
- */
8
- import { timingSafeEqual } from "crypto";
9
- import { ValidationError } from "../../../errors";
10
- import { serverLogger } from "../../../monitoring";
11
- import { sendEmail } from "../../contact/email";
12
- import { CONSENT_OTP_EXPIRY_MS, CONSENT_OTP_MAX_ATTEMPTS, hashOtp, maskEmail, generateOtpCode, buildConsentOtpEmail, enforceConsentOtpRateLimit, saveConsentOtp, readConsentOtp, patchConsentOtp, } from "../../auth/consent-otp";
13
- import { addressesRepository } from "../../addresses/repository/addresses.repository";
14
- const ERR_ADDRESS_NOT_FOUND = "Address not found.";
15
- async function findUserAddress(userId, addressId) {
16
- const address = await addressesRepository.findById(addressId);
17
- if (!address || address.ownerType !== "user" || address.ownerId !== userId) {
18
- return null;
19
- }
20
- return address;
21
- }
22
- import { userRepository } from "../../auth/repository/user.repository";
23
- import { resolveDate } from "../../../utils";
24
- /** Returns masked email so the UI can confirm which inbox to check. */
25
- export async function sendCheckoutConsentOtp(userId, userEmail, addressId) {
26
- const address = await findUserAddress(userId, addressId);
27
- if (!address)
28
- throw new ValidationError(ERR_ADDRESS_NOT_FOUND);
29
- await enforceConsentOtpRateLimit(userId);
30
- const code = generateOtpCode();
31
- const codeHash = hashOtp(code);
32
- const expiresAt = new Date(Date.now() + CONSENT_OTP_EXPIRY_MS);
33
- await saveConsentOtp(userId, addressId, {
34
- codeHash,
35
- expiresAt,
36
- attempts: 0,
37
- verified: false,
38
- addressId,
39
- createdAt: new Date(),
40
- });
41
- const siteName = process.env.NEXT_PUBLIC_SITE_NAME || "LetItRip";
42
- const { subject, html } = buildConsentOtpEmail(address.fullName, code, siteName);
43
- await sendEmail({ to: userEmail, subject, html });
44
- serverLogger.info(`Consent OTP sent: uid=${userId} addressId=${addressId}`);
45
- return { maskedEmail: maskEmail(userEmail) };
46
- }
47
- /** Verifies the 6-digit consent OTP and marks the Firestore record verified. */
48
- export async function verifyCheckoutConsentOtp(userId, addressId, code) {
49
- const otpDoc = await readConsentOtp(userId, addressId);
50
- if (!otpDoc)
51
- throw new ValidationError("No consent OTP found. Please request a new code.");
52
- if (otpDoc.verified)
53
- return;
54
- if (otpDoc.attempts >= CONSENT_OTP_MAX_ATTEMPTS) {
55
- throw new ValidationError("Too many failed attempts. Please request a new code.");
56
- }
57
- if (Date.now() > (resolveDate(otpDoc.expiresAt)?.getTime() ?? 0)) {
58
- throw new ValidationError("Code expired. Please request a new one.");
59
- }
60
- const inputHash = hashOtp(code);
61
- if (!timingSafeEqual(Buffer.from(inputHash, "hex"), Buffer.from(otpDoc.codeHash, "hex"))) {
62
- await patchConsentOtp(userId, addressId, {
63
- attempts: otpDoc.attempts + 1,
64
- });
65
- throw new ValidationError("Invalid code. Please check and try again.");
66
- }
67
- await patchConsentOtp(userId, addressId, {
68
- verified: true,
69
- verifiedAt: new Date(),
70
- });
71
- serverLogger.info(`Consent OTP verified: uid=${userId} addressId=${addressId}`);
72
- }
73
- /**
74
- * Grants consent via SMS when the shipping address phone matches the buyer's
75
- * registered phone number.
76
- */
77
- export async function grantCheckoutConsentViaSms(userId, userPhone, addressId) {
78
- if (!userPhone)
79
- throw new ValidationError("No phone number registered on your account.");
80
- const address = await findUserAddress(userId, addressId);
81
- if (!address)
82
- throw new ValidationError(ERR_ADDRESS_NOT_FOUND);
83
- const normalizePhone = (s) => s.replace(/[^0-9]/g, "").slice(-10);
84
- if (normalizePhone(address.phone) !== normalizePhone(userPhone)) {
85
- throw new ValidationError("Phone number does not match the shipping address. Please use email verification for this address.");
86
- }
87
- await saveConsentOtp(userId, addressId, {
88
- codeHash: "",
89
- expiresAt: new Date(Date.now() + CONSENT_OTP_EXPIRY_MS),
90
- attempts: 0,
91
- verified: true,
92
- verifiedVia: "sms",
93
- addressId,
94
- createdAt: new Date(),
95
- });
96
- serverLogger.info(`Checkout consent granted via SMS: uid=${userId} addressId=${addressId}`);
97
- }
98
- /**
99
- * Grants checkout consent for an admin test order, bypassing the normal OTP flow.
100
- *
101
- * The resulting Firestore doc is identical to a verified consent OTP so that
102
- * `createCheckoutOrderAction` can proceed without modification. The
103
- * `verifiedVia: "admin_bypass"` marker prevents bypass-credit grants.
104
- *
105
- * Security: callers MUST verify the requesting user is an admin AND that
106
- * sieveFilter("siteSettings.featureFlags.adminCheckoutBypass", SIEVE_OP.EQ, "= true") before calling this.
107
- */
108
- export async function grantAdminCheckoutBypass(userId, addressId, bypassingAdminUid) {
109
- const address = await findUserAddress(userId, addressId);
110
- if (!address)
111
- throw new ValidationError(ERR_ADDRESS_NOT_FOUND);
112
- await saveConsentOtp(userId, addressId, {
113
- codeHash: "",
114
- expiresAt: new Date(Date.now() + CONSENT_OTP_EXPIRY_MS),
115
- attempts: 0,
116
- verified: true,
117
- verifiedVia: "admin_bypass",
118
- adminBypassBy: bypassingAdminUid,
119
- addressId,
120
- createdAt: new Date(),
121
- });
122
- serverLogger.info(`Admin checkout bypass granted: uid=${userId} addressId=${addressId} bypassedBy=${bypassingAdminUid}`);
123
- }
124
- export { userRepository };
@@ -1,5 +0,0 @@
1
- import React from "react";
2
- export interface ClassifiedIndexListingProps {
3
- initialData?: any;
4
- }
5
- export declare function ClassifiedIndexListing({ initialData }: ClassifiedIndexListingProps): React.JSX.Element;
@@ -1,123 +0,0 @@
1
- "use client";
2
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { useState, useCallback } from "react";
4
- import { useUrlTable } from "../../../react/hooks/useUrlTable";
5
- import { useProducts } from "../../products/hooks/useProducts";
6
- import { BulkActionBar, Div, FilterDrawer, ListingToolbar, LoginRequiredModal, Pagination, Row, Stack, Text, StickyToolbar } from "../../../ui";
7
- import { usePendingTable } from "../../../react/hooks/usePendingTable";
8
- import { useAuthGate } from "../../../react/hooks/useAuthGate";
9
- import { useBulkSelection } from "../../../react/hooks/useBulkSelection";
10
- import { useGuestWishlist } from "../../wishlist/hooks/useGuestWishlist";
11
- import { pushWishlistOp } from "../../cart/utils/pending-ops";
12
- import { useCategoryTree, categoriesToFacetOptions } from "../../categories/hooks/useCategoryTree";
13
- import { TABLE_KEYS, VIEW_MODE } from "../../../constants/table-keys";
14
- import { sortBy } from "../../../constants/sort";
15
- import { PRODUCT_FIELDS } from "../../../constants/field-names";
16
- import { ROUTES } from "../../../next";
17
- import { InteractiveProductCard } from "../../products/components/InteractiveProductCard";
18
- import { ClassifiedFilters } from "./ClassifiedFilters";
19
- import { ACTION_ID } from "../../products/constants/action-defs";
20
- import { useToast } from "../../../ui";
21
- import { useBottomActions } from "../../layout";
22
- const __P = {
23
- p3: "p-[var(--appkit-space-3)]",
24
- };
25
- const __O = {
26
- hidden: "overflow-hidden",
27
- };
28
- const DEFAULT_SORT = sortBy(PRODUCT_FIELDS.CREATED_AT);
29
- const SORT_OPTIONS = [
30
- { value: sortBy(PRODUCT_FIELDS.CREATED_AT), label: "Newest First" },
31
- { value: sortBy(PRODUCT_FIELDS.CREATED_AT, "ASC"), label: "Oldest First" },
32
- { value: sortBy(PRODUCT_FIELDS.PRICE, "ASC"), label: "Price: Low to High" },
33
- { value: sortBy(PRODUCT_FIELDS.PRICE), label: "Price: High to Low" },
34
- ];
35
- const FILTER_KEYS = [
36
- TABLE_KEYS.CATEGORY,
37
- TABLE_KEYS.CITY,
38
- TABLE_KEYS.MIN_PRICE,
39
- TABLE_KEYS.MAX_PRICE,
40
- TABLE_KEYS.NEGOTIABLE,
41
- TABLE_KEYS.ACCEPTS_SHIPPING,
42
- ];
43
- export function ClassifiedIndexListing({ initialData }) {
44
- const table = useUrlTable({ defaults: { pageSize: "24", sort: DEFAULT_SORT } });
45
- const { showToast } = useToast();
46
- const { requireAuth, modalOpen, modalMessage, closeModal } = useAuthGate();
47
- const [searchInput, setSearchInput] = useState(table.get(TABLE_KEYS.QUERY) || "");
48
- const [filterOpen, setFilterOpen] = useState(false);
49
- const [view, setView] = useState(table.get(TABLE_KEYS.VIEW) || VIEW_MODE.GRID);
50
- const localWishlist = useGuestWishlist();
51
- const { categories } = useCategoryTree();
52
- const categoryOptions = categoriesToFacetOptions(categories);
53
- const wishlistedIds = new Set(localWishlist.items.filter((i) => i.type === "classified").map((i) => i.itemId));
54
- const { pendingTable, filterActiveCount, onFilterApply, onFilterClear, onResetAll, onFilterReset } = usePendingTable(table, FILTER_KEYS);
55
- const openFilters = useCallback(() => {
56
- onFilterReset();
57
- setFilterOpen(true);
58
- }, [onFilterReset]);
59
- const applyFilters = useCallback(() => {
60
- onFilterApply();
61
- setFilterOpen(false);
62
- }, [onFilterApply]);
63
- const resetAll = useCallback(() => {
64
- onResetAll({ [TABLE_KEYS.QUERY]: "", [TABLE_KEYS.SORT]: "" });
65
- setSearchInput("");
66
- }, [onResetAll]);
67
- const hasActiveState = !!table.get(TABLE_KEYS.QUERY) ||
68
- table.get(TABLE_KEYS.SORT) !== DEFAULT_SORT ||
69
- filterActiveCount > 0;
70
- const params = {
71
- q: table.get(TABLE_KEYS.QUERY) || undefined,
72
- category: table.get(TABLE_KEYS.CATEGORY) || undefined,
73
- minPrice: table.get(TABLE_KEYS.MIN_PRICE) ? Number(table.get(TABLE_KEYS.MIN_PRICE)) : undefined,
74
- maxPrice: table.get(TABLE_KEYS.MAX_PRICE) ? Number(table.get(TABLE_KEYS.MAX_PRICE)) : undefined,
75
- sort: table.get(TABLE_KEYS.SORT) || DEFAULT_SORT,
76
- page: table.getNumber(TABLE_KEYS.PAGE, 1),
77
- perPage: table.getNumber(TABLE_KEYS.PAGE_SIZE, 24),
78
- listingType: "classified",
79
- };
80
- const { products, totalPages, page, isLoading } = useProducts(params, { initialData });
81
- const commitSearch = useCallback(() => {
82
- table.set(TABLE_KEYS.QUERY, searchInput.trim());
83
- }, [searchInput, table]);
84
- const selection = useBulkSelection({ items: products, keyExtractor: (p) => p.id });
85
- const gridClass = "grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 gap-[var(--appkit-space-4)]";
86
- const wishlistActions = {
87
- addToWishlist: (productId) => {
88
- requireAuth(ACTION_ID.ADD_TO_WISHLIST, () => {
89
- localWishlist.add(productId, "classified");
90
- pushWishlistOp({ op: "add", itemId: productId, type: "classified" });
91
- showToast("Added to wishlist", "success");
92
- });
93
- return Promise.resolve();
94
- },
95
- removeFromWishlist: (productId) => {
96
- requireAuth(ACTION_ID.REMOVE_FROM_WISHLIST, () => {
97
- localWishlist.remove(productId, "classified");
98
- pushWishlistOp({ op: "remove", itemId: productId, type: "classified" });
99
- showToast("Removed from wishlist", "info");
100
- });
101
- return Promise.resolve();
102
- },
103
- isWishlisted: (productId) => wishlistedIds.has(productId),
104
- };
105
- function handleToggleWishlist(id) {
106
- if (wishlistActions.isWishlisted(id)) {
107
- wishlistActions.removeFromWishlist(id);
108
- }
109
- else {
110
- wishlistActions.addToWishlist(id);
111
- }
112
- }
113
- useBottomActions(selection.selectedCount > 0 ? { bulk: { selectedCount: selection.selectedCount, onClearSelection: selection.clearSelection, actions: [] } } : {});
114
- return (_jsxs(Div, { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: filterActiveCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search classified listings\u2026", onSearchChange: setSearchInput, onSearchCommit: commitSearch, onSearchKeyDown: (e) => { if (e.key === "Enter")
115
- commitSearch(); }, sortValue: table.get(TABLE_KEYS.SORT) || DEFAULT_SORT, sortOptions: SORT_OPTIONS, onSortChange: (v) => { table.set(TABLE_KEYS.SORT, v); }, view: view, onViewChange: (v) => { if (v !== "table")
116
- setView(v); }, onResetAll: resetAll, hasActiveState: hasActiveState, bulkMode: selection.isSelecting, bulkSelectedCount: selection.selectedCount, bulkTotalCount: products.length, onBulkSelectAll: selection.toggleAll, onBulkClear: selection.clearSelection, toggles: [
117
- // Inline quick-filter: the highest-frequency drawer-only facet promoted
118
- // to the sticky toolbar so it doesn't require open-drawer → check → Apply
119
- // → close for every toggle. The full facet set (category/city/price/
120
- // negotiable) remains in <FilterDrawer> below.
121
- { label: "Ships nationwide", active: table.get(TABLE_KEYS.ACCEPTS_SHIPPING) === "true", onChange: (next) => table.set(TABLE_KEYS.ACCEPTS_SHIPPING, next ? "true" : "") },
122
- ] }), _jsx(BulkActionBar, { selectedCount: selection.selectedCount, onClearSelection: selection.clearSelection, actions: [] }), totalPages > 1 && (_jsx(StickyToolbar, { offset: "header+pagination", tone: "translucent", border: true, padding: "toolbar", children: _jsx(Row, { justify: "center", children: _jsx(Pagination, { currentPage: page, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) }) })), _jsx(Div, { padding: "y-lg", children: isLoading ? (_jsx(Div, { className: gridClass, children: Array.from({ length: 10 }).map((_, i) => (_jsxs(Div, { className: `${__O.hidden} animate-pulse`, border: "subtle", rounded: "xl", children: [_jsx(Div, { className: "aspect-square", surface: "subtle" }), _jsxs(Stack, { className: `${__P.p3}`, gap: "sm", children: [_jsx(Div, { className: "h-3 w-3/4", surface: "subtle", rounded: "default" }), _jsx(Div, { className: "h-4 w-1/3", surface: "subtle", rounded: "default" })] })] }, i))) })) : products.length === 0 ? (_jsx(Text, { paddingY: "3xl", color: "muted", size: "sm", align: "start", children: "No classified listings found." })) : (_jsx(Div, { className: view === "list" ? "flex flex-col divide-y divide-zinc-100 divide-[var(--appkit-color-border-subtle)] rounded-xl border border-[var(--appkit-color-border-subtle)]" : gridClass, children: products.map((product) => (_jsx(InteractiveProductCard, { product: product, href: String(ROUTES.PUBLIC.CLASSIFIED_DETAIL(product.id)), variant: view === "list" ? "list" : "grid", isWishlisted: wishlistActions.isWishlisted(product.id), onToggleWishlist: handleToggleWishlist, selectable: selection.isSelecting, isSelected: selection.isSelected(product.id), onSelect: (id) => selection.toggle(id) }, product.id))) })) }), _jsx(FilterDrawer, { open: filterOpen, onOpen: openFilters, onClose: () => setFilterOpen(false), onApply: applyFilters, onReset: onFilterClear, activeCount: filterActiveCount, hideTrigger: true, children: _jsx(ClassifiedFilters, { table: pendingTable, categoryOptions: categoryOptions }) }), _jsx(LoginRequiredModal, { isOpen: modalOpen, onClose: closeModal, message: modalMessage })] }));
123
- }
@@ -1,6 +0,0 @@
1
- type SearchParams = Record<string, string | string[]>;
2
- export interface ClassifiedListViewProps {
3
- searchParams?: SearchParams;
4
- }
5
- export declare function ClassifiedListView({ searchParams }: ClassifiedListViewProps): Promise<import("react").JSX.Element>;
6
- export {};
@@ -1,44 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { sieveFilter, SIEVE_OP } from "@mohasinac/appkit";
3
- import { productRepository } from "../../../repositories";
4
- import { Container, Heading, Main, Section } from "../../../ui";
5
- import { AdSlot } from "../../homepage/components/AdSlot";
6
- import { parseListingSearchParams } from "../../../utils/listing-params";
7
- import { ClassifiedIndexListing } from "./ClassifiedIndexListing";
8
- const DEFAULT_PAGE = 1;
9
- const DEFAULT_PAGE_SIZE = 24;
10
- const DEFAULT_SORT = "-createdAt";
11
- function sp(params, key) {
12
- const v = params[key];
13
- return Array.isArray(v) ? v[0] ?? "" : v ?? "";
14
- }
15
- function buildClassifiedFilters(params) {
16
- const parts = ["status==published", "listingType==classified"];
17
- const minPrice = sp(params, "minPrice");
18
- const maxPrice = sp(params, "maxPrice");
19
- if (minPrice)
20
- parts.push(sieveFilter("price", SIEVE_OP.GTE, minPrice));
21
- if (maxPrice)
22
- parts.push(sieveFilter("price", SIEVE_OP.LTE, maxPrice));
23
- const city = sp(params, "city");
24
- if (city)
25
- parts.push(sieveFilter("classifiedCity", SIEVE_OP.EQ, city));
26
- const negotiable = sp(params, "negotiable");
27
- if (negotiable === "true")
28
- parts.push(sieveFilter("classifiedNegotiable", SIEVE_OP.EQ, "true"));
29
- const acceptsShipping = sp(params, "acceptsShipping");
30
- if (acceptsShipping === "true")
31
- parts.push(sieveFilter("classifiedAcceptsShipping", SIEVE_OP.EQ, "true"));
32
- return parts.join(",");
33
- }
34
- export async function ClassifiedListView({ searchParams = {} }) {
35
- const std = parseListingSearchParams(searchParams);
36
- const sort = std.sorts ?? DEFAULT_SORT;
37
- const page = std.page ?? DEFAULT_PAGE;
38
- const pageSize = std.pageSize ?? DEFAULT_PAGE_SIZE;
39
- const filters = buildClassifiedFilters(searchParams);
40
- const result = await productRepository
41
- .list({ filters, sorts: sort, page, pageSize })
42
- .catch(() => null);
43
- return (_jsx(Main, { children: _jsx(Section, { padding: "y-2xl", children: _jsxs(Container, { size: "xl", children: [_jsx(Heading, { level: 1, className: "mb-8", color: "primary", size: "3xl", weight: "semibold", children: "Classifieds" }), _jsx(AdSlot, { id: "listing-sidebar-top", className: "mb-6" }), _jsx(ClassifiedIndexListing, { initialData: result ?? undefined }), _jsx(AdSlot, { id: "listing-sidebar-bottom", className: "mt-8" })] }) }) }));
44
- }
@@ -1,5 +0,0 @@
1
- import React from "react";
2
- export interface DigitalCodesIndexListingProps {
3
- initialData?: any;
4
- }
5
- export declare function DigitalCodesIndexListing({ initialData }: DigitalCodesIndexListingProps): React.JSX.Element;
@@ -1,114 +0,0 @@
1
- "use client";
2
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { useState, useCallback } from "react";
4
- import { useUrlTable } from "../../../react/hooks/useUrlTable";
5
- import { useProducts } from "../../products/hooks/useProducts";
6
- import { BulkActionBar, Div, FilterDrawer, ListingToolbar, LoginRequiredModal, Pagination, Row, Stack, Text, useToast, StickyToolbar } from "../../../ui";
7
- import { usePendingTable } from "../../../react/hooks/usePendingTable";
8
- import { useAuthGate } from "../../../react/hooks/useAuthGate";
9
- import { useBulkSelection } from "../../../react/hooks/useBulkSelection";
10
- import { useGuestWishlist } from "../../wishlist/hooks/useGuestWishlist";
11
- import { pushWishlistOp } from "../../cart/utils/pending-ops";
12
- import { useCategoryTree, categoriesToFacetOptions } from "../../categories/hooks/useCategoryTree";
13
- import { TABLE_KEYS, VIEW_MODE } from "../../../constants/table-keys";
14
- import { sortBy } from "../../../constants/sort";
15
- import { PRODUCT_FIELDS } from "../../../constants/field-names";
16
- import { ROUTES } from "../../../next";
17
- import { InteractiveProductCard } from "../../products/components/InteractiveProductCard";
18
- import { DigitalCodeFilters } from "./DigitalCodeFilters";
19
- import { ACTION_ID } from "../../products/constants/action-defs";
20
- import { useBottomActions } from "../../layout";
21
- const __P = {
22
- p3: "p-[var(--appkit-space-3)]",
23
- };
24
- const __O = {
25
- hidden: "overflow-hidden",
26
- };
27
- const DEFAULT_SORT = sortBy(PRODUCT_FIELDS.CREATED_AT);
28
- const SORT_OPTIONS = [
29
- { value: sortBy(PRODUCT_FIELDS.CREATED_AT), label: "Newest First" },
30
- { value: sortBy(PRODUCT_FIELDS.CREATED_AT, "ASC"), label: "Oldest First" },
31
- { value: sortBy(PRODUCT_FIELDS.PRICE, "ASC"), label: "Price: Low to High" },
32
- { value: sortBy(PRODUCT_FIELDS.PRICE), label: "Price: High to Low" },
33
- ];
34
- const FILTER_KEYS = [
35
- TABLE_KEYS.CATEGORY,
36
- TABLE_KEYS.DELIVERY_METHOD,
37
- TABLE_KEYS.MIN_PRICE,
38
- TABLE_KEYS.MAX_PRICE,
39
- ];
40
- export function DigitalCodesIndexListing({ initialData }) {
41
- const table = useUrlTable({ defaults: { pageSize: "24", sort: DEFAULT_SORT } });
42
- const { showToast } = useToast();
43
- const { requireAuth, modalOpen, modalMessage, closeModal } = useAuthGate();
44
- const [searchInput, setSearchInput] = useState(table.get(TABLE_KEYS.QUERY) || "");
45
- const [filterOpen, setFilterOpen] = useState(false);
46
- const [view, setView] = useState(table.get(TABLE_KEYS.VIEW) || VIEW_MODE.GRID);
47
- const localWishlist = useGuestWishlist();
48
- const { categories } = useCategoryTree();
49
- const categoryOptions = categoriesToFacetOptions(categories);
50
- const wishlistedIds = new Set(localWishlist.items.filter((i) => i.type === "digital-code").map((i) => i.itemId));
51
- const { pendingTable, filterActiveCount, onFilterApply, onFilterClear, onResetAll, onFilterReset } = usePendingTable(table, FILTER_KEYS);
52
- const openFilters = useCallback(() => {
53
- onFilterReset();
54
- setFilterOpen(true);
55
- }, [onFilterReset]);
56
- const applyFilters = useCallback(() => {
57
- onFilterApply();
58
- setFilterOpen(false);
59
- }, [onFilterApply]);
60
- const resetAll = useCallback(() => {
61
- onResetAll({ [TABLE_KEYS.QUERY]: "", [TABLE_KEYS.SORT]: "" });
62
- setSearchInput("");
63
- }, [onResetAll]);
64
- const hasActiveState = !!table.get(TABLE_KEYS.QUERY) ||
65
- table.get(TABLE_KEYS.SORT) !== DEFAULT_SORT ||
66
- filterActiveCount > 0;
67
- const params = {
68
- q: table.get(TABLE_KEYS.QUERY) || undefined,
69
- category: table.get(TABLE_KEYS.CATEGORY) || undefined,
70
- minPrice: table.get(TABLE_KEYS.MIN_PRICE) ? Number(table.get(TABLE_KEYS.MIN_PRICE)) : undefined,
71
- maxPrice: table.get(TABLE_KEYS.MAX_PRICE) ? Number(table.get(TABLE_KEYS.MAX_PRICE)) : undefined,
72
- sort: table.get(TABLE_KEYS.SORT) || DEFAULT_SORT,
73
- page: table.getNumber(TABLE_KEYS.PAGE, 1),
74
- perPage: table.getNumber(TABLE_KEYS.PAGE_SIZE, 24),
75
- listingType: "digital-code",
76
- };
77
- const { products, totalPages, page, isLoading } = useProducts(params, { initialData });
78
- const commitSearch = useCallback(() => {
79
- table.set(TABLE_KEYS.QUERY, searchInput.trim());
80
- }, [searchInput, table]);
81
- const selection = useBulkSelection({ items: products, keyExtractor: (p) => p.id });
82
- const gridClass = "grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 gap-[var(--appkit-space-4)]";
83
- const wishlistActions = {
84
- addToWishlist: (productId) => {
85
- requireAuth(ACTION_ID.ADD_TO_WISHLIST, () => {
86
- localWishlist.add(productId, "digital-code");
87
- pushWishlistOp({ op: "add", itemId: productId, type: "digital-code" });
88
- showToast("Added to wishlist", "success");
89
- });
90
- return Promise.resolve();
91
- },
92
- removeFromWishlist: (productId) => {
93
- requireAuth(ACTION_ID.REMOVE_FROM_WISHLIST, () => {
94
- localWishlist.remove(productId, "digital-code");
95
- pushWishlistOp({ op: "remove", itemId: productId, type: "digital-code" });
96
- showToast("Removed from wishlist", "info");
97
- });
98
- return Promise.resolve();
99
- },
100
- isWishlisted: (productId) => wishlistedIds.has(productId),
101
- };
102
- function handleToggleWishlist(id) {
103
- if (wishlistActions.isWishlisted(id)) {
104
- wishlistActions.removeFromWishlist(id);
105
- }
106
- else {
107
- wishlistActions.addToWishlist(id);
108
- }
109
- }
110
- useBottomActions(selection.selectedCount > 0 ? { bulk: { selectedCount: selection.selectedCount, onClearSelection: selection.clearSelection, actions: [] } } : {});
111
- return (_jsxs(Div, { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: filterActiveCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search digital codes\u2026", onSearchChange: setSearchInput, onSearchCommit: commitSearch, onSearchKeyDown: (e) => { if (e.key === "Enter")
112
- commitSearch(); }, sortValue: table.get(TABLE_KEYS.SORT) || DEFAULT_SORT, sortOptions: SORT_OPTIONS, onSortChange: (v) => { table.set(TABLE_KEYS.SORT, v); }, view: view, onViewChange: (v) => { if (v !== "table")
113
- setView(v); }, onResetAll: resetAll, hasActiveState: hasActiveState, bulkMode: selection.isSelecting, bulkSelectedCount: selection.selectedCount, bulkTotalCount: products.length, onBulkSelectAll: selection.toggleAll, onBulkClear: selection.clearSelection }), _jsx(BulkActionBar, { selectedCount: selection.selectedCount, onClearSelection: selection.clearSelection, actions: [] }), totalPages > 1 && (_jsx(StickyToolbar, { offset: "header+pagination", tone: "translucent", border: true, padding: "toolbar", children: _jsx(Row, { justify: "center", children: _jsx(Pagination, { currentPage: page, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) }) })), _jsx(Div, { padding: "y-lg", children: isLoading ? (_jsx(Div, { className: gridClass, children: Array.from({ length: 10 }).map((_, i) => (_jsxs(Div, { className: `${__O.hidden} animate-pulse`, border: "subtle", rounded: "xl", children: [_jsx(Div, { className: "aspect-square", surface: "subtle" }), _jsxs(Stack, { className: `${__P.p3}`, gap: "sm", children: [_jsx(Div, { className: "h-3 w-3/4", surface: "subtle", rounded: "default" }), _jsx(Div, { className: "h-4 w-1/3", surface: "subtle", rounded: "default" })] })] }, i))) })) : products.length === 0 ? (_jsx(Text, { paddingY: "3xl", color: "muted", size: "sm", align: "start", children: "No digital code listings found." })) : (_jsx(Div, { className: view === "list" ? "flex flex-col divide-y divide-zinc-100 divide-[var(--appkit-color-border-subtle)] rounded-xl border border-[var(--appkit-color-border-subtle)]" : gridClass, children: products.map((product) => (_jsx(InteractiveProductCard, { product: product, href: String(ROUTES.PUBLIC.DIGITAL_CODE_DETAIL(product.id)), variant: view === "list" ? "list" : "grid", isWishlisted: wishlistActions.isWishlisted(product.id), onToggleWishlist: handleToggleWishlist, selectable: selection.isSelecting, isSelected: selection.isSelected(product.id), onSelect: (id) => selection.toggle(id) }, product.id))) })) }), _jsx(FilterDrawer, { open: filterOpen, onOpen: openFilters, onClose: () => setFilterOpen(false), onApply: applyFilters, onReset: onFilterClear, activeCount: filterActiveCount, hideTrigger: true, children: _jsx(DigitalCodeFilters, { table: pendingTable, categoryOptions: categoryOptions }) }), _jsx(LoginRequiredModal, { isOpen: modalOpen, onClose: closeModal, message: modalMessage })] }));
114
- }
@@ -1,6 +0,0 @@
1
- type SearchParams = Record<string, string | string[]>;
2
- export interface DigitalCodesListViewProps {
3
- searchParams?: SearchParams;
4
- }
5
- export declare function DigitalCodesListView({ searchParams }: DigitalCodesListViewProps): Promise<import("react").JSX.Element>;
6
- export {};
@@ -1,38 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { sieveFilter, SIEVE_OP } from "@mohasinac/appkit";
3
- import { productRepository } from "../../../repositories";
4
- import { Container, Heading, Main, Section } from "../../../ui";
5
- import { AdSlot } from "../../homepage/components/AdSlot";
6
- import { parseListingSearchParams } from "../../../utils/listing-params";
7
- import { DigitalCodesIndexListing } from "./DigitalCodesIndexListing";
8
- const DEFAULT_PAGE = 1;
9
- const DEFAULT_PAGE_SIZE = 24;
10
- const DEFAULT_SORT = "-createdAt";
11
- function sp(params, key) {
12
- const v = params[key];
13
- return Array.isArray(v) ? v[0] ?? "" : v ?? "";
14
- }
15
- function buildDigitalCodeFilters(params) {
16
- const parts = ["status==published", "listingType==digital-code"];
17
- const minPrice = sp(params, "minPrice");
18
- const maxPrice = sp(params, "maxPrice");
19
- if (minPrice)
20
- parts.push(sieveFilter("price", SIEVE_OP.GTE, minPrice));
21
- if (maxPrice)
22
- parts.push(sieveFilter("price", SIEVE_OP.LTE, maxPrice));
23
- const deliveryMethod = sp(params, "deliveryMethod");
24
- if (deliveryMethod)
25
- parts.push(sieveFilter("digitalCodeDelivery", SIEVE_OP.EQ, deliveryMethod));
26
- return parts.join(",");
27
- }
28
- export async function DigitalCodesListView({ searchParams = {} }) {
29
- const std = parseListingSearchParams(searchParams);
30
- const sort = std.sorts ?? DEFAULT_SORT;
31
- const page = std.page ?? DEFAULT_PAGE;
32
- const pageSize = std.pageSize ?? DEFAULT_PAGE_SIZE;
33
- const filters = buildDigitalCodeFilters(searchParams);
34
- const result = await productRepository
35
- .list({ filters, sorts: sort, page, pageSize })
36
- .catch(() => null);
37
- return (_jsx(Main, { children: _jsx(Section, { padding: "y-2xl", children: _jsxs(Container, { size: "xl", children: [_jsx(Heading, { level: 1, className: "mb-8", color: "primary", size: "3xl", weight: "semibold", children: "Digital Codes" }), _jsx(AdSlot, { id: "listing-sidebar-top", className: "mb-6" }), _jsx(DigitalCodesIndexListing, { initialData: result ?? undefined }), _jsx(AdSlot, { id: "listing-sidebar-bottom", className: "mt-8" })] }) }) }));
38
- }
@@ -1,5 +0,0 @@
1
- import React from "react";
2
- export interface LiveItemsIndexListingProps {
3
- initialData?: any;
4
- }
5
- export declare function LiveItemsIndexListing({ initialData }: LiveItemsIndexListingProps): React.JSX.Element;
@@ -1,115 +0,0 @@
1
- "use client";
2
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { useState, useCallback } from "react";
4
- import { useUrlTable } from "../../../react/hooks/useUrlTable";
5
- import { useProducts } from "../../products/hooks/useProducts";
6
- import { BulkActionBar, Div, FilterDrawer, ListingToolbar, LoginRequiredModal, Pagination, Row, Stack, Text, useToast, StickyToolbar } from "../../../ui";
7
- import { usePendingTable } from "../../../react/hooks/usePendingTable";
8
- import { useAuthGate } from "../../../react/hooks/useAuthGate";
9
- import { useBulkSelection } from "../../../react/hooks/useBulkSelection";
10
- import { useGuestWishlist } from "../../wishlist/hooks/useGuestWishlist";
11
- import { pushWishlistOp } from "../../cart/utils/pending-ops";
12
- import { useCategoryTree, categoriesToFacetOptions } from "../../categories/hooks/useCategoryTree";
13
- import { TABLE_KEYS, VIEW_MODE } from "../../../constants/table-keys";
14
- import { sortBy } from "../../../constants/sort";
15
- import { PRODUCT_FIELDS } from "../../../constants/field-names";
16
- import { ROUTES } from "../../../next";
17
- import { InteractiveProductCard } from "../../products/components/InteractiveProductCard";
18
- import { LiveItemFilters } from "./LiveItemFilters";
19
- import { ACTION_ID } from "../../products/constants/action-defs";
20
- import { useBottomActions } from "../../layout";
21
- const __P = {
22
- p3: "p-[var(--appkit-space-3)]",
23
- };
24
- const __O = {
25
- hidden: "overflow-hidden",
26
- };
27
- const DEFAULT_SORT = sortBy(PRODUCT_FIELDS.CREATED_AT);
28
- const SORT_OPTIONS = [
29
- { value: sortBy(PRODUCT_FIELDS.CREATED_AT), label: "Newest First" },
30
- { value: sortBy(PRODUCT_FIELDS.CREATED_AT, "ASC"), label: "Oldest First" },
31
- { value: sortBy(PRODUCT_FIELDS.PRICE, "ASC"), label: "Price: Low to High" },
32
- { value: sortBy(PRODUCT_FIELDS.PRICE), label: "Price: High to Low" },
33
- ];
34
- const FILTER_KEYS = [
35
- TABLE_KEYS.CATEGORY,
36
- TABLE_KEYS.SPECIES,
37
- TABLE_KEYS.JURISDICTION,
38
- TABLE_KEYS.MIN_PRICE,
39
- TABLE_KEYS.MAX_PRICE,
40
- ];
41
- export function LiveItemsIndexListing({ initialData }) {
42
- const table = useUrlTable({ defaults: { pageSize: "24", sort: DEFAULT_SORT } });
43
- const { showToast } = useToast();
44
- const { requireAuth, modalOpen, modalMessage, closeModal } = useAuthGate();
45
- const [searchInput, setSearchInput] = useState(table.get(TABLE_KEYS.QUERY) || "");
46
- const [filterOpen, setFilterOpen] = useState(false);
47
- const [view, setView] = useState(table.get(TABLE_KEYS.VIEW) || VIEW_MODE.GRID);
48
- const localWishlist = useGuestWishlist();
49
- const { categories } = useCategoryTree();
50
- const categoryOptions = categoriesToFacetOptions(categories);
51
- const wishlistedIds = new Set(localWishlist.items.filter((i) => i.type === "live").map((i) => i.itemId));
52
- const { pendingTable, filterActiveCount, onFilterApply, onFilterClear, onResetAll, onFilterReset } = usePendingTable(table, FILTER_KEYS);
53
- const openFilters = useCallback(() => {
54
- onFilterReset();
55
- setFilterOpen(true);
56
- }, [onFilterReset]);
57
- const applyFilters = useCallback(() => {
58
- onFilterApply();
59
- setFilterOpen(false);
60
- }, [onFilterApply]);
61
- const resetAll = useCallback(() => {
62
- onResetAll({ [TABLE_KEYS.QUERY]: "", [TABLE_KEYS.SORT]: "" });
63
- setSearchInput("");
64
- }, [onResetAll]);
65
- const hasActiveState = !!table.get(TABLE_KEYS.QUERY) ||
66
- table.get(TABLE_KEYS.SORT) !== DEFAULT_SORT ||
67
- filterActiveCount > 0;
68
- const params = {
69
- q: table.get(TABLE_KEYS.QUERY) || undefined,
70
- category: table.get(TABLE_KEYS.CATEGORY) || undefined,
71
- minPrice: table.get(TABLE_KEYS.MIN_PRICE) ? Number(table.get(TABLE_KEYS.MIN_PRICE)) : undefined,
72
- maxPrice: table.get(TABLE_KEYS.MAX_PRICE) ? Number(table.get(TABLE_KEYS.MAX_PRICE)) : undefined,
73
- sort: table.get(TABLE_KEYS.SORT) || DEFAULT_SORT,
74
- page: table.getNumber(TABLE_KEYS.PAGE, 1),
75
- perPage: table.getNumber(TABLE_KEYS.PAGE_SIZE, 24),
76
- listingType: "live",
77
- };
78
- const { products, totalPages, page, isLoading } = useProducts(params, { initialData });
79
- const commitSearch = useCallback(() => {
80
- table.set(TABLE_KEYS.QUERY, searchInput.trim());
81
- }, [searchInput, table]);
82
- const selection = useBulkSelection({ items: products, keyExtractor: (p) => p.id });
83
- const gridClass = "grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 gap-[var(--appkit-space-4)]";
84
- const wishlistActions = {
85
- addToWishlist: (productId) => {
86
- requireAuth(ACTION_ID.ADD_TO_WISHLIST, () => {
87
- localWishlist.add(productId, "live");
88
- pushWishlistOp({ op: "add", itemId: productId, type: "live" });
89
- showToast("Added to wishlist", "success");
90
- });
91
- return Promise.resolve();
92
- },
93
- removeFromWishlist: (productId) => {
94
- requireAuth(ACTION_ID.REMOVE_FROM_WISHLIST, () => {
95
- localWishlist.remove(productId, "live");
96
- pushWishlistOp({ op: "remove", itemId: productId, type: "live" });
97
- showToast("Removed from wishlist", "info");
98
- });
99
- return Promise.resolve();
100
- },
101
- isWishlisted: (productId) => wishlistedIds.has(productId),
102
- };
103
- function handleToggleWishlist(id) {
104
- if (wishlistActions.isWishlisted(id)) {
105
- wishlistActions.removeFromWishlist(id);
106
- }
107
- else {
108
- wishlistActions.addToWishlist(id);
109
- }
110
- }
111
- useBottomActions(selection.selectedCount > 0 ? { bulk: { selectedCount: selection.selectedCount, onClearSelection: selection.clearSelection, actions: [] } } : {});
112
- return (_jsxs(Div, { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: filterActiveCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search live items\u2026", onSearchChange: setSearchInput, onSearchCommit: commitSearch, onSearchKeyDown: (e) => { if (e.key === "Enter")
113
- commitSearch(); }, sortValue: table.get(TABLE_KEYS.SORT) || DEFAULT_SORT, sortOptions: SORT_OPTIONS, onSortChange: (v) => { table.set(TABLE_KEYS.SORT, v); }, view: view, onViewChange: (v) => { if (v !== "table")
114
- setView(v); }, onResetAll: resetAll, hasActiveState: hasActiveState, bulkMode: selection.isSelecting, bulkSelectedCount: selection.selectedCount, bulkTotalCount: products.length, onBulkSelectAll: selection.toggleAll, onBulkClear: selection.clearSelection }), _jsx(BulkActionBar, { selectedCount: selection.selectedCount, onClearSelection: selection.clearSelection, actions: [] }), totalPages > 1 && (_jsx(StickyToolbar, { offset: "header+pagination", tone: "translucent", border: true, padding: "toolbar", children: _jsx(Row, { justify: "center", children: _jsx(Pagination, { currentPage: page, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) }) })), _jsx(Div, { padding: "y-lg", children: isLoading ? (_jsx(Div, { className: gridClass, children: Array.from({ length: 10 }).map((_, i) => (_jsxs(Div, { className: `${__O.hidden} animate-pulse`, border: "subtle", rounded: "xl", children: [_jsx(Div, { className: "aspect-square", surface: "subtle" }), _jsxs(Stack, { className: `${__P.p3}`, gap: "sm", children: [_jsx(Div, { className: "h-3 w-3/4", surface: "subtle", rounded: "default" }), _jsx(Div, { className: "h-4 w-1/3", surface: "subtle", rounded: "default" })] })] }, i))) })) : products.length === 0 ? (_jsx(Text, { paddingY: "3xl", color: "muted", size: "sm", align: "start", children: "No live item listings found." })) : (_jsx(Div, { className: view === "list" ? "flex flex-col divide-y divide-zinc-100 divide-[var(--appkit-color-border-subtle)] rounded-xl border border-[var(--appkit-color-border-subtle)]" : gridClass, children: products.map((product) => (_jsx(InteractiveProductCard, { product: product, href: String(ROUTES.PUBLIC.LIVE_DETAIL(product.id)), variant: view === "list" ? "list" : "grid", isWishlisted: wishlistActions.isWishlisted(product.id), onToggleWishlist: handleToggleWishlist, selectable: selection.isSelecting, isSelected: selection.isSelected(product.id), onSelect: (id) => selection.toggle(id) }, product.id))) })) }), _jsx(FilterDrawer, { open: filterOpen, onOpen: openFilters, onClose: () => setFilterOpen(false), onApply: applyFilters, onReset: onFilterClear, activeCount: filterActiveCount, hideTrigger: true, children: _jsx(LiveItemFilters, { table: pendingTable, categoryOptions: categoryOptions }) }), _jsx(LoginRequiredModal, { isOpen: modalOpen, onClose: closeModal, message: modalMessage })] }));
115
- }