@mohasinac/appkit 4.53.0 → 4.53.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (500) hide show
  1. package/dist/.build-stamp.json +3 -3
  2. package/dist/_internal/client/scaffolds/AppShell.js +1 -1
  3. package/dist/_internal/server/features/auctions/og.d.ts +3 -1
  4. package/dist/_internal/server/features/auctions/og.js +3 -1
  5. package/dist/_internal/server/features/bundles/actions.js +1 -1
  6. package/dist/_internal/server/features/business-documents/download.d.ts +41 -0
  7. package/dist/_internal/server/features/business-documents/download.js +91 -0
  8. package/dist/_internal/server/features/categories/adapters.d.ts +1 -1
  9. package/dist/_internal/server/features/categories/adapters.js +10 -0
  10. package/dist/_internal/server/features/checkout/actions.d.ts +14 -0
  11. package/dist/_internal/server/features/checkout/actions.js +106 -12
  12. package/dist/_internal/server/features/checkout/bundle-expansion.js +17 -0
  13. package/dist/_internal/server/features/checkout/data.d.ts +22 -0
  14. package/dist/_internal/server/features/checkout/group-rule-revalidation.d.ts +34 -0
  15. package/dist/_internal/server/features/checkout/group-rule-revalidation.js +124 -0
  16. package/dist/_internal/server/features/checkout/supplier-identity.d.ts +55 -0
  17. package/dist/_internal/server/features/checkout/supplier-identity.js +81 -0
  18. package/dist/_internal/server/features/classified/metadata.js +1 -1
  19. package/dist/_internal/server/features/classified/og.d.ts +3 -1
  20. package/dist/_internal/server/features/classified/og.js +3 -1
  21. package/dist/_internal/server/features/digital-code/metadata.js +1 -1
  22. package/dist/_internal/server/features/digital-code/og.d.ts +3 -1
  23. package/dist/_internal/server/features/digital-code/og.js +3 -1
  24. package/dist/_internal/server/features/digital-code/pool.d.ts +19 -4
  25. package/dist/_internal/server/features/digital-code/pool.js +19 -4
  26. package/dist/_internal/server/features/live/metadata.js +1 -1
  27. package/dist/_internal/server/features/live/og.d.ts +3 -1
  28. package/dist/_internal/server/features/live/og.js +3 -1
  29. package/dist/_internal/server/features/messages/actions.js +1 -1
  30. package/dist/_internal/server/features/parts/og.js +1 -1
  31. package/dist/_internal/server/features/pre-orders/og.d.ts +3 -1
  32. package/dist/_internal/server/features/pre-orders/og.js +3 -1
  33. package/dist/_internal/server/features/price-index/search.d.ts +18 -0
  34. package/dist/_internal/server/features/price-index/search.js +47 -7
  35. package/dist/_internal/server/features/prize-draws/og.d.ts +3 -1
  36. package/dist/_internal/server/features/prize-draws/og.js +3 -1
  37. package/dist/_internal/server/features/products/group-members.d.ts +26 -0
  38. package/dist/_internal/server/features/products/group-members.js +7 -1
  39. package/dist/_internal/server/features/products/og.d.ts +3 -1
  40. package/dist/_internal/server/features/products/og.js +3 -1
  41. package/dist/_internal/server/features/search/scoped-sources.d.ts +26 -0
  42. package/dist/_internal/server/features/search/scoped-sources.js +252 -0
  43. package/dist/_internal/server/features/search/search-everything.d.ts +57 -0
  44. package/dist/_internal/server/features/search/search-everything.js +70 -20
  45. package/dist/_internal/server/features/seo/sitemap.js +6 -0
  46. package/dist/_internal/server/features/shared/listing-adapters.js +4 -2
  47. package/dist/_internal/server/features/sticker-trace/adapters.d.ts +11 -0
  48. package/dist/_internal/server/features/sticker-trace/adapters.js +1 -0
  49. package/dist/_internal/server/features/sticker-trace/promote-to-listings.d.ts +26 -0
  50. package/dist/_internal/server/features/sticker-trace/promote-to-listings.js +264 -0
  51. package/dist/_internal/server/features/sticker-trace/sheet-actions.d.ts +3 -1
  52. package/dist/_internal/server/features/sticker-trace/sheet-actions.js +32 -0
  53. package/dist/_internal/server/features/sticker-trace/storage.d.ts +20 -1
  54. package/dist/_internal/server/features/sticker-trace/storage.js +20 -1
  55. package/dist/_internal/server/features/stores/adapters.d.ts +1 -1
  56. package/dist/_internal/server/features/stores/adapters.js +16 -0
  57. package/dist/_internal/server/features/stores/index.d.ts +1 -0
  58. package/dist/_internal/server/features/stores/index.js +1 -0
  59. package/dist/_internal/server/features/stores/tabs.d.ts +25 -0
  60. package/dist/_internal/server/features/stores/tabs.js +122 -0
  61. package/dist/_internal/shared/checkout/rules/_defaults.js +0 -2
  62. package/dist/_internal/shared/checkout/rules/_limits.d.ts +14 -1
  63. package/dist/_internal/shared/checkout/rules/_limits.js +14 -1
  64. package/dist/_internal/shared/checkout/rules/_registry.js +2 -0
  65. package/dist/_internal/shared/checkout/rules/auction.rule.js +9 -1
  66. package/dist/_internal/shared/checkout/rules/bundle.rule.js +13 -0
  67. package/dist/_internal/shared/checkout/rules/index.d.ts +2 -0
  68. package/dist/_internal/shared/checkout/rules/index.js +9 -0
  69. package/dist/_internal/shared/checkout/rules/offer.rule.js +0 -1
  70. package/dist/_internal/shared/checkout/rules/payment-gate.d.ts +26 -0
  71. package/dist/_internal/shared/checkout/rules/payment-gate.js +133 -0
  72. package/dist/_internal/shared/checkout/rules/prize-draw.rule.js +29 -8
  73. package/dist/_internal/shared/checkout/rules/spares.rule.d.ts +16 -0
  74. package/dist/_internal/shared/checkout/rules/spares.rule.js +5 -0
  75. package/dist/_internal/shared/checkout/rules/types.d.ts +26 -2
  76. package/dist/_internal/shared/config/schema.d.ts +122 -0
  77. package/dist/_internal/shared/features/cart/schema.js +3 -2
  78. package/dist/_internal/shared/features/products/group-rules.d.ts +75 -0
  79. package/dist/_internal/shared/features/products/group-rules.js +142 -0
  80. package/dist/_internal/shared/listing-types/_registry.d.ts +24 -0
  81. package/dist/_internal/shared/listing-types/_registry.js +39 -1
  82. package/dist/_internal/shared/listing-types/art/config.d.ts +1 -0
  83. package/dist/_internal/shared/listing-types/art/config.js +2 -0
  84. package/dist/_internal/shared/listing-types/auction/config.d.ts +1 -0
  85. package/dist/_internal/shared/listing-types/auction/config.js +2 -0
  86. package/dist/_internal/shared/listing-types/availability.js +48 -0
  87. package/dist/_internal/shared/listing-types/capabilities.js +19 -0
  88. package/dist/_internal/shared/listing-types/classified/config.d.ts +1 -0
  89. package/dist/_internal/shared/listing-types/classified/config.js +2 -0
  90. package/dist/_internal/shared/listing-types/digital-code/config.d.ts +1 -0
  91. package/dist/_internal/shared/listing-types/digital-code/config.js +2 -0
  92. package/dist/_internal/shared/listing-types/feature-flags.js +1 -0
  93. package/dist/_internal/shared/listing-types/live/config.d.ts +1 -0
  94. package/dist/_internal/shared/listing-types/live/config.js +2 -0
  95. package/dist/_internal/shared/listing-types/pre-order/config.d.ts +1 -0
  96. package/dist/_internal/shared/listing-types/pre-order/config.js +2 -0
  97. package/dist/_internal/shared/listing-types/prize-draw/config.d.ts +1 -0
  98. package/dist/_internal/shared/listing-types/prize-draw/config.js +2 -0
  99. package/dist/_internal/shared/listing-types/spares/config.d.ts +74 -0
  100. package/dist/_internal/shared/listing-types/spares/config.js +60 -0
  101. package/dist/_internal/shared/listing-types/spares/ctas.d.ts +1 -0
  102. package/dist/_internal/shared/listing-types/spares/ctas.js +4 -0
  103. package/dist/_internal/shared/listing-types/spares/og.js +1 -0
  104. package/dist/_internal/shared/listing-types/spares/schema.d.ts +2 -0
  105. package/dist/_internal/shared/listing-types/spares/schema.js +3 -0
  106. package/dist/_internal/shared/listing-types/spares/seed-factory.d.ts +1 -0
  107. package/dist/_internal/shared/listing-types/spares/seed-factory.js +1 -0
  108. package/dist/_internal/shared/listing-types/standard/config.d.ts +1 -0
  109. package/dist/_internal/shared/listing-types/standard/config.js +2 -0
  110. package/dist/_internal/shared/listing-types/stickers/config.d.ts +1 -0
  111. package/dist/_internal/shared/listing-types/stickers/config.js +2 -0
  112. package/dist/_internal/shared/search/match.d.ts +11 -0
  113. package/dist/_internal/shared/search/match.js +2 -1
  114. package/dist/_internal/shared/search/to-groups.d.ts +52 -0
  115. package/dist/_internal/shared/search/to-groups.js +141 -0
  116. package/dist/client.d.ts +8 -2
  117. package/dist/client.js +4 -1
  118. package/dist/constants/api-endpoints.d.ts +39 -15
  119. package/dist/constants/api-endpoints.js +28 -5
  120. package/dist/constants/cache-invalidation.js +3 -1
  121. package/dist/constants/field-names.d.ts +6 -0
  122. package/dist/constants/field-names.js +6 -0
  123. package/dist/features/about/components/PolicyPageView.d.ts +22 -2
  124. package/dist/features/about/components/PolicyPageView.js +14 -2
  125. package/dist/features/about/components/PublicProfileView.js +2 -1
  126. package/dist/features/about/components/ShippingPolicyView.js +42 -2
  127. package/dist/features/account/components/NotificationBell.js +1 -1
  128. package/dist/features/account/hooks/useProfileStats.js +5 -1
  129. package/dist/features/addresses/repository/addresses.repository.d.ts +16 -0
  130. package/dist/features/addresses/repository/addresses.repository.js +21 -0
  131. package/dist/features/admin/actions/admin-actions.js +16 -1
  132. package/dist/features/admin/components/AdminCategoryEditorView.js +19 -2
  133. package/dist/features/admin/components/AdminNavigationView.js +1 -1
  134. package/dist/features/admin/components/AdminPageHeader.js +1 -1
  135. package/dist/features/admin/components/AdminProductEditorView.js +17 -2
  136. package/dist/features/admin/components/AdminSidebar.js +1 -1
  137. package/dist/features/admin/components/AdminSiteSettingsView.js +39 -8
  138. package/dist/features/admin/components/AdminSparesView.d.ts +12 -0
  139. package/dist/features/admin/components/AdminSparesView.js +30 -0
  140. package/dist/features/admin/components/AdminUsersView.js +9 -7
  141. package/dist/features/admin/components/index.d.ts +2 -0
  142. package/dist/features/admin/components/index.js +1 -0
  143. package/dist/features/admin/components/sections/adminSectionsTypes.js +7 -0
  144. package/dist/features/admin/constants/filter-tabs.d.ts +3 -0
  145. package/dist/features/admin/constants/filter-tabs.js +1 -0
  146. package/dist/features/admin/schemas/firestore.d.ts +26 -2
  147. package/dist/features/auctions/components/AuctionDetailPageView.js +4 -11
  148. package/dist/features/auth/repository/user.repository.d.ts +13 -0
  149. package/dist/features/auth/repository/user.repository.js +28 -0
  150. package/dist/features/auth/schemas/firestore.d.ts +2 -0
  151. package/dist/features/auth/schemas/firestore.js +2 -0
  152. package/dist/features/business-documents/repository/business-documents.repository.d.ts +58 -0
  153. package/dist/features/business-documents/repository/business-documents.repository.js +54 -0
  154. package/dist/features/business-documents/schemas/firestore.d.ts +105 -0
  155. package/dist/features/business-documents/schemas/firestore.js +75 -0
  156. package/dist/features/cart/actions/cart-actions.js +83 -6
  157. package/dist/features/cart/repository/cart.repository.js +32 -4
  158. package/dist/features/cart/schemas/firestore.d.ts +20 -0
  159. package/dist/features/cart/schemas/index.d.ts +2 -2
  160. package/dist/features/catalogue/components/PublicCatalogueItemDetailView.js +1 -1
  161. package/dist/features/categories/api/[id]/price-guidance/route.d.ts +42 -0
  162. package/dist/features/categories/api/[id]/price-guidance/route.js +113 -0
  163. package/dist/features/categories/components/BrandDetailPageView.js +19 -3
  164. package/dist/features/categories/components/BrandDetailTabs.js +10 -0
  165. package/dist/features/categories/components/CategoriesIndexListing.js +23 -8
  166. package/dist/features/categories/components/CategoryDetailPageView.js +141 -14
  167. package/dist/features/categories/components/CategoryDetailTabs.js +12 -0
  168. package/dist/features/categories/components/CategoryGrid.js +10 -1
  169. package/dist/features/categories/components/PriceGuidanceHint.d.ts +29 -0
  170. package/dist/features/categories/components/PriceGuidanceHint.js +61 -0
  171. package/dist/features/categories/components/SubcategoryCardRow.d.ts +27 -0
  172. package/dist/features/categories/components/SubcategoryCardRow.js +98 -0
  173. package/dist/features/categories/constants/category-tabs.d.ts +20 -25
  174. package/dist/features/categories/constants/category-tabs.js +22 -39
  175. package/dist/features/categories/repository/categories.repository.d.ts +12 -0
  176. package/dist/features/categories/repository/categories.repository.js +20 -0
  177. package/dist/features/categories/schemas/firestore.d.ts +22 -1
  178. package/dist/features/categories/schemas/firestore.js +10 -0
  179. package/dist/features/categories/schemas/index.d.ts +6 -0
  180. package/dist/features/categories/schemas/index.js +2 -0
  181. package/dist/features/categories/types/index.d.ts +8 -0
  182. package/dist/features/categories/utils/taxonomy-visual.d.ts +16 -3
  183. package/dist/features/categories/utils/taxonomy-visual.js +26 -10
  184. package/dist/features/classified/components/ClassifiedDetailPageView.js +4 -9
  185. package/dist/features/contact/components/ContactPageView.d.ts +33 -1
  186. package/dist/features/contact/components/ContactPageView.js +58 -28
  187. package/dist/features/digital-codes/components/DigitalCodeDetailPageView.js +4 -9
  188. package/dist/features/grouped/components/GroupedListingDetailView.js +9 -1
  189. package/dist/features/grouped/schemas/firestore.d.ts +5 -0
  190. package/dist/features/homepage/actions/homepage-section-actions.d.ts +3 -3
  191. package/dist/features/homepage/components/CollectionCardsSection.d.ts +28 -6
  192. package/dist/features/homepage/components/CollectionCardsSection.js +43 -8
  193. package/dist/features/homepage/components/HowItWorksSection.d.ts +23 -6
  194. package/dist/features/homepage/components/HowItWorksSection.js +4 -4
  195. package/dist/features/homepage/components/PromoGrid.d.ts +31 -2
  196. package/dist/features/homepage/components/PromoGrid.js +12 -3
  197. package/dist/features/homepage/components/TestimonialsCarousel.d.ts +34 -2
  198. package/dist/features/homepage/components/TestimonialsCarousel.js +1 -1
  199. package/dist/features/homepage/components/index.d.ts +0 -20
  200. package/dist/features/homepage/components/index.js +0 -11
  201. package/dist/features/homepage/index.d.ts +0 -2
  202. package/dist/features/homepage/index.js +0 -2
  203. package/dist/features/homepage/lib/section-renderer.d.ts +9 -0
  204. package/dist/features/homepage/lib/section-renderer.js +202 -58
  205. package/dist/features/homepage/repository/homepage-sections.repository.d.ts +35 -3
  206. package/dist/features/homepage/repository/homepage-sections.repository.js +58 -8
  207. package/dist/features/homepage/schemas/firestore.d.ts +106 -3
  208. package/dist/features/homepage/schemas/firestore.js +32 -2
  209. package/dist/features/homepage/schemas/index.d.ts +36 -7
  210. package/dist/features/homepage/schemas/index.js +39 -27
  211. package/dist/features/homepage/server.d.ts +0 -3
  212. package/dist/features/homepage/server.js +0 -3
  213. package/dist/features/homepage/types/index.d.ts +0 -47
  214. package/dist/features/homepage/types/index.js +49 -0
  215. package/dist/features/layout/AppLayoutShell.js +1 -1
  216. package/dist/features/layout/BottomNavItem.js +1 -1
  217. package/dist/features/layout/BottomSheet.js +1 -1
  218. package/dist/features/layout/TitleBarLayout.js +1 -1
  219. package/dist/features/live/components/LiveItemDetailPageView.js +4 -9
  220. package/dist/features/media/hooks/useMedia.js +17 -0
  221. package/dist/features/media/repository/media-assets.repository.d.ts +46 -0
  222. package/dist/features/media/repository/media-assets.repository.js +31 -0
  223. package/dist/features/media/trust-signals.d.ts +85 -0
  224. package/dist/features/media/trust-signals.js +126 -0
  225. package/dist/features/parts/components/PartDetailPageView.d.ts +14 -1
  226. package/dist/features/parts/components/PartDetailPageView.js +2 -2
  227. package/dist/features/parts/components/PartsIndexPageView.d.ts +11 -2
  228. package/dist/features/parts/components/PartsIndexPageView.js +2 -2
  229. package/dist/features/parts/components/PartsSearchBox.d.ts +9 -0
  230. package/dist/features/parts/components/PartsSearchBox.js +57 -0
  231. package/dist/features/parts/schemas/firestore.d.ts +149 -3
  232. package/dist/features/parts/schemas/firestore.js +184 -0
  233. package/dist/features/payments/repository/payout.repository.d.ts +15 -0
  234. package/dist/features/payments/repository/payout.repository.js +21 -0
  235. package/dist/features/pre-orders/components/PreOrderDetailPageView.js +4 -11
  236. package/dist/features/pre-orders/components/PreOrdersIndexListing.js +1 -1
  237. package/dist/features/price-index/components/PriceIndexToolView.d.ts +3 -1
  238. package/dist/features/price-index/components/PriceIndexToolView.js +92 -13
  239. package/dist/features/products/components/CompareOverlay.d.ts +18 -1
  240. package/dist/features/products/components/CompareOverlay.js +21 -2
  241. package/dist/features/products/components/CustomSectionTabContent.d.ts +31 -0
  242. package/dist/features/products/components/CustomSectionTabContent.js +29 -3
  243. package/dist/features/products/components/GroupMemberPicker.d.ts +19 -1
  244. package/dist/features/products/components/GroupMemberPicker.js +46 -8
  245. package/dist/features/products/components/GroupSettingsPanel.d.ts +11 -1
  246. package/dist/features/products/components/GroupSettingsPanel.js +91 -3
  247. package/dist/features/products/components/PrizeDrawDetailPageView.js +1 -1
  248. package/dist/features/products/components/PrizeDrawEntryActions.d.ts +15 -1
  249. package/dist/features/products/components/PrizeDrawEntryActions.js +70 -8
  250. package/dist/features/products/components/ProductDetailPageView.js +74 -15
  251. package/dist/features/products/components/ProductForm.d.ts +13 -1
  252. package/dist/features/products/components/ProductForm.js +16 -2
  253. package/dist/features/products/components/ProductsIndexListing.d.ts +16 -1
  254. package/dist/features/products/components/ProductsIndexListing.js +3 -3
  255. package/dist/features/products/components/ShowGroupSection.js +15 -2
  256. package/dist/features/products/components/SparesListView.d.ts +7 -0
  257. package/dist/features/products/components/SparesListView.js +39 -0
  258. package/dist/features/products/constants/action-defs.d.ts +1 -0
  259. package/dist/features/products/constants/action-defs.js +1 -0
  260. package/dist/features/products/repository/products.repository.d.ts +98 -0
  261. package/dist/features/products/repository/products.repository.js +235 -17
  262. package/dist/features/products/schemas/firestore.d.ts +97 -4
  263. package/dist/features/products/schemas/firestore.js +104 -0
  264. package/dist/features/products/schemas/index.d.ts +10 -19
  265. package/dist/features/products/schemas/index.js +23 -6
  266. package/dist/features/products/types/index.d.ts +45 -1
  267. package/dist/features/products/utils/group-member-image.d.ts +39 -0
  268. package/dist/features/products/utils/group-member-image.js +3 -0
  269. package/dist/features/products/utils/listing-badge-variant.js +4 -0
  270. package/dist/features/reviews/components/ReviewsList.js +1 -1
  271. package/dist/features/scams/components/ScamProfileView.js +1 -1
  272. package/dist/features/search/components/EntityTypeahead.d.ts +44 -0
  273. package/dist/features/search/components/EntityTypeahead.js +92 -0
  274. package/dist/features/search/components/Search.d.ts +12 -1
  275. package/dist/features/search/components/Search.js +47 -3
  276. package/dist/features/search/components/SearchFacetRail.d.ts +11 -0
  277. package/dist/features/search/components/SearchFacetRail.js +92 -0
  278. package/dist/features/search/components/SearchResultsClient.d.ts +33 -0
  279. package/dist/features/search/components/SearchResultsClient.js +103 -0
  280. package/dist/features/search/components/SearchResultsPageView.d.ts +15 -1
  281. package/dist/features/search/components/SearchResultsPageView.js +2 -2
  282. package/dist/features/search/components/index.d.ts +0 -6
  283. package/dist/features/search/components/index.js +0 -3
  284. package/dist/features/search/hooks/useNavSuggestions.d.ts +13 -0
  285. package/dist/features/search/hooks/useScopedSearch.d.ts +34 -0
  286. package/dist/features/search/hooks/useScopedSearch.js +114 -0
  287. package/dist/features/search/hooks/useSiteSearch.d.ts +26 -0
  288. package/dist/features/search/hooks/useSiteSearch.js +149 -0
  289. package/dist/features/search/index.d.ts +0 -4
  290. package/dist/features/search/index.js +0 -4
  291. package/dist/features/search/manifest.d.ts +12 -0
  292. package/dist/features/search/manifest.js +17 -2
  293. package/dist/features/search/server.d.ts +0 -3
  294. package/dist/features/search/server.js +0 -3
  295. package/dist/features/seller/components/SellerListingTypeView.d.ts +1 -1
  296. package/dist/features/seller/components/SellerListingTypeView.js +20 -0
  297. package/dist/features/seller/components/SellerPayoutSettingsView.js +35 -14
  298. package/dist/features/seller/components/SellerProductShell.d.ts +1 -1
  299. package/dist/features/seller/components/SellerProductShell.js +2 -1
  300. package/dist/features/seller/components/SellerSidebar.js +1 -1
  301. package/dist/features/seller/components/SellerSparesView.d.ts +17 -0
  302. package/dist/features/seller/components/SellerSparesView.js +6 -0
  303. package/dist/features/seller/components/index.d.ts +2 -0
  304. package/dist/features/seller/components/index.js +1 -0
  305. package/dist/features/shell/CommandPalette.js +1 -1
  306. package/dist/features/sticker-trace/schemas/firestore.d.ts +19 -0
  307. package/dist/features/stores/components/StoreDetailLayoutView.d.ts +1 -1
  308. package/dist/features/stores/components/StoreDetailLayoutView.js +7 -82
  309. package/dist/features/stores/components/StoreHeader.js +1 -2
  310. package/dist/features/stores/components/StoreSparesPageView.d.ts +27 -0
  311. package/dist/features/stores/components/StoreSparesPageView.js +48 -0
  312. package/dist/features/stores/schemas/firestore.d.ts +19 -0
  313. package/dist/features/tester/seed-data/authored/buying__spares-and-groups.d.ts +2 -0
  314. package/dist/features/tester/seed-data/authored/buying__spares-and-groups.js +96 -0
  315. package/dist/features/tester/seed-data/authored/content-discovery__authenticity-checker.d.ts +2 -0
  316. package/dist/features/tester/seed-data/authored/content-discovery__authenticity-checker.js +280 -45
  317. package/dist/features/tester/seed-data/authored/content-discovery__category-tabs-franchise-bundles.js +54 -32
  318. package/dist/features/tester/seed-data/authored/content-discovery__parts-database.js +3 -2
  319. package/dist/features/tester/seed-data/authored/content-discovery__price-index-and-compare.d.ts +2 -0
  320. package/dist/features/tester/seed-data/authored/content-discovery__price-index-and-compare.js +73 -0
  321. package/dist/features/tester/seed-data/authored/content-discovery__site-wide-index.d.ts +2 -0
  322. package/dist/features/tester/seed-data/authored/content-discovery__site-wide-index.js +340 -0
  323. package/dist/features/tester/seed-data/authored/index.js +16 -0
  324. package/dist/features/tester/seed-data/authored/search-and-nav__header-search.d.ts +1 -0
  325. package/dist/features/tester/seed-data/authored/search-and-nav__header-search.js +57 -0
  326. package/dist/features/tester/seed-data/authored/selling__spares-and-group-editing.d.ts +2 -0
  327. package/dist/features/tester/seed-data/authored/selling__spares-and-group-editing.js +77 -0
  328. package/dist/features/tester/seed-data/authored/tools__sticker-promote-to-listings.d.ts +2 -0
  329. package/dist/features/tester/seed-data/authored/tools__sticker-promote-to-listings.js +58 -0
  330. package/dist/features/tester/seed-data/authored/tools__sticker-tracer.js +5 -4
  331. package/dist/features/tester/seed-data/tester-checklist-seed-data.js +372 -18
  332. package/dist/index.d.ts +17 -57
  333. package/dist/index.js +35 -96
  334. package/dist/next/routing/route-map.d.ts +42 -1
  335. package/dist/next/routing/route-map.js +45 -0
  336. package/dist/providers/db-firebase/base.repository.d.ts +22 -0
  337. package/dist/providers/db-firebase/base.repository.js +36 -0
  338. package/dist/schemas/registry.d.ts +15 -6
  339. package/dist/security/blind-index-tokens.d.ts +54 -0
  340. package/dist/security/blind-index-tokens.js +152 -0
  341. package/dist/security/index.d.ts +1 -1
  342. package/dist/security/index.js +1 -1
  343. package/dist/security/settings-encryption.d.ts +35 -0
  344. package/dist/security/settings-encryption.js +91 -0
  345. package/dist/seed/_helpers/brand-rows.js +38 -14
  346. package/dist/seed/_helpers/category-forest.js +73 -7
  347. package/dist/seed/_helpers/category-images-generated.d.ts +25 -6
  348. package/dist/seed/_helpers/category-images-generated.js +81 -6
  349. package/dist/seed/_helpers/category-models.d.ts +1 -1
  350. package/dist/seed/_helpers/category-models.js +7 -1
  351. package/dist/seed/_helpers/category-parts-glossary-map.js +8 -0
  352. package/dist/seed/_helpers/category-parts.d.ts +1 -1
  353. package/dist/seed/_helpers/category-parts.js +50 -2
  354. package/dist/seed/_helpers/character-features.d.ts +1 -1
  355. package/dist/seed/_helpers/character-features.js +2 -1
  356. package/dist/seed/_helpers/franchise-rows.js +10 -2
  357. package/dist/seed/_helpers/media.d.ts +47 -0
  358. package/dist/seed/_helpers/media.js +34 -0
  359. package/dist/seed/authenticity-knowledge-seed-data.d.ts +49 -5
  360. package/dist/seed/authenticity-knowledge-seed-data.js +10001 -549
  361. package/dist/seed/categories-seed-data.js +124 -20
  362. package/dist/seed/grouped-listings-seed-data.js +16 -0
  363. package/dist/seed/homepage-sections-seed-data.js +94 -5
  364. package/dist/seed/index.d.ts +2 -3
  365. package/dist/seed/index.js +2 -2
  366. package/dist/seed/manifest.js +3 -0
  367. package/dist/seed/media-assets-seed-data.d.ts +1 -1
  368. package/dist/seed/media-assets-seed-data.js +746 -196
  369. package/dist/seed/model-composition-seed-data.js +122 -6
  370. package/dist/seed/part-glossary-seed-data.d.ts +1 -1
  371. package/dist/seed/part-glossary-seed-data.js +37219 -463
  372. package/dist/seed/products-art-seed-data.js +7 -18
  373. package/dist/seed/products-auctions-seed-data.js +56 -47
  374. package/dist/seed/products-beyblade-x-seed-data.d.ts +2 -0
  375. package/dist/seed/products-beyblade-x-seed-data.js +441 -0
  376. package/dist/seed/products-catalogue-seed-data.d.ts +5 -5
  377. package/dist/seed/products-catalogue-seed-data.js +1500 -1240
  378. package/dist/seed/products-classifieds-seed-data.js +9 -27
  379. package/dist/seed/products-digital-codes-seed-data.js +9 -18
  380. package/dist/seed/products-live-items-seed-data.js +5 -13
  381. package/dist/seed/products-preorders-seed-data.js +67 -33
  382. package/dist/seed/products-prize-draws-seed-data.js +8 -17
  383. package/dist/seed/products-spares-seed-data.d.ts +2 -0
  384. package/dist/seed/products-spares-seed-data.js +368 -0
  385. package/dist/seed/products-standard-seed-data.js +39 -126
  386. package/dist/seed/products-stickers-seed-data.js +59 -15
  387. package/dist/seed/site-settings-seed-data.js +8 -0
  388. package/dist/seo/json-ld.d.ts +25 -0
  389. package/dist/seo/json-ld.js +28 -0
  390. package/dist/server-entry.d.ts +3 -1
  391. package/dist/server-entry.js +2 -2
  392. package/dist/server.d.ts +9 -9
  393. package/dist/server.js +27 -25
  394. package/dist/styles.css +202 -1
  395. package/dist/tailwind-input.css +31 -17
  396. package/dist/tailwind-utilities.css +1 -1
  397. package/dist/ui/components/Accordion.js +1 -1
  398. package/dist/ui/components/Breadcrumb.js +1 -1
  399. package/dist/ui/components/DetailPageGallery.js +0 -2
  400. package/dist/ui/components/DetailPageHero.js +0 -1
  401. package/dist/ui/components/DetailPageTabs.js +1 -1
  402. package/dist/ui/components/Divider.js +3 -3
  403. package/dist/ui/components/GatedPrice.js +5 -1
  404. package/dist/ui/components/HorizontalScroller.js +1 -1
  405. package/dist/ui/components/ListingLayout.js +3 -3
  406. package/dist/ui/components/MediaTabs.style.css +81 -0
  407. package/dist/ui/components/Pagination.js +1 -1
  408. package/dist/ui/components/PendingValue.d.ts +45 -0
  409. package/dist/ui/components/PendingValue.js +7 -0
  410. package/dist/ui/components/Radio.style.css +119 -0
  411. package/dist/ui/components/RatingDisplay.js +2 -2
  412. package/dist/ui/components/SettingsSection.js +0 -2
  413. package/dist/ui/components/SiteLogo.js +3 -0
  414. package/dist/ui/components/SiteMark.js +3 -0
  415. package/dist/ui/components/SortDropdown.js +1 -1
  416. package/dist/ui/components/SpecTable.d.ts +58 -0
  417. package/dist/ui/components/SpecTable.js +8 -0
  418. package/dist/ui/components/StarRating.js +1 -3
  419. package/dist/ui/components/SummaryCard.js +1 -1
  420. package/dist/ui/components/index.style.css +1 -0
  421. package/dist/ui/icons/icon-registry.d.ts +1 -0
  422. package/dist/ui/icons/icon-registry.js +9 -1
  423. package/dist/ui/index.d.ts +4 -0
  424. package/dist/ui/index.js +2 -0
  425. package/dist/utils/search-txt-query.d.ts +12 -0
  426. package/dist/utils/search-txt-query.js +12 -0
  427. package/package.json +1 -1
  428. package/scripts/dedupe-peer-deps.mjs +18 -0
  429. package/scripts/seed-cli.mjs +135 -26
  430. package/scripts/verify-css-build.mjs +118 -37
  431. package/dist/_internal/server/features/search/actions.d.ts +0 -7
  432. package/dist/_internal/server/features/search/actions.js +0 -21
  433. package/dist/_internal/server/features/search/data.d.ts +0 -15
  434. package/dist/_internal/server/features/search/data.js +0 -18
  435. package/dist/_internal/server/features/search/index.d.ts +0 -3
  436. package/dist/_internal/server/features/search/index.js +0 -2
  437. package/dist/features/homepage/api/[id]/route.d.ts +0 -23
  438. package/dist/features/homepage/api/[id]/route.js +0 -103
  439. package/dist/features/homepage/api/route.d.ts +0 -14
  440. package/dist/features/homepage/api/route.js +0 -140
  441. package/dist/features/homepage/api/sections/[id]/route.d.ts +0 -26
  442. package/dist/features/homepage/api/sections/[id]/route.js +0 -95
  443. package/dist/features/homepage/api/sections/route.d.ts +0 -13
  444. package/dist/features/homepage/api/sections/route.js +0 -82
  445. package/dist/features/homepage/components/AdvertisementBanner.d.ts +0 -14
  446. package/dist/features/homepage/components/AdvertisementBanner.js +0 -35
  447. package/dist/features/homepage/components/BeforeAfterCard.d.ts +0 -9
  448. package/dist/features/homepage/components/BeforeAfterCard.js +0 -73
  449. package/dist/features/homepage/components/FeaturedResultsSection.d.ts +0 -17
  450. package/dist/features/homepage/components/FeaturedResultsSection.js +0 -17
  451. package/dist/features/homepage/components/HeroBanner.d.ts +0 -7
  452. package/dist/features/homepage/components/HeroBanner.js +0 -43
  453. package/dist/features/homepage/components/HeroSection.d.ts +0 -7
  454. package/dist/features/homepage/components/HeroSection.js +0 -6
  455. package/dist/features/homepage/components/HomepageSkeleton.d.ts +0 -2
  456. package/dist/features/homepage/components/HomepageSkeleton.js +0 -21
  457. package/dist/features/homepage/components/HomepageView.d.ts +0 -25
  458. package/dist/features/homepage/components/HomepageView.js +0 -5
  459. package/dist/features/homepage/components/NewsletterBanner.d.ts +0 -23
  460. package/dist/features/homepage/components/NewsletterBanner.js +0 -37
  461. package/dist/features/homepage/components/SiteFeaturesSection.d.ts +0 -14
  462. package/dist/features/homepage/components/SiteFeaturesSection.js +0 -11
  463. package/dist/features/homepage/components/TrustBadges.d.ts +0 -6
  464. package/dist/features/homepage/components/TrustBadges.js +0 -46
  465. package/dist/features/homepage/components/TrustIndicatorsSection.d.ts +0 -12
  466. package/dist/features/homepage/components/TrustIndicatorsSection.js +0 -11
  467. package/dist/features/homepage/hooks/useHomepage.d.ts +0 -11
  468. package/dist/features/homepage/hooks/useHomepage.js +0 -17
  469. package/dist/features/homepage/hooks/useHomepageSections.d.ts +0 -2
  470. package/dist/features/homepage/hooks/useHomepageSections.js +0 -10
  471. package/dist/features/homepage/repository/homepage.repository.d.ts +0 -11
  472. package/dist/features/homepage/repository/homepage.repository.js +0 -25
  473. package/dist/features/search/actions/index.d.ts +0 -1
  474. package/dist/features/search/actions/index.js +0 -1
  475. package/dist/features/search/actions/search-actions.d.ts +0 -18
  476. package/dist/features/search/actions/search-actions.js +0 -43
  477. package/dist/features/search/api/route.d.ts +0 -22
  478. package/dist/features/search/api/route.js +0 -128
  479. package/dist/features/search/columns/index.d.ts +0 -17
  480. package/dist/features/search/columns/index.js +0 -49
  481. package/dist/features/search/components/SearchFiltersRow.d.ts +0 -22
  482. package/dist/features/search/components/SearchFiltersRow.js +0 -21
  483. package/dist/features/search/components/SearchResultsSection.d.ts +0 -53
  484. package/dist/features/search/components/SearchResultsSection.js +0 -35
  485. package/dist/features/search/components/SearchView.d.ts +0 -27
  486. package/dist/features/search/components/SearchView.js +0 -8
  487. package/dist/features/search/hooks/useSearch.d.ts +0 -21
  488. package/dist/features/search/hooks/useSearch.js +0 -33
  489. package/dist/features/search/repository/search.repository.d.ts +0 -14
  490. package/dist/features/search/repository/search.repository.js +0 -59
  491. package/dist/features/search/schemas/index.d.ts +0 -70
  492. package/dist/features/search/schemas/index.js +0 -36
  493. package/dist/features/search/types/index.d.ts +0 -48
  494. package/dist/features/stores/components/StoreScopedSearch.d.ts +0 -17
  495. package/dist/features/stores/components/StoreScopedSearch.js +0 -27
  496. package/dist/seed/defaults/homepage-sections.d.ts +0 -2
  497. package/dist/seed/defaults/homepage-sections.js +0 -60
  498. package/dist/seed/factories/homepage-section.factory.d.ts +0 -10
  499. package/dist/seed/factories/homepage-section.factory.js +0 -13
  500. /package/dist/{features/search/types/index.js → _internal/shared/listing-types/spares/og.d.ts} +0 -0
@@ -1,6 +1,6 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
- import { useCallback, useState } from "react";
3
+ import React, { useCallback, useState } from "react";
4
4
  import { useQuery } from "@tanstack/react-query";
5
5
  import { Search, TrendingUp } from "lucide-react";
6
6
  import { Container, Div, Heading, Row, Section, Span, Stack, Text } from "../../../ui/index.js";
@@ -11,6 +11,7 @@ import { FieldInput } from "../../../ui/forms/FieldInput.js";
11
11
  import { MediaImage } from "../../media/MediaImage.js";
12
12
  import { TOOL_ENDPOINTS } from "../../../constants/api-endpoints.js";
13
13
  import { apiClient } from "../../../http/ApiClient.js";
14
+ import { useSiteSearch } from "../../search/hooks/useSiteSearch.js";
14
15
  import { formatCurrency } from "../../../utils/number.formatter.js";
15
16
  import { normalizeError } from "../../../errors/normalize.js";
16
17
  import { toUserMessage } from "../../../errors/error-display-map.js";
@@ -87,7 +88,14 @@ function ResultCard({ row, threshold }) {
87
88
  */
88
89
  _jsx(Text, { size: "sm", color: "muted", children: "No completed sales recorded here yet. Prices appear once something sells on LetItRip." }))] }));
89
90
  }
90
- export function PriceIndexToolView({ initialQuery = "", initialData = null }) {
91
+ /** Stable identity — a `useMemo` dependency inside `useSiteSearch`. */
92
+ const identityLabel = (typeKey) => typeKey;
93
+ /** `/categories/{slug}` -> `{slug}`. Category documents are pure slugs (id === slug). */
94
+ function categoryIdFromHref(href) {
95
+ const m = /^\/categories\/([^/?#]+)/.exec(href);
96
+ return m ? m[1] : null;
97
+ }
98
+ export function PriceIndexToolView({ initialQuery = "", initialData = null, searchIndexEnabled, }) {
91
99
  const [draft, setDraft] = useState(initialQuery);
92
100
  /**
93
101
  * The SUBMITTED term, distinct from `draft`.
@@ -100,26 +108,97 @@ export function PriceIndexToolView({ initialQuery = "", initialData = null }) {
100
108
  */
101
109
  const [term, setTerm] = useState(initialQuery.trim());
102
110
  /*
103
- * `useQuery` + `apiClient`, not a hand-rolled `fetch` with its own loading
104
- * and error state — which is what this had, and what `audit-form-mutation-hook`
105
- * correctly rejected. It also deletes three `useState`s: React Query already
106
- * owns in-flight, error and the response, and it dedupes the identical lookup
107
- * rather than issuing it twice.
111
+ * 🛑 TIER 1 ADOPTION. `searchPriceIndex` (the fallback below) pays a
112
+ * `searchTxt` push-down query to turn `term` into category leaves —
113
+ * exactly the resolution `useSiteSearch` already does in the browser, at
114
+ * zero reads, for every other search surface on the site. Resolving here
115
+ * removes that query from the HOT path; the server is left with only the
116
+ * one multi-get it cannot avoid.
117
+ *
118
+ * `active` mirrors `term`'s own gate (2+ chars) rather than firing on
119
+ * mount — consistent with every other `useSiteSearch` caller, which loads
120
+ * the ~420 KB artifact only once intent is shown.
121
+ */
122
+ const local = useSiteSearch(term, {
123
+ active: term.length >= 2,
124
+ indexEnabled: searchIndexEnabled,
125
+ types: ["model", "category"],
126
+ label: identityLabel,
127
+ });
128
+ const localIds = React.useMemo(() => {
129
+ if (!local.result)
130
+ return [];
131
+ const ids = new Set();
132
+ for (const group of local.result.groups) {
133
+ for (const hit of group.hits) {
134
+ const id = categoryIdFromHref(hit.href);
135
+ if (id)
136
+ ids.add(id);
137
+ }
138
+ }
139
+ return [...ids];
140
+ }, [local.result]);
141
+ const localReady = local.status === "ready";
142
+ /*
143
+ * The bands-only request. Enabled ONLY once local resolution has actually
144
+ * produced something — an empty `localIds` with the gate open would be a
145
+ * request shaped `?ids=`, which the route already treats as "no results"
146
+ * but which is still a request worth not making.
147
+ */
148
+ const bandsQuery = useQuery({
149
+ queryKey: ["tool", "price-index", "bands", localIds],
150
+ queryFn: () => apiClient.get(TOOL_ENDPOINTS.PRICE_INDEX_BANDS(localIds)),
151
+ enabled: localReady && localIds.length > 0,
152
+ staleTime: Infinity,
153
+ refetchOnWindowFocus: false,
154
+ });
155
+ /*
156
+ * The FALLBACK. Fires only when the local index is off, failed, or
157
+ * (rarely) resolved to nothing — never alongside the local path, so a
158
+ * working index never pays for both. Same cache posture as it always had.
108
159
  */
109
- const query = useQuery({
160
+ const fallbackEnabled = term.length >= 2 && (local.status === "failed" || local.status === "disabled" ||
161
+ (localReady && localIds.length === 0));
162
+ const fallbackQuery = useQuery({
110
163
  queryKey: ["tool", "price-index", term],
111
164
  queryFn: () => apiClient.get(TOOL_ENDPOINTS.PRICE_INDEX_SEARCH(term)),
112
- enabled: term.length >= 2,
113
- /*
114
- * The endpoint is edge-cached for an hour over a day-stale aggregate, so
115
- * refetching on focus or remount would spend invocations to learn nothing.
116
- */
165
+ enabled: fallbackEnabled,
117
166
  staleTime: Infinity,
118
167
  refetchOnWindowFocus: false,
119
168
  ...(initialData && initialData.query === initialQuery.trim()
120
169
  ? { initialData }
121
170
  : {}),
122
171
  });
172
+ /*
173
+ * One shape out, regardless of which path answered. `truncated` is always
174
+ * `false` on the local path — `PRICE_INDEX_SEARCH_LIMIT` already bounded
175
+ * how many ids were resolved, so there is nothing left uncounted the way
176
+ * the query-based path's search step could leave behind.
177
+ */
178
+ const query = localReady && localIds.length > 0
179
+ ? {
180
+ data: bandsQuery.data
181
+ ? {
182
+ query: term,
183
+ results: bandsQuery.data.results,
184
+ truncated: false,
185
+ /*
186
+ * The confidence threshold is a server-side constant
187
+ * (`PRICE_INDEX_MIN_CONFIDENT_SAMPLES = 3`), not something
188
+ * resolved per-request — the bands endpoint does not echo it
189
+ * back because it is already baked into each band's own
190
+ * `confident` flag. Hardcoded to match the renderer's own
191
+ * existing fallback below (`data?.minConfidentSamples ?? 3`),
192
+ * so the two paths show the same explanatory copy.
193
+ */
194
+ minConfidentSamples: 3,
195
+ }
196
+ : undefined,
197
+ isFetching: bandsQuery.isFetching,
198
+ isError: bandsQuery.isError,
199
+ error: bandsQuery.error,
200
+ }
201
+ : fallbackQuery;
123
202
  const run = useCallback((q) => setTerm(q.trim()), []);
124
203
  const data = query.data ?? null;
125
204
  const loading = query.isFetching;
@@ -60,6 +60,8 @@ export interface CompareOverlayLabels {
60
60
  close?: string;
61
61
  remove?: string;
62
62
  view?: string;
63
+ /** Placeholder/aria-label for the "add another" typeahead. Only rendered when `onAdd` is passed. */
64
+ addAnother?: string;
63
65
  field?: Partial<CompareFieldLabels>;
64
66
  }
65
67
  export interface CompareOverlayProps {
@@ -73,7 +75,22 @@ export interface CompareOverlayProps {
73
75
  onClose: () => void;
74
76
  /** Called when a column's Remove button is clicked. */
75
77
  onRemove?: (id: string) => void;
78
+ /**
79
+ * Called with a resolved slug when the user picks a result from the
80
+ * "Add another" typeahead. Omit to leave the overlay exactly as it was —
81
+ * every existing caller keeps working unchanged.
82
+ *
83
+ * 🛑 Resolution only, no fetch. The overlay already knows how to turn an
84
+ * id into a full `CompareProductLike` via `productIds` (`useFetchedProducts`
85
+ * fetches `/api/products?ids=…`); this hands back ONE more id for that same
86
+ * mechanism, rather than teaching the overlay a second way to load a
87
+ * product. The caller decides whether to append to its own `productIds`
88
+ * state or ignore it.
89
+ */
90
+ onAdd?: (id: string) => void;
91
+ /** `siteSettings.search?.indexEnabled`. Threaded through to the typeahead. */
92
+ searchIndexEnabled?: boolean;
76
93
  labels?: CompareOverlayLabels;
77
94
  }
78
- export declare function CompareOverlay({ isOpen, items: passedItems, productIds, productType, onClose, onRemove, labels: labelOverrides, }: CompareOverlayProps): import("react").JSX.Element | null;
95
+ export declare function CompareOverlay({ isOpen, items: passedItems, productIds, productType, onClose, onRemove, onAdd, searchIndexEnabled, labels: labelOverrides, }: CompareOverlayProps): import("react").JSX.Element | null;
79
96
  export {};
@@ -20,12 +20,14 @@ import { COMPARE_MAX_ITEMS } from "../constants/action-defs.js";
20
20
  import { normalizeListingType } from "../utils/listing-type.js";
21
21
  import { pluginFor } from "../../../_internal/shared/listing-types/_registry.js";
22
22
  import { PRODUCT_ENDPOINTS } from "../../../constants/api-endpoints.js";
23
+ import { EntityTypeahead } from "../../search/components/EntityTypeahead.js";
23
24
  const DEFAULT_LABELS = {
24
25
  title: "Compare items",
25
26
  empty: "Select up to 4 items from the listing to compare.",
26
27
  close: "Close",
27
28
  remove: "Remove",
28
29
  view: "View",
30
+ addAnother: "Add another to compare",
29
31
  field: {
30
32
  image: "Photo",
31
33
  price: "Price",
@@ -35,6 +37,12 @@ const DEFAULT_LABELS = {
35
37
  store: "Store",
36
38
  },
37
39
  };
40
+ /** Index type keys for each `productType` discriminator the overlay accepts. */
41
+ const TYPEAHEAD_TYPES = {
42
+ product: ["standard"],
43
+ auction: ["auction"],
44
+ preorder: ["pre-order"],
45
+ };
38
46
  const OVERLAY_CLASS = "fixed inset-0 flex flex-col bg-[var(--appkit-color-bg)] overflow-hidden z-[var(--appkit-z-modal,60)]";
39
47
  const HEADER_CLASS = "flex items-center justify-between gap-[var(--appkit-space-3)] border-b border-[var(--appkit-color-border-subtle)] px-[var(--appkit-space-4)] py-[var(--appkit-space-3)]";
40
48
  const FIELD_LABEL_CLASS = "text-[length:var(--appkit-text-xs)] font-medium uppercase tracking-wider text-[var(--appkit-color-text-muted)]";
@@ -108,7 +116,7 @@ function useFetchedProducts(productIds, passedItems, isOpen) {
108
116
  isLoading: !passedItems && isLoading,
109
117
  };
110
118
  }
111
- export function CompareOverlay({ isOpen, items: passedItems, productIds, productType = "product", onClose, onRemove, labels: labelOverrides, }) {
119
+ export function CompareOverlay({ isOpen, items: passedItems, productIds, productType = "product", onClose, onRemove, onAdd, searchIndexEnabled, labels: labelOverrides, }) {
112
120
  const labels = useMemo(() => ({
113
121
  ...DEFAULT_LABELS,
114
122
  ...labelOverrides,
@@ -167,7 +175,18 @@ export function CompareOverlay({ isOpen, items: passedItems, productIds, product
167
175
  }, [onRemove]);
168
176
  if (!isOpen)
169
177
  return null;
170
- return (_jsxs(Div, { className: OVERLAY_CLASS, role: "dialog", "aria-modal": "true", "aria-label": labels.title, children: [_jsxs(Row, { justify: "between", align: "center", className: HEADER_CLASS, children: [_jsx(Heading, { level: 2, size: "base", weight: "semibold", children: labels.title }), _jsx(IconButton, { type: "button", onClick: onClose, "aria-label": labels.close, variant: "ghost", rounded: "full", className: "text-[var(--appkit-color-text-muted)] hover:bg-[var(--appkit-color-surface-elevated)]", children: _jsx(X, { className: "h-5 w-5" }) })] }), _jsx(Div, { className: `flex-1 ${__O.auto} ${__P.p4}`, children: isLoading ? (
178
+ return (_jsxs(Div, { className: OVERLAY_CLASS, role: "dialog", "aria-modal": "true", "aria-label": labels.title, children: [_jsxs(Row, { justify: "between", align: "center", className: HEADER_CLASS, children: [_jsx(Heading, { level: 2, size: "base", weight: "semibold", children: labels.title }), _jsx(IconButton, { type: "button", onClick: onClose, "aria-label": labels.close, variant: "ghost", rounded: "full", className: "text-[var(--appkit-color-text-muted)] hover:bg-[var(--appkit-color-surface-elevated)]", children: _jsx(X, { className: "h-5 w-5" }) })] }), onAdd && trimmed.length < COMPARE_MAX_ITEMS && (_jsx(Div, { className: `${__P.p4} border-b border-[var(--appkit-color-border-subtle)]`, children: _jsx(EntityTypeahead, { types: TYPEAHEAD_TYPES[productType], indexEnabled: searchIndexEnabled, label: labels.addAnother, placeholder: labels.addAnother, limit: 5, onPick: (pick) => {
179
+ /*
180
+ * The LAST path segment, not a type-specific pattern. The
181
+ * three listing types this overlay accepts have three
182
+ * different detail routes (/products, /auctions,
183
+ * /pre-orders), and every one of them ends in the slug — the
184
+ * same thing `productIds` already expects.
185
+ */
186
+ const slug = pick.href.split("/").filter(Boolean).pop();
187
+ if (slug)
188
+ onAdd(slug);
189
+ } }) })), _jsx(Div, { className: `flex-1 ${__O.auto} ${__P.p4}`, children: isLoading ? (
171
190
  // audit-content-alignment-ok: loading-state text inside a transient overlay panel, not marketing content
172
191
  _jsx(Text, { paddingY: "3xl", variant: "secondary", align: "center", children: "Loading\u00E2\u20AC\u00A6" })) : trimmed.length === 0 ? (
173
192
  // audit-content-alignment-ok: empty-state inside a transient overlay panel, not marketing content
@@ -1,5 +1,36 @@
1
1
  import React from "react";
2
2
  import type { CustomSection } from "../schemas/firestore";
3
+ /**
4
+ * Shape `ProductTabsShell` expects for `customTabs`. Duplicated here rather
5
+ * than imported from `ProductTabsShell.tsx` — that file is `"use client"` and
6
+ * this one is a plain function, so importing its type keeps this module
7
+ * server-renderable. The two must stay structurally compatible; they already
8
+ * do (`{ id, label, content }`).
9
+ */
10
+ interface CustomSectionTab {
11
+ id: string;
12
+ label: string;
13
+ content: React.ReactNode;
14
+ }
15
+ /**
16
+ * `customSections` → `ProductTabsShell`'s `customTabs` prop, in one place.
17
+ *
18
+ * Six detail views (`ProductDetailPageView`, `AuctionDetailPageView`,
19
+ * `PreOrderDetailPageView`, `ClassifiedDetailPageView`,
20
+ * `DigitalCodeDetailPageView`, `LiveItemDetailPageView`) had this EXACT
21
+ * five-line block — the same `Array.isArray` guard and the same `.map` —
22
+ * copy-pasted six times. Rule-of-Three material by the Duplication Decision
23
+ * Framework (same prop surface, same shape, a bug fix would have needed six
24
+ * commits), consolidated here rather than left as six places that could
25
+ * silently drift the next time one of them is touched and the other five are
26
+ * not.
27
+ *
28
+ * Takes the raw, possibly-untyped `customSections` field directly (every call
29
+ * site reads it off a cast Firestore document) so callers do one line instead
30
+ * of two: `customTabs={toCustomSectionTabs(p.customSections)}`.
31
+ */
32
+ export declare function toCustomSectionTabs(input: unknown): CustomSectionTab[];
3
33
  export declare function CustomSectionTabContent({ section }: {
4
34
  section: CustomSection;
5
35
  }): React.JSX.Element;
36
+ export {};
@@ -1,14 +1,40 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { Row } from "@mohasinac/appkit/ui";
3
- import { Anchor, Dd, Dl, Dt, RichText, Stack, Text } from "../../../ui/index.js";
2
+ import { RichText, SpecTable, Stack, Text } from "../../../ui/index.js";
4
3
  import { normalizeRichTextHtml } from "../../../utils/string.formatter.js";
5
4
  function renderFieldValue(f) {
6
5
  if (f.type === "boolean")
7
6
  return f.value === "true" ? "Yes" : "No";
8
7
  return f.value + (f.unit ? ` ${f.unit}` : "");
9
8
  }
9
+ /**
10
+ * `customSections` → `ProductTabsShell`'s `customTabs` prop, in one place.
11
+ *
12
+ * Six detail views (`ProductDetailPageView`, `AuctionDetailPageView`,
13
+ * `PreOrderDetailPageView`, `ClassifiedDetailPageView`,
14
+ * `DigitalCodeDetailPageView`, `LiveItemDetailPageView`) had this EXACT
15
+ * five-line block — the same `Array.isArray` guard and the same `.map` —
16
+ * copy-pasted six times. Rule-of-Three material by the Duplication Decision
17
+ * Framework (same prop surface, same shape, a bug fix would have needed six
18
+ * commits), consolidated here rather than left as six places that could
19
+ * silently drift the next time one of them is touched and the other five are
20
+ * not.
21
+ *
22
+ * Takes the raw, possibly-untyped `customSections` field directly (every call
23
+ * site reads it off a cast Firestore document) so callers do one line instead
24
+ * of two: `customTabs={toCustomSectionTabs(p.customSections)}`.
25
+ */
26
+ export function toCustomSectionTabs(input) {
27
+ const sections = Array.isArray(input)
28
+ ? input
29
+ : [];
30
+ return sections.map((s) => ({
31
+ id: s.id,
32
+ label: s.title,
33
+ content: _jsx(CustomSectionTabContent, { section: s }),
34
+ }));
35
+ }
10
36
  export function CustomSectionTabContent({ section }) {
11
37
  const html = section.text ? normalizeRichTextHtml(section.text) : null;
12
38
  const fields = section.fields?.filter((f) => f.key && f.value) ?? [];
13
- return (_jsxs(Stack, { gap: "md", children: [html && (_jsx(RichText, { html: html, proseClass: "prose prose-sm sm:prose max-w-none dark:prose-invert", className: "text-[var(--appkit-color-text-muted)]" })), fields.length > 0 && (_jsx(Dl, { divide: "subtle", rounded: "xl", border: "subtle", className: "overflow-hidden", children: fields.map((f, i) => (_jsxs(Row, { gap: "md", oddEven: "zebra", surface: "default", padding: "inline", children: [_jsx(Dt, { weight: "medium", className: "w-36 flex-shrink-0 capitalize", color: "primary", children: f.key }), _jsx(Dd, { className: "flex-1", color: "muted", children: f.type === "url" ? (_jsx(Anchor, { href: f.value, tone: "none", underline: "hover", className: "text-primary-600 dark:text-primary-400 break-all", children: f.value })) : (renderFieldValue(f)) })] }, i))) })), !html && fields.length === 0 && (_jsx(Text, { size: "sm", color: "faint", children: "No content in this section." }))] }));
39
+ return (_jsxs(Stack, { gap: "md", children: [html && (_jsx(RichText, { html: html, proseClass: "prose prose-sm sm:prose max-w-none dark:prose-invert", className: "text-[var(--appkit-color-text-muted)]" })), fields.length > 0 && (_jsx(SpecTable, { rows: fields.map((f) => ({ name: f.key, value: String(f.value), unit: f.unit })) })), !html && fields.length === 0 && (_jsx(Text, { size: "sm", color: "faint", children: "No content in this section." }))] }));
14
40
  }
@@ -16,6 +16,7 @@
16
16
  * consumer pages untouched.
17
17
  */
18
18
  import React from "react";
19
+ import { type GroupRules } from "../../../_internal/shared/features/products/group-rules";
19
20
  import type { ListingType } from "../types";
20
21
  export interface GroupPickerMember {
21
22
  id: string;
@@ -29,6 +30,14 @@ export interface GroupPickerMember {
29
30
  isGroupParent?: boolean;
30
31
  condition?: string;
31
32
  availableQuantity?: number;
33
+ /**
34
+ * Printed or built when ordered — no stock ceiling.
35
+ *
36
+ * 🛑 Both the "Out of stock" refusal and the stepper's `max` read
37
+ * `availableQuantity`, so without this a print-on-demand member at quantity 0
38
+ * renders as unselectable while being perfectly buyable.
39
+ */
40
+ madeToOrder?: boolean;
32
41
  storeId?: string;
33
42
  storeName?: string;
34
43
  isSold?: boolean;
@@ -38,9 +47,18 @@ export interface GroupMemberPickerProps {
38
47
  groupId: string;
39
48
  groupSource: "product-group" | "grouped-listing";
40
49
  members: GroupPickerMember[];
50
+ /**
51
+ * The group's own selection rules, from whichever home owns them.
52
+ *
53
+ * 🛑 Absent means UNRESTRICTED, which is what every group that exists today
54
+ * is. The server enforces the same rules through `checkGroupRules` whatever
55
+ * is passed here, so omitting them costs correctness nothing — the buyer just
56
+ * finds out on submit instead of while choosing.
57
+ */
58
+ rules?: GroupRules;
41
59
  /** Called after a successful add so the host can close its overlay. */
42
60
  onAdded?: () => void;
43
61
  /** Called when the API reports the caller isn't signed in. */
44
62
  onAuthRequired?: () => void;
45
63
  }
46
- export declare function GroupMemberPicker({ groupId, groupSource, members, onAdded, onAuthRequired, }: GroupMemberPickerProps): React.JSX.Element;
64
+ export declare function GroupMemberPicker({ groupId, groupSource, members, rules, onAdded, onAuthRequired, }: GroupMemberPickerProps): React.JSX.Element;
@@ -27,10 +27,10 @@ import { CART_ENDPOINTS } from "../../../constants/api-endpoints.js";
27
27
  import { normalizeError } from "../../../errors/normalize.js";
28
28
  import { pluginFor } from "../../../_internal/shared/listing-types/_registry.js";
29
29
  import { normalizeListingType } from "../utils/listing-type.js";
30
+ import { resolveGroupMemberImage } from "../utils/group-member-image.js";
31
+ import { checkGroupRules, describeGroupRules, EMPTY_GROUP_RULES, } from "../../../_internal/shared/features/products/group-rules.js";
30
32
  const __O = { xAuto: "overflow-x-auto", hidden: "overflow-hidden" };
31
- function memberImage(m) {
32
- return m.image ?? m.images?.[0] ?? "";
33
- }
33
+ const memberImage = resolveGroupMemberImage;
34
34
  function memberHref(m) {
35
35
  return pluginFor(normalizeListingType(m)).detailRoute(m.slug ?? m.id);
36
36
  }
@@ -40,7 +40,10 @@ function blockedReason(m) {
40
40
  return "Sold";
41
41
  if (m.status && m.status !== "published")
42
42
  return "Unavailable";
43
- if ((m.availableQuantity ?? 0) <= 0)
43
+ // Made to order has no stock to be out of — checked BEFORE the quantity
44
+ // test, and after `isSold`, which stays the seller's kill switch whatever
45
+ // the production model.
46
+ if (m.madeToOrder !== true && (m.availableQuantity ?? 0) <= 0)
44
47
  return "Out of stock";
45
48
  // `cartLine: "blocked"` is how a classified / live listing says "contact the
46
49
  // seller instead" — those can never join a cart line.
@@ -48,7 +51,7 @@ function blockedReason(m) {
48
51
  return "Not purchasable here";
49
52
  return null;
50
53
  }
51
- export function GroupMemberPicker({ groupId, groupSource, members, onAdded, onAuthRequired, }) {
54
+ export function GroupMemberPicker({ groupId, groupSource, members, rules = EMPTY_GROUP_RULES, onAdded, onAuthRequired, }) {
52
55
  const [qty, setQty] = useState({});
53
56
  const [isSaving, setIsSaving] = useState(false);
54
57
  const [error, setError] = useState(null);
@@ -64,6 +67,32 @@ export function GroupMemberPicker({ groupId, groupSource, members, onAdded, onAu
64
67
  const selectedTotal = selected.reduce((sum, s) => sum + s.member.price * s.quantity, 0);
65
68
  const selectedUnits = selected.reduce((sum, s) => sum + s.quantity, 0);
66
69
  const currency = members[0]?.currency ?? "INR";
70
+ /*
71
+ * The SAME predicate the server applies, so the button's enabled state and
72
+ * the API's answer cannot disagree about what a valid selection is. A
73
+ * server-only rule reads as a broken button; a client-only one is not a rule.
74
+ */
75
+ const violations = useMemo(() => checkGroupRules(rules, selected.map((s) => ({
76
+ productId: s.member.id,
77
+ quantity: s.quantity,
78
+ title: s.member.title,
79
+ }))), [rules, selected]);
80
+ /*
81
+ * The discount, shown but NOT the number charged.
82
+ *
83
+ * 🛑 This is display arithmetic over a quoted percentage; the authoritative
84
+ * price is computed server-side inside `deriveLinePrice`, the single writer
85
+ * of a grouped line's `price`. Showing it here is honest (it is the figure
86
+ * the buyer is being offered) and computing it here is unavoidable (there is
87
+ * no cart line yet) — but it must never become a second money rule. The 2dp
88
+ * rounding deliberately matches `deriveLinePrice`'s so the two agree to the
89
+ * paisa rather than drifting by a rounding step.
90
+ */
91
+ const discountPercent = Math.min(100, Math.max(0, rules.groupDiscountPercent ?? 0));
92
+ const discountedTotal = discountPercent > 0
93
+ ? Math.round(selectedTotal * (1 - discountPercent / 100) * 100) / 100
94
+ : selectedTotal;
95
+ const ruleNotes = useMemo(() => describeGroupRules(rules), [rules]);
67
96
  const handleAdd = async () => {
68
97
  setError(null);
69
98
  setIsSaving(true);
@@ -101,8 +130,17 @@ export function GroupMemberPicker({ groupId, groupSource, members, onAdded, onAu
101
130
  };
102
131
  return (_jsxs(Stack, { gap: "sm", children: [isCrossStore && (_jsx(Alert, { variant: "info", children: "These items are sold by different sellers, so they can't be combined into one cart line. Open an item to buy it on its own." })), _jsx(Div, { className: __O.xAuto, children: _jsxs(Table, { className: "text-left min-w-[440px]", children: [_jsx(Thead, { children: _jsxs(Tr, { border: "default", children: [_jsx(Th, { paddingY: "sm", paddingSide: "pr-sm", color: "muted", size: "xs", weight: "semibold", children: "Image" }), _jsx(Th, { paddingY: "sm", paddingSide: "pr-sm", color: "muted", size: "xs", weight: "semibold", children: "Name" }), _jsx(Th, { paddingY: "sm", paddingSide: "pr-sm", color: "muted", size: "xs", weight: "semibold", children: "Price" }), !isCrossStore && (_jsx(Th, { paddingY: "sm", paddingSide: "pr-sm", color: "muted", size: "xs", weight: "semibold", children: "Qty" })), _jsx(Th, { paddingY: "sm", paddingSide: "pb-sm", color: "muted", size: "xs", weight: "semibold" })] }) }), _jsx(Tbody, { children: members.map((m) => {
103
132
  const blocked = blockedReason(m);
104
- return (_jsxs(Tr, { className: "last:border-0", border: "subtle", children: [_jsx(Td, { paddingSide: "pr-sm", padding: "xs-tall", children: _jsx(Div, { className: `w-10 h-10 ${__O.hidden}`, rounded: "full", border: "default", children: _jsx(MediaImage, { src: memberImage(m), alt: m.title, size: "thumbnail" }) }) }), _jsxs(Td, { paddingSide: "pr-sm", padding: "xs-tall", children: [_jsx(Text, { className: "line-clamp-2", color: "primary", size: "sm", weight: "medium", children: m.title }), m.isGroupParent && (_jsx(Span, { weight: "semibold", className: "text-[10px] text-[var(--appkit-color-primary)]", children: "Parent" }))] }), _jsx(Td, { paddingSide: "pr-sm", padding: "xs-tall", children: _jsx(Text, { size: "sm", color: "muted", children: _jsx(GatedPrice, { children: formatCurrency(m.price, m.currency ?? currency) }) }) }), !isCrossStore && (_jsx(Td, { paddingSide: "pr-sm", padding: "xs-tall", children: blocked ? (_jsx(Span, { size: "xs", color: "muted", surface: "muted", rounded: "default", padding: "pill-2xs", children: blocked })) : (_jsx(QuantityStepper, { value: qty[m.id] ?? 0, onChange: (next) => setQty((prev) => ({ ...prev, [m.id]: next })), min: 0, max: m.availableQuantity, ariaLabel: `Quantity for ${m.title}`, decrementLabel: ACTIONS.CART["decrease-quantity"].ariaLabel, incrementLabel: ACTIONS.CART["increase-quantity"].ariaLabel })) })), _jsx(Td, { padding: "xs-tall", children: _jsx("a", { href: memberHref(m), className: "text-[length:var(--appkit-text-xs)] text-[var(--appkit-color-primary)] hover:underline", children: "View \u2192" }) })] }, m.id));
105
- }) })] }) }), !isCrossStore && (_jsxs(_Fragment, { children: [error && _jsx(Alert, { variant: "error", children: error }), _jsxs(Row, { justify: "between", align: "center", gap: "sm", className: "min-w-0", children: [_jsxs(Text, { size: "sm", color: "muted", children: [selectedUnits > 0
133
+ return (_jsxs(Tr, { className: "last:border-0", border: "subtle", children: [_jsx(Td, { paddingSide: "pr-sm", padding: "xs-tall", children: _jsx(Div, { className: `w-10 h-10 ${__O.hidden}`, rounded: "full", border: "default", children: _jsx(MediaImage, { src: memberImage(m), alt: m.title, size: "thumbnail" }) }) }), _jsxs(Td, { paddingSide: "pr-sm", padding: "xs-tall", children: [_jsx(Text, { className: "line-clamp-2", color: "primary", size: "sm", weight: "medium", children: m.title }), m.isGroupParent && (_jsx(Span, { weight: "semibold", className: "text-[10px] text-[var(--appkit-color-primary)]", children: "Parent" }))] }), _jsx(Td, { paddingSide: "pr-sm", padding: "xs-tall", children: _jsx(Text, { size: "sm", color: "muted", children: _jsx(GatedPrice, { children: formatCurrency(m.price, m.currency ?? currency) }) }) }), !isCrossStore && (_jsx(Td, { paddingSide: "pr-sm", padding: "xs-tall", children: blocked ? (_jsx(Span, { size: "xs", color: "muted", surface: "muted", rounded: "default", padding: "pill-2xs", children: blocked })) : (_jsx(QuantityStepper, { value: qty[m.id] ?? 0, onChange: (next) => setQty((prev) => ({ ...prev, [m.id]: next })), min: 0,
134
+ /*
135
+ * `undefined` means uncapped — `QuantityStepper`
136
+ * only disables its increment when a `max` is given.
137
+ * A made-to-order member has no ceiling; the line's
138
+ * own 99-per-line cap still applies at the cart.
139
+ */
140
+ max: m.madeToOrder === true ? undefined : m.availableQuantity, ariaLabel: `Quantity for ${m.title}`, decrementLabel: ACTIONS.CART["decrease-quantity"].ariaLabel, incrementLabel: ACTIONS.CART["increase-quantity"].ariaLabel })) })), _jsx(Td, { padding: "xs-tall", children: _jsx("a", { href: memberHref(m), className: "text-[length:var(--appkit-text-xs)] text-[var(--appkit-color-primary)] hover:underline", children: "View \u2192" }) })] }, m.id));
141
+ }) })] }) }), !isCrossStore && (_jsxs(_Fragment, { children: [ruleNotes.length > 0 && (_jsx(Text, { size: "xs", color: "muted", children: ruleNotes.join(" · ") })), error && _jsx(Alert, { variant: "error", children: error }), selectedUnits > 0 && violations.length > 0 && (_jsx(Alert, { variant: "warning", children: _jsx(Stack, { gap: "dense", children: violations.map((v) => (_jsx(Text, { size: "sm", children: v.message }, v.code + v.message))) }) })), _jsxs(Row, { justify: "between", align: "center", gap: "sm", className: "min-w-0", children: [_jsxs(Text, { size: "sm", color: "muted", children: [selectedUnits > 0
106
142
  ? `${selectedUnits} item${selectedUnits === 1 ? "" : "s"}`
107
- : "Nothing selected yet", selectedUnits > 0 && (_jsx(PricesOnly, { children: ` · ${formatCurrency(selectedTotal, currency)}` }))] }), _jsx(Button, { action: ACTIONS.CART["add-group-to-cart"], disabled: selectedUnits === 0, isLoading: isSaving, onClick: handleAdd })] })] }))] }));
143
+ : "Nothing selected yet", selectedUnits > 0 && (_jsx(PricesOnly, { children: discountPercent > 0
144
+ ? ` · ${formatCurrency(discountedTotal, currency)} (${discountPercent}% off ${formatCurrency(selectedTotal, currency)})`
145
+ : ` · ${formatCurrency(selectedTotal, currency)}` }))] }), _jsx(Button, { action: ACTIONS.CART["add-group-to-cart"], disabled: selectedUnits === 0 || violations.length > 0, isLoading: isSaving, onClick: handleAdd })] })] }))] }));
108
146
  }
@@ -1,4 +1,5 @@
1
1
  import React from "react";
2
+ import type { GroupRules } from "../../../_internal/shared/features/products/group-rules";
2
3
  export interface GroupSettingsPanelProps {
3
4
  productId: string;
4
5
  productSlug: string;
@@ -7,8 +8,17 @@ export interface GroupSettingsPanelProps {
7
8
  groupParentSlug?: string;
8
9
  groupChildSlugs?: string[];
9
10
  groupTitle?: string;
11
+ /**
12
+ * The group's current selection rules, for seeding the form.
13
+ *
14
+ * 🛑 Seeded from the PARENT PRODUCT the editor is already showing — not
15
+ * re-fetched. The rules live on the parent and this panel is mounted from the
16
+ * parent's own editor, so the values are already in hand; a second read would
17
+ * be a second source of truth for the same five fields.
18
+ */
19
+ groupRules?: GroupRules;
10
20
  isAuction?: boolean;
11
21
  storeProductsEndpoint: string;
12
22
  onGroupChanged: () => void;
13
23
  }
14
- export declare function GroupSettingsPanel({ productId, productSlug, groupId, isGroupParent, groupParentSlug, groupChildSlugs, groupTitle, isAuction, storeProductsEndpoint, onGroupChanged, }: GroupSettingsPanelProps): React.JSX.Element | null;
24
+ export declare function GroupSettingsPanel({ productId, productSlug, groupId, isGroupParent, groupParentSlug, groupChildSlugs, groupTitle, groupRules, isAuction, storeProductsEndpoint, onGroupChanged, }: GroupSettingsPanelProps): React.JSX.Element | null;