@mohasinac/appkit 4.26.0 → 4.28.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 (277) hide show
  1. package/dist/_internal/client/features/lottery/LotteryAdminEditView.d.ts +16 -16
  2. package/dist/_internal/client/features/lottery/LotteryAdminEditView.js +29 -6
  3. package/dist/_internal/client/features/lottery/LotteryListView.js +13 -1
  4. package/dist/_internal/server/features/bundles/data.d.ts +19 -0
  5. package/dist/_internal/server/features/bundles/data.js +32 -0
  6. package/dist/_internal/server/features/bundles/index.d.ts +1 -1
  7. package/dist/_internal/server/features/bundles/index.js +1 -1
  8. package/dist/_internal/server/features/checkout/actions.js +6 -4
  9. package/dist/_internal/server/features/classified/metadata.js +3 -1
  10. package/dist/_internal/server/features/digital-code/metadata.js +3 -1
  11. package/dist/_internal/server/features/live/metadata.js +3 -1
  12. package/dist/_internal/server/features/refunds/actions.js +3 -2
  13. package/dist/_internal/server/features/reviews/actions.js +3 -2
  14. package/dist/_internal/server/features/seo/sitemap.js +189 -40
  15. package/dist/_internal/server/jobs/core/listingProcessor.d.ts +0 -1
  16. package/dist/_internal/server/jobs/core/listingProcessor.js +19 -1
  17. package/dist/_internal/shared/actions/action-registry.js +14 -0
  18. package/dist/_internal/shared/checkout/order-math.d.ts +25 -3
  19. package/dist/_internal/shared/features/checkout/config.d.ts +3 -3
  20. package/dist/_internal/shared/features/checkout/config.js +6 -3
  21. package/dist/client.d.ts +32 -2
  22. package/dist/client.js +37 -2
  23. package/dist/constants/api-endpoints.d.ts +21 -3
  24. package/dist/constants/api-endpoints.js +14 -1
  25. package/dist/constants/field-names.d.ts +2 -2
  26. package/dist/constants/field-names.js +2 -2
  27. package/dist/core/newsletter.repository.js +14 -1
  28. package/dist/features/about/components/FAQPageView.js +1 -1
  29. package/dist/features/about/components/FeesView.js +1 -1
  30. package/dist/features/account/components/AddressForm.d.ts +2 -0
  31. package/dist/features/account/components/AddressForm.js +5 -1
  32. package/dist/features/account/components/UserBidsView.js +1 -1
  33. package/dist/features/account/components/UserSidebar.js +1 -1
  34. package/dist/features/account/components/UserSupportView.js +24 -2
  35. package/dist/features/account/hooks/useAddresses.d.ts +29 -1
  36. package/dist/features/account/hooks/useAddresses.js +4 -1
  37. package/dist/features/account/hooks/useProfile.d.ts +1 -1
  38. package/dist/features/account/hooks/useProfile.js +1 -1
  39. package/dist/features/addresses/repository/addresses.repository.d.ts +13 -0
  40. package/dist/features/addresses/repository/addresses.repository.js +19 -0
  41. package/dist/features/admin/actions/chat-actions.js +5 -1
  42. package/dist/features/admin/components/AdminAdsView.js +1 -1
  43. package/dist/features/admin/components/AdminBidsView.js +6 -1
  44. package/dist/features/admin/components/AdminCarouselGroupEditorView.d.ts +9 -1
  45. package/dist/features/admin/components/AdminCarouselGroupEditorView.js +42 -10
  46. package/dist/features/admin/components/AdminCarouselView.js +1 -1
  47. package/dist/features/admin/components/AdminFeatureEditorView.js +1 -1
  48. package/dist/features/admin/components/AdminGroupedListingsView.js +14 -2
  49. package/dist/features/admin/components/AdminSidebar.js +1 -1
  50. package/dist/features/admin/components/AdminViewCards.js +2 -2
  51. package/dist/features/admin/components/DataTable.js +2 -2
  52. package/dist/features/admin/schemas/firestore.js +9 -3
  53. package/dist/features/auctions/actions/bid-actions.js +45 -4
  54. package/dist/features/auctions/components/AuctionBottomActions.d.ts +21 -6
  55. package/dist/features/auctions/components/AuctionBottomActions.js +42 -11
  56. package/dist/features/auctions/components/AuctionDetailPageView.d.ts +8 -3
  57. package/dist/features/auctions/components/AuctionDetailPageView.js +2 -2
  58. package/dist/features/auctions/components/MarketplaceAuctionCard.js +4 -5
  59. package/dist/features/auctions/components/PlaceBidFormClient.d.ts +27 -2
  60. package/dist/features/auctions/components/PlaceBidFormClient.js +42 -20
  61. package/dist/features/auth/components/SocialAuthButtons.js +1 -1
  62. package/dist/features/auth/hooks/useAuth.d.ts +23 -1
  63. package/dist/features/auth/hooks/useAuth.js +27 -1
  64. package/dist/features/auth/permissions/constants.d.ts +1 -1
  65. package/dist/features/auth/permissions/constants.js +3 -0
  66. package/dist/features/auth/repository/session.repository.d.ts +16 -1
  67. package/dist/features/auth/repository/session.repository.js +23 -0
  68. package/dist/features/auth/repository/user.repository.js +13 -2
  69. package/dist/features/blog/components/BlogListView.js +2 -2
  70. package/dist/features/cart/components/CartPriceBreakdown.d.ts +11 -1
  71. package/dist/features/cart/components/CartPriceBreakdown.js +3 -3
  72. package/dist/features/cart/components/StoreAddonsPicker.d.ts +9 -0
  73. package/dist/features/cart/components/StoreAddonsPicker.js +15 -5
  74. package/dist/features/cart/components/index.d.ts +1 -1
  75. package/dist/features/cart/components/index.js +1 -1
  76. package/dist/features/cart/hooks/useGuestCartMerge.d.ts +8 -1
  77. package/dist/features/cart/hooks/useGuestCartMerge.js +2 -1
  78. package/dist/features/cart/utils/line-total.d.ts +25 -0
  79. package/dist/features/cart/utils/line-total.js +26 -0
  80. package/dist/features/catalogue/repository/catalogue.repository.d.ts +12 -0
  81. package/dist/features/catalogue/repository/catalogue.repository.js +15 -0
  82. package/dist/features/categories/components/CategoryTree.js +2 -2
  83. package/dist/features/contact/components/ContactForm.js +1 -1
  84. package/dist/features/copilot/hooks/useCopilotFeedback.d.ts +1 -1
  85. package/dist/features/copilot/hooks/useCopilotFeedback.js +1 -1
  86. package/dist/features/events/components/AdminEventEditorView.js +16 -9
  87. package/dist/features/events/components/AdminEventEntriesView.js +7 -13
  88. package/dist/features/events/components/EventRafflesSection.js +14 -2
  89. package/dist/features/events/components/EventStatusBadge.js +1 -1
  90. package/dist/features/events/components/event-entry-detail-fields.d.ts +24 -0
  91. package/dist/features/events/components/event-entry-detail-fields.js +13 -0
  92. package/dist/features/faq/components/ContactCTA.js +1 -1
  93. package/dist/features/faq/components/FAQHelpfulButtons.js +2 -2
  94. package/dist/features/faq/repository/faqs.repository.d.ts +16 -1
  95. package/dist/features/faq/repository/faqs.repository.js +64 -54
  96. package/dist/features/faq/schemas/firestore.d.ts +3 -2
  97. package/dist/features/faq/schemas/firestore.js +2 -2
  98. package/dist/features/faq/schemas/index.d.ts +3 -3
  99. package/dist/features/faq/schemas/index.js +1 -1
  100. package/dist/features/filters/FilterFacetSection.js +2 -2
  101. package/dist/features/forms/Toggle.js +1 -1
  102. package/dist/features/grouped/components/GroupedListingEditorView.d.ts +18 -0
  103. package/dist/features/grouped/components/GroupedListingEditorView.js +192 -0
  104. package/dist/features/homepage/components/AdvertisementBanner.js +2 -2
  105. package/dist/features/homepage/components/BrandsSection.js +1 -1
  106. package/dist/features/homepage/components/CTABannerSection.js +1 -1
  107. package/dist/features/homepage/components/HomepageCustomerReviewsSection.js +1 -1
  108. package/dist/features/homepage/components/HomepageSectionBoundary.d.ts +39 -0
  109. package/dist/features/homepage/components/HomepageSectionBoundary.js +49 -0
  110. package/dist/features/homepage/components/ShopByCategorySection.js +1 -1
  111. package/dist/features/homepage/components/WelcomeSection.js +1 -1
  112. package/dist/features/homepage/lib/section-renderer.js +2 -1
  113. package/dist/features/layout/AppLayoutShell.js +5 -5
  114. package/dist/features/layout/BottomActions.js +5 -5
  115. package/dist/features/layout/BottomSheet.js +1 -1
  116. package/dist/features/layout/MainNavbar.js +1 -1
  117. package/dist/features/layout/NavbarLayout.js +1 -1
  118. package/dist/features/layout/TitleBarLayout.js +3 -3
  119. package/dist/features/layout/hooks/useFormBottomActions.d.ts +11 -1
  120. package/dist/features/layout/hooks/useFormBottomActions.js +16 -2
  121. package/dist/features/lottery/schemas/config-write.d.ts +141 -0
  122. package/dist/features/lottery/schemas/config-write.js +139 -0
  123. package/dist/features/media/MediaPickerModal.js +3 -1
  124. package/dist/features/media/MediaVideo.js +4 -2
  125. package/dist/features/media/components/MediaSlider.js +1 -1
  126. package/dist/features/orders/components/OrdersList.js +2 -2
  127. package/dist/features/payments/repository/payout.repository.d.ts +11 -1
  128. package/dist/features/payments/repository/payout.repository.js +14 -6
  129. package/dist/features/pre-orders/components/MarketplacePreorderCard.js +3 -2
  130. package/dist/features/pre-orders/components/PreOrderBadge.js +1 -1
  131. package/dist/features/pre-orders/components/PreOrderDetailPageView.js +1 -1
  132. package/dist/features/products/columns/productTableColumns.js +3 -3
  133. package/dist/features/products/components/CompareOverlay.js +1 -1
  134. package/dist/features/products/components/PrizeDrawDetailPageView.js +1 -1
  135. package/dist/features/products/components/PrizeDrawEntryActions.js +2 -2
  136. package/dist/features/products/components/PrizeDrawsSection.js +5 -1
  137. package/dist/features/products/components/ProductDetailActions.js +11 -7
  138. package/dist/features/products/components/ProductDetailPageView.js +3 -3
  139. package/dist/features/products/components/ProductFeatureBadges.js +2 -2
  140. package/dist/features/products/components/ProductFeaturesSelector.js +1 -1
  141. package/dist/features/products/components/ProductFilters.js +15 -8
  142. package/dist/features/products/components/ProductGrid.js +8 -17
  143. package/dist/features/products/components/ProductTabs.js +7 -4
  144. package/dist/features/products/components/ProductTabsShell.js +2 -7
  145. package/dist/features/products/constants/action-defs.d.ts +21 -4
  146. package/dist/features/products/constants/action-defs.js +7 -3
  147. package/dist/features/products/hooks/useRelatedProducts.js +2 -1
  148. package/dist/features/products/repository/products.repository.d.ts +1 -1
  149. package/dist/features/products/repository/products.repository.js +27 -13
  150. package/dist/features/products/schemas/firestore.d.ts +3 -2
  151. package/dist/features/products/schemas/firestore.js +1 -1
  152. package/dist/features/reviews/actions/review-actions.d.ts +8 -1
  153. package/dist/features/reviews/actions/review-actions.js +14 -3
  154. package/dist/features/reviews/components/ReviewDetailShell.js +10 -4
  155. package/dist/features/reviews/components/ReviewModal.js +1 -1
  156. package/dist/features/reviews/repository/reviews.repository.d.ts +25 -1
  157. package/dist/features/reviews/repository/reviews.repository.js +53 -13
  158. package/dist/features/reviews/schemas/firestore.d.ts +6 -0
  159. package/dist/features/scams/components/ScamRegistryView.js +2 -2
  160. package/dist/features/search/components/Search.js +3 -3
  161. package/dist/features/search/components/SearchResultsSection.js +1 -1
  162. package/dist/features/seller/components/SellerAddressesView.js +3 -1
  163. package/dist/features/seller/components/SellerBidsView.js +6 -1
  164. package/dist/features/seller/components/SellerSidebar.js +1 -1
  165. package/dist/features/shell/SectionForm.d.ts +21 -1
  166. package/dist/features/shell/SectionForm.js +15 -3
  167. package/dist/features/shell/build-sections.js +10 -1
  168. package/dist/features/shell/field-ui-meta.d.ts +21 -0
  169. package/dist/features/shipments/components/AdminShipmentLotItemsView.js +23 -6
  170. package/dist/features/site-settings/components/ActionPermissionsManager.js +1 -1
  171. package/dist/features/store-extensions/repository/store-extensions.repositories.d.ts +21 -4
  172. package/dist/features/store-extensions/repository/store-extensions.repositories.js +24 -4
  173. package/dist/features/store-extensions/schemas/shipping-config-form.js +13 -0
  174. package/dist/features/store-extensions/schemas/store-category-form.js +1 -1
  175. package/dist/features/stores/actions/store-query-actions.js +25 -1
  176. package/dist/features/stores/components/InteractiveStoreCard.js +1 -1
  177. package/dist/features/stores/components/StoreHeader.js +1 -1
  178. package/dist/features/stores/components/StoreNavTabs.js +20 -8
  179. package/dist/features/support/repository/support.repository.d.ts +12 -0
  180. package/dist/features/support/repository/support.repository.js +15 -0
  181. package/dist/features/support/schemas/ticket-create-form.d.ts +33 -0
  182. package/dist/features/support/schemas/ticket-create-form.js +82 -0
  183. package/dist/features/tester/seed-data/products-tester-seed-data.js +108 -2
  184. package/dist/features/tester/seed-data/tester-checklist-seed-data.js +235 -13
  185. package/dist/features/wishlist/components/WishlistHeartButton.d.ts +55 -0
  186. package/dist/features/wishlist/components/WishlistHeartButton.js +52 -0
  187. package/dist/features/wishlist/components/index.d.ts +2 -1
  188. package/dist/features/wishlist/components/index.js +1 -1
  189. package/dist/index.d.ts +10 -3
  190. package/dist/index.js +19 -4
  191. package/dist/monitoring/error-tracking.js +39 -5
  192. package/dist/monitoring/server-logger.js +70 -32
  193. package/dist/next/components/ErrorView.js +1 -1
  194. package/dist/next/routing/route-map.d.ts +39 -7
  195. package/dist/next/routing/route-map.js +34 -4
  196. package/dist/providers/db-firebase/base.repository.d.ts +30 -0
  197. package/dist/providers/db-firebase/base.repository.js +56 -9
  198. package/dist/react/ErrorBoundary.js +9 -1
  199. package/dist/react/contexts/SessionContext.js +6 -2
  200. package/dist/react/hooks/useAuthGate.d.ts +22 -0
  201. package/dist/react/hooks/useAuthGate.js +86 -7
  202. package/dist/schemas/registry.d.ts +3 -3
  203. package/dist/security/index.d.ts +1 -1
  204. package/dist/security/index.js +1 -1
  205. package/dist/security/pii-encrypt.d.ts +13 -5
  206. package/dist/security/pii-encrypt.js +132 -6
  207. package/dist/security/pii-schemas.d.ts +20 -4
  208. package/dist/security/pii-schemas.js +23 -5
  209. package/dist/seed/_helpers/product-search-txt.d.ts +18 -0
  210. package/dist/seed/_helpers/product-search-txt.js +33 -0
  211. package/dist/seed/faq-seed-data.d.ts +16 -0
  212. package/dist/seed/faq-seed-data.js +22 -1
  213. package/dist/seed/products-art-seed-data.js +49 -7
  214. package/dist/seed/products-auctions-seed-data.js +12 -4
  215. package/dist/seed/products-classifieds-seed-data.d.ts +5 -0
  216. package/dist/seed/products-classifieds-seed-data.js +65 -9
  217. package/dist/seed/products-digital-codes-seed-data.d.ts +5 -0
  218. package/dist/seed/products-digital-codes-seed-data.js +65 -9
  219. package/dist/seed/products-live-items-seed-data.d.ts +5 -0
  220. package/dist/seed/products-live-items-seed-data.js +33 -5
  221. package/dist/seed/products-preorders-seed-data.d.ts +1 -1
  222. package/dist/seed/products-preorders-seed-data.js +6 -2
  223. package/dist/seed/products-prize-draws-seed-data.d.ts +5 -0
  224. package/dist/seed/products-prize-draws-seed-data.js +57 -8
  225. package/dist/seed/products-standard-seed-data.js +6 -2
  226. package/dist/seed/products-stickers-seed-data.js +49 -7
  227. package/dist/seed/site-settings-seed-data.js +5 -3
  228. package/dist/seo/json-ld.d.ts +44 -2
  229. package/dist/seo/json-ld.js +57 -11
  230. package/dist/server.d.ts +1 -1
  231. package/dist/server.js +1 -1
  232. package/dist/styles.css +368 -90
  233. package/dist/tailwind-utilities.css +1 -1
  234. package/dist/tokens/index.d.ts +1 -1
  235. package/dist/tokens/index.js +1 -1
  236. package/dist/tokens/themes/default-dark.js +6 -0
  237. package/dist/tokens/themes/default-light.js +5 -0
  238. package/dist/tokens/tokens.css +84 -0
  239. package/dist/ui/DataTable.js +1 -1
  240. package/dist/ui/DataTable.style.css +3 -3
  241. package/dist/ui/components/BulkActionBar.style.css +1 -3
  242. package/dist/ui/components/Button.js +15 -6
  243. package/dist/ui/components/Dropdown.style.css +1 -4
  244. package/dist/ui/components/FilterChipGroup.js +1 -1
  245. package/dist/ui/components/FilterDrawer.style.css +1 -1
  246. package/dist/ui/components/Icon.d.ts +53 -0
  247. package/dist/ui/components/Icon.js +41 -0
  248. package/dist/ui/components/IconButton.d.ts +13 -0
  249. package/dist/ui/components/IconButton.js +13 -0
  250. package/dist/ui/components/IconButton.style.css +18 -10
  251. package/dist/ui/components/ListingLayout.style.css +2 -10
  252. package/dist/ui/components/ListingToolbar.js +6 -6
  253. package/dist/ui/components/Modal.js +1 -1
  254. package/dist/ui/components/PaginatedSelect.style.css +3 -3
  255. package/dist/ui/components/Pagination.style.css +1 -6
  256. package/dist/ui/components/RichTextEditor.js +1 -1
  257. package/dist/ui/components/RowActionMenu.style.css +1 -1
  258. package/dist/ui/components/SideDrawer.style.css +1 -1
  259. package/dist/ui/components/Slider.js +1 -1
  260. package/dist/ui/components/TabBarShell.d.ts +78 -0
  261. package/dist/ui/components/TabBarShell.js +95 -0
  262. package/dist/ui/components/Tabs.js +12 -8
  263. package/dist/ui/components/Tabs.style.css +249 -41
  264. package/dist/ui/components/ViewToggle.style.css +1 -5
  265. package/dist/ui/components/tabs-overflow.d.ts +17 -0
  266. package/dist/ui/components/tabs-overflow.js +142 -0
  267. package/dist/ui/icons/icon-registry.d.ts +38 -0
  268. package/dist/ui/icons/icon-registry.js +37 -0
  269. package/dist/ui/index.d.ts +6 -0
  270. package/dist/ui/index.js +3 -0
  271. package/dist/utils/index.d.ts +1 -1
  272. package/dist/utils/index.js +1 -1
  273. package/dist/utils/media-url.d.ts +27 -4
  274. package/dist/utils/media-url.js +37 -6
  275. package/dist/utils/search-txt.d.ts +71 -0
  276. package/dist/utils/search-txt.js +118 -0
  277. package/package.json +1 -1
@@ -14,6 +14,50 @@ const PRODUCT_STATUS = "status";
14
14
  const PRODUCT_STATUS_PUBLISHED = "published";
15
15
  const PRODUCT_SLUG = "slug";
16
16
  const PRODUCT_UPDATED_AT = "updatedAt";
17
+ /**
18
+ * Tester-sandbox fixtures must never reach the public sitemap. They are created
19
+ * and destroyed on a cycle by `testerSandboxCleanup` / `testerSandboxRefresh`,
20
+ * so anything Google indexes from them 404s shortly after. 34 of 182 sitemap
21
+ * URLs were disposable test fixtures before this filter existed.
22
+ *
23
+ * 🛑 This MUST be an in-memory filter, never a Firestore inequality on the
24
+ * field. An inequality silently excludes every document that does not have the
25
+ * field at all — which is every real product, store, category and post, since
26
+ * the flag is optional and was added later. Such a query returns ONLY test
27
+ * data. Same trap documented in CLAUDE.md § Tester QA Program.
28
+ *
29
+ * The field must therefore appear in each fetcher's `.select(...)` list, or it
30
+ * comes back undefined and nothing is filtered.
31
+ *
32
+ * The flag is not product-specific — it is set on categories, stores, blog
33
+ * posts and events too — but `PRODUCT_FIELDS` is where the canonical constant
34
+ * lives, so alias it rather than reintroducing a raw string.
35
+ */
36
+ const TEST_DATA_FIELD = PRODUCT_FIELDS.IS_TEST_DATA;
37
+ const isTestDoc = (data) => data.isTestData === true;
38
+ /**
39
+ * A sitemap section that throws returns `[]` so a single broken query degrades
40
+ * the sitemap instead of 500-ing the whole route. That trade-off is right, but
41
+ * it is also why `fetchCategoryUrls` could return zero URLs for months without
42
+ * anyone noticing — it was logged at `warn` and looked identical to "this site
43
+ * genuinely has no categories".
44
+ *
45
+ * Log at `error`, and always name the section, so a section dropping to zero is
46
+ * greppable in production logs. `buildSitemap` additionally logs a per-section
47
+ * count digest on every build (see below), and `scripts/deploy.mjs` fails the
48
+ * deploy when any section is empty.
49
+ *
50
+ * NOTE: callers must still call `normalizeError(err)` themselves, in the catch
51
+ * block, BEFORE delegating here. `audit-catch-normalize` requires that call to
52
+ * be lexically inside the `catch` — a helper that normalises on the caller's
53
+ * behalf reads as an unnormalised catch and fails the audit. Keeping the call at
54
+ * the catch site is also what guarantees `unknown` never escapes into downstream
55
+ * logic, which is the rule's actual point.
56
+ */
57
+ function sitemapSectionFailed(label, err) {
58
+ serverLogger.error(`sitemap: section "${label}" failed and returned 0 URLs`, { section: label, error: err });
59
+ return [];
60
+ }
17
61
  function staticPages(baseUrl) {
18
62
  const page = (path, changeFreq, priority) => ({ url: `${baseUrl}${path}`, lastModified: new Date(), changeFrequency: changeFreq, priority });
19
63
  return [
@@ -70,10 +114,12 @@ async function fetchProductUrls(baseUrl) {
70
114
  .collection(PRODUCT_COLLECTION)
71
115
  .where(PRODUCT_STATUS, "==", PRODUCT_STATUS_PUBLISHED)
72
116
  .where("listingType", "in", PRODUCT_DETAIL_LISTING_TYPES)
73
- .select(PRODUCT_SLUG, PRODUCT_UPDATED_AT)
117
+ .select(PRODUCT_SLUG, PRODUCT_UPDATED_AT, TEST_DATA_FIELD)
74
118
  .limit(5000)
75
119
  .get();
76
- return snap.docs.map((doc) => {
120
+ return snap.docs
121
+ .filter((doc) => !isTestDoc(doc.data()))
122
+ .map((doc) => {
77
123
  const data = doc.data();
78
124
  const slug = data[PRODUCT_SLUG] ?? doc.id;
79
125
  return {
@@ -86,8 +132,7 @@ async function fetchProductUrls(baseUrl) {
86
132
  }
87
133
  catch (err) {
88
134
  void normalizeError(err);
89
- serverLogger.warn("sitemap: failed to fetch product URLs", { error: err });
90
- return [];
135
+ return sitemapSectionFailed("product", err);
91
136
  }
92
137
  }
93
138
  async function fetchListingTypeUrls(baseUrl, listingType, buildUrl, label) {
@@ -97,10 +142,12 @@ async function fetchListingTypeUrls(baseUrl, listingType, buildUrl, label) {
97
142
  .collection(PRODUCT_COLLECTION)
98
143
  .where(PRODUCT_STATUS, "==", PRODUCT_STATUS_PUBLISHED)
99
144
  .where("listingType", "==", listingType)
100
- .select(PRODUCT_SLUG, PRODUCT_UPDATED_AT)
145
+ .select(PRODUCT_SLUG, PRODUCT_UPDATED_AT, TEST_DATA_FIELD)
101
146
  .limit(2000)
102
147
  .get();
103
- return snap.docs.map((doc) => {
148
+ return snap.docs
149
+ .filter((doc) => !isTestDoc(doc.data()))
150
+ .map((doc) => {
104
151
  const data = doc.data();
105
152
  const slug = data[PRODUCT_SLUG] ?? doc.id;
106
153
  return {
@@ -113,8 +160,7 @@ async function fetchListingTypeUrls(baseUrl, listingType, buildUrl, label) {
113
160
  }
114
161
  catch (err) {
115
162
  void normalizeError(err);
116
- serverLogger.warn(`sitemap: failed to fetch ${label} URLs`, { error: err });
117
- return [];
163
+ return sitemapSectionFailed(label, err);
118
164
  }
119
165
  }
120
166
  const fetchPreOrderUrls = (baseUrl) => fetchListingTypeUrls(baseUrl, "pre-order", (slug) => ROUTES.PUBLIC.PRE_ORDER_DETAIL(slug), "pre-order");
@@ -129,10 +175,12 @@ async function fetchAuctionUrls(baseUrl) {
129
175
  .collection(PRODUCT_COLLECTION)
130
176
  .where(PRODUCT_STATUS, "==", PRODUCT_STATUS_PUBLISHED)
131
177
  .where("listingType", "==", "auction")
132
- .select(PRODUCT_SLUG, PRODUCT_UPDATED_AT)
178
+ .select(PRODUCT_SLUG, PRODUCT_UPDATED_AT, TEST_DATA_FIELD)
133
179
  .limit(2000)
134
180
  .get();
135
- return snap.docs.map((doc) => {
181
+ return snap.docs
182
+ .filter((doc) => !isTestDoc(doc.data()))
183
+ .map((doc) => {
136
184
  const data = doc.data();
137
185
  const slug = data[PRODUCT_SLUG] ?? doc.id;
138
186
  return {
@@ -145,8 +193,7 @@ async function fetchAuctionUrls(baseUrl) {
145
193
  }
146
194
  catch (err) {
147
195
  void normalizeError(err);
148
- serverLogger.warn("sitemap: failed to fetch auction URLs", { error: err });
149
- return [];
196
+ return sitemapSectionFailed("auction", err);
150
197
  }
151
198
  }
152
199
  async function fetchEventUrls(baseUrl) {
@@ -155,10 +202,12 @@ async function fetchEventUrls(baseUrl) {
155
202
  const snap = await db
156
203
  .collection(EVENTS_COLLECTION)
157
204
  .where(EVENT_FIELDS.STATUS, "==", EVENT_FIELDS.STATUS_VALUES.ACTIVE)
158
- .select(EVENT_FIELDS.UPDATED_AT, EVENT_FIELDS.SLUG)
205
+ .select(EVENT_FIELDS.UPDATED_AT, EVENT_FIELDS.SLUG, TEST_DATA_FIELD)
159
206
  .limit(500)
160
207
  .get();
161
- return snap.docs.map((doc) => {
208
+ return snap.docs
209
+ .filter((doc) => !isTestDoc(doc.data()))
210
+ .map((doc) => {
162
211
  const data = doc.data();
163
212
  const slug = typeof data[EVENT_FIELDS.SLUG] === "string" ? data[EVENT_FIELDS.SLUG] : undefined;
164
213
  return {
@@ -171,15 +220,25 @@ async function fetchEventUrls(baseUrl) {
171
220
  }
172
221
  catch (err) {
173
222
  void normalizeError(err);
174
- serverLogger.warn("sitemap: failed to fetch event URLs", { error: err });
175
- return [];
223
+ return sitemapSectionFailed("event", err);
176
224
  }
177
225
  }
178
- // categories is a discriminated-union collection (categoryType: "listing" |
179
- // "brand" | "bundle" — see CLAUDE.md's SB-UNI-C/SB-UNI-D). Each discriminator
180
- // renders at a different public route, so each needs its own filtered query;
181
- // querying the collection without a categoryType filter (the prior bug here)
182
- // mapped brand/bundle rows onto the wrong /categories/{slug} URL.
226
+ // `categories` is a discriminated-union collection. Each discriminator renders
227
+ // at a different public route, so each needs its own filtered query; querying
228
+ // the collection without a categoryType filter (an older bug here) mapped
229
+ // brand/bundle rows onto the wrong /categories/{slug} URL.
230
+ //
231
+ // 🛑 The union is `"category" | "sublisting" | "brand" | "bundle"` — see
232
+ // CategoryType in features/categories/types/index.ts. There is NO "listing"
233
+ // value and there never was. This function used to be called with "listing"
234
+ // for the plain-category case, which matched zero documents, so ~47 category
235
+ // pages were missing from the sitemap for as long as that call existed — with
236
+ // no error, because a zero-row query is indistinguishable from "no categories".
237
+ //
238
+ // It is still only safe to use this helper for discriminators that are actually
239
+ // STORED. A plain listing category OMITS `categoryType` entirely (that is the
240
+ // convention every seeded listing category follows), so no equality can select
241
+ // it — see fetchCategoryUrls below, which inverts the test instead.
183
242
  async function fetchCategoryTypeUrls(baseUrl, categoryType, buildUrl, label) {
184
243
  try {
185
244
  const db = getAdminDb();
@@ -187,10 +246,12 @@ async function fetchCategoryTypeUrls(baseUrl, categoryType, buildUrl, label) {
187
246
  .collection(CATEGORIES_COLLECTION)
188
247
  .where(CATEGORY_FIELDS.CATEGORY_TYPE, "==", categoryType)
189
248
  .where(CATEGORY_FIELDS.IS_ACTIVE, "==", true)
190
- .select(CATEGORY_FIELDS.SLUG, CATEGORY_FIELDS.UPDATED_AT)
249
+ .select(CATEGORY_FIELDS.SLUG, CATEGORY_FIELDS.UPDATED_AT, TEST_DATA_FIELD)
191
250
  .limit(500)
192
251
  .get();
193
- return snap.docs.map((doc) => {
252
+ return snap.docs
253
+ .filter((doc) => !isTestDoc(doc.data()))
254
+ .map((doc) => {
194
255
  const data = doc.data();
195
256
  const slug = data[CATEGORY_FIELDS.SLUG] ?? doc.id;
196
257
  return {
@@ -203,11 +264,57 @@ async function fetchCategoryTypeUrls(baseUrl, categoryType, buildUrl, label) {
203
264
  }
204
265
  catch (err) {
205
266
  void normalizeError(err);
206
- serverLogger.warn(`sitemap: failed to fetch ${label} URLs`, { error: err });
207
- return [];
267
+ return sitemapSectionFailed(label, err);
268
+ }
269
+ }
270
+ /**
271
+ * Plain listing categories — the /categories/{slug} pages.
272
+ *
273
+ * These CANNOT be selected by an equality on `categoryType`, because a plain
274
+ * listing category omits the field entirely rather than storing "category".
275
+ * `.where(CATEGORY_TYPE, "==", <anything>)` therefore excludes every one of
276
+ * them, and a Firestore `!=` would be worse still — it silently drops documents
277
+ * that lack the field, which is exactly the set we want.
278
+ *
279
+ * So: select on `isActive` alone (the one predicate that IS stored on every
280
+ * row) and reject the three discriminated kinds in memory. `categoryType` must
281
+ * stay in the `.select()` list or it comes back undefined and nothing is
282
+ * rejected.
283
+ */
284
+ const NON_LISTING_CATEGORY_TYPES = new Set(["brand", "bundle", "sublisting"]);
285
+ async function fetchCategoryUrls(baseUrl) {
286
+ try {
287
+ const db = getAdminDb();
288
+ const snap = await db
289
+ .collection(CATEGORIES_COLLECTION)
290
+ .where(CATEGORY_FIELDS.IS_ACTIVE, "==", true)
291
+ .select(CATEGORY_FIELDS.SLUG, CATEGORY_FIELDS.UPDATED_AT, CATEGORY_FIELDS.CATEGORY_TYPE, TEST_DATA_FIELD)
292
+ .limit(500)
293
+ .get();
294
+ return snap.docs
295
+ .filter((doc) => {
296
+ const data = doc.data();
297
+ if (isTestDoc(data))
298
+ return false;
299
+ const kind = data[CATEGORY_FIELDS.CATEGORY_TYPE];
300
+ return typeof kind !== "string" || !NON_LISTING_CATEGORY_TYPES.has(kind);
301
+ })
302
+ .map((doc) => {
303
+ const data = doc.data();
304
+ const slug = data[CATEGORY_FIELDS.SLUG] ?? doc.id;
305
+ return {
306
+ url: `${baseUrl}${ROUTES.PUBLIC.CATEGORY_DETAIL(slug)}`,
307
+ lastModified: data[CATEGORY_FIELDS.UPDATED_AT]?.toDate?.() ?? new Date(),
308
+ changeFrequency: "weekly",
309
+ priority: 0.7,
310
+ };
311
+ });
312
+ }
313
+ catch (err) {
314
+ void normalizeError(err);
315
+ return sitemapSectionFailed("category", err);
208
316
  }
209
317
  }
210
- const fetchCategoryUrls = (baseUrl) => fetchCategoryTypeUrls(baseUrl, "listing", (slug) => ROUTES.PUBLIC.CATEGORY_DETAIL(slug), "category");
211
318
  const fetchBrandUrls = (baseUrl) => fetchCategoryTypeUrls(baseUrl, "brand", (slug) => ROUTES.PUBLIC.BRAND_DETAIL(slug), "brand");
212
319
  const fetchBundleUrls = (baseUrl) => fetchCategoryTypeUrls(baseUrl, "bundle", (slug) => ROUTES.PUBLIC.BUNDLE_DETAIL(slug), "bundle");
213
320
  /**
@@ -228,7 +335,9 @@ async function fetchGroupedListingUrls(baseUrl) {
228
335
  }
229
336
  catch (err) {
230
337
  void normalizeError(err);
231
- return [];
338
+ // This catch used to swallow silently — no log at all, so a failure here was
339
+ // indistinguishable from "this site has no grouped listings".
340
+ return sitemapSectionFailed("grouped-listing", err);
232
341
  }
233
342
  }
234
343
  async function fetchBlogPostUrls(baseUrl) {
@@ -237,10 +346,12 @@ async function fetchBlogPostUrls(baseUrl) {
237
346
  const snap = await db
238
347
  .collection(BLOG_POSTS_COLLECTION)
239
348
  .where(BLOG_POST_FIELDS.STATUS, "==", BLOG_POST_FIELDS.STATUS_VALUES.PUBLISHED)
240
- .select(BLOG_POST_FIELDS.SLUG, BLOG_POST_FIELDS.PUBLISHED_AT, BLOG_POST_FIELDS.UPDATED_AT)
349
+ .select(BLOG_POST_FIELDS.SLUG, BLOG_POST_FIELDS.PUBLISHED_AT, BLOG_POST_FIELDS.UPDATED_AT, TEST_DATA_FIELD)
241
350
  .limit(1000)
242
351
  .get();
243
- return snap.docs.map((doc) => {
352
+ return snap.docs
353
+ .filter((doc) => !isTestDoc(doc.data()))
354
+ .map((doc) => {
244
355
  const data = doc.data();
245
356
  const slug = data[BLOG_POST_FIELDS.SLUG] ?? doc.id;
246
357
  const lastModified = data[BLOG_POST_FIELDS.UPDATED_AT]?.toDate?.() ??
@@ -256,8 +367,7 @@ async function fetchBlogPostUrls(baseUrl) {
256
367
  }
257
368
  catch (err) {
258
369
  void normalizeError(err);
259
- serverLogger.warn("sitemap: failed to fetch blog post URLs", { error: err });
260
- return [];
370
+ return sitemapSectionFailed("blog-post", err);
261
371
  }
262
372
  }
263
373
  async function fetchStoreUrls(baseUrl) {
@@ -267,10 +377,12 @@ async function fetchStoreUrls(baseUrl) {
267
377
  .collection(STORE_COLLECTION)
268
378
  .where(STORE_FIELDS.STATUS, "==", STORE_FIELDS.STATUS_VALUES.ACTIVE)
269
379
  .where(STORE_FIELDS.IS_PUBLIC, "==", true)
270
- .select(STORE_FIELDS.STORE_SLUG, STORE_FIELDS.UPDATED_AT)
380
+ .select(STORE_FIELDS.STORE_SLUG, STORE_FIELDS.UPDATED_AT, TEST_DATA_FIELD)
271
381
  .limit(1000)
272
382
  .get();
273
- return snap.docs.map((doc) => {
383
+ return snap.docs
384
+ .filter((doc) => !isTestDoc(doc.data()))
385
+ .map((doc) => {
274
386
  const data = doc.data();
275
387
  const slug = data[STORE_FIELDS.STORE_SLUG] ?? doc.id;
276
388
  return {
@@ -283,8 +395,7 @@ async function fetchStoreUrls(baseUrl) {
283
395
  }
284
396
  catch (err) {
285
397
  void normalizeError(err);
286
- serverLogger.warn("sitemap: failed to fetch store URLs", { error: err });
287
- return [];
398
+ return sitemapSectionFailed("store", err);
288
399
  }
289
400
  }
290
401
  async function fetchScammerUrls(baseUrl) {
@@ -293,10 +404,12 @@ async function fetchScammerUrls(baseUrl) {
293
404
  const snap = await db
294
405
  .collection(SCAMMER_COLLECTION)
295
406
  .where(PRODUCT_FIELDS.STATUS, "==", "verified")
296
- .select("seo", "updatedAt")
407
+ .select("seo", "updatedAt", TEST_DATA_FIELD)
297
408
  .limit(2000)
298
409
  .get();
299
- return snap.docs.map((doc) => {
410
+ return snap.docs
411
+ .filter((doc) => !isTestDoc(doc.data()))
412
+ .map((doc) => {
300
413
  const data = doc.data();
301
414
  const slug = data.seo?.slug ?? doc.id;
302
415
  return {
@@ -309,8 +422,7 @@ async function fetchScammerUrls(baseUrl) {
309
422
  }
310
423
  catch (err) {
311
424
  void normalizeError(err);
312
- serverLogger.warn("sitemap: failed to fetch scammer URLs", { error: err });
313
- return [];
425
+ return sitemapSectionFailed("scammer", err);
314
426
  }
315
427
  }
316
428
  export async function buildSitemap({ baseUrl }) {
@@ -331,8 +443,45 @@ export async function buildSitemap({ baseUrl }) {
331
443
  fetchStoreUrls(baseUrl),
332
444
  fetchScammerUrls(baseUrl),
333
445
  ]);
446
+ const staticUrls = staticPages(baseUrl);
447
+ // Per-section count digest. A section that drops to zero is otherwise
448
+ // indistinguishable from a site that genuinely has none of that thing — which
449
+ // is exactly how `category: 0` survived unnoticed while every other section
450
+ // worked (the query filtered on a `categoryType` value that does not exist).
451
+ //
452
+ // `scripts/deploy.mjs` asserts the same counts against the deployed sitemap
453
+ // and fails the deploy on an empty section; this log is the production-side
454
+ // record for when a section empties out later, between deploys.
455
+ const sections = {
456
+ static: staticUrls.length,
457
+ category: categoryUrls.length,
458
+ brand: brandUrls.length,
459
+ bundle: bundleUrls.length,
460
+ group: groupedListingUrls.length,
461
+ blog: blogUrls.length,
462
+ product: productUrls.length,
463
+ auction: auctionUrls.length,
464
+ preOrder: preOrderUrls.length,
465
+ prizeDraw: prizeDrawUrls.length,
466
+ classified: classifiedUrls.length,
467
+ digitalCode: digitalCodeUrls.length,
468
+ live: liveUrls.length,
469
+ event: eventUrls.length,
470
+ store: storeUrls.length,
471
+ scammer: scammerUrls.length,
472
+ };
473
+ const empty = Object.entries(sections)
474
+ .filter(([, n]) => n === 0)
475
+ .map(([name]) => name);
476
+ const total = Object.values(sections).reduce((a, b) => a + b, 0);
477
+ if (empty.length > 0) {
478
+ serverLogger.warn(`sitemap: ${empty.length} section(s) produced 0 URLs`, { empty, sections, total });
479
+ }
480
+ else {
481
+ serverLogger.info("sitemap: built", { sections, total });
482
+ }
334
483
  return [
335
- ...staticPages(baseUrl),
484
+ ...staticUrls,
336
485
  ...categoryUrls,
337
486
  ...brandUrls,
338
487
  ...bundleUrls,
@@ -10,7 +10,6 @@ import type { JobContext } from "../runtime/types";
10
10
  import type { JsonValue } from "../../../../schemas/types";
11
11
  export interface ListingRequestBody {
12
12
  collection: string;
13
- q?: string;
14
13
  f?: string;
15
14
  s?: string;
16
15
  p?: number | string;
@@ -36,6 +36,17 @@ function getBoolOpt(baseOpts, key, fallback) {
36
36
  return false;
37
37
  return fallback;
38
38
  }
39
+ function getStringOpt(baseOpts, key) {
40
+ const value = baseOpts[key];
41
+ return typeof value === "string" && value.length > 0 ? value : undefined;
42
+ }
43
+ function getStringArrayOpt(baseOpts, key) {
44
+ const value = baseOpts[key];
45
+ if (!Array.isArray(value))
46
+ return undefined;
47
+ const strings = value.filter((v) => typeof v === "string" && v.length > 0);
48
+ return strings.length > 0 ? strings : undefined;
49
+ }
39
50
  const LISTERS = {
40
51
  products: (m, o) => productRepository.list(m, o),
41
52
  categories: (m) => categoriesRepository.list(m),
@@ -52,7 +63,14 @@ const LISTERS = {
52
63
  payouts: (m) => payoutRepository.list(m),
53
64
  blogPosts: (m) => blogRepository.listAll(m),
54
65
  events: (m) => eventRepository.list(m),
55
- faqs: (m) => faqsRepository.list(m),
66
+ // Forwards opts so `baseOpts.search` reaches the repository's token search.
67
+ // This was `(m) => faqsRepository.list(m)` — the second argument was dropped,
68
+ // which made FAQ token search structurally unreachable through the Function
69
+ // even though the repository has implemented it all along.
70
+ faqs: (m, o) => faqsRepository.list(m, {
71
+ search: getStringOpt(o, "search"),
72
+ tags: getStringArrayOpt(o, "tags"),
73
+ }),
56
74
  notifications: (m) => notificationRepository.list(m),
57
75
  scammers: (m) => scammerRepository.listAll(m),
58
76
  sublistingCategories: (m) => categoriesRepository.list({
@@ -2469,6 +2469,20 @@ export const ACTIONS = {
2469
2469
  description: "Advance from address selection to the payment step.",
2470
2470
  kind: "primary",
2471
2471
  },
2472
+ "continue-to-payment": {
2473
+ iconKey: "cart",
2474
+ id: "checkout.continue-to-payment",
2475
+ label: "Continue to payment",
2476
+ description: "Advance from the add-ons & fees step to the payment step.",
2477
+ kind: "primary",
2478
+ },
2479
+ back: {
2480
+ iconKey: "back",
2481
+ id: "checkout.back",
2482
+ label: "Back",
2483
+ description: "Return to the previous checkout step. Safe at any point before payment — add-on selections are persisted to the cart as they are made, so nothing is lost.",
2484
+ kind: "ghost",
2485
+ },
2472
2486
  "verify-otp": {
2473
2487
  iconKey: "verify",
2474
2488
  id: "checkout.verify-otp",
@@ -4,8 +4,30 @@
4
4
  * directly, unlike `checkout/actions.ts` whose module graph pulls in
5
5
  * `server-only` guards that fail outside a real server-action test harness.
6
6
  */
7
- import type { CartItemDocument } from "../../../features/cart/schemas/firestore";
7
+ import type { CartItemDocument, CartLineKind } from "../../../features/cart/schemas/firestore";
8
8
  import type { ProductDocument } from "../../../features/products/schemas/firestore";
9
+ /**
10
+ * Exactly the fields `unitPriceFor` reads.
11
+ *
12
+ * Deliberately narrower than `CartItemDocument`, which a client cannot satisfy:
13
+ * `checkoutDeadline` is a `Date` on the document and a string once the cart has
14
+ * been through `JSON.parse`. Without this seam the checkout page would have to
15
+ * hand-roll a seventh copy of the pricing rule — the exact thing the docstring
16
+ * below spends a paragraph forbidding. `CartItemDocument` is structurally
17
+ * assignable to it, so every existing server call site is unaffected.
18
+ */
19
+ export interface PricedCartLine {
20
+ price: number;
21
+ quantity: number;
22
+ lockedPrice?: number;
23
+ lineKind?: CartLineKind;
24
+ bundleCategorySlug?: string;
25
+ bundleProductIds?: string[];
26
+ groupMembers?: Array<{
27
+ unitPrice: number;
28
+ quantity: number;
29
+ }>;
30
+ }
9
31
  /**
10
32
  * The single source of truth for "what do we charge for ONE COPY of this line".
11
33
  *
@@ -38,13 +60,13 @@ import type { ProductDocument } from "../../../features/products/schemas/firesto
38
60
  * for an accepted offer while the cart displayed the negotiated one. Call
39
61
  * `lineTotalFor` and let this function stay the only definition.
40
62
  */
41
- export declare function unitPriceFor(item: CartItemDocument, product: ProductDocument | null): number;
63
+ export declare function unitPriceFor(item: PricedCartLine, product: ProductDocument | null): number;
42
64
  /**
43
65
  * What this line contributes to a subtotal: one copy's price × the number of
44
66
  * copies. THE call site should use this rather than multiplying `unitPriceFor`
45
67
  * itself — that multiplication is where the six hand-rolled copies diverged.
46
68
  */
47
- export declare function lineTotalFor(item: CartItemDocument, product: ProductDocument | null): number;
69
+ export declare function lineTotalFor(item: PricedCartLine, product: ProductDocument | null): number;
48
70
  /** One taxable slice of a line. A single-product line has exactly one. */
49
71
  export interface LineTaxComponent {
50
72
  productId: string;
@@ -6,11 +6,11 @@ export declare const CHECKOUT_DEFAULT_COMMISSIONS: {
6
6
  readonly codDepositPercent: 10;
7
7
  readonly codHandlingFeeMin: 200;
8
8
  readonly codHandlingFeePercent: 10;
9
- readonly whatsappNotifyFeeEnabled: false;
9
+ readonly whatsappNotifyFeeEnabled: true;
10
10
  readonly whatsappNotifyFee: 10;
11
- readonly giftWrapFeeEnabled: false;
11
+ readonly giftWrapFeeEnabled: true;
12
12
  readonly giftWrapFee: 49;
13
- readonly shipmentProtectionFeeEnabled: false;
13
+ readonly shipmentProtectionFeeEnabled: true;
14
14
  readonly shipmentProtectionFeePercent: 2;
15
15
  readonly shipmentProtectionFeeMin: 30;
16
16
  readonly sellerShippingFixed: 50;
@@ -10,11 +10,14 @@ export const CHECKOUT_DEFAULT_COMMISSIONS = {
10
10
  codDepositPercent: 10,
11
11
  codHandlingFeeMin: 200,
12
12
  codHandlingFeePercent: 10,
13
- whatsappNotifyFeeEnabled: false,
13
+ // Mirrors the SiteSettingsDocument defaults, which enable all three add-ons.
14
+ // Consequence of the mirror: if the settings read fails, an add-on already
15
+ // ticked on the cart document is still billed rather than silently dropped.
16
+ whatsappNotifyFeeEnabled: true,
14
17
  whatsappNotifyFee: 10,
15
- giftWrapFeeEnabled: false,
18
+ giftWrapFeeEnabled: true,
16
19
  giftWrapFee: 49,
17
- shipmentProtectionFeeEnabled: false,
20
+ shipmentProtectionFeeEnabled: true,
18
21
  shipmentProtectionFeePercent: 2,
19
22
  shipmentProtectionFeeMin: 30,
20
23
  sellerShippingFixed: 50,
package/dist/client.d.ts CHANGED
@@ -11,6 +11,8 @@ export type { SurfaceErrorOptions } from "./client/api/surface-error";
11
11
  export { reportClientError, } from "./client/observability/reportClientError";
12
12
  export type { ClientErrorPayload } from "./client/observability/reportClientError";
13
13
  export { installClientErrorReporter, useClientErrorReporter, } from "./client/observability/installClientErrorReporter";
14
+ export { setErrorTracker, trackError, ErrorCategory, ErrorSeverity, } from "./monitoring/error-tracking";
15
+ export type { ErrorTrackerFn, ErrorContext, } from "./monitoring/error-tracking";
14
16
  export { useApiMutation } from "./client/api/useApiMutation";
15
17
  export type { UseApiMutationOptions } from "./client/api/useApiMutation";
16
18
  export { useApiQuery } from "./client/api/useApiQuery";
@@ -38,6 +40,8 @@ export type { SkeletonProps } from "./ui/components/Skeleton";
38
40
  export { Divider } from "./ui/components/Divider";
39
41
  export { Tabs, TabsList, TabsTrigger, TabsContent } from "./ui/components/Tabs";
40
42
  export type { TabsProps, TabsListProps, TabsTriggerProps, TabsContentProps } from "./ui/components/Tabs";
43
+ export { TabBarShell, TabBarButton, TabsNavSelect } from "./ui/components/TabBarShell";
44
+ export type { TabBarShellProps, TabBarButtonProps, TabsNavSelectProps } from "./ui/components/TabBarShell";
41
45
  export { SellerProductsView } from "./features/seller/components/SellerProductsView";
42
46
  export { SellerOrderDetailPanel } from "./features/seller/components/SellerOrdersView";
43
47
  export { SellerPayoutDetailContent } from "./features/seller/components/SellerPayoutsView";
@@ -90,6 +94,20 @@ export { LotteryDetailView } from "./_internal/client/features/lottery/LotteryDe
90
94
  export { LotteryEntriesView } from "./_internal/client/features/lottery/LotteryEntriesView";
91
95
  export { LotteryAdminSlotView } from "./_internal/client/features/lottery/LotteryAdminSlotView";
92
96
  export { LotteryAdminEditView } from "./_internal/client/features/lottery/LotteryAdminEditView";
97
+ export { lotteryConfigWriteSchema, lotterySlotWriteSchema } from "./features/lottery/schemas/config-write";
98
+ export { SectionForm, useSectionFormNav } from "./features/shell/SectionForm";
99
+ export type { SectionDef, SectionFormProps } from "./features/shell/SectionForm";
100
+ export { buildSectionsFromSchema } from "./features/shell/build-sections";
101
+ export { reportCreateSchema } from "./features/store-extensions/schemas/report-create-form";
102
+ export { itemRequestCreateSchema } from "./features/store-extensions/schemas/item-request-create-form";
103
+ export { supportTicketCreateSchema } from "./features/support/schemas/ticket-create-form";
104
+ export type { SupportTicketCreateValues } from "./features/support/schemas/ticket-create-form";
105
+ export { analyticsCardCreateSchema, ANALYTICS_CARD_TYPES } from "./features/store-extensions/schemas/analytics-forms";
106
+ export { QuickFormDrawer } from "./features/shell/QuickFormDrawer";
107
+ export type { QuickFormDrawerProps, QuickFieldDef } from "./features/shell/QuickFormDrawer";
108
+ export type { ItemRequestCreateValues } from "./features/store-extensions/schemas/item-request-create-form";
109
+ export type { ReportCreateValues } from "./features/store-extensions/schemas/report-create-form";
110
+ export type { LotteryConfigWriteInput } from "./features/lottery/schemas/config-write";
93
111
  export { PrizeDrawLotteryDetailView } from "./_internal/client/features/lottery/PrizeDrawLotteryDetailView";
94
112
  export type { LotteryConfig, LotterySlot, ClientLotterySlot, ClientLotteryConfig, LotteryPricingMode, LotteryEntryStatus } from "./features/lottery/types";
95
113
  export { BottomSheet } from "./features/layout/index";
@@ -220,13 +238,19 @@ export type { PageViewTrackerProps } from "./features/analytics/components/PageV
220
238
  export { PAGE_VIEW_ENTITY_TYPES } from "./features/analytics/types";
221
239
  export type { PageViewEntityType } from "./features/analytics/types";
222
240
  export { AuctionBidsTable } from "./features/auctions/components/AuctionBidsTable";
241
+ export { buildBidDetailFields, bidStatusBadge } from "./features/auctions/components/bid-detail-fields";
242
+ export { buildEventEntryDetailFields } from "./features/events/components/event-entry-detail-fields";
243
+ export type { EntryFieldRow } from "./features/events/components/event-entry-detail-fields";
244
+ export type { BidDetailViewer } from "./features/auctions/components/bid-detail-fields";
223
245
  export type { AuctionBidsTableProps, AuctionWithBids } from "./features/auctions/components/AuctionBidsTable";
224
246
  export { ProtectedRoute, AuthStatusPanel, ForgotPasswordView, LoginForm, RegisterForm, ResetPasswordView, VerifyEmailView } from "./features/auth/index";
225
247
  export type { AuthGuardUser, ForgotPasswordViewProps, LoginFormProps, LoginFormValues, RegisterFormProps, RegisterFormValues, ResetPasswordViewProps, VerifyEmailViewProps, } from "./features/auth/index";
226
248
  export { useLogout, useLogin, useGoogleLogin, useLinkGoogleAccount, useRegister, useForgotPassword, useResetPassword, useVerifyEmail, useChangeEmail } from "./features/auth/index";
227
249
  export type { LoginCredentials, RegisterData, ForgotPasswordData, ResetPasswordData, VerifyEmailData, ChangeEmailData } from "./features/auth/index";
228
250
  export { CartView, CartItemRow, CartGroupLineRow, CartSummary, CartPriceBreakdown, CartDrawer, CheckoutView, CheckoutSuccessView, CheckoutAddressStep, useGuestCart, useCartCount, useAddToCart, useCart, useGuestCartMerge, useCartQuery } from "./features/cart/index";
229
- export { StoreAddonsPicker } from "./features/cart/index";
251
+ export { StoreAddonsPicker, hasAnyStoreAddon } from "./features/cart/index";
252
+ export { clientLineTotal } from "./features/cart/utils/line-total";
253
+ export type { PricedCartLine } from "./features/cart/utils/line-total";
230
254
  export type { CartPriceBreakdownData, CartPriceBreakdownStore, StoreAddonsValue, StoreAddonsRates } from "./features/cart/index";
231
255
  export { getCartOps, CART_OPS_CHANGE_EVENT } from "./features/cart/utils/pending-ops";
232
256
  export type { CartOp } from "./features/cart/utils/pending-ops";
@@ -258,12 +282,16 @@ export type { AdSlotId, AdProvider, AdSlotConfig } from "./features/homepage/ad-
258
282
  export { WishlistView, useGuestWishlist, useWishlistWithGuest } from "./features/wishlist/index";
259
283
  export type { WishlistViewProps, GuestWishlistItem, WishlistItem, WishlistResponse, WishlistProductData, EnrichedWishlistItem } from "./features/wishlist/index";
260
284
  export { WishlistCapWatcher } from "./features/wishlist/components/WishlistCapWatcher";
285
+ export { WishlistHeartButton } from "./features/wishlist/components/WishlistHeartButton";
286
+ export type { WishlistHeartButtonProps, WishlistHeartPlacement, } from "./features/wishlist/components/WishlistHeartButton";
261
287
  export { SyncManagerMount } from "./core/components/SyncManagerMount";
262
288
  export { useWishlistCount, useWishlistCountWithLimit, WISHLIST_CAP_EVENT } from "./features/wishlist/hooks/useWishlistCount";
263
289
  export type { WishlistCapEventDetail } from "./features/wishlist/hooks/useWishlistCount";
264
290
  export { getGuestWishlistItems, addToGuestWishlist, removeFromGuestWishlist, isInGuestWishlist, clearGuestWishlist, getGuestWishlistCount, getGuestWishlistByType, } from "./features/wishlist/utils/guest-wishlist";
265
291
  export { InteractiveProductCard } from "./features/products/index";
266
292
  export type { InteractiveProductCardProps } from "./features/products/index";
293
+ export { GroupedListingEditorView } from "./features/grouped/components/GroupedListingEditorView";
294
+ export type { GroupedListingEditorViewProps } from "./features/grouped/components/GroupedListingEditorView";
267
295
  export { GroupedListingDetailView } from "./features/grouped/components/GroupedListingDetailView";
268
296
  export type { GroupedListingDetailViewProps } from "./features/grouped/components/GroupedListingDetailView";
269
297
  export { GroupMemberPicker } from "./features/products/components/GroupMemberPicker";
@@ -414,8 +442,10 @@ export type { PaymentProofReviewMessageInput } from "./features/whatsapp-bot/typ
414
442
  export type { AdminNotificationDocument, ItemRequestDocument, ModerationQueueDocument, ReportDocument, CustomRoleDocument, AnalyticsCardDocument, ListingTemplateDocument, } from "./features/store-extensions/index";
415
443
  export { CommandPalette, useCommandPaletteHotkey } from "./features/shell/index";
416
444
  export type { CommandPaletteGroup } from "./features/shell/index";
417
- export { AdminBlogEditorView, AdminBundleEditorView, AdminCarouselEditorView, AdminCategoryEditorView, AdminCouponEditorView, AdminFaqEditorView, AdminFeatureEditorView, AdminOrderEditorView, AdminPayoutMarkPaidModal, AdminAuditLogView, ViewAuditLogEntryModal, AdminProductEditorView, AdminScammerEditorView, AdminStoreEditorView, AdminSublistingCategoryEditorView, AdminSupportTicketDetailView, } from "./features/admin/index";
445
+ export { AdminBlogEditorView, AdminBundleEditorView, AdminCarouselEditorView, AdminCategoryEditorView, AdminCouponEditorView, AdminFaqEditorView, AdminFeatureEditorView, AdminOrderEditorView, AdminPayoutMarkPaidModal, AdminAuditLogView, ViewAuditLogEntryModal, AdminProductEditorView, AdminScammerEditorView, AdminEmployeeEditorView, AdminNavEditorView, AdminUserEditorView, AdminStoreEditorView, AdminSublistingCategoryEditorView, AdminSupportTicketDetailView, } from "./features/admin/index";
418
446
  export type { AdminStoreEditorViewProps, AdminPayoutMarkPaidModalProps, AdminAuditLogViewProps, AuditLogEntryDetail, ViewAuditLogEntryModalProps } from "./features/admin/index";
447
+ export type { NavItemData } from "./features/admin/index";
448
+ export { PageLoader } from "./ui/index";
419
449
  export { ADMIN_CHECKOUT_BYPASS_FLAG_KEY } from "./features/admin/schemas/firestore";
420
450
  export { SpinWheelView, EventRaffleEntryForm } from "./features/events/index";
421
451
  export { Card, CardBody } from "./ui/index";