@mohasinac/appkit 4.30.19 → 4.31.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (417) hide show
  1. package/dist/_internal/client/features/layout/useSidebarSearch.d.ts +21 -5
  2. package/dist/_internal/client/features/layout/useSidebarSearch.js +27 -6
  3. package/dist/_internal/client/features/lottery/LotteryAdminEditView.d.ts +23 -3
  4. package/dist/_internal/client/features/lottery/LotteryAdminEditView.js +106 -87
  5. package/dist/_internal/client/features/lottery/LotteryPullForm.d.ts +16 -5
  6. package/dist/_internal/client/features/lottery/LotteryPullForm.js +65 -40
  7. package/dist/_internal/server/features/auth/permissions.d.ts +2 -0
  8. package/dist/_internal/server/features/auth/permissions.js +3 -2
  9. package/dist/_internal/server/features/categories/data.js +15 -4
  10. package/dist/_internal/server/features/classified/index.d.ts +0 -1
  11. package/dist/_internal/server/features/classified/index.js +0 -1
  12. package/dist/_internal/server/features/digital-code/index.d.ts +0 -1
  13. package/dist/_internal/server/features/digital-code/index.js +0 -1
  14. package/dist/_internal/server/features/live/index.d.ts +0 -1
  15. package/dist/_internal/server/features/live/index.js +0 -1
  16. package/dist/_internal/server/features/products/data.js +15 -5
  17. package/dist/_internal/server/features/products/list-public.d.ts +9 -0
  18. package/dist/_internal/server/features/products/list-public.js +95 -7
  19. package/dist/_internal/server/features/site-settings/adapters.d.ts +2 -2
  20. package/dist/_internal/server/features/site-settings/adapters.js +8 -5
  21. package/dist/_internal/server/features/tester/visibility.d.ts +2 -1
  22. package/dist/_internal/server/features/tester/visibility.js +2 -2
  23. package/dist/_internal/server/functions/scheduled.d.ts +3 -1
  24. package/dist/_internal/server/functions/scheduled.js +27 -1
  25. package/dist/_internal/server/jobs/core/hardBanCascade.js +4 -0
  26. package/dist/_internal/server/jobs/core/index.d.ts +2 -0
  27. package/dist/_internal/server/jobs/core/index.js +2 -0
  28. package/dist/_internal/server/jobs/core/pageViewPrune.d.ts +10 -0
  29. package/dist/_internal/server/jobs/core/pageViewPrune.js +44 -0
  30. package/dist/_internal/server/jobs/core/pageViewRollup.d.ts +2 -0
  31. package/dist/_internal/server/jobs/core/pageViewRollup.js +87 -0
  32. package/dist/_internal/server/jobs/handlers/index.d.ts +2 -0
  33. package/dist/_internal/server/jobs/handlers/index.js +2 -0
  34. package/dist/_internal/server/jobs/handlers/pageViewPrune.d.ts +2 -0
  35. package/dist/_internal/server/jobs/handlers/pageViewPrune.js +2 -0
  36. package/dist/_internal/server/jobs/handlers/pageViewRollup.d.ts +2 -0
  37. package/dist/_internal/server/jobs/handlers/pageViewRollup.js +2 -0
  38. package/dist/_internal/shared/actions/action-registry.d.ts +1 -1
  39. package/dist/_internal/shared/actions/action-registry.js +69 -22
  40. package/dist/_internal/shared/checkout/rules/classified.rule.d.ts +9 -4
  41. package/dist/_internal/shared/checkout/rules/classified.rule.js +7 -2
  42. package/dist/_internal/shared/features/layout/matchesNavQuery.d.ts +14 -0
  43. package/dist/_internal/shared/features/layout/matchesNavQuery.js +72 -0
  44. package/dist/_internal/shared/features/layout/types.d.ts +37 -1
  45. package/dist/_internal/shared/features/layout/types.js +19 -1
  46. package/dist/_internal/shared/features/notifications/action-url.d.ts +9 -1
  47. package/dist/_internal/shared/features/notifications/action-url.js +47 -0
  48. package/dist/_internal/shared/features/offers/config.d.ts +65 -0
  49. package/dist/_internal/shared/features/offers/config.js +73 -2
  50. package/dist/_internal/shared/features/orders/schema.d.ts +19 -1
  51. package/dist/_internal/shared/features/orders/schema.js +22 -2
  52. package/dist/_internal/shared/features/site-settings/types.d.ts +12 -3
  53. package/dist/_internal/shared/listing-types/capabilities.d.ts +8 -2
  54. package/dist/_internal/shared/listing-types/capabilities.js +6 -1
  55. package/dist/_internal/shared/listing-types/classified/ctas.js +6 -1
  56. package/dist/_internal/shared/listing-types/feature-flags.d.ts +10 -5
  57. package/dist/_internal/shared/listing-types/feature-flags.js +11 -6
  58. package/dist/client.d.ts +37 -20
  59. package/dist/client.js +36 -14
  60. package/dist/configs/next.js +10 -2
  61. package/dist/constants/api-endpoints.d.ts +18 -54
  62. package/dist/constants/api-endpoints.js +6 -26
  63. package/dist/constants/field-names.d.ts +6 -24
  64. package/dist/constants/field-names.js +10 -27
  65. package/dist/constants/geo/countries.d.ts +50 -0
  66. package/dist/constants/geo/countries.js +129 -0
  67. package/dist/constants/geo/subdivisions.d.ts +13 -0
  68. package/dist/constants/geo/subdivisions.js +104 -0
  69. package/dist/constants/index.d.ts +2 -2
  70. package/dist/constants/index.js +2 -2
  71. package/dist/constants/table-keys.d.ts +24 -0
  72. package/dist/constants/table-keys.js +28 -0
  73. package/dist/contracts/extend.d.ts +0 -9
  74. package/dist/contracts/index.d.ts +1 -1
  75. package/dist/contracts/realtime-session.js +7 -0
  76. package/dist/core/browser.d.ts +1 -1
  77. package/dist/core/browser.js +1 -1
  78. package/dist/core/index.d.ts +1 -1
  79. package/dist/core/index.js +1 -1
  80. package/dist/core/site-config.d.ts +0 -3
  81. package/dist/core/site-config.js +0 -3
  82. package/dist/errors/messages.d.ts +0 -8
  83. package/dist/errors/messages.js +0 -9
  84. package/dist/features/about/schemas/index.d.ts +8 -8
  85. package/dist/features/account/components/AddressFilters.js +32 -11
  86. package/dist/features/account/components/AddressForm.d.ts +26 -25
  87. package/dist/features/account/components/AddressForm.js +81 -103
  88. package/dist/features/account/components/AddressesIndexListing.js +8 -4
  89. package/dist/features/account/components/UserSidebar.d.ts +3 -4
  90. package/dist/features/account/components/UserSupportView.js +89 -58
  91. package/dist/features/account/components/index.d.ts +1 -7
  92. package/dist/features/account/components/index.js +0 -3
  93. package/dist/features/account/hooks/useAddresses.d.ts +4 -3
  94. package/dist/features/account/hooks/useAddresses.js +4 -6
  95. package/dist/features/account/hooks/useCollapsedSections.d.ts +51 -10
  96. package/dist/features/account/hooks/useCollapsedSections.js +211 -38
  97. package/dist/features/account/hooks/useProfile.d.ts +8 -0
  98. package/dist/features/account/index.d.ts +0 -3
  99. package/dist/features/account/index.js +0 -3
  100. package/dist/features/account/schemas/index.d.ts +0 -92
  101. package/dist/features/account/schemas/index.js +42 -20
  102. package/dist/features/addresses/components/AddressLocationFields.d.ts +13 -0
  103. package/dist/features/addresses/components/AddressLocationFields.js +41 -0
  104. package/dist/features/addresses/hooks/usePostalLookup.d.ts +17 -0
  105. package/dist/features/addresses/hooks/usePostalLookup.js +133 -0
  106. package/dist/features/addresses/schemas/address-form.d.ts +423 -0
  107. package/dist/features/addresses/schemas/address-form.js +212 -0
  108. package/dist/features/addresses/schemas/index.d.ts +1 -0
  109. package/dist/features/addresses/schemas/index.js +8 -0
  110. package/dist/features/admin/actions/admin-read-actions.d.ts +1 -1
  111. package/dist/features/admin/actions/index.d.ts +0 -1
  112. package/dist/features/admin/actions/index.js +0 -1
  113. package/dist/features/admin/actions/notification-actions.js +1 -41
  114. package/dist/features/admin/components/AdminActionIndexView.d.ts +32 -0
  115. package/dist/features/admin/components/AdminActionIndexView.js +128 -0
  116. package/dist/features/admin/components/AdminAdEditorView.js +159 -163
  117. package/dist/features/admin/components/AdminAddressEditorView.js +97 -55
  118. package/dist/features/admin/components/AdminBrandEditorView.d.ts +15 -0
  119. package/dist/features/admin/components/AdminBrandEditorView.js +113 -77
  120. package/dist/features/admin/components/AdminBundleEditorView.d.ts +19 -9
  121. package/dist/features/admin/components/AdminBundleEditorView.js +136 -168
  122. package/dist/features/admin/components/AdminCategoryEditorView.js +120 -43
  123. package/dist/features/admin/components/AdminCouponEditorView.js +145 -109
  124. package/dist/features/admin/components/AdminEmployeeEditorView.js +125 -56
  125. package/dist/features/admin/components/AdminFaqEditorView.js +112 -58
  126. package/dist/features/admin/components/AdminFeatureEditorView.js +108 -68
  127. package/dist/features/admin/components/AdminNavEditorView.js +69 -44
  128. package/dist/features/admin/components/AdminOrderEditorView.js +47 -10
  129. package/dist/features/admin/components/AdminPrizeDrawsView.js +16 -2
  130. package/dist/features/admin/components/AdminSidebar.d.ts +11 -7
  131. package/dist/features/admin/components/AdminSiteSettingsView.js +37 -37
  132. package/dist/features/admin/components/AdminStoreEditorView.js +75 -52
  133. package/dist/features/admin/components/AdminSublistingCategoryEditorView.js +71 -30
  134. package/dist/features/admin/components/AdminTesterChecklistItemEditorView.js +95 -65
  135. package/dist/features/admin/components/BrandQuickCreateForm.d.ts +10 -0
  136. package/dist/features/admin/components/BrandQuickCreateForm.js +49 -26
  137. package/dist/features/admin/components/CategoryQuickCreateForm.d.ts +11 -0
  138. package/dist/features/admin/components/CategoryQuickCreateForm.js +48 -26
  139. package/dist/features/admin/components/index.d.ts +2 -2
  140. package/dist/features/admin/components/index.js +1 -1
  141. package/dist/features/admin/constants/site-settings-tabs.d.ts +120 -0
  142. package/dist/features/admin/constants/site-settings-tabs.js +152 -0
  143. package/dist/features/admin/index.d.ts +0 -3
  144. package/dist/features/admin/index.js +0 -2
  145. package/dist/features/admin/repository/analytics-rollup.repository.d.ts +35 -0
  146. package/dist/features/admin/repository/analytics-rollup.repository.js +30 -0
  147. package/dist/features/admin/schemas/admin-editor-forms.d.ts +56 -57
  148. package/dist/features/admin/schemas/admin-editor-forms.js +97 -54
  149. package/dist/features/admin/schemas/admin-ops-forms.d.ts +57 -5
  150. package/dist/features/admin/schemas/admin-ops-forms.js +104 -20
  151. package/dist/features/admin/schemas/admin-user-form.js +7 -0
  152. package/dist/features/admin/schemas/firestore.d.ts +48 -95
  153. package/dist/features/admin/schemas/firestore.js +15 -151
  154. package/dist/features/admin/schemas/nav-item-form.js +9 -2
  155. package/dist/features/admin/server.d.ts +0 -4
  156. package/dist/features/admin/server.js +0 -2
  157. package/dist/features/admin/types/index.d.ts +0 -7
  158. package/dist/features/analytics/repository/page-views.repository.d.ts +14 -0
  159. package/dist/features/analytics/repository/page-views.repository.js +24 -0
  160. package/dist/features/auth/actions/index.d.ts +0 -1
  161. package/dist/features/auth/actions/index.js +0 -1
  162. package/dist/features/auth/actions/realtime-token-actions.js +1 -12
  163. package/dist/features/auth/auth-helpers.js +5 -7
  164. package/dist/features/auth/components/Guards.js +2 -1
  165. package/dist/features/auth/components/LoginForm.d.ts +0 -2
  166. package/dist/features/auth/components/LoginForm.js +1 -3
  167. package/dist/features/auth/components/RegisterForm.js +62 -19
  168. package/dist/features/auth/permissions/constants.d.ts +27 -1
  169. package/dist/features/auth/role-predicates.d.ts +75 -7
  170. package/dist/features/auth/role-predicates.js +28 -2
  171. package/dist/features/auth/schemas/firestore.d.ts +22 -0
  172. package/dist/features/auth/schemas/index.d.ts +1 -1
  173. package/dist/features/auth/schemas/index.js +44 -8
  174. package/dist/features/auth/types/index.d.ts +14 -1
  175. package/dist/features/blog/api/route.js +10 -2
  176. package/dist/features/blog/components/BlogIndexListing.js +1 -1
  177. package/dist/features/blog/hooks/useBlog.js +2 -2
  178. package/dist/features/blog/schemas/index.d.ts +4 -0
  179. package/dist/features/blog/schemas/index.js +2 -0
  180. package/dist/features/blog/types/index.d.ts +1 -1
  181. package/dist/features/catalogue/components/CatalogueItemEditorView.d.ts +22 -0
  182. package/dist/features/catalogue/components/CatalogueItemEditorView.js +112 -52
  183. package/dist/features/catalogue/schemas/validation.d.ts +4 -4
  184. package/dist/features/catalogue/schemas/validation.js +47 -10
  185. package/dist/features/categories/schemas/bundle-form.d.ts +142 -0
  186. package/dist/features/categories/schemas/bundle-form.js +149 -0
  187. package/dist/features/classified/components/ClassifiedDetailPageView.d.ts +5 -4
  188. package/dist/features/classified/components/ClassifiedDetailPageView.js +37 -16
  189. package/dist/features/consultation/components/ConsultationForm.js +1 -1
  190. package/dist/features/consultation/schemas/index.js +34 -10
  191. package/dist/features/corporate/schemas/index.js +36 -15
  192. package/dist/features/filters/FilterPanel.js +3 -3
  193. package/dist/features/filters/RangeFilter.d.ts +10 -0
  194. package/dist/features/filters/RangeFilter.js +41 -4
  195. package/dist/features/homepage/components/MarketplaceHomepageView.js +24 -5
  196. package/dist/features/homepage/hooks/useBlogArticles.js +1 -1
  197. package/dist/features/layout/BottomActions.js +84 -11
  198. package/dist/features/layout/BottomActionsContext.d.ts +1 -6
  199. package/dist/features/layout/BottomActionsContext.js +1 -1
  200. package/dist/features/layout/hooks/useBottomActions.js +2 -2
  201. package/dist/features/layout/hooks/useFormBottomActions.d.ts +12 -1
  202. package/dist/features/layout/hooks/useFormBottomActions.js +12 -6
  203. package/dist/features/lottery/schemas/config-write.d.ts +9 -5
  204. package/dist/features/lottery/schemas/config-write.js +69 -9
  205. package/dist/features/orders/schemas/buyer-forms.d.ts +6 -3
  206. package/dist/features/orders/schemas/buyer-forms.js +31 -3
  207. package/dist/features/orders/schemas/index.d.ts +2 -2
  208. package/dist/features/products/components/GroupSettingsPanel.js +3 -1
  209. package/dist/features/products/components/MakeOfferButton.d.ts +13 -2
  210. package/dist/features/products/components/MakeOfferButton.js +33 -11
  211. package/dist/features/products/components/ProductDetailActions.js +0 -1
  212. package/dist/features/products/components/ProductDetailPageView.d.ts +7 -3
  213. package/dist/features/products/components/ProductDetailPageView.js +8 -5
  214. package/dist/features/products/constants/action-defs.d.ts +1 -2
  215. package/dist/features/products/constants/action-defs.js +40 -43
  216. package/dist/features/products/repository/products.repository.d.ts +5 -1
  217. package/dist/features/products/repository/products.repository.js +23 -1
  218. package/dist/features/products/schemas/firestore.d.ts +7 -3
  219. package/dist/features/products/schemas/product-features.validators.js +3 -0
  220. package/dist/features/promotions/components/CouponCard.js +8 -7
  221. package/dist/features/scams/schemas/firestore.d.ts +15 -1
  222. package/dist/features/scams/schemas/report-form.d.ts +5 -5
  223. package/dist/features/scams/schemas/report-form.js +18 -3
  224. package/dist/features/search/action-index/derive.d.ts +49 -0
  225. package/dist/features/search/action-index/derive.js +121 -0
  226. package/dist/features/search/action-index/repository.d.ts +42 -0
  227. package/dist/features/search/action-index/repository.js +100 -0
  228. package/dist/features/search/action-index/settings-entries.d.ts +41 -0
  229. package/dist/features/search/action-index/settings-entries.js +268 -0
  230. package/dist/features/search/action-index/types.d.ts +81 -0
  231. package/dist/features/search/action-index/types.js +102 -0
  232. package/dist/features/search/action-index/validate-entry.d.ts +34 -0
  233. package/dist/features/search/action-index/validate-entry.js +114 -0
  234. package/dist/features/search/hooks/useNavSuggestions.d.ts +5 -1
  235. package/dist/features/search/hooks/useNavSuggestions.js +23 -3
  236. package/dist/features/search/repository/search.repository.js +3 -1
  237. package/dist/features/seller/actions/offer-actions.js +43 -24
  238. package/dist/features/seller/actions/seller-actions.js +29 -5
  239. package/dist/features/seller/components/CategoryChipPicker.d.ts +9 -0
  240. package/dist/features/seller/components/CategoryChipPicker.js +12 -0
  241. package/dist/features/seller/components/SellerAddressesView.js +5 -1
  242. package/dist/features/seller/components/SellerAnalyticsAlertsView.js +83 -43
  243. package/dist/features/seller/components/SellerArtView.d.ts +14 -7
  244. package/dist/features/seller/components/SellerArtView.js +4 -119
  245. package/dist/features/seller/components/SellerClassifiedView.d.ts +14 -7
  246. package/dist/features/seller/components/SellerClassifiedView.js +4 -124
  247. package/dist/features/seller/components/SellerCouponEditorView.d.ts +22 -1
  248. package/dist/features/seller/components/SellerCouponEditorView.js +91 -45
  249. package/dist/features/seller/components/SellerDigitalCodesView.d.ts +14 -7
  250. package/dist/features/seller/components/SellerDigitalCodesView.js +4 -126
  251. package/dist/features/seller/components/SellerListingTypeView.d.ts +66 -0
  252. package/dist/features/seller/components/SellerListingTypeView.js +277 -0
  253. package/dist/features/seller/components/SellerLiveView.d.ts +14 -7
  254. package/dist/features/seller/components/SellerLiveView.js +4 -126
  255. package/dist/features/seller/components/SellerSidebar.d.ts +3 -6
  256. package/dist/features/seller/components/SellerStickersView.d.ts +14 -7
  257. package/dist/features/seller/components/SellerStickersView.js +4 -119
  258. package/dist/features/seller/components/SellerSupportView.d.ts +31 -0
  259. package/dist/features/seller/components/SellerSupportView.js +100 -0
  260. package/dist/features/seller/components/index.d.ts +4 -8
  261. package/dist/features/seller/components/index.js +2 -4
  262. package/dist/features/seller/index.d.ts +0 -1
  263. package/dist/features/seller/index.js +0 -1
  264. package/dist/features/seller/schemas/coupon-form.d.ts +214 -0
  265. package/dist/features/seller/schemas/coupon-form.js +155 -23
  266. package/dist/features/seller/schemas/offer-forms.d.ts +16 -7
  267. package/dist/features/seller/schemas/offer-forms.js +28 -12
  268. package/dist/features/shell/FormShell.js +2 -2
  269. package/dist/features/shell/QuickFormDrawer.js +2 -2
  270. package/dist/features/shell/SectionForm.d.ts +19 -1
  271. package/dist/features/shell/SectionForm.js +104 -17
  272. package/dist/features/shell/build-sections.d.ts +23 -0
  273. package/dist/features/shell/build-sections.js +106 -8
  274. package/dist/features/shell/field-ui-meta.d.ts +44 -0
  275. package/dist/features/shell/field-ui-meta.js +14 -7
  276. package/dist/features/shell/form-sections.d.ts +46 -0
  277. package/dist/features/shell/form-sections.js +114 -0
  278. package/dist/features/shipments/components/AdminShipmentEditorView.js +87 -64
  279. package/dist/features/shipments/schemas/validation.d.ts +2 -2
  280. package/dist/features/shipments/schemas/validation.js +44 -13
  281. package/dist/features/store-extensions/schemas/custom-role-form.js +6 -2
  282. package/dist/features/store-extensions/schemas/listing-template-form.d.ts +7 -7
  283. package/dist/features/store-extensions/schemas/payout-method-form.js +14 -0
  284. package/dist/features/stores/actions/store-query-actions.js +3 -2
  285. package/dist/features/stores/api/[storeSlug]/auctions/route.js +3 -1
  286. package/dist/features/stores/api/[storeSlug]/products/route.js +3 -1
  287. package/dist/features/support/repository/support.repository.d.ts +21 -0
  288. package/dist/features/support/repository/support.repository.js +63 -0
  289. package/dist/features/support/schemas/firestore.d.ts +27 -2
  290. package/dist/features/support/schemas/firestore.js +2 -0
  291. package/dist/features/support/schemas/ticket-create-form.js +7 -0
  292. package/dist/features/tester/schemas/checklist-item-form.d.ts +43 -0
  293. package/dist/features/tester/schemas/checklist-item-form.js +109 -0
  294. package/dist/features/tester/seed-data/blog-tester-seed-data.js +2 -2
  295. package/dist/features/tester/seed-data/categories-tester-seed-data.js +2 -2
  296. package/dist/features/tester/seed-data/events-tester-seed-data.js +5 -5
  297. package/dist/features/tester/seed-data/orders-tester-seed-data.js +2 -2
  298. package/dist/features/tester/seed-data/products-tester-seed-data.js +48 -49
  299. package/dist/features/tester/seed-data/stores-tester-seed-data.js +3 -3
  300. package/dist/features/tester/seed-data/tester-checklist-seed-data.js +275 -29
  301. package/dist/features/whatsapp-bot/components/SellerWhatsAppSettingsView.js +48 -12
  302. package/dist/http/api-handler.js +2 -1
  303. package/dist/index.d.ts +24 -103
  304. package/dist/index.js +33 -141
  305. package/dist/jobs.d.ts +1 -1
  306. package/dist/jobs.js +1 -1
  307. package/dist/monitoring/browser.d.ts +0 -2
  308. package/dist/monitoring/browser.js +12 -1
  309. package/dist/monitoring/index.d.ts +0 -2
  310. package/dist/monitoring/index.js +0 -1
  311. package/dist/next/api/routeHandler.js +6 -3
  312. package/dist/next/index.d.ts +7 -2
  313. package/dist/next/index.js +36 -1
  314. package/dist/next/middleware/index.d.ts +0 -2
  315. package/dist/next/middleware/index.js +0 -1
  316. package/dist/next/routing/route-map.d.ts +8 -7
  317. package/dist/next/routing/route-map.js +10 -3
  318. package/dist/providers/db-firebase/admin.js +15 -1
  319. package/dist/providers/db-firebase/sieve.d.ts +22 -0
  320. package/dist/react/hooks/useListingTypeFlags.d.ts +1 -1
  321. package/dist/react/hooks/useListingTypeFlags.js +1 -1
  322. package/dist/react/hooks/usePendingFilters.js +5 -3
  323. package/dist/react/hooks/usePendingTable.js +15 -3
  324. package/dist/repositories/index.d.ts +0 -1
  325. package/dist/repositories/index.js +0 -1
  326. package/dist/schemas/registry.d.ts +197 -232
  327. package/dist/schemas/registry.js +5 -6
  328. package/dist/schemas/webhooks/razorpay.d.ts +50 -50
  329. package/dist/security/authorization.d.ts +9 -0
  330. package/dist/security/authorization.js +16 -2
  331. package/dist/security/browser.d.ts +1 -3
  332. package/dist/security/browser.js +5 -2
  333. package/dist/security/index.d.ts +1 -3
  334. package/dist/security/index.js +19 -2
  335. package/dist/security/pii-encrypt.js +16 -0
  336. package/dist/security/pii-schemas.d.ts +0 -14
  337. package/dist/security/pii-schemas.js +7 -15
  338. package/dist/seed/_helpers/category-forest.js +2 -2
  339. package/dist/seed/_helpers/media.d.ts +19 -0
  340. package/dist/seed/_helpers/media.js +75 -0
  341. package/dist/seed/blog-posts-seed-data.js +17 -17
  342. package/dist/seed/carousel-slides-seed-data.js +7 -7
  343. package/dist/seed/cart-seed-data.js +13 -13
  344. package/dist/seed/categories-seed-data.js +10 -10
  345. package/dist/seed/events-seed-data.js +24 -24
  346. package/dist/seed/factories/product.factory.js +4 -4
  347. package/dist/seed/factories/store.factory.js +3 -3
  348. package/dist/seed/factories/user.factory.js +2 -2
  349. package/dist/seed/grouped-listings-seed-data.js +11 -11
  350. package/dist/seed/homepage-sections-seed-data.js +2 -2
  351. package/dist/seed/index.d.ts +0 -1
  352. package/dist/seed/index.js +0 -1
  353. package/dist/seed/manifest.d.ts +1 -1
  354. package/dist/seed/manifest.js +0 -5
  355. package/dist/seed/notifications-seed-data.js +20 -1
  356. package/dist/seed/orders-seed-data.js +2 -2
  357. package/dist/seed/products-art-seed-data.js +16 -16
  358. package/dist/seed/products-auctions-seed-data.js +34 -34
  359. package/dist/seed/products-classifieds-seed-data.js +35 -37
  360. package/dist/seed/products-digital-codes-seed-data.js +20 -20
  361. package/dist/seed/products-live-items-seed-data.js +11 -11
  362. package/dist/seed/products-preorders-seed-data.js +13 -13
  363. package/dist/seed/products-prize-draws-seed-data.js +43 -43
  364. package/dist/seed/products-standard-seed-data.js +54 -54
  365. package/dist/seed/products-stickers-seed-data.js +15 -15
  366. package/dist/seed/reviews-seed-data.js +4 -4
  367. package/dist/seed/scammers-seed-data.js +3 -3
  368. package/dist/seed/site-settings-seed-data.js +11 -24
  369. package/dist/seed/store-extensions-seed-data.js +10 -10
  370. package/dist/seed/stores-seed-data.js +11 -11
  371. package/dist/seed/support-tickets-seed-data.d.ts +14 -0
  372. package/dist/seed/support-tickets-seed-data.js +20 -1
  373. package/dist/seed/users-seed-data.js +20 -20
  374. package/dist/server-entry.d.ts +0 -1
  375. package/dist/server-entry.js +0 -1
  376. package/dist/server.d.ts +9 -6
  377. package/dist/server.js +7 -13
  378. package/dist/styles.css +248 -68
  379. package/dist/tailwind-utilities.css +1 -1
  380. package/dist/tokens/themes/default-dark.js +2 -2
  381. package/dist/tokens/themes/default-light.js +2 -2
  382. package/dist/tokens/tokens.css +24 -4
  383. package/dist/ui/components/ActionRow.d.ts +50 -0
  384. package/dist/ui/components/ActionRow.js +53 -0
  385. package/dist/ui/components/ActionRow.style.css +145 -0
  386. package/dist/ui/components/Drawer.d.ts +10 -0
  387. package/dist/ui/components/Drawer.js +2 -1
  388. package/dist/ui/components/FilterDrawer.js +4 -1
  389. package/dist/ui/components/FilterDrawer.style.css +6 -12
  390. package/dist/ui/components/FormActionBar.js +14 -1
  391. package/dist/ui/components/FormActionBar.style.css +12 -11
  392. package/dist/ui/components/ListingLayout.js +8 -1
  393. package/dist/ui/components/ListingLayout.style.css +6 -13
  394. package/dist/ui/components/Modal.d.ts +15 -1
  395. package/dist/ui/components/Modal.js +17 -3
  396. package/dist/ui/components/QuickCreateModal.js +2 -2
  397. package/dist/ui/components/QuickCreateModal.style.css +78 -84
  398. package/dist/ui/components/RecordDetailModal.js +2 -2
  399. package/dist/ui/components/SideDrawer.d.ts +8 -0
  400. package/dist/ui/components/SideDrawer.js +2 -1
  401. package/dist/ui/components/SideDrawer.style.css +6 -3
  402. package/dist/ui/components/SideModal.d.ts +11 -1
  403. package/dist/ui/components/SideModal.js +3 -2
  404. package/dist/ui/components/SideModal.style.css +14 -0
  405. package/dist/ui/components/Typography.style.css +33 -24
  406. package/dist/ui/components/index.style.css +1 -0
  407. package/dist/ui/forms/FormShell.style.css +260 -311
  408. package/dist/ui/index.d.ts +2 -0
  409. package/dist/ui/index.js +1 -0
  410. package/dist/utils/index.d.ts +0 -1
  411. package/dist/utils/index.js +0 -1
  412. package/dist/validation/index.d.ts +1 -1
  413. package/dist/validation/index.js +1 -1
  414. package/dist/validation/schemas.d.ts +0 -19
  415. package/dist/validation/schemas.js +8 -19
  416. package/package.json +1 -1
  417. package/scripts/seed-cli.mjs +3 -6
@@ -1,14 +1,18 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
3
  import { Li, useApiMutation } from "@mohasinac/appkit/client";
4
- import { useState } from "react";
4
+ import { useMemo, useState } from "react";
5
5
  import { useQuery, useQueryClient } from "@tanstack/react-query";
6
- import { Button, Div, FormActions, Row, Select, SideDrawer, Span, Stack, Text, Ul, useToast } from "../../../ui";
7
- import { FieldInput } from "../../../ui/forms/FieldInput";
6
+ import { Button, Div, Row, SideDrawer, Span, Stack, Text, Ul, useToast } from "../../../ui";
8
7
  import { FieldTextarea } from "../../../ui/forms/FieldTextarea";
8
+ import { FormErrorSummary } from "../../../ui/forms/FormErrorSummary";
9
+ import { FormShellContext, useFormShellState } from "../../../ui/forms/FormShell";
10
+ import { applyZodIssues } from "../../../ui/forms/apply-zod-issues";
11
+ import { buildSectionsFromSchema, visibleValues } from "../../shell/build-sections";
12
+ import { SectionForm, useSectionFormNav } from "../../shell/SectionForm";
9
13
  import { apiClient } from "../../../http";
10
14
  import { supportTicketCreateSchema } from "../../support/schemas/ticket-create-form";
11
- import { ValidationError } from "../../../errors/validation-error";
15
+ import { TicketCategoryValues } from "../../support/schemas/firestore";
12
16
  import { SUPPORT_ENDPOINTS } from "../../../constants/api-endpoints";
13
17
  const __P = {
14
18
  p3: "p-[var(--appkit-space-3)]",
@@ -18,23 +22,44 @@ const __O = {
18
22
  yAuto: "overflow-y-auto",
19
23
  };
20
24
  // --- Constants ---------------------------------------------------------------
21
- const CATEGORY_OPTIONS = [
22
- { label: "Order Issue", value: "order_issue" },
23
- { label: "Billing / Payment", value: "billing_payment" },
24
- { label: "Account", value: "account" },
25
- { label: "Listing Dispute", value: "listing_dispute" },
26
- { label: "Refund Request", value: "refund_request" },
27
- { label: "Auction Dispute", value: "auction_dispute" },
25
+ /**
26
+ * Category labels.
27
+ *
28
+ * Keyed off `TicketCategoryValues` rather than written as a parallel array, so
29
+ * a twelfth category cannot be added to the schema and silently omitted here —
30
+ * Root Cause #61, which the schema itself already guards on its own side.
31
+ *
32
+ * There are ten, so the generator renders this as a `PaginatedSelect`: it was
33
+ * a native `<Select>` with no `name`, which is over the five-option threshold
34
+ * AND unreachable by `applyZodIssues`.
35
+ */
36
+ const CATEGORY_LABELS = {
37
+ [TicketCategoryValues.ORDER_ISSUE]: "Order issue",
38
+ [TicketCategoryValues.BILLING_PAYMENT]: "Billing / payment",
39
+ [TicketCategoryValues.ACCOUNT]: "Account",
40
+ [TicketCategoryValues.LISTING_DISPUTE]: "Listing dispute",
41
+ /*
42
+ * The hand-written array this replaced had TEN entries and the schema has
43
+ * ELEVEN — `scam_report` was simply missing, so a user could never file one
44
+ * from this drawer even though the route accepts it.
45
+ */
46
+ [TicketCategoryValues.SCAM_REPORT]: "Report a scam",
47
+ [TicketCategoryValues.REFUND_REQUEST]: "Refund request",
48
+ [TicketCategoryValues.AUCTION_DISPUTE]: "Auction dispute",
28
49
  // ST-4 — sellers use this to request changes to admin-controlled
29
50
  // store fields (status, capabilities, verification badge).
30
- { label: "Store Change Request (sellers)", value: "store_change_request" },
51
+ [TicketCategoryValues.STORE_CHANGE_REQUEST]: "Store change request (sellers)",
31
52
  // ST-3 — buyers/sellers request mutation of order line items
32
- { label: "Order Modification Request", value: "order_modification_request" },
53
+ [TicketCategoryValues.ORDER_MODIFICATION_REQUEST]: "Order modification request",
33
54
  // ST-5 — appeal a soft or hard ban; bypasses the create_support_tickets
34
55
  // soft-ban guard server-side.
35
- { label: "Appeal a ban (unban request)", value: "unban_request" },
36
- { label: "General", value: "general" },
37
- ];
56
+ [TicketCategoryValues.UNBAN_REQUEST]: "Appeal a ban (unban request)",
57
+ [TicketCategoryValues.GENERAL]: "General",
58
+ };
59
+ const CATEGORY_OPTIONS = Object.values(TicketCategoryValues).map((value) => ({
60
+ value,
61
+ label: CATEGORY_LABELS[value] ?? value.replace(/_/g, " "),
62
+ }));
38
63
  const CLS_MSG_USER = "border bg-[var(--appkit-color-surface)]/40 border-[var(--appkit-color-border)]";
39
64
  const CLS_MSG_STAFF = "bg-info-surface border border-info dark:bg-info-surface dark:border-info";
40
65
  const STATUS_BADGE = {
@@ -49,17 +74,20 @@ const ROLE_LABEL = {
49
74
  support: "Support",
50
75
  admin: "Admin",
51
76
  };
77
+ const EMPTY_TICKET = {
78
+ category: TicketCategoryValues.GENERAL,
79
+ subject: "",
80
+ description: "",
81
+ orderId: "",
82
+ };
52
83
  export function UserSupportView(_props) {
53
84
  const queryClient = useQueryClient();
54
85
  const { showToast } = useToast();
55
86
  const [detailOpen, setDetailOpen] = useState(false);
56
87
  const [selectedTicket, setSelectedTicket] = useState(null);
57
88
  const [newTicketOpen, setNewTicketOpen] = useState(false);
58
- // New ticket form state
59
- const [newSubject, setNewSubject] = useState("");
60
- const [newCategory, setNewCategory] = useState("general");
61
- const [newDescription, setNewDescription] = useState("");
62
- const [newOrderId, setNewOrderId] = useState("");
89
+ const [newTicket, setNewTicket] = useState(EMPTY_TICKET);
90
+ const patchTicket = (partial) => setNewTicket((prev) => Object.assign({}, prev, partial));
63
91
  // Reply state
64
92
  const [replyBody, setReplyBody] = useState("");
65
93
  const { data, isLoading, error } = useQuery({
@@ -70,44 +98,45 @@ export function UserSupportView(_props) {
70
98
  const invalidate = () => queryClient.invalidateQueries({ queryKey: ["user", "support-tickets"] });
71
99
  const createMutation = useApiMutation({
72
100
  errorMessage: "Failed to create ticket.",
73
- mutationFn: async () => {
74
- /*
75
- * This drawer validated NOTHING. Its sibling page
76
- * (`/user/support/new`) hand-rolled a `canSubmit` boolean that required
77
- * an order id for an `order_issue` ticket — a real rule, since an order
78
- * complaint with no order is unactionable — and the route did not
79
- * enforce it either. So the same ticket was acceptable or not purely
80
- * depending on which surface the user happened to open.
81
- *
82
- * One schema now, across both surfaces and the route.
83
- */
84
- const parsed = supportTicketCreateSchema.safeParse({
85
- subject: newSubject.trim(),
86
- category: newCategory,
87
- description: newDescription.trim(),
88
- orderId: newOrderId.trim() || undefined,
89
- });
90
- if (!parsed.success) {
91
- /*
92
- * ValidationError, not a bare Error — it carries the issue LIST, so
93
- * the failure keeps which field caused it. A plain Error flattens the
94
- * whole thing to one string, which is what `audit-unvalidated-safeparse`
95
- * exists to stop. It caught this in my own code.
96
- */
97
- throw new ValidationError(parsed.error.issues[0]?.message ?? "Check the ticket details.", parsed.error.issues);
98
- }
99
- await apiClient.post(SUPPORT_ENDPOINTS.TICKETS, parsed.data);
100
- },
101
+ mutationFn: async (values) => apiClient.post(SUPPORT_ENDPOINTS.TICKETS, values),
101
102
  onSuccess: () => {
102
103
  showToast("Support ticket created.", "success");
103
104
  setNewTicketOpen(false);
104
- setNewSubject("");
105
- setNewCategory("general");
106
- setNewDescription("");
107
- setNewOrderId("");
105
+ setNewTicket(EMPTY_TICKET);
108
106
  invalidate();
109
107
  },
110
108
  });
109
+ /*
110
+ * This drawer validated NOTHING before the schema landed, and its sibling
111
+ * page (`/user/support/new`) hand-rolled a `canSubmit` that required an
112
+ * order id for an `order_issue` ticket — a real rule the route did not
113
+ * enforce either, so the same ticket was acceptable or not depending purely
114
+ * on which surface the user opened. One schema now covers both and the
115
+ * route; the parse below is what reports it, per field.
116
+ */
117
+ const sections = useMemo(() => buildSectionsFromSchema(supportTicketCreateSchema, {
118
+ options: { category: CATEGORY_OPTIONS },
119
+ }), []);
120
+ const nav = useSectionFormNav(sections, newTicket, { scope: "user:support-ticket" });
121
+ const { shellCtx, setFieldError, clearErrors } = useFormShellState(supportTicketCreateSchema, {
122
+ sections: nav.sectionMeta,
123
+ onGoToSection: nav.goToSection,
124
+ fieldToSectionIndex: nav.fieldToSectionIndex,
125
+ });
126
+ const submitTicket = () => {
127
+ clearErrors();
128
+ /*
129
+ * `visibleValues` is what keeps an order id typed before switching
130
+ * category out of a ticket that has nothing to do with an order — the
131
+ * `when` on that field hides the control and never clears it.
132
+ */
133
+ const parsed = supportTicketCreateSchema.safeParse(visibleValues(supportTicketCreateSchema, newTicket));
134
+ if (!parsed.success) {
135
+ applyZodIssues(parsed.error.issues, setFieldError);
136
+ return;
137
+ }
138
+ createMutation.mutate(parsed.data);
139
+ };
111
140
  const replyMutation = useApiMutation({
112
141
  errorMessage: "Failed to send reply.",
113
142
  mutationFn: async () => {
@@ -119,16 +148,18 @@ export function UserSupportView(_props) {
119
148
  invalidate();
120
149
  },
121
150
  });
122
- return (_jsxs(_Fragment, { children: [_jsxs(Div, { className: "mx-auto max-w-2xl", paddingY: "y-lg", paddingX: "x-md", children: [_jsxs(Row, { className: "mb-4", align: "center", justify: "between", children: [_jsx(Text, { size: "xl", weight: "semibold", color: "primary", children: "Support Tickets" }), _jsx(Button, { type: "button", variant: "primary", size: "sm", onClick: () => setNewTicketOpen(true), children: "New ticket" })] }), renderTicketListArea({ isLoading, error, tickets, setSelectedTicket, setDetailOpen })] }), renderNewTicketDrawer({ newTicketOpen, setNewTicketOpen, newCategory, setNewCategory, newSubject, setNewSubject, newOrderId, setNewOrderId, newDescription, setNewDescription, createMutation }), renderTicketDetailDrawer({ detailOpen, setDetailOpen, selectedTicket, replyBody, setReplyBody, replyMutation })] }));
151
+ return (_jsxs(_Fragment, { children: [_jsxs(Div, { className: "mx-auto max-w-2xl", paddingY: "y-lg", paddingX: "x-md", children: [_jsxs(Row, { className: "mb-4", align: "center", justify: "between", children: [_jsx(Text, { size: "xl", weight: "semibold", color: "primary", children: "Support Tickets" }), _jsx(Button, { type: "button", variant: "primary", size: "sm", onClick: () => setNewTicketOpen(true), children: "New ticket" })] }), renderTicketListArea({ isLoading, error, tickets, setSelectedTicket, setDetailOpen })] }), _jsx(SideDrawer, { isOpen: newTicketOpen, onClose: () => setNewTicketOpen(false), title: "Open a support ticket", children: _jsx(Stack, { className: `${__P.p4}`, gap: "md", children: _jsxs(FormShellContext.Provider, { value: shellCtx, children: [_jsx(FormErrorSummary, {}), _jsx(SectionForm, { sections: sections, values: newTicket, onChange: patchTicket, onSubmit: submitTicket, schema: supportTicketCreateSchema, openIds: nav.openIds, onOpenChange: nav.setOpenIds, isLoading: createMutation.isPending, submitLabel: "Submit ticket", onCancel: () => setNewTicketOpen(false),
152
+ /*
153
+ * A drawer owns its own footer — `useIsInsideOverlay` already
154
+ * suppresses the viewport-fixed bar, which would otherwise
155
+ * render BEHIND the backdrop. Stated rather than relied on.
156
+ */
157
+ bottomBar: false })] }) }) }), renderTicketDetailDrawer({ detailOpen, setDetailOpen, selectedTicket, replyBody, setReplyBody, replyMutation })] }));
123
158
  }
124
159
  function renderTicketListArea(props) {
125
160
  const { isLoading, error, tickets, setSelectedTicket, setDetailOpen } = props;
126
161
  return (_jsxs(_Fragment, { children: [isLoading && (_jsx(Stack, { gap: "3", children: [1, 2, 3].map((i) => _jsx(Div, { className: "h-16 animate-pulse", surface: "subtle", rounded: "lg" }, i)) })), error && (_jsx(Div, { textSize: "sm", className: "border border-error/20", color: "error", surface: "danger-surface", padding: "inline", rounded: "xl", children: "Failed to load support tickets." })), !isLoading && tickets.length === 0 && (_jsx(Div, { className: "text-left", surface: "muted", paddingY: "y-2xl", paddingX: "x-lg", rounded: "xl", border: "default", children: _jsx(Text, { size: "sm", color: "muted", children: "You haven't opened any support tickets yet." }) })), _jsx(Ul, { spacing: "loose", children: tickets.map((ticket) => (_jsx(Li, { children: _jsx(Button, { rounded: "xl", variant: "ghost", type: "button", shadow: "sm", border: "default", paddingX: "md", paddingY: "md", className: "w-full bg-[var(--appkit-color-surface)] text-left hover:border-[var(--appkit-color-primary-300)] transition-colors", onClick: () => { setSelectedTicket(ticket); setDetailOpen(true); }, children: _jsxs(Row, { align: "start", justify: "between", gap: "sm", children: [_jsxs(Div, { className: "min-w-0 flex-1", children: [_jsx(Text, { className: "truncate", color: "primary", weight: "medium", children: ticket.subject }), _jsxs(Text, { size: "xs", color: "muted", children: [ticket.category.replace(/_/g, " "), ticket.orderId ? ` · Order: ${ticket.orderId}` : ""] })] }), _jsx(Span, { size: "xs", weight: "medium", className: `shrink-0 inline-flex ${STATUS_BADGE[ticket.status] ?? STATUS_BADGE.open}`, padding: "pill-sm", rounded: "full", children: ticket.status.replace(/_/g, " ") })] }) }) }, ticket.id))) })] }));
127
162
  }
128
- function renderNewTicketDrawer(props) {
129
- const { newTicketOpen, setNewTicketOpen, newCategory, setNewCategory, newSubject, setNewSubject, newOrderId, setNewOrderId, newDescription, setNewDescription, createMutation } = props;
130
- return (_jsx(SideDrawer, { isOpen: newTicketOpen, onClose: () => setNewTicketOpen(false), title: "Open a support ticket", children: _jsxs(Stack, { className: `${__P.p4}`, gap: "md", children: [_jsx(Select, { label: "Category", options: CATEGORY_OPTIONS, value: newCategory, onValueChange: setNewCategory }), _jsx(FieldInput, { name: "subject", label: "Subject", type: "text", value: newSubject, onChange: setNewSubject, placeholder: "Brief description of the issue" }), newCategory === "order_issue" && (_jsx(FieldInput, { name: "orderId", label: "Order ID", type: "text", value: newOrderId, onChange: setNewOrderId, placeholder: "e.g. order-3-20260508-a1b2c3" })), _jsx(FieldTextarea, { name: "description", label: "Description", value: newDescription, onChange: setNewDescription, rows: 4, placeholder: "Describe the issue in detail\u2026" }), _jsxs(FormActions, { align: "right", children: [_jsx(Button, { type: "button", variant: "secondary", onClick: () => setNewTicketOpen(false), children: "Cancel" }), _jsx(Button, { type: "button", isLoading: createMutation.isPending, disabled: !newSubject.trim() || !newDescription.trim() || createMutation.isPending, onClick: () => createMutation.mutate(), children: "Submit ticket" })] })] }) }));
131
- }
132
163
  function renderTicketDetailDrawer(props) {
133
164
  const { detailOpen, setDetailOpen, selectedTicket, replyBody, setReplyBody, replyMutation } = props;
134
165
  return (_jsx(SideDrawer, { isOpen: detailOpen, onClose: () => setDetailOpen(false), title: selectedTicket?.subject ?? "Ticket", children: selectedTicket && (_jsxs(Stack, { className: `${__P.p4}`, gap: "md", children: [_jsxs(Row, { wrap: true, gap: "sm", children: [_jsx(Span, { size: "xs", weight: "medium", className: `inline-flex ${STATUS_BADGE[selectedTicket.status] ?? STATUS_BADGE.open}`, rounded: "full", padding: "pill-sm-tall", children: selectedTicket.status.replace(/_/g, " ") }), _jsx(Span, { size: "xs", rounded: "full", padding: "pill-sm-tall", surface: "subtle", color: "muted", children: selectedTicket.category.replace(/_/g, " ") }), selectedTicket.orderId && _jsxs(Span, { size: "xs", rounded: "full", padding: "pill-sm-tall", surface: "subtle", color: "muted", children: ["Order: ", selectedTicket.orderId] })] }), selectedTicket.description && (_jsxs(Div, { className: `${__P.p3}`, rounded: "lg", surface: "muted", border: "default", children: [_jsx(Text, { className: "mb-1 tracking-wide", size: "xs", weight: "semibold", color: "muted", transform: "uppercase", children: "Your description" }), _jsx(Text, { className: "whitespace-pre-wrap", color: "primary", size: "sm", children: selectedTicket.description })] })), (selectedTicket.messages ?? []).length > 0 && (_jsxs(Stack, { gap: "sm", children: [_jsx(Text, { className: "tracking-wide", color: "muted", size: "xs", weight: "semibold", transform: "uppercase", children: "Messages" }), _jsx(Stack, { className: `max-h-72 ${__O.yAuto}`, gap: "sm", children: (selectedTicket.messages ?? []).map((msg, i) => (_jsxs(Div, { textSize: "sm", className: `${__P.p3} ${msg.authorRole === "user" ? CLS_MSG_USER : CLS_MSG_STAFF}`, rounded: "lg", children: [_jsxs(Row, { color: "muted", textSize: "xs", className: "mb-1", align: "center", gap: "sm", children: [_jsx(Span, { weight: "medium", color: "muted", children: ROLE_LABEL[msg.authorRole ?? "user"] ?? msg.authorRole }), msg.createdAt && _jsx(Span, { children: new Date(msg.createdAt).toLocaleString() })] }), _jsx(Text, { className: "whitespace-pre-wrap", color: "primary", children: msg.body })] }, msg.id ?? i))) })] })), selectedTicket.status !== "closed" && selectedTicket.status !== "resolved" && (_jsxs(Stack, { gap: "sm", children: [_jsx(FieldTextarea, { name: "reply", label: "Reply", value: replyBody, onChange: setReplyBody, rows: 3, placeholder: "Add a message to your ticket\u2026" }), _jsx(Button, { type: "button", variant: "primary", size: "sm", isLoading: replyMutation.isPending, disabled: !replyBody.trim() || replyMutation.isPending, onClick: () => replyMutation.mutate(), children: "Send reply" })] }))] })) }));
@@ -3,7 +3,7 @@ export type { UserSidebarProps, UserNavItem } from "./UserSidebar";
3
3
  export { AddressCard, AddressBook } from "./AddressBook";
4
4
  export type { AddressCardAddress, AddressCardProps } from "./AddressBook";
5
5
  export { AddressForm } from "./AddressForm";
6
- export type { AddressFormLabels, AddressFormPlaceholders } from "./AddressForm";
6
+ export type { AddressFormLabels } from "./AddressForm";
7
7
  export { AddressSelectorCreate } from "./AddressSelectorCreate";
8
8
  export { NotificationBell } from "./NotificationBell";
9
9
  export type { NotificationBellLabels, NotificationBellProps, NotificationBellRenderLinkProps, } from "./NotificationBell";
@@ -27,12 +27,6 @@ export { UserNotificationsView } from "./UserNotificationsView";
27
27
  export type { UserNotificationsViewProps, UserNotificationsViewLabels, } from "./UserNotificationsView";
28
28
  export { UserOffersPanel } from "./UserOffersPanel";
29
29
  export type { UserOffersPanelProps } from "./UserOffersPanel";
30
- export { MessagesView } from "./MessagesView";
31
- export type { MessagesViewProps, MessagesViewLabels } from "./MessagesView";
32
- export { ChatList } from "./ChatList";
33
- export type { ChatListProps, ChatListLabels } from "./ChatList";
34
- export { ChatWindow } from "./ChatWindow";
35
- export type { ChatWindowProps, ChatWindowLabels } from "./ChatWindow";
36
30
  export { BecomeSellerView } from "./BecomeSellerView";
37
31
  export type { BecomeSellerViewProps, BecomeSellerViewLabels, } from "./BecomeSellerView";
38
32
  export { UserOrderTrackView } from "./UserOrderTrackView";
@@ -13,9 +13,6 @@ export { OrderDetailView } from "./OrderDetailView";
13
13
  export { UserAddressesView } from "./UserAddressesView";
14
14
  export { UserNotificationsView } from "./UserNotificationsView";
15
15
  export { UserOffersPanel } from "./UserOffersPanel";
16
- export { MessagesView } from "./MessagesView";
17
- export { ChatList } from "./ChatList";
18
- export { ChatWindow } from "./ChatWindow";
19
16
  export { BecomeSellerView } from "./BecomeSellerView";
20
17
  export { UserOrderTrackView } from "./UserOrderTrackView";
21
18
  export { UserReturnsView } from "./UserReturnsView";
@@ -42,9 +42,10 @@ interface SetDefaultAddressData {
42
42
  }
43
43
  export interface AddressFilterParams {
44
44
  q?: string;
45
- addressType?: string;
46
- verified?: boolean;
47
- activeOnly?: boolean;
45
+ /** Only the owner's default address. */
46
+ defaultOnly?: boolean;
47
+ /** Pipe-joined `AddressBanStatus` values. */
48
+ banStatus?: string;
48
49
  }
49
50
  export declare function useAddresses(options?: {
50
51
  enabled?: boolean;
@@ -10,12 +10,10 @@ export function useAddresses(options) {
10
10
  const sp = new URLSearchParams();
11
11
  if (options?.filters?.q)
12
12
  sp.set("q", options.filters.q);
13
- if (options?.filters?.addressType)
14
- sp.set("addressType", options.filters.addressType);
15
- if (options?.filters?.verified)
16
- sp.set("verified", "true");
17
- if (options?.filters?.activeOnly)
18
- sp.set("activeOnly", "true");
13
+ if (options?.filters?.defaultOnly)
14
+ sp.set("defaultOnly", "true");
15
+ if (options?.filters?.banStatus)
16
+ sp.set("banStatus", options.filters.banStatus);
19
17
  const qs = sp.toString();
20
18
  const base = options?.listEndpoint ?? DEFAULT_ENDPOINTS.list;
21
19
  const endpoint = qs ? `${base}?${qs}` : base;
@@ -1,11 +1,47 @@
1
+ export type SectionExpandMode = "multi" | "single";
2
+ export interface UseSectionStateOptions {
3
+ /**
4
+ * `"{portal}:{surface}"`, e.g. `"admin:dashboard"` or `"store:product-form"`.
5
+ *
6
+ * **The empty string means EPHEMERAL** — in-memory only, no profile read, no
7
+ * profile write, no localStorage. It exists so a surface can adopt this hook
8
+ * before it has chosen a durable scope name, without every such surface
9
+ * writing junk under `sectionState[""]` and without the legacy-derivation
10
+ * branch below reading another page's `collapsedSections` into it.
11
+ */
12
+ scope: string;
13
+ /** Every section id this surface manages, in display order. */
14
+ sectionIds: string[];
15
+ /**
16
+ * Which sections are open for a user who has never touched this surface.
17
+ * Defaults to the FIRST section — "required first, everything else
18
+ * collapsed". Pass `[]` for all-collapsed.
19
+ */
20
+ defaultOpen?: string[];
21
+ /** `"multi"` (default) lets several sections be open at once. `"single"` is
22
+ * accordion: opening one closes its siblings. */
23
+ mode?: SectionExpandMode;
24
+ }
25
+ export interface UseSectionStateResult {
26
+ isOpen: (id: string) => boolean;
27
+ toggle: (id: string) => void;
28
+ /** Force a section open without closing anything — what a validation error
29
+ * needs. Never collapses, even under `mode: "single"`. */
30
+ open: (id: string) => void;
31
+ /** Replace the whole open set. For a caller that already computes the next
32
+ * array itself (`SectionForm`'s controlled/uncontrolled `setOpenIds`
33
+ * contract) rather than expressing the change as a per-id toggle. */
34
+ setOpenIds: (ids: string[]) => void;
35
+ openIds: string[];
36
+ }
37
+ export declare function useSectionState({ scope, sectionIds, defaultOpen, mode, }: UseSectionStateOptions): UseSectionStateResult;
1
38
  export interface UseCollapsedSectionsOptions {
2
- /** Every section ID this page manages, in display order. IDs must be
3
- * globally unique across pages (prefix with a page key, e.g.
4
- * "admin-dashboard:alerts") — uiPreferences.collapsedSections is a single
5
- * flat list shared by the whole user document, not namespaced server-side.
6
- * A section ID absent from the saved list defaults to collapsed, which is
7
- * also the state for a user who has never touched this page. */
39
+ /** Every section ID this page manages, in display order. */
8
40
  sectionIds: string[];
41
+ /** Scope key. Optional only so the pre-existing call sites keep compiling;
42
+ * they share `"legacy:collapsed-sections"`, which reproduces the old global
43
+ * behaviour exactly. New code should call `useSectionState` directly. */
44
+ scope?: string;
9
45
  }
10
46
  export interface UseCollapsedSectionsResult {
11
47
  isCollapsed: (id: string) => boolean;
@@ -14,7 +50,12 @@ export interface UseCollapsedSectionsResult {
14
50
  * everything collapsed. */
15
51
  toggle: (id: string) => void;
16
52
  }
17
- /** Persisted, per-user, accordion-scoped collapse state for a page's
18
- * dashboard sections. Debounces writes to PATCH /api/user/profile so rapid
19
- * toggling doesn't fire a request per click. */
20
- export declare function useCollapsedSections({ sectionIds, }: UseCollapsedSectionsOptions): UseCollapsedSectionsResult;
53
+ /**
54
+ * @deprecated Prefer `useSectionState`, which is namespaced per surface,
55
+ * supports several sections open at once, and defaults to section 1 open.
56
+ *
57
+ * Kept so the existing dashboard call sites need no edit. Semantics are
58
+ * byte-identical to the original: default all-collapsed (`defaultOpen: []`)
59
+ * and accordion (`mode: "single"`).
60
+ */
61
+ export declare function useCollapsedSections({ sectionIds, scope, }: UseCollapsedSectionsOptions): UseCollapsedSectionsResult;
@@ -1,63 +1,236 @@
1
1
  "use client";
2
- import { useCallback, useEffect, useRef, useState } from "react";
2
+ import { useCallback, useEffect, useMemo, useRef, useState } from "react";
3
+ import { normalizeError } from "../../../errors/normalize";
3
4
  import { useCurrentProfile, useUpdateCurrentProfile } from "./useProfile";
4
5
  const PERSIST_DEBOUNCE_MS = 600;
5
- /** Persisted, per-user, accordion-scoped collapse state for a page's
6
- * dashboard sections. Debounces writes to PATCH /api/user/profile so rapid
7
- * toggling doesn't fire a request per click. */
8
- export function useCollapsedSections({ sectionIds, }) {
6
+ const LOCAL_KEY = "letitrip:section-state";
7
+ /**
8
+ * ─────────────────────────────────────────────────────────────────────────────
9
+ * Persisted, per-user, per-surface open/closed state for collapsible sections.
10
+ *
11
+ * `useSectionState` is the implementation; `useCollapsedSections` below is a
12
+ * thin adapter preserving the older hook's exact semantics for its existing
13
+ * call sites.
14
+ *
15
+ * 🛑 THE STORED VALUE IS WHAT IS **OPEN**, NOT WHAT IS COLLAPSED.
16
+ *
17
+ * The predecessor stored `uiPreferences.collapsedSections`: one flat, global,
18
+ * un-namespaced array of collapsed ids, shared by every page and kept apart
19
+ * only by a naming convention. Two consequences, both of which the polarity
20
+ * flip removes rather than patches:
21
+ *
22
+ * - a never-seen surface had to DEFAULT to collapsed, because "absent" and
23
+ * "deliberately collapsed" were the same value. Storing what is open makes
24
+ * a never-seen scope resolve to `defaultOpen` by absence, so "section 1
25
+ * open, the rest collapsed" needs no sentinel;
26
+ * - one page's ids could collide with another's.
27
+ *
28
+ * The scope key is `"{portal}:{surface}"` and is supplied by the caller. It is
29
+ * deliberately NOT derived from the section ids — a hash would change the
30
+ * moment a section is added, silently resetting every user's layout.
31
+ *
32
+ * ── localStorage mirror ─────────────────────────────────────────────────────
33
+ * The profile arrives over the network, so a server-only read paints every
34
+ * section in its default state and then snaps to the user's real layout a
35
+ * moment later. Writes go to localStorage synchronously (instant, and it
36
+ * survives for signed-out users) and to the user document debounced (durable,
37
+ * and it follows the user to another device). localStorage seeds first paint;
38
+ * the profile is authoritative once it lands.
39
+ *
40
+ * ── Migration ───────────────────────────────────────────────────────────────
41
+ * One-way and lossless: the first time a scope is read with no `sectionState`
42
+ * entry, its open set is derived from the legacy `collapsedSections`
43
+ * (`sectionIds − collapsed`) and persisted under the new key. `collapsedSections`
44
+ * keeps being written for one release so a rollback loses nothing.
45
+ */
46
+ // ─────────────────────────────────────────────────────────────────────────────
47
+ // localStorage mirror
48
+ // ─────────────────────────────────────────────────────────────────────────────
49
+ function readLocalScopes() {
50
+ if (typeof window === "undefined")
51
+ return {};
52
+ try {
53
+ const raw = window.localStorage.getItem(LOCAL_KEY);
54
+ if (!raw)
55
+ return {};
56
+ const parsed = JSON.parse(raw);
57
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
58
+ return {};
59
+ // Validate rather than trust: this value survives across releases and a
60
+ // shape written by an older version must degrade to "no preference", not
61
+ // to a render crash. Every entry is checked, so the cast above is a claim
62
+ // this loop then makes true.
63
+ const out = {};
64
+ for (const [scope, ids] of Object.entries(parsed)) {
65
+ if (Array.isArray(ids))
66
+ out[scope] = ids.filter((id) => typeof id === "string");
67
+ }
68
+ return out;
69
+ // localStorage throws on access in private mode, and JSON.parse throws on a
70
+ // value some other version wrote. Neither is reportable — the user document
71
+ // is the durable copy and this is only a first-paint cache — but the throw
72
+ // is still normalized rather than swallowed raw.
73
+ }
74
+ catch (err) {
75
+ void normalizeError(err);
76
+ return {};
77
+ }
78
+ }
79
+ function writeLocalScope(scope, openIds) {
80
+ if (typeof window === "undefined")
81
+ return;
82
+ try {
83
+ const all = readLocalScopes();
84
+ all[scope] = openIds;
85
+ window.localStorage.setItem(LOCAL_KEY, JSON.stringify(all));
86
+ // setItem throws on quota exhaustion and in private mode. Same reasoning as
87
+ // readLocalScopes — the debounced profile PATCH is the durable write, so
88
+ // losing the mirror costs one frame of default layout and nothing else.
89
+ }
90
+ catch (err) {
91
+ void normalizeError(err);
92
+ }
93
+ }
94
+ export function useSectionState({ scope, sectionIds, defaultOpen, mode = "multi", }) {
9
95
  const { data: profile } = useCurrentProfile();
10
96
  const updateProfile = useUpdateCurrentProfile();
11
- // Full server-side list, including other pages' entries this hook must
12
- // never clobber when it persists its own page's next state.
13
- const savedFullListRef = useRef([]);
14
- const [localCollapsed, setLocalCollapsed] = useState(null);
15
- const debounceRef = useRef(null);
16
97
  const sectionIdsRef = useRef(sectionIds);
17
98
  sectionIdsRef.current = sectionIds;
99
+ const fallbackOpen = useMemo(() => defaultOpen ?? (sectionIds.length > 0 ? [sectionIds[0]] : []),
100
+ // Only the first id matters for the default, so this does not need to
101
+ // re-run every time the array identity changes.
102
+ // eslint-disable-next-line react-hooks/exhaustive-deps
103
+ [defaultOpen, sectionIds[0], sectionIds.length]);
104
+ const fallbackOpenRef = useRef(fallbackOpen);
105
+ fallbackOpenRef.current = fallbackOpen;
106
+ // Server copy of EVERY scope, so persisting this surface never clobbers
107
+ // another's entry.
108
+ const savedScopesRef = useRef({});
109
+ const legacyCollapsedRef = useRef(undefined);
110
+ const debounceRef = useRef(null);
111
+ /** `scope: ""` opts out of every durable store — see the option's docstring. */
112
+ const persistent = scope !== "";
113
+ // Seeded synchronously from localStorage so first paint is already correct.
114
+ const [localOpen, setLocalOpen] = useState(() => {
115
+ if (!persistent)
116
+ return null;
117
+ const cached = readLocalScopes()[scope];
118
+ return cached ? new Set(cached) : null;
119
+ });
18
120
  useEffect(() => {
19
- if (!profile)
121
+ if (!persistent || !profile)
20
122
  return;
21
- const serverList = profile.uiPreferences?.collapsedSections;
22
- savedFullListRef.current = serverList ?? [];
23
- setLocalCollapsed((prev) => {
24
- if (prev !== null)
25
- return prev; // local interaction already in progress — don't clobber it
26
- const known = new Set(serverList ?? []);
27
- const initial = new Set();
28
- for (const id of sectionIdsRef.current) {
29
- // Never-set user (serverList undefined) → default every section collapsed.
30
- if (serverList === undefined || known.has(id))
31
- initial.add(id);
32
- }
33
- return initial;
34
- });
35
- }, [profile]);
123
+ const prefs = profile.uiPreferences;
124
+ savedScopesRef.current = prefs?.sectionState ?? {};
125
+ legacyCollapsedRef.current = prefs?.collapsedSections;
126
+ const stored = savedScopesRef.current[scope];
127
+ if (stored) {
128
+ setLocalOpen(new Set(stored));
129
+ return;
130
+ }
131
+ // No entry for this scope yet — derive one from the legacy flat list if the
132
+ // user has one, otherwise fall back to the default. Persisting is left to
133
+ // the first real toggle: writing on mere page view would fire a profile
134
+ // PATCH for every visitor on every dashboard.
135
+ const legacy = legacyCollapsedRef.current;
136
+ if (legacy !== undefined) {
137
+ const collapsed = new Set(legacy);
138
+ setLocalOpen(new Set(sectionIdsRef.current.filter((id) => !collapsed.has(id))));
139
+ }
140
+ else {
141
+ setLocalOpen(new Set(fallbackOpenRef.current));
142
+ }
143
+ }, [profile, scope, persistent]);
36
144
  useEffect(() => () => {
37
145
  if (debounceRef.current)
38
146
  clearTimeout(debounceRef.current);
39
147
  }, []);
40
- const persist = useCallback((nextForThisPage) => {
148
+ const persist = useCallback((next) => {
149
+ if (!persistent)
150
+ return;
151
+ const openIds = sectionIdsRef.current.filter((id) => next.has(id));
152
+ writeLocalScope(scope, openIds);
153
+ savedScopesRef.current = { ...savedScopesRef.current, [scope]: openIds };
41
154
  if (debounceRef.current)
42
155
  clearTimeout(debounceRef.current);
43
156
  debounceRef.current = setTimeout(() => {
157
+ // Keep writing the legacy list for one release: derived from this
158
+ // surface's ids only, merged over whatever other pages recorded, so a
159
+ // rollback finds the same state it would have written itself.
44
160
  const ids = sectionIdsRef.current;
45
- const others = savedFullListRef.current.filter((id) => !ids.includes(id));
46
- const merged = [...others, ...nextForThisPage];
47
- savedFullListRef.current = merged;
48
- updateProfile.mutate({ uiPreferences: { collapsedSections: merged } });
161
+ const others = (legacyCollapsedRef.current ?? []).filter((id) => !ids.includes(id));
162
+ const collapsedHere = ids.filter((id) => !next.has(id));
163
+ const mergedLegacy = [...others, ...collapsedHere];
164
+ legacyCollapsedRef.current = mergedLegacy;
165
+ updateProfile.mutate({
166
+ uiPreferences: {
167
+ sectionState: { [scope]: openIds },
168
+ collapsedSections: mergedLegacy,
169
+ },
170
+ });
49
171
  }, PERSIST_DEBOUNCE_MS);
50
- }, [updateProfile]);
51
- const isCollapsed = useCallback((id) => localCollapsed?.has(id) ?? true, [localCollapsed]);
172
+ }, [scope, persistent, updateProfile]);
173
+ const effectiveOpen = localOpen ?? new Set(fallbackOpen);
174
+ const isOpen = useCallback((id) => (localOpen ?? new Set(fallbackOpenRef.current)).has(id), [localOpen]);
52
175
  const toggle = useCallback((id) => {
53
- setLocalCollapsed((prev) => {
54
- const wasCollapsed = prev?.has(id) ?? true;
55
- const next = wasCollapsed
56
- ? new Set(sectionIdsRef.current.filter((s) => s !== id)) // opening id collapses every sibling
57
- : new Set(sectionIdsRef.current); // closing the open one — back to all-collapsed
176
+ setLocalOpen((prev) => {
177
+ const current = prev ?? new Set(fallbackOpenRef.current);
178
+ const next = new Set(current);
179
+ if (next.has(id))
180
+ next.delete(id);
181
+ else if (mode === "single") {
182
+ next.clear();
183
+ next.add(id);
184
+ }
185
+ else
186
+ next.add(id);
187
+ persist(next);
188
+ return next;
189
+ });
190
+ }, [mode, persist]);
191
+ const open = useCallback((id) => {
192
+ setLocalOpen((prev) => {
193
+ const current = prev ?? new Set(fallbackOpenRef.current);
194
+ if (current.has(id))
195
+ return current;
196
+ // Deliberately additive even under `mode: "single"`: this exists for
197
+ // error recovery, and collapsing the section the user was just editing
198
+ // in order to reveal the one that failed loses their place.
199
+ const next = new Set(current);
200
+ next.add(id);
201
+ persist(next);
202
+ return next;
203
+ });
204
+ }, [persist]);
205
+ const setOpenIds = useCallback((ids) => {
206
+ setLocalOpen(() => {
207
+ const next = new Set(ids);
58
208
  persist(next);
59
209
  return next;
60
210
  });
61
211
  }, [persist]);
212
+ const openIds = useMemo(() => sectionIds.filter((id) => effectiveOpen.has(id)),
213
+ // effectiveOpen is derived from localOpen; depending on the Set identity
214
+ // directly would recompute on every render.
215
+ // eslint-disable-next-line react-hooks/exhaustive-deps
216
+ [sectionIds, localOpen, fallbackOpen]);
217
+ return { isOpen, toggle, open, setOpenIds, openIds };
218
+ }
219
+ /**
220
+ * @deprecated Prefer `useSectionState`, which is namespaced per surface,
221
+ * supports several sections open at once, and defaults to section 1 open.
222
+ *
223
+ * Kept so the existing dashboard call sites need no edit. Semantics are
224
+ * byte-identical to the original: default all-collapsed (`defaultOpen: []`)
225
+ * and accordion (`mode: "single"`).
226
+ */
227
+ export function useCollapsedSections({ sectionIds, scope = "legacy:collapsed-sections", }) {
228
+ const { isOpen, toggle } = useSectionState({
229
+ scope,
230
+ sectionIds,
231
+ defaultOpen: [],
232
+ mode: "single",
233
+ });
234
+ const isCollapsed = useCallback((id) => !isOpen(id), [isOpen]);
62
235
  return { isCollapsed, toggle };
63
236
  }