@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
package/dist/client.js CHANGED
@@ -12,6 +12,12 @@ export { ApiError, isApiError } from "./client/api/ApiError";
12
12
  export { surfaceError } from "./client/api/surface-error";
13
13
  export { reportClientError, } from "./client/observability/reportClientError";
14
14
  export { installClientErrorReporter, useClientErrorReporter, } from "./client/observability/installClientErrorReporter";
15
+ // [CLIENT-SAFE] Error-tracker registration. The consumer calls setErrorTracker
16
+ // once at bootstrap to route every trackError() — notably the `error.digest`
17
+ // captured by ErrorView / GlobalError / ErrorBoundary — into reportClientError.
18
+ // Without a registration, trackError falls back to console.error and the digest
19
+ // (the only link to the server-side error) is lost.
20
+ export { setErrorTracker, trackError, ErrorCategory, ErrorSeverity, } from "./monitoring/error-tracking";
15
21
  // [CLIENT-SAFE] React Query wrappers — auto-toast / auto-inline-field-error
16
22
  // on failure, optional `loadingMessage` for long ops.
17
23
  export { useApiMutation } from "./client/api/useApiMutation";
@@ -51,6 +57,7 @@ export { IconBox } from "./ui/components/IconBox";
51
57
  export { Alert } from "./ui/components/Alert";
52
58
  export { Divider } from "./ui/components/Divider";
53
59
  export { Tabs, TabsList, TabsTrigger, TabsContent } from "./ui/components/Tabs";
60
+ export { TabBarShell, TabBarButton, TabsNavSelect } from "./ui/components/TabBarShell";
54
61
  export { SellerProductsView } from "./features/seller/components/SellerProductsView";
55
62
  export { SellerOrderDetailPanel } from "./features/seller/components/SellerOrdersView";
56
63
  export { SellerPayoutDetailContent } from "./features/seller/components/SellerPayoutsView";
@@ -144,6 +151,16 @@ export { LotteryDetailView } from "./_internal/client/features/lottery/LotteryDe
144
151
  export { LotteryEntriesView } from "./_internal/client/features/lottery/LotteryEntriesView";
145
152
  export { LotteryAdminSlotView } from "./_internal/client/features/lottery/LotteryAdminSlotView";
146
153
  export { LotteryAdminEditView } from "./_internal/client/features/lottery/LotteryAdminEditView";
154
+ export { lotteryConfigWriteSchema, lotterySlotWriteSchema } from "./features/lottery/schemas/config-write";
155
+ // The SectionForm engine + its schema-derived section builder. Client-only:
156
+ // consumer forms need these and the bare package resolves to the SERVER entry.
157
+ export { SectionForm, useSectionFormNav } from "./features/shell/SectionForm";
158
+ export { buildSectionsFromSchema } from "./features/shell/build-sections";
159
+ export { reportCreateSchema } from "./features/store-extensions/schemas/report-create-form";
160
+ export { itemRequestCreateSchema } from "./features/store-extensions/schemas/item-request-create-form";
161
+ export { supportTicketCreateSchema } from "./features/support/schemas/ticket-create-form";
162
+ export { analyticsCardCreateSchema, ANALYTICS_CARD_TYPES } from "./features/store-extensions/schemas/analytics-forms";
163
+ export { QuickFormDrawer } from "./features/shell/QuickFormDrawer";
147
164
  export { PrizeDrawLotteryDetailView } from "./_internal/client/features/lottery/PrizeDrawLotteryDetailView";
148
165
  // [CLIENT-ONLY]-Cannot run in SSR mode â€" uses browser-only APIs (window, navigator, localStorage, matchMedia, DOM events) that do not exist in Node.js.
149
166
  // BottomSheet - Shared export for bottom sheet.
@@ -258,10 +275,19 @@ export { useCollapsedSections } from "./features/account/hooks/useCollapsedSecti
258
275
  export { PageViewTracker } from "./features/analytics/components/PageViewTracker";
259
276
  export { PAGE_VIEW_ENTITY_TYPES } from "./features/analytics/types";
260
277
  export { AuctionBidsTable } from "./features/auctions/components/AuctionBidsTable";
278
+ // The ONE bid-detail field builder, shared by all three portals' list modals
279
+ // and — since W22 — their detail pages. `viewer` is what decides whether the
280
+ // bidder's identity is shown, so keeping it in one place is what stops a
281
+ // buyer-facing surface ever rendering another bidder's name.
282
+ export { buildBidDetailFields, bidStatusBadge } from "./features/auctions/components/bid-detail-fields";
283
+ // Same reason, one feature over: the entry detail modal and its full page
284
+ // render from one builder rather than two lists that can drift.
285
+ export { buildEventEntryDetailFields } from "./features/events/components/event-entry-detail-fields";
261
286
  export { ProtectedRoute, AuthStatusPanel, ForgotPasswordView, LoginForm, RegisterForm, ResetPasswordView, VerifyEmailView } from "./features/auth/index";
262
287
  export { useLogout, useLogin, useGoogleLogin, useLinkGoogleAccount, useRegister, useForgotPassword, useResetPassword, useVerifyEmail, useChangeEmail } from "./features/auth/index";
263
288
  export { CartView, CartItemRow, CartGroupLineRow, CartSummary, CartPriceBreakdown, CartDrawer, CheckoutView, CheckoutSuccessView, CheckoutAddressStep, useGuestCart, useCartCount, useAddToCart, useCart, useGuestCartMerge, useCartQuery } from "./features/cart/index";
264
- export { StoreAddonsPicker } from "./features/cart/index";
289
+ export { StoreAddonsPicker, hasAnyStoreAddon } from "./features/cart/index";
290
+ export { clientLineTotal } from "./features/cart/utils/line-total";
265
291
  export { getCartOps, CART_OPS_CHANGE_EVENT } from "./features/cart/utils/pending-ops";
266
292
  export { useAddresses, useCreateAddress, useUpdateAddress, useDeleteAddress, useSetDefaultAddress, useAddress } from "./features/account/index";
267
293
  export { AddressBook, AddressCard, AddressForm } from "./features/account/index";
@@ -278,10 +304,14 @@ export { useActiveAd } from "./features/homepage/hooks/useActiveAd";
278
304
  export { registerAdSlot, registerAdSlots, unregisterAdSlot, clearAdRegistry, setAdConsentGranted, isAdConsentGranted, isAdSlotRenderable, } from "./features/homepage/ad-registry";
279
305
  export { WishlistView, useGuestWishlist, useWishlistWithGuest } from "./features/wishlist/index";
280
306
  export { WishlistCapWatcher } from "./features/wishlist/components/WishlistCapWatcher";
307
+ // The single wishlist heart. Exported here too because it is a "use client"
308
+ // UI component and consumer client code imports from this entry.
309
+ export { WishlistHeartButton } from "./features/wishlist/components/WishlistHeartButton";
281
310
  export { SyncManagerMount } from "./core/components/SyncManagerMount";
282
311
  export { useWishlistCount, useWishlistCountWithLimit, WISHLIST_CAP_EVENT } from "./features/wishlist/hooks/useWishlistCount";
283
312
  export { getGuestWishlistItems, addToGuestWishlist, removeFromGuestWishlist, isInGuestWishlist, clearGuestWishlist, getGuestWishlistCount, getGuestWishlistByType, } from "./features/wishlist/utils/guest-wishlist";
284
313
  export { InteractiveProductCard } from "./features/products/index";
314
+ export { GroupedListingEditorView } from "./features/grouped/components/GroupedListingEditorView";
285
315
  export { GroupedListingDetailView } from "./features/grouped/components/GroupedListingDetailView";
286
316
  export { GroupMemberPicker } from "./features/products/components/GroupMemberPicker";
287
317
  export { MarketplaceBundleCard } from "./features/products/components/MarketplaceBundleCard";
@@ -400,7 +430,12 @@ export { normalizeError, getErrorMessage, isApiNormalized, isAppNormalized, isFi
400
430
  // never touches the sibling crypto-importing helpers.
401
431
  export { buildPaymentProofReviewMessage } from "./features/whatsapp-bot/helpers/payment-proof-message";
402
432
  export { CommandPalette, useCommandPaletteHotkey } from "./features/shell/index";
403
- export { AdminBlogEditorView, AdminBundleEditorView, AdminCarouselEditorView, AdminCategoryEditorView, AdminCouponEditorView, AdminFaqEditorView, AdminFeatureEditorView, AdminOrderEditorView, AdminPayoutMarkPaidModal, AdminAuditLogView, ViewAuditLogEntryModal, AdminProductEditorView, AdminScammerEditorView, AdminStoreEditorView, AdminSublistingCategoryEditorView, AdminSupportTicketDetailView, } from "./features/admin/index";
433
+ export { AdminBlogEditorView, AdminBundleEditorView, AdminCarouselEditorView, AdminCategoryEditorView, AdminCouponEditorView, AdminFaqEditorView, AdminFeatureEditorView, AdminOrderEditorView, AdminPayoutMarkPaidModal, AdminAuditLogView, ViewAuditLogEntryModal, AdminProductEditorView, AdminScammerEditorView,
434
+ // Client-exported for the W22 /admin/team/{new,[id]/edit} pages. It was
435
+ // reachable only from `index.ts` (the SERVER entry), which a "use client"
436
+ // page must never import — that is the Turbopack client-bundle trap.
437
+ AdminEmployeeEditorView, AdminNavEditorView, AdminUserEditorView, AdminStoreEditorView, AdminSublistingCategoryEditorView, AdminSupportTicketDetailView, } from "./features/admin/index";
438
+ export { PageLoader } from "./ui/index";
404
439
  export { ADMIN_CHECKOUT_BYPASS_FLAG_KEY } from "./features/admin/schemas/firestore";
405
440
  export { SpinWheelView, EventRaffleEntryForm } from "./features/events/index";
406
441
  export { Card, CardBody } from "./ui/index";
@@ -25,7 +25,6 @@ export declare const AUTH_ENDPOINTS: {
25
25
  readonly SESSION_ACTIVITY: "/api/auth/session/activity";
26
26
  readonly SESSION_VALIDATE: "/api/auth/session/validate";
27
27
  readonly EVENT_INIT: "/api/auth/event/init";
28
- readonly RESEND_VERIFICATION: "/api/auth/resend-verification";
29
28
  };
30
29
  export declare const ACCOUNT_ENDPOINTS: {
31
30
  /** @param userId — Firestore user document id */
@@ -51,6 +50,7 @@ export declare const ACCOUNT_ENDPOINTS: {
51
50
  readonly HISTORY_MERGE: "/api/user/history/merge";
52
51
  readonly REVIEWS: "/api/user/reviews";
53
52
  readonly BIDS: "/api/user/bids";
53
+ readonly BID_BY_ID: (id: string) => string;
54
54
  readonly EXPORT: "/api/user/export";
55
55
  /** Claimed-coupons wallet — GET list, POST claim, DELETE by id. */
56
56
  readonly COUPONS: "/api/user/coupons";
@@ -150,6 +150,12 @@ export declare const ADMIN_ENDPOINTS: {
150
150
  readonly EVENTS: "/api/admin/events";
151
151
  readonly EVENT_BY_ID: (id: string) => string;
152
152
  readonly EVENT_STATUS: (id: string) => string;
153
+ /**
154
+ * The ONLY sanctioned writer of `lotteryConfig`. The generic EVENT_BY_ID
155
+ * PATCH rejects that field — it is `.passthrough()`, and letting a slot
156
+ * array through it erased every already-pulled slot.
157
+ */
158
+ readonly EVENT_LOTTERY_CONFIG: (id: string) => string;
153
159
  readonly EVENT_STATS: (id: string) => string;
154
160
  readonly EVENT_ENTRIES: (id: string) => string;
155
161
  readonly EVENT_ENTRIES_EXPORT: (id: string) => string;
@@ -488,7 +494,6 @@ export declare const API_ENDPOINTS: {
488
494
  readonly SESSION_ACTIVITY: "/api/auth/session/activity";
489
495
  readonly SESSION_VALIDATE: "/api/auth/session/validate";
490
496
  readonly EVENT_INIT: "/api/auth/event/init";
491
- readonly RESEND_VERIFICATION: "/api/auth/resend-verification";
492
497
  };
493
498
  readonly ACCOUNT: {
494
499
  /** @param userId — Firestore user document id */
@@ -514,6 +519,7 @@ export declare const API_ENDPOINTS: {
514
519
  readonly HISTORY_MERGE: "/api/user/history/merge";
515
520
  readonly REVIEWS: "/api/user/reviews";
516
521
  readonly BIDS: "/api/user/bids";
522
+ readonly BID_BY_ID: (id: string) => string;
517
523
  readonly EXPORT: "/api/user/export";
518
524
  /** Claimed-coupons wallet — GET list, POST claim, DELETE by id. */
519
525
  readonly COUPONS: "/api/user/coupons";
@@ -613,6 +619,12 @@ export declare const API_ENDPOINTS: {
613
619
  readonly EVENTS: "/api/admin/events";
614
620
  readonly EVENT_BY_ID: (id: string) => string;
615
621
  readonly EVENT_STATUS: (id: string) => string;
622
+ /**
623
+ * The ONLY sanctioned writer of `lotteryConfig`. The generic EVENT_BY_ID
624
+ * PATCH rejects that field — it is `.passthrough()`, and letting a slot
625
+ * array through it erased every already-pulled slot.
626
+ */
627
+ readonly EVENT_LOTTERY_CONFIG: (id: string) => string;
616
628
  readonly EVENT_STATS: (id: string) => string;
617
629
  readonly EVENT_ENTRIES: (id: string) => string;
618
630
  readonly EVENT_ENTRIES_EXPORT: (id: string) => string;
@@ -953,7 +965,6 @@ export declare const API_ROUTES: {
953
965
  readonly SESSION_ACTIVITY: "/api/auth/session/activity";
954
966
  readonly SESSION_VALIDATE: "/api/auth/session/validate";
955
967
  readonly EVENT_INIT: "/api/auth/event/init";
956
- readonly RESEND_VERIFICATION: "/api/auth/resend-verification";
957
968
  };
958
969
  readonly ACCOUNT: {
959
970
  /** @param userId — Firestore user document id */
@@ -979,6 +990,7 @@ export declare const API_ROUTES: {
979
990
  readonly HISTORY_MERGE: "/api/user/history/merge";
980
991
  readonly REVIEWS: "/api/user/reviews";
981
992
  readonly BIDS: "/api/user/bids";
993
+ readonly BID_BY_ID: (id: string) => string;
982
994
  readonly EXPORT: "/api/user/export";
983
995
  /** Claimed-coupons wallet — GET list, POST claim, DELETE by id. */
984
996
  readonly COUPONS: "/api/user/coupons";
@@ -1078,6 +1090,12 @@ export declare const API_ROUTES: {
1078
1090
  readonly EVENTS: "/api/admin/events";
1079
1091
  readonly EVENT_BY_ID: (id: string) => string;
1080
1092
  readonly EVENT_STATUS: (id: string) => string;
1093
+ /**
1094
+ * The ONLY sanctioned writer of `lotteryConfig`. The generic EVENT_BY_ID
1095
+ * PATCH rejects that field — it is `.passthrough()`, and letting a slot
1096
+ * array through it erased every already-pulled slot.
1097
+ */
1098
+ readonly EVENT_LOTTERY_CONFIG: (id: string) => string;
1081
1099
  readonly EVENT_STATS: (id: string) => string;
1082
1100
  readonly EVENT_ENTRIES: (id: string) => string;
1083
1101
  readonly EVENT_ENTRIES_EXPORT: (id: string) => string;
@@ -37,7 +37,13 @@ export const AUTH_ENDPOINTS = {
37
37
  SESSION_ACTIVITY: "/api/auth/session/activity",
38
38
  SESSION_VALIDATE: "/api/auth/session/validate",
39
39
  EVENT_INIT: "/api/auth/event/init",
40
- RESEND_VERIFICATION: "/api/auth/resend-verification",
40
+ /*
41
+ * RESEND_VERIFICATION was here and is gone. The route was deleted in the
42
+ * Firebase-native auth migration (Root Cause #54: the Admin SDK cannot send
43
+ * email, only generate action links), and the constant outlived it — so the
44
+ * one hook that used it 404'd on every call. `useResendVerification` now
45
+ * calls the client SDK directly and needs no endpoint. Do not re-add this.
46
+ */
41
47
  };
42
48
  // ---------------------------------------------------------------------------
43
49
  // User / Account
@@ -66,6 +72,7 @@ export const ACCOUNT_ENDPOINTS = {
66
72
  HISTORY_MERGE: "/api/user/history/merge",
67
73
  REVIEWS: "/api/user/reviews",
68
74
  BIDS: "/api/user/bids",
75
+ BID_BY_ID: (id) => `/api/user/bids/${id}`,
69
76
  EXPORT: "/api/user/export",
70
77
  /** Claimed-coupons wallet — GET list, POST claim, DELETE by id. */
71
78
  COUPONS: "/api/user/coupons",
@@ -177,6 +184,12 @@ export const ADMIN_ENDPOINTS = {
177
184
  EVENTS: "/api/admin/events",
178
185
  EVENT_BY_ID: (id) => `/api/admin/events/${id}`,
179
186
  EVENT_STATUS: (id) => `/api/admin/events/${id}/status`,
187
+ /**
188
+ * The ONLY sanctioned writer of `lotteryConfig`. The generic EVENT_BY_ID
189
+ * PATCH rejects that field — it is `.passthrough()`, and letting a slot
190
+ * array through it erased every already-pulled slot.
191
+ */
192
+ EVENT_LOTTERY_CONFIG: (id) => `/api/admin/events/${id}/lottery-config`,
180
193
  EVENT_STATS: (id) => `/api/admin/events/${id}/stats`,
181
194
  EVENT_ENTRIES: (id) => `/api/admin/events/${id}/entries`,
182
195
  EVENT_ENTRIES_EXPORT: (id) => `/api/admin/events/${id}/entries/export`,
@@ -122,7 +122,7 @@ export declare const PRODUCT_FIELDS: {
122
122
  * matches zero documents — always map across via this constant.
123
123
  */
124
124
  readonly SUBLISTING_CATEGORY_ID: "sublistingCategoryId";
125
- readonly SEARCH_TOKENS: "searchTokens";
125
+ readonly SEARCH_TXT: "searchTxt";
126
126
  readonly CREATED_AT: "createdAt";
127
127
  readonly UPDATED_AT: "updatedAt";
128
128
  readonly STATUS_VALUES: {
@@ -812,7 +812,7 @@ export declare const FAQ_FIELDS: {
812
812
  readonly ORDER: "order";
813
813
  readonly PRIORITY: "priority";
814
814
  readonly TAGS: "tags";
815
- readonly SEARCH_TOKENS: "searchTokens";
815
+ readonly SEARCH_TXT: "searchTxt";
816
816
  readonly RELATED_FAQS: "relatedFAQs";
817
817
  readonly USE_SITE_SETTINGS: "useSiteSettings";
818
818
  readonly VARIABLES: "variables";
@@ -131,7 +131,7 @@ export const PRODUCT_FIELDS = {
131
131
  * matches zero documents — always map across via this constant.
132
132
  */
133
133
  SUBLISTING_CATEGORY_ID: "sublistingCategoryId",
134
- SEARCH_TOKENS: "searchTokens",
134
+ SEARCH_TXT: "searchTxt",
135
135
  CREATED_AT: "createdAt",
136
136
  UPDATED_AT: "updatedAt",
137
137
  STATUS_VALUES: {
@@ -949,7 +949,7 @@ export const FAQ_FIELDS = {
949
949
  ORDER: "order",
950
950
  PRIORITY: "priority",
951
951
  TAGS: "tags",
952
- SEARCH_TOKENS: "searchTokens",
952
+ SEARCH_TXT: "searchTxt",
953
953
  RELATED_FAQS: "relatedFAQs",
954
954
  USE_SITE_SETTINGS: "useSiteSettings",
955
955
  VARIABLES: "variables",
@@ -16,7 +16,20 @@ export const NEWSLETTER_SUBSCRIBER_FIELDS = {
16
16
  POPUP: "popup",
17
17
  },
18
18
  };
19
- const NEWSLETTER_PII_FIELDS = [];
19
+ /**
20
+ * Subscriber email, encrypted at rest.
21
+ *
22
+ * Was an empty array, so the encrypt/decrypt calls wrapped around it were both
23
+ * no-ops and every subscriber address sat in cleartext — with a perfectly good
24
+ * `emailIndex` beside it.
25
+ *
26
+ * Safe to enable because the lookup path never depended on the plaintext:
27
+ * `emailIndex` is written explicitly on create and queried first on read, with
28
+ * a plaintext-equality fallback for rows written before this, and
29
+ * `decryptNewsletterDoc` already covers all eight read sites. `encryptPiiFields`
30
+ * derives the same `emailIndex` name, so the two agree rather than conflict.
31
+ */
32
+ const NEWSLETTER_PII_FIELDS = ["email"];
20
33
  function resolveRepository() {
21
34
  const provider = getProviders().db;
22
35
  if (!provider) {
@@ -25,7 +25,7 @@ export async function FAQPageView({ category, faqs = [], } = {}) {
25
25
  : [];
26
26
  const visibleFaqs = (category ? faqs.filter((f) => f.category === category) : faqs).map(toDisplayFaq);
27
27
  const activeCategory = categories.find((c) => c.slug === category);
28
- return (_jsxs(Div, { className: "-mx-4 md:-mx-6 lg:-mx-8 -mt-6 sm:-mt-8 lg:-mt-10", children: [_jsx(Section, { color: "inverse", tone: "accent-banner", padding: "banner", children: _jsxs(Container, { size: "md", className: "text-center", children: [_jsx(Heading, { color: "inverse", level: 1, variant: "none", className: "mb-3", children: activeCategory ? activeCategory.label : t("title") }), _jsx(Text, { color: "inverse", variant: "none", className: "/80 max-w-2xl mx-auto", children: t("subtitle") })] }) }), _jsx(Container, { size: "md", padding: "content-banner", children: _jsxs(Stack, { direction: "md-row", gap: "xl", children: [categories.length > 0 && (_jsxs(Aside, { className: "md:w-56 flex-shrink-0", children: [_jsx(Heading, { level: 3, className: "uppercase tracking-wide mb-3", color: "muted", size: "sm", weight: "semibold", children: t("categoriesLabel") }), _jsxs(Nav, { "aria-label": "FAQ categories", spacing: "xs", children: [_jsxs(TextLink, { rounded: "lg", href: String(ROUTES.PUBLIC.FAQS), paddingX: "sm", paddingY: "xs", layout: "flex", align: "center", justify: "between", weight: !category ? "semibold" : undefined, className: `transition-colors ${!category ? "bg-primary/10 text-primary" : "hover:bg-[var(--appkit-color-surface)] hover:bg-[var(--appkit-color-surface-elevated)]"}`, size: "sm", children: [_jsxs(Span, { className: `${flex.row}`, gap: "md", children: [_jsx(HelpCircle, { className: "w-4 h-4" }), t("allCategories")] }), _jsx(ChevronRight, { className: "w-3.5 h-3.5 opacity-50" })] }), categories.map((cat) => (_jsxs(TextLink, { rounded: "lg", href: String(ROUTES.PUBLIC.FAQ_CATEGORY(cat.slug)), paddingX: "sm", paddingY: "xs", layout: "flex", align: "center", justify: "between", weight: category === cat.slug ? "semibold" : undefined, className: `transition-colors ${category === cat.slug ? "bg-primary/10 text-primary" : "hover:bg-[var(--appkit-color-surface)] hover:bg-[var(--appkit-color-surface-elevated)]"}`, size: "sm", children: [_jsxs(Span, { className: `${flex.row}`, gap: "md", children: [_jsx(Span, { children: cat.icon }), cat.label] }), _jsx(ChevronRight, { className: "w-3.5 h-3.5 opacity-50" })] }, cat.slug)))] })] })), _jsxs(Div, { className: "flex-1 min-w-0", children: [visibleFaqs.length === 0 ? (_jsxs(Section, { className: `text-center`, border: "default", surface: "subtle", rounded: "2xl", padding: "y-4xl", children: [_jsx(HelpCircle, { className: "w-10 h-10 mx-auto mb-3 text-zinc-300 dark:text-[var(--appkit-color-text-muted)]" }), _jsx(Heading, { level: 3, className: "mb-2", size: "base", children: t("emptyTitle") }), _jsx(Text, { variant: "secondary", size: "sm", children: t("emptyText") })] })) : (_jsx(FAQSearchableList, { faqs: visibleFaqs, labels: {
28
+ return (_jsxs(Div, { className: "-mx-4 md:-mx-6 lg:-mx-8 -mt-6 sm:-mt-8 lg:-mt-10", children: [_jsx(Section, { color: "inverse", tone: "accent-banner", padding: "banner", children: _jsxs(Container, { size: "md", className: "text-center", children: [_jsx(Heading, { color: "inverse", level: 1, variant: "none", className: "mb-3", children: activeCategory ? activeCategory.label : t("title") }), _jsx(Text, { color: "inverse", variant: "none", className: "/80 max-w-2xl mx-auto", children: t("subtitle") })] }) }), _jsx(Container, { size: "md", padding: "content-banner", children: _jsxs(Stack, { direction: "md-row", gap: "xl", children: [categories.length > 0 && (_jsxs(Aside, { className: "md:w-56 flex-shrink-0", children: [_jsx(Heading, { level: 3, className: "uppercase tracking-wide mb-3", color: "muted", size: "sm", weight: "semibold", children: t("categoriesLabel") }), _jsxs(Nav, { "aria-label": "FAQ categories", spacing: "xs", children: [_jsxs(TextLink, { rounded: "lg", href: String(ROUTES.PUBLIC.FAQS), paddingX: "sm", paddingY: "xs", layout: "flex", align: "center", justify: "between", weight: !category ? "semibold" : undefined, className: `transition-colors ${!category ? "bg-primary/10 text-primary" : "hover:bg-surface-hover"}`, size: "sm", children: [_jsxs(Span, { className: `${flex.row}`, gap: "md", children: [_jsx(HelpCircle, { className: "w-4 h-4" }), t("allCategories")] }), _jsx(ChevronRight, { className: "w-3.5 h-3.5 opacity-50" })] }), categories.map((cat) => (_jsxs(TextLink, { rounded: "lg", href: String(ROUTES.PUBLIC.FAQ_CATEGORY(cat.slug)), paddingX: "sm", paddingY: "xs", layout: "flex", align: "center", justify: "between", weight: category === cat.slug ? "semibold" : undefined, className: `transition-colors ${category === cat.slug ? "bg-primary/10 text-primary" : "hover:bg-surface-hover"}`, size: "sm", children: [_jsxs(Span, { className: `${flex.row}`, gap: "md", children: [_jsx(Span, { children: cat.icon }), cat.label] }), _jsx(ChevronRight, { className: "w-3.5 h-3.5 opacity-50" })] }, cat.slug)))] })] })), _jsxs(Div, { className: "flex-1 min-w-0", children: [visibleFaqs.length === 0 ? (_jsxs(Section, { className: `text-center`, border: "default", surface: "subtle", rounded: "2xl", padding: "y-4xl", children: [_jsx(HelpCircle, { className: "w-10 h-10 mx-auto mb-3 text-zinc-300 dark:text-[var(--appkit-color-text-muted)]" }), _jsx(Heading, { level: 3, className: "mb-2", size: "base", children: t("emptyTitle") }), _jsx(Text, { variant: "secondary", size: "sm", children: t("emptyText") })] })) : (_jsx(FAQSearchableList, { faqs: visibleFaqs, labels: {
29
29
  searchPlaceholder: t("searchPlaceholder"),
30
30
  noResults: t("searchNoResults"),
31
31
  } })), _jsxs(Section, { className: `mt-10 text-center`, border: "default", surface: "subtle", rounded: "2xl", padding: "lg", children: [_jsx(Heading, { level: 3, className: "mb-2", size: "base", children: t("stillNeedHelpTitle") }), _jsx(Text, { variant: "secondary", className: "mb-4", size: "sm", children: t("stillNeedHelpText") }), _jsxs(Row, { align: "center", justify: "center", gap: "md", wrap: true, children: [_jsx(TextLink, { href: String(ROUTES.PUBLIC.HELP), children: t("helpCenter") }), _jsx(TextLink, { href: String(ROUTES.PUBLIC.CONTACT), variant: "muted", children: t("contactUs") })] })] })] })] }) })] }));
@@ -95,7 +95,7 @@ export async function FeesView({} = {}) {
95
95
  return (_jsxs(Div, { className: "-mx-4 md:-mx-6 lg:-mx-8 -mt-6 sm:-mt-8 lg:-mt-10", children: [_jsx(Section, { color: "inverse", tone: "accent-banner", padding: "banner", children: _jsxs(Div, { className: `${page.container.sm} text-center`, children: [_jsx(Heading, { color: "inverse", level: 1, variant: "none", className: "mb-4", children: t("title") }), _jsx(Text, { color: "inverse", variant: "none", className: "/80 max-w-2xl mx-auto", children: t("subtitle") })] }) }), _jsxs(Stack, { gap: "section", className: `${page.container.sm}`, padding: "content-banner", children: [renderFeeTableSection(t, themed, FEE_ROWS), renderPayoutExampleSection(t, themed, OFFER_PAYOUT_ROWS), renderDisclaimerSection(t, themed)] })] }));
96
96
  }
97
97
  function renderFeeTableSection(t, themed, rows) {
98
- return (_jsxs(Section, { children: [_jsx(Heading, { level: 2, className: "mb-6", children: t("tableTitle") }), _jsx(Div, { className: `${__O.xAuto}`, border: "default", rounded: "xl", children: _jsxs(Table, { size: "sm", children: [_jsx(Thead, { className: themed.bgSecondary, children: _jsxs(Tr, { children: [_jsx(Th, { className: "text-left", padding: "md", weight: "semibold", children: t("colFeeType") }), _jsx(Th, { className: "text-left", padding: "md", weight: "semibold", children: t("colRate") }), _jsx(Th, { className: "text-left", padding: "md", weight: "semibold", children: t("colPaidBy") }), _jsx(Th, { className: "text-left hidden md:table-cell", padding: "md", weight: "semibold", children: t("colNote") })] }) }), _jsx(Tbody, { className: "divide-y divide-neutral-100 divide-[var(--appkit-color-border)]", children: rows.map((row) => (_jsxs(Tr, { className: `${themed.bgPrimary} hover:bg-neutral-50 hover:bg-[var(--appkit-color-surface-elevated)]/50`, children: [_jsx(Td, { weight: "medium", padding: "md", children: row.category }), _jsx(Td, { className: CLS_RATE_CELL, children: row.rate }), _jsx(Td, { padding: "md", children: _jsx(Caption, { children: row.who }) }), _jsx(Td, { className: "text-[var(--appkit-color-text-muted)] hidden md:table-cell", padding: "md", size: "xs", children: row.note })] }, row.category))) })] }) }), _jsx(Caption, { className: "mt-3 block", color: "muted", children: t("tableNote") })] }));
98
+ return (_jsxs(Section, { children: [_jsx(Heading, { level: 2, className: "mb-6", children: t("tableTitle") }), _jsx(Div, { className: `${__O.xAuto}`, border: "default", rounded: "xl", children: _jsxs(Table, { size: "sm", children: [_jsx(Thead, { className: themed.bgSecondary, children: _jsxs(Tr, { children: [_jsx(Th, { className: "text-left", padding: "md", weight: "semibold", children: t("colFeeType") }), _jsx(Th, { className: "text-left", padding: "md", weight: "semibold", children: t("colRate") }), _jsx(Th, { className: "text-left", padding: "md", weight: "semibold", children: t("colPaidBy") }), _jsx(Th, { className: "text-left hidden md:table-cell", padding: "md", weight: "semibold", children: t("colNote") })] }) }), _jsx(Tbody, { className: "divide-y divide-neutral-100 divide-[var(--appkit-color-border)]", children: rows.map((row) => (_jsxs(Tr, { className: `${themed.bgPrimary} hover:bg-surface-hover`, children: [_jsx(Td, { weight: "medium", padding: "md", children: row.category }), _jsx(Td, { className: CLS_RATE_CELL, children: row.rate }), _jsx(Td, { padding: "md", children: _jsx(Caption, { children: row.who }) }), _jsx(Td, { className: "text-[var(--appkit-color-text-muted)] hidden md:table-cell", padding: "md", size: "xs", children: row.note })] }, row.category))) })] }) }), _jsx(Caption, { className: "mt-3 block", color: "muted", children: t("tableNote") })] }));
99
99
  }
100
100
  function renderPayoutExampleSection(t, themed, rows) {
101
101
  return (_jsxs(Section, { children: [_jsx(Heading, { level: 2, className: "mb-3", children: t("payoutExampleTitle") }), _jsx(Text, { variant: "secondary", className: "mb-6", children: t("payoutExampleSubtitle") }), _jsxs(Div, { className: `${__P.p5} max-w-sm`, border: "default", surface: "muted", rounded: "xl", children: [_jsx(Heading, { level: 3, className: "mb-4", size: "base", children: t("payoutExampleProduct") }), _jsx(Stack, { gap: "sm", children: rows.map((row, i) => (_jsxs(Row, { textWeight: "bold", textSize: "sm", className: `${row.highlight ? "border-t border-[var(--appkit-color-border)] mt-2" : ""}`, paddingY: row.highlight ? "t-xs" : undefined, justify: "between", children: [_jsx(Text, { className: row.highlight ? "font-bold" : "text-[var(--appkit-color-text-muted)]", children: row.label }), _jsx(Text, { className: row.highlight ? CLS_HIGHLIGHT : "", children: row.example })] }, i))) })] })] }));
@@ -5,6 +5,7 @@ export interface AddressFormLabels {
5
5
  phone: string;
6
6
  addressLine1: string;
7
7
  addressLine2: string;
8
+ landmark: string;
8
9
  city: string;
9
10
  state: string;
10
11
  postalCode: string;
@@ -20,6 +21,7 @@ export interface AddressFormPlaceholders {
20
21
  phone: string;
21
22
  addressLine1: string;
22
23
  addressLine2: string;
24
+ landmark: string;
23
25
  city: string;
24
26
  state: string;
25
27
  postalCode: string;
@@ -15,6 +15,7 @@ const addressFormSchema = z.object({
15
15
  phone: z.string().min(1, "Phone number is required"),
16
16
  addressLine1: z.string().min(1, "Address is required"),
17
17
  addressLine2: z.string().optional(),
18
+ landmark: z.string().max(100, "Keep the landmark under 100 characters.").optional(),
18
19
  city: z.string().min(1, "City is required"),
19
20
  state: z.string().min(1, "State is required"),
20
21
  postalCode: z.string().min(1, "Postal code is required"),
@@ -27,6 +28,7 @@ const DEFAULT_LABELS = {
27
28
  phone: "Phone Number",
28
29
  addressLine1: "Address Line 1",
29
30
  addressLine2: "Address Line 2",
31
+ landmark: "Landmark",
30
32
  city: "City",
31
33
  state: "State",
32
34
  postalCode: "Postal Code",
@@ -42,6 +44,7 @@ const DEFAULT_PLACEHOLDERS = {
42
44
  phone: "Enter phone number",
43
45
  addressLine1: "Street address, P.O. box",
44
46
  addressLine2: "Apartment, suite, unit, building, floor, etc.",
47
+ landmark: "Opposite the Metro station, next to the temple, etc.",
45
48
  city: "City",
46
49
  state: "State/Province",
47
50
  postalCode: "Postal/ZIP code",
@@ -58,6 +61,7 @@ export function AddressForm({ initialData, onSubmit, onCancel, isLoading = false
58
61
  phone: initialData?.phone || "",
59
62
  addressLine1: initialData?.addressLine1 || "",
60
63
  addressLine2: initialData?.addressLine2 || "",
64
+ landmark: initialData?.landmark || "",
61
65
  city: initialData?.city || "",
62
66
  state: initialData?.state || "",
63
67
  postalCode: initialData?.postalCode || "",
@@ -114,5 +118,5 @@ export function AddressForm({ initialData, onSubmit, onCancel, isLoading = false
114
118
  submitLabel: effectiveSubmitLabel,
115
119
  cancelLabel: mergedLabels.cancel,
116
120
  isLoading,
117
- }, children: ({ setFieldError, clearErrors }) => (_jsxs(_Fragment, { children: [_jsx(FormField, { label: mergedLabels.label, name: "label", type: "text", value: formData.label, onChange: (value) => handleChange("label", value), placeholder: mergedPlaceholders.label, required: true }), _jsx(FormField, { label: mergedLabels.fullName, name: "fullName", type: "text", value: formData.fullName, onChange: (value) => handleChange("fullName", value), placeholder: mergedPlaceholders.fullName, required: true }), _jsx(FormField, { label: mergedLabels.phone, name: "phone", type: "tel", value: formData.phone, onChange: (value) => handleChange("phone", value), placeholder: mergedPlaceholders.phone, required: true }), _jsx(FormField, { label: mergedLabels.addressLine1, name: "addressLine1", type: "text", value: formData.addressLine1, onChange: (value) => handleChange("addressLine1", value), placeholder: mergedPlaceholders.addressLine1, required: true }), _jsx(FormField, { label: mergedLabels.addressLine2, name: "addressLine2", type: "text", value: formData.addressLine2, onChange: (value) => handleChange("addressLine2", value), placeholder: mergedPlaceholders.addressLine2 }), _jsxs(FormGroup, { columns: 3, children: [_jsx(FormField, { label: mergedLabels.city, name: "city", type: "text", value: formData.city, onChange: (value) => handleChange("city", value), placeholder: mergedPlaceholders.city, required: true }), _jsx(FormField, { label: mergedLabels.state, name: "state", type: "text", value: formData.state, onChange: (value) => handleChange("state", value), placeholder: mergedPlaceholders.state, required: true }), _jsx(FormField, { label: mergedLabels.postalCode, name: "postalCode", type: "text", value: formData.postalCode, onChange: (value) => handleChange("postalCode", value), placeholder: mergedPlaceholders.postalCode, required: true })] }), _jsx(FormField, { label: mergedLabels.country, name: "country", type: "text", value: formData.country, onChange: (value) => handleChange("country", value), placeholder: mergedPlaceholders.country, required: true }), _jsx(Checkbox, { checked: formData.isDefault, onChange: (e) => handleChange("isDefault", e.target.checked), label: mergedLabels.setDefault }), _jsx(FormErrorSummary, {}), _jsxs(Row, { padding: "t-xs", align: "center", justify: "start", gap: "xs", children: [_jsx(Button, { type: "button", variant: "outline", onClick: onCancel, disabled: isLoading, children: mergedLabels.cancel }), _jsx(Button, { type: "submit", variant: "primary", disabled: isLoading, isLoading: isLoading, onClick: () => void submitAddress({ setFieldError, clearErrors }), children: isLoading ? mergedLabels.loading : effectiveSubmitLabel })] })] })) }));
121
+ }, children: ({ setFieldError, clearErrors }) => (_jsxs(_Fragment, { children: [_jsx(FormField, { label: mergedLabels.label, name: "label", type: "text", value: formData.label, onChange: (value) => handleChange("label", value), placeholder: mergedPlaceholders.label, required: true }), _jsx(FormField, { label: mergedLabels.fullName, name: "fullName", type: "text", value: formData.fullName, onChange: (value) => handleChange("fullName", value), placeholder: mergedPlaceholders.fullName, required: true }), _jsx(FormField, { label: mergedLabels.phone, name: "phone", type: "tel", value: formData.phone, onChange: (value) => handleChange("phone", value), placeholder: mergedPlaceholders.phone, required: true }), _jsx(FormField, { label: mergedLabels.addressLine1, name: "addressLine1", type: "text", value: formData.addressLine1, onChange: (value) => handleChange("addressLine1", value), placeholder: mergedPlaceholders.addressLine1, required: true }), _jsx(FormField, { label: mergedLabels.addressLine2, name: "addressLine2", type: "text", value: formData.addressLine2, onChange: (value) => handleChange("addressLine2", value), placeholder: mergedPlaceholders.addressLine2 }), _jsx(FormField, { label: mergedLabels.landmark, name: "landmark", type: "text", value: formData.landmark ?? "", onChange: (value) => handleChange("landmark", value), placeholder: mergedPlaceholders.landmark }), _jsxs(FormGroup, { columns: 3, children: [_jsx(FormField, { label: mergedLabels.city, name: "city", type: "text", value: formData.city, onChange: (value) => handleChange("city", value), placeholder: mergedPlaceholders.city, required: true }), _jsx(FormField, { label: mergedLabels.state, name: "state", type: "text", value: formData.state, onChange: (value) => handleChange("state", value), placeholder: mergedPlaceholders.state, required: true }), _jsx(FormField, { label: mergedLabels.postalCode, name: "postalCode", type: "text", value: formData.postalCode, onChange: (value) => handleChange("postalCode", value), placeholder: mergedPlaceholders.postalCode, required: true })] }), _jsx(FormField, { label: mergedLabels.country, name: "country", type: "text", value: formData.country, onChange: (value) => handleChange("country", value), placeholder: mergedPlaceholders.country, required: true }), _jsx(Checkbox, { checked: formData.isDefault, onChange: (e) => handleChange("isDefault", e.target.checked), label: mergedLabels.setDefault }), _jsx(FormErrorSummary, {}), _jsxs(Row, { padding: "t-xs", align: "center", justify: "start", gap: "xs", children: [_jsx(Button, { type: "button", variant: "outline", onClick: onCancel, disabled: isLoading, children: mergedLabels.cancel }), _jsx(Button, { type: "submit", variant: "primary", disabled: isLoading, isLoading: isLoading, onClick: () => void submitAddress({ setFieldError, clearErrors }), children: isLoading ? mergedLabels.loading : effectiveSubmitLabel })] })] })) }));
118
122
  }
@@ -51,5 +51,5 @@ export function UserBidsView() {
51
51
  },
52
52
  };
53
53
  const badge = detail ? bidStatusBadge(detail.status) : null;
54
- return (_jsxs(_Fragment, { children: [_jsx(DataListingView, { config: config }), _jsx(RecordDetailModal, { isOpen: detail !== null, onClose: () => setDetail(null), title: detail?.productTitle || "Bid", badges: badge ? [{ label: badge.label }] : undefined, fields: detail ? buildBidDetailFields(detail, "buyer") : undefined, footer: detail ? (_jsx(TextLink, { href: String(ROUTES.PUBLIC.AUCTION_DETAIL(detail.productId)), children: "View auction \u2192" })) : undefined })] }));
54
+ return (_jsxs(_Fragment, { children: [_jsx(DataListingView, { config: config }), _jsx(RecordDetailModal, { isOpen: detail !== null, onClose: () => setDetail(null), title: detail?.productTitle || "Bid", badges: badge ? [{ label: badge.label }] : undefined, fields: detail ? buildBidDetailFields(detail, "buyer") : undefined, footer: detail ? (_jsxs(_Fragment, { children: [_jsx(TextLink, { href: String(ROUTES.USER.BID_DETAIL(detail.id)), children: "Open full page \u2192" }), _jsx(TextLink, { href: String(ROUTES.PUBLIC.AUCTION_DETAIL(detail.productId)), children: "View auction \u2192" })] })) : undefined })] }));
55
55
  }
@@ -27,7 +27,7 @@ function NavLink({ item, isActive, onClick }) {
27
27
  };
28
28
  const linkClass = `flex items-center justify-end gap-[var(--appkit-space-2-5)] rounded-lg px-[var(--appkit-space-3)] py-[var(--appkit-space-2)] text-[0.8125rem] font-medium leading-tight transition-colors ${isActive
29
29
  ? "bg-primary-50 dark:bg-primary-900/25 text-primary-700 dark:text-primary-300"
30
- : "text-[var(--appkit-color-text-muted)] hover:bg-zinc-50 hover:bg-[var(--appkit-color-surface-elevated)] hover:text-zinc-900 dark:hover:text-zinc-100"}`;
30
+ : "text-[var(--appkit-color-text-muted)] hover:bg-surface-hover hover:text-zinc-900 dark:hover:text-zinc-100"}`;
31
31
  return (_jsxs(_Fragment, { children: [_jsxs(Link, { href: item.href, onClick: handleClick, className: linkClass, children: [item.icon && _jsx(Span, { size: "base", className: "shrink-0 opacity-70", children: item.icon }), _jsx(Span, { className: "truncate", children: item.label })] }), showConfirm && item.confirm && (_jsx(ConfirmDeleteModal, { isOpen: true, title: item.confirm.title ?? "Confirm", message: item.confirm.message, confirmText: "Continue", variant: "warning", onConfirm: () => {
32
32
  setShowConfirm(false);
33
33
  onClick?.();
@@ -7,6 +7,8 @@ import { Button, Div, FormActions, Row, Select, SideDrawer, Span, Stack, Text, U
7
7
  import { FieldInput } from "../../../ui/forms/FieldInput";
8
8
  import { FieldTextarea } from "../../../ui/forms/FieldTextarea";
9
9
  import { apiClient } from "../../../http";
10
+ import { supportTicketCreateSchema } from "../../support/schemas/ticket-create-form";
11
+ import { ValidationError } from "../../../errors/validation-error";
10
12
  import { SUPPORT_ENDPOINTS } from "../../../constants/api-endpoints";
11
13
  const __P = {
12
14
  p3: "p-[var(--appkit-space-3)]",
@@ -33,7 +35,7 @@ const CATEGORY_OPTIONS = [
33
35
  { label: "Appeal a ban (unban request)", value: "unban_request" },
34
36
  { label: "General", value: "general" },
35
37
  ];
36
- const CLS_MSG_USER = "bg-zinc-50 border border-zinc-200 bg-[var(--appkit-color-surface)]/40 border-[var(--appkit-color-border)]";
38
+ const CLS_MSG_USER = "border bg-[var(--appkit-color-surface)]/40 border-[var(--appkit-color-border)]";
37
39
  const CLS_MSG_STAFF = "bg-info-surface border border-info dark:bg-info-surface dark:border-info";
38
40
  const STATUS_BADGE = {
39
41
  open: "bg-info-surface text-info",
@@ -68,12 +70,32 @@ export function UserSupportView(_props) {
68
70
  const invalidate = () => queryClient.invalidateQueries({ queryKey: ["user", "support-tickets"] });
69
71
  const createMutation = useApiMutation({
70
72
  mutationFn: async () => {
71
- await apiClient.post(SUPPORT_ENDPOINTS.TICKETS, {
73
+ /*
74
+ * This drawer validated NOTHING. Its sibling page
75
+ * (`/user/support/new`) hand-rolled a `canSubmit` boolean that required
76
+ * an order id for an `order_issue` ticket — a real rule, since an order
77
+ * complaint with no order is unactionable — and the route did not
78
+ * enforce it either. So the same ticket was acceptable or not purely
79
+ * depending on which surface the user happened to open.
80
+ *
81
+ * One schema now, across both surfaces and the route.
82
+ */
83
+ const parsed = supportTicketCreateSchema.safeParse({
72
84
  subject: newSubject.trim(),
73
85
  category: newCategory,
74
86
  description: newDescription.trim(),
75
87
  orderId: newOrderId.trim() || undefined,
76
88
  });
89
+ if (!parsed.success) {
90
+ /*
91
+ * ValidationError, not a bare Error — it carries the issue LIST, so
92
+ * the failure keeps which field caused it. A plain Error flattens the
93
+ * whole thing to one string, which is what `audit-unvalidated-safeparse`
94
+ * exists to stop. It caught this in my own code.
95
+ */
96
+ throw new ValidationError(parsed.error.issues[0]?.message ?? "Check the ticket details.", parsed.error.issues);
97
+ }
98
+ await apiClient.post(SUPPORT_ENDPOINTS.TICKETS, parsed.data);
77
99
  },
78
100
  onSuccess: () => {
79
101
  showToast("Support ticket created.", "success");
@@ -9,6 +9,18 @@ export interface Address {
9
9
  state: string;
10
10
  postalCode: string;
11
11
  country: string;
12
+ /**
13
+ * A nearby reference point ("opposite the Metro station").
14
+ *
15
+ * Store addresses have always accepted it — `POST /api/store/addresses`
16
+ * declares `landmark: z.string().max(100).optional()` and
17
+ * `SellerAddressesView`'s drawer collects it — while this shape did not.
18
+ * So an address created in that drawer WITH a landmark, then edited through
19
+ * any surface built on `AddressForm`, was saved back with `landmark`
20
+ * absent and lost it. Added here, on the form, and on the `Address` type so
21
+ * the round trip is lossless.
22
+ */
23
+ landmark?: string;
12
24
  isDefault?: boolean;
13
25
  }
14
26
  export interface AddressFormData {
@@ -21,6 +33,8 @@ export interface AddressFormData {
21
33
  state: string;
22
34
  postalCode: string;
23
35
  country: string;
36
+ /** See Address.landmark — the form must carry it or an edit drops it. */
37
+ landmark?: string;
24
38
  isDefault?: boolean;
25
39
  }
26
40
  interface SetDefaultAddressData {
@@ -46,8 +60,22 @@ export declare function useCreateAddress(options?: {
46
60
  onSuccess?: (data: Address) => void;
47
61
  onError?: (error: Error) => void;
48
62
  }): import("@tanstack/react-query").UseMutationResult<Address, Error, AddressFormData, unknown>;
49
- export declare function useUpdateAddress(id: string, options?: {
63
+ /**
64
+ * Update options.
65
+ *
66
+ * `method` exists because the two portals disagree: `/api/user/addresses/[id]`
67
+ * exports PATCH and `/api/store/addresses/[id]` exports PUT, and
68
+ * `SellerAddressesView` hardcodes PUT against the latter. Changing the route's
69
+ * verb would break that working call site, so the hook bends instead.
70
+ *
71
+ * Caught by `audit-client-verb-match` before the store pages were written —
72
+ * this hook would have 405'd the moment they reused it.
73
+ */
74
+ export interface UpdateAddressOptions {
50
75
  byIdEndpoint?: (id: string) => string;
76
+ method?: "PUT" | "PATCH";
77
+ }
78
+ export declare function useUpdateAddress(id: string, options?: UpdateAddressOptions & {
51
79
  onSuccess?: (data: Address) => void;
52
80
  onError?: (error: Error) => void;
53
81
  }): import("@tanstack/react-query").UseMutationResult<Address, Error, AddressFormData, unknown>;
@@ -48,8 +48,11 @@ export function useCreateAddress(options) {
48
48
  export function useUpdateAddress(id, options) {
49
49
  const queryClient = useQueryClient();
50
50
  const byIdEndpoint = options?.byIdEndpoint ?? DEFAULT_ENDPOINTS.byId;
51
+ const method = options?.method ?? "PATCH";
51
52
  return useMutation({
52
- mutationFn: (data) => apiClient.patch(byIdEndpoint(id), data),
53
+ mutationFn: (data) => method === "PUT"
54
+ ? apiClient.put(byIdEndpoint(id), data)
55
+ : apiClient.patch(byIdEndpoint(id), data),
53
56
  onSuccess: (data) => {
54
57
  queryClient.invalidateQueries({ queryKey: ["addresses"] });
55
58
  options?.onSuccess?.(data);
@@ -1,4 +1,4 @@
1
- import { type JsonValue } from "@mohasinac/appkit/client";
1
+ import type { JsonValue } from "../../../schemas/types";
2
2
  export interface UserProfile {
3
3
  uid: string;
4
4
  email: string | null;
@@ -1,4 +1,4 @@
1
- import { useApiMutation } from "@mohasinac/appkit/client";
1
+ import { useApiMutation } from "../../../client/api/useApiMutation";
2
2
  import { useQuery, useQueryClient } from "@tanstack/react-query";
3
3
  import { apiClient } from "../../../http";
4
4
  import { ACCOUNT_ENDPOINTS } from "../../../constants/api-endpoints";
@@ -11,6 +11,19 @@ export declare class AddressesRepository extends BaseRepository<AddressDocument>
11
11
  listByOwnerType(ownerType: AddressOwnerType, limit?: number): Promise<AddressDocument[]>;
12
12
  countByOwner(ownerType: AddressOwnerType, ownerId: string): Promise<number>;
13
13
  createForOwner(ownerType: AddressOwnerType, ownerId: string, input: AddressCreateInput): Promise<AddressDocument>;
14
+ /**
15
+ * Read one address, scoped to its owner.
16
+ *
17
+ * The owner check is `ownerType` AND `ownerId` — the same pair
18
+ * `updateForOwner` and `deleteForOwner` use, because `addresses` is one
19
+ * top-level collection holding both buyer and store addresses discriminated
20
+ * by `ownerType` (SB-UNI-A). Checking only `ownerId` would let a seller read
21
+ * their own personal address through the store endpoint and vice versa.
22
+ *
23
+ * Returns null rather than throwing: the caller turns it into a 404, and a
24
+ * missing address and a foreign one must be indistinguishable to the client.
25
+ */
26
+ getForOwner(ownerType: AddressOwnerType, ownerId: string, addressId: string): Promise<AddressDocument | null>;
14
27
  updateForOwner(ownerType: AddressOwnerType, ownerId: string, addressId: string, input: AddressUpdateInput): Promise<AddressDocument>;
15
28
  deleteForOwner(ownerType: AddressOwnerType, ownerId: string, addressId: string): Promise<void>;
16
29
  setDefault(ownerType: AddressOwnerType, ownerId: string, addressId: string): Promise<AddressDocument>;
@@ -115,6 +115,25 @@ export class AddressesRepository extends BaseRepository {
115
115
  throw new DatabaseError(`Failed to create address for ${ownerType}:${ownerId}`, error);
116
116
  }
117
117
  }
118
+ /**
119
+ * Read one address, scoped to its owner.
120
+ *
121
+ * The owner check is `ownerType` AND `ownerId` — the same pair
122
+ * `updateForOwner` and `deleteForOwner` use, because `addresses` is one
123
+ * top-level collection holding both buyer and store addresses discriminated
124
+ * by `ownerType` (SB-UNI-A). Checking only `ownerId` would let a seller read
125
+ * their own personal address through the store endpoint and vice versa.
126
+ *
127
+ * Returns null rather than throwing: the caller turns it into a 404, and a
128
+ * missing address and a foreign one must be indistinguishable to the client.
129
+ */
130
+ async getForOwner(ownerType, ownerId, addressId) {
131
+ const existing = await this.findById(addressId);
132
+ if (!existing || existing.ownerType !== ownerType || existing.ownerId !== ownerId) {
133
+ return null;
134
+ }
135
+ return existing;
136
+ }
118
137
  async updateForOwner(ownerType, ownerId, addressId, input) {
119
138
  const existing = await this.findById(addressId);
120
139
  if (!existing || existing.ownerType !== ownerType || existing.ownerId !== ownerId) {
@@ -14,6 +14,7 @@ import { getAdminRealtimeDb } from "../../../providers/db-firebase/index";
14
14
  import { AuthorizationError, NotFoundError, ValidationError, } from "../../../errors/index";
15
15
  import { ERROR_MESSAGES } from "../../../errors/messages";
16
16
  import { serverLogger } from "../../../monitoring/index";
17
+ import { normalizeError } from "../../../errors/normalize";
17
18
  export async function getChatRooms(userId, chatEnabled) {
18
19
  if (!chatEnabled)
19
20
  throw new AuthorizationError("Chat is temporarily unavailable.");
@@ -77,7 +78,10 @@ export async function sendChatMessage(userId, chatId, message) {
77
78
  : (room.buyerName ?? "Member");
78
79
  await msgRef.set({ userId, userName, message, timestamp: Date.now() });
79
80
  chatRepository.updateLastMessage(chatId, message).catch((err) => {
80
- serverLogger.warn("Failed to update chat lastMessage", { chatId, err });
81
+ serverLogger.warn("Failed to update chat lastMessage", {
82
+ chatId,
83
+ error: normalizeError(err).message,
84
+ });
81
85
  });
82
86
  serverLogger.debug("sendChatMessage", { userId, chatId });
83
87
  return { messageId: id, timestamp: Date.now() };
@@ -116,7 +116,7 @@ export function AdminAdsView({ endpoint = ADMIN_ENDPOINTS.ADS, labels = {}, crea
116
116
  render: (row) => {
117
117
  const nextStatus = row.status === "active" ? "paused" : "active";
118
118
  const cannotPublish = nextStatus === "active" && row.publishReady === false;
119
- return (_jsxs(Row, { align: "center", gap: "sm", children: [renderEditLink ? renderEditLink(row) : (_jsx(TextLink, { variant: "bare", href: `/admin/ads/${row.id}/edit`, rounded: "md", paddingX: "sm", size: "xs", layout: "inline-flex", align: "center", className: "h-8 border border-neutral-200 border-[var(--appkit-color-border)]", children: "Edit" })), _jsx(Button, { size: "sm", variant: "outline", disabled: statusMutation.isPending || cannotPublish, onClick: () => statusMutation.mutate({ id: row.id, nextStatus }), children: row.status === "active" ? "Pause" : "Publish" })] }));
119
+ return (_jsxs(Row, { align: "center", gap: "sm", children: [renderEditLink ? renderEditLink(row) : (_jsx(TextLink, { variant: "bare", href: `/admin/ads/${row.id}/edit`, rounded: "md", paddingX: "sm", size: "xs", layout: "inline-flex", align: "center", className: "h-8 border border-[var(--appkit-color-border)]", children: "Edit" })), _jsx(Button, { size: "sm", variant: "outline", disabled: statusMutation.isPending || cannotPublish, onClick: () => statusMutation.mutate({ id: row.id, nextStatus }), children: row.status === "active" ? "Pause" : "Publish" })] }));
120
120
  },
121
121
  },
122
122
  ];