@burdenoff/microfe-admin 2026.511.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 (370) hide show
  1. package/README.md +74 -0
  2. package/dist/admin/AdminRoot.d.ts +31 -0
  3. package/dist/admin/AdminRoot.d.ts.map +1 -0
  4. package/dist/admin/AdminRoot.js +175 -0
  5. package/dist/admin/AdminRoot.js.map +1 -0
  6. package/dist/admin/AdminRoutes.d.ts +29 -0
  7. package/dist/admin/AdminRoutes.d.ts.map +1 -0
  8. package/dist/admin/AdminRoutes.js +143 -0
  9. package/dist/admin/AdminRoutes.js.map +1 -0
  10. package/dist/admin/components/AccessDenied.d.ts +8 -0
  11. package/dist/admin/components/AccessDenied.d.ts.map +1 -0
  12. package/dist/admin/components/AccessDenied.js +28 -0
  13. package/dist/admin/components/AccessDenied.js.map +1 -0
  14. package/dist/admin/components/ActivityFeed.d.ts +20 -0
  15. package/dist/admin/components/ActivityFeed.d.ts.map +1 -0
  16. package/dist/admin/components/ActivityFeed.js +91 -0
  17. package/dist/admin/components/ActivityFeed.js.map +1 -0
  18. package/dist/admin/components/Charts.d.ts +33 -0
  19. package/dist/admin/components/Charts.d.ts.map +1 -0
  20. package/dist/admin/components/Charts.js +37 -0
  21. package/dist/admin/components/Charts.js.map +1 -0
  22. package/dist/admin/components/ConfirmDialog.d.ts +45 -0
  23. package/dist/admin/components/ConfirmDialog.d.ts.map +1 -0
  24. package/dist/admin/components/ConfirmDialog.js +4 -0
  25. package/dist/admin/components/DataTable.d.ts +64 -0
  26. package/dist/admin/components/DataTable.d.ts.map +1 -0
  27. package/dist/admin/components/DataTable.js +127 -0
  28. package/dist/admin/components/DataTable.js.map +1 -0
  29. package/dist/admin/components/FormSheet.d.ts +79 -0
  30. package/dist/admin/components/FormSheet.d.ts.map +1 -0
  31. package/dist/admin/components/FormSheet.js +111 -0
  32. package/dist/admin/components/FormSheet.js.map +1 -0
  33. package/dist/admin/components/MetricCard.d.ts +40 -0
  34. package/dist/admin/components/MetricCard.d.ts.map +1 -0
  35. package/dist/admin/components/MetricCard.js +57 -0
  36. package/dist/admin/components/MetricCard.js.map +1 -0
  37. package/dist/admin/components/PageLayout.d.ts +34 -0
  38. package/dist/admin/components/PageLayout.d.ts.map +1 -0
  39. package/dist/admin/components/PageLayout.js +31 -0
  40. package/dist/admin/components/PageLayout.js.map +1 -0
  41. package/dist/admin/components/PermissionGuard.d.ts +12 -0
  42. package/dist/admin/components/PermissionGuard.d.ts.map +1 -0
  43. package/dist/admin/components/PermissionGuard.js +18 -0
  44. package/dist/admin/components/PermissionGuard.js.map +1 -0
  45. package/dist/admin/components/RowActions.d.ts +41 -0
  46. package/dist/admin/components/RowActions.d.ts.map +1 -0
  47. package/dist/admin/components/RowActions.js +93 -0
  48. package/dist/admin/components/RowActions.js.map +1 -0
  49. package/dist/admin/components/StatusBadge.d.ts +35 -0
  50. package/dist/admin/components/StatusBadge.d.ts.map +1 -0
  51. package/dist/admin/components/StatusBadge.js +41 -0
  52. package/dist/admin/components/StatusBadge.js.map +1 -0
  53. package/dist/admin/components/Toast.d.ts +26 -0
  54. package/dist/admin/components/Toast.d.ts.map +1 -0
  55. package/dist/admin/components/Toast.js +133 -0
  56. package/dist/admin/components/Toast.js.map +1 -0
  57. package/dist/admin/components/forms/SubscriptionForm.d.ts +57 -0
  58. package/dist/admin/components/forms/SubscriptionForm.d.ts.map +1 -0
  59. package/dist/admin/components/forms/SubscriptionForm.js +299 -0
  60. package/dist/admin/components/forms/SubscriptionForm.js.map +1 -0
  61. package/dist/admin/components/forms/TenantForm.d.ts +68 -0
  62. package/dist/admin/components/forms/TenantForm.d.ts.map +1 -0
  63. package/dist/admin/components/forms/TenantForm.js +346 -0
  64. package/dist/admin/components/forms/TenantForm.js.map +1 -0
  65. package/dist/admin/components/forms/TicketForm.d.ts +67 -0
  66. package/dist/admin/components/forms/TicketForm.d.ts.map +1 -0
  67. package/dist/admin/components/forms/TicketForm.js +55 -0
  68. package/dist/admin/components/forms/TicketForm.js.map +1 -0
  69. package/dist/admin/components/forms/UserForm.d.ts +57 -0
  70. package/dist/admin/components/forms/UserForm.d.ts.map +1 -0
  71. package/dist/admin/components/forms/UserForm.js +253 -0
  72. package/dist/admin/components/forms/UserForm.js.map +1 -0
  73. package/dist/admin/components/forms/index.d.ts +5 -0
  74. package/dist/admin/components/forms/index.d.ts.map +1 -0
  75. package/dist/admin/components/forms/index.js +4 -0
  76. package/dist/admin/components/index.d.ts +15 -0
  77. package/dist/admin/components/index.d.ts.map +1 -0
  78. package/dist/admin/components/index.js +17 -0
  79. package/dist/admin/components/ui.d.ts +52 -0
  80. package/dist/admin/components/ui.d.ts.map +1 -0
  81. package/dist/admin/components/ui.js +140 -0
  82. package/dist/admin/components/ui.js.map +1 -0
  83. package/dist/admin/constants/index.d.ts +75 -0
  84. package/dist/admin/constants/index.d.ts.map +1 -0
  85. package/dist/admin/constants/index.js +90 -0
  86. package/dist/admin/constants/index.js.map +1 -0
  87. package/dist/admin/constants/permissions.d.ts +27 -0
  88. package/dist/admin/constants/permissions.d.ts.map +1 -0
  89. package/dist/admin/constants/permissions.js +23 -0
  90. package/dist/admin/constants/permissions.js.map +1 -0
  91. package/dist/admin/hooks/useAdminPermissions.d.ts +30 -0
  92. package/dist/admin/hooks/useAdminPermissions.d.ts.map +1 -0
  93. package/dist/admin/hooks/useAdminPermissions.js +56 -0
  94. package/dist/admin/hooks/useAdminPermissions.js.map +1 -0
  95. package/dist/admin/index.d.ts +31 -0
  96. package/dist/admin/index.d.ts.map +1 -0
  97. package/dist/admin/index.js +9 -0
  98. package/dist/admin/pages/AuditLogsPage.d.ts +5 -0
  99. package/dist/admin/pages/AuditLogsPage.d.ts.map +1 -0
  100. package/dist/admin/pages/AuditLogsPage.js +209 -0
  101. package/dist/admin/pages/AuditLogsPage.js.map +1 -0
  102. package/dist/admin/pages/BillingPage.d.ts +2 -0
  103. package/dist/admin/pages/BillingPage.d.ts.map +1 -0
  104. package/dist/admin/pages/BillingPage.js +164 -0
  105. package/dist/admin/pages/BillingPage.js.map +1 -0
  106. package/dist/admin/pages/CommunicationsPage.d.ts +2 -0
  107. package/dist/admin/pages/CommunicationsPage.d.ts.map +1 -0
  108. package/dist/admin/pages/CommunicationsPage.js +118 -0
  109. package/dist/admin/pages/CommunicationsPage.js.map +1 -0
  110. package/dist/admin/pages/DashboardPage.d.ts +5 -0
  111. package/dist/admin/pages/DashboardPage.d.ts.map +1 -0
  112. package/dist/admin/pages/DashboardPage.js +121 -0
  113. package/dist/admin/pages/DashboardPage.js.map +1 -0
  114. package/dist/admin/pages/FeatureFlagsPage.d.ts +2 -0
  115. package/dist/admin/pages/FeatureFlagsPage.d.ts.map +1 -0
  116. package/dist/admin/pages/FeatureFlagsPage.js +116 -0
  117. package/dist/admin/pages/FeatureFlagsPage.js.map +1 -0
  118. package/dist/admin/pages/OrganizationsListPage.d.ts +2 -0
  119. package/dist/admin/pages/OrganizationsListPage.d.ts.map +1 -0
  120. package/dist/admin/pages/OrganizationsListPage.js +112 -0
  121. package/dist/admin/pages/OrganizationsListPage.js.map +1 -0
  122. package/dist/admin/pages/ProductsPage.d.ts +5 -0
  123. package/dist/admin/pages/ProductsPage.d.ts.map +1 -0
  124. package/dist/admin/pages/ProductsPage.js +392 -0
  125. package/dist/admin/pages/ProductsPage.js.map +1 -0
  126. package/dist/admin/pages/ReferralsPage.d.ts +2 -0
  127. package/dist/admin/pages/ReferralsPage.d.ts.map +1 -0
  128. package/dist/admin/pages/ReferralsPage.js +272 -0
  129. package/dist/admin/pages/ReferralsPage.js.map +1 -0
  130. package/dist/admin/pages/RolesPermissionsPage.d.ts +2 -0
  131. package/dist/admin/pages/RolesPermissionsPage.d.ts.map +1 -0
  132. package/dist/admin/pages/RolesPermissionsPage.js +145 -0
  133. package/dist/admin/pages/RolesPermissionsPage.js.map +1 -0
  134. package/dist/admin/pages/SREPage.d.ts +5 -0
  135. package/dist/admin/pages/SREPage.d.ts.map +1 -0
  136. package/dist/admin/pages/SREPage.js +405 -0
  137. package/dist/admin/pages/SREPage.js.map +1 -0
  138. package/dist/admin/pages/SandboxSettingsPage.d.ts +7 -0
  139. package/dist/admin/pages/SandboxSettingsPage.d.ts.map +1 -0
  140. package/dist/admin/pages/SandboxSettingsPage.js +497 -0
  141. package/dist/admin/pages/SandboxSettingsPage.js.map +1 -0
  142. package/dist/admin/pages/SettingsPage.d.ts +5 -0
  143. package/dist/admin/pages/SettingsPage.d.ts.map +1 -0
  144. package/dist/admin/pages/SettingsPage.js +279 -0
  145. package/dist/admin/pages/SettingsPage.js.map +1 -0
  146. package/dist/admin/pages/TenantDetailPage.d.ts +5 -0
  147. package/dist/admin/pages/TenantDetailPage.d.ts.map +1 -0
  148. package/dist/admin/pages/TenantDetailPage.js +274 -0
  149. package/dist/admin/pages/TenantDetailPage.js.map +1 -0
  150. package/dist/admin/pages/TenantFormRoutePage.d.ts +6 -0
  151. package/dist/admin/pages/TenantFormRoutePage.d.ts.map +1 -0
  152. package/dist/admin/pages/TenantFormRoutePage.js +136 -0
  153. package/dist/admin/pages/TenantFormRoutePage.js.map +1 -0
  154. package/dist/admin/pages/TenantsListPage.d.ts +5 -0
  155. package/dist/admin/pages/TenantsListPage.d.ts.map +1 -0
  156. package/dist/admin/pages/TenantsListPage.js +339 -0
  157. package/dist/admin/pages/TenantsListPage.js.map +1 -0
  158. package/dist/admin/pages/UsersListPage.d.ts +5 -0
  159. package/dist/admin/pages/UsersListPage.d.ts.map +1 -0
  160. package/dist/admin/pages/UsersListPage.js +250 -0
  161. package/dist/admin/pages/UsersListPage.js.map +1 -0
  162. package/dist/admin/pages/WaitlistPage.d.ts +2 -0
  163. package/dist/admin/pages/WaitlistPage.d.ts.map +1 -0
  164. package/dist/admin/pages/WaitlistPage.js +241 -0
  165. package/dist/admin/pages/WaitlistPage.js.map +1 -0
  166. package/dist/admin/pages/WorkflowsPage.d.ts +2 -0
  167. package/dist/admin/pages/WorkflowsPage.d.ts.map +1 -0
  168. package/dist/admin/pages/WorkflowsPage.js +126 -0
  169. package/dist/admin/pages/WorkflowsPage.js.map +1 -0
  170. package/dist/admin/pages/WorkspacesListPage.d.ts +2 -0
  171. package/dist/admin/pages/WorkspacesListPage.d.ts.map +1 -0
  172. package/dist/admin/pages/WorkspacesListPage.js +100 -0
  173. package/dist/admin/pages/WorkspacesListPage.js.map +1 -0
  174. package/dist/admin/pages/index.d.ts +20 -0
  175. package/dist/admin/pages/index.d.ts.map +1 -0
  176. package/dist/admin/pages/index.js +19 -0
  177. package/dist/admin/providers/AdminProvider.d.ts +13 -0
  178. package/dist/admin/providers/AdminProvider.d.ts.map +1 -0
  179. package/dist/admin/providers/AdminProvider.js +43 -0
  180. package/dist/admin/providers/AdminProvider.js.map +1 -0
  181. package/dist/admin/schemas/index.d.ts +409 -0
  182. package/dist/admin/schemas/index.d.ts.map +1 -0
  183. package/dist/admin/schemas/index.js +111 -0
  184. package/dist/admin/schemas/index.js.map +1 -0
  185. package/dist/admin/store/adminStore.d.ts +28 -0
  186. package/dist/admin/store/adminStore.d.ts.map +1 -0
  187. package/dist/admin/store/adminStore.js +48 -0
  188. package/dist/admin/store/adminStore.js.map +1 -0
  189. package/dist/admin/types/index.d.ts +325 -0
  190. package/dist/admin/types/index.d.ts.map +1 -0
  191. package/dist/admin/utils/cn.d.ts +7 -0
  192. package/dist/admin/utils/cn.d.ts.map +1 -0
  193. package/dist/admin/utils/cn.js +10 -0
  194. package/dist/admin/utils/cn.js.map +1 -0
  195. package/dist/admin/utils/directActivityGraphql.d.ts +11 -0
  196. package/dist/admin/utils/directActivityGraphql.d.ts.map +1 -0
  197. package/dist/admin/utils/directActivityGraphql.js +25 -0
  198. package/dist/admin/utils/directActivityGraphql.js.map +1 -0
  199. package/dist/admin/utils/formatters.d.ts +33 -0
  200. package/dist/admin/utils/formatters.d.ts.map +1 -0
  201. package/dist/admin/utils/formatters.js +54 -0
  202. package/dist/admin/utils/formatters.js.map +1 -0
  203. package/dist/admin/utils/gatewayGraphql.d.ts +9 -0
  204. package/dist/admin/utils/gatewayGraphql.d.ts.map +1 -0
  205. package/dist/admin/utils/i18n.d.ts +6 -0
  206. package/dist/admin/utils/i18n.d.ts.map +1 -0
  207. package/dist/admin/utils/i18n.js +15 -0
  208. package/dist/admin/utils/i18n.js.map +1 -0
  209. package/dist/admin/utils/mappers.d.ts +15 -0
  210. package/dist/admin/utils/mappers.d.ts.map +1 -0
  211. package/dist/admin/utils/mappers.js +73 -0
  212. package/dist/admin/utils/mappers.js.map +1 -0
  213. package/dist/admin/utils/navigation.d.ts +9 -0
  214. package/dist/admin/utils/navigation.d.ts.map +1 -0
  215. package/dist/admin/utils/navigation.js +8 -0
  216. package/dist/admin/utils/navigation.js.map +1 -0
  217. package/dist/admin/utils/tenantEvents.d.ts +11 -0
  218. package/dist/admin/utils/tenantEvents.d.ts.map +1 -0
  219. package/dist/admin/utils/tenantEvents.js +8 -0
  220. package/dist/admin/utils/tenantEvents.js.map +1 -0
  221. package/dist/dev/main.d.ts +2 -0
  222. package/dist/dev/main.d.ts.map +1 -0
  223. package/dist/generated/global-operations.d.ts +2749 -0
  224. package/dist/generated/global-operations.d.ts.map +1 -0
  225. package/dist/generated/global-operations.js +907 -0
  226. package/dist/generated/global-operations.js.map +1 -0
  227. package/dist/generated/global-types.d.ts +20773 -0
  228. package/dist/generated/global-types.d.ts.map +1 -0
  229. package/dist/generated/global-types.js +12 -0
  230. package/dist/generated/global-types.js.map +1 -0
  231. package/dist/generated/wspace-operations.d.ts +132 -0
  232. package/dist/generated/wspace-operations.d.ts.map +1 -0
  233. package/dist/generated/wspace-operations.js +65 -0
  234. package/dist/generated/wspace-operations.js.map +1 -0
  235. package/dist/generated/wspace-types.d.ts +53363 -0
  236. package/dist/generated/wspace-types.d.ts.map +1 -0
  237. package/dist/index.d.ts +6 -0
  238. package/dist/index.d.ts.map +1 -0
  239. package/dist/index.js +10 -0
  240. package/dist/node_modules/@hookform/resolvers/dist/resolvers.js +34 -0
  241. package/dist/node_modules/@hookform/resolvers/dist/resolvers.js.map +1 -0
  242. package/dist/node_modules/@hookform/resolvers/zod/dist/zod.js +118 -0
  243. package/dist/node_modules/@hookform/resolvers/zod/dist/zod.js.map +1 -0
  244. package/dist/node_modules/clsx/dist/clsx.js +18 -0
  245. package/dist/node_modules/clsx/dist/clsx.js.map +1 -0
  246. package/dist/node_modules/date-fns/_lib/addLeadingZeros.js +8 -0
  247. package/dist/node_modules/date-fns/_lib/addLeadingZeros.js.map +1 -0
  248. package/dist/node_modules/date-fns/_lib/defaultOptions.js +9 -0
  249. package/dist/node_modules/date-fns/_lib/defaultOptions.js.map +1 -0
  250. package/dist/node_modules/date-fns/_lib/format/formatters.js +384 -0
  251. package/dist/node_modules/date-fns/_lib/format/formatters.js.map +1 -0
  252. package/dist/node_modules/date-fns/_lib/format/lightFormatters.js +45 -0
  253. package/dist/node_modules/date-fns/_lib/format/lightFormatters.js.map +1 -0
  254. package/dist/node_modules/date-fns/_lib/format/longFormatters.js +42 -0
  255. package/dist/node_modules/date-fns/_lib/format/longFormatters.js.map +1 -0
  256. package/dist/node_modules/date-fns/_lib/getRoundingMethod.js +11 -0
  257. package/dist/node_modules/date-fns/_lib/getRoundingMethod.js.map +1 -0
  258. package/dist/node_modules/date-fns/_lib/getTimezoneOffsetInMilliseconds.js +10 -0
  259. package/dist/node_modules/date-fns/_lib/getTimezoneOffsetInMilliseconds.js.map +1 -0
  260. package/dist/node_modules/date-fns/_lib/normalizeDates.js +10 -0
  261. package/dist/node_modules/date-fns/_lib/normalizeDates.js.map +1 -0
  262. package/dist/node_modules/date-fns/_lib/protectedTokens.js +25 -0
  263. package/dist/node_modules/date-fns/_lib/protectedTokens.js.map +1 -0
  264. package/dist/node_modules/date-fns/compareAsc.js +10 -0
  265. package/dist/node_modules/date-fns/compareAsc.js.map +1 -0
  266. package/dist/node_modules/date-fns/constants.js +8 -0
  267. package/dist/node_modules/date-fns/constants.js.map +1 -0
  268. package/dist/node_modules/date-fns/constructFrom.js +9 -0
  269. package/dist/node_modules/date-fns/constructFrom.js.map +1 -0
  270. package/dist/node_modules/date-fns/constructNow.js +9 -0
  271. package/dist/node_modules/date-fns/constructNow.js.map +1 -0
  272. package/dist/node_modules/date-fns/differenceInCalendarDays.js +13 -0
  273. package/dist/node_modules/date-fns/differenceInCalendarDays.js.map +1 -0
  274. package/dist/node_modules/date-fns/differenceInCalendarMonths.js +10 -0
  275. package/dist/node_modules/date-fns/differenceInCalendarMonths.js.map +1 -0
  276. package/dist/node_modules/date-fns/differenceInMilliseconds.js +9 -0
  277. package/dist/node_modules/date-fns/differenceInMilliseconds.js.map +1 -0
  278. package/dist/node_modules/date-fns/differenceInMonths.js +18 -0
  279. package/dist/node_modules/date-fns/differenceInMonths.js.map +1 -0
  280. package/dist/node_modules/date-fns/differenceInSeconds.js +11 -0
  281. package/dist/node_modules/date-fns/differenceInSeconds.js.map +1 -0
  282. package/dist/node_modules/date-fns/endOfDay.js +10 -0
  283. package/dist/node_modules/date-fns/endOfDay.js.map +1 -0
  284. package/dist/node_modules/date-fns/endOfMonth.js +10 -0
  285. package/dist/node_modules/date-fns/endOfMonth.js.map +1 -0
  286. package/dist/node_modules/date-fns/format.js +61 -0
  287. package/dist/node_modules/date-fns/format.js.map +1 -0
  288. package/dist/node_modules/date-fns/formatDistance.js +39 -0
  289. package/dist/node_modules/date-fns/formatDistance.js.map +1 -0
  290. package/dist/node_modules/date-fns/formatDistanceToNow.js +10 -0
  291. package/dist/node_modules/date-fns/formatDistanceToNow.js.map +1 -0
  292. package/dist/node_modules/date-fns/getDayOfYear.js +12 -0
  293. package/dist/node_modules/date-fns/getDayOfYear.js.map +1 -0
  294. package/dist/node_modules/date-fns/getISOWeek.js +13 -0
  295. package/dist/node_modules/date-fns/getISOWeek.js.map +1 -0
  296. package/dist/node_modules/date-fns/getISOWeekYear.js +16 -0
  297. package/dist/node_modules/date-fns/getISOWeekYear.js.map +1 -0
  298. package/dist/node_modules/date-fns/getWeek.js +13 -0
  299. package/dist/node_modules/date-fns/getWeek.js.map +1 -0
  300. package/dist/node_modules/date-fns/getWeekYear.js +17 -0
  301. package/dist/node_modules/date-fns/getWeekYear.js.map +1 -0
  302. package/dist/node_modules/date-fns/isDate.js +8 -0
  303. package/dist/node_modules/date-fns/isDate.js.map +1 -0
  304. package/dist/node_modules/date-fns/isLastDayOfMonth.js +12 -0
  305. package/dist/node_modules/date-fns/isLastDayOfMonth.js.map +1 -0
  306. package/dist/node_modules/date-fns/isValid.js +10 -0
  307. package/dist/node_modules/date-fns/isValid.js.map +1 -0
  308. package/dist/node_modules/date-fns/locale/_lib/buildFormatLongFn.js +11 -0
  309. package/dist/node_modules/date-fns/locale/_lib/buildFormatLongFn.js.map +1 -0
  310. package/dist/node_modules/date-fns/locale/_lib/buildLocalizeFn.js +19 -0
  311. package/dist/node_modules/date-fns/locale/_lib/buildLocalizeFn.js.map +1 -0
  312. package/dist/node_modules/date-fns/locale/_lib/buildMatchFn.js +24 -0
  313. package/dist/node_modules/date-fns/locale/_lib/buildMatchFn.js.map +1 -0
  314. package/dist/node_modules/date-fns/locale/_lib/buildMatchPatternFn.js +20 -0
  315. package/dist/node_modules/date-fns/locale/_lib/buildMatchPatternFn.js.map +1 -0
  316. package/dist/node_modules/date-fns/locale/en-US/_lib/formatDistance.js +71 -0
  317. package/dist/node_modules/date-fns/locale/en-US/_lib/formatDistance.js.map +1 -0
  318. package/dist/node_modules/date-fns/locale/en-US/_lib/formatLong.js +34 -0
  319. package/dist/node_modules/date-fns/locale/en-US/_lib/formatLong.js.map +1 -0
  320. package/dist/node_modules/date-fns/locale/en-US/_lib/formatRelative.js +13 -0
  321. package/dist/node_modules/date-fns/locale/en-US/_lib/formatRelative.js.map +1 -0
  322. package/dist/node_modules/date-fns/locale/en-US/_lib/localize.js +204 -0
  323. package/dist/node_modules/date-fns/locale/en-US/_lib/localize.js.map +1 -0
  324. package/dist/node_modules/date-fns/locale/en-US/_lib/match.js +126 -0
  325. package/dist/node_modules/date-fns/locale/en-US/_lib/match.js.map +1 -0
  326. package/dist/node_modules/date-fns/locale/en-US.js +22 -0
  327. package/dist/node_modules/date-fns/locale/en-US.js.map +1 -0
  328. package/dist/node_modules/date-fns/parseISO.js +119 -0
  329. package/dist/node_modules/date-fns/parseISO.js.map +1 -0
  330. package/dist/node_modules/date-fns/startOfDay.js +10 -0
  331. package/dist/node_modules/date-fns/startOfDay.js.map +1 -0
  332. package/dist/node_modules/date-fns/startOfISOWeek.js +12 -0
  333. package/dist/node_modules/date-fns/startOfISOWeek.js.map +1 -0
  334. package/dist/node_modules/date-fns/startOfISOWeekYear.js +12 -0
  335. package/dist/node_modules/date-fns/startOfISOWeekYear.js.map +1 -0
  336. package/dist/node_modules/date-fns/startOfWeek.js +11 -0
  337. package/dist/node_modules/date-fns/startOfWeek.js.map +1 -0
  338. package/dist/node_modules/date-fns/startOfWeekYear.js +13 -0
  339. package/dist/node_modules/date-fns/startOfWeekYear.js.map +1 -0
  340. package/dist/node_modules/date-fns/startOfYear.js +10 -0
  341. package/dist/node_modules/date-fns/startOfYear.js.map +1 -0
  342. package/dist/node_modules/date-fns/toDate.js +9 -0
  343. package/dist/node_modules/date-fns/toDate.js.map +1 -0
  344. package/dist/node_modules/react-hook-form/dist/index.esm.js +1210 -0
  345. package/dist/node_modules/react-hook-form/dist/index.esm.js.map +1 -0
  346. package/dist/node_modules/tailwind-merge/dist/bundle-mjs.js +1716 -0
  347. package/dist/node_modules/tailwind-merge/dist/bundle-mjs.js.map +1 -0
  348. package/dist/node_modules/zod/v3/ZodError.js +82 -0
  349. package/dist/node_modules/zod/v3/ZodError.js.map +1 -0
  350. package/dist/node_modules/zod/v3/errors.js +10 -0
  351. package/dist/node_modules/zod/v3/errors.js.map +1 -0
  352. package/dist/node_modules/zod/v3/helpers/errorUtil.js +9 -0
  353. package/dist/node_modules/zod/v3/helpers/errorUtil.js.map +1 -0
  354. package/dist/node_modules/zod/v3/helpers/parseUtil.js +93 -0
  355. package/dist/node_modules/zod/v3/helpers/parseUtil.js.map +1 -0
  356. package/dist/node_modules/zod/v3/helpers/util.js +76 -0
  357. package/dist/node_modules/zod/v3/helpers/util.js.map +1 -0
  358. package/dist/node_modules/zod/v3/locales/en.js +62 -0
  359. package/dist/node_modules/zod/v3/locales/en.js.map +1 -0
  360. package/dist/node_modules/zod/v3/types.js +2459 -0
  361. package/dist/node_modules/zod/v3/types.js.map +1 -0
  362. package/dist/node_modules/zod/v4/core/core.js +35 -0
  363. package/dist/node_modules/zod/v4/core/core.js.map +1 -0
  364. package/dist/node_modules/zod/v4/core/errors.js +24 -0
  365. package/dist/node_modules/zod/v4/core/errors.js.map +1 -0
  366. package/dist/node_modules/zod/v4/core/parse.js +29 -0
  367. package/dist/node_modules/zod/v4/core/parse.js.map +1 -0
  368. package/dist/node_modules/zod/v4/core/util.js +20 -0
  369. package/dist/node_modules/zod/v4/core/util.js.map +1 -0
  370. package/package.json +111 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TenantsListPage.js","names":[],"sources":["../../../src/admin/pages/TenantsListPage.tsx"],"sourcesContent":["import { useState, useMemo, useCallback } from 'react';\nimport { useNavigate } from 'react-router-dom';\nimport { Plus, Search, Building2 } from 'lucide-react';\nimport { useAdmin } from '../providers/AdminProvider';\nimport { useAdminStore } from '../store/adminStore';\nimport { useAdminPermissions } from '../hooks/useAdminPermissions';\nimport {\n PageLayout,\n Button,\n Input,\n DataTable,\n Pagination,\n TenantStatusBadge,\n EmptyState,\n Avatar,\n TenantForm,\n RowActions,\n createRowActions,\n useToast,\n PermissionGuard,\n} from '../components';\nimport { joinPath } from '../utils/navigation';\nimport { formatDate, formatNumber, formatBytes } from '../utils/formatters';\nimport { mapTenantToUi } from '../utils/mappers';\nimport type { Tenant, TenantFilter } from '../types';\nimport type { CreateTenantInput, UpdateTenantInput } from '../schemas';\nimport {\n useListTenantsQuery,\n useCreateTenantMutation,\n useUpdateTenantMutation,\n useSuspendTenantMutation,\n useActivateTenantMutation,\n} from '../../generated/global-operations';\nimport type { RowAction } from '../components/RowActions';\nimport type { TenantStatus as GqlTenantStatus } from '../../generated/global-types';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { useFeatureFlag } from '@burdenoff/fe-libs/shared/feature-flags';\nimport { useEventBus } from '@burdenoff/fe-libs/shared/events';\nimport { tWithFallback } from '../utils/i18n';\nimport { emitTenantLifecycle } from '../utils/tenantEvents';\n\nconst PAGE_SIZE = 20;\n\n/**\n * Tenants list page\n */\nexport function TenantsListPage() {\n const { canViewTenants, canManageTenants, isLoading: permissionsLoading } = useAdminPermissions();\n\n const navigate = useNavigate();\n const { basePath = '' } = useAdmin();\n const { tenantFilter, updateTenantFilter, resetTenantFilter } = useAdminStore();\n const toast = useToast();\n const { t } = useI18n();\n // Page-level feature flags. Default-allow keeps prior behavior intact.\n // Both list-page entry points (the Create CTA and the row Edit action that\n // opens the local TenantForm modal) have to honor the flags — otherwise\n // turning the edit flag off would still let an admin edit via the modal\n // even though the route-level gate denies /admin/tenants/:id/edit.\n const createFlagEnabled = useFeatureFlag('admin.tenant.create', true);\n const editFlagEnabled = useFeatureFlag('admin.tenant.edit', true);\n const events = useEventBus();\n const tr = useCallback(\n (key: string, fallback: string, params?: Record<string, string | number>): string =>\n tWithFallback(t, key, fallback, params),\n [t]\n );\n\n const [page, setPage] = useState(1);\n\n // Form state\n const [formOpen, setFormOpen] = useState(false);\n const [editingTenant, setEditingTenant] = useState<Tenant | undefined>(undefined);\n\n // Build filter for GraphQL — only pass supported fields\n const gqlFilter = useMemo(() => {\n const f: { search?: string; status?: GqlTenantStatus } = {};\n if (tenantFilter.search) f.search = tenantFilter.search;\n const statusMap: Record<string, GqlTenantStatus> = {\n active: 'ACTIVE' as GqlTenantStatus,\n suspended: 'SUSPENDED' as GqlTenantStatus,\n deleted: 'DELETED' as GqlTenantStatus,\n };\n if (tenantFilter.status !== 'all' && statusMap[tenantFilter.status]) {\n f.status = statusMap[tenantFilter.status];\n }\n return f;\n }, [tenantFilter.search, tenantFilter.status]);\n\n const skipQueries = permissionsLoading || !canViewTenants;\n\n // Query tenants from backend\n const { data, loading, error, refetch } = useListTenantsQuery({\n variables: {\n filter: gqlFilter,\n limit: PAGE_SIZE,\n offset: (page - 1) * PAGE_SIZE,\n },\n fetchPolicy: 'cache-and-network',\n skip: skipQueries,\n });\n\n // Mutations\n const [createTenant, { loading: creating }] = useCreateTenantMutation({\n onCompleted: () => {\n void refetch();\n },\n });\n const [updateTenant, { loading: updating }] = useUpdateTenantMutation({\n onCompleted: () => {\n void refetch();\n },\n });\n const [suspendTenant] = useSuspendTenantMutation({\n onCompleted: () => {\n void refetch();\n },\n });\n const [activateTenant] = useActivateTenantMutation({\n onCompleted: () => {\n void refetch();\n },\n });\n\n const tenants: Tenant[] = useMemo(() => (data?.tenants?.items ?? []).map(mapTenantToUi), [data]);\n\n const total = data?.tenants?.total ?? 0;\n const totalPages = Math.ceil(total / PAGE_SIZE);\n\n const handleCreateTenant = useCallback(() => {\n setEditingTenant(undefined);\n setFormOpen(true);\n }, []);\n\n const handleEditTenant = useCallback((tenant: Tenant) => {\n setEditingTenant(tenant);\n setFormOpen(true);\n }, []);\n\n const handleSuspendTenant = useCallback(\n async (tenant: Tenant) => {\n try {\n await suspendTenant({ variables: { id: tenant.id } });\n emitTenantLifecycle(events, 'tenant.tenant.suspended', {\n tenantId: tenant.id,\n tenantSlug: tenant.slug,\n status: 'SUSPENDED',\n });\n toast.success(\n tr('admin.tenants.toast.suspendedTitle', 'Tenant suspended'),\n tr('admin.tenants.toast.suspendedDescription', '{{name}} has been suspended.', {\n name: tenant.name,\n })\n );\n } catch {\n toast.error(\n tr('admin.tenants.toast.errorTitle', 'Error'),\n tr('admin.tenants.toast.suspendFailed', 'Failed to suspend tenant.')\n );\n }\n },\n [suspendTenant, toast, tr, events]\n );\n\n const handleActivateTenant = useCallback(\n async (tenant: Tenant) => {\n try {\n await activateTenant({ variables: { id: tenant.id } });\n emitTenantLifecycle(events, 'tenant.tenant.activated', {\n tenantId: tenant.id,\n tenantSlug: tenant.slug,\n status: 'ACTIVE',\n });\n toast.success(\n tr('admin.tenants.toast.activatedTitle', 'Tenant activated'),\n tr('admin.tenants.toast.activatedDescription', '{{name}} has been activated.', {\n name: tenant.name,\n })\n );\n } catch {\n toast.error(\n tr('admin.tenants.toast.errorTitle', 'Error'),\n tr('admin.tenants.toast.activateFailed', 'Failed to activate tenant.')\n );\n }\n },\n [activateTenant, toast, tr, events]\n );\n\n const handleSaveTenant = useCallback(\n async (data: CreateTenantInput | UpdateTenantInput) => {\n const isEditing = 'id' in data && data.id;\n try {\n if (isEditing) {\n const updated = await updateTenant({\n variables: {\n id: (data as UpdateTenantInput).id,\n input: {\n name: data.name,\n customDomain: data.domain,\n metadata: {\n ownerEmail: data.ownerEmail,\n billingEmail: data.billingEmail,\n plan: data.plan,\n } as Record<string, unknown>,\n },\n },\n });\n const updatedId = updated.data?.updateTenant?.id ?? (data as UpdateTenantInput).id;\n // Use the slug from the persisted record. The update mutation does\n // not accept slug as input, so reading it from the form would emit\n // a value that may not match what the backend actually has.\n const persistedSlug = updated.data?.updateTenant?.slug;\n emitTenantLifecycle(events, 'tenant.tenant.updated', {\n tenantId: updatedId,\n tenantSlug: persistedSlug,\n });\n toast.success(\n tr('admin.tenants.toast.updatedTitle', 'Tenant updated'),\n tr('admin.tenants.toast.updatedDescription', '{{name}} has been updated.', {\n name: data.name ?? '',\n })\n );\n } else {\n const created = await createTenant({\n variables: {\n input: {\n name: data.name!,\n slug: data.slug!,\n customDomain: data.domain,\n metadata: {\n ownerEmail: data.ownerEmail,\n billingEmail: data.billingEmail,\n plan: data.plan,\n } as Record<string, unknown>,\n },\n },\n });\n const createdRecord = created.data?.createTenant;\n if (createdRecord?.id) {\n // Slug from the server-confirmed record so listeners never see a\n // form value the backend rejected (e.g. uniqueness collision).\n emitTenantLifecycle(events, 'tenant.tenant.created', {\n tenantId: createdRecord.id,\n tenantSlug: createdRecord.slug,\n });\n }\n toast.success(\n tr('admin.tenants.toast.createdTitle', 'Tenant created'),\n tr('admin.tenants.toast.createdDescription', '{{name}} has been created.', {\n name: data.name ?? '',\n })\n );\n }\n setFormOpen(false);\n } catch (err) {\n const msg = err instanceof Error ? err.message : 'Unknown error';\n toast.error(\n isEditing\n ? tr('admin.tenants.toast.updateFailedTitle', 'Update failed')\n : tr('admin.tenants.toast.createFailedTitle', 'Create failed'),\n msg\n );\n // Rethrow so TenantForm.onSubmit's await rejects and the modal stays\n // open with the user's input intact. Symmetric to the rethrow already\n // applied in TenantFormRoutePage.handleSave.\n throw err;\n }\n },\n [createTenant, updateTenant, toast, tr, events]\n );\n\n const handleRowClick = (tenant: Tenant) => {\n navigate(joinPath(basePath, `tenants/${tenant.id}`));\n };\n\n const getRowActions = useCallback(\n (tenant: Tenant): RowAction[] => {\n const actions = createRowActions({\n onView: () => navigate(joinPath(basePath, `tenants/${tenant.id}`)),\n onEdit: canManageTenants && editFlagEnabled ? () => handleEditTenant(tenant) : undefined,\n });\n if (canManageTenants) {\n actions.push({\n key: tenant.status === 'active' ? 'suspend' : 'activate',\n label:\n tenant.status === 'active'\n ? tr('admin.tenants.actions.suspend', 'Suspend')\n : tr('admin.tenants.actions.activate', 'Activate'),\n separator: true,\n onClick:\n tenant.status === 'active'\n ? () => handleSuspendTenant(tenant)\n : () => handleActivateTenant(tenant),\n });\n }\n return actions;\n },\n [\n navigate,\n basePath,\n canManageTenants,\n editFlagEnabled,\n handleEditTenant,\n handleSuspendTenant,\n handleActivateTenant,\n tr,\n ]\n );\n\n const columns = [\n {\n key: 'name',\n header: tr('admin.tenants.table.tenant', 'Tenant'),\n sortable: true,\n render: (tenant: Tenant) => (\n <div className=\"flex items-center gap-3\">\n <Avatar fallback={tenant.name.slice(0, 2).toUpperCase()} size=\"sm\" />\n <div className=\"min-w-0\">\n <div className=\"truncate font-medium text-foreground\">{tenant.name}</div>\n <div className=\"truncate text-xs text-muted-foreground\">{tenant.slug}</div>\n </div>\n </div>\n ),\n },\n {\n key: 'status',\n header: tr('admin.tenants.table.status', 'Status'),\n width: '120px',\n render: (tenant: Tenant) => <TenantStatusBadge status={tenant.status} />,\n },\n {\n key: 'plan',\n header: tr('admin.tenants.table.plan', 'Plan'),\n width: '100px',\n },\n {\n key: 'userCount',\n header: tr('admin.tenants.table.users', 'Users'),\n width: '80px',\n align: 'right' as const,\n sortable: true,\n render: (tenant: Tenant) => formatNumber(tenant.userCount),\n },\n {\n key: 'storageUsed',\n header: tr('admin.tenants.table.storage', 'Storage'),\n width: '100px',\n align: 'right' as const,\n render: (tenant: Tenant) => formatBytes(tenant.storageUsed),\n },\n {\n key: 'createdAt',\n header: tr('admin.tenants.table.created', 'Created'),\n width: '120px',\n sortable: true,\n render: (tenant: Tenant) => formatDate(tenant.createdAt),\n },\n {\n key: 'actions',\n header: '',\n width: '50px',\n align: 'right' as const,\n render: (tenant: Tenant) => (\n <RowActions\n actions={getRowActions(tenant)}\n ariaLabel={tr('admin.tenants.table.actionsFor', 'Actions for {{name}}', {\n name: tenant.name,\n })}\n />\n ),\n },\n ];\n\n return (\n <PermissionGuard\n allowed={canViewTenants}\n isLoading={permissionsLoading}\n pageTitle={tr('admin.tenants.title', 'Tenants')}\n denyMessage={tr('admin.tenants.denyMessage', \"You don't have permission to view tenants.\")}\n >\n <PageLayout\n title={tr('admin.tenants.title', 'Tenants')}\n description={\n total\n ? tr(\n 'admin.tenants.descriptionWithTotal',\n 'Manage all tenants on the platform · {{total}} total',\n { total }\n )\n : tr('admin.tenants.description', 'Manage all tenants on the platform')\n }\n actions={\n canManageTenants && createFlagEnabled ? (\n <Button onClick={handleCreateTenant} disabled={creating || updating}>\n <Plus className=\"mr-2 h-4 w-4\" />\n {tr('admin.tenants.actions.createTenant', 'Create Tenant')}\n </Button>\n ) : undefined\n }\n >\n <div className=\"space-y-4\">\n {/* Filters */}\n <div className=\"flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between\">\n <div className=\"relative max-w-sm flex-1\">\n <Search className=\"absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground\" />\n <Input\n placeholder={tr('admin.tenants.searchPlaceholder', 'Search tenants...')}\n value={tenantFilter.search}\n onChange={(e) => {\n setPage(1);\n updateTenantFilter({ search: e.target.value });\n }}\n className=\"pl-9\"\n />\n </div>\n <div className=\"flex items-center gap-2\">\n <select\n value={tenantFilter.status}\n onChange={(e) => {\n setPage(1);\n updateTenantFilter({ status: e.target.value as TenantFilter['status'] });\n }}\n className=\"h-10 rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2\"\n >\n <option value=\"all\">{tr('admin.tenants.filters.status.all', 'All Status')}</option>\n <option value=\"active\">\n {tr('admin.tenants.filters.status.active', 'Active')}\n </option>\n <option value=\"suspended\">\n {tr('admin.tenants.filters.status.suspended', 'Suspended')}\n </option>\n <option value=\"deleted\">\n {tr('admin.tenants.filters.status.deleted', 'Deleted')}\n </option>\n </select>\n {(tenantFilter.search || tenantFilter.status !== 'all') && (\n <Button\n variant=\"ghost\"\n onClick={() => {\n setPage(1);\n resetTenantFilter();\n }}\n >\n {tr('admin.tenants.filters.clear', 'Clear')}\n </Button>\n )}\n </div>\n </div>\n\n {/* Error state */}\n {error && (\n <div className=\"rounded-md border border-destructive/50 bg-destructive/10 p-4 text-sm text-destructive\">\n {tr('admin.tenants.error.loadFailed', 'Failed to load tenants')}: {error.message}\n </div>\n )}\n\n {/* Table */}\n <DataTable\n columns={columns}\n data={tenants}\n keyExtractor={(t) => t.id}\n onRowClick={handleRowClick}\n sortBy={tenantFilter.sortBy}\n sortOrder={tenantFilter.sortOrder}\n onSort={(column, order) =>\n updateTenantFilter({\n sortBy: column as TenantFilter['sortBy'],\n sortOrder: order,\n })\n }\n loading={loading}\n emptyState={\n <EmptyState\n icon={<Building2 className=\"h-8 w-8\" />}\n title={tr('admin.tenants.empty.title', 'No tenants found')}\n description={tr(\n 'admin.tenants.empty.description',\n 'Try adjusting your search or filters'\n )}\n action={\n <Button\n variant=\"outline\"\n onClick={() => {\n setPage(1);\n resetTenantFilter();\n }}\n >\n {tr('admin.tenants.empty.clearFilters', 'Clear filters')}\n </Button>\n }\n />\n }\n />\n\n {/* Pagination */}\n {total > PAGE_SIZE && (\n <Pagination\n page={page}\n totalPages={totalPages}\n totalItems={total}\n pageSize={PAGE_SIZE}\n onPageChange={setPage}\n />\n )}\n </div>\n\n {/* Tenant Form */}\n <TenantForm\n open={formOpen}\n onClose={() => setFormOpen(false)}\n tenant={editingTenant}\n onSave={handleSaveTenant}\n />\n </PageLayout>\n </PermissionGuard>\n );\n}\n"],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Users list page — connected to real backend with enable/disable actions
3
+ */
4
+ export declare function UsersListPage(): import("react/jsx-runtime").JSX.Element;
5
+ //# sourceMappingURL=UsersListPage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"UsersListPage.d.ts","sourceRoot":"","sources":["../../../src/admin/pages/UsersListPage.tsx"],"names":[],"mappings":"AA8BA;;GAEG;AACH,wBAAgB,aAAa,4CAsS5B"}
@@ -0,0 +1,250 @@
1
+ import { useToast as e } from "../components/Toast.js";
2
+ import { useAdmin as t } from "../providers/AdminProvider.js";
3
+ import { useAdminPermissions as n } from "../hooks/useAdminPermissions.js";
4
+ import { Avatar as r, Button as i, EmptyState as a, Input as o } from "../components/ui.js";
5
+ import { PageLayout as s } from "../components/PageLayout.js";
6
+ import { UserStatusBadge as c } from "../components/StatusBadge.js";
7
+ import { formatDate as ee, formatRelativeTime as l } from "../utils/formatters.js";
8
+ import { DataTable as u, Pagination as d } from "../components/DataTable.js";
9
+ import { tWithFallback as f } from "../utils/i18n.js";
10
+ import { RowActions as p } from "../components/RowActions.js";
11
+ import { PermissionGuard as m } from "../components/PermissionGuard.js";
12
+ import "../components/index.js";
13
+ import { joinPath as h } from "../utils/navigation.js";
14
+ import { mapUserToUi as g } from "../utils/mappers.js";
15
+ import { useListUsersQuery as _, useUpdateUserMutation as v } from "../../generated/global-operations.js";
16
+ import { useAdminStore as y } from "../store/adminStore.js";
17
+ import { UserStatus as b } from "../../generated/global-types.js";
18
+ import { useCallback as x, useMemo as S, useState as C } from "react";
19
+ import { Search as w, Users as T } from "lucide-react";
20
+ import { useNavigate as E } from "react-router-dom";
21
+ import { jsx as D, jsxs as O } from "react/jsx-runtime";
22
+ import { useI18n as k } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
23
+ //#region src/admin/pages/UsersListPage.tsx
24
+ var A = 20;
25
+ function j() {
26
+ let { canViewUsers: j, canUpdateUsers: M, isLoading: N } = n(), P = E(), { basePath: F = "" } = t(), { userFilter: I, updateUserFilter: L } = y(), R = e(), { t: z } = k(), B = x((e, t, n) => f(z, e, t, n), [z]), [V, H] = C(1), U = S(() => {
27
+ let e = {};
28
+ I.search && (e.search = I.search);
29
+ let t = {
30
+ active: b.Active,
31
+ inactive: b.Inactive,
32
+ blocked: b.Suspended
33
+ };
34
+ return I.status !== "all" && t[I.status] && (e.status = t[I.status]), e;
35
+ }, [I.search, I.status]), W = N || !j, { data: G, loading: K, error: q, refetch: J } = _({
36
+ variables: {
37
+ filter: U,
38
+ pagination: {
39
+ limit: A,
40
+ offset: (V - 1) * A
41
+ }
42
+ },
43
+ fetchPolicy: "cache-and-network",
44
+ skip: W
45
+ }), [Y, { loading: X }] = v({
46
+ onCompleted: () => {
47
+ R.success(B("admin.users.toast.updatedTitle", "User updated"), B("admin.users.toast.updatedDescription", "User status has been successfully updated.")), J();
48
+ },
49
+ onError: (e) => {
50
+ R.error(B("admin.users.toast.updateFailedTitle", "Update failed"), e.message);
51
+ }
52
+ }), Z = x(async (e) => {
53
+ let t = e.status === "active" ? b.Inactive : b.Active;
54
+ await Y({ variables: {
55
+ id: e.id,
56
+ input: { status: t }
57
+ } });
58
+ }, [Y]), Q = S(() => (G?.users?.users ?? []).map(g), [G]), $ = G?.users?.totalCount ?? 0, te = Math.ceil($ / A), ne = x((e) => {
59
+ let t = [{
60
+ key: "view",
61
+ label: B("admin.users.actions.viewDetails", "View Details"),
62
+ onClick: () => P(h(F, `users/${e.id}`))
63
+ }];
64
+ return M && t.push({
65
+ key: "toggle-status",
66
+ label: e.status === "active" ? B("admin.users.actions.disableUser", "Disable User") : B("admin.users.actions.enableUser", "Enable User"),
67
+ onClick: () => Z(e)
68
+ }), t;
69
+ }, [
70
+ P,
71
+ F,
72
+ M,
73
+ Z,
74
+ B
75
+ ]), re = [
76
+ {
77
+ key: "name",
78
+ header: B("admin.users.table.user", "User"),
79
+ render: (e) => /* @__PURE__ */ O("div", {
80
+ className: "flex items-center gap-3",
81
+ children: [/* @__PURE__ */ D(r, {
82
+ fallback: (e.firstName?.[0] ?? e.email[0]).toUpperCase(),
83
+ size: "sm"
84
+ }), /* @__PURE__ */ O("div", {
85
+ className: "min-w-0",
86
+ children: [/* @__PURE__ */ D("div", {
87
+ className: "truncate font-medium text-foreground",
88
+ children: e.name || e.email
89
+ }), /* @__PURE__ */ D("div", {
90
+ className: "truncate text-xs text-muted-foreground",
91
+ children: e.email
92
+ })]
93
+ })]
94
+ })
95
+ },
96
+ {
97
+ key: "status",
98
+ header: B("admin.users.table.status", "Status"),
99
+ width: "120px",
100
+ render: (e) => /* @__PURE__ */ D(c, { status: e.status })
101
+ },
102
+ {
103
+ key: "role",
104
+ header: B("admin.users.table.role", "Role"),
105
+ width: "120px"
106
+ },
107
+ {
108
+ key: "tenantId",
109
+ header: B("admin.users.table.tenant", "Tenant"),
110
+ width: "150px",
111
+ render: (e) => /* @__PURE__ */ D("span", {
112
+ className: "text-xs text-muted-foreground",
113
+ children: e.tenantId || "—"
114
+ })
115
+ },
116
+ {
117
+ key: "createdAt",
118
+ header: B("admin.users.table.joined", "Joined"),
119
+ width: "120px",
120
+ sortable: !0,
121
+ render: (e) => ee(e.createdAt)
122
+ },
123
+ {
124
+ key: "lastLoginAt",
125
+ header: B("admin.users.table.lastLogin", "Last Login"),
126
+ width: "130px",
127
+ render: (e) => e.lastLoginAt ? l(e.lastLoginAt) : B("admin.users.table.never", "Never")
128
+ },
129
+ {
130
+ key: "actions",
131
+ header: "",
132
+ width: "50px",
133
+ align: "right",
134
+ render: (e) => /* @__PURE__ */ D(p, {
135
+ actions: ne(e),
136
+ ariaLabel: B("admin.users.table.actionsFor", "Actions for {{email}}", { email: e.email })
137
+ })
138
+ }
139
+ ];
140
+ return /* @__PURE__ */ D(m, {
141
+ allowed: j,
142
+ isLoading: N,
143
+ pageTitle: B("admin.users.title", "Users"),
144
+ denyMessage: B("admin.users.denyMessage", "You don't have permission to view users."),
145
+ children: /* @__PURE__ */ D(s, {
146
+ title: B("admin.users.title", "Users"),
147
+ description: $ ? B("admin.users.descriptionWithTotal", "Manage all users on the platform · {{total}} total", { total: $ }) : B("admin.users.description", "Manage all users on the platform"),
148
+ children: /* @__PURE__ */ O("div", {
149
+ className: "space-y-4",
150
+ children: [
151
+ /* @__PURE__ */ O("div", {
152
+ className: "flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between",
153
+ children: [/* @__PURE__ */ O("div", {
154
+ className: "relative max-w-sm flex-1",
155
+ children: [/* @__PURE__ */ D(w, { className: "absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground" }), /* @__PURE__ */ D(o, {
156
+ placeholder: B("admin.users.searchPlaceholder", "Search by email or name..."),
157
+ value: I.search,
158
+ onChange: (e) => {
159
+ H(1), L({ search: e.target.value });
160
+ },
161
+ className: "pl-9"
162
+ })]
163
+ }), /* @__PURE__ */ O("div", {
164
+ className: "flex items-center gap-2",
165
+ children: [/* @__PURE__ */ O("select", {
166
+ value: I.status,
167
+ onChange: (e) => {
168
+ H(1), L({ status: e.target.value });
169
+ },
170
+ className: "h-10 rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
171
+ children: [
172
+ /* @__PURE__ */ D("option", {
173
+ value: "all",
174
+ children: B("admin.users.filters.status.all", "All Status")
175
+ }),
176
+ /* @__PURE__ */ D("option", {
177
+ value: "active",
178
+ children: B("admin.users.filters.status.active", "Active")
179
+ }),
180
+ /* @__PURE__ */ D("option", {
181
+ value: "inactive",
182
+ children: B("admin.users.filters.status.inactive", "Inactive")
183
+ }),
184
+ /* @__PURE__ */ D("option", {
185
+ value: "blocked",
186
+ children: B("admin.users.filters.status.suspended", "Suspended")
187
+ })
188
+ ]
189
+ }), (I.search || I.status !== "all") && /* @__PURE__ */ D(i, {
190
+ variant: "ghost",
191
+ onClick: () => {
192
+ H(1), L({
193
+ search: "",
194
+ status: "all"
195
+ });
196
+ },
197
+ children: B("admin.users.filters.clear", "Clear")
198
+ })]
199
+ })]
200
+ }),
201
+ q && /* @__PURE__ */ O("div", {
202
+ className: "rounded-md border border-destructive/50 bg-destructive/10 p-4 text-sm text-destructive",
203
+ children: [
204
+ B("admin.users.error.loadFailed", "Failed to load users"),
205
+ ": ",
206
+ q.message
207
+ ]
208
+ }),
209
+ /* @__PURE__ */ D(u, {
210
+ columns: re,
211
+ data: Q,
212
+ keyExtractor: (e) => e.id,
213
+ onRowClick: (e) => P(h(F, `users/${e.id}`)),
214
+ sortBy: I.sortBy,
215
+ sortOrder: I.sortOrder,
216
+ onSort: (e, t) => L({
217
+ sortBy: e,
218
+ sortOrder: t
219
+ }),
220
+ loading: K || X,
221
+ emptyState: /* @__PURE__ */ D(a, {
222
+ icon: /* @__PURE__ */ D(T, { className: "h-8 w-8" }),
223
+ title: B("admin.users.empty.title", "No users found"),
224
+ description: B("admin.users.empty.description", "Try adjusting your search or filters"),
225
+ action: /* @__PURE__ */ D(i, {
226
+ variant: "outline",
227
+ onClick: () => L({
228
+ search: "",
229
+ status: "all"
230
+ }),
231
+ children: B("admin.users.empty.clearFilters", "Clear filters")
232
+ })
233
+ })
234
+ }),
235
+ $ > A && /* @__PURE__ */ D(d, {
236
+ page: V,
237
+ totalPages: te,
238
+ totalItems: $,
239
+ pageSize: A,
240
+ onPageChange: H
241
+ })
242
+ ]
243
+ })
244
+ })
245
+ });
246
+ }
247
+ //#endregion
248
+ export { j as UsersListPage };
249
+
250
+ //# sourceMappingURL=UsersListPage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"UsersListPage.js","names":[],"sources":["../../../src/admin/pages/UsersListPage.tsx"],"sourcesContent":["import { useState, useMemo, useCallback } from 'react';\nimport { useNavigate } from 'react-router-dom';\nimport { Search, Users as UsersIcon } from 'lucide-react';\nimport { useAdmin } from '../providers/AdminProvider';\nimport { useAdminStore } from '../store/adminStore';\nimport { useAdminPermissions } from '../hooks/useAdminPermissions';\nimport {\n PageLayout,\n Input,\n DataTable,\n Pagination,\n UserStatusBadge,\n EmptyState,\n Avatar,\n Button,\n RowActions,\n useToast,\n PermissionGuard,\n} from '../components';\nimport { joinPath } from '../utils/navigation';\nimport { formatDate, formatRelativeTime } from '../utils/formatters';\nimport { mapUserToUi } from '../utils/mappers';\nimport type { User, UserFilter } from '../types';\nimport { useListUsersQuery, useUpdateUserMutation } from '../../generated/global-operations';\nimport { UserStatus as GqlUserStatus } from '../../generated/global-types';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { tWithFallback } from '../utils/i18n';\n\nconst PAGE_SIZE = 20;\n\n/**\n * Users list page — connected to real backend with enable/disable actions\n */\nexport function UsersListPage() {\n const { canViewUsers, canUpdateUsers, isLoading: permissionsLoading } = useAdminPermissions();\n\n const navigate = useNavigate();\n const { basePath = '' } = useAdmin();\n const { userFilter, updateUserFilter } = useAdminStore();\n const toast = useToast();\n const { t } = useI18n();\n const tr = useCallback(\n (key: string, fallback: string, params?: Record<string, string | number>): string =>\n tWithFallback(t, key, fallback, params),\n [t]\n );\n\n const [page, setPage] = useState(1);\n\n // Build GraphQL filter\n const gqlFilter = useMemo(() => {\n const f: {\n search?: string;\n status?: GqlUserStatus;\n } = {};\n if (userFilter.search) f.search = userFilter.search;\n const statusMap: Record<string, GqlUserStatus> = {\n active: GqlUserStatus.Active,\n inactive: GqlUserStatus.Inactive,\n blocked: GqlUserStatus.Suspended,\n };\n if (userFilter.status !== 'all' && statusMap[userFilter.status]) {\n f.status = statusMap[userFilter.status];\n }\n return f;\n }, [userFilter.search, userFilter.status]);\n\n const skipQueries = permissionsLoading || !canViewUsers;\n\n const { data, loading, error, refetch } = useListUsersQuery({\n variables: {\n filter: gqlFilter,\n pagination: {\n limit: PAGE_SIZE,\n offset: (page - 1) * PAGE_SIZE,\n },\n },\n fetchPolicy: 'cache-and-network',\n skip: skipQueries,\n });\n\n const [updateUser, { loading: isUpdating }] = useUpdateUserMutation({\n onCompleted: () => {\n toast.success(\n tr('admin.users.toast.updatedTitle', 'User updated'),\n tr('admin.users.toast.updatedDescription', 'User status has been successfully updated.')\n );\n void refetch();\n },\n onError: (err) => {\n toast.error(tr('admin.users.toast.updateFailedTitle', 'Update failed'), err.message);\n },\n });\n\n const handleToggleStatus = useCallback(\n async (user: User) => {\n const newStatus = user.status === 'active' ? GqlUserStatus.Inactive : GqlUserStatus.Active;\n await updateUser({\n variables: {\n id: user.id,\n input: {\n status: newStatus,\n },\n },\n });\n },\n [updateUser]\n );\n\n const users: User[] = useMemo(() => (data?.users?.users ?? []).map(mapUserToUi), [data]);\n\n const total = data?.users?.totalCount ?? 0;\n const totalPages = Math.ceil(total / PAGE_SIZE);\n\n const getRowActions = useCallback(\n (user: User) => {\n const actions = [\n {\n key: 'view',\n label: tr('admin.users.actions.viewDetails', 'View Details'),\n onClick: () => navigate(joinPath(basePath, `users/${user.id}`)),\n },\n ];\n\n if (canUpdateUsers) {\n actions.push({\n key: 'toggle-status',\n label:\n user.status === 'active'\n ? tr('admin.users.actions.disableUser', 'Disable User')\n : tr('admin.users.actions.enableUser', 'Enable User'),\n onClick: () => handleToggleStatus(user),\n });\n }\n\n return actions;\n },\n [navigate, basePath, canUpdateUsers, handleToggleStatus, tr]\n );\n\n const columns = [\n {\n key: 'name',\n header: tr('admin.users.table.user', 'User'),\n render: (user: User) => (\n <div className=\"flex items-center gap-3\">\n <Avatar fallback={(user.firstName?.[0] ?? user.email[0]).toUpperCase()} size=\"sm\" />\n <div className=\"min-w-0\">\n <div className=\"truncate font-medium text-foreground\">{user.name || user.email}</div>\n <div className=\"truncate text-xs text-muted-foreground\">{user.email}</div>\n </div>\n </div>\n ),\n },\n {\n key: 'status',\n header: tr('admin.users.table.status', 'Status'),\n width: '120px',\n render: (user: User) => <UserStatusBadge status={user.status} />,\n },\n {\n key: 'role',\n header: tr('admin.users.table.role', 'Role'),\n width: '120px',\n },\n {\n key: 'tenantId',\n header: tr('admin.users.table.tenant', 'Tenant'),\n width: '150px',\n render: (user: User) => (\n <span className=\"text-xs text-muted-foreground\">{user.tenantId || '—'}</span>\n ),\n },\n {\n key: 'createdAt',\n header: tr('admin.users.table.joined', 'Joined'),\n width: '120px',\n sortable: true,\n render: (user: User) => formatDate(user.createdAt),\n },\n {\n key: 'lastLoginAt',\n header: tr('admin.users.table.lastLogin', 'Last Login'),\n width: '130px',\n render: (user: User) =>\n user.lastLoginAt\n ? formatRelativeTime(user.lastLoginAt)\n : tr('admin.users.table.never', 'Never'),\n },\n {\n key: 'actions',\n header: '',\n width: '50px',\n align: 'right' as const,\n render: (user: User) => (\n <RowActions\n actions={getRowActions(user)}\n ariaLabel={tr('admin.users.table.actionsFor', 'Actions for {{email}}', {\n email: user.email,\n })}\n />\n ),\n },\n ];\n\n return (\n <PermissionGuard\n allowed={canViewUsers}\n isLoading={permissionsLoading}\n pageTitle={tr('admin.users.title', 'Users')}\n denyMessage={tr('admin.users.denyMessage', \"You don't have permission to view users.\")}\n >\n <PageLayout\n title={tr('admin.users.title', 'Users')}\n description={\n total\n ? tr(\n 'admin.users.descriptionWithTotal',\n 'Manage all users on the platform · {{total}} total',\n { total }\n )\n : tr('admin.users.description', 'Manage all users on the platform')\n }\n >\n <div className=\"space-y-4\">\n {/* Filters */}\n <div className=\"flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between\">\n <div className=\"relative max-w-sm flex-1\">\n <Search className=\"absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground\" />\n <Input\n placeholder={tr('admin.users.searchPlaceholder', 'Search by email or name...')}\n value={userFilter.search}\n onChange={(e) => {\n setPage(1);\n updateUserFilter({ search: e.target.value });\n }}\n className=\"pl-9\"\n />\n </div>\n <div className=\"flex items-center gap-2\">\n <select\n value={userFilter.status}\n onChange={(e) => {\n setPage(1);\n updateUserFilter({ status: e.target.value as UserFilter['status'] });\n }}\n className=\"h-10 rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2\"\n >\n <option value=\"all\">{tr('admin.users.filters.status.all', 'All Status')}</option>\n <option value=\"active\">{tr('admin.users.filters.status.active', 'Active')}</option>\n <option value=\"inactive\">\n {tr('admin.users.filters.status.inactive', 'Inactive')}\n </option>\n <option value=\"blocked\">\n {tr('admin.users.filters.status.suspended', 'Suspended')}\n </option>\n </select>\n {(userFilter.search || userFilter.status !== 'all') && (\n <Button\n variant=\"ghost\"\n onClick={() => {\n setPage(1);\n updateUserFilter({ search: '', status: 'all' });\n }}\n >\n {tr('admin.users.filters.clear', 'Clear')}\n </Button>\n )}\n </div>\n </div>\n\n {/* Error */}\n {error && (\n <div className=\"rounded-md border border-destructive/50 bg-destructive/10 p-4 text-sm text-destructive\">\n {tr('admin.users.error.loadFailed', 'Failed to load users')}: {error.message}\n </div>\n )}\n\n {/* Table */}\n <DataTable\n columns={columns}\n data={users}\n keyExtractor={(u) => u.id}\n onRowClick={(u) => navigate(joinPath(basePath, `users/${u.id}`))}\n sortBy={userFilter.sortBy}\n sortOrder={userFilter.sortOrder}\n onSort={(column, order) =>\n updateUserFilter({\n sortBy: column as UserFilter['sortBy'],\n sortOrder: order,\n })\n }\n loading={loading || isUpdating}\n emptyState={\n <EmptyState\n icon={<UsersIcon className=\"h-8 w-8\" />}\n title={tr('admin.users.empty.title', 'No users found')}\n description={tr(\n 'admin.users.empty.description',\n 'Try adjusting your search or filters'\n )}\n action={\n <Button\n variant=\"outline\"\n onClick={() => updateUserFilter({ search: '', status: 'all' })}\n >\n {tr('admin.users.empty.clearFilters', 'Clear filters')}\n </Button>\n }\n />\n }\n />\n\n {/* Pagination */}\n {total > PAGE_SIZE && (\n <Pagination\n page={page}\n totalPages={totalPages}\n totalItems={total}\n pageSize={PAGE_SIZE}\n onPageChange={setPage}\n />\n )}\n </div>\n </PageLayout>\n </PermissionGuard>\n );\n}\n"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export declare function WaitlistPage(): import("react/jsx-runtime").JSX.Element;
2
+ //# sourceMappingURL=WaitlistPage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"WaitlistPage.d.ts","sourceRoot":"","sources":["../../../src/admin/pages/WaitlistPage.tsx"],"names":[],"mappings":"AAmFA,wBAAgB,YAAY,4CA4O3B"}
@@ -0,0 +1,241 @@
1
+ import { useAdminPermissions as e } from "../hooks/useAdminPermissions.js";
2
+ import { Badge as t, EmptyState as n, Input as r } from "../components/ui.js";
3
+ import { PageLayout as i } from "../components/PageLayout.js";
4
+ import { formatDate as a } from "../utils/formatters.js";
5
+ import { DataTable as o, Pagination as s } from "../components/DataTable.js";
6
+ import { PermissionGuard as c } from "../components/PermissionGuard.js";
7
+ import "../components/index.js";
8
+ import { useCallback as l, useEffect as u, useMemo as d, useState as f } from "react";
9
+ import { Check as p, ClipboardList as m, Search as h, Trash2 as g, X as _ } from "lucide-react";
10
+ import { Fragment as v, jsx as y, jsxs as b } from "react/jsx-runtime";
11
+ import { gql as x } from "@apollo/client";
12
+ import { useGatewayClient as S } from "@burdenoff/fe-libs/shared/graphql";
13
+ //#region src/admin/pages/WaitlistPage.tsx
14
+ var C = 20, w = {
15
+ PENDING: "outline",
16
+ APPROVED: "default",
17
+ REJECTED: "destructive"
18
+ }, T = x`
19
+ query GetWaitlist($filter: WaitlistFilterInput) {
20
+ waitlist(filter: $filter) {
21
+ id
22
+ email
23
+ name
24
+ company
25
+ platformProductId
26
+ status
27
+ reason
28
+ rejectionReason
29
+ processedAt
30
+ processedBy
31
+ createdAt
32
+ }
33
+ }
34
+ `, E = x`
35
+ mutation ApproveWaitlist($input: ApproveWaitlistInput!) {
36
+ approveWaitlist(input: $input) {
37
+ id
38
+ status
39
+ }
40
+ }
41
+ `, D = x`
42
+ mutation RejectWaitlist($input: RejectWaitlistInput!) {
43
+ rejectWaitlist(input: $input) {
44
+ id
45
+ status
46
+ }
47
+ }
48
+ `, O = x`
49
+ mutation RemoveFromWaitlist($id: ID!) {
50
+ removeFromWaitlist(id: $id)
51
+ }
52
+ `;
53
+ function k() {
54
+ let { canViewWaitlist: x, canManageWaitlist: k, isLoading: A } = e(), j = S("global"), [M, N] = f(1), [P, F] = f(""), [I, L] = f(""), [R, z] = f(null), [B, V] = f(!1), [H, U] = f(null), [W, G] = f(0), K = d(() => {
55
+ let e = {};
56
+ return I && (e.status = I), Object.keys(e).length > 0 ? e : void 0;
57
+ }, [I]), q = l(() => G((e) => e + 1), []);
58
+ u(() => {
59
+ A || !x || (V(!0), j.query({
60
+ query: T,
61
+ variables: { filter: K },
62
+ fetchPolicy: "network-only"
63
+ }).then((e) => {
64
+ let t = e.data;
65
+ z(t ?? null), U(null);
66
+ }).catch((e) => U({ message: e.message })).finally(() => V(!1)));
67
+ }, [
68
+ j,
69
+ K,
70
+ A,
71
+ x,
72
+ W
73
+ ]);
74
+ let J = d(() => R?.waitlist ?? [], [R]), Y = d(() => {
75
+ if (!P) return J;
76
+ let e = P.toLowerCase();
77
+ return J.filter((t) => t.email.toLowerCase().includes(e) || t.name && t.name.toLowerCase().includes(e) || t.company && t.company.toLowerCase().includes(e));
78
+ }, [J, P]), X = Y.length, Z = Math.ceil(X / C), Q = Y.slice((M - 1) * C, M * C), $ = l(async (e) => {
79
+ await j.mutate({
80
+ mutation: E,
81
+ variables: { input: { id: e } }
82
+ }), q();
83
+ }, [j, q]), ee = l(async (e) => {
84
+ let t = window.prompt("Rejection reason (optional):");
85
+ await j.mutate({
86
+ mutation: D,
87
+ variables: { input: {
88
+ id: e,
89
+ reason: t
90
+ } }
91
+ }), q();
92
+ }, [j, q]), te = l(async (e) => {
93
+ window.confirm("Remove this waitlist entry?") && (await j.mutate({
94
+ mutation: O,
95
+ variables: { id: e }
96
+ }), q());
97
+ }, [j, q]), ne = [
98
+ {
99
+ key: "email",
100
+ header: "Email",
101
+ render: (e) => /* @__PURE__ */ b("div", {
102
+ className: "min-w-0",
103
+ children: [/* @__PURE__ */ y("div", {
104
+ className: "font-medium text-foreground",
105
+ children: e.email
106
+ }), e.name && /* @__PURE__ */ y("div", {
107
+ className: "text-xs text-muted-foreground",
108
+ children: e.name
109
+ })]
110
+ })
111
+ },
112
+ {
113
+ key: "company",
114
+ header: "Company",
115
+ width: "140px",
116
+ render: (e) => /* @__PURE__ */ y("span", {
117
+ className: "text-sm text-muted-foreground",
118
+ children: e.company || "-"
119
+ })
120
+ },
121
+ {
122
+ key: "status",
123
+ header: "Status",
124
+ width: "100px",
125
+ render: (e) => /* @__PURE__ */ y(t, {
126
+ variant: w[e.status] ?? "outline",
127
+ className: "text-xs capitalize",
128
+ children: e.status.toLowerCase()
129
+ })
130
+ },
131
+ {
132
+ key: "createdAt",
133
+ header: "Joined",
134
+ width: "110px",
135
+ render: (e) => a(e.createdAt)
136
+ },
137
+ ...k ? [{
138
+ key: "actions",
139
+ header: "Actions",
140
+ width: "140px",
141
+ render: (e) => /* @__PURE__ */ b("div", {
142
+ className: "flex items-center gap-1",
143
+ children: [e.status === "PENDING" && /* @__PURE__ */ b(v, { children: [/* @__PURE__ */ y("button", {
144
+ onClick: () => $(e.id),
145
+ className: "rounded p-1 text-green-500 hover:bg-green-500/10",
146
+ title: "Approve",
147
+ children: /* @__PURE__ */ y(p, { className: "h-4 w-4" })
148
+ }), /* @__PURE__ */ y("button", {
149
+ onClick: () => ee(e.id),
150
+ className: "rounded p-1 text-red-500 hover:bg-red-500/10",
151
+ title: "Reject",
152
+ children: /* @__PURE__ */ y(_, { className: "h-4 w-4" })
153
+ })] }), /* @__PURE__ */ y("button", {
154
+ onClick: () => te(e.id),
155
+ className: "rounded p-1 text-muted-foreground hover:bg-muted",
156
+ title: "Remove",
157
+ children: /* @__PURE__ */ y(g, { className: "h-4 w-4" })
158
+ })]
159
+ })
160
+ }] : []
161
+ ];
162
+ return /* @__PURE__ */ y(c, {
163
+ allowed: x,
164
+ isLoading: A,
165
+ pageTitle: "Waitlist",
166
+ denyMessage: "You don't have permission to view the waitlist.",
167
+ children: /* @__PURE__ */ y(i, {
168
+ title: "Waitlist Management",
169
+ description: `Product waitlist entries${X ? ` \u00b7 ${X} total` : ""}`,
170
+ children: /* @__PURE__ */ b("div", {
171
+ className: "space-y-4",
172
+ children: [
173
+ /* @__PURE__ */ b("div", {
174
+ className: "flex flex-wrap items-center gap-3",
175
+ children: [/* @__PURE__ */ b("div", {
176
+ className: "relative max-w-sm flex-1",
177
+ children: [/* @__PURE__ */ y(h, { className: "absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground" }), /* @__PURE__ */ y(r, {
178
+ placeholder: "Search by email, name, or company...",
179
+ value: P,
180
+ onChange: (e) => {
181
+ N(1), F(e.target.value);
182
+ },
183
+ className: "pl-9"
184
+ })]
185
+ }), /* @__PURE__ */ b("select", {
186
+ value: I,
187
+ onChange: (e) => {
188
+ N(1), L(e.target.value);
189
+ },
190
+ className: "rounded-md border border-input bg-background px-3 py-2 text-sm",
191
+ children: [
192
+ /* @__PURE__ */ y("option", {
193
+ value: "",
194
+ children: "All statuses"
195
+ }),
196
+ /* @__PURE__ */ y("option", {
197
+ value: "PENDING",
198
+ children: "Pending"
199
+ }),
200
+ /* @__PURE__ */ y("option", {
201
+ value: "APPROVED",
202
+ children: "Approved"
203
+ }),
204
+ /* @__PURE__ */ y("option", {
205
+ value: "REJECTED",
206
+ children: "Rejected"
207
+ })
208
+ ]
209
+ })]
210
+ }),
211
+ H && /* @__PURE__ */ b("div", {
212
+ className: "rounded-md border border-destructive/50 bg-destructive/10 p-4 text-sm text-destructive",
213
+ children: ["Failed to load waitlist: ", H.message]
214
+ }),
215
+ /* @__PURE__ */ y(o, {
216
+ columns: ne,
217
+ data: Q,
218
+ keyExtractor: (e) => e.id,
219
+ loading: B,
220
+ emptyState: /* @__PURE__ */ y(n, {
221
+ icon: /* @__PURE__ */ y(m, { className: "h-8 w-8" }),
222
+ title: "No waitlist entries",
223
+ description: "No entries match your current filters"
224
+ })
225
+ }),
226
+ X > C && /* @__PURE__ */ y(s, {
227
+ page: M,
228
+ totalPages: Z,
229
+ totalItems: X,
230
+ pageSize: C,
231
+ onPageChange: N
232
+ })
233
+ ]
234
+ })
235
+ })
236
+ });
237
+ }
238
+ //#endregion
239
+ export { k as WaitlistPage };
240
+
241
+ //# sourceMappingURL=WaitlistPage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"WaitlistPage.js","names":[],"sources":["../../../src/admin/pages/WaitlistPage.tsx"],"sourcesContent":["import { useState, useMemo, useCallback, useEffect } from 'react';\nimport { ClipboardList, Search, Check, X, Trash2 } from 'lucide-react';\nimport { useAdminPermissions } from '../hooks/useAdminPermissions';\nimport {\n PageLayout,\n Input,\n DataTable,\n Pagination,\n EmptyState,\n Badge,\n PermissionGuard,\n} from '../components';\nimport { formatDate } from '../utils/formatters';\nimport { useGatewayClient } from '@burdenoff/fe-libs/shared/graphql';\nimport { gql } from '@apollo/client';\n\nconst PAGE_SIZE = 20;\n\ntype WaitlistStatus = 'PENDING' | 'APPROVED' | 'REJECTED';\n\nconst STATUS_VARIANT: Record<WaitlistStatus, 'default' | 'secondary' | 'outline' | 'destructive'> =\n {\n PENDING: 'outline',\n APPROVED: 'default',\n REJECTED: 'destructive',\n };\n\nconst GET_WAITLIST = gql`\n query GetWaitlist($filter: WaitlistFilterInput) {\n waitlist(filter: $filter) {\n id\n email\n name\n company\n platformProductId\n status\n reason\n rejectionReason\n processedAt\n processedBy\n createdAt\n }\n }\n`;\n\nconst APPROVE_WAITLIST = gql`\n mutation ApproveWaitlist($input: ApproveWaitlistInput!) {\n approveWaitlist(input: $input) {\n id\n status\n }\n }\n`;\n\nconst REJECT_WAITLIST = gql`\n mutation RejectWaitlist($input: RejectWaitlistInput!) {\n rejectWaitlist(input: $input) {\n id\n status\n }\n }\n`;\n\nconst REMOVE_WAITLIST = gql`\n mutation RemoveFromWaitlist($id: ID!) {\n removeFromWaitlist(id: $id)\n }\n`;\n\ntype WaitlistItem = {\n id: string;\n email: string;\n name: string | null;\n company: string | null;\n platformProductId: string;\n status: WaitlistStatus;\n reason: string | null;\n rejectionReason: string | null;\n processedAt: string | null;\n processedBy: string | null;\n createdAt: string;\n};\n\nexport function WaitlistPage() {\n const {\n canViewWaitlist,\n canManageWaitlist,\n isLoading: permissionsLoading,\n } = useAdminPermissions();\n const globalClient = useGatewayClient('global');\n\n const [page, setPage] = useState(1);\n const [search, setSearch] = useState('');\n const [statusFilter, setStatusFilter] = useState<WaitlistStatus | ''>('');\n const [data, setData] = useState<{ waitlist?: WaitlistItem[] } | null>(null);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<{ message: string } | null>(null);\n const [fetchCount, setFetchCount] = useState(0);\n\n const filter = useMemo(() => {\n const f: Record<string, unknown> = {};\n if (statusFilter) f.status = statusFilter;\n return Object.keys(f).length > 0 ? f : undefined;\n }, [statusFilter]);\n\n const refetch = useCallback(() => setFetchCount((c) => c + 1), []);\n\n useEffect(() => {\n if (permissionsLoading || !canViewWaitlist) return;\n setLoading(true);\n globalClient\n .query({ query: GET_WAITLIST, variables: { filter }, fetchPolicy: 'network-only' })\n .then((result) => {\n const d = result.data as { waitlist?: WaitlistItem[] } | undefined;\n setData(d ?? null);\n setError(null);\n })\n .catch((err: Error) => setError({ message: err.message }))\n .finally(() => setLoading(false));\n }, [globalClient, filter, permissionsLoading, canViewWaitlist, fetchCount]);\n\n const allEntries: WaitlistItem[] = useMemo(\n () => (data?.waitlist ?? []) as WaitlistItem[],\n [data]\n );\n\n // Client-side search filter (email, name, company)\n const filtered = useMemo(() => {\n if (!search) return allEntries;\n const q = search.toLowerCase();\n return allEntries.filter(\n (e) =>\n e.email.toLowerCase().includes(q) ||\n (e.name && e.name.toLowerCase().includes(q)) ||\n (e.company && e.company.toLowerCase().includes(q))\n );\n }, [allEntries, search]);\n\n const total = filtered.length;\n const totalPages = Math.ceil(total / PAGE_SIZE);\n const paged = filtered.slice((page - 1) * PAGE_SIZE, page * PAGE_SIZE);\n\n const handleApprove = useCallback(\n async (id: string) => {\n await globalClient.mutate({ mutation: APPROVE_WAITLIST, variables: { input: { id } } });\n refetch();\n },\n [globalClient, refetch]\n );\n\n const handleReject = useCallback(\n async (id: string) => {\n const reason = window.prompt('Rejection reason (optional):');\n await globalClient.mutate({\n mutation: REJECT_WAITLIST,\n variables: { input: { id, reason } },\n });\n refetch();\n },\n [globalClient, refetch]\n );\n\n const handleRemove = useCallback(\n async (id: string) => {\n if (!window.confirm('Remove this waitlist entry?')) return;\n await globalClient.mutate({ mutation: REMOVE_WAITLIST, variables: { id } });\n refetch();\n },\n [globalClient, refetch]\n );\n\n const columns = [\n {\n key: 'email',\n header: 'Email',\n render: (e: WaitlistItem) => (\n <div className=\"min-w-0\">\n <div className=\"font-medium text-foreground\">{e.email}</div>\n {e.name && <div className=\"text-xs text-muted-foreground\">{e.name}</div>}\n </div>\n ),\n },\n {\n key: 'company',\n header: 'Company',\n width: '140px',\n render: (e: WaitlistItem) => (\n <span className=\"text-sm text-muted-foreground\">{e.company || '-'}</span>\n ),\n },\n {\n key: 'status',\n header: 'Status',\n width: '100px',\n render: (e: WaitlistItem) => (\n <Badge variant={STATUS_VARIANT[e.status] ?? 'outline'} className=\"text-xs capitalize\">\n {e.status.toLowerCase()}\n </Badge>\n ),\n },\n {\n key: 'createdAt',\n header: 'Joined',\n width: '110px',\n render: (e: WaitlistItem) => formatDate(e.createdAt),\n },\n ...(canManageWaitlist\n ? [\n {\n key: 'actions',\n header: 'Actions',\n width: '140px',\n render: (e: WaitlistItem) => (\n <div className=\"flex items-center gap-1\">\n {e.status === 'PENDING' && (\n <>\n <button\n onClick={() => handleApprove(e.id)}\n className=\"rounded p-1 text-green-500 hover:bg-green-500/10\"\n title=\"Approve\"\n >\n <Check className=\"h-4 w-4\" />\n </button>\n <button\n onClick={() => handleReject(e.id)}\n className=\"rounded p-1 text-red-500 hover:bg-red-500/10\"\n title=\"Reject\"\n >\n <X className=\"h-4 w-4\" />\n </button>\n </>\n )}\n <button\n onClick={() => handleRemove(e.id)}\n className=\"rounded p-1 text-muted-foreground hover:bg-muted\"\n title=\"Remove\"\n >\n <Trash2 className=\"h-4 w-4\" />\n </button>\n </div>\n ),\n },\n ]\n : []),\n ];\n\n return (\n <PermissionGuard\n allowed={canViewWaitlist}\n isLoading={permissionsLoading}\n pageTitle=\"Waitlist\"\n denyMessage=\"You don't have permission to view the waitlist.\"\n >\n <PageLayout\n title=\"Waitlist Management\"\n description={`Product waitlist entries${total ? ` \\u00b7 ${total} total` : ''}`}\n >\n <div className=\"space-y-4\">\n <div className=\"flex flex-wrap items-center gap-3\">\n <div className=\"relative max-w-sm flex-1\">\n <Search className=\"absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground\" />\n <Input\n placeholder=\"Search by email, name, or company...\"\n value={search}\n onChange={(e) => {\n setPage(1);\n setSearch(e.target.value);\n }}\n className=\"pl-9\"\n />\n </div>\n <select\n value={statusFilter}\n onChange={(e) => {\n setPage(1);\n setStatusFilter(e.target.value as WaitlistStatus | '');\n }}\n className=\"rounded-md border border-input bg-background px-3 py-2 text-sm\"\n >\n <option value=\"\">All statuses</option>\n <option value=\"PENDING\">Pending</option>\n <option value=\"APPROVED\">Approved</option>\n <option value=\"REJECTED\">Rejected</option>\n </select>\n </div>\n\n {error && (\n <div className=\"rounded-md border border-destructive/50 bg-destructive/10 p-4 text-sm text-destructive\">\n Failed to load waitlist: {error.message}\n </div>\n )}\n\n <DataTable\n columns={columns}\n data={paged}\n keyExtractor={(e) => e.id}\n loading={loading}\n emptyState={\n <EmptyState\n icon={<ClipboardList className=\"h-8 w-8\" />}\n title=\"No waitlist entries\"\n description=\"No entries match your current filters\"\n />\n }\n />\n\n {total > PAGE_SIZE && (\n <Pagination\n page={page}\n totalPages={totalPages}\n totalItems={total}\n pageSize={PAGE_SIZE}\n onPageChange={setPage}\n />\n )}\n </div>\n </PageLayout>\n </PermissionGuard>\n );\n}\n"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export declare function WorkflowsPage(): import("react/jsx-runtime").JSX.Element;
2
+ //# sourceMappingURL=WorkflowsPage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"WorkflowsPage.d.ts","sourceRoot":"","sources":["../../../src/admin/pages/WorkflowsPage.tsx"],"names":[],"mappings":"AAiBA,wBAAgB,aAAa,4CAwH5B"}