@simple-auth-kit/cli 1.3.2 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. package/package.json +1 -1
  2. package/registry/admin-apps/nextjs/shared/.husky/commit-msg +1 -0
  3. package/registry/admin-apps/nextjs/shared/.husky/pre-commit +1 -0
  4. package/registry/admin-apps/nextjs/shared/.lintstagedrc.json +4 -0
  5. package/registry/admin-apps/nextjs/shared/.prettierrc.json +3 -0
  6. package/registry/admin-apps/nextjs/shared/commitlint.config.js +5 -0
  7. package/registry/admin-apps/nextjs/shared/eslint.config.js +28 -0
  8. package/registry/admin-apps/nextjs/variants/base/package.json +11 -2
  9. package/registry/admin-apps/nextjs/variants/base/src/app/(console)/audit-log/page.tsx +72 -16
  10. package/registry/admin-apps/nextjs/variants/base/src/app/(console)/customers/[id]/edit/page.tsx +107 -26
  11. package/registry/admin-apps/nextjs/variants/base/src/app/(console)/customers/[id]/page.tsx +131 -29
  12. package/registry/admin-apps/nextjs/variants/base/src/app/(console)/languages/[id]/edit/page.tsx +111 -25
  13. package/registry/admin-apps/nextjs/variants/base/src/app/(console)/languages/[id]/page.tsx +97 -22
  14. package/registry/admin-apps/nextjs/variants/base/src/app/(console)/permissions/[id]/edit/page.tsx +129 -46
  15. package/registry/admin-apps/nextjs/variants/base/src/app/(console)/permissions/[id]/page.tsx +64 -14
  16. package/registry/admin-apps/nextjs/variants/base/src/app/(console)/roles/[id]/page.tsx +91 -22
  17. package/registry/admin-apps/nextjs/variants/base/src/app/(console)/users/[id]/edit/page.tsx +94 -23
  18. package/registry/admin-apps/nextjs/variants/workspaces/package.json +11 -2
  19. package/registry/admin-apps/nextjs/variants/workspaces/src/app/(console)/account/page.tsx +179 -41
  20. package/registry/admin-apps/react/shared/.husky/commit-msg +1 -0
  21. package/registry/admin-apps/react/shared/.husky/pre-commit +1 -0
  22. package/registry/admin-apps/react/shared/.lintstagedrc.json +4 -0
  23. package/registry/admin-apps/react/shared/.prettierrc.json +3 -0
  24. package/registry/admin-apps/react/shared/commitlint.config.js +3 -0
  25. package/registry/admin-apps/react/shared/eslint.config.js +38 -0
  26. package/registry/admin-apps/react/variants/base/package.json +15 -2
  27. package/registry/admin-apps/react/variants/base/src/pages/CountryDetailPage.tsx +76 -19
  28. package/registry/admin-apps/react/variants/base/src/pages/CustomerDetailPage.tsx +110 -26
  29. package/registry/admin-apps/react/variants/base/src/pages/EditCountryPage.tsx +115 -29
  30. package/registry/admin-apps/react/variants/base/src/pages/EditCustomerPage.tsx +123 -31
  31. package/registry/admin-apps/react/variants/base/src/pages/EditLanguagePage.tsx +100 -24
  32. package/registry/admin-apps/react/variants/base/src/pages/EditRolePage.tsx +99 -26
  33. package/registry/admin-apps/react/variants/base/src/pages/EditUserPage.tsx +126 -31
  34. package/registry/admin-apps/react/variants/base/src/pages/LanguageDetailPage.tsx +75 -19
  35. package/registry/admin-apps/react/variants/base/src/pages/PermissionDetailPage.tsx +58 -13
  36. package/registry/admin-apps/react/variants/base/src/pages/PermissionsPage.tsx +132 -29
  37. package/registry/admin-apps/react/variants/base/src/pages/RoleDetailPage.tsx +70 -17
  38. package/registry/admin-apps/react/variants/base/src/pages/RolesPage.tsx +277 -59
  39. package/registry/admin-apps/react/variants/workspaces/package.json +15 -2
  40. package/registry/combos/express-drizzle/eslint.config.js +18 -0
  41. package/registry/combos/express-drizzle/package.json +5 -1
  42. package/registry/combos/express-drizzle/shared/src/request-context.ts +3 -0
  43. package/registry/combos/express-prisma/eslint.config.js +18 -0
  44. package/registry/combos/express-prisma/package.json +5 -1
  45. package/registry/combos/express-prisma/shared/src/request-context.ts +3 -0
  46. package/registry/combos/nestjs-drizzle/eslint.config.js +18 -0
  47. package/registry/combos/nestjs-drizzle/package.json +5 -1
  48. package/registry/combos/nestjs-drizzle/shared/src/auth.controller.ts +138 -33
  49. package/registry/combos/nestjs-drizzle/shared/src/route-tiers.ts +71 -18
  50. package/registry/combos/nestjs-prisma/eslint.config.js +18 -0
  51. package/registry/combos/nestjs-prisma/package.json +5 -1
  52. package/registry/combos/nestjs-prisma/shared/src/auth.controller.ts +148 -35
  53. package/registry/combos/nestjs-prisma/shared/src/route-tiers.ts +72 -18
  54. package/registry/mobile-apps/bare-rn/shared/.husky/commit-msg +1 -0
  55. package/registry/mobile-apps/bare-rn/shared/.husky/pre-commit +1 -0
  56. package/registry/mobile-apps/bare-rn/shared/.lintstagedrc.json +4 -0
  57. package/registry/mobile-apps/bare-rn/shared/.prettierrc.json +5 -0
  58. package/registry/mobile-apps/bare-rn/shared/commitlint.config.js +3 -0
  59. package/registry/mobile-apps/bare-rn/shared/eslint.config.js +19 -0
  60. package/registry/mobile-apps/bare-rn/variants/base/package.json +17 -12
  61. package/registry/mobile-apps/bare-rn/variants/workspaces/package.json +17 -12
  62. package/registry/mobile-apps/expo/shared/.husky/commit-msg +1 -0
  63. package/registry/mobile-apps/expo/shared/.husky/pre-commit +1 -0
  64. package/registry/mobile-apps/expo/shared/.lintstagedrc.json +4 -0
  65. package/registry/mobile-apps/expo/shared/.prettierrc.json +5 -0
  66. package/registry/mobile-apps/expo/shared/commitlint.config.js +3 -0
  67. package/registry/mobile-apps/expo/shared/eslint.config.js +9 -0
  68. package/registry/mobile-apps/expo/shared/src/screens/SessionsScreen.tsx +6 -17
  69. package/registry/mobile-apps/expo/shared/src/store/sessionsStore.ts +35 -0
  70. package/registry/mobile-apps/expo/variants/base/package.json +12 -3
  71. package/registry/mobile-apps/expo/variants/workspaces/package.json +12 -3
  72. package/registry/mobile-apps/expo/variants/workspaces/src/screens/WorkspaceSelectScreen.tsx +1 -1
  73. package/registry.json +13 -36
  74. package/registry/mobile-apps/bare-rn/shared/.eslintrc.js +0 -4
  75. package/registry/mobile-apps/bare-rn/shared/.prettierrc.js +0 -5
@@ -1,6 +1,15 @@
1
- import { type FormEvent, useCallback, useEffect, useState } from "react";
1
+ import {
2
+ type FormEvent,
3
+ startTransition,
4
+ useCallback,
5
+ useEffect,
6
+ useState,
7
+ } from "react";
2
8
  import { Link } from "react-router-dom";
3
- import type { DefinePermissionInput, PermissionSummary } from "@simple-auth-kit/auth-client";
9
+ import type {
10
+ DefinePermissionInput,
11
+ PermissionSummary,
12
+ } from "@simple-auth-kit/auth-client";
4
13
  import { AuthApiError } from "@simple-auth-kit/auth-client";
5
14
  import { EyeIcon, PowerIcon } from "lucide-react";
6
15
  import { toast } from "sonner";
@@ -14,10 +23,18 @@ import { Label } from "@/components/ui/label";
14
23
  import { Button, buttonVariants } from "@/components/ui/button";
15
24
  import { Badge } from "@/components/ui/badge";
16
25
  import { Modal } from "@/components/ui/modal";
17
- import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
26
+ import {
27
+ Table,
28
+ TableBody,
29
+ TableCell,
30
+ TableHead,
31
+ TableHeader,
32
+ TableRow,
33
+ } from "@/components/ui/table";
18
34
 
19
35
  /** One dialog serves both create (slug editable) and edit (slug fixed) — `definePermission` upserts on slug. */
20
- export type DialogState = { mode: "create" } | { mode: "edit"; permission: PermissionSummary };
36
+ export type DialogState =
37
+ { mode: "create" } | { mode: "edit"; permission: PermissionSummary };
21
38
 
22
39
  interface PermissionGroup {
23
40
  name: string;
@@ -51,7 +68,8 @@ export function PermissionsPage() {
51
68
  const [error, setError] = useState<string | null>(null);
52
69
  const [dialogState, setDialogState] = useState<DialogState | null>(null);
53
70
  const [confirmOpen, setConfirmOpen] = useState(false);
54
- const [pendingPermission, setPendingPermission] = useState<PermissionSummary | null>(null);
71
+ const [pendingPermission, setPendingPermission] =
72
+ useState<PermissionSummary | null>(null);
55
73
  const [pendingBusy, setPendingBusy] = useState(false);
56
74
 
57
75
  const load = useCallback(async () => {
@@ -60,14 +78,18 @@ export function PermissionsPage() {
60
78
  try {
61
79
  setPermissions(await authClient.listPermissions());
62
80
  } catch (err) {
63
- setError(err instanceof AuthApiError ? err.message : "Couldn't load permissions. Check that the backend is running, then try again.");
81
+ setError(
82
+ err instanceof AuthApiError
83
+ ? err.message
84
+ : "Couldn't load permissions. Check that the backend is running, then try again.",
85
+ );
64
86
  } finally {
65
87
  setLoading(false);
66
88
  }
67
89
  }, []);
68
90
 
69
91
  useEffect(() => {
70
- void load();
92
+ startTransition(() => void load());
71
93
  }, [load]);
72
94
 
73
95
  function openStatusConfirm(permission: PermissionSummary) {
@@ -79,13 +101,26 @@ export function PermissionsPage() {
79
101
  if (!pendingPermission) return;
80
102
  setPendingBusy(true);
81
103
  try {
82
- const updated = await authClient.definePermission({ slug: pendingPermission.slug, isActive: !pendingPermission.isActive });
83
- setPermissions((prev) => prev.map((p) => (p.id === updated.id ? updated : p)));
84
- toast.success(pendingPermission.isActive ? "Permission deactivated." : "Permission activated.");
104
+ const updated = await authClient.definePermission({
105
+ slug: pendingPermission.slug,
106
+ isActive: !pendingPermission.isActive,
107
+ });
108
+ setPermissions((prev) =>
109
+ prev.map((p) => (p.id === updated.id ? updated : p)),
110
+ );
111
+ toast.success(
112
+ pendingPermission.isActive
113
+ ? "Permission deactivated."
114
+ : "Permission activated.",
115
+ );
85
116
  setConfirmOpen(false);
86
117
  setPendingPermission(null);
87
118
  } catch (err) {
88
- toast.error(err instanceof AuthApiError ? err.message : "Couldn't change this permission's status. Try again.");
119
+ toast.error(
120
+ err instanceof AuthApiError
121
+ ? err.message
122
+ : "Couldn't change this permission's status. Try again.",
123
+ );
89
124
  } finally {
90
125
  setPendingBusy(false);
91
126
  }
@@ -98,9 +133,16 @@ export function PermissionsPage() {
98
133
  <div className="flex items-start justify-between gap-4">
99
134
  <div>
100
135
  <h1 className="text-2xl font-semibold">Permissions</h1>
101
- <p className="text-sm text-muted-foreground">The full capability catalog, grouped the way the console reads it elsewhere.</p>
136
+ <p className="text-sm text-muted-foreground">
137
+ The full capability catalog, grouped the way the console reads it
138
+ elsewhere.
139
+ </p>
102
140
  </div>
103
- {canDefine ? <Button onClick={() => setDialogState({ mode: "create" })}>New permission</Button> : null}
141
+ {canDefine ? (
142
+ <Button onClick={() => setDialogState({ mode: "create" })}>
143
+ New permission
144
+ </Button>
145
+ ) : null}
104
146
  </div>
105
147
 
106
148
  <AlertModal
@@ -117,16 +159,22 @@ export function PermissionsPage() {
117
159
 
118
160
  <Card>
119
161
  <CardContent className="pt-6">
120
- {error ? <p className="mb-4 text-sm text-destructive">{error}</p> : null}
162
+ {error ? (
163
+ <p className="mb-4 text-sm text-destructive">{error}</p>
164
+ ) : null}
121
165
  {loading ? (
122
166
  <p className="text-sm text-muted-foreground">Loading…</p>
123
167
  ) : permissions.length === 0 ? (
124
- <p className="text-sm text-muted-foreground">No permissions defined yet.</p>
168
+ <p className="text-sm text-muted-foreground">
169
+ No permissions defined yet.
170
+ </p>
125
171
  ) : (
126
172
  <div className="flex flex-col gap-6">
127
173
  {groups.map((group) => (
128
174
  <div key={group.name}>
129
- <h2 className="mb-2 text-sm font-semibold text-muted-foreground">{group.name}</h2>
175
+ <h2 className="mb-2 text-sm font-semibold text-muted-foreground">
176
+ {group.name}
177
+ </h2>
130
178
  <Table>
131
179
  <TableHeader>
132
180
  <TableRow>
@@ -140,19 +188,37 @@ export function PermissionsPage() {
140
188
  <TableBody>
141
189
  {group.permissions.map((permission) => (
142
190
  <TableRow key={permission.id}>
143
- <TableCell className="font-mono text-xs">{permission.slug}</TableCell>
144
- <TableCell className="font-medium">{permission.displayName}</TableCell>
145
- <TableCell className="max-w-xs truncate text-muted-foreground" title={permission.description ?? undefined}>
191
+ <TableCell className="font-mono text-xs">
192
+ {permission.slug}
193
+ </TableCell>
194
+ <TableCell className="font-medium">
195
+ {permission.displayName}
196
+ </TableCell>
197
+ <TableCell
198
+ className="max-w-xs truncate text-muted-foreground"
199
+ title={permission.description ?? undefined}
200
+ >
146
201
  {permission.description ?? "—"}
147
202
  </TableCell>
148
203
  <TableCell>
149
- <Badge variant={permission.isActive ? "success" : "destructive"}>{permission.isActive ? "Active" : "Inactive"}</Badge>
204
+ <Badge
205
+ variant={
206
+ permission.isActive ? "success" : "destructive"
207
+ }
208
+ >
209
+ {permission.isActive ? "Active" : "Inactive"}
210
+ </Badge>
150
211
  </TableCell>
151
212
  <TableCell className="text-right">
152
213
  <div className="flex justify-end gap-2">
153
214
  <Link
154
215
  to={`/permissions/${permission.id}`}
155
- className={cn(buttonVariants({ variant: "outline", size: "icon" }))}
216
+ className={cn(
217
+ buttonVariants({
218
+ variant: "outline",
219
+ size: "icon",
220
+ }),
221
+ )}
156
222
  title="View details"
157
223
  >
158
224
  <EyeIcon />
@@ -162,12 +228,27 @@ export function PermissionsPage() {
162
228
  <Button
163
229
  size="icon"
164
230
  variant="outline"
165
- title={permission.isActive ? "Deactivate this permission" : "Activate this permission"}
166
- onClick={() => openStatusConfirm(permission)}
231
+ title={
232
+ permission.isActive
233
+ ? "Deactivate this permission"
234
+ : "Activate this permission"
235
+ }
236
+ onClick={() =>
237
+ openStatusConfirm(permission)
238
+ }
167
239
  >
168
240
  <PowerIcon />
169
241
  </Button>
170
- <Button size="sm" variant="outline" onClick={() => setDialogState({ mode: "edit", permission })}>
242
+ <Button
243
+ size="sm"
244
+ variant="outline"
245
+ onClick={() =>
246
+ setDialogState({
247
+ mode: "edit",
248
+ permission,
249
+ })
250
+ }
251
+ >
171
252
  Edit
172
253
  </Button>
173
254
  </>
@@ -221,7 +302,11 @@ export function DefinePermissionDialog({
221
302
  const [submitting, setSubmitting] = useState(false);
222
303
  const [loadedFor, setLoadedFor] = useState<string | null>(null);
223
304
 
224
- const key = state ? (state.mode === "edit" ? state.permission.id : "__create__") : null;
305
+ const key = state
306
+ ? state.mode === "edit"
307
+ ? state.permission.id
308
+ : "__create__"
309
+ : null;
225
310
 
226
311
  // Seed from the permission being edited (or blank, for create), once per open.
227
312
  if (state && loadedFor !== key) {
@@ -258,7 +343,11 @@ export function DefinePermissionDialog({
258
343
  const permission = await authClient.definePermission(input);
259
344
  onSaved(permission);
260
345
  } catch (err) {
261
- setError(err instanceof AuthApiError ? err.message : "Couldn't save the permission. Try again.");
346
+ setError(
347
+ err instanceof AuthApiError
348
+ ? err.message
349
+ : "Couldn't save the permission. Try again.",
350
+ );
262
351
  } finally {
263
352
  setSubmitting(false);
264
353
  }
@@ -294,15 +383,29 @@ export function DefinePermissionDialog({
294
383
  </div>
295
384
  <div className="flex flex-col gap-1.5">
296
385
  <Label htmlFor="permission-displayName">Display name</Label>
297
- <Input id="permission-displayName" value={displayName} onChange={(e) => setDisplayName(e.target.value)} placeholder="Defaults to the slug" />
386
+ <Input
387
+ id="permission-displayName"
388
+ value={displayName}
389
+ onChange={(e) => setDisplayName(e.target.value)}
390
+ placeholder="Defaults to the slug"
391
+ />
298
392
  </div>
299
393
  <div className="flex flex-col gap-1.5">
300
394
  <Label htmlFor="permission-description">Description</Label>
301
- <Input id="permission-description" value={description} onChange={(e) => setDescription(e.target.value)} />
395
+ <Input
396
+ id="permission-description"
397
+ value={description}
398
+ onChange={(e) => setDescription(e.target.value)}
399
+ />
302
400
  </div>
303
401
  <div className="flex flex-col gap-1.5">
304
402
  <Label htmlFor="permission-group">Group</Label>
305
- <Input id="permission-group" value={group} onChange={(e) => setGroup(e.target.value)} placeholder='Defaults to "Custom"' />
403
+ <Input
404
+ id="permission-group"
405
+ value={group}
406
+ onChange={(e) => setGroup(e.target.value)}
407
+ placeholder='Defaults to "Custom"'
408
+ />
306
409
  </div>
307
410
  {state?.mode === "edit" ? (
308
411
  <div className="flex items-center gap-2">
@@ -1,6 +1,9 @@
1
- import { useCallback, useEffect, useState } from "react";
1
+ import { startTransition, useCallback, useEffect, useState } from "react";
2
2
  import { useNavigate, useParams } from "react-router-dom";
3
- import type { PermissionSummary, RoleSummary } from "@simple-auth-kit/auth-client";
3
+ import type {
4
+ PermissionSummary,
5
+ RoleSummary,
6
+ } from "@simple-auth-kit/auth-client";
4
7
  import { AuthApiError } from "@simple-auth-kit/auth-client";
5
8
  import { PencilIcon } from "lucide-react";
6
9
  import { toast } from "sonner";
@@ -11,7 +14,13 @@ import { EditRoleDialog, DeleteRoleDialog } from "@/pages/RolesPage";
11
14
  import { Breadcrumb } from "@/components/breadcrumb";
12
15
  import { Badge } from "@/components/ui/badge";
13
16
  import { Button } from "@/components/ui/button";
14
- import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
17
+ import {
18
+ Card,
19
+ CardContent,
20
+ CardDescription,
21
+ CardHeader,
22
+ CardTitle,
23
+ } from "@/components/ui/card";
15
24
 
16
25
  function apiErrorMessage(err: unknown, fallback: string): string {
17
26
  return err instanceof AuthApiError ? err.message : fallback;
@@ -22,10 +31,15 @@ export function RoleDetailPage() {
22
31
  const navigate = useNavigate();
23
32
  const ability = useAbility();
24
33
  const canManageRoles = ability.can(PERMISSIONS.rolesManage, "permission");
25
- const canReadPermissions = ability.can(PERMISSIONS.permissionsRead, "permission");
34
+ const canReadPermissions = ability.can(
35
+ PERMISSIONS.permissionsRead,
36
+ "permission",
37
+ );
26
38
 
27
39
  const [role, setRole] = useState<RoleSummary | null>(null);
28
- const [permissionCatalog, setPermissionCatalog] = useState<PermissionSummary[]>([]);
40
+ const [permissionCatalog, setPermissionCatalog] = useState<
41
+ PermissionSummary[]
42
+ >([]);
29
43
  const [loading, setLoading] = useState(true);
30
44
  const [statusPending, setStatusPending] = useState(false);
31
45
  const [editingRole, setEditingRole] = useState<RoleSummary | null>(null);
@@ -45,7 +59,7 @@ export function RoleDetailPage() {
45
59
  }, [id]);
46
60
 
47
61
  useEffect(() => {
48
- void load();
62
+ startTransition(() => void load());
49
63
  }, [load]);
50
64
 
51
65
  useEffect(() => {
@@ -53,7 +67,11 @@ export function RoleDetailPage() {
53
67
  authClient
54
68
  .listPermissions({ activeOnly: true })
55
69
  .then(setPermissionCatalog)
56
- .catch((err) => toast.error(apiErrorMessage(err, "Couldn't load the permission catalog.")));
70
+ .catch((err) =>
71
+ toast.error(
72
+ apiErrorMessage(err, "Couldn't load the permission catalog."),
73
+ ),
74
+ );
57
75
  }, [canReadPermissions]);
58
76
 
59
77
  async function toggleActive() {
@@ -64,7 +82,9 @@ export function RoleDetailPage() {
64
82
  setRole((prev) => (prev ? { ...prev, isActive: !prev.isActive } : prev));
65
83
  toast.success(role.isActive ? "Role deactivated." : "Role activated.");
66
84
  } catch (err) {
67
- toast.error(apiErrorMessage(err, "Couldn't change this role's status. Try again."));
85
+ toast.error(
86
+ apiErrorMessage(err, "Couldn't change this role's status. Try again."),
87
+ );
68
88
  } finally {
69
89
  setStatusPending(false);
70
90
  }
@@ -74,9 +94,19 @@ export function RoleDetailPage() {
74
94
 
75
95
  return (
76
96
  <div className="flex flex-col gap-4">
77
- <Breadcrumb items={[{ title: "Roles", href: "/roles" }, { title: role?.displayName ?? role?.name ?? "Details", href: `/roles/${id}` }]} />
97
+ <Breadcrumb
98
+ items={[
99
+ { title: "Roles", href: "/roles" },
100
+ {
101
+ title: role?.displayName ?? role?.name ?? "Details",
102
+ href: `/roles/${id}`,
103
+ },
104
+ ]}
105
+ />
78
106
 
79
- {loading && !role && <p className="text-sm text-muted-foreground">Loading…</p>}
107
+ {loading && !role && (
108
+ <p className="text-sm text-muted-foreground">Loading…</p>
109
+ )}
80
110
 
81
111
  {role && (
82
112
  <>
@@ -92,12 +122,18 @@ export function RoleDetailPage() {
92
122
  </CardDescription>
93
123
  </div>
94
124
  <div className="flex items-center gap-1.5">
95
- <Badge variant={role.isActive ? "success" : "destructive"}>{role.isActive ? "Active" : "Inactive"}</Badge>
125
+ <Badge variant={role.isActive ? "success" : "destructive"}>
126
+ {role.isActive ? "Active" : "Inactive"}
127
+ </Badge>
96
128
  <Button
97
129
  variant="outline"
98
130
  size="sm"
99
131
  disabled={!canManageRoles}
100
- title={canManageRoles ? undefined : `You need the "${PERMISSIONS.rolesManage}" permission to do this.`}
132
+ title={
133
+ canManageRoles
134
+ ? undefined
135
+ : `You need the "${PERMISSIONS.rolesManage}" permission to do this.`
136
+ }
101
137
  onClick={() => setEditingRole(role)}
102
138
  >
103
139
  <PencilIcon />
@@ -107,9 +143,14 @@ export function RoleDetailPage() {
107
143
  </CardHeader>
108
144
  <CardContent className="flex flex-col gap-4">
109
145
  <div className="flex flex-col gap-1.5">
110
- <span className="text-xs text-muted-foreground">Permissions ({role.permissions.length} attached)</span>
146
+ <span className="text-xs text-muted-foreground">
147
+ Permissions ({role.permissions.length} attached)
148
+ </span>
111
149
  {canReadPermissions ? (
112
- <PermissionGroupView permissions={permissionCatalog} selected={role.permissions} />
150
+ <PermissionGroupView
151
+ permissions={permissionCatalog}
152
+ selected={role.permissions}
153
+ />
113
154
  ) : (
114
155
  <div className="flex flex-wrap gap-1">
115
156
  {role.permissions.map((slug) => (
@@ -131,7 +172,11 @@ export function RoleDetailPage() {
131
172
  <Button
132
173
  variant={role.isActive ? "destructive" : "outline"}
133
174
  disabled={!canManageRoles || statusPending}
134
- title={canManageRoles ? undefined : `You need the "${PERMISSIONS.rolesManage}" permission to do this.`}
175
+ title={
176
+ canManageRoles
177
+ ? undefined
178
+ : `You need the "${PERMISSIONS.rolesManage}" permission to do this.`
179
+ }
135
180
  onClick={() => void toggleActive()}
136
181
  >
137
182
  {role.isActive ? "Deactivate" : "Activate"}
@@ -140,7 +185,11 @@ export function RoleDetailPage() {
140
185
  <Button
141
186
  variant="destructive"
142
187
  disabled={!canManageRoles}
143
- title={canManageRoles ? undefined : `You need the "${PERMISSIONS.rolesManage}" permission to do this.`}
188
+ title={
189
+ canManageRoles
190
+ ? undefined
191
+ : `You need the "${PERMISSIONS.rolesManage}" permission to do this.`
192
+ }
144
193
  onClick={() => setDeletingRole(role)}
145
194
  >
146
195
  Delete role
@@ -161,7 +210,11 @@ export function RoleDetailPage() {
161
210
  }}
162
211
  />
163
212
 
164
- <DeleteRoleDialog role={deletingRole} onClose={() => setDeletingRole(null)} onDeleted={() => navigate("/roles")} />
213
+ <DeleteRoleDialog
214
+ role={deletingRole}
215
+ onClose={() => setDeletingRole(null)}
216
+ onDeleted={() => navigate("/roles")}
217
+ />
165
218
  </div>
166
219
  );
167
220
  }