@mohasinac/appkit 4.9.5 → 4.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (395) 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/auctions/actions.js +3 -2
  8. package/dist/_internal/server/features/auctions/service.d.ts +4 -3
  9. package/dist/_internal/server/features/auctions/service.js +6 -6
  10. package/dist/_internal/server/features/audit-log/actions.d.ts +25 -0
  11. package/dist/_internal/server/features/audit-log/actions.js +38 -0
  12. package/dist/_internal/server/features/brands/actions.js +0 -2
  13. package/dist/_internal/server/features/brands/og.d.ts +4 -1
  14. package/dist/_internal/server/features/brands/og.js +1 -1
  15. package/dist/_internal/server/features/bundles/data.d.ts +9 -0
  16. package/dist/_internal/server/features/bundles/data.js +14 -0
  17. package/dist/_internal/server/features/bundles/index.d.ts +1 -1
  18. package/dist/_internal/server/features/bundles/index.js +1 -1
  19. package/dist/_internal/server/features/catalogue/og.d.ts +20 -0
  20. package/dist/_internal/server/features/catalogue/og.js +27 -0
  21. package/dist/_internal/server/features/grouped/data.d.ts +16 -0
  22. package/dist/_internal/server/features/grouped/data.js +50 -1
  23. package/dist/_internal/server/features/grouped/index.d.ts +1 -1
  24. package/dist/_internal/server/features/grouped/index.js +1 -1
  25. package/dist/_internal/server/features/orders/actions.js +9 -0
  26. package/dist/_internal/server/features/orders/adapters.js +25 -0
  27. package/dist/_internal/server/features/products/data.d.ts +23 -0
  28. package/dist/_internal/server/features/products/data.js +159 -0
  29. package/dist/_internal/server/features/products/index.d.ts +1 -1
  30. package/dist/_internal/server/features/products/index.js +1 -1
  31. package/dist/_internal/server/functions/scheduled.d.ts +3 -1
  32. package/dist/_internal/server/functions/scheduled.js +17 -1
  33. package/dist/_internal/server/jobs/core/dailyStatusDigest.d.ts +40 -0
  34. package/dist/_internal/server/jobs/core/dailyStatusDigest.js +164 -0
  35. package/dist/_internal/server/jobs/core/hardBanCascade.js +11 -0
  36. package/dist/_internal/server/jobs/core/testerSandboxCleanup.d.ts +1 -0
  37. package/dist/_internal/server/jobs/core/testerSandboxCleanup.js +4 -1
  38. package/dist/_internal/server/jobs/core/testerSandboxRefresh.d.ts +12 -0
  39. package/dist/_internal/server/jobs/core/testerSandboxRefresh.js +85 -0
  40. package/dist/_internal/server/jobs/core/whatsappNotify.js +16 -0
  41. package/dist/_internal/server/jobs/handlers/dailyStatusDigest.d.ts +2 -0
  42. package/dist/_internal/server/jobs/handlers/dailyStatusDigest.js +4 -0
  43. package/dist/_internal/server/jobs/handlers/index.d.ts +2 -0
  44. package/dist/_internal/server/jobs/handlers/index.js +3 -0
  45. package/dist/_internal/server/jobs/handlers/testerSandboxRefresh.d.ts +2 -0
  46. package/dist/_internal/server/jobs/handlers/testerSandboxRefresh.js +2 -0
  47. package/dist/_internal/server/notifications/channel-health.d.ts +27 -0
  48. package/dist/_internal/server/notifications/channel-health.js +101 -0
  49. package/dist/_internal/shared/features/auctions/config.d.ts +20 -0
  50. package/dist/_internal/shared/features/auctions/config.js +31 -1
  51. package/dist/_internal/shared/features/brands/schema.d.ts +0 -6
  52. package/dist/_internal/shared/features/brands/schema.js +0 -1
  53. package/dist/_internal/shared/features/categories/bundle-schemas.d.ts +7 -0
  54. package/dist/_internal/shared/features/categories/bundle-schemas.js +3 -0
  55. package/dist/_internal/shared/features/orders/schema.d.ts +8 -8
  56. package/dist/_internal/shared/listing-types/art/config.d.ts +1 -1
  57. package/dist/_internal/shared/listing-types/art/config.js +1 -1
  58. package/dist/_internal/shared/listing-types/classified/config.d.ts +1 -1
  59. package/dist/_internal/shared/listing-types/classified/config.js +1 -1
  60. package/dist/_internal/shared/listing-types/digital-code/config.d.ts +1 -1
  61. package/dist/_internal/shared/listing-types/digital-code/config.js +1 -1
  62. package/dist/_internal/shared/listing-types/live/config.d.ts +1 -1
  63. package/dist/_internal/shared/listing-types/live/config.js +1 -1
  64. package/dist/_internal/shared/listing-types/prize-draw/config.d.ts +1 -1
  65. package/dist/_internal/shared/listing-types/prize-draw/config.js +1 -1
  66. package/dist/_internal/shared/listing-types/stickers/config.d.ts +1 -1
  67. package/dist/_internal/shared/listing-types/stickers/config.js +1 -1
  68. package/dist/client.d.ts +21 -12
  69. package/dist/client.js +14 -7
  70. package/dist/constants/api-endpoints.d.ts +9 -9
  71. package/dist/constants/api-endpoints.js +3 -3
  72. package/dist/contracts/client-auth.d.ts +3 -7
  73. package/dist/contracts/email.d.ts +1 -0
  74. package/dist/features/account/components/AddressForm.js +24 -2
  75. package/dist/features/account/components/BecomeSellerView.d.ts +1 -2
  76. package/dist/features/account/components/BecomeSellerView.js +1 -1
  77. package/dist/features/account/components/NotificationBell.js +1 -1
  78. package/dist/features/account/components/UserBidsView.d.ts +1 -0
  79. package/dist/features/account/components/UserBidsView.js +47 -0
  80. package/dist/features/account/components/UserOrdersView.d.ts +4 -11
  81. package/dist/features/account/components/UserOrdersView.js +54 -4
  82. package/dist/features/account/components/UserReturnsView.d.ts +4 -9
  83. package/dist/features/account/components/UserReturnsView.js +37 -3
  84. package/dist/features/account/components/index.d.ts +3 -2
  85. package/dist/features/account/components/index.js +1 -0
  86. package/dist/features/admin/actions/admin-actions.js +22 -0
  87. package/dist/features/admin/actions/admin-coupon-actions.js +10 -0
  88. package/dist/features/admin/actions/notification-actions.js +4 -3
  89. package/dist/features/admin/components/AdminAdsView.d.ts +2 -3
  90. package/dist/features/admin/components/AdminAdsView.js +86 -89
  91. package/dist/features/admin/components/AdminAllEventEntriesView.js +18 -3
  92. package/dist/features/admin/components/AdminAuditLogView.d.ts +11 -0
  93. package/dist/features/admin/components/AdminAuditLogView.js +95 -0
  94. package/dist/features/admin/components/AdminBlogEditorView.js +37 -3
  95. package/dist/features/admin/components/AdminBrandEditorView.js +10 -6
  96. package/dist/features/admin/components/AdminBundleEditorView.js +18 -2
  97. package/dist/features/admin/components/AdminCarouselEditorView.js +2 -2
  98. package/dist/features/admin/components/AdminCarouselGroupEditorView.js +2 -2
  99. package/dist/features/admin/components/AdminCartsView.js +34 -4
  100. package/dist/features/admin/components/AdminCategoryEditorView.js +5 -4
  101. package/dist/features/admin/components/AdminCouponEditorView.js +2 -2
  102. package/dist/features/admin/components/AdminFaqEditorView.js +2 -2
  103. package/dist/features/admin/components/AdminHistoryView.js +11 -1
  104. package/dist/features/admin/components/AdminNotificationsView.js +27 -1
  105. package/dist/features/admin/components/AdminOrderEditorView.d.ts +10 -1
  106. package/dist/features/admin/components/AdminOrderEditorView.js +4 -3
  107. package/dist/features/admin/components/AdminOrdersView.js +92 -24
  108. package/dist/features/admin/components/AdminPayoutMarkPaidModal.d.ts +7 -0
  109. package/dist/features/admin/components/AdminPayoutMarkPaidModal.js +47 -0
  110. package/dist/features/admin/components/AdminPayoutsView.js +15 -31
  111. package/dist/features/admin/components/AdminProductEditorView.js +26 -3
  112. package/dist/features/admin/components/AdminSiteSettingsView.js +31 -4
  113. package/dist/features/admin/components/AdminStoreAddressesView.js +22 -2
  114. package/dist/features/admin/components/AdminStoresView.js +7 -0
  115. package/dist/features/admin/components/AdminSublistingCategoryEditorView.js +2 -2
  116. package/dist/features/admin/components/AdminTesterChecklistItemEditorView.js +2 -2
  117. package/dist/features/admin/components/AdminUserEditorView.d.ts +2 -2
  118. package/dist/features/admin/components/AdminUserEditorView.js +4 -2
  119. package/dist/features/admin/components/AdminUsersView.js +1 -1
  120. package/dist/features/admin/components/AdminWishlistsView.js +14 -1
  121. package/dist/features/admin/components/DataListingView.d.ts +2 -2
  122. package/dist/features/admin/components/ViewAuditLogEntryModal.d.ts +19 -0
  123. package/dist/features/admin/components/ViewAuditLogEntryModal.js +22 -0
  124. package/dist/features/admin/components/ViewNotificationModal.d.ts +21 -0
  125. package/dist/features/admin/components/ViewNotificationModal.js +15 -0
  126. package/dist/features/admin/components/analytics/AdminPageViewsReportView.d.ts +4 -7
  127. package/dist/features/admin/components/analytics/AdminPageViewsReportView.js +56 -36
  128. package/dist/features/admin/components/index.d.ts +8 -1
  129. package/dist/features/admin/components/index.js +4 -0
  130. package/dist/features/admin/constants/filter-tabs.d.ts +49 -0
  131. package/dist/features/admin/constants/filter-tabs.js +27 -0
  132. package/dist/features/admin/hooks/useAdminListing.d.ts +6 -0
  133. package/dist/features/admin/hooks/useAdminListing.js +3 -1
  134. package/dist/features/admin/hooks/useAdminListingData.d.ts +9 -1
  135. package/dist/features/admin/hooks/useAdminListingData.js +9 -2
  136. package/dist/features/admin/repository/site-settings.repository.d.ts +5 -0
  137. package/dist/features/admin/schemas/firestore.d.ts +21 -0
  138. package/dist/features/admin/schemas/firestore.js +6 -0
  139. package/dist/features/analytics/types.d.ts +1 -1
  140. package/dist/features/analytics/types.js +8 -0
  141. package/dist/features/auctions/actions/bid-actions.js +4 -1
  142. package/dist/features/auctions/components/AuctionBottomActions.d.ts +2 -1
  143. package/dist/features/auctions/components/AuctionBottomActions.js +14 -1
  144. package/dist/features/auctions/components/AuctionDetailPageView.js +21 -9
  145. package/dist/features/auctions/components/LiveMinIncrement.d.ts +18 -0
  146. package/dist/features/auctions/components/LiveMinIncrement.js +17 -0
  147. package/dist/features/auctions/components/PlaceBidFormClient.d.ts +5 -2
  148. package/dist/features/auctions/components/PlaceBidFormClient.js +7 -3
  149. package/dist/features/auctions/schemas/index.d.ts +5 -0
  150. package/dist/features/audit-log/repository/audit-log.repository.d.ts +45 -0
  151. package/dist/features/audit-log/repository/audit-log.repository.js +30 -0
  152. package/dist/features/audit-log/schemas/firestore.d.ts +59 -0
  153. package/dist/features/audit-log/schemas/firestore.js +35 -0
  154. package/dist/features/auth/actions/index.d.ts +0 -1
  155. package/dist/features/auth/actions/index.js +0 -1
  156. package/dist/features/auth/hooks/useAuth.d.ts +8 -35
  157. package/dist/features/auth/hooks/useAuth.js +21 -37
  158. package/dist/features/auth/permissions/constants.d.ts +1 -1
  159. package/dist/features/auth/permissions/constants.js +2 -0
  160. package/dist/features/blog/schemas/index.d.ts +32 -0
  161. package/dist/features/catalogue/components/AdminCatalogueApprovalsView.js +31 -3
  162. package/dist/features/catalogue/components/CatalogueItemEditorView.js +2 -1
  163. package/dist/features/catalogue/components/PublicCatalogueItemDetailView.d.ts +11 -0
  164. package/dist/features/catalogue/components/PublicCatalogueItemDetailView.js +33 -0
  165. package/dist/features/catalogue/components/PublicCatalogueView.js +2 -1
  166. package/dist/features/catalogue/index.d.ts +2 -0
  167. package/dist/features/catalogue/index.js +1 -0
  168. package/dist/features/categories/components/BrandDetailPageView.js +14 -12
  169. package/dist/features/categories/components/BundleDetailView.d.ts +3 -1
  170. package/dist/features/categories/components/BundleDetailView.js +3 -2
  171. package/dist/features/categories/components/CategoryDetailPageView.js +7 -3
  172. package/dist/features/categories/components/CategoryHighlightsAndFaqSection.d.ts +14 -0
  173. package/dist/features/categories/components/CategoryHighlightsAndFaqSection.js +22 -0
  174. package/dist/features/categories/components/CategoryProductsView.d.ts +1 -5
  175. package/dist/features/categories/components/CategoryProductsView.js +1 -1
  176. package/dist/features/categories/components/CategorySelectorCreate.js +16 -2
  177. package/dist/features/categories/schemas/firestore.d.ts +19 -2
  178. package/dist/features/categories/schemas/index.d.ts +24 -3
  179. package/dist/features/categories/schemas/index.js +3 -1
  180. package/dist/features/categories/types/index.d.ts +7 -0
  181. package/dist/features/checkout/actions/checkout-value-otp-actions.d.ts +17 -3
  182. package/dist/features/checkout/actions/checkout-value-otp-actions.js +101 -22
  183. package/dist/features/classified/components/ClassifiedContactSellerPanel.d.ts +12 -0
  184. package/dist/features/classified/components/ClassifiedContactSellerPanel.js +49 -0
  185. package/dist/features/classified/components/ClassifiedDetailPageView.d.ts +7 -0
  186. package/dist/features/classified/components/ClassifiedDetailPageView.js +69 -0
  187. package/dist/features/contact/components/ContactForm.js +17 -4
  188. package/dist/features/contact/components/ContactPageView.js +8 -2
  189. package/dist/features/contact/email.d.ts +1 -0
  190. package/dist/features/contact/email.js +3 -0
  191. package/dist/features/digital-codes/components/DigitalCodeDetailPageView.d.ts +19 -0
  192. package/dist/features/digital-codes/components/DigitalCodeDetailPageView.js +79 -0
  193. package/dist/features/events/components/AdminEventEditorView.js +54 -2
  194. package/dist/features/events/components/AdminEventEntriesView.d.ts +2 -16
  195. package/dist/features/events/components/AdminEventEntriesView.js +56 -89
  196. package/dist/features/events/components/EventFormDrawer.js +5 -1
  197. package/dist/features/events/components/EventRaffleEntryForm.d.ts +1 -3
  198. package/dist/features/events/components/EventRaffleEntryForm.js +1 -1
  199. package/dist/features/events/schemas/index.d.ts +22 -0
  200. package/dist/features/grouped/components/GroupedListingsCarousel.d.ts +13 -0
  201. package/dist/features/grouped/components/GroupedListingsCarousel.js +31 -0
  202. package/dist/features/grouped/repository/grouped-listings.repository.d.ts +12 -0
  203. package/dist/features/grouped/repository/grouped-listings.repository.js +34 -0
  204. package/dist/features/homepage/components/CharacterHotspotForm.js +202 -187
  205. package/dist/features/homepage/components/SectionCarousel.d.ts +1 -7
  206. package/dist/features/homepage/components/SectionCarousel.js +1 -1
  207. package/dist/features/layout/BottomActions.js +4 -3
  208. package/dist/features/layout/BottomActionsContext.d.ts +10 -0
  209. package/dist/features/layout/BottomActionsContext.js +5 -1
  210. package/dist/features/layout/TitleBarLayout.d.ts +1 -1
  211. package/dist/features/layout/TitleBarLayout.js +3 -3
  212. package/dist/features/layout/hooks/useBottomActions.d.ts +2 -0
  213. package/dist/features/layout/hooks/useBottomActions.js +4 -1
  214. package/dist/features/live/components/LiveItemDetailPageView.d.ts +9 -0
  215. package/dist/features/live/components/LiveItemDetailPageView.js +74 -0
  216. package/dist/features/media/MediaImage.js +39 -3
  217. package/dist/features/media/MediaVideo.js +17 -1
  218. package/dist/features/media/types/index.d.ts +10 -0
  219. package/dist/features/media/types/index.js +1 -0
  220. package/dist/features/media/upload/MediaUploadField.d.ts +7 -1
  221. package/dist/features/media/upload/MediaUploadField.js +11 -7
  222. package/dist/features/media/upload/video-poster.d.ts +2 -0
  223. package/dist/features/media/upload/video-poster.js +16 -1
  224. package/dist/features/media/upload/video-poster.test.js +1 -1
  225. package/dist/features/orders/components/OrderStatusTimeline.d.ts +12 -0
  226. package/dist/features/orders/components/OrderStatusTimeline.js +55 -0
  227. package/dist/features/orders/components/index.d.ts +2 -0
  228. package/dist/features/orders/components/index.js +1 -0
  229. package/dist/features/orders/constants/payment-window.d.ts +25 -0
  230. package/dist/features/orders/constants/payment-window.js +25 -0
  231. package/dist/features/orders/repository/orders.repository.d.ts +32 -0
  232. package/dist/features/orders/repository/orders.repository.js +56 -0
  233. package/dist/features/orders/schemas/index.d.ts +24 -24
  234. package/dist/features/orders/types/index.d.ts +33 -0
  235. package/dist/features/pre-orders/components/PreOrderDetailPageView.js +9 -1
  236. package/dist/features/pre-orders/schemas/index.d.ts +5 -0
  237. package/dist/features/products/components/BidHistory.js +9 -1
  238. package/dist/features/products/components/GroupSettingsPanel.js +25 -3
  239. package/dist/features/products/components/PrizeDrawDetailPageView.js +11 -1
  240. package/dist/features/products/components/PrizeDrawEntryActions.d.ts +1 -4
  241. package/dist/features/products/components/PrizeDrawEntryActions.js +1 -1
  242. package/dist/features/products/components/ProductDetailActions.d.ts +1 -2
  243. package/dist/features/products/components/ProductDetailActions.js +1 -1
  244. package/dist/features/products/components/ProductDetailPageView.js +11 -116
  245. package/dist/features/products/components/ProductGrid.js +15 -1
  246. package/dist/features/products/components/RelatedItemsSection.d.ts +16 -0
  247. package/dist/features/products/components/RelatedItemsSection.js +17 -0
  248. package/dist/features/products/constants/action-defs.d.ts +1 -0
  249. package/dist/features/products/constants/action-defs.js +1 -0
  250. package/dist/features/products/schemas/index.d.ts +5 -0
  251. package/dist/features/promotions/components/CouponsIndexListing.d.ts +1 -3
  252. package/dist/features/promotions/components/CouponsIndexListing.js +1 -1
  253. package/dist/features/search/components/SearchView.d.ts +1 -3
  254. package/dist/features/search/components/SearchView.js +1 -1
  255. package/dist/features/seller/components/QuickProductForm.js +2 -1
  256. package/dist/features/seller/components/SellerBidsView.js +66 -107
  257. package/dist/features/seller/components/SellerGoogleReviewsView.js +17 -1
  258. package/dist/features/seller/components/SellerOrdersView.d.ts +15 -3
  259. package/dist/features/seller/components/SellerOrdersView.js +134 -163
  260. package/dist/features/seller/components/SellerPayoutMethodsView.js +60 -48
  261. package/dist/features/seller/components/SellerPayoutSettingsView.js +69 -4
  262. package/dist/features/seller/components/SellerPayoutsView.d.ts +12 -0
  263. package/dist/features/seller/components/SellerPayoutsView.js +25 -5
  264. package/dist/features/seller/components/SellerProductShell.d.ts +10 -3
  265. package/dist/features/seller/components/SellerProductShell.js +90 -37
  266. package/dist/features/seller/components/SellerProductsFilterDrawer.d.ts +2 -7
  267. package/dist/features/seller/components/SellerProductsFilterDrawer.js +4 -4
  268. package/dist/features/seller/components/SellerProductsView.d.ts +2 -4
  269. package/dist/features/seller/components/SellerProductsView.js +131 -189
  270. package/dist/features/seller/components/SellerReviewsView.js +95 -133
  271. package/dist/features/seller/components/SellerShippingConfigsView.js +47 -67
  272. package/dist/features/seller/components/SellerShippingView.js +41 -4
  273. package/dist/features/seller/components/SellerStoreCategoriesView.js +51 -59
  274. package/dist/features/seller/components/SellerStorefrontView.d.ts +6 -1
  275. package/dist/features/seller/components/SellerStorefrontView.js +42 -5
  276. package/dist/features/seller/components/index.d.ts +2 -2
  277. package/dist/features/seller/components/index.js +2 -2
  278. package/dist/features/shell/FormShell.d.ts +24 -4
  279. package/dist/features/shell/FormShell.js +12 -6
  280. package/dist/features/shell/QuickFormDrawer.js +67 -12
  281. package/dist/features/shell/StepForm.d.ts +30 -1
  282. package/dist/features/shell/StepForm.js +47 -4
  283. package/dist/features/shell/index.d.ts +1 -1
  284. package/dist/features/shell/index.js +1 -1
  285. package/dist/features/shipments/components/AdminShipmentEditorView.js +2 -2
  286. package/dist/features/shipments/schemas/validation.d.ts +14 -4
  287. package/dist/features/tester/components/AdminTesterFeedbackListView.js +23 -2
  288. package/dist/features/tester/components/TesterChecklistStepRow.js +1 -1
  289. package/dist/features/tester/components/TesterHubView.js +3 -2
  290. package/dist/features/tester/seed-data/orders-tester-seed-data.js +9 -1
  291. package/dist/features/tester/seed-data/products-tester-seed-data.js +43 -32
  292. package/dist/features/tester/seed-data/tester-checklist-seed-data.js +487 -18
  293. package/dist/index.d.ts +17 -3
  294. package/dist/index.js +23 -5
  295. package/dist/next/routing/route-map.d.ts +13 -0
  296. package/dist/next/routing/route-map.js +12 -0
  297. package/dist/providers/email-resend/provider.js +1 -0
  298. package/dist/providers/firebase-client/auth.d.ts +1 -0
  299. package/dist/providers/firebase-client/auth.js +7 -1
  300. package/dist/repositories/index.d.ts +1 -0
  301. package/dist/repositories/index.js +1 -0
  302. package/dist/schemas/registry.d.ts +61 -25
  303. package/dist/security/pii-mask.js +1 -1
  304. package/dist/seed/bids-seed-data.js +48 -3
  305. package/dist/seed/categories-seed-data.js +69 -0
  306. package/dist/seed/grouped-listings-seed-data.js +167 -0
  307. package/dist/seed/payouts-seed-data.js +4 -4
  308. package/dist/seed/products-art-seed-data.js +188 -5
  309. package/dist/seed/products-auctions-seed-data.js +226 -6
  310. package/dist/seed/products-classifieds-seed-data.js +255 -8
  311. package/dist/seed/products-digital-codes-seed-data.js +258 -19
  312. package/dist/seed/products-live-items-seed-data.js +143 -10
  313. package/dist/seed/products-preorders-seed-data.js +213 -8
  314. package/dist/seed/products-prize-draws-seed-data.js +198 -0
  315. package/dist/seed/products-standard-seed-data.js +234 -4
  316. package/dist/seed/products-stickers-seed-data.js +178 -5
  317. package/dist/seed/site-settings-seed-data.js +23 -30
  318. package/dist/seed/store-extensions-seed-data.js +2 -2
  319. package/dist/server-entry.d.ts +6 -2
  320. package/dist/server-entry.js +24 -3
  321. package/dist/server.d.ts +10 -3
  322. package/dist/server.js +11 -4
  323. package/dist/styles.css +29 -1
  324. package/dist/tailwind-utilities.css +1 -1
  325. package/dist/ui/components/Form.d.ts +11 -2
  326. package/dist/ui/components/Form.js +4 -3
  327. package/dist/ui/components/FormField.d.ts +3 -1
  328. package/dist/ui/components/FormField.js +22 -4
  329. package/dist/ui/components/ImageLightbox.js +37 -3
  330. package/dist/ui/components/ImageLightbox.style.css +28 -0
  331. package/dist/ui/components/ListingLayout.d.ts +1 -2
  332. package/dist/ui/components/ListingLayout.js +1 -1
  333. package/dist/ui/components/RecordDetailModal.d.ts +68 -0
  334. package/dist/ui/components/RecordDetailModal.js +8 -0
  335. package/dist/ui/components/SlottedListingView.js +9 -1
  336. package/dist/ui/forms/FormErrorSummary.d.ts +25 -0
  337. package/dist/ui/forms/FormErrorSummary.js +37 -0
  338. package/dist/ui/forms/FormShell.d.ts +37 -2
  339. package/dist/ui/forms/FormShell.js +37 -7
  340. package/dist/ui/forms/FormShell.style.css +17 -0
  341. package/dist/ui/forms/index.d.ts +2 -0
  342. package/dist/ui/forms/index.js +1 -0
  343. package/dist/ui/index.d.ts +4 -0
  344. package/dist/ui/index.js +5 -0
  345. package/dist/utils/media-url.d.ts +12 -1
  346. package/dist/utils/media-url.js +59 -1
  347. package/dist/validation/schemas.d.ts +4 -4
  348. package/package.json +1 -1
  349. package/dist/_internal/client/features/classified/ClassifiedDetailView.d.ts +0 -9
  350. package/dist/_internal/client/features/classified/ClassifiedDetailView.js +0 -55
  351. package/dist/_internal/client/features/digital-code/DigitalCodeDetailView.d.ts +0 -18
  352. package/dist/_internal/client/features/digital-code/DigitalCodeDetailView.js +0 -21
  353. package/dist/_internal/client/features/live/LiveItemDetailView.d.ts +0 -9
  354. package/dist/_internal/client/features/live/LiveItemDetailView.js +0 -25
  355. package/dist/_internal/server/jobs/core/prizeRevealClose.d.ts +0 -2
  356. package/dist/_internal/server/jobs/core/prizeRevealClose.js +0 -28
  357. package/dist/_internal/server/jobs/core/prizeRevealExpiry.d.ts +0 -2
  358. package/dist/_internal/server/jobs/core/prizeRevealExpiry.js +0 -61
  359. package/dist/_internal/server/jobs/core/prizeRevealOpen.d.ts +0 -2
  360. package/dist/_internal/server/jobs/core/prizeRevealOpen.js +0 -68
  361. package/dist/_internal/server/jobs/core/prizeRevealReminder.d.ts +0 -2
  362. package/dist/_internal/server/jobs/core/prizeRevealReminder.js +0 -49
  363. package/dist/_internal/server/jobs/handlers/prizeRevealClose.d.ts +0 -2
  364. package/dist/_internal/server/jobs/handlers/prizeRevealClose.js +0 -2
  365. package/dist/_internal/server/jobs/handlers/prizeRevealExpiry.d.ts +0 -2
  366. package/dist/_internal/server/jobs/handlers/prizeRevealExpiry.js +0 -2
  367. package/dist/_internal/server/jobs/handlers/prizeRevealOpen.d.ts +0 -2
  368. package/dist/_internal/server/jobs/handlers/prizeRevealOpen.js +0 -2
  369. package/dist/_internal/server/jobs/handlers/prizeRevealReminder.d.ts +0 -2
  370. package/dist/_internal/server/jobs/handlers/prizeRevealReminder.js +0 -2
  371. package/dist/features/auth/consent-otp.d.ts +0 -57
  372. package/dist/features/auth/consent-otp.js +0 -127
  373. package/dist/features/cart/components/CheckoutOtpModal.d.ts +0 -15
  374. package/dist/features/cart/components/CheckoutOtpModal.js +0 -12
  375. package/dist/features/checkout/actions/checkout-actions.d.ts +0 -31
  376. package/dist/features/checkout/actions/checkout-actions.js +0 -124
  377. package/dist/features/classified/components/ClassifiedIndexListing.d.ts +0 -5
  378. package/dist/features/classified/components/ClassifiedIndexListing.js +0 -123
  379. package/dist/features/classified/components/ClassifiedListView.d.ts +0 -6
  380. package/dist/features/classified/components/ClassifiedListView.js +0 -44
  381. package/dist/features/digital-codes/components/DigitalCodesIndexListing.d.ts +0 -5
  382. package/dist/features/digital-codes/components/DigitalCodesIndexListing.js +0 -114
  383. package/dist/features/digital-codes/components/DigitalCodesListView.d.ts +0 -6
  384. package/dist/features/digital-codes/components/DigitalCodesListView.js +0 -38
  385. package/dist/features/live/components/LiveItemsIndexListing.d.ts +0 -5
  386. package/dist/features/live/components/LiveItemsIndexListing.js +0 -115
  387. package/dist/features/live/components/LiveItemsListView.d.ts +0 -6
  388. package/dist/features/live/components/LiveItemsListView.js +0 -44
  389. package/dist/features/seller/components/SellerTemplatesView.d.ts +0 -12
  390. package/dist/features/seller/components/SellerTemplatesView.js +0 -276
  391. package/dist/react/hooks/useVisibleItems.d.ts +0 -37
  392. package/dist/react/hooks/useVisibleItems.js +0 -59
  393. package/dist/ui/components/TabStrip.d.ts +0 -34
  394. package/dist/ui/components/TabStrip.js +0 -56
  395. package/dist/ui/components/TabStrip.style.css +0 -97
@@ -32,7 +32,10 @@ function group(groupKey, groupLabel, pages, opts) {
32
32
  pageLabel: page.pageLabel,
33
33
  label: c.label,
34
34
  description: c.description,
35
- href: c.href,
35
+ // Every case gets a link: its own href, or the page's default —
36
+ // guarantees "Go test this ->" always has somewhere real to send
37
+ // the tester, even for cases nobody bothered to link individually.
38
+ href: c.href ?? page.href,
36
39
  order: (opts?.orderOffset ?? 0) + pageIdx * 10 + caseIdx,
37
40
  isActive: true,
38
41
  adminOnly: opts?.adminOnly ?? false,
@@ -52,29 +55,65 @@ const rawTesterChecklistItems = [
52
55
  {
53
56
  pageKey: "signup-login",
54
57
  pageLabel: "Signup & Login",
58
+ href: "/auth/login",
55
59
  cases: [
56
60
  { key: "email-signup", label: "Sign up with email works", href: "/auth/register" },
57
61
  { key: "google-oauth", label: "Sign up / log in with Google works", href: "/auth/login" },
58
- { key: "google-link-existing", label: "Linking Google sign-in to an existing email/password account works" },
62
+ {
63
+ key: "google-link-existing",
64
+ label: "Signing in with Google using the SAME email as an existing password account logs into that same account automatically — no duplicate account, no explicit linking needed",
65
+ description: "This is the auto-merge path: register with email/password (email A), log out, then \"Sign in with Google\" using a Google account whose email is also A. You should land back in the same account (same orders/wishlist/etc.), and Settings > Account tab should now show Google as Connected.",
66
+ href: "/auth/login",
67
+ },
68
+ {
69
+ key: "google-link-different-email",
70
+ label: "Linking a Google account whose email differs from your original signup email requires the explicit \"Connect Google\" button (My Account dashboard or Settings > Account tab) — it does not happen automatically just by signing in with Google",
71
+ description: "Sign up/log in normally with email A (password). While logged in as A, use the \"Connect Google\" button and complete the popup with a Google account using a DIFFERENT email B. Afterward, Settings > Account tab should show Google as Connected with email B, while your account's primary login email is still A — the account now has two associated emails, not a second account.",
72
+ href: "/user/settings",
73
+ },
74
+ {
75
+ key: "google-link-confirmation-shown",
76
+ label: "After Google is connected, the My Account dashboard shows a \"Google account connected\" confirmation with a Connected badge and your linked email — it does not just silently stop showing the \"Connect Google\" prompt",
77
+ description: "Fixed 2026-08-20 — the /user dashboard only ever handled the unlinked case; once linked, the whole alert block disappeared with no confirmation, unlike Settings > Account tab which already showed a Connected badge. Link a Google account (or use an already-linked test account) and confirm the green confirmation alert appears on /user, not just an absence of the blue prompt.",
78
+ href: "/user",
79
+ },
80
+ {
81
+ key: "google-link-conflict-rejected",
82
+ label: "Trying to link a Google account whose email is already used by a DIFFERENT existing account is rejected with a clear error — it does not silently merge the two accounts or move the email over",
83
+ href: "/user/settings",
84
+ },
59
85
  { key: "google-popup-blocked-fallback", label: "If the Google sign-in popup is blocked, the fallback (RTDB signal + postMessage) still completes sign-in" },
60
86
  { key: "login", label: "Log in with email + password works", href: "/auth/login" },
61
87
  { key: "logout", label: "Log out works and clears the session" },
62
88
  { key: "email-verify", label: "Email verification link works" },
63
89
  { key: "password-reset", label: "Forgot-password / reset-password flow works" },
90
+ {
91
+ key: "signup-verification-email-arrives",
92
+ label: "The verification email actually arrives in the inbox after signing up — not just a \"check your email\" message on screen",
93
+ description: "Changed 2026-08-21 — signup verification is now sent by Firebase itself (client SDK) instead of being generated server-side and delivered through Resend. Register a brand-new account with a real inbox you can check, and confirm the email lands (check spam too) and its link marks the account verified. The email will come from Firebase's sender with Firebase's template, NOT the branded LetItRip template — that difference is expected, not a bug.",
94
+ href: "/auth/register",
95
+ },
96
+ {
97
+ key: "forgot-password-no-account-enumeration",
98
+ label: "Submitting \"Forgot password\" for an email that has NO account shows the same generic message as a real account — it never reveals whether the account exists",
99
+ description: "Changed 2026-08-21 — this flow now calls Firebase directly from the browser instead of going through a server route, so the client must swallow Firebase's auth/user-not-found error. Submit a made-up address like nobody-here-12345@example.com and confirm you see \"If an account exists for that email, a reset link is on its way.\" — the same wording a real address produces, with no error toast and nothing in the UI distinguishing the two cases.",
100
+ href: "/auth/forgot-password",
101
+ },
64
102
  ],
65
103
  },
66
104
  {
67
105
  pageKey: "profile-settings",
68
106
  pageLabel: "Profile & Settings",
107
+ href: "/user/settings",
69
108
  cases: [
70
109
  { key: "edit-profile", label: "Editing display name / bio saves correctly", href: "/user/profile" },
71
110
  { key: "avatar-upload", label: "Uploading a profile avatar works" },
72
111
  { key: "notification-prefs", label: "Notification preferences save correctly" },
73
112
  { key: "public-profile-toggle", label: "Public profile visibility toggle works" },
74
113
  {
75
- key: "password-change-otp-required",
76
- label: "Changing your password from Settings requires entering an emailed 6-digit code before it actually takes effect — the password does not change just from submitting the current/new password form",
77
- description: "Fixed 2026-08-20the password-change API previously trusted the session cookie alone, with no server-side check of the current password at all. Submit the Change Password form with a correct current password; confirm you land on a \"we sent a code to your email\" step, and the password only actually updates after entering the correct code. Also verify: an expired/wrong code is rejected with a clear error, and \"Resend code\" works.",
114
+ key: "password-change-reset-link",
115
+ label: "Changing your password from Settings sends a reset link to your account email — the password only changes after you open that link, never from the Settings page alone",
116
+ description: "Changed 2026-08-21password change is now Firebase-native (the same reset-link flow as \"Forgot Password\"), replacing the older emailed 6-digit-code flow. Click \"Send password reset link\", confirm the email arrives at your account address, and that following the link lets you set a new password which then works for sign-in. Identity is proved by access to the inbox, so verify no password change is possible from the Settings page by itself.",
78
117
  href: "/user/settings",
79
118
  },
80
119
  {
@@ -98,6 +137,7 @@ const rawTesterChecklistItems = [
98
137
  {
99
138
  pageKey: "testing-program",
100
139
  pageLabel: "Tester Hub",
140
+ href: "/user/tester",
101
141
  cases: [
102
142
  { key: "tester-hub-loads", label: "Tester Hub loads the full checklist grouped by section", href: "/user/tester" },
103
143
  { key: "tester-hub-search", label: "Tester Hub search finds test cases by typing part of the title or the route (e.g. \"/store/payouts\")", href: "/user/tester" },
@@ -117,6 +157,7 @@ const rawTesterChecklistItems = [
117
157
  {
118
158
  pageKey: "browsing-search",
119
159
  pageLabel: "Browsing & Search",
160
+ href: "/products",
120
161
  cases: [
121
162
  { key: "browse-categories", label: "Browsing categories shows relevant products", href: "/categories" },
122
163
  { key: "search", label: "Search returns relevant results", href: "/search" },
@@ -186,10 +227,16 @@ const rawTesterChecklistItems = [
186
227
  {
187
228
  pageKey: "product-detail",
188
229
  pageLabel: "Product / Auction / Pre-order Detail",
230
+ href: "/products",
189
231
  cases: [
190
- { key: "standard-detail", label: "Standard product detail page loads correctly" },
191
- { key: "auction-detail", label: "Auction detail page shows current bid + countdown correctly" },
192
- { key: "preorder-detail", label: "Pre-order detail page shows expected ship date correctly" },
232
+ { key: "standard-detail", label: "Standard product detail page loads correctly", href: "/products/product-tester-standard-1" },
233
+ {
234
+ key: "auction-detail",
235
+ label: "Auction detail page shows current bid + a live, ticking countdown correctly",
236
+ description: "The countdown (\"Ends in 2d 5h 30m\", ticking every second — not a static date) must appear directly above every \"Place a bid\" button: the info panel, the desktop and mobile compact bid-summary cards, inside the \"Place your bid\" modal above the submit button, and in the mobile sticky bottom bar (as a row above the current-bid/bid-count line). It should switch to \"Ended\" once the end time passes.",
237
+ href: "/auctions/auction-tester-sandbox-cycle-1",
238
+ },
239
+ { key: "preorder-detail", label: "Pre-order detail page shows expected ship date correctly", href: "/pre-orders/preorder-tester-sandbox-1" },
193
240
  {
194
241
  key: "image-gallery",
195
242
  label: "Product image gallery thumbnails load reliably (no broken-image icons) and click-to-zoom/rotate works in the lightbox",
@@ -198,7 +245,24 @@ const rawTesterChecklistItems = [
198
245
  {
199
246
  key: "video-playback",
200
247
  label: "A product's video slide opens in theater mode with playback, zoom, and rotate controls",
201
- description: "Product video is no longer a YouTube embed — when a product has a video, it appears as a trailing gallery slide (poster image + play badge) alongside the photos. Clicking it opens the full-screen lightbox in theater mode, plays the video with native controls, and the zoom (+/-) and rotate (R) buttons in the top bar still apply. Test on the two seeded fixtures: \"Beyblade Original — Dragoon F (Video Demo)\" and \"Beyblade X BX-02 Dran Sword (Video Demo)\".",
248
+ description: "When a product has a video, it appears as a trailing gallery slide (poster image + play badge) alongside the photos. Clicking it opens the full-screen lightbox in theater mode. For a raw-file video (native <video> element) the zoom (+/-) and rotate (R) buttons in the top bar apply; for a YouTube-sourced video (see \"video-playback-youtube\" below) they don't — the embed has its own player chrome. Test on the raw-file fixtures: \"Beyblade Original — Dragoon F (Video Demo)\" and \"Beyblade X BX-02 Dran Sword (Video Demo)\".",
249
+ },
250
+ {
251
+ key: "video-playback-youtube",
252
+ label: "A product whose video is sourced from YouTube (via MediaUploadField's \"YouTube\" tab) actually plays — not \"No video with supported format\"",
253
+ description: "Fixed 2026-08-21 — a YouTube-sourced video.url (e.g. youtube.com/watch?v=...) was always rendered as a raw <video src>, which can't play a YouTube watch-page URL. getYouTubeVideoId() now detects this and renders a youtube-nocookie.com iframe embed instead, in both the gallery lightbox and the standalone MediaVideo preview (used by the upload form's own \"YouTube\" tab preview). Verify on \"Metal Fight Beyblade BB-118 Dark Bull (Video Demo, YouTube)\" (standard product) and \"Beyblade Original — Dragoon Storm (Rare Sealed)\" (auction) — the video slide should open and actually play the embedded YouTube video, not show an error.",
254
+ href: "/products",
255
+ },
256
+ {
257
+ key: "video-lightbox-fullscreen-sizing",
258
+ label: "The video/YouTube-embed lightbox has a real minimum size on both mobile and desktop, and the top-bar \"expand\" button toggles real browser fullscreen",
259
+ description: "Fixed 2026-08-21 — the lightbox's \"Maximize2\" button used to just duplicate the zoom-reset button (no real fullscreen). It now toggles the native Fullscreen API (icon swaps to a \"shrink\" glyph while active) and syncs correctly if you exit via Esc instead of the button. Separately, the video/embed area now has a minimum width/height (bigger on desktop than mobile) so it never collapses to a tiny box while loading or on a low-resolution video. Test on a narrow (mobile-width) browser window and a full desktop window against \"Beyblade Burst B-97 Spryzen S2 (Video Demo, Wikimedia)\".",
260
+ },
261
+ {
262
+ key: "video-real-file-upload",
263
+ label: "Uploading a real video file via the product form's \"Upload\" tab (not YouTube or External URL) plays back correctly with an auto-captured poster frame",
264
+ description: "This path can't be covered by a permanent seed fixture — there's no real Storage object for a seeded URL to point at, so it has to be tested by hand. As a seller/admin, edit any product's Video field, use the default \"Upload\" tab (not the YouTube or External URL tab) to upload a real .mp4/.webm/.mov file, and confirm: (1) a poster/thumbnail frame is auto-captured and shown once upload finishes, (2) the video plays correctly in both the product form's own preview panel and the public product detail page's gallery lightbox, (3) it opens the trim modal if enabled, and (4) removing it and re-uploading works without leaving orphaned files (no error toast).",
265
+ href: "/store/products",
202
266
  },
203
267
  {
204
268
  key: "related-listings-sections",
@@ -206,22 +270,22 @@ const rawTesterChecklistItems = [
206
270
  description: "Verify against \"Beyblade Burst B-01 Valkyrie\" (product-beyblade-burst-valkyrie) — all 4 sections should show real items: other Beyblade Burst products, other \"Beyblade\"-brand products, other attack-type/starter-set tagged products, and other Beyblade Arena store listings.",
207
271
  href: "/products",
208
272
  },
209
- { key: "prizedraw-buy-reveal", label: "Buying a prize-draw entry correctly assigns a prize once payment is confirmed (instant mode) or shows a pending state until the draw closes (scheduled mode)" },
273
+ { key: "prizedraw-buy-reveal", label: "Buying a prize-draw entry correctly assigns a prize once payment is confirmed (instant mode) or shows a pending state until the draw closes (scheduled mode)", href: "/prize-draws/prizedraw-tester-sandbox-1" },
210
274
  {
211
275
  key: "bundle-purchase",
212
276
  label: "Purchasing a bundle works and shows all included items in the order",
213
277
  description: "Verify against \"Test Bundle\" (bundle-tester-sandbox, findable from the bundles listing page) — after checkout, the order should show a single \"Test Bundle\" line item, not two separate product lines.",
214
- href: "/bundles",
278
+ href: "/bundles/bundle-tester-sandbox",
215
279
  },
216
280
  {
217
281
  key: "product-group-set-widget",
218
282
  label: "A product's detail page shows a collapsible \"Part of / Parts in this group\" panel with a working thumbnail strip and a \"View whole group\" table when the product belongs to a product-group (\"Set\")",
219
283
  description: "Verify against \"Test Product Set — Standard #1 + Standard #2\" (group-tester-sandbox-bundle) and either of its two children (product-tester-standard-1 / product-tester-standard-2), findable from the products listing page — all three should show the panel with each other listed, the arrow/triangle expand icons should render as real glyphs (not garbled text), and \"View whole group\" should open a working modal/drawer.",
220
- href: "/products",
284
+ href: "/products/group-tester-sandbox-bundle",
221
285
  },
222
- { key: "classified-contact-flow", label: "A classified listing shows a contact-seller flow with deliberately no checkout/buy button" },
223
- { key: "digitalcode-delivery", label: "Purchasing a digital-code listing delivers the code to the buyer post-purchase" },
224
- { key: "live-item-detail", label: "A live-item listing's detail page shows the livestream link correctly" },
286
+ { key: "classified-contact-flow", label: "A classified listing shows a contact-seller flow with deliberately no checkout/buy button", href: "/classified/classified-tester-sandbox-1" },
287
+ { key: "digitalcode-delivery", label: "Purchasing a digital-code listing delivers the code to the buyer post-purchase", href: "/digital-codes/digitalcode-tester-sandbox-1" },
288
+ { key: "live-item-detail", label: "A live-item listing's detail page shows the livestream link correctly", href: "/live/live-tester-sandbox-1" },
225
289
  {
226
290
  key: "live-item-video-mandatory",
227
291
  label: "Creating a live-item listing (species: animals/plants) without a video is rejected with a clear error; a live listing WITH a video plays correctly in the gallery's video slide and its poster thumbnail is watermarked",
@@ -233,6 +297,7 @@ const rawTesterChecklistItems = [
233
297
  {
234
298
  pageKey: "buying-checkout",
235
299
  pageLabel: "Buying & Checkout",
300
+ href: "/checkout",
236
301
  cases: [
237
302
  { key: "add-to-cart", label: "Add to cart works from the product page" },
238
303
  { key: "add-to-cart-out-of-stock", label: "Add to cart is disabled/blocked once a listing's stock reaches zero" },
@@ -250,6 +315,16 @@ const rawTesterChecklistItems = [
250
315
  label: "Carts ≥ the admin-configured high-value OTP threshold (Site Settings → Shipping → \"High-value checkout OTP threshold\", default ₹5,000) prompt a \"Verify this order\" email OTP right before payment (except COD); carts below the threshold check out directly with no OTP step at all",
251
316
  description: "Root-caused 2026-08-20 — the OTP email send used to be fire-and-forget, so a Resend/API failure silently left the buyer stuck with no code and no error. Sending now surfaces a real error if the email genuinely fails to send, and the \"already sent, retry in N minutes\" rate-limit message is a clear sentence instead of a raw error code. Place a real order ≥ the threshold and confirm the code actually arrives by email (check spam too) — report the exact error text if it still doesn't.",
252
317
  },
318
+ {
319
+ key: "checkout-otp-whatsapp-option",
320
+ label: "When the admin has WhatsApp OTP switched on, the high-value checkout verification step offers a \"Send via WhatsApp instead\" option — and email, not WhatsApp, is still what gets sent by default",
321
+ description: "Added 2026-08-21. WhatsApp is deliberately opt-in for OTP: the code must arrive by email unless the buyer explicitly clicks the WhatsApp option. Requires Site Settings → Notifications → WhatsApp → \"WhatsApp OTP\" enabled AND real Meta Cloud API credentials saved, AND the selected delivery address having a phone number. Verify: (a) the default send is email; (b) the WhatsApp option only appears when all three conditions hold; (c) clicking it delivers the code to the address's phone and the on-screen text switches to the masked phone number; (d) the code from WhatsApp verifies successfully.",
322
+ },
323
+ {
324
+ key: "checkout-otp-whatsapp-hidden-without-phone",
325
+ label: "The \"Send via WhatsApp instead\" option is NOT offered when the selected delivery address has no phone number on it, even with WhatsApp OTP enabled",
326
+ description: "Added 2026-08-21. The phone is resolved server-side from the selected address (never from anything the browser sends), so an address with no phone has nowhere to send the code. Select/create a delivery address with the phone field empty and confirm the WhatsApp option is absent rather than appearing and then failing.",
327
+ },
253
328
  { key: "payment-method-selection", label: "Choosing between COD, UPI/manual, and Razorpay (when enabled) at checkout works" },
254
329
  { key: "payment-window-countdown", label: "Manual-payment orders show a 15-minute countdown timer on the payment page" },
255
330
  { key: "payment-proof-upload", label: "Uploading manual payment proof (screenshot, UTR, mark-as-paid + agreement checkboxes) works" },
@@ -267,11 +342,30 @@ const rawTesterChecklistItems = [
267
342
  label: "Choosing manual payment (UPI/Cash) at checkout shows a how-it-works guide (payment steps, refund summary, what happens if an item sells out) and requires a consent checkbox before the \"Pay via UPI / Cash\" button becomes clickable",
268
343
  href: "/checkout",
269
344
  },
345
+ {
346
+ key: "checkout-cash-payment-no-validation-error",
347
+ label: "Checking the consent box and clicking \"Pay via UPI / Cash\" places the order successfully — no red \"Validation failed\" error appears",
348
+ description: "Fixed 2026-08-20 — the checkout API's schema only accepted paymentMethod \"cod\"/\"online\"/\"upi_manual\"/\"emi\"; the manual-payment button has always sent \"cash\", which was never in that list, so every attempt was rejected with a generic \"Validation failed\" message even with the out-of-stock policy and consent checkbox correctly filled in. Add items to cart, reach the payment step, check the consent box, and click \"Pay via UPI / Cash\" — confirm it succeeds and lands on the order confirmation / payment-proof page.",
349
+ href: "/checkout",
350
+ },
351
+ {
352
+ key: "checkout-order-summary-full-breakdown",
353
+ label: "The checkout Order Summary panel shows Shipping, COD handling fee (when COD is the likely method), the selected add-on fees (WhatsApp updates / gift wrap / shipment protection — only the ones checked), GST (when enabled), and Coupon discount as separate line items with icons before you pay — not just Subtotal and Total",
354
+ description: "Fixed 2026-08-20 — the Order Summary previously showed Total = Subtotal − coupon discount only; shipping and every fee were computed and charged server-side but never shown to the buyer beforehand. Reach the payment step with an address selected, toggle an add-on checkbox, and confirm the matching line appears in the summary a moment later and the Total updates to match — then confirm the same Total appears on the order confirmation page afterward.",
355
+ href: "/checkout",
356
+ },
357
+ {
358
+ key: "checkout-razorpay-charge-includes-shipping",
359
+ label: "When Razorpay online payment is enabled, the amount charged in the Razorpay checkout modal matches the order's recorded Total exactly, including shipping fee",
360
+ description: "Fixed 2026-08-20 — the Razorpay pre-charge amount (computed before opening the payment modal) omitted the seller's shipping fee entirely, while the order created afterward recorded a higher total that included it, so the buyer was silently charged less via the gateway than what got recorded as owed. Only testable when Site Settings → Payments → Razorpay is enabled and the seller has a configured shipping fee. Compare the amount shown in the Razorpay modal against the order's Total on the confirmation page — they should match exactly.",
361
+ href: "/checkout",
362
+ },
270
363
  ],
271
364
  },
272
365
  {
273
366
  pageKey: "my-orders",
274
367
  pageLabel: "My Orders — List & Dashboard",
368
+ href: "/user/orders",
275
369
  cases: [
276
370
  {
277
371
  key: "orders-item-summary",
@@ -295,24 +389,86 @@ const rawTesterChecklistItems = [
295
389
  description: "A 2026-08-19 bug meant real checkout orders never carried a per-item image at all (the field didn't exist on the order document, so the detail page's image block was silently skipped rather than showing a broken-image icon). Place a fresh order through checkout and confirm its item thumbnail shows up both in the My Orders list and on the order-detail page.",
296
390
  href: "/user/orders",
297
391
  },
392
+ {
393
+ key: "my-orders-search-filter-sort",
394
+ label: "My Orders has a working search box (by order id), a status filter (drawer), and a sort dropdown (Newest/Oldest/Highest total/Lowest total) — the same toolbar pattern as every other dashboard listing",
395
+ description: "Converted 2026-08-21 off a page-specific hand-wired toolbar onto the standard DataListingView scaffold. Type part of a real order id into search and confirm it narrows the list, open the filter drawer and pick a status, and confirm changing sort actually reorders the cards.",
396
+ href: "/user/orders",
397
+ },
398
+ {
399
+ key: "order-tracking-timeline",
400
+ label: "\"Track Shipment\" opens a real page with a status timeline (Order placed → Shipped, each with a real date) and, when a tracking URL is set, a \"Track with carrier\" link that opens in a new tab — not a blank page",
401
+ description: "Fixed 2026-08-21 — /user/orders/[id]/track previously rendered <UserOrderTrackView /> with zero render-props (a Root Cause #8 blank slot-shell), and the underlying adapter dropped orderDate/shippingDate/deliveryDate/cancellationDate/trackingUrl entirely, so even a fixed page would have had nothing to show. From My Orders, open the seeded \"order-tester-sandbox-standard-shipped\" order and click \"Track Shipment\" — confirm both timeline steps show real dates and the tracking link opens www.example.com/track/TEST-TRACK-001 in a new tab.",
402
+ href: "/user/orders",
403
+ },
404
+ {
405
+ key: "manual-payment-panel-on-order-detail",
406
+ label: "A manual-payment (UPI/Cash) order's detail page shows a \"Payment pending\" panel with a \"Complete payment\" button that opens the proof-upload page — you can get back to it at any time, not only right after checkout",
407
+ description: "Fixed 2026-08-21. `ROUTES.USER.ORDER_PAYMENT` previously had exactly one caller — the post-checkout redirect — so a buyer who navigated away could never find the upload page again. Place a UPI/Cash order, leave the payment page WITHOUT uploading, go to My Orders, open that order, and confirm the panel + \"Complete payment\" button are there and land on the upload page.",
408
+ href: "/user/orders",
409
+ },
410
+ {
411
+ key: "manual-payment-page-renders-upi-and-countdown",
412
+ label: "The payment-proof page for a UPI/Cash order actually shows the upload form, the UPI ID to pay, and a live 15-minute countdown — NOT the message \"This order does not require manual payment upload\"",
413
+ description: "Fixed 2026-08-21 — the order adapter (`orderDocumentToOrder`) dropped `paymentMethod`, `displayedUpiId` and `paymentDeadline` entirely, so the page's own manual-payment check always failed and every buyer saw the \"does not require manual payment upload\" dead end, with no UPI ID and no timer. This is the single most important case in this group: if it fails, the whole manual-payment flow is unusable.",
414
+ href: "/user/orders",
415
+ },
416
+ {
417
+ key: "manual-payment-awaiting-review-state",
418
+ label: "After submitting proof, the order detail page switches to a \"Payment under review\" panel and the \"Complete payment\" button disappears (you can't double-submit)",
419
+ href: "/user/orders",
420
+ },
421
+ {
422
+ key: "manual-payment-reupload-note-visible-to-buyer",
423
+ label: "When an admin requests a proof re-upload, the buyer's order detail page shows \"Payment proof needs correction\" WITH the admin's note, and a \"Re-upload proof\" button that works",
424
+ description: "Added 2026-08-21. Pair with the admin-side case `admin-orders-request-reupload`. The buyer previously got a notification telling them to re-upload but had no link anywhere to do it. Confirm the admin's exact note text is shown to the buyer, and that re-submitting succeeds (it must not fail with \"proof already attached\").",
425
+ href: "/user/orders",
426
+ },
427
+ {
428
+ key: "manual-payment-rejected-state",
429
+ label: "An order rejected as fraudulent shows a \"Payment rejected\" panel with the admin's reason, and the account is suspended for 7 days",
430
+ description: "Pair with the admin-side case `admin-orders-reject-fraud`. Use a throwaway buyer account — this really does ban it for 7 days.",
431
+ href: "/user/orders",
432
+ },
298
433
  ],
299
434
  },
300
435
  {
301
436
  pageKey: "bidding",
302
437
  pageLabel: "Bidding",
438
+ href: "/user/bids",
303
439
  cases: [
304
- { key: "place-bid", label: "Placing a bid on an auction works", href: "/user/bids" },
440
+ { key: "place-bid", label: "Placing a bid on an auction works", href: "/auctions/auction-tester-sandbox-cycle-1" },
305
441
  { key: "place-bid-live-self", label: "After placing a bid, the current bid amount and bid count update immediately on the auction page for the bidder — no manual page refresh needed" },
306
442
  { key: "place-bid-live-other-viewer", label: "Opening the same auction in two browser tabs (or two accounts) and placing a bid in one updates the current bid and bid count in the other within a few seconds, without a manual refresh (realtime SSE)" },
307
443
  { key: "outbid-notification", label: "Getting outbid triggers a notification" },
308
- { key: "win-auction", label: "Winning an auction creates a payable order correctly" },
444
+ { key: "win-auction", label: "Winning an auction creates a payable order correctly", href: "/auctions/auction-tester-sandbox-won" },
309
445
  { key: "bid-history", label: "My Bids page shows accurate bid history, paginated with the most recent bid first", href: "/user/bids" },
310
446
  { key: "bid-history-auction-detail-pagination", label: "An auction detail page's Bid History section shows the most recent bid first and paginates once there are more bids than fit on one page (try the L-Drago auction — 13 seeded bids)" },
447
+ {
448
+ key: "bid-history-shows-date-time-and-masked-name",
449
+ label: "Each row in an auction detail page's Bid History section shows the bid amount, the exact date AND time the bid was placed (e.g. \"Aug 21, 3:45 PM\"), and the bidder's identity as a masked name (e.g. \"R*** K***\") or a partial bidder id — never a bidder's real, unmasked full name",
450
+ description: "Fixed 2026-08-21 — `maskPublicBid()` (the helper meant to mask a bidder's display name before it reaches the public product page) was a silent no-op that returned the bid document unchanged, so a real bidder's full name was being sent to every visitor; separately, the Bid History row never displayed any bidder identity at all (only amount + date), so the leak had no visible symptom. Open the L-Drago auction (or any auction with bids) as a DIFFERENT, non-bidder account and expand Bid History — every row should show a masked name, not a bidder's real full name.",
451
+ },
452
+ {
453
+ key: "bid-increment-tiered",
454
+ label: "The \"min increment\" shown on an auction matches the admin-configured tier for the current bid amount, not a flat ₹1 — and a bid below that increment is rejected",
455
+ description: "Default tiers: current bid ≤₹100 → ₹10 increment, ≤₹1,000 → ₹100, ≤₹5,000 → ₹200, ≤₹10,000 → ₹500, above that → ₹1,000. On an auction with no per-listing \"Minimum Bid Increment\" set, confirm the displayed \"min increment\" matches the tier for its current bid, and that trying to bid only current+1 (below the tier) is rejected with a minimum-increment error, while current+tier (or more) is accepted.",
456
+ },
457
+ {
458
+ key: "bid-increment-override-floor-raising",
459
+ label: "A seller's per-listing \"Minimum Bid Increment\" can require MORE than the admin tier, but can never let a bid undercut the tier",
460
+ description: "Set a listing's \"Minimum Bid Increment\" below the current tier's value (e.g. 1) and confirm the effective minimum bid still enforces the tier, not the smaller override. Then set it above the tier (e.g. 500 when the tier is 100) and confirm the higher override is now enforced instead.",
461
+ },
462
+ {
463
+ key: "bid-increment-live-tier-change",
464
+ label: "The displayed \"min increment\" on an open auction updates live (without a page refresh) if another bidder's bid pushes the current bid across a tier boundary",
465
+ },
311
466
  ],
312
467
  },
313
468
  {
314
469
  pageKey: "wishlist-history",
315
470
  pageLabel: "Wishlist & History",
471
+ href: "/wishlist",
316
472
  cases: [
317
473
  { key: "add-wishlist-pdp", label: "Adding a product to the wishlist from the product detail page works", href: "/wishlist" },
318
474
  { key: "add-wishlist-card", label: "Adding a product to the wishlist from a listing card (checkbox/long-press) works on both listing and search pages" },
@@ -339,6 +495,7 @@ const rawTesterChecklistItems = [
339
495
  {
340
496
  pageKey: "cart",
341
497
  pageLabel: "Cart",
498
+ href: "/cart",
342
499
  cases: [
343
500
  { key: "update-qty", label: "Updating item quantity in cart recalculates the total" },
344
501
  { key: "apply-coupon", label: "Applying a coupon code at checkout works" },
@@ -375,6 +532,7 @@ const rawTesterChecklistItems = [
375
532
  {
376
533
  pageKey: "reviews",
377
534
  pageLabel: "Reviews",
535
+ href: "/reviews",
378
536
  cases: [
379
537
  { key: "leave-review", label: "Leaving a review with rating + photo works" },
380
538
  { key: "view-seller-reviews", label: "Viewing a seller's reviews on their store page works" },
@@ -396,6 +554,7 @@ const rawTesterChecklistItems = [
396
554
  {
397
555
  pageKey: "messaging",
398
556
  pageLabel: "Messaging a Seller",
557
+ href: "/user/messages",
399
558
  cases: [
400
559
  { key: "start-conversation", label: "Starting a conversation with a seller works" },
401
560
  { key: "receive-reply", label: "Receiving and reading a seller's reply works" },
@@ -406,6 +565,7 @@ const rawTesterChecklistItems = [
406
565
  {
407
566
  pageKey: "user-dashboard-extras",
408
567
  pageLabel: "User Dashboard — Addresses, Catalogue, Settings, My Orders-by-Type",
568
+ href: "/user",
409
569
  cases: [
410
570
  { key: "addresses-crud", label: "Adding, editing, and listing delivery addresses works", href: "/user/addresses" },
411
571
  { key: "catalogue-crud", label: "Adding, editing, and deleting a personal catalogue item works", href: "/user/catalogue" },
@@ -420,11 +580,18 @@ const rawTesterChecklistItems = [
420
580
  href: "/user/returns",
421
581
  },
422
582
  { key: "my-reviews", label: "\"My Reviews\" shows reviews the user has left", href: "/user/reviews" },
583
+ {
584
+ key: "user-personal-listings-search-sort",
585
+ label: "My Digital Codes, My Pre-Orders, My Prize Draws, and My Reviews each now have a working search box and sort dropdown (Newest/Oldest, plus a rating sort on Reviews) using the standard listing toolbar — not a bare unsearchable list",
586
+ description: "Converted 2026-08-21 off page-specific hand-wired toolbars onto the standard DataListingView scaffold. Spot-check at least two of the four: type a product name into search and confirm it narrows the list, and change the sort dropdown and confirm the order changes.",
587
+ href: "/user/digital-codes",
588
+ },
423
589
  ],
424
590
  },
425
591
  {
426
592
  pageKey: "user-dashboard-navigation",
427
593
  pageLabel: "User Dashboard Navigation",
594
+ href: "/user",
428
595
  cases: [
429
596
  { key: "sidebar-all-links-work", label: "Every item in the user dashboard sidebar navigates to its page without a 404 or broken layout", href: "/user" },
430
597
  { key: "sidebar-active-highlight", label: "The sidebar correctly highlights the currently active section as you navigate between pages" },
@@ -447,6 +614,7 @@ const rawTesterChecklistItems = [
447
614
  {
448
615
  pageKey: "become-seller",
449
616
  pageLabel: "Become a Seller & Store Setup",
617
+ href: "/user/become-seller",
450
618
  cases: [
451
619
  { key: "apply-seller", label: "Applying to become a seller works", href: "/user/become-seller" },
452
620
  { key: "store-setup", label: "Setting up store name/description/logo works" },
@@ -456,42 +624,82 @@ const rawTesterChecklistItems = [
456
624
  {
457
625
  pageKey: "listing-a-product",
458
626
  pageLabel: "Listing a Product",
627
+ href: "/store/products",
459
628
  cases: [
460
629
  { key: "list-standard", label: "Listing a standard product works" },
461
630
  { key: "list-auction", label: "Listing an auction works" },
462
631
  { key: "list-preorder", label: "Listing a pre-order works" },
463
632
  { key: "edit-listing", label: "Editing an existing listing works" },
464
633
  { key: "media-upload", label: "Uploading product images/video during listing works" },
634
+ {
635
+ key: "media-upload-preview-no-white-box",
636
+ label: "A freshly-uploaded image's thumbnail renders as a real preview within a couple seconds — it never gets stuck as a blank/white box",
637
+ description: "Fixed 2026-08-21 — a fresh upload can briefly 404 while the Firestore doc and Storage object finish propagating; the thumbnail now retries with backoff instead of latching a permanent broken-image placeholder. Try uploading 2-3 images back-to-back on a slower connection to see the retry in action.",
638
+ href: "/store/products/new",
639
+ },
640
+ {
641
+ key: "media-upload-images-capped-at-5",
642
+ label: "The product gallery upload UI caps at 5 images (not 10) and its label reads \"up to 5\"",
643
+ description: "Fixed 2026-08-21 — the UI previously advertised/allowed up to 10 images while the server schema only ever accepted 5, so a 6th+ image silently failed validation with no clear reason.",
644
+ href: "/store/products/new",
645
+ },
646
+ {
647
+ key: "media-upload-video-duration",
648
+ label: "Attaching a directly-uploaded video file during listing captures its duration automatically and saves without a validation error; attaching a YouTube or external video URL also saves fine without needing a duration",
649
+ description: "Fixed 2026-08-21 — the video schema always required a duration the UI never collected, so ANY product with a directly-uploaded video previously failed server validation invisibly. Duration is now captured client-side for file uploads; YouTube/external sources are exempt since their duration can't be read client-side.",
650
+ href: "/store/products/new",
651
+ },
465
652
  { key: "seller-quick-add-drawer-flips", label: "With Left-hand mode ON, the seller's quick-add-listing side drawer opens from the left instead of the right" },
466
653
  ],
467
654
  },
468
655
  {
469
656
  pageKey: "seller-orders",
470
657
  pageLabel: "Seller Order Management & Shipping/Tracking",
658
+ href: "/store/orders",
471
659
  cases: [
472
660
  { key: "view-orders", label: "Seller order list shows accurate incoming orders", href: "/store/orders" },
661
+ {
662
+ key: "seller-order-manual-payment-badge",
663
+ label: "A manual-payment (UPI/Cash) order's seller detail shows a payment badge — Awaiting payment / Awaiting verification / Verified / Re-upload requested / Rejected — next to the Payment heading, plus the UTR once the buyer submits one",
664
+ description: "Added 2026-08-21. Sellers previously saw only the payment method word (\"upi_manual\") with no indication of whether the money had actually landed. The buyer's payment screenshot is deliberately NOT shown to sellers — it's a bank/UPI capture, and verifying is admin/moderator-only — so confirm the badge and UTR appear but no screenshot image does.",
665
+ href: "/store/orders",
666
+ },
473
667
  { key: "confirm-payment", label: "Approving a buyer's manual payment proof works" },
474
668
  { key: "request-reupload", label: "Requesting a proof re-upload (honest-mistake tier) clears the proof and extends the buyer's deadline" },
475
669
  { key: "reject-fraud", label: "Rejecting a proof as fraudulent cancels the order, restores stock, and bans the buyer's account for 7 days" },
476
670
  { key: "whatsapp-admin-share", label: "Uploading payment proof pings the admin WhatsApp numbers, and the buyer's \"Share for review\" link opens with a pre-filled message" },
477
671
  { key: "mark-shipped", label: "Marking an order shipped with tracking info works" },
478
672
  { key: "tracking-visible", label: "Buyer sees updated tracking status after seller ships" },
673
+ {
674
+ key: "seller-order-detail-full-page",
675
+ label: "Every seller order row has an \"Open full page\" action landing on a real bookmarkable /store/orders/[id]/view page showing the same items (with thumbnails)/address/payment/EMI/status content as the drawer",
676
+ description: "Added 2026-08-21 — sellers previously had no dedicated order-detail page, only a drawer, unlike buyer/admin. The drawer and the new page now share one content component (SellerOrderDetailPanel) so they can't drift.",
677
+ href: "/store/orders",
678
+ },
479
679
  ],
480
680
  },
481
681
  {
482
682
  pageKey: "seller-analytics-payouts",
483
683
  pageLabel: "Seller Analytics & Payouts",
684
+ href: "/store/analytics",
484
685
  cases: [
485
686
  { key: "view-analytics", label: "Seller analytics dashboard shows accurate sales data", href: "/store/analytics" },
486
687
  { key: "view-payouts", label: "Seller payouts list shows accurate payout history", href: "/store/payouts" },
487
688
  { key: "payouts-checkbox-select", label: "Selecting payouts with the row checkboxes shows a working bulk action bar (Export Selected)", href: "/store/payouts" },
488
689
  { key: "payouts-detail-panel", label: "Opening \"View Details\" on a payout shows a side panel with status progress, transaction ID, and expected payout date" },
489
690
  { key: "payouts-reminder-toggle", label: "Toggling the payout reminder flag in the detail panel saves correctly" },
691
+ {
692
+ key: "seller-payout-detail-full-page",
693
+ label: "Every payout row has an \"Open full page\" action landing on a real /store/payouts/[id]/view page showing a gross/platform-fee/refund-deduction/net breakdown, not just the drawer's flat amount",
694
+ description: "Added 2026-08-21 — the drawer previously showed only a flat amount with orderIds as plain unlinked monospace text. Both the drawer and the new page now render from one shared SellerPayoutDetailContent component.",
695
+ href: "/store/payouts",
696
+ },
490
697
  ],
491
698
  },
492
699
  {
493
700
  pageKey: "seller-shipping-payouts-setup",
494
701
  pageLabel: "Seller Shipping & Payout Setup",
702
+ href: "/store/shipping",
495
703
  cases: [
496
704
  { key: "shipping-page", label: "Store shipping settings page saves correctly", href: "/store/shipping" },
497
705
  { key: "shipping-configs-crud", label: "Creating, editing, and listing shipping configs works", href: "/store/shipping-configs" },
@@ -502,6 +710,7 @@ const rawTesterChecklistItems = [
502
710
  {
503
711
  pageKey: "seller-listing-types",
504
712
  pageLabel: "Seller — All Listing Types (Coupons, Bundles, Classifieds, Digital Codes, Live, Prize Draws, Art, Stickers)",
713
+ href: "/store/products",
505
714
  cases: [
506
715
  { key: "seller-coupons-crud", label: "Seller can create, edit, and list their own coupons", href: "/store/coupons" },
507
716
  { key: "seller-bundles-crud", label: "Seller can create, edit, and list bundles/grouped listings", href: "/store/bundles" },
@@ -527,15 +736,23 @@ const rawTesterChecklistItems = [
527
736
  {
528
737
  pageKey: "seller-catalog-org",
529
738
  pageLabel: "Seller Categories, Sublisting Categories & Listing Templates",
739
+ href: "/store/categories",
530
740
  cases: [
531
741
  { key: "seller-categories-crud", label: "Seller can create and edit their own categories", href: "/store/categories" },
532
742
  { key: "seller-sublisting-categories-crud", label: "Seller can create and edit sublisting categories", href: "/store/sublisting-categories" },
743
+ {
744
+ key: "sublisting-categories-standard-toolbar",
745
+ label: "Seller Sublisting Categories now uses the same search+sort+pagination toolbar and \"New Category\" button placement as every other dashboard listing, instead of its own bespoke layout",
746
+ description: "Converted 2026-08-21 off a fully custom self-contained page onto the standard DataListingView scaffold. Confirm search, sort, and pagination all still work, and the row-level View/Edit/Delete buttons are unchanged.",
747
+ href: "/store/sublisting-categories",
748
+ },
533
749
  { key: "seller-listing-templates-crud", label: "Seller can create, edit, and reuse listing templates when creating a new product", href: "/store/listing-templates" },
534
750
  ],
535
751
  },
536
752
  {
537
753
  pageKey: "seller-ops-comms",
538
754
  pageLabel: "Seller Addresses, Messages, Fulfillment & Print",
755
+ href: "/store/addresses",
539
756
  cases: [
540
757
  { key: "seller-addresses-crud", label: "Seller can add and edit store addresses", href: "/store/addresses" },
541
758
  { key: "seller-messages", label: "Seller messages list and thread view both work", href: "/store/messages" },
@@ -547,6 +764,7 @@ const rawTesterChecklistItems = [
547
764
  {
548
765
  pageKey: "seller-marketing-extras",
549
766
  pageLabel: "Seller Offers, Features, Google Reviews & WhatsApp Catalog",
767
+ href: "/store/offers",
550
768
  cases: [
551
769
  {
552
770
  key: "seller-offers-list",
@@ -563,6 +781,7 @@ const rawTesterChecklistItems = [
563
781
  {
564
782
  pageKey: "seller-guide",
565
783
  pageLabel: "Seller Guide Pages",
784
+ href: "/store/guide",
566
785
  cases: [
567
786
  { key: "seller-guide-pages", label: "The 5 seller guide pages (overview, capabilities, finance, listings, orders, settings) all load correctly", href: "/store/guide" },
568
787
  ],
@@ -570,6 +789,7 @@ const rawTesterChecklistItems = [
570
789
  {
571
790
  pageKey: "store-dashboard-navigation",
572
791
  pageLabel: "Store Dashboard Navigation",
792
+ href: "/store",
573
793
  cases: [
574
794
  { key: "store-sidebar-all-links-work", label: "Every item in the store/seller dashboard sidebar navigates to its page without a 404 or broken layout", href: "/store" },
575
795
  { key: "store-sidebar-active-highlight", label: "The store sidebar correctly highlights the currently active section as you navigate between pages" },
@@ -591,6 +811,7 @@ const rawTesterChecklistItems = [
591
811
  {
592
812
  pageKey: "blog",
593
813
  pageLabel: "Blog",
814
+ href: "/blog",
594
815
  cases: [
595
816
  { key: "read-post", label: "Reading a blog post renders correctly (images, formatting)" },
596
817
  { key: "blog-listing", label: "Blog listing page shows all published posts" },
@@ -607,6 +828,7 @@ const rawTesterChecklistItems = [
607
828
  {
608
829
  pageKey: "events",
609
830
  pageLabel: "Events, Raffles & Spin Wheel",
831
+ href: "/events",
610
832
  cases: [
611
833
  { key: "view-event", label: "Viewing an event detail page works", href: "/user/events" },
612
834
  { key: "events-listing-cards-images", label: "The events listing page shows each event's real cover image (not a generic icon placeholder) when one is set, and all cards in a row are the same height", href: "/events" },
@@ -655,6 +877,7 @@ const rawTesterChecklistItems = [
655
877
  {
656
878
  pageKey: "coupons",
657
879
  pageLabel: "Coupons",
880
+ href: "/user/coupons",
658
881
  cases: [
659
882
  { key: "view-claimed-coupons", label: "Claimed coupons list shows accurate coupons", href: "/user/coupons" },
660
883
  { key: "coupon-discount-applied", label: "Coupon discount is correctly reflected in the order total" },
@@ -667,6 +890,7 @@ const rawTesterChecklistItems = [
667
890
  {
668
891
  pageKey: "notifications",
669
892
  pageLabel: "Notifications",
893
+ href: "/user/notifications",
670
894
  cases: [
671
895
  { key: "receive-notification", label: "In-app notifications appear for order/bid/message events", href: "/user/notifications" },
672
896
  { key: "mark-read", label: "Marking a notification as read works" },
@@ -678,6 +902,7 @@ const rawTesterChecklistItems = [
678
902
  {
679
903
  pageKey: "faq-help",
680
904
  pageLabel: "FAQ & Help",
905
+ href: "/faqs",
681
906
  cases: [
682
907
  { key: "faq-bottom-borders", label: "FAQ question rows show a clear bottom-border divider on the homepage and the FAQs page", href: "/faqs" },
683
908
  { key: "faq-mobile-count", label: "Homepage FAQ section shows a good number of questions on mobile, not just 1-2", href: "/" },
@@ -689,14 +914,22 @@ const rawTesterChecklistItems = [
689
914
  {
690
915
  pageKey: "support-tickets",
691
916
  pageLabel: "Support Tickets",
917
+ href: "/user/support",
692
918
  cases: [
693
919
  { key: "create-ticket", label: "Creating a support ticket works", href: "/user/support" },
694
920
  { key: "reply-ticket", label: "Replying to an open support ticket works" },
921
+ {
922
+ key: "support-tickets-search-box",
923
+ label: "The support tickets list now has a working search box (by subject, id, or category) — it did not have one at all before",
924
+ description: "Converted 2026-08-21 off a hand-wired toolbar (which had status filter + \"hide resolved/closed\" toggle + sort, but no search input) onto the standard DataListingView scaffold, which adds search for free. Confirm typing part of a ticket's subject narrows the list, and the existing status filter + \"Hide resolved/closed\" toggle still work.",
925
+ href: "/user/support",
926
+ },
695
927
  ],
696
928
  },
697
929
  {
698
930
  pageKey: "public-profile",
699
931
  pageLabel: "Public Profiles & Stores",
932
+ href: "/stores",
700
933
  cases: [
701
934
  { key: "view-seller-store", label: "Viewing a seller's public store page works" },
702
935
  { key: "view-public-profile", label: "Viewing another user's public profile works" },
@@ -707,6 +940,7 @@ const rawTesterChecklistItems = [
707
940
  {
708
941
  pageKey: "general-design",
709
942
  pageLabel: "Colors, Styles, Readability, Mobile",
943
+ href: "/",
710
944
  cases: [
711
945
  { key: "contrast-readability", label: "Text contrast/readability is good on product cards" },
712
946
  { key: "mobile-nav", label: "Mobile navigation (bottom bar, menu) works and looks correct" },
@@ -724,9 +958,38 @@ const rawTesterChecklistItems = [
724
958
  },
725
959
  ],
726
960
  },
961
+ {
962
+ pageKey: "form-validation-errors",
963
+ pageLabel: "Form Validation & Error Summary",
964
+ href: "/store/products/new",
965
+ cases: [
966
+ {
967
+ key: "error-summary-shows-beside-submit",
968
+ label: "Submitting an admin/seller form (e.g. new product, new blog post, new event) with missing/invalid required fields shows a clearly-visible list of every problem right beside the Publish/Save button — not a generic \"something went wrong\" message",
969
+ description: "Fixed 2026-08-21 — validation failures previously surfaced as an opaque, unhelpful production error with no indication of which field failed. A new shared FormErrorSummary component now lists every simultaneous issue live.",
970
+ href: "/store/products/new",
971
+ },
972
+ {
973
+ key: "error-summary-live-on-change",
974
+ label: "The error summary beside Publish/Save updates live as you fix a field (the error disappears from the list immediately), not only after you click Submit again",
975
+ href: "/store/products/new",
976
+ },
977
+ {
978
+ key: "error-summary-step-tagged",
979
+ label: "On a multi-step form (e.g. new product wizard), an error for a field on a step you're not currently viewing is labeled with that step's name in the summary, and clicking it jumps you to the correct step",
980
+ href: "/store/products/new",
981
+ },
982
+ {
983
+ key: "error-summary-supplements-inline",
984
+ label: "Field-level inline error messages (shown directly under/beside the input) still appear as before — the new summary is in addition to them, not a replacement",
985
+ href: "/store/products/new",
986
+ },
987
+ ],
988
+ },
727
989
  {
728
990
  pageKey: "dashboard-layout",
729
991
  pageLabel: "Dashboards — Collapsible Sections & Mobile Tables",
992
+ href: "/admin/dashboard",
730
993
  cases: [
731
994
  { key: "collapsible-admin", label: "Admin dashboard sections expand/collapse and remember their state on reload", href: "/admin/dashboard" },
732
995
  { key: "collapsible-store", label: "Store dashboard sections expand/collapse and remember their state on reload", href: "/store" },
@@ -748,11 +1011,18 @@ const rawTesterChecklistItems = [
748
1011
  label: "Row-level action buttons (the same ones in the table's overflow menu — edit, approve, delete, etc.) also appear directly on each list/grid card, not just in the table view",
749
1012
  href: "/admin/orders",
750
1013
  },
1014
+ {
1015
+ key: "listing-toolbar-consistency",
1016
+ label: "Every admin/seller/user dashboard listing page uses the exact same search+filter-drawer+sort-dropdown+pagination toolbar layout — no page has a bespoke, differently-laid-out search/filter UI of its own",
1017
+ description: "A 2026-08-21 sweep found 22 dashboard listings (admin Page Views/Ads/Carousel-adjacent/Event Entries, seller Bids/Orders/Products/Shipping Configs/Payout Methods/Store Categories/Reviews, and 10 user pages — Orders, Returns, Bids, Digital Codes, Events, Pre-Orders, Prize Draws, Reviews, Support, Notifications, plus seller Sublisting Categories) that had each hand-rolled their own toolbar with inconsistent capabilities (some had no search at all, some had no filter drawer). All were migrated onto the shared DataListingView scaffold, and a new strict-zero audit (audit-listing-view-standard.mjs) now blocks any future page from reintroducing a bespoke one. Spot-check 2-3 pages across different portals (e.g. /admin/analytics, /store/payout-methods, /user/support) and confirm they all look and behave identically in toolbar layout.",
1018
+ href: "/admin/analytics",
1019
+ },
751
1020
  ],
752
1021
  },
753
1022
  {
754
1023
  pageKey: "hand-mode-layout",
755
1024
  pageLabel: "Left-Hand Mode",
1025
+ href: "/user/settings",
756
1026
  cases: [
757
1027
  { key: "sidebar-flips", label: "With Left-hand mode ON, the admin/store/user dashboard's persistent left navigation sidebar (and its collapse handle) moves to the right side of the screen; with it OFF (default), the sidebar stays on the left", href: "/admin/dashboard" },
758
1028
  { key: "drawers-flip", label: "With Left-hand mode ON, side drawers/panels (filters, quick-add forms, cart, edit/create panels) that normally slide in from the right now slide in from the left", href: "/products" },
@@ -768,13 +1038,21 @@ const rawTesterChecklistItems = [
768
1038
  {
769
1039
  pageKey: "footer-theme",
770
1040
  pageLabel: "Footer & Dark Mode",
1041
+ href: "/",
771
1042
  cases: [
772
1043
  { key: "footer-dark-mode", label: "Footer background and all link/text colors switch correctly between light and dark mode", href: "/" },
1044
+ {
1045
+ key: "footer-github-icon",
1046
+ label: "The footer's social icon row (brand column, bottom-left) shows a GitHub icon alongside Instagram/Twitter/WhatsApp, and clicking it opens the developer's real GitHub profile in a new tab",
1047
+ description: "Added 2026-08-20 — GITHUB was a new socialUrls key with no footer icon before this. Confirm the icon renders (not a broken/missing glyph) and the link target is a real, working github.com profile, not a placeholder or dead link.",
1048
+ href: "/",
1049
+ },
773
1050
  ],
774
1051
  },
775
1052
  {
776
1053
  pageKey: "homepage-carousels",
777
1054
  pageLabel: "Homepage Carousels",
1055
+ href: "/",
778
1056
  cases: [
779
1057
  { key: "carousel-loops", label: "Homepage carousels (Shop by Category, Top Brands, Featured Products, Live Auctions, Reserve Before It Ships, Verified Stores, Tournaments & Events, Collector Reviews) loop back to the first item after reaching the last instead of getting stuck at the end", href: "/" },
780
1058
  { key: "carousel-no-flicker", label: "Homepage carousel auto-scroll does not flash/flicker when looping back to the first item" },
@@ -804,10 +1082,21 @@ const rawTesterChecklistItems = [
804
1082
  {
805
1083
  pageKey: "core-listing-pages",
806
1084
  pageLabel: "Homepage & Core Listing Pages",
1085
+ href: "/",
807
1086
  cases: [
808
1087
  { key: "homepage-loads", label: "The homepage loads without errors on both desktop and mobile", href: "/" },
809
1088
  { key: "about-us-in-main-nav", label: "\"About Us\" appears as an item in the main public top navigation, not just the footer", href: "/about" },
810
- { key: "developer-page-loads", label: "The Developer page loads correctly and is linked from the footer's Support column next to About Us", href: "/developer" },
1089
+ {
1090
+ key: "about-team-real-founder",
1091
+ label: "The About page's \"Who's Behind LetItRip\" team section shows the real founder's name (not a placeholder persona) and a working \"GitHub ↗\" link on their card",
1092
+ description: "Updated 2026-08-20 — the founder card previously used a fictional placeholder name with no GitHub link. Confirm the founder card's name matches the real developer and the GitHub link opens their actual profile.",
1093
+ href: "/about",
1094
+ },
1095
+ {
1096
+ key: "developer-page-loads",
1097
+ label: "The Developer page loads correctly, is linked from the footer's Support column next to About Us, shows the real developer's name, and its \"GitHub ↗\" link opens their actual GitHub profile",
1098
+ href: "/developer",
1099
+ },
811
1100
  { key: "products-listing-page", label: "The products listing page loads and paginates correctly", href: "/products" },
812
1101
  { key: "auctions-listing-page", label: "The auctions listing page loads correctly", href: "/auctions" },
813
1102
  { key: "preorders-listing-page", label: "The pre-orders listing page loads correctly", href: "/pre-orders" },
@@ -832,11 +1121,18 @@ const rawTesterChecklistItems = [
832
1121
  description: "Verify against \"Beyblade Burst\" (category-beyblade-burst) — Related Categories should show Beyblade Original, Metal Fight, and Beyblade X (siblings under the \"Spinning Tops\" root).",
833
1122
  href: "/categories",
834
1123
  },
1124
+ {
1125
+ key: "category-brand-highlights-faq-grouped-listings",
1126
+ label: "A category detail page shows a highlights list + FAQ accordion right after the hero, a brand detail page shows the same plus an \"About this brand\" panel (website/country/founded), and both show a \"Grouped listings\" carousel populated with real items",
1127
+ description: "Added 2026-08-21 — CategoryDocument previously had no schema field at all for editorial content (highlights/FAQ), and GroupedListingsCarousel had zero public consumers on category/brand pages. Verify against \"Beyblade Burst\" (category-beyblade-burst, /categories/category-beyblade-burst) and \"Takara-Tomy\" (brand-takara-tomy, /brands/brand-takara-tomy) — both should show 3-5 highlight bullets, 2-4 FAQ entries, and (brand only) a working website link + country + founded year.",
1128
+ href: "/categories/category-beyblade-burst",
1129
+ },
835
1130
  ],
836
1131
  },
837
1132
  {
838
1133
  pageKey: "stores-sellers-directories",
839
1134
  pageLabel: "Store & Seller Directories",
1135
+ href: "/stores",
840
1136
  cases: [
841
1137
  { key: "store-directory", label: "The store directory page loads correctly", href: "/stores" },
842
1138
  { key: "store-detail-tabs", label: "A store detail page's listing-type dropdown (Products/Auctions/Pre-Orders/Prize Draws/Bundles/Classifieds/Digital Codes/Live Items/Art & Stickers) switches correctly between listing types, and the separate Coupons/Reviews/About tabs next to it all load correctly", description: "The listing-type dropdown is always a dropdown (not just on narrow/mobile widths, unlike category/brand/product/event tabs) since a store can have up to 9 listing types — Coupons, Reviews, and About stay as standalone tabs beside it, never folded into the dropdown." },
@@ -860,6 +1156,7 @@ const rawTesterChecklistItems = [
860
1156
  {
861
1157
  pageKey: "help-how-it-works",
862
1158
  pageLabel: "Help & How-It-Works Pages",
1159
+ href: "/help",
863
1160
  cases: [
864
1161
  { key: "contact-page", label: "The contact page loads and the form submits correctly", href: "/contact" },
865
1162
  { key: "help-page", label: "The help page loads correctly", href: "/help" },
@@ -870,6 +1167,7 @@ const rawTesterChecklistItems = [
870
1167
  {
871
1168
  pageKey: "legal-policy-pages",
872
1169
  pageLabel: "Legal & Policy Pages",
1170
+ href: "/help",
873
1171
  cases: [
874
1172
  { key: "shipping-refund-policy", label: "Shipping-policy and refund-policy pages load correctly" },
875
1173
  { key: "privacy-cookies-security", label: "Privacy, cookies, and security pages load correctly" },
@@ -878,6 +1176,7 @@ const rawTesterChecklistItems = [
878
1176
  {
879
1177
  pageKey: "auth-error-pages",
880
1178
  pageLabel: "Remaining Auth & Error Pages",
1179
+ href: "/auth/login",
881
1180
  cases: [
882
1181
  { key: "register-page", label: "The register page loads and account creation works", href: "/auth/register" },
883
1182
  { key: "forgot-reset-password-pages", label: "Forgot-password and reset-password pages both work" },
@@ -890,6 +1189,7 @@ const rawTesterChecklistItems = [
890
1189
  {
891
1190
  pageKey: "bug-hunters",
892
1191
  pageLabel: "Bug Hunters Leaderboard",
1192
+ href: "/bug-hunters",
893
1193
  cases: [
894
1194
  {
895
1195
  key: "leaderboard-loads",
@@ -906,6 +1206,7 @@ const rawTesterChecklistItems = [
906
1206
  {
907
1207
  pageKey: "catalog-listings",
908
1208
  pageLabel: "Catalog & Listings",
1209
+ href: "/admin/products",
909
1210
  cases: [
910
1211
  { key: "brands-crud", label: "Admin can create, edit, and list brands", href: "/admin/brands" },
911
1212
  { key: "categories-crud", label: "Admin can create, edit, and list categories", href: "/admin/categories" },
@@ -932,6 +1233,7 @@ const rawTesterChecklistItems = [
932
1233
  {
933
1234
  pageKey: "coupons",
934
1235
  pageLabel: "Coupons",
1236
+ href: "/admin/coupons",
935
1237
  cases: [
936
1238
  { key: "coupon-create-percentage", label: "Admin can create a percentage-type coupon", href: "/admin/coupons/new" },
937
1239
  { key: "coupon-create-fixed-freeship-bxgy", label: "Admin can create fixed, free_shipping, and buy_x_get_y coupon types" },
@@ -944,6 +1246,7 @@ const rawTesterChecklistItems = [
944
1246
  {
945
1247
  pageKey: "events-raffles-spin",
946
1248
  pageLabel: "Events, Raffles & Spin Wheel",
1249
+ href: "/admin/events",
947
1250
  cases: [
948
1251
  { key: "raffle-create-open", label: "Admin can create an open_raffle event", href: "/admin/events/new" },
949
1252
  { key: "raffle-create-top-n-scorers", label: "Admin can create a top_n_scorers raffle event" },
@@ -952,11 +1255,18 @@ const rawTesterChecklistItems = [
952
1255
  { key: "spin-wheel-create", label: "Admin can create a spin_wheel event with weighted prizes" },
953
1256
  { key: "spin-wheel-limits-enforced", label: "spinMaxPerUser and spinWindowStart/End are correctly enforced once configured by admin" },
954
1257
  { key: "event-entries-admin-view", label: "Admin event-entries list shows accurate entries", href: "/admin/event-entries" },
1258
+ {
1259
+ key: "event-entries-view-before-deciding",
1260
+ label: "Admin → Event Entries rows have a \"View details\" action (and are click-openable) showing the entrant's email, points and submitted responses before Confirm / Waitlist / Cancel is used",
1261
+ description: "Fixed 2026-08-21 — the row offered only Confirm/Waitlist/Cancel, so an admin decided an entry's fate without ever seeing what the entrant actually submitted. Open an entry on a poll or survey event and confirm its responses payload is visible in the detail modal.",
1262
+ href: "/admin/event-entries",
1263
+ },
955
1264
  ],
956
1265
  },
957
1266
  {
958
1267
  pageKey: "prize-draws-lotteries",
959
1268
  pageLabel: "Prize Draws / Lotteries",
1269
+ href: "/admin/prize-draws",
960
1270
  cases: [
961
1271
  { key: "prizedraw-create", label: "Admin can create a prize-draw listing, choosing instant or scheduled reveal mode and a 1–15 day duration", href: "/admin/prize-draws" },
962
1272
  { key: "prizedraw-reveal-winner", label: "Winners are assigned automatically via crypto.randomInt (on payment confirmation for instant mode, or at expiry/sellout for scheduled mode) — never by a manual admin click" },
@@ -968,15 +1278,23 @@ const rawTesterChecklistItems = [
968
1278
  {
969
1279
  pageKey: "bundles",
970
1280
  pageLabel: "Bundles / Grouped Listings",
1281
+ href: "/admin/bundles",
971
1282
  cases: [
972
1283
  { key: "bundle-create", label: "Admin can create a bundle from existing products", href: "/admin/bundles" },
973
1284
  { key: "bundle-stock-sync", label: "Bundle stock correctly syncs when a component product's stock changes" },
974
1285
  { key: "bundle-edit-delete", label: "Admin can edit and delete a bundle" },
1286
+ {
1287
+ key: "bundle-brand-picker",
1288
+ label: "The bundle editor has a \"Brand\" select (Takara-Tomy / Beyblade / No specific brand) that saves to the bundle's own brandSlug field, and that bundle then appears under the matching brand's page",
1289
+ description: "Added 2026-08-21 — bundle→brand association used to be a fragile heuristic (checking the brand name against seo.keywords), replaced with a real brandSlug field. Edit an existing bundle (e.g. \"Original Series Collector's Set\"), confirm the Brand select shows its current brand, change it, save, and verify it now appears on the new brand's page instead of the old one.",
1290
+ href: "/admin/bundles",
1291
+ },
975
1292
  ],
976
1293
  },
977
1294
  {
978
1295
  pageKey: "classifieds-digitalcodes-live",
979
1296
  pageLabel: "Classifieds, Digital Codes & Live Listings",
1297
+ href: "/admin/classified",
980
1298
  cases: [
981
1299
  { key: "classified-create-moderate", label: "Admin can create and moderate classified listings", href: "/admin/classified" },
982
1300
  { key: "digitalcode-create-moderate", label: "Admin can create and moderate digital-code listings", href: "/admin/digital-codes" },
@@ -986,6 +1304,7 @@ const rawTesterChecklistItems = [
986
1304
  {
987
1305
  pageKey: "blog-faqs",
988
1306
  pageLabel: "Blog & FAQs",
1307
+ href: "/admin/blog",
989
1308
  cases: [
990
1309
  { key: "blog-create-edit-publish", label: "Admin can create, edit, and publish a blog post", href: "/admin/blog" },
991
1310
  { key: "blog-media-step-save", label: "On the blog editor's Media step, setting a Cover Image and/or a YouTube Video ID and saving succeeds (no validation error) and both persist correctly when reopening the post for edit" },
@@ -995,8 +1314,47 @@ const rawTesterChecklistItems = [
995
1314
  {
996
1315
  pageKey: "orders-fulfillment",
997
1316
  pageLabel: "Orders & Fulfillment",
1317
+ href: "/admin/orders",
998
1318
  cases: [
999
1319
  { key: "orders-status-change", label: "Admin orders list shows accurate orders and status changes save correctly", href: "/admin/orders" },
1320
+ {
1321
+ key: "admin-orders-payment-review-filters",
1322
+ label: "The Admin Orders filter drawer has a \"Manual payment\" chip group with \"Awaiting payment\" and \"Awaiting verification\" — and each one actually returns the right orders",
1323
+ description: "Added 2026-08-21 — before this there was no way to find manual-payment orders needing action; they were indistinguishable from any other pending order. \"Awaiting payment\" = buyer hasn't uploaded a screenshot yet. \"Awaiting verification\" = proof submitted, nobody has approved/rejected it. Place a UPI/Cash order and check it appears under \"Awaiting payment\"; upload proof and confirm it moves to \"Awaiting verification\". Picking a Manual-payment chip clears the Status chip (and vice versa) on purpose — the queue is always status=pending, so combining them could only ever return nothing.",
1324
+ href: "/admin/orders",
1325
+ },
1326
+ {
1327
+ key: "admin-orders-payment-state-on-row",
1328
+ label: "Each manual-payment order's row in the Admin Orders list shows its payment state inline (\"Awaiting payment\" / \"Awaiting verification\" / \"Payment verified\" / \"Re-upload requested\" / \"Payment rejected\") — you don't have to open the drawer to tell which ones need action",
1329
+ href: "/admin/orders",
1330
+ },
1331
+ {
1332
+ key: "admin-orders-view-payment-proof",
1333
+ label: "Opening a manual-payment order shows the buyer's payment screenshot, the UTR, the expected-vs-reported UPI IDs, and a red \"UPI mismatch\" warning when they disagree",
1334
+ href: "/admin/orders",
1335
+ },
1336
+ {
1337
+ key: "admin-orders-verify-payment",
1338
+ label: "\"Verify payment\" on an order with submitted proof marks it paid and moves it to Processing — and it then disappears from the \"Awaiting verification\" queue",
1339
+ href: "/admin/orders",
1340
+ },
1341
+ {
1342
+ key: "admin-orders-request-reupload",
1343
+ label: "\"Request re-upload\" (requires a review note) clears the buyer's proof, gives them another 15 minutes, and sends them a notification containing the note",
1344
+ description: "Pair with the buyer-side case `manual-payment-reupload-note-visible-to-buyer`. Critically, verify the buyer CAN actually re-submit afterwards, and that the re-submitted proof shows up again under admin \"Awaiting verification\" — a 2026-08-21 fix; previously the re-upload left a stale review outcome on the order, so the corrected proof was invisible to both the admin queue and the 2-hour auto-approve sweep, and the order just silently stalled.",
1345
+ href: "/admin/orders",
1346
+ },
1347
+ {
1348
+ key: "admin-orders-reject-fraud",
1349
+ label: "\"Reject as fraud\" (requires a review note) cancels the order, returns the item to stock, and suspends the buyer's account for 7 days",
1350
+ description: "Use a throwaway buyer account — this really does ban it. Confirm the stock actually goes back up on the product page, and that the banned account can't sign in.",
1351
+ href: "/admin/orders",
1352
+ },
1353
+ {
1354
+ key: "admin-orders-payment-actions-hidden-when-paid",
1355
+ label: "Once a payment is verified, the Verify / Request re-upload / Reject buttons are replaced by a \"Payment verified\" badge — an already-paid order can't be re-verified or rejected",
1356
+ href: "/admin/orders",
1357
+ },
1000
1358
  { key: "bids-admin-view", label: "Admin bids view shows accurate bid data", href: "/admin/bids" },
1001
1359
  { key: "return-requests-triage", label: "Admin can triage return requests", href: "/admin/return-requests" },
1002
1360
  { key: "fulfillment-queue-admin", label: "Admin fulfillment queue shows accurate pending items", href: "/admin/fulfillment" },
@@ -1004,13 +1362,32 @@ const rawTesterChecklistItems = [
1004
1362
  { key: "print-center-admin", label: "Admin print-center generates labels/invoices correctly", href: "/admin/print-center" },
1005
1363
  { key: "payouts-export-admin", label: "Admin can export payouts", href: "/admin/payouts" },
1006
1364
  { key: "bulk-action-realtime-progress", label: "A bulk admin action (e.g. bulk order status update) shows live progress via the bulk_events realtime channel and resolves without polling" },
1365
+ {
1366
+ key: "admin-order-list-item-and-detail",
1367
+ label: "Admin order list rows show the order's item (thumbnail + title), not a raw order id, and clicking through (or \"Open full page\") shows the full items list on the order detail",
1368
+ description: "Fixed 2026-08-21 — AdminOrdersView.mapRows built \"Order {id}\" from a nonexistent orderNumber field, and AdminOrderEditorView showed zero items even when opened. Both now read the order's already-denormalized items[] (Root Cause #52).",
1369
+ href: "/admin/orders",
1370
+ },
1371
+ {
1372
+ key: "admin-payout-detail-view",
1373
+ label: "Every admin payout row has an \"Open full page\" action + a working \"Mark as paid\" action, landing on a real /admin/payouts/[id]/view page with a full gross/platform-fee/gateway-fee/GST/refund-deduction/net breakdown and linked order ids — not just a bare \"transaction ID\" modal",
1374
+ description: "Added 2026-08-21 — admin payouts previously had no detail view of any kind. Open any payout from /admin/payouts and confirm the breakdown numbers add up (gross − fees − deductions = net).",
1375
+ href: "/admin/payouts",
1376
+ },
1007
1377
  ],
1008
1378
  },
1009
1379
  {
1010
1380
  pageKey: "users-trust",
1011
1381
  pageLabel: "Users & Trust",
1382
+ href: "/admin/users",
1012
1383
  cases: [
1013
1384
  { key: "users-role-change", label: "Admin can change a user's role and toggle isTester/canTestAdmin", href: "/admin/users" },
1385
+ {
1386
+ key: "admin-user-detail-enriched",
1387
+ label: "An admin user's own detail page (/admin/users/[id]) shows an avatar, stat tiles (orders/auctions won/items sold/reviews/rating), the user's public profile (bio/location/website/social links), last-sign-in + login count, and a small inline address list — not just 3 plain text lines",
1388
+ description: "Fixed 2026-08-21 — the page fetched the full UserDocument but rendered almost none of it. Open any real user (e.g. a tester or seller persona) from /admin/users and confirm the header/stats/profile/addresses sections are populated, not blank.",
1389
+ href: "/admin/users",
1390
+ },
1014
1391
  {
1015
1392
  key: "admin-delete-user-complete",
1016
1393
  label: "Deleting a user from the admin Users editor removes their Firestore profile, their active sessions, AND their Firebase Auth record — not just the Firestore doc",
@@ -1028,11 +1405,18 @@ const rawTesterChecklistItems = [
1028
1405
  { key: "reports-admin", label: "Admin reports queue works correctly", href: "/admin/reports" },
1029
1406
  { key: "payment-methods-clusters-admin", label: "Admin can manage payment methods and payment-method clusters", href: "/admin/payment-methods" },
1030
1407
  { key: "catalogue-approvals-admin", label: "Admin can approve/reject personal catalogue submissions", href: "/admin/catalogue-approvals" },
1408
+ {
1409
+ key: "catalogue-approvals-view-before-deciding",
1410
+ label: "Admin → Catalogue Approvals rows have a \"View\" action (and are click-openable) showing the submitted item's PHOTOS, description, estimated price, quantity and condition — with Approve and Reject available from inside that detail view",
1411
+ description: "Fixed 2026-08-21 — the row previously offered only Approve/Reject buttons, so an admin accepted or refused a user's submission with none of its content rendered anywhere: no photos, no description, no price. This was the most severe finding of the dead-end-listing sweep. Confirm you can see the item's images before deciding, and that approving/rejecting from inside the modal behaves the same as the row buttons (reject still prompts for a reason the owner will see).",
1412
+ href: "/admin/catalogue-approvals",
1413
+ },
1031
1414
  ],
1032
1415
  },
1033
1416
  {
1034
1417
  pageKey: "content-marketing",
1035
1418
  pageLabel: "Content & Marketing",
1419
+ href: "/admin/ads",
1036
1420
  cases: [
1037
1421
  { key: "ads-crud-preview", label: "Admin can create, edit, and preview ads", href: "/admin/ads" },
1038
1422
  { key: "newsletter-export-admin", label: "Admin can export the newsletter subscriber list", href: "/admin/newsletter" },
@@ -1046,16 +1430,72 @@ const rawTesterChecklistItems = [
1046
1430
  {
1047
1431
  pageKey: "site-system",
1048
1432
  pageLabel: "Site & System",
1433
+ href: "/admin/site",
1049
1434
  cases: [
1050
1435
  { key: "site-settings-admin", label: "Admin site settings page saves correctly", href: "/admin/site" },
1436
+ {
1437
+ key: "auction-bid-tiers-admin",
1438
+ label: "Admin can add, edit, and remove bid-increment tiers in Site Settings → Auction Config, and the changes persist after saving and reloading the page",
1439
+ description: "Each tier row has an editable \"up to (₹)\" amount and \"increment (₹)\" amount; the last row is fixed as \"and above\" (open-ended) and can't have its threshold edited. Add a new tier, edit an existing increment, remove a tier, save, then reload the Site Settings page and confirm the tier list matches what was saved — not reverted to the old defaults.",
1440
+ href: "/admin/site",
1441
+ },
1442
+ {
1443
+ key: "daily-digest-recipients-save",
1444
+ label: "Site Settings → Notifications → \"Daily status digest\" saves its recipient and CC lists (one address per line) and they survive a page reload",
1445
+ description: "Added 2026-08-21. Toggle the digest on, enter a couple of addresses in Recipients (one per line) and one in CC, save, then reload Site Settings and confirm both lists come back exactly as entered. Also confirm the whole recipients/CC block is hidden when the toggle is off.",
1446
+ href: "/admin/site",
1447
+ },
1448
+ {
1449
+ key: "daily-digest-on-deploy",
1450
+ label: "A digest email arrives once after a new version is deployed — exactly once per deployment, not repeatedly as the site gets traffic",
1451
+ description: "Added 2026-08-21. The deploy digest fires from server startup, which on Vercel happens on every serverless cold start — so it's guarded by a stored version marker and only the first cold start of a given deployment sends. After a deploy, confirm: (a) one digest arrives whose body opens with \"Deployment <version> is live.\"; (b) browsing the site for a while (forcing more cold starts) does NOT produce further copies; (c) the next deploy produces exactly one new email again.",
1452
+ href: "/admin/site",
1453
+ },
1454
+ {
1455
+ key: "daily-digest-email-content",
1456
+ label: "The daily status digest email arrives with subject exactly \"Daily Status\" and shows the last 24h order count, revenue, active listings, pending-over-24h count, and a per-status breakdown",
1457
+ // audit-hardcoded-api-routes-ok: prose in a tester-facing description (a copy-pasteable console snippet), not a call site — nothing to route through the registry.
1458
+ description: "Added 2026-08-21. The digest runs automatically at 10:00 IST, but you don't need to wait — an admin can trigger it on demand by POSTing to /api/admin/daily-digest/trigger (e.g. from the browser console while logged in as admin: fetch('/api/admin/daily-digest/trigger',{method:'POST'})). Confirm the email reaches every configured recipient AND any CC addresses, and that the numbers match what the admin orders list actually shows for the last 24 hours. Note: this requires the Firebase Functions deploy to have happened for the scheduled 10:00 run — the manual trigger works regardless.",
1459
+ href: "/admin/site",
1460
+ },
1051
1461
  { key: "admin-dashboard-widgets", label: "Admin dashboard widgets show accurate data", href: "/admin/dashboard" },
1052
1462
  { key: "analytics-admin", label: "Admin analytics dashboard shows accurate data", href: "/admin/analytics" },
1463
+ {
1464
+ key: "pageviews-report-listing-standard",
1465
+ label: "The \"Page Views\" tab in Admin Analytics is a full standard listing — a search box (by entity id or URL), an entity-type filter drawer, a \"Most views\"/\"Fewest views\" sort dropdown, and real pagination — not a bare unsearchable table",
1466
+ description: "Converted 2026-08-21 off a hand-rolled two-table layout onto the standard DataListingView scaffold. Switch to the Page Views tab, confirm the search box and filter drawer are present and actually narrow the rows, the sort dropdown reorders by view count, and clicking a row's entity/URL navigates to that real page.",
1467
+ href: "/admin/analytics",
1468
+ },
1469
+ {
1470
+ key: "pageviews-tracks-all-listing-types",
1471
+ label: "Visiting a blog post, event, pre-order, prize draw, classified, digital-code, live item, or bundle detail page records a page view that shows up in Admin Analytics → Page Views, filtered to that entity type",
1472
+ description: "Added 2026-08-21 — these 8 detail-page types previously recorded nothing at all (only products/auctions/categories/homepage/stores/reviews/profiles were tracked). Visit a few of the following, then filter the Page Views report to the matching entity type and confirm a row appears: /blog/spot-genuine-takara-tomy-beyblade (blog), /events/event-original-series-clearance (event), /pre-orders/preorder-tester-sandbox-1 (pre-order), /prize-draws/prizedraw-tester-sandbox-1 (prize-draw), /classified/classified-tester-sandbox-1 (classified), /digital-codes/digitalcode-tester-sandbox-1 (digital-code), /live/live-tester-sandbox-1 (live), /bundles/bundle-tester-sandbox (bundle).",
1473
+ href: "/blog/spot-genuine-takara-tomy-beyblade",
1474
+ },
1053
1475
  { key: "maintenance-pages-admin", label: "The maintenance pages (analysis, client-errors, cloud-logs, function-errors, payment-rollbacks, server-errors + detail) all load correctly", href: "/admin/maintenance" },
1054
1476
  { key: "copilot-admin", label: "Admin copilot page works correctly", href: "/admin/copilot" },
1055
1477
  { key: "team-admin", label: "Admin team page works correctly", href: "/admin/team" },
1056
1478
  { key: "guide-pages-admin", label: "The 8 admin guide pages all load correctly", href: "/admin/guide" },
1057
1479
  { key: "tester-checklist-crud-admin", label: "Admin can create, edit, and toggle adminOnly on tester checklist items", href: "/admin/tester-checklist" },
1058
1480
  { key: "tester-feedback-report-export", label: "Admin tester-feedback report shows Yes/No analytics grouped correctly and the Download Report export works", href: "/admin/tester-feedback" },
1481
+ {
1482
+ key: "tester-feedback-view-before-confirming-bug",
1483
+ label: "Admin → Tester Feedback rows have a \"View details\" action (and are click-openable) showing the tester's full comment and screenshot before \"Confirm bug\" is used",
1484
+ description: "Fixed 2026-08-21 — the row truncated the comment into a one-line secondary and never surfaced the screenshot at all, yet offered \"Mark reviewed\" and \"Confirm bug\" (which credits the reporting tester and disables the checklist item). Answer a checklist item \"No\" with a comment + screenshot as a tester, then confirm an admin can read both in the detail modal.",
1485
+ href: "/admin/tester-feedback",
1486
+ },
1487
+ {
1488
+ key: "admin-audit-log-page",
1489
+ label: "The new /admin/audit-log page (Finance nav group) lists real entries — actor, action, target, date — with working Action and Actor UID filters, and clicking a row opens a detail modal with the full reason + metadata payload",
1490
+ description: "Added 2026-08-21 — no admin action audit trail existed anywhere before this (the only prior \"logs\" surface was raw Cloud Logging infra output, not actor/action semantics). Perform any instrumented action — hard-ban/soft-ban/unban a test user, mark a payout paid, edit a coupon, change a store's status, change a user's role, or use admin checkout bypass — then confirm a matching entry appears here within a few seconds.",
1491
+ href: "/admin/audit-log",
1492
+ },
1493
+ {
1494
+ key: "admin-notification-detail-modal",
1495
+ label: "Admin Notifications rows have a \"View details\" action (and the row itself is clickable) opening a modal with the full title/message/image/related-entity/action-link — not list-only with just Resend/Delete",
1496
+ description: "Added 2026-08-21 — admin/notifications was list-only before, with no way to see a notification's full body/payload/link.",
1497
+ href: "/admin/notifications",
1498
+ },
1059
1499
  {
1060
1500
  key: "admin-sidebar-logout-button",
1061
1501
  label: "The admin dashboard sidebar has a visible \"Log out\" action at the bottom (not just the header profile dropdown), and clicking it signs out and redirects to login",
@@ -1072,20 +1512,46 @@ const rawTesterChecklistItems = [
1072
1512
  {
1073
1513
  pageKey: "buyer-data-admin",
1074
1514
  pageLabel: "Buyer-Data Admin Views",
1515
+ href: "/admin/carts",
1075
1516
  cases: [
1076
1517
  { key: "carts-admin-view", label: "Admin carts view shows accurate data", href: "/admin/carts" },
1518
+ {
1519
+ key: "carts-admin-row-opens-items",
1520
+ label: "Clicking a row on Admin → Carts opens a detail modal listing the actual items in that cart (thumbnail, title, quantity, line total) — not just the \"N items\" count the row already showed",
1521
+ description: "Fixed 2026-08-21 — the list response always contained the cart's items[] array, but mapRows only ever read `.length` for the row subtitle and discarded the rest, with no surface to show it (Root Cause #52). Open a cart that has at least one item and confirm each item renders with a thumbnail; a guest cart should show its session id, an authenticated one its user id.",
1522
+ href: "/admin/carts",
1523
+ },
1077
1524
  { key: "wishlists-admin-view", label: "Admin wishlists view shows accurate data", href: "/admin/wishlists" },
1525
+ {
1526
+ key: "wishlists-history-admin-row-opens-user",
1527
+ label: "Clicking a row on Admin → Wishlists or Admin → History navigates to that user's admin detail page",
1528
+ description: "Added 2026-08-21 — both rows were previously inert. Their list APIs deliberately return only a per-user summary (userId / itemCount / cap state), never the items themselves, since pulling every user's items into one payload would breach the Vercel Hobby response ceiling (Rule #6) — so the row already shows its whole record and the useful destination is the owning user. Confirm the click lands on /admin/users/{that user's id} and that page loads their real profile.",
1529
+ href: "/admin/wishlists",
1530
+ },
1078
1531
  { key: "history-admin-view", label: "Admin history view shows accurate data", href: "/admin/history" },
1079
1532
  { key: "notifications-admin-view", label: "Admin notifications and admin-notifications views show accurate data" },
1080
1533
  { key: "reviews-admin-view", label: "Admin reviews view shows accurate data", href: "/admin/reviews" },
1081
1534
  { key: "store-addresses-admin", label: "Admin store-addresses view shows accurate data", href: "/admin/store-addresses" },
1535
+ {
1536
+ key: "store-addresses-admin-row-opens-detail",
1537
+ label: "Clicking a row on Admin → Store Addresses opens a detail modal with the full address (contact name, phone, both address lines, landmark, city, state, postal code, country) and a \"Default pickup location\" badge where applicable",
1538
+ description: "Added 2026-08-21 — the row was completely inert (no click, no row actions, no editor) and truncated the address to label/city/state, so the phone and street lines were unreachable anywhere in the admin UI.",
1539
+ href: "/admin/store-addresses",
1540
+ },
1082
1541
  { key: "addresses-crud-admin", label: "Admin can create and edit addresses", href: "/admin/addresses" },
1083
1542
  { key: "stores-admin-view", label: "Admin stores view shows accurate data", href: "/admin/stores" },
1543
+ {
1544
+ key: "admin-store-detail-page",
1545
+ label: "Every admin store row's row menu has a \"View full page\" action landing on a real dedicated /admin/stores/[id]/view page — logo, status/verified/featured badges, an owner link to that owner's (now-enriched) user page, stats (products/items sold/reviews/rating), capabilities, and a \"Manage\" button opening the existing edit drawer",
1546
+ description: "Added 2026-08-21 — admin previously had no dedicated store detail page at all, only the edit drawer (no logo, no owner link, no stats). From /admin/stores, open the seeded \"Tester Sandbox Store\" (store-tester-sandbox) and confirm every section renders real data, and the owner link lands on that seller's admin user page.",
1547
+ href: "/admin/stores",
1548
+ },
1084
1549
  ],
1085
1550
  },
1086
1551
  {
1087
1552
  pageKey: "media-watermark",
1088
1553
  pageLabel: "Media Watermark Settings",
1554
+ href: "/admin/site",
1089
1555
  cases: [
1090
1556
  { key: "watermark-size-opacity-controls", label: "Adjusting the watermark Size and Opacity sliders in Site Settings → Watermark visibly changes how prominent the watermark is on newly-loaded product images", href: "/admin/site" },
1091
1557
  { key: "watermark-position-presets", label: "Each of the 5 watermark Position presets (Center, Top left, Top right, Bottom left, Bottom right) correctly repositions the watermark on newly-loaded images" },
@@ -1097,6 +1563,7 @@ const rawTesterChecklistItems = [
1097
1563
  {
1098
1564
  pageKey: "bug-hunter-rewards",
1099
1565
  pageLabel: BUG_HUNTER_REWARDS_PAGE_LABEL,
1566
+ href: "/admin/tester-feedback",
1100
1567
  cases: [
1101
1568
  {
1102
1569
  key: "confirm-bug",
@@ -1136,6 +1603,7 @@ const rawTesterChecklistItems = [
1136
1603
  pageLabel: BUG_HUNTER_REWARDS_PAGE_LABEL,
1137
1604
  label: "Demo fixture — reported bug, already confirmed and reopened (v1, disabled)",
1138
1605
  description: "Seed-only fixture demonstrating a confirmed bug: this v1 case is disabled (isActive:false) and credited to \"Mock User 18\". Its retest is \"Demo fixture — reported bug, already confirmed and reopened (v2, active)\" in this same page.",
1606
+ href: "/admin/tester-checklist",
1139
1607
  order: 100,
1140
1608
  isActive: false,
1141
1609
  adminOnly: true,
@@ -1154,6 +1622,7 @@ const rawTesterChecklistItems = [
1154
1622
  pageLabel: BUG_HUNTER_REWARDS_PAGE_LABEL,
1155
1623
  label: "Demo fixture — reported bug, already confirmed and reopened (v2, active)",
1156
1624
  description: "Seed-only fixture — the retest version reopened from the disabled v1 case in this same page. Active and answerable again; a fresh \"No\" answer here can be used to try Mark as Bug end-to-end.",
1625
+ href: "/admin/tester-checklist",
1157
1626
  order: 101,
1158
1627
  isActive: true,
1159
1628
  adminOnly: true,