@alepha/ui 0.14.1 → 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 (183) hide show
  1. package/dist/admin/AdminAudits-B3EhKhN7.js +3 -0
  2. package/dist/admin/{AdminAudits-CwvH8e8c.js → AdminAudits-DIrCCPk3.js} +3 -2
  3. package/dist/admin/AdminAudits-DIrCCPk3.js.map +1 -0
  4. package/dist/admin/AdminFiles-C8OG4dtD.js +3 -0
  5. package/dist/admin/{AdminFiles-C_w1tb_x.js → AdminFiles-RsL178Ta.js} +2 -2
  6. package/dist/admin/{AdminFiles-C_w1tb_x.js.map → AdminFiles-RsL178Ta.js.map} +1 -1
  7. package/dist/admin/AdminNotifications-BSL4B2fQ.js +3 -0
  8. package/dist/admin/{AdminNotifications-DuYy74AN.js → AdminNotifications-cIbywWKi.js} +2 -2
  9. package/dist/admin/AdminNotifications-cIbywWKi.js.map +1 -0
  10. package/dist/admin/{AdminParameters-DYg48Jwe.js → AdminParameters-BKObzzpN.js} +1 -1
  11. package/dist/admin/{AdminParameters-YagqWTG3.js → AdminParameters-D-q3Qmhv.js} +2 -2
  12. package/dist/admin/{AdminParameters-YagqWTG3.js.map → AdminParameters-D-q3Qmhv.js.map} +1 -1
  13. package/dist/admin/AdminSessions-DHG9zPfr.js +3 -0
  14. package/dist/admin/{AdminSessions-BCjgJ-93.js → AdminSessions-vOgkrQ2U.js} +3 -2
  15. package/dist/admin/AdminSessions-vOgkrQ2U.js.map +1 -0
  16. package/dist/admin/{AdminUserAudits-B_PUXCKC.js → AdminUserAudits-CSsN1fIC.js} +3 -2
  17. package/dist/admin/AdminUserAudits-CSsN1fIC.js.map +1 -0
  18. package/dist/admin/{AdminUserAudits-D7cTcElL.js → AdminUserAudits-DmAnivo3.js} +1 -1
  19. package/dist/admin/{AdminUserCreate-DzfRbGZ4.js → AdminUserCreate-B72nu-3W.js} +3 -2
  20. package/dist/admin/AdminUserCreate-B72nu-3W.js.map +1 -0
  21. package/dist/admin/{AdminUserCreate-oUA1KDIl.js → AdminUserCreate-DpA13zwj.js} +1 -1
  22. package/dist/admin/AdminUserDetails-CKM2IEMr.js +475 -0
  23. package/dist/admin/AdminUserDetails-CKM2IEMr.js.map +1 -0
  24. package/dist/admin/{AdminUserDetails-y1H5DW8Y.js → AdminUserDetails-Zib_B6Al.js} +1 -1
  25. package/dist/admin/{AdminUserLayout-Dejnz13m.js → AdminUserLayout-BNBOEiAO.js} +1 -1
  26. package/dist/admin/AdminUserLayout-D7En9UBq.js +334 -0
  27. package/dist/admin/AdminUserLayout-D7En9UBq.js.map +1 -0
  28. package/dist/admin/AdminUserSessions-D9X2_HMA.js +3 -0
  29. package/dist/admin/{AdminUserSessions-DO9H85O-.js → AdminUserSessions-DEaGu6n6.js} +3 -2
  30. package/dist/admin/AdminUserSessions-DEaGu6n6.js.map +1 -0
  31. package/dist/admin/{AdminUserSettings-B3jA8g3p.js → AdminUserSettings-Di73D7g2.js} +8 -6
  32. package/dist/admin/AdminUserSettings-Di73D7g2.js.map +1 -0
  33. package/dist/admin/AdminUserSettings-yI-JECf5.js +3 -0
  34. package/dist/admin/{AdminUsers-ebbrJBT0.js → AdminUsers-BnGIRvmV.js} +3 -2
  35. package/dist/admin/AdminUsers-BnGIRvmV.js.map +1 -0
  36. package/dist/admin/AdminUsers-CG9-2Z8W.js +3 -0
  37. package/dist/admin/index.d.ts +25 -25
  38. package/dist/admin/index.d.ts.map +1 -1
  39. package/dist/admin/index.js +37 -36
  40. package/dist/admin/index.js.map +1 -1
  41. package/dist/auth/{AuthLayout-BAZJHzDG.js → AuthLayout-B1sUB8fB.js} +2 -2
  42. package/dist/auth/AuthLayout-B1sUB8fB.js.map +1 -0
  43. package/dist/auth/Login-BWi-pPbO.js +4 -0
  44. package/dist/auth/{Login-CeNZZjrr.js → Login-Cjxv3EDi.js} +2 -2
  45. package/dist/auth/Login-Cjxv3EDi.js.map +1 -0
  46. package/dist/auth/{Register-s4ENeyiE.js → Register-BKBIpHhW.js} +3 -2
  47. package/dist/auth/Register-BKBIpHhW.js.map +1 -0
  48. package/dist/auth/Register-CtdvihIM.js +4 -0
  49. package/dist/auth/ResetPassword-BUdM7T_R.js +3 -0
  50. package/dist/auth/{ResetPassword-GLIFkJT7.js → ResetPassword-DvqD_1SJ.js} +3 -2
  51. package/dist/auth/ResetPassword-DvqD_1SJ.js.map +1 -0
  52. package/dist/auth/VerifyEmail-BYmtnkEl.js +3 -0
  53. package/dist/auth/{VerifyEmail-R79sSej_.js → VerifyEmail-VaBruOnO.js} +3 -2
  54. package/dist/auth/VerifyEmail-VaBruOnO.js.map +1 -0
  55. package/dist/auth/index.d.ts +11 -11
  56. package/dist/auth/index.d.ts.map +1 -1
  57. package/dist/auth/index.js +10 -10
  58. package/dist/auth/index.js.map +1 -1
  59. package/dist/core/index.d.ts +36 -55
  60. package/dist/core/index.d.ts.map +1 -1
  61. package/dist/core/index.js +50 -350
  62. package/dist/core/index.js.map +1 -1
  63. package/dist/demo/DemoDataTable-2mzzf__a.js +150 -0
  64. package/dist/demo/DemoDataTable-2mzzf__a.js.map +1 -0
  65. package/dist/demo/DemoHome-CnuL5WV9.js +25 -0
  66. package/dist/demo/DemoHome-CnuL5WV9.js.map +1 -0
  67. package/dist/demo/DemoHome-D6Z7EE4V.js +3 -0
  68. package/dist/demo/DemoJsonViewer-CYUggLop.js +4 -0
  69. package/dist/demo/DemoJsonViewer-NUGst5wW.js +430 -0
  70. package/dist/demo/DemoJsonViewer-NUGst5wW.js.map +1 -0
  71. package/dist/demo/DemoLayout-ZFDzyvY3.js +3 -0
  72. package/dist/demo/DemoLayout-dvbeuBBf.js +47 -0
  73. package/dist/demo/DemoLayout-dvbeuBBf.js.map +1 -0
  74. package/dist/demo/DemoLogin--wE44i23.js +327 -0
  75. package/dist/demo/DemoLogin--wE44i23.js.map +1 -0
  76. package/dist/demo/DemoRegister-BtrMksx6.js +488 -0
  77. package/dist/demo/DemoRegister-BtrMksx6.js.map +1 -0
  78. package/dist/demo/DemoResetPassword-DVXiiiX7.js +341 -0
  79. package/dist/demo/DemoResetPassword-DVXiiiX7.js.map +1 -0
  80. package/dist/demo/DemoSidebar-DWnjYHoP.js +82 -0
  81. package/dist/demo/DemoSidebar-DWnjYHoP.js.map +1 -0
  82. package/dist/demo/DemoTypeForm-P5_VInW2.js +83 -0
  83. package/dist/demo/DemoTypeForm-P5_VInW2.js.map +1 -0
  84. package/dist/demo/DemoVerifyEmail-C_ooC5u8.js +152 -0
  85. package/dist/demo/DemoVerifyEmail-C_ooC5u8.js.map +1 -0
  86. package/dist/demo/IconGoogle-DvmFiEDB.js +58 -0
  87. package/dist/demo/IconGoogle-DvmFiEDB.js.map +1 -0
  88. package/dist/demo/Showcase-vemLuO2t.js +187 -0
  89. package/dist/demo/Showcase-vemLuO2t.js.map +1 -0
  90. package/dist/demo/index.d.ts +97 -0
  91. package/dist/demo/index.d.ts.map +1 -0
  92. package/dist/demo/index.js +121 -0
  93. package/dist/demo/index.js.map +1 -0
  94. package/dist/json/index.d.ts +58 -0
  95. package/dist/json/index.d.ts.map +1 -0
  96. package/dist/json/index.js +325 -0
  97. package/dist/json/index.js.map +1 -0
  98. package/package.json +25 -14
  99. package/src/admin/AdminRouter.ts +23 -20
  100. package/src/admin/MainRouter.ts +2 -2
  101. package/src/admin/components/audits/AdminAudits.tsx +4 -3
  102. package/src/admin/components/jobs/AdminJobs.tsx +2 -2
  103. package/src/admin/components/notifications/AdminNotifications.tsx +2 -2
  104. package/src/admin/components/parameters/AdminParameters.tsx +2 -2
  105. package/src/admin/components/sessions/AdminSessions.tsx +4 -3
  106. package/src/admin/components/shared/AdminResourceHeader.tsx +281 -0
  107. package/src/admin/components/shared/AdminResourceTabs.tsx +94 -0
  108. package/src/admin/components/shared/index.ts +10 -0
  109. package/src/admin/components/users/AdminUserAudits.tsx +4 -3
  110. package/src/admin/components/users/AdminUserCreate.tsx +4 -3
  111. package/src/admin/components/users/AdminUserDetails.tsx +339 -86
  112. package/src/admin/components/users/AdminUserLayout.tsx +165 -113
  113. package/src/admin/components/users/AdminUserSessions.tsx +4 -3
  114. package/src/admin/components/users/AdminUserSettings.tsx +12 -6
  115. package/src/admin/components/users/AdminUsers.tsx +8 -3
  116. package/src/auth/AuthRouter.ts +1 -1
  117. package/src/auth/components/AuthLayout.tsx +1 -1
  118. package/src/auth/components/Login.tsx +1 -1
  119. package/src/auth/components/Register.tsx +2 -1
  120. package/src/auth/components/ResetPassword.tsx +2 -1
  121. package/src/auth/components/VerifyEmail.tsx +2 -1
  122. package/src/auth/components/buttons/UserButton.tsx +1 -1
  123. package/src/core/RootRouter.ts +1 -1
  124. package/src/core/components/buttons/ActionButton.tsx +3 -4
  125. package/src/core/components/form/Control.tsx +12 -1
  126. package/src/core/components/form/ControlNumber.tsx +5 -0
  127. package/src/core/components/form/TypeForm.tsx +3 -2
  128. package/src/core/components/layout/AdminShell.tsx +2 -1
  129. package/src/core/components/layout/AlephaMantineProvider.tsx +7 -2
  130. package/src/core/components/layout/Omnibar.tsx +2 -1
  131. package/src/core/components/layout/Sidebar.tsx +18 -18
  132. package/src/core/index.ts +1 -2
  133. package/src/core/services/DialogService.tsx +0 -17
  134. package/{styles.css → src/core/styles.css} +1 -5
  135. package/src/demo/DemoRouter.ts +123 -0
  136. package/src/demo/components/DemoHome.tsx +29 -0
  137. package/src/demo/components/DemoLayout.tsx +52 -0
  138. package/src/demo/components/auth/DemoLogin.tsx +130 -0
  139. package/src/demo/components/auth/DemoRegister.tsx +144 -0
  140. package/src/demo/components/auth/DemoResetPassword.tsx +69 -0
  141. package/src/demo/components/auth/DemoVerifyEmail.tsx +28 -0
  142. package/src/demo/components/core/DemoDataTable.tsx +174 -0
  143. package/src/demo/components/core/DemoSidebar.tsx +85 -0
  144. package/src/demo/components/core/DemoTypeForm.tsx +69 -0
  145. package/src/demo/components/json/DemoJsonViewer.tsx +128 -0
  146. package/src/demo/components/shared/MacWindow.tsx +105 -0
  147. package/src/demo/components/shared/Showcase.tsx +112 -0
  148. package/src/demo/index.ts +30 -0
  149. package/src/demo/styles.css +0 -0
  150. package/src/json/components/JsonViewer.css +25 -0
  151. package/src/json/components/JsonViewer.tsx +526 -0
  152. package/src/json/extensions/DialogService.tsx +31 -0
  153. package/src/json/index.ts +5 -0
  154. package/src/json/styles.css +1 -0
  155. package/dist/admin/AdminAudits-CwvH8e8c.js.map +0 -1
  156. package/dist/admin/AdminAudits-Dv8Vk_6r.js +0 -3
  157. package/dist/admin/AdminFiles-5CPA3lQk.js +0 -3
  158. package/dist/admin/AdminNotifications-DLjmZWtf.js +0 -3
  159. package/dist/admin/AdminNotifications-DuYy74AN.js.map +0 -1
  160. package/dist/admin/AdminSessions-BCjgJ-93.js.map +0 -1
  161. package/dist/admin/AdminSessions-DEh2uN-4.js +0 -3
  162. package/dist/admin/AdminUserAudits-B_PUXCKC.js.map +0 -1
  163. package/dist/admin/AdminUserCreate-DzfRbGZ4.js.map +0 -1
  164. package/dist/admin/AdminUserDetails-DeTrJm-t.js +0 -221
  165. package/dist/admin/AdminUserDetails-DeTrJm-t.js.map +0 -1
  166. package/dist/admin/AdminUserLayout-CsfrrZkD.js +0 -150
  167. package/dist/admin/AdminUserLayout-CsfrrZkD.js.map +0 -1
  168. package/dist/admin/AdminUserSessions-Bbhcpz4k.js +0 -3
  169. package/dist/admin/AdminUserSessions-DO9H85O-.js.map +0 -1
  170. package/dist/admin/AdminUserSettings-B3jA8g3p.js.map +0 -1
  171. package/dist/admin/AdminUserSettings-CE0xpbQc.js +0 -3
  172. package/dist/admin/AdminUsers-CegGZDhW.js +0 -3
  173. package/dist/admin/AdminUsers-ebbrJBT0.js.map +0 -1
  174. package/dist/auth/AuthLayout-BAZJHzDG.js.map +0 -1
  175. package/dist/auth/Login-CeNZZjrr.js.map +0 -1
  176. package/dist/auth/Login-hQcu1nlu.js +0 -4
  177. package/dist/auth/Register-B6HBNVHS.js +0 -4
  178. package/dist/auth/Register-s4ENeyiE.js.map +0 -1
  179. package/dist/auth/ResetPassword-Cjd-W-Nu.js +0 -3
  180. package/dist/auth/ResetPassword-GLIFkJT7.js.map +0 -1
  181. package/dist/auth/VerifyEmail-Dc9ABKUw.js +0 -3
  182. package/dist/auth/VerifyEmail-R79sSej_.js.map +0 -1
  183. package/src/core/components/data/JsonViewer.tsx +0 -361
@@ -1,5 +1,6 @@
1
- import { useClient, useRouter } from "@alepha/react";
1
+ import { useClient } from "@alepha/react";
2
2
  import { useI18n } from "@alepha/react/i18n";
3
+ import { useRouter } from "@alepha/react/router";
3
4
  import { DataTable, Flex, Text } from "@alepha/ui";
4
5
  import { Badge, Group, Stack, Tooltip } from "@mantine/core";
5
6
  import {
@@ -10,7 +11,7 @@ import {
10
11
  IconX,
11
12
  } from "@tabler/icons-react";
12
13
  import { type Page, t } from "alepha";
13
- import type { AuditController, AuditEntity } from "alepha/api/audits";
14
+ import type { AdminAuditController, AuditEntity } from "alepha/api/audits";
14
15
  import type { AdminRouter } from "../../AdminRouter.ts";
15
16
 
16
17
  export interface AdminAuditsProps {
@@ -61,7 +62,7 @@ const getTypeColor = (type: string) => {
61
62
  };
62
63
 
63
64
  const AdminAudits = (props: AdminAuditsProps) => {
64
- const client = useClient<AuditController>();
65
+ const client = useClient<AdminAuditController>();
65
66
  const router = useRouter<AdminRouter>();
66
67
  const { l } = useI18n();
67
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[]>([]);
@@ -1,5 +1,6 @@
1
- import { useClient, useRouter } from "@alepha/react";
1
+ import { useClient } from "@alepha/react";
2
2
  import { useI18n } from "@alepha/react/i18n";
3
+ import { useRouter } from "@alepha/react/router";
3
4
  import { ActionButton, DataTable, Flex, Text } from "@alepha/ui";
4
5
  import { Badge, Group } from "@mantine/core";
5
6
  import {
@@ -10,7 +11,7 @@ import {
10
11
  } from "@tabler/icons-react";
11
12
  import { type Page, t } from "alepha";
12
13
  import {
13
- type SessionController,
14
+ type AdminSessionController,
14
15
  type SessionEntity,
15
16
  sessions,
16
17
  } from "alepha/api/users";
@@ -22,7 +23,7 @@ export interface AdminSessionsProps {
22
23
  }
23
24
 
24
25
  const AdminSessions = (props: AdminSessionsProps) => {
25
- const client = useClient<SessionController>();
26
+ const client = useClient<AdminSessionController>();
26
27
  const router = useRouter<AdminRouter>();
27
28
  const { l } = useI18n();
28
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";
@@ -1,5 +1,6 @@
1
- import { useClient, useRouterState } from "@alepha/react";
1
+ import { useClient } from "@alepha/react";
2
2
  import { useI18n } from "@alepha/react/i18n";
3
+ import { useRouterState } from "@alepha/react/router";
3
4
  import { DataTable, Flex, Text } from "@alepha/ui";
4
5
  import { Badge, Group, Tooltip } from "@mantine/core";
5
6
  import {
@@ -9,7 +10,7 @@ import {
9
10
  IconX,
10
11
  } from "@tabler/icons-react";
11
12
  import { type Page, t } from "alepha";
12
- import type { AuditController, AuditEntity } from "alepha/api/audits";
13
+ import type { AdminAuditController, AuditEntity } from "alepha/api/audits";
13
14
 
14
15
  export interface AdminUserAuditsProps {
15
16
  userRealmName?: string;
@@ -39,7 +40,7 @@ const getSeverityIcon = (severity: string) => {
39
40
 
40
41
  const AdminUserAudits = (_props: AdminUserAuditsProps) => {
41
42
  const state = useRouterState();
42
- const client = useClient<AuditController>();
43
+ const client = useClient<AdminAuditController>();
43
44
  const { l } = useI18n();
44
45
  const userId = state.params.userId as string;
45
46
 
@@ -1,9 +1,10 @@
1
- import { useClient, useRouter } from "@alepha/react";
1
+ import { useClient } from "@alepha/react";
2
2
  import { useForm } from "@alepha/react/form";
3
+ import { useRouter } from "@alepha/react/router";
3
4
  import { ActionButton, Control, Flex } from "@alepha/ui";
4
5
  import { Card, Stack, Text } from "@mantine/core";
5
6
  import { t } from "alepha";
6
- import type { UserController } from "alepha/api/users";
7
+ import type { AdminUserController } from "alepha/api/users";
7
8
  import type { AdminRouter } from "../../AdminRouter.ts";
8
9
 
9
10
  export interface AdminUserCreateProps {
@@ -11,7 +12,7 @@ export interface AdminUserCreateProps {
11
12
  }
12
13
 
13
14
  const AdminUserCreate = (props: AdminUserCreateProps) => {
14
- const client = useClient<UserController>();
15
+ const client = useClient<AdminUserController>();
15
16
  const router = useRouter<AdminRouter>();
16
17
 
17
18
  const form = useForm({