@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,33 +1,26 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
3
  import { normalizeError } from "../../../errors/normalize";
4
- import { Row } from "@mohasinac/appkit/ui";
5
4
  import { useApiMutation } from "@mohasinac/appkit/client";
6
5
  import React from "react";
7
6
  import { useQuery } from "@tanstack/react-query";
8
- import { z } from "zod";
9
- import { Button, ConfirmDeleteModal, Div, Form, IconButton, Input, Label, Select, Span, Stack, StackedViewShell, Text, Toggle, useToast } from "../../../ui";
7
+ import { ConfirmDeleteModal, Div, StackedViewShell, useToast } from "../../../ui";
10
8
  import { FieldInput, FormErrorSummary } from "../../../ui/forms";
11
9
  import { apiClient } from "../../../http";
12
10
  import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
11
+ import { adminCouponFormSchema } from "../../seller/schemas/coupon-form";
12
+ import { FormShellContext, useFormShellState } from "../../../ui/forms/FormShell";
13
+ import { applyZodIssues } from "../../../ui/forms/apply-zod-issues";
14
+ import { buildSectionsFromSchema, visibleValues } from "../../shell/build-sections";
15
+ import { SectionForm, useSectionFormNav } from "../../shell/SectionForm";
13
16
  import { ProductInlineSelect } from "../../seller/components/ProductInlineSelect";
14
- import { CategoryInlineSelect } from "../../seller/components/CategoryInlineSelect";
17
+ import { CategoryChipPicker } from "../../seller/components/CategoryChipPicker";
15
18
  const __P = {
16
19
  p4: "p-[var(--appkit-space-4)]",
17
20
  };
18
21
  const __O = {
19
22
  yAuto: "overflow-y-auto",
20
23
  };
21
- // --- Types -------------------------------------------------------------------
22
- const couponFormSchema = z.object({
23
- code: z.string().min(2, "Coupon code is required").max(40).regex(/^[A-Z0-9_-]+$/i),
24
- name: z.string().min(1).max(120),
25
- type: z.enum(["percentage", "fixed", "free_shipping", "buy_x_get_y"]),
26
- scope: z.enum(["admin", "seller"]),
27
- validity: z.object({
28
- isActive: z.boolean(),
29
- }).passthrough(),
30
- }).passthrough();
31
24
  const TYPE_OPTIONS = [
32
25
  { label: "Percentage Discount", value: "percentage" },
33
26
  { label: "Fixed Amount Discount", value: "fixed" },
@@ -49,42 +42,33 @@ function toDateInputValue(val) {
49
42
  return "";
50
43
  }
51
44
  }
52
- function CouponDiscountFields({ type, discountValue, setDiscountValue, maxDiscount, setMaxDiscount, minPurchase, setMinPurchase, buyQty, setBuyQty, getQty, setGetQty, discountLabel, }) {
53
- return (_jsxs(_Fragment, { children: [type !== "free_shipping" && type !== "buy_x_get_y" && (_jsx(Input, { label: discountLabel, value: discountValue, onChange: (e) => setDiscountValue(e.target.value), type: "number", min: 0, required: true, placeholder: type === "percentage" ? "e.g. 20" : "e.g. 5000" })), type === "percentage" && (_jsx(Input, { label: "Max discount cap (\u20B9, optional)", value: maxDiscount, onChange: (e) => setMaxDiscount(e.target.value), type: "number", min: 0, placeholder: "e.g. 200", helperText: "Leave blank for no cap." })), (type === "percentage" || type === "fixed") && (_jsx(Input, { label: "Min order value (\u20B9, optional)", value: minPurchase, onChange: (e) => setMinPurchase(e.target.value), type: "number", min: 0, placeholder: "e.g. 500", helperText: "Leave blank for no minimum." })), type === "buy_x_get_y" && (_jsxs(Div, { layout: "grid", gap: "4", className: "grid-cols-2", children: [_jsx(Input, { label: "Buy quantity", value: buyQty, onChange: (e) => setBuyQty(e.target.value), type: "number", min: 1, required: true }), _jsx(Input, { label: "Get quantity", value: getQty, onChange: (e) => setGetQty(e.target.value), type: "number", min: 1, required: true })] }))] }));
54
- }
55
- function CouponValidityFields({ startDate, setStartDate, endDate, setEndDate, isActive, setIsActive, totalLimit, setTotalLimit, perUserLimit, setPerUserLimit, isEdit, currentUsage, }) {
56
- return (_jsxs(_Fragment, { children: [_jsxs(Div, { layout: "grid", gap: "4", className: "grid-cols-2", children: [_jsx(Input, { label: "Total usage limit (optional)", value: totalLimit, onChange: (e) => setTotalLimit(e.target.value), type: "number", min: 0, placeholder: "Unlimited" }), _jsx(Input, { label: "Per-user limit (optional)", value: perUserLimit, onChange: (e) => setPerUserLimit(e.target.value), type: "number", min: 0, placeholder: "Unlimited" })] }), isEdit && (_jsx(Input, { label: "Current usage", value: String(currentUsage), disabled: true, helperText: "Read-only \u2014 updated by orders." })), _jsxs(Div, { layout: "grid", gap: "4", className: "grid-cols-2", children: [_jsx(Input, { label: "Start date", value: startDate, onChange: (e) => setStartDate(e.target.value), type: "date", required: true }), _jsx(Input, { label: "End date (optional)", value: endDate, onChange: (e) => setEndDate(e.target.value), type: "date", helperText: "Leave blank for no expiry." })] }), _jsx(Toggle, { label: "Active", checked: isActive, onChange: setIsActive })] }));
57
- }
58
45
  // --- Component ---------------------------------------------------------------
59
46
  export function AdminCouponEditorView({ couponId, onSaved, onDeleted, embedded, initialData, ...rest }) {
60
47
  const isEdit = Boolean(couponId);
61
- // --- form state ---
62
- const [code, setCode] = React.useState("");
48
+ const [form, setForm] = React.useState({
49
+ code: "",
50
+ name: "",
51
+ description: "",
52
+ type: "percentage",
53
+ value: "",
54
+ maxDiscount: "",
55
+ minPurchase: "",
56
+ buyQuantity: "1",
57
+ getQuantity: "1",
58
+ totalLimit: "",
59
+ perUserLimit: "",
60
+ currentUsage: 0,
61
+ startDate: new Date().toISOString().split("T")[0],
62
+ endDate: "",
63
+ isActive: true,
64
+ firstTimeUserOnly: false,
65
+ appliesToAuctions: false,
66
+ applicableProducts: [],
67
+ applicableCategories: [],
68
+ });
63
69
  const [codeManual, setCodeManual] = React.useState(false);
64
- const [name, setName] = React.useState("");
65
- const [description, setDescription] = React.useState("");
66
- const [type, setType] = React.useState("percentage");
67
- // discount
68
- const [discountValue, setDiscountValue] = React.useState("");
69
- const [maxDiscount, setMaxDiscount] = React.useState("");
70
- const [minPurchase, setMinPurchase] = React.useState("");
71
- // bxgy (buy_x_get_y only)
72
- const [buyQty, setBuyQty] = React.useState("1");
73
- const [getQty, setGetQty] = React.useState("1");
74
- // usage
75
- const [totalLimit, setTotalLimit] = React.useState("");
76
- const [perUserLimit, setPerUserLimit] = React.useState("");
77
- const [currentUsage, setCurrentUsage] = React.useState(0);
78
- // validity
79
- const [startDate, setStartDate] = React.useState(new Date().toISOString().split("T")[0]);
80
- const [endDate, setEndDate] = React.useState("");
81
- const [isActive, setIsActive] = React.useState(true);
82
- // restrictions
83
- const [firstTimeOnly, setFirstTimeOnly] = React.useState(false);
84
- const [appliesToAuctions, setAppliesToAuctions] = React.useState(false);
85
- const [applicableProducts, setApplicableProducts] = React.useState([]);
86
- const [applicableCategories, setApplicableCategories] = React.useState([]);
87
70
  const [deleteConfirmOpen, setDeleteConfirmOpen] = React.useState(false);
71
+ const patch = (partial) => setForm((prev) => Object.assign({}, prev, partial));
88
72
  const { showToast } = useToast();
89
73
  // --- load existing data (edit mode) ---
90
74
  const couponQuery = useQuery({
@@ -104,74 +88,91 @@ export function AdminCouponEditorView({ couponId, onSaved, onDeleted, embedded,
104
88
  const c = couponQuery.data;
105
89
  if (!c)
106
90
  return;
107
- setCode(c.code ?? "");
108
91
  setCodeManual(true);
109
- setName(c.name ?? "");
110
- setDescription(c.description ?? "");
111
- setType(c.type ?? "percentage");
112
- setDiscountValue(c.discount?.value !== undefined ? String(c.discount.value) : "");
113
- setMaxDiscount(c.discount?.maxDiscount !== undefined ? String(c.discount.maxDiscount) : "");
114
- setMinPurchase(c.discount?.minPurchase !== undefined ? String(c.discount.minPurchase) : "");
115
- setBuyQty(c.bxgy?.buyQuantity !== undefined ? String(c.bxgy.buyQuantity) : "1");
116
- setGetQty(c.bxgy?.getQuantity !== undefined ? String(c.bxgy.getQuantity) : "1");
117
- setTotalLimit(c.usage?.totalLimit !== undefined ? String(c.usage.totalLimit) : "");
118
- setPerUserLimit(c.usage?.perUserLimit !== undefined ? String(c.usage.perUserLimit) : "");
119
- setCurrentUsage(c.usage?.currentUsage ?? 0);
120
- setStartDate(toDateInputValue(c.validity?.startDate));
121
- setEndDate(toDateInputValue(c.validity?.endDate));
122
- setIsActive(c.validity?.isActive ?? false);
123
- setFirstTimeOnly(c.restrictions?.firstTimeUserOnly ?? false);
124
- setApplicableProducts(Array.isArray(c.restrictions?.applicableProducts) ? c.restrictions.applicableProducts : []);
125
- setApplicableCategories(Array.isArray(c.restrictions?.applicableCategories) ? c.restrictions.applicableCategories : []);
126
- setAppliesToAuctions(c.applicableToAuctions ?? false);
92
+ patch({
93
+ code: c.code ?? "",
94
+ name: c.name ?? "",
95
+ description: c.description ?? "",
96
+ type: c.type ?? "percentage",
97
+ value: c.discount?.value !== undefined ? String(c.discount.value) : "",
98
+ maxDiscount: c.discount?.maxDiscount !== undefined ? String(c.discount.maxDiscount) : "",
99
+ minPurchase: c.discount?.minPurchase !== undefined ? String(c.discount.minPurchase) : "",
100
+ buyQuantity: c.bxgy?.buyQuantity !== undefined ? String(c.bxgy.buyQuantity) : "1",
101
+ getQuantity: c.bxgy?.getQuantity !== undefined ? String(c.bxgy.getQuantity) : "1",
102
+ totalLimit: c.usage?.totalLimit !== undefined ? String(c.usage.totalLimit) : "",
103
+ perUserLimit: c.usage?.perUserLimit !== undefined ? String(c.usage.perUserLimit) : "",
104
+ currentUsage: c.usage?.currentUsage ?? 0,
105
+ startDate: toDateInputValue(c.validity?.startDate),
106
+ endDate: toDateInputValue(c.validity?.endDate),
107
+ isActive: c.validity?.isActive ?? false,
108
+ firstTimeUserOnly: c.restrictions?.firstTimeUserOnly ?? false,
109
+ applicableProducts: Array.isArray(c.restrictions?.applicableProducts)
110
+ ? c.restrictions.applicableProducts
111
+ : [],
112
+ applicableCategories: Array.isArray(c.restrictions?.applicableCategories)
113
+ ? c.restrictions.applicableCategories
114
+ : [],
115
+ appliesToAuctions: c.applicableToAuctions ?? false,
116
+ });
127
117
  }, [couponQuery.data]);
128
118
  const handleNameChange = (value) => {
129
- setName(value);
130
- if (!codeManual)
131
- setCode(toCouponCode(value));
119
+ patch(codeManual ? { name: value } : { name: value, code: toCouponCode(value) });
132
120
  };
133
121
  // --- save ---
134
122
  const saveMutation = useApiMutation({
135
123
  errorMessage: "Failed to save coupon.",
136
124
  mutationFn: async () => {
125
+ /*
126
+ * `visibleValues` is what makes hidden-implies-not-sent structural here.
127
+ * The cap, the minimum and the buy/get pair each render only for certain
128
+ * types and used to be filtered again by a hand-written ternary in this
129
+ * builder — correct, but a second statement of the same rule, which is
130
+ * how it drifted on the seller side and then here.
131
+ */
132
+ const draft = visibleValues(adminCouponFormSchema, form);
133
+ const num = (v) => (v && v.trim() !== "" ? Number(v) : undefined);
137
134
  const payload = {
138
- name,
139
- description: description || undefined,
140
- type,
135
+ name: form.name,
136
+ description: form.description || undefined,
137
+ type: form.type,
141
138
  discount: {
142
- value: discountValue !== "" ? Number(discountValue) : 0,
143
- maxDiscount: maxDiscount !== "" ? Number(maxDiscount) : undefined,
144
- minPurchase: minPurchase !== "" ? Number(minPurchase) : undefined,
139
+ value: num(draft.value) ?? 0,
140
+ maxDiscount: num(draft.maxDiscount),
141
+ minPurchase: num(draft.minPurchase),
145
142
  },
146
- ...(type === "buy_x_get_y" && {
143
+ ...(form.type === "buy_x_get_y" && {
147
144
  bxgy: {
148
- buyQuantity: Number(buyQty) || 1,
149
- getQuantity: Number(getQty) || 1,
145
+ buyQuantity: num(draft.buyQuantity) ?? 1,
146
+ getQuantity: num(draft.getQuantity) ?? 1,
150
147
  },
151
148
  }),
152
149
  usage: {
153
- totalLimit: totalLimit !== "" ? Number(totalLimit) : undefined,
154
- perUserLimit: perUserLimit !== "" ? Number(perUserLimit) : undefined,
155
- currentUsage,
150
+ totalLimit: num(form.totalLimit),
151
+ perUserLimit: num(form.perUserLimit),
152
+ currentUsage: form.currentUsage,
156
153
  },
157
154
  validity: {
158
- startDate,
159
- endDate: endDate || undefined,
160
- isActive,
155
+ startDate: form.startDate,
156
+ endDate: form.endDate || undefined,
157
+ isActive: form.isActive,
161
158
  },
162
159
  restrictions: {
163
- firstTimeUserOnly: firstTimeOnly,
164
- ...(applicableProducts.length > 0 && { applicableProducts }),
165
- ...(applicableCategories.length > 0 && { applicableCategories }),
160
+ firstTimeUserOnly: form.firstTimeUserOnly,
161
+ ...(form.applicableProducts.length > 0 && {
162
+ applicableProducts: form.applicableProducts,
163
+ }),
164
+ ...(form.applicableCategories.length > 0 && {
165
+ applicableCategories: form.applicableCategories,
166
+ }),
166
167
  },
167
- applicableToAuctions: appliesToAuctions,
168
+ applicableToAuctions: form.appliesToAuctions,
168
169
  };
169
170
  if (isEdit) {
170
171
  return apiClient.patch(ADMIN_ENDPOINTS.COUPON_BY_ID(couponId), payload);
171
172
  }
172
173
  return apiClient.post(ADMIN_ENDPOINTS.COUPONS, {
173
174
  ...payload,
174
- code: code || toCouponCode(name),
175
+ code: form.code || toCouponCode(form.name),
175
176
  createdBy: "admin",
176
177
  });
177
178
  },
@@ -193,28 +194,63 @@ export function AdminCouponEditorView({ couponId, onSaved, onDeleted, embedded,
193
194
  },
194
195
  });
195
196
  const isSubmitting = saveMutation.isPending || couponQuery.isLoading;
196
- const canSave = Boolean(name && (isEdit ? true : code) && discountValue !== "" && startDate);
197
- // discount label varies by type
198
- const discountLabel = type === "percentage"
199
- ? "Discount percentage (%)"
200
- : type === "fixed"
201
- ? "Discount amount (₹)"
202
- : "Discount value";
203
- const formSection = (_jsxs(_Fragment, { children: [_jsx(Form, { schema: couponFormSchema, onSubmit: (e) => e.preventDefault(), spacing: "md", children: ({ setFieldError, clearErrors }) => (_jsxs(_Fragment, { children: [_jsx(Select, { label: "Coupon type", options: TYPE_OPTIONS, value: type, onValueChange: (v) => setType(v), required: true }), _jsx(FieldInput, { name: "name", label: "Campaign name", value: name, onChange: (v) => handleNameChange(v), required: true, placeholder: "e.g. Summer Sale 20%" }), !isEdit && (_jsx(Input, { label: "Coupon code", value: code, onChange: (e) => {
204
- setCode(toCouponCode(e.target.value));
205
- setCodeManual(true);
206
- }, required: true, placeholder: "e.g. SUMMER20", helperText: "Auto-generated from name. Uppercase alphanumeric + hyphens only." })), isEdit && (_jsx(Input, { label: "Coupon code", value: code, disabled: true, helperText: "Code cannot be changed after creation." })), _jsx(Input, { label: "Description (optional)", value: description, onChange: (e) => setDescription(e.target.value), placeholder: "Internal notes about this coupon" }), _jsx(CouponDiscountFields, { type: type, discountValue: discountValue, setDiscountValue: setDiscountValue, maxDiscount: maxDiscount, setMaxDiscount: setMaxDiscount, minPurchase: minPurchase, setMinPurchase: setMinPurchase, buyQty: buyQty, setBuyQty: setBuyQty, getQty: getQty, setGetQty: setGetQty, discountLabel: discountLabel }), _jsx(CouponValidityFields, { startDate: startDate, setStartDate: setStartDate, endDate: endDate, setEndDate: setEndDate, isActive: isActive, setIsActive: setIsActive, totalLimit: totalLimit, setTotalLimit: setTotalLimit, perUserLimit: perUserLimit, setPerUserLimit: setPerUserLimit, isEdit: isEdit, currentUsage: currentUsage }), _jsx(Toggle, { label: "First-time users only", checked: firstTimeOnly, onChange: setFirstTimeOnly }), _jsx(Toggle, { label: "Applies to auctions", checked: appliesToAuctions, onChange: setAppliesToAuctions }), _jsxs(Stack, { gap: "xs", children: [_jsx(Label, { size: "sm", weight: "medium", color: "primary", children: "Applicable products (optional)" }), _jsx(ProductInlineSelect, { scope: "admin", multiple: true, value: applicableProducts, onChange: setApplicableProducts, placeholder: "Restrict to specific products\u2026" }), _jsx(Text, { size: "xs", color: "muted", children: "Leave empty to apply the coupon to every eligible product." })] }), _jsxs(Stack, { gap: "xs", children: [_jsx(Label, { size: "sm", weight: "medium", color: "primary", children: "Applicable categories (optional)" }), _jsx(CategoryInlineSelect, { value: "", onChange: (id) => {
207
- if (!id || applicableCategories.includes(id))
208
- return;
209
- setApplicableCategories([...applicableCategories, id]);
210
- }, allowCreate: true, placeholder: "Add a category\u2026" }), applicableCategories.length > 0 && (_jsx(Row, { wrap: true, gap: "sm", padding: "t-2xs", children: applicableCategories.map((cid) => (_jsxs(Span, { layout: "inline-flex", gap: "xs", border: "strong", padding: "pill-sm", rounded: "full", surface: "muted", color: "primary", size: "xs", children: [cid, _jsx(IconButton, { "aria-label": `Remove ${cid}`, variant: "ghost", size: "sm", onClick: () => setApplicableCategories(applicableCategories.filter((c) => c !== cid)), icon: "\u00D7" })] }, cid))) })), _jsx(Text, { size: "xs", color: "muted", children: "Leave empty to apply the coupon to every category." })] }), _jsx(FormErrorSummary, {}), _jsxs(Row, { gap: "3", padding: "t-xs", children: [_jsx(Button, { type: "submit", isLoading: isSubmitting, disabled: !canSave || isSubmitting, onClick: () => {
211
- clearErrors();
212
- if (!name.trim()) {
213
- setFieldError("name", "Campaign name is required");
214
- return;
215
- }
216
- saveMutation.mutate();
217
- }, children: isEdit ? "Save changes" : "Create coupon" }), isEdit && (_jsx(Button, { type: "button", variant: "danger", isLoading: deleteMutation.isPending, onClick: () => setDeleteConfirmOpen(true), children: "Delete coupon" }))] })] })) }, "coupon-form"), deleteConfirmOpen && (_jsx(ConfirmDeleteModal, { isOpen: true, title: "Delete Coupon", message: "Delete this coupon? This cannot be undone.", onConfirm: () => { deleteMutation.mutate(); setDeleteConfirmOpen(false); }, onClose: () => setDeleteConfirmOpen(false), isDeleting: deleteMutation.isPending }))] }));
197
+ /*
198
+ * `canSave` is gone.
199
+ *
200
+ * It was `name && (isEdit || code) && discountValue !== "" && startDate` — a
201
+ * hand-rolled restatement of four rules that disagreed with the schema on
202
+ * every other one, disabling the button with no way to tell WHICH field it
203
+ * was waiting on. Safe to delete only now, in the same commit that makes the
204
+ * schema actually execute: before this the form parsed a five-field
205
+ * `.passthrough()` stub, so removing the gate would have replaced a
206
+ * too-strict check with none at all.
207
+ */
208
+ const sections = React.useMemo(() => buildSectionsFromSchema(adminCouponFormSchema, {
209
+ options: { type: TYPE_OPTIONS.map((o) => ({ value: String(o.value), label: o.label })) },
210
+ renderers: {
211
+ // Typing the campaign name fills the code until the admin edits it.
212
+ name: ({ values, errors }) => (_jsx(FieldInput, { name: "name", label: "Campaign name", required: true, value: values.name, error: errors.name, onChange: handleNameChange })),
213
+ code: ({ values, onChange, errors }) => (_jsx(FieldInput, { name: "code", label: "Coupon code", required: true, disabled: isEdit, hint: isEdit ? "A code cannot change once customers have it." : undefined, value: values.code, error: errors.code, onChange: (v) => {
214
+ setCodeManual(true);
215
+ onChange({ code: toCouponCode(v) });
216
+ } })),
217
+ // The label names the unit, which the type decides.
218
+ value: ({ values, onChange, errors }) => (_jsx(FieldInput, { name: "value", label: values.type === "percentage"
219
+ ? "Discount percentage (%)"
220
+ : values.type === "fixed"
221
+ ? "Discount amount (₹)"
222
+ : "Discount value", type: "number", required: true, value: values.value, error: errors.value, onChange: (v) => onChange({ value: v }) })),
223
+ applicableProducts: ({ values, onChange }) => (_jsx(ProductInlineSelect, { multiple: true, scope: "admin", value: values.applicableProducts, onChange: (ids) => onChange({ applicableProducts: ids }) })),
224
+ /*
225
+ * `CategoryInlineSelect` is single-select, so the multi-value list
226
+ * needs an add-one/remove-one chip arrangement around it. That was
227
+ * written identically here and in the SELLER coupon editor, so it is
228
+ * now one shared `CategoryChipPicker`.
229
+ */
230
+ applicableCategories: ({ values, onChange }) => (_jsx(CategoryChipPicker, { selected: values.applicableCategories, onSelectedChange: (next) => onChange({ applicableCategories: next }) })),
231
+ },
232
+ }),
233
+ // eslint-disable-next-line react-hooks/exhaustive-deps
234
+ [isEdit, codeManual]);
235
+ const nav = useSectionFormNav(sections, form, { scope: "admin:coupon-editor" });
236
+ const { shellCtx, setFieldError, clearErrors } = useFormShellState(adminCouponFormSchema, {
237
+ sections: nav.sectionMeta,
238
+ onGoToSection: nav.goToSection,
239
+ fieldToSectionIndex: nav.fieldToSectionIndex,
240
+ });
241
+ const onSubmit = () => {
242
+ clearErrors();
243
+ const parsed = adminCouponFormSchema.safeParse(visibleValues(adminCouponFormSchema, form));
244
+ if (!parsed.success) {
245
+ applyZodIssues(parsed.error.issues, setFieldError);
246
+ return;
247
+ }
248
+ saveMutation.mutate();
249
+ };
250
+ const formSection = (_jsxs(_Fragment, { children: [_jsxs(FormShellContext.Provider, { value: shellCtx, children: [_jsx(FormErrorSummary, {}), _jsx(SectionForm, { sections: sections, values: form, onChange: patch, onSubmit: onSubmit, schema: adminCouponFormSchema, openIds: nav.openIds, onOpenChange: nav.setOpenIds, isLoading: isSubmitting, submitLabel: isEdit ? "Save changes" : "Create coupon", destructiveAction: isEdit ? { label: "Delete coupon", onClick: () => setDeleteConfirmOpen(true) } : undefined })] }), deleteConfirmOpen && (_jsx(ConfirmDeleteModal, { isOpen: true, title: "Delete Coupon", message: "Delete this coupon? This cannot be undone.", onConfirm: () => {
251
+ deleteMutation.mutate();
252
+ setDeleteConfirmOpen(false);
253
+ }, onClose: () => setDeleteConfirmOpen(false), isDeleting: deleteMutation.isPending }))] }));
218
254
  if (embedded) {
219
255
  return _jsx(Div, { className: `${__O.yAuto} ${__P.p4}`, children: formSection });
220
256
  }
@@ -1,87 +1,119 @@
1
1
  "use client";
2
- import { jsxs as _jsxs, jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
3
  import { useApiMutation } from "@mohasinac/appkit/client";
4
4
  import React from "react";
5
5
  import { useQueryClient } from "@tanstack/react-query";
6
- import { Button, Checkbox, ConfirmDeleteModal, Details, Div, Form, FormActions, Input, Label, Select, SideDrawer, Span, Stack, Summary, Text, useToast } from "../../../ui";
6
+ import { Checkbox, ConfirmDeleteModal, Details, Div, PaginatedSelect, SideDrawer, Span, Stack, Summary, Text, useToast } from "../../../ui";
7
7
  import { apiClient } from "../../../http";
8
8
  import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
9
9
  import { PERMISSION_GROUPS, PERMISSION_DOMAINS, getPermissionsForDomain, formatPermLabel, } from "../../auth/permissions/constants";
10
- import { employeeInviteSchema } from "../schemas/admin-ops-forms";
10
+ import { employeeInviteSchema, employeePermissionsSchema, } from "../schemas/admin-ops-forms";
11
11
  import { FormErrorSummary } from "../../../ui/forms/FormErrorSummary";
12
+ import { FormShellContext, useFormShellState } from "../../../ui/forms/FormShell";
13
+ import { applyZodIssues } from "../../../ui/forms/apply-zod-issues";
14
+ import { buildSectionsFromSchema, visibleValues } from "../../shell/build-sections";
15
+ import { SectionForm, useSectionFormNav } from "../../shell/SectionForm";
12
16
  const __O = {
13
17
  yAuto: "overflow-y-auto",
14
18
  };
15
19
  // --- Helpers -----------------------------------------------------------------
20
+ /** Two acronyms the generic humaniser would render as "Seo" / "Qa". */
21
+ const GROUP_LABEL_OVERRIDES = {
22
+ seo_manager: "SEO Manager",
23
+ trust_and_safety: "Trust & Safety",
24
+ };
25
+ /**
26
+ * Permission groups, DERIVED from the presets rather than restated.
27
+ *
28
+ * The hand-written array this replaces listed eighteen of the nineteen —
29
+ * `maintenance_employee` was simply absent, so that preset could never be
30
+ * assigned from this drawer even though `PERMISSION_GROUPS` defines it and the
31
+ * route accepts it. Root Cause #61, on the permission-group axis.
32
+ *
33
+ * Nineteen options is well over the five-option threshold, so the generator
34
+ * renders it as a `PaginatedSelect`: it was a native `<Select>` with no
35
+ * `name`, which is both unsearchable and unreachable by `applyZodIssues`.
36
+ */
16
37
  const GROUP_OPTIONS = [
17
- { label: "Content Moderator", value: "content_moderator" },
18
- { label: "Review Manager", value: "review_manager" },
19
- { label: "Blog Poster", value: "blog_poster" },
20
- { label: "Community Manager", value: "community_manager" },
21
- { label: "Event Handler", value: "event_handler" },
22
- { label: "Newsletter Manager", value: "newsletter_manager" },
23
- { label: "SEO Manager", value: "seo_manager" },
24
- { label: "Ad Manager", value: "ad_manager" },
25
- { label: "Site Manager", value: "site_manager" },
26
- { label: "Catalog Manager", value: "catalog_manager" },
27
- { label: "Finance Manager", value: "finance_manager" },
28
- { label: "Data Analyst", value: "data_analyst" },
29
- { label: "Customer Support", value: "customer_support" },
30
- { label: "Support Agent", value: "support_agent" },
31
- { label: "Store Onboarding", value: "store_onboarding" },
32
- { label: "Trust & Safety", value: "trust_and_safety" },
33
- { label: "Auction Monitor", value: "auction_monitor" },
34
- { label: "Scam Moderator", value: "scam_moderator" },
38
+ ...Object.keys(PERMISSION_GROUPS).map((value) => ({
39
+ value,
40
+ label: GROUP_LABEL_OVERRIDES[value] ??
41
+ value.replace(/_/g, " ").replace(/\b\w/g, (c) => c.toUpperCase()),
42
+ })),
35
43
  { label: "Custom", value: "custom" },
36
44
  ];
45
+ /**
46
+ * The permission-group picker.
47
+ *
48
+ * Its own component because choosing a group is not a plain field write — it
49
+ * replaces the permission set with that group's preset, which the generator's
50
+ * default `onChange` cannot know about. Module level rather than inline for
51
+ * the DEEP_NESTING reason.
52
+ */
53
+ function FieldSelectGroup({ value, error, onChange, }) {
54
+ return (_jsxs(Stack, { gap: "xs", children: [_jsx(Text, { size: "sm", weight: "medium", color: "muted", children: "Permission group *" }), _jsx(PaginatedSelect, { value: value, options: GROUP_OPTIONS, onChange: (next) => onChange(next ?? "custom"), placeholder: "Choose a permission group\u2026", searchPlaceholder: "Search groups\u2026", ariaLabel: "Permission group" }), _jsx(Text, { size: "xs", color: "muted", children: "Selecting a group auto-fills the permissions below. You can still customise individual permissions." }), error && (_jsx(Text, { size: "xs", color: "error", role: "alert", children: error }))] }));
55
+ }
56
+ /**
57
+ * The permission matrix.
58
+ *
59
+ * An array rather than a `Set` now: it is a schema field, and a `Set` is not
60
+ * JSON — it would serialise to `{}` at any payload boundary that forgot to
61
+ * convert it, which the two mutations previously did by hand.
62
+ */
37
63
  function PermissionDomainsPanel({ permissions, togglePerm }) {
38
- return (_jsxs(Stack, { gap: "sm", children: [_jsxs(Span, { size: "sm", weight: "medium", color: "muted", children: ["Permissions", _jsxs(Span, { size: "xs", weight: "normal", className: "ml-2", color: "muted", children: ["(", permissions.size, " selected)"] })] }), _jsx(Div, { className: `divide-y divide-zinc-100 divide-[var(--appkit-color-border)] max-h-[42vh] ${__O.yAuto}`, rounded: "xl", border: "default", children: PERMISSION_DOMAINS.map((domain) => {
64
+ const selected = new Set(permissions);
65
+ return (_jsxs(Stack, { gap: "sm", children: [_jsxs(Span, { size: "sm", weight: "medium", color: "muted", children: ["Permissions", _jsxs(Span, { size: "xs", weight: "normal", className: "ml-2", color: "muted", children: ["(", selected.size, " selected)"] })] }), _jsx(Div, { className: `divide-y divide-zinc-100 divide-[var(--appkit-color-border)] max-h-[42vh] ${__O.yAuto}`, rounded: "xl", border: "default", children: PERMISSION_DOMAINS.map((domain) => {
39
66
  const domainPerms = getPermissionsForDomain(domain.prefix);
40
67
  if (domainPerms.length === 0)
41
68
  return null;
42
- const checked = domainPerms.filter((p) => permissions.has(p)).length;
43
- return (_jsxs(Details, { className: "group", children: [_jsxs(Summary, { paddingX: "x-sm", paddingY: "y-xs", weight: "semibold", color: "muted", layout: "flex", align: "center", justify: "between", className: "uppercase tracking-wide hover:bg-[var(--appkit-color-bg)] transition-colors", children: [_jsx(Span, { size: "xs", children: domain.label }), _jsxs(Span, { size: "xs", weight: "normal", className: "normal-case", color: "faint", children: [checked, "/", domainPerms.length] })] }), _jsx(Div, { layout: "grid", paddingY: "y-xs-tall", className: "grid-cols-2 gap-x-2 gap-y-1.5", surface: "muted", padding: "x-sm", children: domainPerms.map((perm) => (_jsxs("label", { className: "flex items-center gap-[var(--appkit-space-2)] cursor-pointer text-[length:var(--appkit-text-xs)] text-[var(--appkit-color-text-muted)]", children: [_jsx(Checkbox, { bare: true, checked: permissions.has(perm), onChange: () => togglePerm(perm), className: "h-3.5 w-3.5 rounded border-[var(--appkit-color-border)] accent-primary" }), formatPermLabel(perm)] }, perm))) })] }, domain.prefix));
69
+ const checked = domainPerms.filter((p) => selected.has(p)).length;
70
+ return (_jsxs(Details, { className: "group", children: [_jsxs(Summary, { paddingX: "x-sm", paddingY: "y-xs", weight: "semibold", color: "muted", layout: "flex", align: "center", justify: "between", className: "uppercase tracking-wide hover:bg-[var(--appkit-color-bg)] transition-colors", children: [_jsx(Span, { size: "xs", children: domain.label }), _jsxs(Span, { size: "xs", weight: "normal", className: "normal-case", color: "faint", children: [checked, "/", domainPerms.length] })] }), _jsx(Div, { layout: "grid", paddingY: "y-xs-tall", className: "grid-cols-2 gap-x-2 gap-y-1.5", surface: "muted", padding: "x-sm", children: domainPerms.map((perm) => (_jsxs("label", { className: "flex items-center gap-[var(--appkit-space-2)] cursor-pointer text-[length:var(--appkit-text-xs)] text-[var(--appkit-color-text-muted)]", children: [_jsx(Checkbox, { bare: true, checked: selected.has(perm), onChange: () => togglePerm(perm), className: "h-3.5 w-3.5 rounded border-[var(--appkit-color-border)] accent-primary" }), formatPermLabel(perm)] }, perm))) })] }, domain.prefix));
44
71
  }) })] }));
45
72
  }
46
73
  // --- Component ---------------------------------------------------------------
47
74
  export function AdminEmployeeEditorView({ open, onClose, mode, userId, displayName, currentPermissionGroup, currentPermissions = [], }) {
48
75
  const queryClient = useQueryClient();
49
76
  const { showToast } = useToast();
50
- const [email, setEmail] = React.useState("");
51
- const [group, setGroup] = React.useState(currentPermissionGroup ?? "custom");
52
- const [permissions, setPermissions] = React.useState(new Set(currentPermissions));
77
+ const [form, setForm] = React.useState({
78
+ email: "",
79
+ permissionGroup: currentPermissionGroup ?? "custom",
80
+ permissions: currentPermissions,
81
+ });
53
82
  const [revokeOpen, setRevokeOpen] = React.useState(false);
54
83
  React.useEffect(() => {
55
84
  if (open) {
56
- setEmail("");
57
- setGroup(currentPermissionGroup ?? "custom");
58
- setPermissions(new Set(currentPermissions));
59
- }
60
- }, [open, currentPermissionGroup, currentPermissions]);
61
- const applyGroupPreset = (newGroup) => {
62
- setGroup(newGroup);
63
- if (newGroup !== "custom") {
64
- const preset = PERMISSION_GROUPS[newGroup] ?? [];
65
- setPermissions(new Set(preset));
85
+ setForm({
86
+ email: "",
87
+ permissionGroup: currentPermissionGroup ?? "custom",
88
+ permissions: currentPermissions,
89
+ });
66
90
  }
67
- };
68
- const togglePerm = (perm) => {
69
- setPermissions((prev) => {
70
- const next = new Set(prev);
71
- if (next.has(perm))
72
- next.delete(perm);
73
- else
74
- next.add(perm);
75
- return next;
76
- });
77
- };
91
+ // eslint-disable-next-line react-hooks/exhaustive-deps
92
+ }, [open, currentPermissionGroup]);
93
+ /**
94
+ * Choosing a group replaces the permission set with its preset; "Custom"
95
+ * leaves whatever is ticked alone, which is what makes it custom.
96
+ */
97
+ const applyGroupPreset = (newGroup) => setForm((prev) => ({
98
+ ...prev,
99
+ permissionGroup: newGroup,
100
+ permissions: newGroup === "custom"
101
+ ? prev.permissions
102
+ : [...(PERMISSION_GROUPS[newGroup] ?? [])],
103
+ }));
104
+ const togglePerm = (perm) => setForm((prev) => ({
105
+ ...prev,
106
+ permissions: prev.permissions.includes(perm)
107
+ ? prev.permissions.filter((p) => p !== perm)
108
+ : [...prev.permissions, perm],
109
+ }));
78
110
  const inviteMutation = useApiMutation({
79
111
  errorMessage: "Failed to invite employee",
80
112
  mutationFn: async () => {
81
113
  await apiClient.post(ADMIN_ENDPOINTS.TEAM, {
82
- email: email.trim(),
83
- permissionGroup: group,
84
- permissions: Array.from(permissions),
114
+ email: form.email.trim(),
115
+ permissionGroup: form.permissionGroup,
116
+ permissions: form.permissions,
85
117
  });
86
118
  },
87
119
  onSuccess: () => {
@@ -94,8 +126,8 @@ export function AdminEmployeeEditorView({ open, onClose, mode, userId, displayNa
94
126
  errorMessage: "Failed to update permissions",
95
127
  mutationFn: async () => {
96
128
  await apiClient.put(ADMIN_ENDPOINTS.TEAM_MEMBER(userId), {
97
- permissionGroup: group,
98
- permissions: Array.from(permissions),
129
+ permissionGroup: form.permissionGroup,
130
+ permissions: form.permissions,
99
131
  });
100
132
  },
101
133
  onSuccess: () => {
@@ -119,12 +151,49 @@ export function AdminEmployeeEditorView({ open, onClose, mode, userId, displayNa
119
151
  const isBusy = inviteMutation.isPending ||
120
152
  updateMutation.isPending ||
121
153
  revokeMutation.isPending;
122
- const handleSubmit = (e) => {
123
- e.preventDefault();
154
+ /*
155
+ * Two schemas, one derived from the other by omission.
156
+ *
157
+ * The email input renders only while inviting, and the full schema was
158
+ * parsed regardless — so an edit could only ever have failed on a control
159
+ * that was not on screen. Nothing noticed, because nothing parsed it.
160
+ */
161
+ const schema = mode === "invite" ? employeeInviteSchema : employeePermissionsSchema;
162
+ const sections = React.useMemo(() => buildSectionsFromSchema(schema, {
163
+ options: { permissionGroup: GROUP_OPTIONS },
164
+ renderers: {
165
+ /*
166
+ * The generator's own `onChange` is deliberately unused: choosing a
167
+ * group also replaces the permission set with its preset, which a
168
+ * plain field write cannot do.
169
+ */
170
+ permissionGroup: ({ values, errors }) => (_jsx(FieldSelectGroup, { value: values.permissionGroup, error: errors.permissionGroup, onChange: applyGroupPreset })),
171
+ permissions: ({ values }) => (_jsx(PermissionDomainsPanel, { permissions: values.permissions, togglePerm: togglePerm })),
172
+ },
173
+ }),
174
+ // eslint-disable-next-line react-hooks/exhaustive-deps
175
+ [schema]);
176
+ const nav = useSectionFormNav(sections, form, { scope: "admin:employee-editor" });
177
+ const { shellCtx, setFieldError, clearErrors } = useFormShellState(schema, {
178
+ sections: nav.sectionMeta,
179
+ onGoToSection: nav.goToSection,
180
+ fieldToSectionIndex: nav.fieldToSectionIndex,
181
+ });
182
+ const handleSubmit = () => {
183
+ clearErrors();
184
+ const parsed = schema.safeParse(visibleValues(schema, form));
185
+ if (!parsed.success) {
186
+ applyZodIssues(parsed.error.issues, setFieldError);
187
+ return;
188
+ }
124
189
  if (mode === "invite")
125
190
  inviteMutation.mutate();
126
191
  else
127
192
  updateMutation.mutate();
128
193
  };
129
- return (_jsxs(_Fragment, { children: [_jsx(SideDrawer, { isOpen: open, onClose: onClose, title: mode === "invite" ? "Invite Employee" : `Edit — ${displayName ?? "Employee"}`, children: _jsxs(Form, { schema: employeeInviteSchema, onSubmit: handleSubmit, spacing: "lg", padding: "md", children: [_jsx(FormErrorSummary, {}), mode === "invite" && (_jsxs(Stack, { gap: "xs", className: "", children: [_jsx(Label, { size: "sm", weight: "medium", color: "primary", children: "Email address" }), _jsx(Input, { type: "email", required: true, value: email, onChange: (e) => setEmail(e.target.value), placeholder: "employee@example.com" })] })), _jsxs(Stack, { gap: "xs", className: "", children: [_jsx(Label, { size: "sm", weight: "medium", color: "primary", children: "Permission group" }), _jsx(Select, { value: group, onChange: (e) => applyGroupPreset(e.target.value), options: GROUP_OPTIONS }), _jsx(Text, { size: "xs", color: "muted", children: "Selecting a group auto-fills the permissions below. You can still customise individual permissions." })] }), _jsx(PermissionDomainsPanel, { permissions: permissions, togglePerm: togglePerm }), _jsxs(FormActions, { children: [_jsx(Button, { type: "submit", variant: "primary", disabled: isBusy, isLoading: isBusy, children: mode === "invite" ? "Send Invite" : "Save" }), mode === "edit" && userId && (_jsx(Button, { type: "button", variant: "danger", disabled: isBusy, onClick: () => setRevokeOpen(true), children: "Revoke Access" }))] })] }) }), mode === "edit" && (_jsx(ConfirmDeleteModal, { isOpen: revokeOpen, onClose: () => setRevokeOpen(false), onConfirm: () => revokeMutation.mutate(), isDeleting: revokeMutation.isPending, title: "Revoke employee access?", message: `${displayName ?? "This employee"} will lose admin panel access immediately. Their user account remains active — only their role is reset to user.`, confirmText: "Revoke Access", variant: "danger" }))] }));
194
+ return (_jsxs(_Fragment, { children: [_jsx(SideDrawer, { isOpen: open, onClose: onClose, title: mode === "invite" ? "Invite Employee" : `Edit — ${displayName ?? "Employee"}`, children: _jsx(Div, { padding: "md", children: _jsxs(FormShellContext.Provider, { value: shellCtx, children: [_jsx(FormErrorSummary, {}), _jsx(SectionForm, { sections: sections, values: form, onChange: (partial) => setForm((prev) => ({ ...prev, ...partial })), onSubmit: handleSubmit, schema: schema, openIds: nav.openIds, onOpenChange: nav.setOpenIds, isLoading: isBusy, submitLabel: mode === "invite" ? "Send invite" : "Save", onCancel: onClose,
195
+ /* A drawer owns its own footer — see `useIsInsideOverlay`. */
196
+ bottomBar: false, destructiveAction: mode === "edit" && userId
197
+ ? { label: "Revoke access", onClick: () => setRevokeOpen(true), disabled: isBusy }
198
+ : undefined })] }) }) }), mode === "edit" && (_jsx(ConfirmDeleteModal, { isOpen: revokeOpen, onClose: () => setRevokeOpen(false), onConfirm: () => revokeMutation.mutate(), isDeleting: revokeMutation.isPending, title: "Revoke employee access?", message: `${displayName ?? "This employee"} will lose admin panel access immediately. Their user account remains active — only their role is reset to user.`, confirmText: "Revoke Access", variant: "danger" }))] }));
130
199
  }