@alepha/ui 0.14.2 → 0.14.3

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 (51) hide show
  1. package/dist/admin/AdminAudits-DIrCCPk3.js.map +1 -1
  2. package/dist/admin/AdminNotifications-cIbywWKi.js.map +1 -1
  3. package/dist/admin/AdminParameters-D-q3Qmhv.js.map +1 -1
  4. package/dist/admin/AdminSessions-vOgkrQ2U.js.map +1 -1
  5. package/dist/admin/AdminUserAudits-CSsN1fIC.js.map +1 -1
  6. package/dist/admin/AdminUserCreate-B72nu-3W.js.map +1 -1
  7. package/dist/admin/AdminUserDetails-CKM2IEMr.js +475 -0
  8. package/dist/admin/AdminUserDetails-CKM2IEMr.js.map +1 -0
  9. package/dist/admin/{AdminUserDetails-z1y8kJeB.js → AdminUserDetails-Zib_B6Al.js} +1 -1
  10. package/dist/admin/{AdminUserLayout-DyQYacQQ.js → AdminUserLayout-BNBOEiAO.js} +1 -1
  11. package/dist/admin/AdminUserLayout-D7En9UBq.js +334 -0
  12. package/dist/admin/AdminUserLayout-D7En9UBq.js.map +1 -0
  13. package/dist/admin/AdminUserSessions-DEaGu6n6.js.map +1 -1
  14. package/dist/admin/{AdminUserSettings-CR7MxX_R.js → AdminUserSettings-Di73D7g2.js} +6 -5
  15. package/dist/admin/AdminUserSettings-Di73D7g2.js.map +1 -0
  16. package/dist/admin/AdminUserSettings-yI-JECf5.js +3 -0
  17. package/dist/admin/AdminUsers-BnGIRvmV.js.map +1 -1
  18. package/dist/admin/index.d.ts +10 -10
  19. package/dist/admin/index.d.ts.map +1 -1
  20. package/dist/admin/index.js +17 -17
  21. package/dist/admin/index.js.map +1 -1
  22. package/dist/core/index.d.ts.map +1 -1
  23. package/dist/core/index.js +6 -5
  24. package/dist/core/index.js.map +1 -1
  25. package/package.json +11 -11
  26. package/src/admin/AdminRouter.ts +22 -19
  27. package/src/admin/MainRouter.ts +1 -1
  28. package/src/admin/components/audits/AdminAudits.tsx +2 -2
  29. package/src/admin/components/jobs/AdminJobs.tsx +2 -2
  30. package/src/admin/components/notifications/AdminNotifications.tsx +2 -2
  31. package/src/admin/components/parameters/AdminParameters.tsx +2 -2
  32. package/src/admin/components/sessions/AdminSessions.tsx +2 -2
  33. package/src/admin/components/shared/AdminResourceHeader.tsx +281 -0
  34. package/src/admin/components/shared/AdminResourceTabs.tsx +94 -0
  35. package/src/admin/components/shared/index.ts +10 -0
  36. package/src/admin/components/users/AdminUserAudits.tsx +2 -2
  37. package/src/admin/components/users/AdminUserCreate.tsx +2 -2
  38. package/src/admin/components/users/AdminUserDetails.tsx +337 -85
  39. package/src/admin/components/users/AdminUserLayout.tsx +164 -108
  40. package/src/admin/components/users/AdminUserSessions.tsx +2 -2
  41. package/src/admin/components/users/AdminUserSettings.tsx +10 -5
  42. package/src/admin/components/users/AdminUsers.tsx +6 -2
  43. package/src/core/components/form/TypeForm.tsx +3 -2
  44. package/src/core/components/layout/AlephaMantineProvider.tsx +5 -1
  45. package/src/core/components/layout/Sidebar.tsx +9 -6
  46. package/dist/admin/AdminUserDetails-BCt8Su-4.js +0 -222
  47. package/dist/admin/AdminUserDetails-BCt8Su-4.js.map +0 -1
  48. package/dist/admin/AdminUserLayout-Ck0GLRE5.js +0 -151
  49. package/dist/admin/AdminUserLayout-Ck0GLRE5.js.map +0 -1
  50. package/dist/admin/AdminUserSettings-CE66UTIP.js +0 -3
  51. package/dist/admin/AdminUserSettings-CR7MxX_R.js.map +0 -1
package/package.json CHANGED
@@ -7,7 +7,7 @@
7
7
  "mantine"
8
8
  ],
9
9
  "author": "Nicolas Foures",
10
- "version": "0.14.2",
10
+ "version": "0.14.3",
11
11
  "type": "module",
12
12
  "engines": {
13
13
  "node": ">=22.0.0"
@@ -22,16 +22,16 @@
22
22
  "styles.css"
23
23
  ],
24
24
  "dependencies": {
25
- "@alepha/react": "0.14.2",
26
- "@mantine/core": "^8.3.10",
27
- "@mantine/dates": "^8.3.10",
28
- "@mantine/hooks": "^8.3.10",
29
- "@mantine/modals": "^8.3.10",
30
- "@mantine/notifications": "^8.3.10",
31
- "@mantine/nprogress": "^8.3.10",
32
- "@mantine/spotlight": "^8.3.10",
25
+ "@alepha/react": "0.14.3",
26
+ "@mantine/core": "^8.3.11",
27
+ "@mantine/dates": "^8.3.11",
28
+ "@mantine/hooks": "^8.3.11",
29
+ "@mantine/modals": "^8.3.11",
30
+ "@mantine/notifications": "^8.3.11",
31
+ "@mantine/nprogress": "^8.3.11",
32
+ "@mantine/spotlight": "^8.3.11",
33
33
  "@tabler/icons-react": "^3.36.1",
34
- "alepha": "0.14.2",
34
+ "alepha": "0.14.3",
35
35
  "dayjs": "^1.11.19"
36
36
  },
37
37
  "devDependencies": {
@@ -39,7 +39,7 @@
39
39
  "react": "^19.2.3",
40
40
  "react-dom": "^19.2.3",
41
41
  "typescript": "^5.9.3",
42
- "vite": "^7.3.0",
42
+ "vite": "^7.3.1",
43
43
  "vitest": "^4.0.16"
44
44
  },
45
45
  "peerDependencies": {
@@ -13,23 +13,26 @@ import {
13
13
  IconUsers,
14
14
  } from "@tabler/icons-react";
15
15
  import { $inject } from "alepha";
16
- import type { AuditController } from "alepha/api/audits";
16
+ import type { AdminAuditController } from "alepha/api/audits";
17
17
  import type { FileController } from "alepha/api/files";
18
- import type { NotificationController } from "alepha/api/notifications";
19
- import type { ConfigController } from "alepha/api/parameters";
20
- import type { SessionController, UserController } from "alepha/api/users";
18
+ import type { AdminNotificationController } from "alepha/api/notifications";
19
+ import type { AdminConfigController } from "alepha/api/parameters";
20
+ import type {
21
+ AdminSessionController,
22
+ AdminUserController,
23
+ } from "alepha/api/users";
21
24
  import { $client } from "alepha/server/links";
22
25
 
23
26
  export class AdminRouter {
24
27
  protected readonly router = $inject(ReactRouter);
25
28
  protected readonly authRouter = $inject(AuthRouter);
26
29
  protected readonly auth = $inject(ReactAuth);
27
- protected readonly userCtrl = $client<UserController>();
28
- protected readonly sessionCtrl = $client<SessionController>();
29
- protected readonly notificationCtrl = $client<NotificationController>();
30
+ protected readonly userCtrl = $client<AdminUserController>();
31
+ protected readonly sessionCtrl = $client<AdminSessionController>();
32
+ protected readonly notificationCtrl = $client<AdminNotificationController>();
30
33
  protected readonly fileCtrl = $client<FileController>();
31
- protected readonly configCtrl = $client<ConfigController>();
32
- protected readonly auditCtrl = $client<AuditController>();
34
+ protected readonly configCtrl = $client<AdminConfigController>();
35
+ protected readonly auditCtrl = $client<AdminAuditController>();
33
36
 
34
37
  protected adminShellProps(): AdminShellProps {
35
38
  return {};
@@ -49,10 +52,10 @@ export class AdminRouter {
49
52
  // Layout
50
53
  // ─────────────────────────────────────────────────────────────────────────────
51
54
 
52
- public readonly layout = $page({
53
- name: "AdminLayout",
55
+ public readonly adminLayout = $page({
54
56
  path: "/admin",
55
57
  label: "Admin",
58
+ can: () => this.userCtrl.findUsers.can(),
56
59
  lazy: () => import("./components/AdminLayout.tsx"),
57
60
  props: () => ({
58
61
  adminShellProps: this.adminShellProps(),
@@ -71,7 +74,7 @@ export class AdminRouter {
71
74
 
72
75
  public readonly adminUsers = $page({
73
76
  icon: IconUsers,
74
- parent: this.layout,
77
+ parent: this.adminLayout,
75
78
  path: "/users",
76
79
  label: "Users",
77
80
  description: "Manage application users and their roles.",
@@ -81,7 +84,7 @@ export class AdminRouter {
81
84
 
82
85
  public readonly adminUserCreate = $page({
83
86
  icon: IconPlus,
84
- parent: this.layout,
87
+ parent: this.adminLayout,
85
88
  path: "/users/create",
86
89
  label: "Create User",
87
90
  description: "Create a new user account.",
@@ -91,7 +94,7 @@ export class AdminRouter {
91
94
 
92
95
  public readonly adminUserLayout = $page({
93
96
  icon: IconUser,
94
- parent: this.layout,
97
+ parent: this.adminLayout,
95
98
  path: "/users/:userId",
96
99
  label: "User",
97
100
  lazy: () => import("./components/users/AdminUserLayout.tsx"),
@@ -133,7 +136,7 @@ export class AdminRouter {
133
136
 
134
137
  public readonly adminAudits = $page({
135
138
  icon: IconHistory,
136
- parent: this.layout,
139
+ parent: this.adminLayout,
137
140
  path: "/audits",
138
141
  label: "Audit Log",
139
142
  description: "View system-wide audit trail and activity logs.",
@@ -147,7 +150,7 @@ export class AdminRouter {
147
150
 
148
151
  public readonly adminSessions = $page({
149
152
  icon: IconDevices,
150
- parent: this.layout,
153
+ parent: this.adminLayout,
151
154
  path: "/sessions",
152
155
  label: "Sessions",
153
156
  description: "View and manage all active sessions.",
@@ -161,7 +164,7 @@ export class AdminRouter {
161
164
 
162
165
  public readonly adminNotifications = $page({
163
166
  icon: IconBell,
164
- parent: this.layout,
167
+ parent: this.adminLayout,
165
168
  path: "/notifications",
166
169
  label: "Notifications",
167
170
  description: "View notification history and status.",
@@ -175,7 +178,7 @@ export class AdminRouter {
175
178
 
176
179
  public readonly adminFiles = $page({
177
180
  icon: IconFile,
178
- parent: this.layout,
181
+ parent: this.adminLayout,
179
182
  path: "/files",
180
183
  label: "Files",
181
184
  description: "Manage uploaded files and storage.",
@@ -189,7 +192,7 @@ export class AdminRouter {
189
192
 
190
193
  public readonly adminParameters = $page({
191
194
  icon: IconSettings,
192
- parent: this.layout,
195
+ parent: this.adminLayout,
193
196
  path: "/parameters",
194
197
  label: "Parameters",
195
198
  description: "View and manage application configuration parameters.",
@@ -18,6 +18,6 @@ export class MainRouter {
18
18
 
19
19
  layout = $page({
20
20
  component: AlephaMantineProvider,
21
- children: () => [this.auth.layout, this.admin.layout],
21
+ children: () => [this.auth.layout, this.admin.adminLayout],
22
22
  });
23
23
  }
@@ -11,7 +11,7 @@ import {
11
11
  IconX,
12
12
  } from "@tabler/icons-react";
13
13
  import { type Page, t } from "alepha";
14
- import type { AuditController, AuditEntity } from "alepha/api/audits";
14
+ import type { AdminAuditController, AuditEntity } from "alepha/api/audits";
15
15
  import type { AdminRouter } from "../../AdminRouter.ts";
16
16
 
17
17
  export interface AdminAuditsProps {
@@ -62,7 +62,7 @@ const getTypeColor = (type: string) => {
62
62
  };
63
63
 
64
64
  const AdminAudits = (props: AdminAuditsProps) => {
65
- const client = useClient<AuditController>();
65
+ const client = useClient<AdminAuditController>();
66
66
  const router = useRouter<AdminRouter>();
67
67
  const { l } = useI18n();
68
68
 
@@ -10,13 +10,13 @@ import {
10
10
  } from "@tabler/icons-react";
11
11
  import { type Page, t } from "alepha";
12
12
  import {
13
- type JobController,
13
+ type AdminJobController,
14
14
  type JobExecutionEntity,
15
15
  jobExecutions,
16
16
  } from "alepha/api/jobs";
17
17
 
18
18
  const AdminJobs = () => {
19
- const client = useClient<JobController>();
19
+ const client = useClient<AdminJobController>();
20
20
  const { l } = useI18n();
21
21
 
22
22
  const filters = t.object({
@@ -11,12 +11,12 @@ import {
11
11
  } from "@tabler/icons-react";
12
12
  import { type Page, t } from "alepha";
13
13
  import type {
14
- NotificationController,
14
+ AdminNotificationController,
15
15
  NotificationEntity,
16
16
  } from "alepha/api/notifications";
17
17
 
18
18
  const AdminNotifications = () => {
19
- const client = useClient<NotificationController>();
19
+ const client = useClient<AdminNotificationController>();
20
20
  const { l } = useI18n();
21
21
 
22
22
  const filters = t.object({
@@ -3,7 +3,7 @@ import { Flex, Text } from "@alepha/ui";
3
3
  import { Loader, Stack } from "@mantine/core";
4
4
  import { IconSettings } from "@tabler/icons-react";
5
5
  import type {
6
- ConfigController,
6
+ AdminConfigController,
7
7
  ConfigTreeNode,
8
8
  Parameter,
9
9
  } from "alepha/api/parameters";
@@ -14,7 +14,7 @@ import ParameterTree from "./ParameterTree.tsx";
14
14
  import type { ConfigValue } from "./types.ts";
15
15
 
16
16
  const AdminParameters = () => {
17
- const client = useClient<ConfigController>();
17
+ const client = useClient<AdminConfigController>();
18
18
 
19
19
  // State
20
20
  const [treeData, setTreeData] = useState<ConfigTreeNode[]>([]);
@@ -11,7 +11,7 @@ import {
11
11
  } from "@tabler/icons-react";
12
12
  import { type Page, t } from "alepha";
13
13
  import {
14
- type SessionController,
14
+ type AdminSessionController,
15
15
  type SessionEntity,
16
16
  sessions,
17
17
  } from "alepha/api/users";
@@ -23,7 +23,7 @@ export interface AdminSessionsProps {
23
23
  }
24
24
 
25
25
  const AdminSessions = (props: AdminSessionsProps) => {
26
- const client = useClient<SessionController>();
26
+ const client = useClient<AdminSessionController>();
27
27
  const router = useRouter<AdminRouter>();
28
28
  const { l } = useI18n();
29
29
  const [refreshKey, setRefreshKey] = useState(0);
@@ -0,0 +1,281 @@
1
+ import { useRouter } from "@alepha/react/router";
2
+ import { ActionButton } from "@alepha/ui";
3
+ import {
4
+ ActionIcon,
5
+ Avatar,
6
+ Badge,
7
+ Button,
8
+ Group,
9
+ Menu,
10
+ Stack,
11
+ Text,
12
+ Tooltip,
13
+ } from "@mantine/core";
14
+ import {
15
+ IconChevronDown,
16
+ IconChevronLeft,
17
+ IconExternalLink,
18
+ } from "@tabler/icons-react";
19
+ import type { ComponentType, ReactNode } from "react";
20
+
21
+ export interface AdminResourceAction {
22
+ label: string;
23
+ icon?: ComponentType<{ size?: number }>;
24
+ onClick?: () => void;
25
+ href?: string;
26
+ color?: string;
27
+ disabled?: boolean;
28
+ loading?: boolean;
29
+ variant?: "filled" | "light" | "outline" | "subtle";
30
+ }
31
+
32
+ export interface AdminResourceHeaderProps {
33
+ /**
34
+ * Back navigation URL
35
+ */
36
+ backHref?: string;
37
+
38
+ /**
39
+ * Back navigation label
40
+ */
41
+ backLabel?: string;
42
+
43
+ /**
44
+ * Avatar content (letter, image URL, or custom node)
45
+ */
46
+ avatar?: string | ReactNode;
47
+
48
+ /**
49
+ * Avatar color
50
+ */
51
+ avatarColor?: string;
52
+
53
+ /**
54
+ * Resource title (e.g., user name)
55
+ */
56
+ title: string;
57
+
58
+ /**
59
+ * Secondary text (e.g., email)
60
+ */
61
+ subtitle?: string;
62
+
63
+ /**
64
+ * Tertiary identifier to copy (e.g., user ID)
65
+ */
66
+ identifier?: string;
67
+
68
+ /**
69
+ * Label for the identifier tooltip
70
+ */
71
+ identifierLabel?: string;
72
+
73
+ /**
74
+ * Status badge
75
+ */
76
+ status?: {
77
+ label: string;
78
+ color: "green" | "red" | "yellow" | "blue" | "gray";
79
+ };
80
+
81
+ /**
82
+ * Additional badges (e.g., roles)
83
+ */
84
+ badges?: Array<{
85
+ label: string;
86
+ color?: string;
87
+ variant?: "filled" | "light" | "outline" | "dot";
88
+ }>;
89
+
90
+ /**
91
+ * Primary action button
92
+ */
93
+ primaryAction?: AdminResourceAction;
94
+
95
+ /**
96
+ * Menu actions (shown in dropdown)
97
+ */
98
+ menuActions?: AdminResourceAction[];
99
+
100
+ /**
101
+ * External link URL
102
+ */
103
+ externalUrl?: string;
104
+
105
+ /**
106
+ * Loading state
107
+ */
108
+ loading?: boolean;
109
+ }
110
+
111
+ const ActionMenuItem = (props: { action: AdminResourceAction }) => {
112
+ const { action } = props;
113
+ const router = useRouter();
114
+
115
+ const menuItemProps: Record<string, unknown> = {};
116
+ if (action.href) {
117
+ Object.assign(menuItemProps, router.anchor(action.href));
118
+ } else if (action.onClick) {
119
+ menuItemProps.onClick = action.onClick;
120
+ }
121
+
122
+ return (
123
+ <Menu.Item
124
+ leftSection={action.icon ? <action.icon size={16} /> : undefined}
125
+ color={action.color}
126
+ disabled={action.disabled}
127
+ {...menuItemProps}
128
+ >
129
+ {action.label}
130
+ </Menu.Item>
131
+ );
132
+ };
133
+
134
+ const AdminResourceHeader = (props: AdminResourceHeaderProps) => {
135
+ const {
136
+ backHref,
137
+ backLabel = "Back",
138
+ avatar,
139
+ avatarColor = "blue",
140
+ title,
141
+ subtitle,
142
+ identifier,
143
+ identifierLabel = "ID",
144
+ status,
145
+ badges = [],
146
+ primaryAction,
147
+ menuActions = [],
148
+ externalUrl,
149
+ } = props;
150
+
151
+ const renderAvatar = () => {
152
+ if (typeof avatar === "string") {
153
+ if (avatar.startsWith("http") || avatar.startsWith("/")) {
154
+ return (
155
+ <Avatar src={avatar} size={56} radius="md" color={avatarColor} />
156
+ );
157
+ }
158
+ return (
159
+ <Avatar size={56} radius="md" color={avatarColor}>
160
+ {avatar}
161
+ </Avatar>
162
+ );
163
+ }
164
+ if (avatar) {
165
+ return avatar;
166
+ }
167
+ return (
168
+ <Avatar size={56} radius="md" color={avatarColor}>
169
+ {title.charAt(0).toUpperCase()}
170
+ </Avatar>
171
+ );
172
+ };
173
+
174
+ return (
175
+ <Stack gap="xs">
176
+ {/* Breadcrumb / Back navigation */}
177
+ {backHref && (
178
+ <Group>
179
+ <ActionButton
180
+ variant="subtle"
181
+ size="xs"
182
+ href={backHref}
183
+ leftSection={<IconChevronLeft size={14} />}
184
+ c="dimmed"
185
+ >
186
+ {backLabel}
187
+ </ActionButton>
188
+ </Group>
189
+ )}
190
+
191
+ {/* Main header */}
192
+ <Group justify="space-between" align="flex-start" wrap="nowrap">
193
+ {/* Left: Avatar + Info */}
194
+ <Group gap="md" wrap="nowrap">
195
+ {renderAvatar()}
196
+
197
+ <Stack gap={2} justify="center" style={{ minHeight: 56 }}>
198
+ {/* Title row */}
199
+ <Group gap="xs" align="center">
200
+ <Text size="md" fw={600} lh={1.2}>
201
+ {title}
202
+ </Text>
203
+ {status && (
204
+ <Badge
205
+ size="xs"
206
+ variant="light"
207
+ color={status.color}
208
+ tt="lowercase"
209
+ >
210
+ {status.label}
211
+ </Badge>
212
+ )}
213
+ </Group>
214
+
215
+ {/* Subtitle */}
216
+ {subtitle && (
217
+ <Text size="xs" c="dimmed">
218
+ {subtitle}
219
+ </Text>
220
+ )}
221
+ </Stack>
222
+ </Group>
223
+
224
+ {/* Right: Actions */}
225
+ <Group gap="xs">
226
+ {externalUrl && (
227
+ <Tooltip label="Open in new tab" openDelay={500}>
228
+ <ActionIcon
229
+ variant="subtle"
230
+ color="gray"
231
+ component="a"
232
+ href={externalUrl}
233
+ target="_blank"
234
+ >
235
+ <IconExternalLink size={18} />
236
+ </ActionIcon>
237
+ </Tooltip>
238
+ )}
239
+
240
+ {primaryAction && (
241
+ <ActionButton
242
+ variant={primaryAction.variant ?? "light"}
243
+ color={primaryAction.color}
244
+ onClick={primaryAction.onClick}
245
+ href={primaryAction.href}
246
+ loading={primaryAction.loading}
247
+ disabled={primaryAction.disabled}
248
+ leftSection={
249
+ primaryAction.icon ? (
250
+ <primaryAction.icon size={16} />
251
+ ) : undefined
252
+ }
253
+ >
254
+ {primaryAction.label}
255
+ </ActionButton>
256
+ )}
257
+
258
+ {menuActions.length > 0 && (
259
+ <Menu position="bottom-end" shadow="md" width={220}>
260
+ <Menu.Target>
261
+ <Button
262
+ variant="default"
263
+ rightSection={<IconChevronDown size={16} />}
264
+ >
265
+ Actions
266
+ </Button>
267
+ </Menu.Target>
268
+ <Menu.Dropdown>
269
+ {menuActions.map((action, index) => (
270
+ <ActionMenuItem key={index} action={action} />
271
+ ))}
272
+ </Menu.Dropdown>
273
+ </Menu>
274
+ )}
275
+ </Group>
276
+ </Group>
277
+ </Stack>
278
+ );
279
+ };
280
+
281
+ export default AdminResourceHeader;
@@ -0,0 +1,94 @@
1
+ import { useActive, useRouter } from "@alepha/react/router";
2
+ import { Tabs } from "@mantine/core";
3
+ import type { ComponentType, ReactNode } from "react";
4
+
5
+ export interface AdminResourceTab {
6
+ /**
7
+ * Tab key/value
8
+ */
9
+ value: string;
10
+
11
+ /**
12
+ * Tab label
13
+ */
14
+ label: string;
15
+
16
+ /**
17
+ * Tab icon
18
+ */
19
+ icon?: ComponentType<{ size?: number }>;
20
+
21
+ /**
22
+ * Navigation href
23
+ */
24
+ href: string;
25
+
26
+ /**
27
+ * Whether tab is disabled
28
+ */
29
+ disabled?: boolean;
30
+
31
+ /**
32
+ * Badge count to show
33
+ */
34
+ count?: number;
35
+ }
36
+
37
+ export interface AdminResourceTabsProps {
38
+ /**
39
+ * Array of tab configurations
40
+ */
41
+ tabs: AdminResourceTab[];
42
+
43
+ /**
44
+ * Currently active tab value
45
+ */
46
+ activeTab?: string;
47
+
48
+ /**
49
+ * Content to render below tabs
50
+ */
51
+ children?: ReactNode;
52
+ }
53
+
54
+ const TabItem = (props: { tab: AdminResourceTab }) => {
55
+ const { tab } = props;
56
+ const router = useRouter();
57
+ const { isActive, isPending } = useActive({ href: tab.href });
58
+ const anchorProps = router.anchor(tab.href);
59
+
60
+ return (
61
+ <Tabs.Tab
62
+ value={tab.value}
63
+ component="a"
64
+ leftSection={tab.icon ? <tab.icon size={16} /> : undefined}
65
+ disabled={tab.disabled}
66
+ data-active={isActive || undefined}
67
+ style={{
68
+ opacity: isPending ? 0.6 : 1,
69
+ }}
70
+ {...anchorProps}
71
+ >
72
+ {tab.label}
73
+ {tab.count !== undefined && tab.count > 0 && ` (${tab.count})`}
74
+ </Tabs.Tab>
75
+ );
76
+ };
77
+
78
+ const AdminResourceTabs = (props: AdminResourceTabsProps) => {
79
+ const { tabs, activeTab, children } = props;
80
+
81
+ return (
82
+ <Tabs value={activeTab} variant="default">
83
+ <Tabs.List>
84
+ {tabs.map((tab) => (
85
+ <TabItem key={tab.value} tab={tab} />
86
+ ))}
87
+ </Tabs.List>
88
+
89
+ {children}
90
+ </Tabs>
91
+ );
92
+ };
93
+
94
+ export default AdminResourceTabs;
@@ -0,0 +1,10 @@
1
+ export {
2
+ type AdminResourceAction,
3
+ type AdminResourceHeaderProps,
4
+ default as AdminResourceHeader,
5
+ } from "./AdminResourceHeader.tsx";
6
+ export {
7
+ type AdminResourceTab,
8
+ type AdminResourceTabsProps,
9
+ default as AdminResourceTabs,
10
+ } from "./AdminResourceTabs.tsx";
@@ -10,7 +10,7 @@ import {
10
10
  IconX,
11
11
  } from "@tabler/icons-react";
12
12
  import { type Page, t } from "alepha";
13
- import type { AuditController, AuditEntity } from "alepha/api/audits";
13
+ import type { AdminAuditController, AuditEntity } from "alepha/api/audits";
14
14
 
15
15
  export interface AdminUserAuditsProps {
16
16
  userRealmName?: string;
@@ -40,7 +40,7 @@ const getSeverityIcon = (severity: string) => {
40
40
 
41
41
  const AdminUserAudits = (_props: AdminUserAuditsProps) => {
42
42
  const state = useRouterState();
43
- const client = useClient<AuditController>();
43
+ const client = useClient<AdminAuditController>();
44
44
  const { l } = useI18n();
45
45
  const userId = state.params.userId as string;
46
46
 
@@ -4,7 +4,7 @@ import { useRouter } from "@alepha/react/router";
4
4
  import { ActionButton, Control, Flex } from "@alepha/ui";
5
5
  import { Card, Stack, Text } from "@mantine/core";
6
6
  import { t } from "alepha";
7
- import type { UserController } from "alepha/api/users";
7
+ import type { AdminUserController } from "alepha/api/users";
8
8
  import type { AdminRouter } from "../../AdminRouter.ts";
9
9
 
10
10
  export interface AdminUserCreateProps {
@@ -12,7 +12,7 @@ export interface AdminUserCreateProps {
12
12
  }
13
13
 
14
14
  const AdminUserCreate = (props: AdminUserCreateProps) => {
15
- const client = useClient<UserController>();
15
+ const client = useClient<AdminUserController>();
16
16
  const router = useRouter<AdminRouter>();
17
17
 
18
18
  const form = useForm({