@mesob/auth-react 0.4.5 → 0.4.7

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 (136) hide show
  1. package/dist/{chunk-TB2ZPGLW.js → chunk-4X3CJHKR.js} +2 -2
  2. package/dist/{chunk-PG65ZD7A.js → chunk-5BFG47VF.js} +4 -4
  3. package/dist/{chunk-OQUUS5ZX.js → chunk-5E3XN6SW.js} +2 -2
  4. package/dist/{chunk-KOBZ34XU.js → chunk-6THPM5LB.js} +7 -1
  5. package/dist/chunk-6THPM5LB.js.map +1 -0
  6. package/dist/{chunk-RBXITSE2.js → chunk-73ZNGEWU.js} +5 -5
  7. package/dist/{chunk-DY3NVBLJ.js → chunk-7CLKBH5Z.js} +10 -1
  8. package/dist/chunk-7CLKBH5Z.js.map +1 -0
  9. package/dist/{chunk-FPYQ7XGV.js → chunk-ECF6S2Y2.js} +90 -20
  10. package/dist/chunk-ECF6S2Y2.js.map +1 -0
  11. package/dist/{chunk-G74DDR4O.js → chunk-GBDNBY6K.js} +2 -2
  12. package/dist/{chunk-LHZ4EEN6.js → chunk-H5PUZDNU.js} +7 -1
  13. package/dist/chunk-H5PUZDNU.js.map +1 -0
  14. package/dist/{chunk-A3GIMM2R.js → chunk-H7JRQFFI.js} +2 -2
  15. package/dist/{chunk-IFAVSKHE.js → chunk-IQNQGPIT.js} +7 -1
  16. package/dist/chunk-IQNQGPIT.js.map +1 -0
  17. package/dist/chunk-MWMSZVH3.js +601 -0
  18. package/dist/chunk-MWMSZVH3.js.map +1 -0
  19. package/dist/{chunk-L32Z3TPA.js → chunk-N4JFMKGK.js} +67 -12
  20. package/dist/chunk-N4JFMKGK.js.map +1 -0
  21. package/dist/{chunk-A7ORWWM5.js → chunk-NFGFJPCX.js} +9 -1
  22. package/dist/chunk-NFGFJPCX.js.map +1 -0
  23. package/dist/{chunk-SW7WD64K.js → chunk-NJMNRSJH.js} +7 -1
  24. package/dist/chunk-NJMNRSJH.js.map +1 -0
  25. package/dist/{chunk-HXHI4FU6.js → chunk-OXUOGOG3.js} +111 -91
  26. package/dist/chunk-OXUOGOG3.js.map +1 -0
  27. package/dist/{chunk-ETOCBXDT.js → chunk-QPEUVMSP.js} +9 -1
  28. package/dist/chunk-QPEUVMSP.js.map +1 -0
  29. package/dist/{chunk-YN7OEQI7.js → chunk-RCQTWNAG.js} +7 -1
  30. package/dist/chunk-RCQTWNAG.js.map +1 -0
  31. package/dist/{chunk-55BMNC4S.js → chunk-RRLFPSSM.js} +25 -11
  32. package/dist/chunk-RRLFPSSM.js.map +1 -0
  33. package/dist/{chunk-NUWAI3FE.js → chunk-SGUROG23.js} +8 -2
  34. package/dist/chunk-SGUROG23.js.map +1 -0
  35. package/dist/{chunk-SXVTYYUT.js → chunk-T34HJRUW.js} +7 -1
  36. package/dist/chunk-T34HJRUW.js.map +1 -0
  37. package/dist/{chunk-UAKGEJUN.js → chunk-TEHMLZFI.js} +2 -2
  38. package/dist/chunk-V6ZHX4LT.js +15 -0
  39. package/dist/chunk-V6ZHX4LT.js.map +1 -0
  40. package/dist/{chunk-CXMPZWMX.js → chunk-VVKXFEAN.js} +2 -2
  41. package/dist/{chunk-ZXKEG3X5.js → chunk-X6EUQZSZ.js} +10 -1
  42. package/dist/chunk-X6EUQZSZ.js.map +1 -0
  43. package/dist/{chunk-WQ3UUUKF.js → chunk-Y4AH5JY4.js} +6 -4
  44. package/dist/chunk-Y4AH5JY4.js.map +1 -0
  45. package/dist/components/auth/forgot-password.js +2 -2
  46. package/dist/components/auth/reset-password-form.js +2 -2
  47. package/dist/components/auth/set-password.js +2 -2
  48. package/dist/components/auth/sign-in.js +2 -2
  49. package/dist/components/auth/sign-up.js +2 -2
  50. package/dist/components/auth/verify-email.js +2 -2
  51. package/dist/components/auth/verify-phone.js +2 -2
  52. package/dist/components/iam/domains-page.d.ts +1 -0
  53. package/dist/components/iam/domains-page.js +14 -0
  54. package/dist/components/iam/domains-page.js.map +1 -0
  55. package/dist/components/iam/iam-guard.d.ts +7 -0
  56. package/dist/components/iam/iam-guard.js +10 -0
  57. package/dist/components/iam/iam-guard.js.map +1 -0
  58. package/dist/components/iam/permissions-page.js +3 -1
  59. package/dist/components/iam/role-detail-page.d.ts +1 -1
  60. package/dist/components/iam/role-detail-page.js +3 -1
  61. package/dist/components/iam/role-permissions-page.d.ts +1 -1
  62. package/dist/components/iam/role-permissions-page.js +3 -1
  63. package/dist/components/iam/roles-page.js +3 -1
  64. package/dist/components/iam/sessions-page.js +3 -1
  65. package/dist/components/iam/tenants-page.js +3 -1
  66. package/dist/components/iam/users-page.js +4 -2
  67. package/dist/components/profile/change-email-form.js +2 -2
  68. package/dist/components/profile/change-phone-form.js +1 -1
  69. package/dist/components/profile/security.js +6 -6
  70. package/dist/index.d.ts +2 -0
  71. package/dist/index.js +60 -52
  72. package/dist/index.js.map +1 -1
  73. package/dist/pages/auth/forgot-password.js +2 -2
  74. package/dist/pages/auth/reset-password.js +2 -2
  75. package/dist/pages/auth/set-password.js +2 -2
  76. package/dist/pages/auth/sign-in.js +2 -2
  77. package/dist/pages/auth/sign-up.js +2 -2
  78. package/dist/pages/auth/verify-email.js +2 -2
  79. package/dist/pages/auth/verify-phone.js +2 -2
  80. package/dist/pages/iam/domains/_components/domain-card.d.ts +6 -0
  81. package/dist/pages/iam/domains/_components/domain-form.d.ts +9 -0
  82. package/dist/pages/iam/domains/_components/domains-data.d.ts +10 -0
  83. package/dist/pages/iam/domains/_components/domains-list.d.ts +15 -0
  84. package/dist/pages/iam/domains.d.ts +1 -0
  85. package/dist/pages/iam/permissions.js +3 -1
  86. package/dist/pages/iam/role-detail.js +3 -1
  87. package/dist/pages/iam/role-detail.js.map +1 -1
  88. package/dist/pages/iam/role-permissions.js +3 -1
  89. package/dist/pages/iam/role-permissions.js.map +1 -1
  90. package/dist/pages/iam/role-users.js +5 -3
  91. package/dist/pages/iam/role-users.js.map +1 -1
  92. package/dist/pages/iam/roles/users/_components/role-users-page.d.ts +1 -1
  93. package/dist/pages/iam/roles.js +3 -1
  94. package/dist/pages/iam/sessions.js +3 -1
  95. package/dist/pages/iam/tenant-detail.js +3 -1
  96. package/dist/pages/iam/tenant-detail.js.map +1 -1
  97. package/dist/pages/iam/tenants/tenant-detail-page-content.d.ts +1 -1
  98. package/dist/pages/iam/tenants.js +3 -1
  99. package/dist/pages/iam/user-activity.js +3 -1
  100. package/dist/pages/iam/user-activity.js.map +1 -1
  101. package/dist/pages/iam/user-detail-layout.js +3 -1
  102. package/dist/pages/iam/user-detail-layout.js.map +1 -1
  103. package/dist/pages/iam/user-detail.js +3 -1
  104. package/dist/pages/iam/user-detail.js.map +1 -1
  105. package/dist/pages/iam/users/_components/user-detail-layout-content.d.ts +1 -1
  106. package/dist/pages/iam/users/_components/user-detail-page-content.d.ts +1 -1
  107. package/dist/pages/iam/users/activity/user-activity-page-content.d.ts +1 -1
  108. package/dist/pages/iam/users/user-selector.js +2 -2
  109. package/dist/pages/iam/users.js +4 -2
  110. package/dist/pages/profile/security.js +6 -6
  111. package/dist/utils/handle-error.d.ts +4 -1
  112. package/package.json +3 -3
  113. package/dist/chunk-55BMNC4S.js.map +0 -1
  114. package/dist/chunk-A7ORWWM5.js.map +0 -1
  115. package/dist/chunk-DY3NVBLJ.js.map +0 -1
  116. package/dist/chunk-ETOCBXDT.js.map +0 -1
  117. package/dist/chunk-FPYQ7XGV.js.map +0 -1
  118. package/dist/chunk-HXHI4FU6.js.map +0 -1
  119. package/dist/chunk-IFAVSKHE.js.map +0 -1
  120. package/dist/chunk-KOBZ34XU.js.map +0 -1
  121. package/dist/chunk-L32Z3TPA.js.map +0 -1
  122. package/dist/chunk-LHZ4EEN6.js.map +0 -1
  123. package/dist/chunk-NUWAI3FE.js.map +0 -1
  124. package/dist/chunk-SW7WD64K.js.map +0 -1
  125. package/dist/chunk-SXVTYYUT.js.map +0 -1
  126. package/dist/chunk-WQ3UUUKF.js.map +0 -1
  127. package/dist/chunk-YN7OEQI7.js.map +0 -1
  128. package/dist/chunk-ZXKEG3X5.js.map +0 -1
  129. /package/dist/{chunk-TB2ZPGLW.js.map → chunk-4X3CJHKR.js.map} +0 -0
  130. /package/dist/{chunk-PG65ZD7A.js.map → chunk-5BFG47VF.js.map} +0 -0
  131. /package/dist/{chunk-OQUUS5ZX.js.map → chunk-5E3XN6SW.js.map} +0 -0
  132. /package/dist/{chunk-RBXITSE2.js.map → chunk-73ZNGEWU.js.map} +0 -0
  133. /package/dist/{chunk-G74DDR4O.js.map → chunk-GBDNBY6K.js.map} +0 -0
  134. /package/dist/{chunk-A3GIMM2R.js.map → chunk-H7JRQFFI.js.map} +0 -0
  135. /package/dist/{chunk-UAKGEJUN.js.map → chunk-TEHMLZFI.js.map} +0 -0
  136. /package/dist/{chunk-CXMPZWMX.js.map → chunk-VVKXFEAN.js.map} +0 -0
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  handleError
3
- } from "./chunk-55BMNC4S.js";
3
+ } from "./chunk-RRLFPSSM.js";
4
4
  import {
5
5
  AuthLayout
6
6
  } from "./chunk-DPH2PHK3.js";
@@ -161,4 +161,4 @@ var ForgotPassword = () => {
161
161
  export {
162
162
  ForgotPassword
163
163
  };
164
- //# sourceMappingURL=chunk-TB2ZPGLW.js.map
164
+ //# sourceMappingURL=chunk-4X3CJHKR.js.map
@@ -1,9 +1,9 @@
1
- import {
2
- RequestChangeEmailForm
3
- } from "./chunk-X2BHF4KC.js";
4
1
  import {
5
2
  VerifyChangeEmailForm
6
3
  } from "./chunk-5AEV7RAN.js";
4
+ import {
5
+ RequestChangeEmailForm
6
+ } from "./chunk-X2BHF4KC.js";
7
7
  import {
8
8
  useSession
9
9
  } from "./chunk-AWSAC7RT.js";
@@ -91,4 +91,4 @@ function ChangeEmailForm() {
91
91
  export {
92
92
  ChangeEmailForm
93
93
  };
94
- //# sourceMappingURL=chunk-PG65ZD7A.js.map
94
+ //# sourceMappingURL=chunk-5BFG47VF.js.map
@@ -3,7 +3,7 @@ import {
3
3
  } from "./chunk-Z34NJZRL.js";
4
4
  import {
5
5
  handleError
6
- } from "./chunk-55BMNC4S.js";
6
+ } from "./chunk-RRLFPSSM.js";
7
7
  import {
8
8
  AuthLayout
9
9
  } from "./chunk-DPH2PHK3.js";
@@ -185,4 +185,4 @@ var VerifyPhone = ({
185
185
  export {
186
186
  VerifyPhone
187
187
  };
188
- //# sourceMappingURL=chunk-OQUUS5ZX.js.map
188
+ //# sourceMappingURL=chunk-5E3XN6SW.js.map
@@ -8,6 +8,9 @@ import {
8
8
  import {
9
9
  defaultEntityQueryOptions
10
10
  } from "./chunk-NPW7D2HZ.js";
11
+ import {
12
+ IAMGuard
13
+ } from "./chunk-V6ZHX4LT.js";
11
14
  import {
12
15
  useApi,
13
16
  useConfig
@@ -439,6 +442,9 @@ function TenantsList({
439
442
  // src/components/iam/tenants-page.tsx
440
443
  import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
441
444
  function TenantsPage() {
445
+ return /* @__PURE__ */ jsx4(IAMGuard, { children: /* @__PURE__ */ jsx4(TenantsPageContent, {}) });
446
+ }
447
+ function TenantsPageContent() {
442
448
  const { hooks } = useApi();
443
449
  const { config } = useConfig();
444
450
  const homeHref = config.navigation?.defaultRedirectUrl || "/";
@@ -535,4 +541,4 @@ function TenantsPage() {
535
541
  export {
536
542
  TenantsPage
537
543
  };
538
- //# sourceMappingURL=chunk-KOBZ34XU.js.map
544
+ //# sourceMappingURL=chunk-6THPM5LB.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/iam/tenants-page.tsx","../src/pages/iam/tenants/_components/tenant-form.tsx","../src/pages/iam/tenants/_components/tenants-list.tsx","../src/pages/iam/tenants/_components/tenant-card.tsx"],"sourcesContent":["'use client';\n\nimport {\n EntityDrawerTrigger,\n EntityFilter,\n EntityHeader,\n EntitySearch,\n EntitySort,\n EntityViewToggle,\n PageBody,\n PageContainer,\n useBreadcrumbs,\n useEntityPagination,\n useEntityParams,\n} from '@mesob/ui/components';\nimport { IconBuilding } from '@tabler/icons-react';\nimport { useState } from 'react';\nimport type { paths } from '../../data/openapi';\nimport { defaultEntityQueryOptions } from '../../lib/query-options';\nimport { TenantForm } from '../../pages/iam/tenants/_components/tenant-form';\nimport { TenantsList } from '../../pages/iam/tenants/_components/tenants-list';\nimport { useApi, useConfig } from '../../provider';\nimport { IAMGuard } from './iam-guard';\n\nexport function TenantsPage() {\n return (\n <IAMGuard>\n <TenantsPageContent />\n </IAMGuard>\n );\n}\n\nfunction TenantsPageContent() {\n const { hooks } = useApi();\n const { config } = useConfig();\n const homeHref = config.navigation?.defaultRedirectUrl || '/';\n useBreadcrumbs({\n items: [\n { label: 'Home', href: homeHref },\n { label: 'IAM', href: '/iam/tenants' },\n { label: 'Tenants' },\n ],\n });\n const [createOpen, setCreateOpen] = useState(false);\n\n const { queryConfig, params, setParams } = useEntityParams({\n searchKey: 'search',\n });\n const tenantsQuery = queryConfig as {\n params: {\n query: NonNullable<paths['/tenants']['get']['parameters']['query']>;\n };\n };\n\n const { data, error, isPending, isFetching } = hooks.useQuery(\n 'get',\n '/tenants',\n tenantsQuery,\n defaultEntityQueryOptions,\n );\n\n const isLoading = isPending || isFetching;\n const tenants = data?.tenants ?? [];\n const { total, pageCount } = useEntityPagination({\n items: tenants,\n total: data?.total,\n pageSize: params.pageSize,\n });\n const errorStatus = (error as { status?: number } | null)?.status;\n const hasAccessError = errorStatus === 401 || errorStatus === 403;\n const hasError = Boolean(error) && !isLoading;\n\n return (\n <PageContainer className=\"flex flex-1 flex-col gap-4 p-4 pt-0\">\n <PageBody className=\"px-0\">\n <EntityHeader\n icon={<IconBuilding className=\"size-5\" />}\n title=\"Tenants\"\n actions={\n hasAccessError ? null : (\n <EntityDrawerTrigger\n mode=\"new\"\n entity=\"Tenant\"\n open={createOpen}\n onOpenChange={setCreateOpen}\n >\n {(open, onClose) => (\n <TenantForm mode=\"new\" open={open} onClose={onClose} />\n )}\n </EntityDrawerTrigger>\n )\n }\n search={\n hasAccessError ? null : (\n <EntitySearch paramKey=\"search\" placeholder=\"Search tenants...\" />\n )\n }\n filter={\n hasAccessError ? null : (\n <EntityFilter\n options={[\n { label: 'All', value: '' },\n { label: 'Active', value: 'isActive:true' },\n { label: 'Inactive', value: 'isActive:false' },\n ]}\n placeholder=\"Filter\"\n />\n )\n }\n sort={\n hasAccessError ? null : (\n <EntitySort\n options={[\n { label: 'Created', value: 'createdAt' },\n { label: 'Updated', value: 'updatedAt' },\n { label: 'Name', value: 'name' },\n ]}\n />\n )\n }\n view={\n hasAccessError ? null : (\n <EntityViewToggle views={['table', 'card']} />\n )\n }\n />\n {hasError ? (\n <div className=\"rounded-[1.75rem] border border-border/60 bg-muted/20 p-8\">\n <div className=\"text-lg font-semibold\">\n {hasAccessError\n ? 'Tenant access denied'\n : 'Unable to load tenants'}\n </div>\n <p className=\"mt-2 text-sm text-muted-foreground\">\n {hasAccessError\n ? 'This account does not have permission to view or manage tenants.'\n : 'The tenants page returned an unexpected error. Retry after the API is healthy.'}\n </p>\n </div>\n ) : (\n <TenantsList\n data={tenants}\n isLoading={isLoading}\n view={(params.view || 'table') as 'table' | 'card'}\n pageIndex={params.page - 1}\n pageSize={params.pageSize}\n pageCount={pageCount}\n totalRows={total}\n onCreateNew={() => setCreateOpen(true)}\n onPageChange={(p) => setParams({ page: p + 1 })}\n onPageSizeChange={(size) => setParams({ pageSize: size, page: 1 })}\n />\n )}\n </PageBody>\n </PageContainer>\n );\n}\n","'use client';\n\nimport { zodResolver } from '@hookform/resolvers/zod';\nimport {\n EntityDrawer,\n EntityFormActions,\n Input,\n Label,\n Skeleton,\n Textarea,\n} from '@mesob/ui/components';\nimport { useQueryClient } from '@tanstack/react-query';\nimport { useEffect } from 'react';\nimport { useForm } from 'react-hook-form';\nimport { z } from 'zod';\nimport { authApi$ } from '../../shared/page-helpers';\nimport { str } from './tenants-data';\n\nconst schema = z.object({\n id: z.string().min(1).max(30),\n name: z.string().optional(),\n description: z.string().optional(),\n isActive: z.boolean(),\n});\n\ntype FormData = z.infer<typeof schema>;\n\nconst defaults: FormData = {\n id: '',\n name: '',\n description: '',\n isActive: true,\n};\n\ntype TenantFormProps = {\n mode: 'new' | 'edit';\n tenantId?: string;\n open: boolean;\n onClose: () => void;\n onSuccess?: () => void;\n};\n\nexport function TenantForm({\n mode,\n tenantId,\n open,\n onClose,\n onSuccess,\n}: TenantFormProps) {\n const qc = useQueryClient();\n const { data, isLoading } = authApi$.useQuery(\n 'get',\n '/tenants/{id}',\n { params: { path: { id: tenantId ?? '' } } },\n { enabled: mode === 'edit' && !!tenantId },\n );\n const create = authApi$.useMutation('post', '/tenants', {\n onSuccess: () => qc.invalidateQueries({ queryKey: ['get', '/tenants'] }),\n });\n const update = authApi$.useMutation('put', '/tenants/{id}', {\n onSuccess: () => {\n qc.invalidateQueries({ queryKey: ['get', '/tenants'] });\n if (tenantId) {\n qc.invalidateQueries({ queryKey: ['get', '/tenants/{id}'] });\n }\n },\n });\n const del = authApi$.useMutation('delete', '/tenants/{id}', {\n onSuccess: () => qc.invalidateQueries({ queryKey: ['get', '/tenants'] }),\n });\n\n const form = useForm<FormData>({\n resolver: zodResolver(schema),\n defaultValues: defaults,\n });\n\n const { reset, formState, register } = form;\n\n useEffect(() => {\n if (!open) {\n return;\n }\n if (mode === 'edit' && data?.tenant && !isLoading) {\n const t = data.tenant;\n reset({\n id: t.id,\n name: str(t.name) || '',\n description: str(t.description) || '',\n isActive: t.isActive,\n });\n } else {\n reset(defaults);\n }\n }, [mode, data, open, isLoading, reset]);\n\n const onSubmit = form.handleSubmit(async (d) => {\n const body = {\n id: d.id,\n name: d.name || undefined,\n description: d.description || undefined,\n isActive: d.isActive,\n };\n if (mode === 'new') {\n await create.mutateAsync({ body });\n } else if (tenantId) {\n await update.mutateAsync({\n params: { path: { id: tenantId } },\n body: {\n name: d.name || undefined,\n description: d.description || undefined,\n isActive: d.isActive,\n },\n });\n }\n onSuccess?.();\n onClose();\n });\n\n const onDelete = async () => {\n if (!tenantId) {\n return;\n }\n await del.mutateAsync({ params: { path: { id: tenantId } } });\n onSuccess?.();\n onClose();\n };\n\n return (\n <EntityDrawer\n title={mode === 'new' ? 'New tenant' : 'Edit tenant'}\n open={open}\n onClose={onClose}\n isDirty={formState.isDirty}\n size=\"md\"\n form={\n isLoading ? (\n <FormSkeleton />\n ) : (\n <form onSubmit={onSubmit} className=\"space-y-4\">\n <div className=\"space-y-2\">\n <Label htmlFor=\"id\">\n ID <span className=\"text-destructive\">*</span>\n </Label>\n <Input\n id=\"id\"\n placeholder=\"e.g. acme\"\n {...register('id')}\n disabled={mode === 'edit'}\n />\n {formState.errors.id && (\n <p className=\"text-sm text-destructive\">\n {formState.errors.id.message}\n </p>\n )}\n </div>\n <div className=\"space-y-2\">\n <Label htmlFor=\"name\">Name</Label>\n <Input\n id=\"name\"\n placeholder=\"Display name\"\n {...register('name')}\n />\n </div>\n <div className=\"space-y-2\">\n <Label htmlFor=\"description\">Description</Label>\n <Textarea\n id=\"description\"\n placeholder=\"Description\"\n rows={3}\n {...register('description')}\n />\n </div>\n {mode === 'edit' && (\n <div className=\"flex items-center gap-2\">\n <input\n type=\"checkbox\"\n id=\"isActive\"\n {...register('isActive', {\n setValueAs: (v) => v === true || v === 'on',\n })}\n className=\"h-4 w-4\"\n />\n <Label htmlFor=\"isActive\">Active</Label>\n </div>\n )}\n </form>\n )\n }\n actions={\n <EntityFormActions\n mode={mode}\n onSubmit={onSubmit}\n onReset={mode === 'new' ? () => reset(defaults) : undefined}\n onDelete={mode === 'edit' ? onDelete : undefined}\n isSubmitting={create.isPending || update.isPending}\n isDeleting={del.isPending}\n disabled={isLoading}\n itemName=\"tenant\"\n />\n }\n />\n );\n}\n\nfunction FormSkeleton() {\n return (\n <div className=\"space-y-4\">\n <div className=\"space-y-2\">\n <Skeleton className=\"h-4 w-12\" />\n <Skeleton className=\"h-10 w-full\" />\n </div>\n <div className=\"space-y-2\">\n <Skeleton className=\"h-4 w-16\" />\n <Skeleton className=\"h-10 w-full\" />\n </div>\n <div className=\"space-y-2\">\n <Skeleton className=\"h-4 w-24\" />\n <Skeleton className=\"h-20 w-full\" />\n </div>\n </div>\n );\n}\n","'use client';\n\nimport {\n Badge,\n DataTableAction,\n DataTablePagination,\n DisplayTable,\n EntityEmptyState,\n EntityLoadingState,\n Tbody,\n Td,\n Th,\n Thead,\n Tr,\n} from '@mesob/ui/components';\nimport { IconBuilding, IconCalendar } from '@tabler/icons-react';\nimport { useState } from 'react';\nimport { Link } from '../../shared/page-helpers';\nimport { TenantCard } from './tenant-card';\nimport { TenantForm } from './tenant-form';\nimport type { Tenant } from './tenants-data';\nimport { str } from './tenants-data';\n\nconst TABLE_COLUMN_COUNT = 5;\n\ntype TenantsListProps = {\n data: Tenant[];\n isLoading?: boolean;\n view: 'table' | 'card';\n pageIndex: number;\n pageSize: number;\n pageCount: number;\n totalRows: number;\n onPageChange: (page: number) => void;\n onPageSizeChange: (size: number) => void;\n onCreateNew?: () => void;\n};\n\nexport function TenantsList({\n data,\n isLoading,\n view,\n pageIndex,\n pageSize,\n pageCount,\n totalRows,\n onPageChange,\n onPageSizeChange,\n onCreateNew,\n}: TenantsListProps) {\n const [editingTenantId, setEditingTenantId] = useState<string | null>(null);\n\n if (isLoading) {\n return (\n <EntityLoadingState\n view={view}\n rowCount={pageSize}\n columnCount={TABLE_COLUMN_COUNT}\n cardCount={pageSize}\n />\n );\n }\n if (totalRows === 0) {\n return (\n <EntityEmptyState\n icon={IconBuilding}\n entityName=\"tenant\"\n title=\"No tenants yet\"\n description=\"Create your first tenant to get started.\"\n onAction={onCreateNew}\n />\n );\n }\n if (view === 'table') {\n return (\n <div className=\"space-y-4\">\n {editingTenantId && (\n <TenantForm\n mode=\"edit\"\n tenantId={editingTenantId}\n open\n onClose={() => setEditingTenantId(null)}\n />\n )}\n <DisplayTable withTableBorder>\n <Thead>\n <Tr>\n <Th>Tenant</Th>\n <Th>Description</Th>\n <Th>Status</Th>\n <Th>Created</Th>\n <Th className=\"w-[50px]\" />\n </Tr>\n </Thead>\n <Tbody>\n {data.map((tenant) => (\n <Tr key={tenant.id} className=\"group\">\n <Td>\n <Link\n href={`/iam/tenants/${tenant.id}`}\n className=\"block text-left font-medium hover:text-primary hover:underline cursor-pointer\"\n >\n <p>{str(tenant.name) || tenant.id}</p>\n <p className=\"text-sm text-muted-foreground\">{tenant.id}</p>\n </Link>\n </Td>\n <Td>\n <span className=\"text-muted-foreground line-clamp-1 max-w-[200px]\">\n {str(tenant.description) || '—'}\n </span>\n </Td>\n <Td>\n <Badge variant={tenant.isActive ? 'default' : 'secondary'}>\n {tenant.isActive ? 'Active' : 'Inactive'}\n </Badge>\n </Td>\n <Td>\n <div className=\"flex items-center gap-1 text-muted-foreground\">\n <IconCalendar className=\"h-4 w-4\" />\n {new Date(tenant.createdAt).toLocaleDateString()}\n </div>\n </Td>\n <Td>\n <DataTableAction\n onClick={() => setEditingTenantId(tenant.id)}\n />\n </Td>\n </Tr>\n ))}\n </Tbody>\n </DisplayTable>\n <DataTablePagination\n pageIndex={pageIndex}\n pageSize={pageSize}\n pageCount={pageCount}\n totalRows={totalRows}\n onPageChange={onPageChange}\n onPageSizeChange={onPageSizeChange}\n />\n </div>\n );\n }\n return (\n <div className=\"space-y-4\">\n <div className=\"grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4\">\n {data.map((t) => (\n <TenantCard key={t.id} tenant={t} />\n ))}\n </div>\n <DataTablePagination\n pageIndex={pageIndex}\n pageSize={pageSize}\n pageCount={pageCount}\n totalRows={totalRows}\n onPageChange={onPageChange}\n onPageSizeChange={onPageSizeChange}\n />\n </div>\n );\n}\n","'use client';\n\nimport {\n Badge,\n Button,\n Card,\n CardContent,\n CardHeader,\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuPortal,\n DropdownMenuTrigger,\n} from '@mesob/ui/components';\nimport { IconDots, IconPencil } from '@tabler/icons-react';\nimport { useState } from 'react';\nimport { Link } from '../../shared/page-helpers';\nimport { TenantForm } from './tenant-form';\nimport { str, type Tenant } from './tenants-data';\n\ntype TenantCardProps = { tenant: Tenant };\n\nexport function TenantCard({ tenant }: TenantCardProps) {\n const [editOpen, setEditOpen] = useState(false);\n return (\n <>\n <Card className=\"group hover:shadow-md transition-shadow\">\n <CardHeader className=\"pb-2\">\n <div className=\"flex items-start justify-between\">\n <Link\n href={`/iam/tenants/${tenant.id}`}\n className=\"text-left font-semibold hover:text-primary hover:underline\"\n >\n {str(tenant.name) || tenant.id}\n </Link>\n <DropdownMenu>\n <DropdownMenuTrigger\n render={\n <Button\n variant=\"ghost\"\n size=\"icon\"\n className=\"h-8 w-8 opacity-0 group-hover:opacity-100 transition-opacity\"\n />\n }\n >\n <IconDots className=\"h-4 w-4\" />\n </DropdownMenuTrigger>\n <DropdownMenuPortal>\n <DropdownMenuContent>\n <DropdownMenuItem onClick={() => setEditOpen(true)}>\n <IconPencil className=\"mr-2 h-4 w-4\" />\n Edit\n </DropdownMenuItem>\n </DropdownMenuContent>\n </DropdownMenuPortal>\n </DropdownMenu>\n </div>\n <p className=\"text-sm text-muted-foreground\">{tenant.id}</p>\n </CardHeader>\n <CardContent className=\"space-y-2\">\n <Badge variant={tenant.isActive ? 'default' : 'secondary'}>\n {tenant.isActive ? 'Active' : 'Inactive'}\n </Badge>\n {str(tenant.description) ? (\n <p className=\"text-sm text-muted-foreground line-clamp-2\">\n {str(tenant.description)}\n </p>\n ) : null}\n <p className=\"text-xs text-muted-foreground\">\n Created {new Date(tenant.createdAt).toLocaleDateString()}\n </p>\n </CardContent>\n </Card>\n {editOpen ? (\n <TenantForm\n mode=\"edit\"\n tenantId={tenant.id}\n open={editOpen}\n onClose={() => setEditOpen(false)}\n />\n ) : null}\n </>\n );\n}\n"],"mappings":"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","names":["IconBuilding","useState","Badge","useState","jsx","jsxs","jsx","jsxs","useState","Badge","jsx","jsxs","useState","IconBuilding"]}
@@ -1,12 +1,12 @@
1
- import {
2
- ChangePhoneForm
3
- } from "./chunk-TLQMK2QF.js";
4
1
  import {
5
2
  ChangeEmailForm
6
- } from "./chunk-PG65ZD7A.js";
3
+ } from "./chunk-5BFG47VF.js";
7
4
  import {
8
5
  ChangePasswordForm
9
6
  } from "./chunk-ZZ6D4KE4.js";
7
+ import {
8
+ ChangePhoneForm
9
+ } from "./chunk-TLQMK2QF.js";
10
10
  import {
11
11
  useSession
12
12
  } from "./chunk-AWSAC7RT.js";
@@ -62,4 +62,4 @@ function Security() {
62
62
  export {
63
63
  Security
64
64
  };
65
- //# sourceMappingURL=chunk-RBXITSE2.js.map
65
+ //# sourceMappingURL=chunk-73ZNGEWU.js.map
@@ -1,3 +1,6 @@
1
+ import {
2
+ IAMGuard
3
+ } from "./chunk-V6ZHX4LT.js";
1
4
  import {
2
5
  useApi,
3
6
  useConfig
@@ -29,6 +32,12 @@ import { jsx, jsxs } from "react/jsx-runtime";
29
32
  function RoleDetailPage({
30
33
  roleId,
31
34
  basePath = "/iam/roles"
35
+ }) {
36
+ return /* @__PURE__ */ jsx(IAMGuard, { children: /* @__PURE__ */ jsx(RoleDetailPageContent, { roleId, basePath }) });
37
+ }
38
+ function RoleDetailPageContent({
39
+ roleId,
40
+ basePath = "/iam/roles"
32
41
  }) {
33
42
  const { hooks } = useApi();
34
43
  const { config } = useConfig();
@@ -191,4 +200,4 @@ function RoleDetailSkeleton() {
191
200
  export {
192
201
  RoleDetailPage
193
202
  };
194
- //# sourceMappingURL=chunk-DY3NVBLJ.js.map
203
+ //# sourceMappingURL=chunk-7CLKBH5Z.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/iam/role-detail-page.tsx"],"sourcesContent":["'use client';\n\nimport { zodResolver } from '@hookform/resolvers/zod';\nimport {\n EntityFormActions,\n Form,\n FormControl,\n FormField,\n FormItem,\n FormLabel,\n FormMessage,\n Input,\n LocaleInputText,\n LocaleInputTextarea,\n Section,\n Skeleton,\n} from '@mesob/ui/components';\nimport { useLocaleSchemas } from '@mesob/ui/providers';\nimport { useQueryClient } from '@tanstack/react-query';\nimport { useEffect, useMemo } from 'react';\nimport type { Resolver } from 'react-hook-form';\nimport { useForm } from 'react-hook-form';\nimport { toast } from 'sonner';\nimport { z } from 'zod';\nimport { useApi, useConfig } from '../../provider';\nimport { IAMGuard } from './iam-guard';\n\ntype RoleFormData = {\n name: Record<string, string>;\n code: string;\n description: Record<string, string>;\n};\n\ntype RoleDetailPageProps = {\n roleId: string;\n basePath?: string;\n};\n\nexport function RoleDetailPage({\n roleId,\n basePath = '/iam/roles',\n}: RoleDetailPageProps) {\n return (\n <IAMGuard>\n <RoleDetailPageContent roleId={roleId} basePath={basePath} />\n </IAMGuard>\n );\n}\n\nfunction RoleDetailPageContent({\n roleId,\n basePath = '/iam/roles',\n}: RoleDetailPageProps) {\n const { hooks } = useApi();\n const { config } = useConfig();\n const qc = useQueryClient();\n const { localeInputDefault, requiredSchema, optionalSchema } =\n useLocaleSchemas();\n const schema = useMemo(\n () =>\n z.object({\n name: requiredSchema,\n code: z.string().min(1, 'Code is required'),\n description: optionalSchema,\n }),\n [requiredSchema, optionalSchema],\n );\n const defaults: RoleFormData = useMemo(\n () => ({\n name: { ...localeInputDefault },\n code: '',\n description: { ...localeInputDefault },\n }),\n [localeInputDefault],\n );\n\n const { data, isLoading } = hooks.useQuery(\n 'get',\n '/roles/{id}',\n { params: { path: { id: roleId } } },\n { enabled: !!roleId },\n );\n\n const update = hooks.useMutation('put', '/roles/{id}', {\n onSuccess: () => {\n qc.invalidateQueries({ queryKey: ['get', '/roles'] });\n qc.invalidateQueries({ queryKey: ['get', '/roles/{id}'] });\n toast.success('Role updated');\n },\n onError: () => {\n toast.error('Failed to update role');\n },\n });\n const remove = hooks.useMutation('delete', '/roles/{id}', {\n onSuccess: () => {\n qc.invalidateQueries({ queryKey: ['get', '/roles'] });\n toast.success('Role deleted');\n config.navigation?.onNavigate?.(basePath);\n },\n onError: () => {\n toast.error('Failed to delete role');\n },\n });\n\n const form = useForm<RoleFormData>({\n resolver: zodResolver(schema) as Resolver<RoleFormData>,\n defaultValues: defaults,\n });\n\n const { reset, formState, control, register } = form;\n\n useEffect(() => {\n if (!data?.role) {\n return;\n }\n const r = data.role;\n reset({\n name: (r.name ?? {}) as RoleFormData['name'],\n code: r.code,\n description: (r.description ?? {}) as RoleFormData['description'],\n });\n }, [data?.role, reset]);\n\n if (!roleId) {\n return null;\n }\n\n const role = data?.role;\n const editable = role?.isEditable !== false;\n const deletable = role?.isDeletable !== false;\n const onSubmit = form.handleSubmit(async (d) => {\n await update.mutateAsync({\n params: { path: { id: roleId } },\n body: {\n name: d.name,\n code: d.code,\n description: d.description ?? undefined,\n },\n });\n });\n\n const footer = role ? (\n <EntityFormActions\n mode=\"edit\"\n onSubmit={onSubmit}\n onDelete={\n deletable\n ? () =>\n remove.mutate({\n params: { path: { id: roleId } },\n })\n : undefined\n }\n isSubmitting={update.isPending}\n isDeleting={remove.isPending}\n disabled={!editable}\n itemName=\"role\"\n />\n ) : null;\n\n return (\n <Section title=\"Role details\" footer={footer} defaultOpen>\n {isLoading || !role ? (\n <RoleDetailSkeleton />\n ) : (\n <Form {...form}>\n <form onSubmit={onSubmit} className=\"space-y-4\">\n <LocaleInputText\n label=\"Name\"\n field=\"name\"\n required\n register={register}\n errors={formState.errors}\n placeholder=\"e.g. Administrator\"\n disabled={!editable}\n />\n <FormField\n control={control}\n name=\"code\"\n render={({ field }) => (\n <FormItem>\n <FormLabel>\n Code <span className=\"text-destructive\">*</span>\n </FormLabel>\n <FormControl>\n <Input\n placeholder=\"e.g. admin\"\n disabled={!editable}\n {...field}\n />\n </FormControl>\n <FormMessage />\n </FormItem>\n )}\n />\n <LocaleInputTextarea\n label=\"Description\"\n field=\"description\"\n register={register}\n errors={formState.errors}\n placeholder=\"Description\"\n rows={3}\n disabled={!editable}\n />\n </form>\n </Form>\n )}\n </Section>\n );\n}\n\nfunction RoleDetailSkeleton() {\n return (\n <div className=\"space-y-4\">\n <div className=\"space-y-2\">\n <Skeleton className=\"h-4 w-16\" />\n <Skeleton className=\"h-10 w-full\" />\n </div>\n <div className=\"space-y-2\">\n <Skeleton className=\"h-4 w-14\" />\n <Skeleton className=\"h-10 w-full\" />\n </div>\n <div className=\"space-y-2\">\n <Skeleton className=\"h-4 w-24\" />\n <Skeleton className=\"h-20 w-full\" />\n </div>\n </div>\n );\n}\n"],"mappings":"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","names":[]}
@@ -8,7 +8,7 @@ import {
8
8
 
9
9
  // src/pages/iam/users/_components/user-card.tsx
10
10
  import {
11
- Button,
11
+ Button as Button2,
12
12
  Card,
13
13
  CardContent,
14
14
  CardHeader,
@@ -27,6 +27,16 @@ import { useState } from "react";
27
27
  // src/pages/iam/users/_components/user-form.tsx
28
28
  import { zodResolver } from "@hookform/resolvers/zod";
29
29
  import {
30
+ AlertDialog,
31
+ AlertDialogAction,
32
+ AlertDialogCancel,
33
+ AlertDialogContent,
34
+ AlertDialogDescription,
35
+ AlertDialogFooter,
36
+ AlertDialogHeader,
37
+ AlertDialogTitle,
38
+ AlertDialogTrigger,
39
+ Button,
30
40
  Checkbox,
31
41
  EntityDrawer,
32
42
  EntityFormActions,
@@ -40,6 +50,7 @@ import {
40
50
  Skeleton,
41
51
  Stack
42
52
  } from "@mesob/ui/components";
53
+ import { IconBan, IconUserCheck } from "@tabler/icons-react";
43
54
  import { useQueryClient } from "@tanstack/react-query";
44
55
  import { useEffect } from "react";
45
56
  import { useForm, useWatch } from "react-hook-form";
@@ -73,7 +84,10 @@ function UserForm({
73
84
  "get",
74
85
  "/users/{id}",
75
86
  { params: { path: { id: userId ?? "" } } },
76
- { enabled: mode === "edit" && !!userId && open }
87
+ {
88
+ enabled: mode === "edit" && !!userId && open,
89
+ refetchOnMount: "always"
90
+ }
77
91
  );
78
92
  const { data: rolesData } = authApi$.useQuery(
79
93
  "get",
@@ -129,9 +143,7 @@ function UserForm({
129
143
  const update = authApi$.useMutation("put", "/users/{id}", {
130
144
  onSuccess: () => {
131
145
  qc.invalidateQueries({ queryKey: ["get", "/users"] });
132
- if (userId) {
133
- qc.invalidateQueries({ queryKey: ["get", "/users/{id}"] });
134
- }
146
+ qc.invalidateQueries({ queryKey: ["get", "/users/{id}"] });
135
147
  }
136
148
  });
137
149
  const del = authApi$.useMutation("delete", "/users/{id}", {
@@ -139,6 +151,14 @@ function UserForm({
139
151
  qc.invalidateQueries({ queryKey: ["get", "/users"] });
140
152
  }
141
153
  });
154
+ const ban = authApi$.useMutation("post", "/users/{id}/ban", {
155
+ onSuccess: () => {
156
+ qc.invalidateQueries({ queryKey: ["get", "/users"] });
157
+ qc.invalidateQueries({ queryKey: ["get", "/users/{id}"] });
158
+ }
159
+ });
160
+ const bannedUntil = user?.bannedUntil;
161
+ const isBanned = !!bannedUntil && new Date(bannedUntil) > /* @__PURE__ */ new Date();
142
162
  const onSubmit = form.handleSubmit(async (d) => {
143
163
  const base = {
144
164
  fullName: d.fullName,
@@ -177,11 +197,24 @@ function UserForm({
177
197
  onSuccess?.();
178
198
  onClose();
179
199
  };
200
+ const onBanUnban = async () => {
201
+ if (!userId) {
202
+ return;
203
+ }
204
+ await ban.mutateAsync({
205
+ params: { path: { id: userId } },
206
+ body: {
207
+ bannedUntil: isBanned ? null : "9999-12-31T23:59:59.999Z"
208
+ }
209
+ });
210
+ onSuccess?.();
211
+ };
180
212
  const toggleRole = (roleId, checked) => {
181
213
  const next = checked ? [...roleIds, roleId] : roleIds.filter((id) => id !== roleId);
182
214
  setValue("roleIds", next, { shouldDirty: true });
183
215
  };
184
216
  const isSubmitting = create.isPending || update.isPending;
217
+ const isBanning = ban.isPending;
185
218
  return /* @__PURE__ */ jsx(
186
219
  EntityDrawer,
187
220
  {
@@ -322,19 +355,56 @@ function UserForm({
322
355
  }) })
323
356
  ] })
324
357
  ] }) }),
325
- actions: /* @__PURE__ */ jsx(
326
- EntityFormActions,
327
- {
328
- mode,
329
- onSubmit,
330
- onReset: mode === "new" ? () => reset(defaults) : void 0,
331
- onDelete: mode === "edit" ? onDelete : void 0,
332
- isSubmitting,
333
- isDeleting: del.isPending,
334
- disabled: userLoading && mode === "edit",
335
- itemName: "user"
336
- }
337
- )
358
+ actions: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
359
+ /* @__PURE__ */ jsx(
360
+ EntityFormActions,
361
+ {
362
+ mode,
363
+ onSubmit,
364
+ onReset: mode === "new" ? () => reset(defaults) : void 0,
365
+ onDelete: mode === "edit" ? onDelete : void 0,
366
+ isSubmitting,
367
+ isDeleting: del.isPending,
368
+ disabled: userLoading && mode === "edit",
369
+ itemName: "user"
370
+ }
371
+ ),
372
+ mode === "edit" && userId && /* @__PURE__ */ jsxs(AlertDialog, { children: [
373
+ /* @__PURE__ */ jsx(
374
+ AlertDialogTrigger,
375
+ {
376
+ render: /* @__PURE__ */ jsx(
377
+ Button,
378
+ {
379
+ variant: "outline",
380
+ disabled: userLoading,
381
+ loading: isBanning,
382
+ className: "cursor-pointer",
383
+ leftIcon: isBanned ? /* @__PURE__ */ jsx(IconUserCheck, { className: "size-4" }) : /* @__PURE__ */ jsx(IconBan, { className: "size-4" })
384
+ }
385
+ ),
386
+ children: isBanned ? "Unban" : "Ban"
387
+ }
388
+ ),
389
+ /* @__PURE__ */ jsxs(AlertDialogContent, { children: [
390
+ /* @__PURE__ */ jsxs(AlertDialogHeader, { children: [
391
+ /* @__PURE__ */ jsx(AlertDialogTitle, { children: isBanned ? "Unban user?" : "Ban user?" }),
392
+ /* @__PURE__ */ jsx(AlertDialogDescription, { children: isBanned ? "This user will be able to sign in again." : "This user will not be able to sign in until unbanned." })
393
+ ] }),
394
+ /* @__PURE__ */ jsxs(AlertDialogFooter, { children: [
395
+ /* @__PURE__ */ jsx(AlertDialogCancel, { children: "Cancel" }),
396
+ /* @__PURE__ */ jsx(
397
+ AlertDialogAction,
398
+ {
399
+ onClick: onBanUnban,
400
+ variant: isBanned ? void 0 : "destructive",
401
+ children: isBanned ? "Unban" : "Ban"
402
+ }
403
+ )
404
+ ] })
405
+ ] })
406
+ ] })
407
+ ] })
338
408
  }
339
409
  );
340
410
  }
@@ -365,7 +435,7 @@ function UserCard({ user }) {
365
435
  DropdownMenuTrigger,
366
436
  {
367
437
  render: /* @__PURE__ */ jsx2(
368
- Button,
438
+ Button2,
369
439
  {
370
440
  variant: "ghost",
371
441
  size: "icon",
@@ -412,4 +482,4 @@ export {
412
482
  UserForm,
413
483
  UserCard
414
484
  };
415
- //# sourceMappingURL=chunk-FPYQ7XGV.js.map
485
+ //# sourceMappingURL=chunk-ECF6S2Y2.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/pages/iam/users/_components/user-card.tsx","../src/pages/iam/users/_components/user-form.tsx"],"sourcesContent":["'use client';\n\nimport {\n Button,\n Card,\n CardContent,\n CardHeader,\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuPortal,\n DropdownMenuTrigger,\n Tooltip,\n TooltipContent,\n TooltipTrigger,\n} from '@mesob/ui/components';\nimport { IconCircleCheck, IconDots, IconPencil } from '@tabler/icons-react';\nimport { useState } from 'react';\nimport { Link } from '../../shared/page-helpers';\nimport { UserForm } from './user-form';\nimport type { User } from './users-data';\n\ntype UserCardProps = { user: User };\n\nexport function UserCard({ user }: UserCardProps) {\n const [editOpen, setEditOpen] = useState(false);\n return (\n <>\n <Card className=\"group hover:shadow-md transition-shadow\">\n <CardHeader className=\"pb-2\">\n <div className=\"flex items-start justify-between\">\n <Link\n href={`/iam/users/${user.id}`}\n className=\"text-left font-semibold hover:text-primary hover:underline\"\n >\n {user.fullName}\n </Link>\n <DropdownMenu>\n <DropdownMenuTrigger\n render={\n <Button\n variant=\"ghost\"\n size=\"icon\"\n className=\"h-8 w-8 opacity-0 group-hover:opacity-100 transition-opacity\"\n />\n }\n >\n <IconDots className=\"h-4 w-4\" />\n </DropdownMenuTrigger>\n <DropdownMenuPortal>\n <DropdownMenuContent>\n <DropdownMenuItem onClick={() => setEditOpen(true)}>\n <IconPencil className=\"mr-2 h-4 w-4\" />\n Edit\n </DropdownMenuItem>\n </DropdownMenuContent>\n </DropdownMenuPortal>\n </DropdownMenu>\n </div>\n </CardHeader>\n <CardContent className=\"space-y-2\">\n {user.email && (\n <div className=\"flex items-center gap-2\">\n <span className=\"text-sm\">{user.email}</span>\n {user.emailVerified && (\n <Tooltip>\n <TooltipTrigger>\n <IconCircleCheck className=\"size-4 text-muted-foreground\" />\n </TooltipTrigger>\n <TooltipContent>Email verified</TooltipContent>\n </Tooltip>\n )}\n </div>\n )}\n <p className=\"text-xs text-muted-foreground\">\n Last sign in{' '}\n {user.lastSignInAt\n ? new Date(user.lastSignInAt).toLocaleDateString()\n : 'never'}\n </p>\n </CardContent>\n </Card>\n {editOpen && (\n <UserForm\n mode=\"edit\"\n userId={user.id}\n open={editOpen}\n onClose={() => setEditOpen(false)}\n />\n )}\n </>\n );\n}\n","'use client';\n\nimport { zodResolver } from '@hookform/resolvers/zod';\nimport {\n AlertDialog,\n AlertDialogAction,\n AlertDialogCancel,\n AlertDialogContent,\n AlertDialogDescription,\n AlertDialogFooter,\n AlertDialogHeader,\n AlertDialogTitle,\n AlertDialogTrigger,\n Button,\n Checkbox,\n EntityDrawer,\n EntityFormActions,\n Form,\n FormControl,\n FormField,\n FormItem,\n FormLabel,\n FormMessage,\n Input,\n Skeleton,\n Stack,\n} from '@mesob/ui/components';\nimport { IconBan, IconUserCheck } from '@tabler/icons-react';\nimport { useQueryClient } from '@tanstack/react-query';\nimport { useEffect } from 'react';\nimport { useForm, useWatch } from 'react-hook-form';\nimport { z } from 'zod';\nimport type { Role } from '../../roles/_components/roles-data';\nimport { str } from '../../roles/_components/roles-data';\nimport { authApi$ } from '../../shared/page-helpers';\n\nconst schema = z.object({\n fullName: z.string().trim().min(1, 'Full name is required'),\n email: z.string().email('Invalid email').nullable().or(z.literal('')),\n phone: z.string().nullable(),\n emailVerified: z.boolean(),\n phoneVerified: z.boolean(),\n roleIds: z.array(z.string().uuid()),\n});\n\ntype FormData = z.infer<typeof schema>;\n\nconst defaults: FormData = {\n fullName: '',\n email: '',\n phone: null,\n emailVerified: false,\n phoneVerified: false,\n roleIds: [],\n};\n\ntype UserFormProps = {\n mode: 'new' | 'edit';\n userId?: string;\n open: boolean;\n onClose: () => void;\n onSuccess?: () => void;\n};\n\n// biome-ignore lint/complexity/noExcessiveCognitiveComplexity: form + roles section\nexport function UserForm({\n mode,\n userId,\n open,\n onClose,\n onSuccess,\n}: UserFormProps) {\n const qc = useQueryClient();\n const { data: userData, isLoading: userLoading } = authApi$.useQuery(\n 'get',\n '/users/{id}',\n { params: { path: { id: userId ?? '' } } },\n {\n enabled: mode === 'edit' && !!userId && open,\n refetchOnMount: 'always',\n },\n );\n const { data: rolesData } = authApi$.useQuery(\n 'get',\n '/roles',\n { params: { query: { limit: 100 } } },\n { enabled: open },\n );\n const allRoles = rolesData?.roles ?? [];\n const roles = allRoles\n .filter((r: Role) => (r.code ?? '').toLowerCase() !== 'owner')\n .sort((a: Role, b: Role) =>\n (str(a.name) || a.code)\n .toLowerCase()\n .localeCompare((str(b.name) || b.code).toLowerCase()),\n );\n const user = userData?.user;\n\n const form = useForm<FormData>({\n resolver: zodResolver(schema),\n defaultValues: defaults,\n });\n const { control, formState, reset, setValue } = form;\n const email = useWatch({ control, name: 'email' }) ?? '';\n const phone = useWatch({ control, name: 'phone' }) ?? '';\n const roleIds = useWatch({ control, name: 'roleIds' }) ?? [];\n const hasEmail = (email ?? '').toString().trim().length > 0;\n const hasPhone = (phone ?? '').toString().trim().length > 0;\n\n useEffect(() => {\n if (!hasEmail) {\n setValue('emailVerified', false, { shouldDirty: false });\n }\n if (!hasPhone) {\n setValue('phoneVerified', false, { shouldDirty: false });\n }\n }, [hasEmail, hasPhone, setValue]);\n\n useEffect(() => {\n if (!open) {\n return;\n }\n if (mode === 'edit' && user && !userLoading) {\n reset({\n fullName: user.fullName,\n email: user.email ?? '',\n phone: user.phone ?? null,\n emailVerified: user.emailVerified ?? false,\n phoneVerified: user.phoneVerified ?? false,\n roleIds: user.roles ?? [],\n });\n } else {\n reset(defaults);\n }\n }, [mode, user, open, userLoading, reset]);\n\n const create = authApi$.useMutation('post', '/users', {\n onSuccess: () => {\n qc.invalidateQueries({ queryKey: ['get', '/users'] });\n },\n });\n const update = authApi$.useMutation('put', '/users/{id}', {\n onSuccess: () => {\n qc.invalidateQueries({ queryKey: ['get', '/users'] });\n qc.invalidateQueries({ queryKey: ['get', '/users/{id}'] });\n },\n });\n const del = authApi$.useMutation('delete', '/users/{id}', {\n onSuccess: () => {\n qc.invalidateQueries({ queryKey: ['get', '/users'] });\n },\n });\n const ban = authApi$.useMutation('post', '/users/{id}/ban', {\n onSuccess: () => {\n qc.invalidateQueries({ queryKey: ['get', '/users'] });\n qc.invalidateQueries({ queryKey: ['get', '/users/{id}'] });\n },\n });\n const bannedUntil = (user as { bannedUntil?: string | null } | undefined)\n ?.bannedUntil;\n const isBanned = !!bannedUntil && new Date(bannedUntil) > new Date();\n\n const onSubmit = form.handleSubmit(async (d) => {\n const base = {\n fullName: d.fullName,\n email: d.email?.trim() || undefined,\n phone: d.phone?.trim() || undefined,\n emailVerified: d.emailVerified,\n phoneVerified: d.phoneVerified,\n };\n if (mode === 'new') {\n await create.mutateAsync({\n body: {\n ...base,\n email: base.email || undefined,\n phone: base.phone || undefined,\n },\n });\n } else if (userId) {\n await update.mutateAsync({\n params: { path: { id: userId } },\n body: {\n ...base,\n email: base.email ?? null,\n phone: base.phone ?? null,\n roleIds: d.roleIds,\n },\n });\n }\n onSuccess?.();\n onClose();\n });\n\n const onDelete = async () => {\n if (!userId) {\n return;\n }\n await del.mutateAsync({ params: { path: { id: userId } } });\n onSuccess?.();\n onClose();\n };\n\n const onBanUnban = async () => {\n if (!userId) {\n return;\n }\n await ban.mutateAsync({\n params: { path: { id: userId } },\n body: {\n bannedUntil: isBanned ? null : '9999-12-31T23:59:59.999Z',\n },\n });\n onSuccess?.();\n };\n\n const toggleRole = (roleId: string, checked: boolean) => {\n const next = checked\n ? [...roleIds, roleId]\n : roleIds.filter((id) => id !== roleId);\n setValue('roleIds', next, { shouldDirty: true });\n };\n\n const isSubmitting = create.isPending || update.isPending;\n const isBanning = ban.isPending;\n\n return (\n <EntityDrawer\n title={mode === 'new' ? 'New user' : 'Edit user'}\n open={open}\n onClose={onClose}\n isDirty={formState.isDirty}\n size=\"lg\"\n form={\n userLoading && mode === 'edit' ? (\n <FormSkeleton />\n ) : (\n <Form {...form}>\n <form onSubmit={onSubmit} className=\"space-y-6\">\n <Stack>\n <FormField\n control={control}\n name=\"fullName\"\n render={({ field }) => (\n <FormItem>\n <FormLabel>\n Full name <span className=\"text-destructive\">*</span>\n </FormLabel>\n <FormControl>\n <Input placeholder=\"Full name\" {...field} />\n </FormControl>\n <FormMessage />\n </FormItem>\n )}\n />\n <FormField\n control={control}\n name=\"email\"\n render={({ field }) => (\n <FormItem>\n <FormLabel>Email</FormLabel>\n <FormControl>\n <Input\n type=\"email\"\n placeholder=\"user@example.com\"\n {...field}\n value={field.value ?? ''}\n />\n </FormControl>\n <FormMessage />\n </FormItem>\n )}\n />\n <FormField\n control={control}\n name=\"phone\"\n render={({ field }) => (\n <FormItem>\n <FormLabel>Phone</FormLabel>\n <FormControl>\n <Input\n placeholder=\"+251911223344\"\n {...field}\n value={field.value ?? ''}\n />\n </FormControl>\n <FormMessage />\n </FormItem>\n )}\n />\n <div className=\"space-y-4\">\n <FormLabel>Verification</FormLabel>\n <div className=\"flex flex-wrap gap-6 pt-1\">\n <FormField\n control={control}\n name=\"emailVerified\"\n render={({ field }) => (\n <FormItem className=\"flex flex-row items-center gap-2 space-y-0\">\n <FormControl>\n <Checkbox\n size=\"lg\"\n checked={field.value}\n disabled={!hasEmail}\n onCheckedChange={(c) =>\n field.onChange(c === true)\n }\n />\n </FormControl>\n <FormLabel className=\"cursor-pointer font-normal\">\n Email verified\n </FormLabel>\n <FormMessage />\n </FormItem>\n )}\n />\n <FormField\n control={control}\n name=\"phoneVerified\"\n render={({ field }) => (\n <FormItem className=\"flex flex-row items-center gap-2 space-y-0\">\n <FormControl>\n <Checkbox\n size=\"lg\"\n checked={field.value}\n disabled={!hasPhone}\n onCheckedChange={(c) =>\n field.onChange(c === true)\n }\n />\n </FormControl>\n <FormLabel className=\"cursor-pointer font-normal\">\n Phone verified\n </FormLabel>\n <FormMessage />\n </FormItem>\n )}\n />\n </div>\n </div>\n </Stack>\n\n {mode === 'edit' && roles.length > 0 && (\n <div className=\"space-y-4\">\n <FormLabel>Roles</FormLabel>\n <div className=\"flex flex-col gap-3 pt-1\">\n {roles.map((role: Role) => {\n const desc = str(role.description);\n return (\n <div key={role.id} className=\"flex items-start gap-3\">\n <Checkbox\n size=\"lg\"\n id={`role-${role.id}`}\n checked={roleIds.includes(role.id)}\n onCheckedChange={(c) =>\n toggleRole(role.id, c === true)\n }\n className=\"mt-0.5\"\n />\n <label\n htmlFor={`role-${role.id}`}\n className=\"flex min-w-0 flex-1 cursor-pointer flex-col gap-0.5\"\n >\n <span className=\"text-sm font-medium\">\n {str(role.name) || role.code}\n </span>\n {desc ? (\n <span className=\"text-xs text-muted-foreground\">\n {desc}\n </span>\n ) : null}\n </label>\n </div>\n );\n })}\n </div>\n </div>\n )}\n </form>\n </Form>\n )\n }\n actions={\n <div className=\"flex items-center gap-2\">\n <EntityFormActions\n mode={mode}\n onSubmit={onSubmit}\n onReset={mode === 'new' ? () => reset(defaults) : undefined}\n onDelete={mode === 'edit' ? onDelete : undefined}\n isSubmitting={isSubmitting}\n isDeleting={del.isPending}\n disabled={userLoading && mode === 'edit'}\n itemName=\"user\"\n />\n {mode === 'edit' && userId && (\n <AlertDialog>\n <AlertDialogTrigger\n render={\n <Button\n variant=\"outline\"\n disabled={userLoading}\n loading={isBanning}\n className=\"cursor-pointer\"\n leftIcon={\n isBanned ? (\n <IconUserCheck className=\"size-4\" />\n ) : (\n <IconBan className=\"size-4\" />\n )\n }\n />\n }\n >\n {isBanned ? 'Unban' : 'Ban'}\n </AlertDialogTrigger>\n <AlertDialogContent>\n <AlertDialogHeader>\n <AlertDialogTitle>\n {isBanned ? 'Unban user?' : 'Ban user?'}\n </AlertDialogTitle>\n <AlertDialogDescription>\n {isBanned\n ? 'This user will be able to sign in again.'\n : 'This user will not be able to sign in until unbanned.'}\n </AlertDialogDescription>\n </AlertDialogHeader>\n <AlertDialogFooter>\n <AlertDialogCancel>Cancel</AlertDialogCancel>\n <AlertDialogAction\n onClick={onBanUnban}\n variant={isBanned ? undefined : 'destructive'}\n >\n {isBanned ? 'Unban' : 'Ban'}\n </AlertDialogAction>\n </AlertDialogFooter>\n </AlertDialogContent>\n </AlertDialog>\n )}\n </div>\n }\n />\n );\n}\n\nfunction FormSkeleton() {\n return (\n <div className=\"space-y-4\">\n {[1, 2, 3, 4, 5].map((i) => (\n <div key={i} className=\"space-y-2\">\n <Skeleton className=\"h-4 w-20\" />\n <Skeleton className=\"h-10 w-full\" />\n </div>\n ))}\n </div>\n );\n}\n"],"mappings":"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","names":["Button","jsx","jsxs","Button"]}
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  UserCard
3
- } from "./chunk-FPYQ7XGV.js";
3
+ } from "./chunk-ECF6S2Y2.js";
4
4
  import {
5
5
  authApi$
6
6
  } from "./chunk-W3D4HG5W.js";
@@ -150,4 +150,4 @@ function UserSelector({
150
150
  export {
151
151
  UserSelector
152
152
  };
153
- //# sourceMappingURL=chunk-G74DDR4O.js.map
153
+ //# sourceMappingURL=chunk-GBDNBY6K.js.map
@@ -4,6 +4,9 @@ import {
4
4
  import {
5
5
  defaultEntityQueryOptions
6
6
  } from "./chunk-NPW7D2HZ.js";
7
+ import {
8
+ IAMGuard
9
+ } from "./chunk-V6ZHX4LT.js";
7
10
  import {
8
11
  useApi,
9
12
  useConfig
@@ -191,6 +194,9 @@ function SessionsList({
191
194
  // src/components/iam/sessions-page.tsx
192
195
  import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
193
196
  function SessionsPage() {
197
+ return /* @__PURE__ */ jsx3(IAMGuard, { children: /* @__PURE__ */ jsx3(SessionsPageContent, {}) });
198
+ }
199
+ function SessionsPageContent() {
194
200
  const { hooks } = useApi();
195
201
  const { config } = useConfig();
196
202
  const homeHref = config.navigation?.defaultRedirectUrl || "/";
@@ -265,4 +271,4 @@ function SessionsPage() {
265
271
  export {
266
272
  SessionsPage
267
273
  };
268
- //# sourceMappingURL=chunk-LHZ4EEN6.js.map
274
+ //# sourceMappingURL=chunk-H5PUZDNU.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/iam/sessions-page.tsx","../src/pages/iam/sessions/_components/sessions-list.tsx","../src/pages/iam/sessions/_components/session-card.tsx"],"sourcesContent":["'use client';\n\nimport {\n EntityFilter,\n EntityHeader,\n EntitySort,\n EntityViewToggle,\n PageBody,\n PageContainer,\n useBreadcrumbs,\n useEntityPagination,\n useEntityParams,\n} from '@mesob/ui/components';\nimport { IconDeviceDesktop } from '@tabler/icons-react';\nimport { defaultEntityQueryOptions } from '../../lib/query-options';\nimport { SessionsList } from '../../pages/iam/sessions/_components/sessions-list';\nimport { useApi, useConfig } from '../../provider';\nimport { IAMGuard } from './iam-guard';\n\nexport function SessionsPage() {\n return (\n <IAMGuard>\n <SessionsPageContent />\n </IAMGuard>\n );\n}\n\nfunction SessionsPageContent() {\n const { hooks } = useApi();\n const { config } = useConfig();\n const homeHref = config.navigation?.defaultRedirectUrl || '/';\n useBreadcrumbs({\n items: [\n { label: 'Home', href: homeHref },\n { label: 'IAM', href: '/iam/sessions' },\n { label: 'Sessions' },\n ],\n });\n const { queryConfig, params, setParams } = useEntityParams();\n\n const { data, isPending, isFetching } = hooks.useQuery(\n 'get',\n '/sessions',\n queryConfig,\n defaultEntityQueryOptions,\n );\n\n const isLoading = isPending || isFetching;\n const sessions = data?.sessions ?? [];\n const { total, pageCount } = useEntityPagination({\n items: sessions,\n total: data?.total,\n pageSize: params.pageSize,\n });\n\n return (\n <PageContainer className=\"flex flex-1 flex-col gap-4 p-4 pt-0\">\n <PageBody className=\"px-0\">\n <EntityHeader\n icon={<IconDeviceDesktop className=\"h-5 w-5\" />}\n title=\"Sessions\"\n filter={\n <EntityFilter\n options={[\n { label: 'All', value: '' },\n { label: 'Active', value: 'active' },\n { label: 'Expired', value: 'expired' },\n ]}\n placeholder=\"Filter\"\n />\n }\n sort={\n <EntitySort\n options={[\n { label: 'Created', value: 'createdAt' },\n { label: 'Updated', value: 'updatedAt' },\n { label: 'Expires', value: 'expiresAt' },\n ]}\n />\n }\n view={<EntityViewToggle views={['table', 'card']} />}\n />\n <SessionsList\n data={sessions}\n isLoading={isLoading}\n view={(params.view || 'table') as 'table' | 'card'}\n pageIndex={params.page - 1}\n pageSize={params.pageSize}\n pageCount={pageCount}\n totalRows={total}\n onPageChange={(p) => setParams({ page: p + 1 })}\n onPageSizeChange={(size) => setParams({ pageSize: size, page: 1 })}\n />\n </PageBody>\n </PageContainer>\n );\n}\n","'use client';\n\nimport {\n DataTableAction,\n DataTablePagination,\n DisplayTable,\n EntityEmptyState,\n EntityLoadingState,\n Tbody,\n Td,\n Th,\n Thead,\n Tr,\n} from '@mesob/ui/components';\nimport { IconCalendar, IconDeviceDesktop } from '@tabler/icons-react';\nimport { useQueryClient } from '@tanstack/react-query';\nimport { authApi$ } from '../../shared/page-helpers';\nimport { SessionCard } from './session-card';\nimport type { Session } from './sessions-data';\n\nconst TABLE_COLUMN_COUNT = 5;\n\nfunction SessionRevokeButton({ sessionId }: { sessionId: string }) {\n const qc = useQueryClient();\n const revoke = authApi$.useMutation('delete', '/sessions/{id}', {\n onSuccess: () => qc.invalidateQueries({ queryKey: ['get', '/sessions'] }),\n });\n return (\n <DataTableAction\n onClick={async () => {\n await revoke.mutateAsync({\n params: { path: { id: sessionId } },\n });\n }}\n disabled={revoke.isPending}\n aria-label=\"Revoke session\"\n />\n );\n}\n\ntype SessionsListProps = {\n data: Session[];\n isLoading?: boolean;\n view: 'table' | 'card';\n pageIndex: number;\n pageSize: number;\n pageCount: number;\n totalRows: number;\n onPageChange: (page: number) => void;\n onPageSizeChange: (size: number) => void;\n};\n\nexport function SessionsList({\n data,\n isLoading,\n view,\n pageIndex,\n pageSize,\n pageCount,\n totalRows,\n onPageChange,\n onPageSizeChange,\n}: SessionsListProps) {\n if (isLoading) {\n return (\n <EntityLoadingState\n view={view}\n rowCount={pageSize}\n columnCount={TABLE_COLUMN_COUNT}\n cardCount={pageSize}\n />\n );\n }\n if (totalRows === 0) {\n return (\n <EntityEmptyState\n icon={IconDeviceDesktop}\n entityName=\"session\"\n title=\"No active sessions\"\n description=\"Active sessions will appear here.\"\n />\n );\n }\n if (view === 'table') {\n return (\n <div className=\"space-y-4\">\n <DisplayTable withTableBorder>\n <Thead>\n <Tr>\n <Th>Created</Th>\n <Th>Expires</Th>\n <Th>Device</Th>\n <Th>IP</Th>\n <Th className=\"w-[50px]\" />\n </Tr>\n </Thead>\n <Tbody>\n {data.map((session) => (\n <Tr key={session.id} className=\"group\">\n <Td>\n {session.createdAt ? (\n <div className=\"flex items-center gap-1 text-muted-foreground\">\n <IconCalendar className=\"h-4 w-4\" />\n <span>\n {new Date(session.createdAt).toLocaleDateString()}{' '}\n {new Date(session.createdAt).toLocaleTimeString()}\n </span>\n </div>\n ) : (\n '—'\n )}\n </Td>\n <Td>{new Date(session.expiresAt).toLocaleDateString()}</Td>\n <Td>\n <span className=\"max-w-xs truncate block\">\n {session.userAgent || 'Unknown'}\n </span>\n </Td>\n <Td>\n <span className=\"font-mono text-sm\">\n {session.ip || 'Unknown'}\n </span>\n </Td>\n <Td>\n <SessionRevokeButton sessionId={session.id} />\n </Td>\n </Tr>\n ))}\n </Tbody>\n </DisplayTable>\n <DataTablePagination\n pageIndex={pageIndex}\n pageSize={pageSize}\n pageCount={pageCount}\n totalRows={totalRows}\n onPageChange={onPageChange}\n onPageSizeChange={onPageSizeChange}\n />\n </div>\n );\n }\n return (\n <div className=\"space-y-4\">\n <div className=\"grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4\">\n {data.map((s) => (\n <SessionCard key={s.id} session={s} />\n ))}\n </div>\n <DataTablePagination\n pageIndex={pageIndex}\n pageSize={pageSize}\n pageCount={pageCount}\n totalRows={totalRows}\n onPageChange={onPageChange}\n onPageSizeChange={onPageSizeChange}\n />\n </div>\n );\n}\n","'use client';\n\nimport { Button, Card, CardContent, CardHeader } from '@mesob/ui/components';\nimport { IconTrash } from '@tabler/icons-react';\nimport { useQueryClient } from '@tanstack/react-query';\nimport { authApi$ } from '../../shared/page-helpers';\nimport type { Session } from './sessions-data';\n\ntype SessionCardProps = { session: Session };\n\nexport function SessionCard({ session }: SessionCardProps) {\n const qc = useQueryClient();\n const revoke = authApi$.useMutation('delete', '/sessions/{id}', {\n onSuccess: () => qc.invalidateQueries({ queryKey: ['get', '/sessions'] }),\n });\n const created = session.createdAt ? new Date(session.createdAt) : null;\n return (\n <Card className=\"hover:shadow-md transition-shadow\">\n <CardHeader className=\"pb-2\">\n <div className=\"flex items-start justify-between\">\n <p className=\"font-semibold\">\n {created\n ? `${created.toLocaleDateString()} ${created.toLocaleTimeString()}`\n : '—'}\n </p>\n <Button\n variant=\"ghost\"\n size=\"sm\"\n className=\"text-destructive hover:text-destructive\"\n onClick={async () => {\n await revoke.mutateAsync({\n params: { path: { id: session.id } },\n });\n }}\n disabled={revoke.isPending}\n >\n <IconTrash className=\"h-4 w-4\" />\n </Button>\n </div>\n </CardHeader>\n <CardContent className=\"space-y-1 text-sm\">\n <p className=\"text-muted-foreground\">\n Expires {new Date(session.expiresAt).toLocaleDateString()}\n </p>\n <p className=\"truncate\">{session.userAgent || 'Unknown device'}</p>\n <p className=\"font-mono\">{session.ip || 'Unknown IP'}</p>\n </CardContent>\n </Card>\n );\n}\n"],"mappings":"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","names":["IconDeviceDesktop","useQueryClient","jsx","jsxs","useQueryClient","jsx","jsxs","IconDeviceDesktop"]}
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  handleError
3
- } from "./chunk-55BMNC4S.js";
3
+ } from "./chunk-RRLFPSSM.js";
4
4
  import {
5
5
  AuthLayout
6
6
  } from "./chunk-DPH2PHK3.js";
@@ -283,4 +283,4 @@ var ResetPasswordForm = ({
283
283
  export {
284
284
  ResetPasswordForm
285
285
  };
286
- //# sourceMappingURL=chunk-A3GIMM2R.js.map
286
+ //# sourceMappingURL=chunk-H7JRQFFI.js.map
@@ -1,6 +1,9 @@
1
1
  import {
2
2
  PermissionSelector
3
3
  } from "./chunk-GVEBIL3O.js";
4
+ import {
5
+ IAMGuard
6
+ } from "./chunk-V6ZHX4LT.js";
4
7
  import {
5
8
  useApi
6
9
  } from "./chunk-AWSAC7RT.js";
@@ -35,6 +38,9 @@ import { toast } from "sonner";
35
38
  import { jsx, jsxs } from "react/jsx-runtime";
36
39
  var TABLE_COLUMN_COUNT = 4;
37
40
  function RolePermissionsPage({ roleId }) {
41
+ return /* @__PURE__ */ jsx(IAMGuard, { children: /* @__PURE__ */ jsx(RolePermissionsPageContent, { roleId }) });
42
+ }
43
+ function RolePermissionsPageContent({ roleId }) {
38
44
  const { hooks } = useApi();
39
45
  const qc = useQueryClient();
40
46
  const { queryConfig, params, setParams } = useEntityParams({
@@ -283,4 +289,4 @@ function RolePermissionsPage({ roleId }) {
283
289
  export {
284
290
  RolePermissionsPage
285
291
  };
286
- //# sourceMappingURL=chunk-IFAVSKHE.js.map
292
+ //# sourceMappingURL=chunk-IQNQGPIT.js.map