@alepha/ui 0.18.3 → 0.19.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.
- package/dist/admin/{AdminApiKeys-Dy_k-4Vd.js → AdminApiKeys-Bt1PjO6o.js} +3 -4
- package/dist/admin/{AdminApiKeys-Dy_k-4Vd.js.map → AdminApiKeys-Bt1PjO6o.js.map} +1 -1
- package/dist/admin/{AdminAudits-CKiFMSSU.js → AdminAudits-C7c1CN4c.js} +3 -4
- package/dist/admin/{AdminAudits-CKiFMSSU.js.map → AdminAudits-C7c1CN4c.js.map} +1 -1
- package/dist/admin/{AdminDashboard-PhC_dZqo.js → AdminDashboard-C3RXpTp6.js} +3 -4
- package/dist/admin/{AdminDashboard-PhC_dZqo.js.map → AdminDashboard-C3RXpTp6.js.map} +1 -1
- package/dist/admin/{AdminFiles-DFTjijGp.js → AdminFiles-31ivR6Wq.js} +3 -4
- package/dist/admin/{AdminFiles-DFTjijGp.js.map → AdminFiles-31ivR6Wq.js.map} +1 -1
- package/dist/admin/{AdminJobDashboard-BL8gGPDp.js → AdminJobDashboard-BABLe7hL.js} +73 -25
- package/dist/admin/AdminJobDashboard-BABLe7hL.js.map +1 -0
- package/dist/admin/{AdminJobExecutions-D9E-CS-U.js → AdminJobExecutions-D-G8RIlr.js} +3 -4
- package/dist/admin/{AdminJobExecutions-D9E-CS-U.js.map → AdminJobExecutions-D-G8RIlr.js.map} +1 -1
- package/dist/admin/{AdminJobRegistry-Ci9ue1zC.js → AdminJobRegistry-oIS3K9NX.js} +3 -4
- package/dist/admin/{AdminJobRegistry-Ci9ue1zC.js.map → AdminJobRegistry-oIS3K9NX.js.map} +1 -1
- package/dist/admin/{AdminLayout-I6TlUMPc.js → AdminLayout-BmZ9mtXh.js} +8 -25
- package/dist/admin/AdminLayout-BmZ9mtXh.js.map +1 -0
- package/dist/admin/{AdminNotifications-ZPHCYrv7.js → AdminNotifications-DHdzksww.js} +3 -4
- package/dist/admin/{AdminNotifications-ZPHCYrv7.js.map → AdminNotifications-DHdzksww.js.map} +1 -1
- package/dist/admin/{AdminParameters-CqgvhRsb.js → AdminParameters-CyZQSXnN.js} +3 -12
- package/dist/admin/{AdminParameters-CqgvhRsb.js.map → AdminParameters-CyZQSXnN.js.map} +1 -1
- package/dist/admin/{AdminSessions-Bz5NRuoW.js → AdminSessions--xwELDSO.js} +3 -4
- package/dist/admin/{AdminSessions-Bz5NRuoW.js.map → AdminSessions--xwELDSO.js.map} +1 -1
- package/dist/admin/{AdminUserLayout-lXT6I0Qq.js → AdminUserLayout-DvBTG5gd.js} +72 -111
- package/dist/admin/AdminUserLayout-DvBTG5gd.js.map +1 -0
- package/dist/admin/{AdminUserProfile-vFBLoJ3h.js → AdminUserProfile-CzsPBl6Z.js} +7 -6
- package/dist/admin/AdminUserProfile-CzsPBl6Z.js.map +1 -0
- package/dist/admin/{AdminUserSessions-CT_YDim0.js → AdminUserSessions-C-aUnhVN.js} +3 -4
- package/dist/admin/{AdminUserSessions-CT_YDim0.js.map → AdminUserSessions-C-aUnhVN.js.map} +1 -1
- package/dist/admin/{AdminUsers-D1UfGya9.js → AdminUsers-BYwei5sj.js} +4 -4
- package/dist/admin/AdminUsers-BYwei5sj.js.map +1 -0
- package/dist/admin/{AuthLayout-_frhdgOO.js → AuthLayout-CkPGLJku.js} +3 -4
- package/dist/admin/{AuthLayout-_frhdgOO.js.map → AuthLayout-CkPGLJku.js.map} +1 -1
- package/dist/{demo/IconGoogle-CSQLPYwX.js → admin/IconGoogle-8Nkx6yax.js} +2 -4
- package/dist/admin/{IconGoogle-Ch1m3Uzl.js.map → IconGoogle-8Nkx6yax.js.map} +1 -1
- package/dist/admin/{Login-xtNmQtGh.js → Login-DSBqNsZc.js} +5 -6
- package/dist/{auth/Login-BA1E8IZl.js.map → admin/Login-DSBqNsZc.js.map} +1 -1
- package/dist/admin/{Profile-_AtPUwAP.js → Profile-CDRjJo0P.js} +3 -5
- package/dist/{demo/Profile-DS5q4vOh.js.map → admin/Profile-CDRjJo0P.js.map} +1 -1
- package/dist/admin/{Register-JcCjHUUn.js → Register-4QGFOnfh.js} +5 -6
- package/dist/{demo/Register-B4hLBeEv.js.map → admin/Register-4QGFOnfh.js.map} +1 -1
- package/dist/admin/{ResetPassword-CwGBPLJO.js → ResetPassword-Gxc9L_mY.js} +4 -5
- package/dist/{auth/ResetPassword-DCtGcneA.js.map → admin/ResetPassword-Gxc9L_mY.js.map} +1 -1
- package/dist/admin/{VerifyEmail-hNxWejWf.js → VerifyEmail-D7G5NnaN.js} +4 -5
- package/dist/{auth/VerifyEmail-DkH7NBfn.js.map → admin/VerifyEmail-D7G5NnaN.js.map} +1 -1
- package/dist/admin/adminUserAtom-DCi4wf-v.js +11 -0
- package/dist/admin/adminUserAtom-DCi4wf-v.js.map +1 -0
- package/dist/admin/{core-CYaRQ8O-.js → core-D1AbU50V.js} +24 -85
- package/dist/admin/core-D1AbU50V.js.map +1 -0
- package/dist/admin/index.d.ts +59 -10
- package/dist/admin/index.d.ts.map +1 -1
- package/dist/admin/index.js +35 -36
- package/dist/admin/index.js.map +1 -1
- package/dist/admin/rolldown-runtime-CiIaOW0V.js +13 -0
- package/dist/{demo/AuthLayout-Brri4A-L.js → auth/AuthLayout-CfRKcTqP.js} +3 -4
- package/dist/auth/{AuthLayout-AvLlcLjS.js.map → AuthLayout-CfRKcTqP.js.map} +1 -1
- package/dist/{admin/IconGoogle-Ch1m3Uzl.js → auth/IconGoogle-8Nkx6yax.js} +2 -4
- package/dist/auth/{IconGoogle-Ch1m3Uzl.js.map → IconGoogle-8Nkx6yax.js.map} +1 -1
- package/dist/auth/{Login-BA1E8IZl.js → Login-DJyweoPS.js} +5 -6
- package/dist/{demo/Login-C12N4oGs.js.map → auth/Login-DJyweoPS.js.map} +1 -1
- package/dist/{demo/Profile-DS5q4vOh.js → auth/Profile-Cy93pNTw.js} +3 -5
- package/dist/auth/{Profile-YcWdeuFz.js.map → Profile-Cy93pNTw.js.map} +1 -1
- package/dist/auth/{Register-CPhEO5MG.js → Register-CSqzzitW.js} +5 -6
- package/dist/{admin/Register-JcCjHUUn.js.map → auth/Register-CSqzzitW.js.map} +1 -1
- package/dist/{demo/ResetPassword-D8g9ha1N.js → auth/ResetPassword-B61QPlQi.js} +4 -5
- package/dist/{admin/ResetPassword-CwGBPLJO.js.map → auth/ResetPassword-B61QPlQi.js.map} +1 -1
- package/dist/auth/{VerifyEmail-DkH7NBfn.js → VerifyEmail-CqBJ11id.js} +4 -5
- package/dist/{admin/VerifyEmail-hNxWejWf.js.map → auth/VerifyEmail-CqBJ11id.js.map} +1 -1
- package/dist/auth/{core-D5jIAVF2.js → core-C6D3pazL.js} +22 -54
- package/dist/auth/core-C6D3pazL.js.map +1 -0
- package/dist/auth/index.d.ts +0 -6
- package/dist/auth/index.d.ts.map +1 -1
- package/dist/auth/index.js +13 -18
- package/dist/auth/index.js.map +1 -1
- package/dist/auth/rolldown-runtime-CiIaOW0V.js +13 -0
- package/dist/core/index.d.ts +10 -4
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +21 -91
- package/dist/core/index.js.map +1 -1
- package/dist/{auth/AuthLayout-AvLlcLjS.js → demo/AuthLayout-Dq5tSLSc.js} +3 -4
- package/dist/demo/{AuthLayout-Brri4A-L.js.map → AuthLayout-Dq5tSLSc.js.map} +1 -1
- package/dist/demo/{DemoButton-wiCxZZ_L.js → DemoButton-_Ws2w-J0.js} +4 -5
- package/dist/demo/{DemoButton-wiCxZZ_L.js.map → DemoButton-_Ws2w-J0.js.map} +1 -1
- package/dist/demo/{DemoControlSelect-D7ILObVg.js → DemoControlSelect-ChP4ZOpQ.js} +4 -5
- package/dist/demo/{DemoControlSelect-D7ILObVg.js.map → DemoControlSelect-ChP4ZOpQ.js.map} +1 -1
- package/dist/demo/{DemoDataTable-DZ5Y8pFX.js → DemoDataTable-Hwf_UUni.js} +4 -5
- package/dist/demo/{DemoDataTable-DZ5Y8pFX.js.map → DemoDataTable-Hwf_UUni.js.map} +1 -1
- package/dist/demo/{DemoDialog-CUWdLHim.js → DemoDialog-B01OMVRd.js} +3 -4
- package/dist/demo/{DemoDialog-CUWdLHim.js.map → DemoDialog-B01OMVRd.js.map} +1 -1
- package/dist/demo/{DemoFlex-a8OhMMvq.js → DemoFlex-870PEl0V.js} +4 -5
- package/dist/demo/{DemoFlex-a8OhMMvq.js.map → DemoFlex-870PEl0V.js.map} +1 -1
- package/dist/demo/{DemoHeading-C13OVDfS.js → DemoHeading-C1YR27fz.js} +4 -5
- package/dist/demo/{DemoHeading-C13OVDfS.js.map → DemoHeading-C1YR27fz.js.map} +1 -1
- package/dist/demo/{DemoHome-D_De3UiT.js → DemoHome-DRbL2eGf.js} +4 -5
- package/dist/demo/{DemoHome-D_De3UiT.js.map → DemoHome-DRbL2eGf.js.map} +1 -1
- package/dist/demo/{DemoJsonViewer-B50s9aGM.js → DemoJsonViewer-DoABiqBW.js} +4 -5
- package/dist/demo/{DemoJsonViewer-B50s9aGM.js.map → DemoJsonViewer-DoABiqBW.js.map} +1 -1
- package/dist/demo/{DemoLayout-CHU8WTwO.js → DemoLayout-CN_PDCX2.js} +4 -5
- package/dist/demo/DemoLayout-CN_PDCX2.js.map +1 -0
- package/dist/demo/{DemoLogin-BBlrWpml.js → DemoLogin-B5x-ug3Q.js} +10 -11
- package/dist/demo/{DemoLogin-BBlrWpml.js.map → DemoLogin-B5x-ug3Q.js.map} +1 -1
- package/dist/demo/{DemoRegister-BuNE3_-f.js → DemoRegister-Q6sg2xuV.js} +10 -11
- package/dist/demo/{DemoRegister-BuNE3_-f.js.map → DemoRegister-Q6sg2xuV.js.map} +1 -1
- package/dist/demo/{DemoResetPassword-D_IjjjOJ.js → DemoResetPassword-DrqZfmEw.js} +10 -11
- package/dist/demo/{DemoResetPassword-D_IjjjOJ.js.map → DemoResetPassword-DrqZfmEw.js.map} +1 -1
- package/dist/demo/{DemoSidebar-Giy2HRBD.js → DemoSidebar-CfKS6w1o.js} +4 -5
- package/dist/demo/{DemoSidebar-Giy2HRBD.js.map → DemoSidebar-CfKS6w1o.js.map} +1 -1
- package/dist/demo/{DemoText-ubcw-vog.js → DemoText-pT6Gi5b5.js} +4 -5
- package/dist/demo/{DemoText-ubcw-vog.js.map → DemoText-pT6Gi5b5.js.map} +1 -1
- package/dist/demo/{DemoToast-9die_dYT.js → DemoToast-I13NBzQQ.js} +3 -4
- package/dist/demo/{DemoToast-9die_dYT.js.map → DemoToast-I13NBzQQ.js.map} +1 -1
- package/dist/demo/{DemoTypeForm-D_d6OVKL.js → DemoTypeForm-BqzcrtvN.js} +4 -5
- package/dist/demo/{DemoTypeForm-D_d6OVKL.js.map → DemoTypeForm-BqzcrtvN.js.map} +1 -1
- package/dist/demo/{DemoVerifyEmail-B43KlF4F.js → DemoVerifyEmail-HwD8xfQw.js} +10 -11
- package/dist/demo/{DemoVerifyEmail-B43KlF4F.js.map → DemoVerifyEmail-HwD8xfQw.js.map} +1 -1
- package/dist/{auth/IconGoogle-Ch1m3Uzl.js → demo/IconGoogle-CwQy4G9y.js} +2 -4
- package/dist/demo/{IconGoogle-CSQLPYwX.js.map → IconGoogle-CwQy4G9y.js.map} +1 -1
- package/dist/demo/{Login-C12N4oGs.js → Login-CqG1iJbn.js} +5 -6
- package/dist/{admin/Login-xtNmQtGh.js.map → demo/Login-CqG1iJbn.js.map} +1 -1
- package/dist/{auth/Profile-YcWdeuFz.js → demo/Profile-C0ojJCaG.js} +3 -5
- package/dist/{admin/Profile-_AtPUwAP.js.map → demo/Profile-C0ojJCaG.js.map} +1 -1
- package/dist/demo/{Register-B4hLBeEv.js → Register-KKZwr_lL.js} +5 -6
- package/dist/{auth/Register-CPhEO5MG.js.map → demo/Register-KKZwr_lL.js.map} +1 -1
- package/dist/{auth/ResetPassword-DCtGcneA.js → demo/ResetPassword-DMrLFEtr.js} +4 -5
- package/dist/demo/{ResetPassword-D8g9ha1N.js.map → ResetPassword-DMrLFEtr.js.map} +1 -1
- package/dist/demo/{Showcase-D6Fxt4X4.js → Showcase-D49Wud2v.js} +3 -5
- package/dist/demo/{Showcase-D6Fxt4X4.js.map → Showcase-D49Wud2v.js.map} +1 -1
- package/dist/demo/{VerifyEmail-BjDo0cZA.js → VerifyEmail-BFCAFz6T.js} +4 -5
- package/dist/demo/{VerifyEmail-BjDo0cZA.js.map → VerifyEmail-BFCAFz6T.js.map} +1 -1
- package/dist/demo/{auth-ByVTreDl.js → auth-D9qTZzCa.js} +18 -35
- package/dist/demo/{auth-ByVTreDl.js.map → auth-D9qTZzCa.js.map} +1 -1
- package/dist/demo/{core-DFgB3yU4.js → core-DRtQklr3.js} +23 -86
- package/dist/demo/core-DRtQklr3.js.map +1 -0
- package/dist/demo/index.js +19 -22
- package/dist/demo/index.js.map +1 -1
- package/dist/demo/rolldown-runtime-CiIaOW0V.js +13 -0
- package/package.json +17 -17
- package/src/admin/AdminRouter.tsx +18 -1
- package/src/admin/atoms/adminUserAtom.ts +7 -0
- package/src/admin/components/AdminLayout.tsx +2 -14
- package/src/admin/components/jobs/AdminJobDashboard.tsx +51 -20
- package/src/admin/components/users/AdminUserLayout.tsx +84 -127
- package/src/admin/components/users/AdminUserProfile.tsx +5 -2
- package/src/admin/components/users/AdminUsers.tsx +1 -1
- package/src/core/components/Flex.tsx +24 -0
- package/src/core/components/buttons/ActionButton.tsx +1 -0
- package/src/core/components/dialogs/PromptDialog.tsx +1 -1
- package/src/core/components/layout/Breadcrumb.tsx +2 -2
- package/src/core/components/layout/DashboardShell.tsx +1 -1
- package/src/core/services/DialogService.tsx +2 -2
- package/src/core/styles.css +2 -1
- package/src/core/table/components/DataTable.tsx +0 -1
- package/dist/admin/AdminJobDashboard-BL8gGPDp.js.map +0 -1
- package/dist/admin/AdminLayout-I6TlUMPc.js.map +0 -1
- package/dist/admin/AdminUserLayout-lXT6I0Qq.js.map +0 -1
- package/dist/admin/AdminUserProfile-vFBLoJ3h.js.map +0 -1
- package/dist/admin/AdminUsers-D1UfGya9.js.map +0 -1
- package/dist/admin/core-CYaRQ8O-.js.map +0 -1
- package/dist/admin/rolldown-runtime-CjeV3_4I.js +0 -18
- package/dist/auth/core-D5jIAVF2.js.map +0 -1
- package/dist/auth/rolldown-runtime-CjeV3_4I.js +0 -18
- package/dist/demo/DemoLayout-CHU8WTwO.js.map +0 -1
- package/dist/demo/core-DFgB3yU4.js.map +0 -1
- package/dist/demo/rolldown-runtime-CjeV3_4I.js +0 -18
|
@@ -1,47 +1,19 @@
|
|
|
1
|
-
import { Flex,
|
|
2
|
-
import { Loader } from "@mantine/core";
|
|
1
|
+
import { Flex, useDialog, useToast } from "@alepha/ui";
|
|
3
2
|
import { IconBan, IconShieldCheck, IconTrash } from "@tabler/icons-react";
|
|
4
|
-
import { t } from "alepha";
|
|
5
3
|
import type { AdminUserController, UserEntity } from "alepha/api/users";
|
|
6
|
-
import { useClient } from "alepha/react";
|
|
7
|
-
import { NestedView, useRouter
|
|
8
|
-
import {
|
|
9
|
-
createContext,
|
|
10
|
-
useCallback,
|
|
11
|
-
useContext,
|
|
12
|
-
useEffect,
|
|
13
|
-
useState,
|
|
14
|
-
} from "react";
|
|
4
|
+
import { useAlepha, useClient, useStore } from "alepha/react";
|
|
5
|
+
import { NestedView, useRouter } from "alepha/react/router";
|
|
6
|
+
import { useCallback, useEffect } from "react";
|
|
15
7
|
import type { AdminRouter } from "../../AdminRouter.ts";
|
|
8
|
+
import { adminUserAtom } from "../../atoms/adminUserAtom.ts";
|
|
16
9
|
import AdminResourceHeader from "../shared/AdminResourceHeader.tsx";
|
|
17
10
|
import AdminResourceTabs from "../shared/AdminResourceTabs.tsx";
|
|
18
11
|
|
|
19
12
|
export interface AdminUserLayoutProps {
|
|
20
|
-
userRealmName?: string;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
interface UserContextValue {
|
|
24
13
|
user: UserEntity;
|
|
25
|
-
|
|
14
|
+
userRealmName?: string;
|
|
26
15
|
}
|
|
27
16
|
|
|
28
|
-
const UserContext = createContext<UserContextValue | null>(null);
|
|
29
|
-
|
|
30
|
-
export const useUser = () => {
|
|
31
|
-
const ctx = useContext(UserContext);
|
|
32
|
-
if (!ctx) throw new Error("useUser must be used within AdminUserLayout");
|
|
33
|
-
return ctx;
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
const updateUserSchema = t.object({
|
|
37
|
-
email: t.optional(t.email()),
|
|
38
|
-
phoneNumber: t.optional(t.e164()),
|
|
39
|
-
firstName: t.optional(t.string()),
|
|
40
|
-
lastName: t.optional(t.string()),
|
|
41
|
-
roles: t.optional(t.array(t.string())),
|
|
42
|
-
enabled: t.optional(t.boolean()),
|
|
43
|
-
});
|
|
44
|
-
|
|
45
17
|
const displayName = (u: UserEntity) =>
|
|
46
18
|
u.firstName || u.lastName
|
|
47
19
|
? `${u.firstName ?? ""} ${u.lastName ?? ""}`.trim()
|
|
@@ -49,63 +21,55 @@ const displayName = (u: UserEntity) =>
|
|
|
49
21
|
|
|
50
22
|
const AdminUserLayout = (props: AdminUserLayoutProps) => {
|
|
51
23
|
const router = useRouter<AdminRouter>();
|
|
52
|
-
const state = useRouterState();
|
|
53
24
|
const client = useClient<AdminUserController>();
|
|
25
|
+
const alepha = useAlepha();
|
|
54
26
|
const dialog = useDialog();
|
|
55
27
|
const toast = useToast();
|
|
56
|
-
const
|
|
28
|
+
const [user] = useStore(adminUserAtom);
|
|
57
29
|
|
|
58
|
-
|
|
59
|
-
|
|
30
|
+
useEffect(() => {
|
|
31
|
+
alepha.store.set(adminUserAtom, props.user);
|
|
32
|
+
}, [props.user]);
|
|
60
33
|
|
|
34
|
+
const currentUser = user ?? props.user;
|
|
35
|
+
const userId = currentUser.id;
|
|
61
36
|
const realmQuery = { userRealmName: props.userRealmName };
|
|
62
37
|
|
|
63
|
-
const
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
});
|
|
70
|
-
setUser(data);
|
|
71
|
-
} finally {
|
|
72
|
-
setLoading(false);
|
|
73
|
-
}
|
|
38
|
+
const reload = useCallback(async () => {
|
|
39
|
+
const data = await client.getUser({
|
|
40
|
+
params: { id: userId },
|
|
41
|
+
query: realmQuery,
|
|
42
|
+
});
|
|
43
|
+
alepha.store.set(adminUserAtom, data);
|
|
74
44
|
}, [userId, client]);
|
|
75
45
|
|
|
76
|
-
useEffect(() => {
|
|
77
|
-
loadUser();
|
|
78
|
-
}, [loadUser]);
|
|
79
|
-
|
|
80
46
|
const handleToggleEnabled = async () => {
|
|
81
|
-
|
|
82
|
-
const action = user.enabled ? "disable" : "enable";
|
|
47
|
+
const action = currentUser.enabled ? "disable" : "enable";
|
|
83
48
|
const confirmed = await dialog.confirm({
|
|
84
|
-
title: `${
|
|
85
|
-
message: `Are you sure you want to ${action} ${displayName(
|
|
49
|
+
title: `${currentUser.enabled ? "Disable" : "Enable"} User`,
|
|
50
|
+
message: `Are you sure you want to ${action} ${displayName(currentUser)}?`,
|
|
86
51
|
});
|
|
87
52
|
if (confirmed) {
|
|
88
53
|
const updated = await client.updateUser({
|
|
89
|
-
params: { id:
|
|
54
|
+
params: { id: currentUser.id },
|
|
90
55
|
query: realmQuery,
|
|
91
|
-
body: { enabled: !
|
|
56
|
+
body: { enabled: !currentUser.enabled },
|
|
92
57
|
});
|
|
93
|
-
|
|
58
|
+
alepha.store.set(adminUserAtom, updated);
|
|
94
59
|
toast.success({ title: `User ${action}d` });
|
|
95
60
|
}
|
|
96
61
|
};
|
|
97
62
|
|
|
98
63
|
const handleDelete = async () => {
|
|
99
|
-
if (!user) return;
|
|
100
64
|
const confirmed = await dialog.confirm({
|
|
101
65
|
title: "Delete User",
|
|
102
|
-
message: `Are you sure you want to delete ${displayName(
|
|
66
|
+
message: `Are you sure you want to delete ${displayName(currentUser)}? This cannot be undone.`,
|
|
103
67
|
confirmColor: "red",
|
|
104
68
|
confirmLabel: "Delete",
|
|
105
69
|
});
|
|
106
70
|
if (confirmed) {
|
|
107
71
|
await client.deleteUser({
|
|
108
|
-
params: { id:
|
|
72
|
+
params: { id: currentUser.id },
|
|
109
73
|
query: realmQuery,
|
|
110
74
|
});
|
|
111
75
|
toast.success({ title: "User deleted" });
|
|
@@ -113,71 +77,64 @@ const AdminUserLayout = (props: AdminUserLayoutProps) => {
|
|
|
113
77
|
}
|
|
114
78
|
};
|
|
115
79
|
|
|
116
|
-
if (loading) {
|
|
117
|
-
return (
|
|
118
|
-
<Flex flex={1} justify="center" align="center">
|
|
119
|
-
<Loader />
|
|
120
|
-
</Flex>
|
|
121
|
-
);
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
if (!user) {
|
|
125
|
-
return (
|
|
126
|
-
<Flex flex={1} justify="center" align="center">
|
|
127
|
-
<Text c="dimmed">User not found</Text>
|
|
128
|
-
</Flex>
|
|
129
|
-
);
|
|
130
|
-
}
|
|
131
|
-
|
|
132
80
|
return (
|
|
133
|
-
<
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
81
|
+
<Flex
|
|
82
|
+
flex={1}
|
|
83
|
+
direction="column"
|
|
84
|
+
gap="lg"
|
|
85
|
+
p="md"
|
|
86
|
+
m="md"
|
|
87
|
+
style={{
|
|
88
|
+
backgroundColor: "var(--mantine-color-body)",
|
|
89
|
+
borderRadius: "var(--mantine-radius-md)",
|
|
90
|
+
border: "1px solid var(--mantine-color-default-border)",
|
|
91
|
+
}}
|
|
92
|
+
>
|
|
93
|
+
<AdminResourceHeader
|
|
94
|
+
backHref={router.path("adminUsers")}
|
|
95
|
+
backLabel="Users"
|
|
96
|
+
title={displayName(currentUser)}
|
|
97
|
+
subtitle={currentUser.email || currentUser.username}
|
|
98
|
+
status={{
|
|
99
|
+
label: currentUser.enabled ? "Active" : "Disabled",
|
|
100
|
+
color: currentUser.enabled ? "green" : "gray",
|
|
101
|
+
}}
|
|
102
|
+
menuActions={[
|
|
103
|
+
{
|
|
104
|
+
label: currentUser.enabled ? "Disable" : "Enable",
|
|
105
|
+
icon: currentUser.enabled ? IconBan : IconShieldCheck,
|
|
106
|
+
onClick: handleToggleEnabled,
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
label: "Delete",
|
|
110
|
+
icon: IconTrash,
|
|
111
|
+
color: "red",
|
|
112
|
+
onClick: handleDelete,
|
|
113
|
+
},
|
|
114
|
+
]}
|
|
115
|
+
/>
|
|
116
|
+
|
|
117
|
+
<AdminResourceTabs
|
|
118
|
+
tabs={[
|
|
119
|
+
{
|
|
120
|
+
value: "profile",
|
|
121
|
+
label: "Profile",
|
|
122
|
+
href: router.path("adminUserProfile", {
|
|
123
|
+
params: { userId },
|
|
124
|
+
}),
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
value: "sessions",
|
|
128
|
+
label: "Sessions",
|
|
129
|
+
href: router.path("adminUserSessions", {
|
|
130
|
+
params: { userId },
|
|
131
|
+
}),
|
|
132
|
+
},
|
|
133
|
+
]}
|
|
134
|
+
/>
|
|
135
|
+
|
|
136
|
+
<NestedView />
|
|
137
|
+
</Flex>
|
|
181
138
|
);
|
|
182
139
|
};
|
|
183
140
|
|
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
import { DetailList, Flex } from "@alepha/ui";
|
|
2
2
|
import { Badge } from "@mantine/core";
|
|
3
|
+
import { useStore } from "alepha/react";
|
|
3
4
|
import { useI18n } from "alepha/react/i18n";
|
|
4
|
-
import {
|
|
5
|
+
import { adminUserAtom } from "../../atoms/adminUserAtom.ts";
|
|
5
6
|
|
|
6
7
|
const AdminUserProfile = () => {
|
|
7
|
-
const
|
|
8
|
+
const [user] = useStore(adminUserAtom);
|
|
8
9
|
const { l } = useI18n();
|
|
9
10
|
|
|
11
|
+
if (!user) return null;
|
|
12
|
+
|
|
10
13
|
return (
|
|
11
14
|
<DetailList
|
|
12
15
|
items={[
|
|
@@ -2,9 +2,17 @@ import {
|
|
|
2
2
|
Flex as MantineFlex,
|
|
3
3
|
type FlexProps as MantineFlexProps,
|
|
4
4
|
} from "@mantine/core";
|
|
5
|
+
import { FormModel } from "alepha/react/form";
|
|
6
|
+
import type { FormHTMLAttributes } from "react";
|
|
5
7
|
import { forwardRef } from "react";
|
|
6
8
|
|
|
7
9
|
export interface FlexProps extends MantineFlexProps {
|
|
10
|
+
/**
|
|
11
|
+
* Render as a `<form>` element.
|
|
12
|
+
* If `true`, renders as a plain form.
|
|
13
|
+
* If an object, spreads the form attributes (onSubmit, id, noValidate, etc.).
|
|
14
|
+
*/
|
|
15
|
+
form?: boolean | FormHTMLAttributes<HTMLFormElement> | FormModel<any>;
|
|
8
16
|
/**
|
|
9
17
|
* flex: 1 — fill available space.
|
|
10
18
|
*/
|
|
@@ -92,6 +100,7 @@ const Flex = forwardRef<HTMLDivElement, FlexProps>((props, ref) => {
|
|
|
92
100
|
borderedBottom,
|
|
93
101
|
shadowed,
|
|
94
102
|
overflow,
|
|
103
|
+
form,
|
|
95
104
|
...rest
|
|
96
105
|
} = props;
|
|
97
106
|
|
|
@@ -155,6 +164,21 @@ const Flex = forwardRef<HTMLDivElement, FlexProps>((props, ref) => {
|
|
|
155
164
|
rest.className = `${rest.className ?? ""} overflow-auto`.trim();
|
|
156
165
|
}
|
|
157
166
|
|
|
167
|
+
if (form) {
|
|
168
|
+
let formProps: any = typeof form === "object" ? form : {};
|
|
169
|
+
if (formProps instanceof FormModel) {
|
|
170
|
+
formProps = formProps.props;
|
|
171
|
+
}
|
|
172
|
+
return (
|
|
173
|
+
<MantineFlex
|
|
174
|
+
ref={ref}
|
|
175
|
+
component={"form"}
|
|
176
|
+
{...(formProps as any)}
|
|
177
|
+
{...rest}
|
|
178
|
+
/>
|
|
179
|
+
);
|
|
180
|
+
}
|
|
181
|
+
|
|
158
182
|
return <MantineFlex ref={ref} {...rest} />;
|
|
159
183
|
});
|
|
160
184
|
|
|
@@ -36,7 +36,7 @@ const PromptDialog = ({ options, onSubmit }: PromptDialogProps) => {
|
|
|
36
36
|
required={options?.required}
|
|
37
37
|
mb="md"
|
|
38
38
|
/>
|
|
39
|
-
<Flex justify="flex-end">
|
|
39
|
+
<Flex justify="flex-end" gap={"xs"}>
|
|
40
40
|
<Button variant="subtle" onClick={() => onSubmit(null)}>
|
|
41
41
|
{options?.cancelLabel || "Cancel"}
|
|
42
42
|
</Button>
|
|
@@ -34,7 +34,7 @@ export interface BreadcrumbProps extends FlexProps {
|
|
|
34
34
|
* Pages should define a `label` in their `$page()` options for best results.
|
|
35
35
|
* Falls back to the page name converted to Title Case.
|
|
36
36
|
*/
|
|
37
|
-
const
|
|
37
|
+
const Breadcrumbs = (props: BreadcrumbProps) => {
|
|
38
38
|
const { home = "Home", separator, size = "sm", ...groupProps } = props;
|
|
39
39
|
|
|
40
40
|
const state = useRouterState();
|
|
@@ -85,4 +85,4 @@ const Breadcrumb = (props: BreadcrumbProps) => {
|
|
|
85
85
|
);
|
|
86
86
|
};
|
|
87
87
|
|
|
88
|
-
export default
|
|
88
|
+
export default Breadcrumbs;
|
|
@@ -69,7 +69,7 @@ export interface DashboardShellProps {
|
|
|
69
69
|
container?: boolean | ContainerProps;
|
|
70
70
|
|
|
71
71
|
/**
|
|
72
|
-
* If true, the DashboardShell will fill the height
|
|
72
|
+
* If true, the DashboardShell will fill the height with its container.
|
|
73
73
|
*/
|
|
74
74
|
fill?: boolean;
|
|
75
75
|
}
|
|
@@ -117,8 +117,8 @@ export class DialogService {
|
|
|
117
117
|
<ConfirmDialog
|
|
118
118
|
options={options}
|
|
119
119
|
onConfirm={(confirmed) => {
|
|
120
|
-
this.close(modalId);
|
|
121
120
|
done(confirmed);
|
|
121
|
+
this.close(modalId);
|
|
122
122
|
}}
|
|
123
123
|
/>
|
|
124
124
|
),
|
|
@@ -147,8 +147,8 @@ export class DialogService {
|
|
|
147
147
|
<PromptDialog
|
|
148
148
|
options={options}
|
|
149
149
|
onSubmit={(value) => {
|
|
150
|
-
this.close(modalId);
|
|
151
150
|
done(value);
|
|
151
|
+
this.close(modalId);
|
|
152
152
|
}}
|
|
153
153
|
/>
|
|
154
154
|
),
|
package/src/core/styles.css
CHANGED
|
@@ -112,7 +112,8 @@
|
|
|
112
112
|
|
|
113
113
|
/* ------------------------------------------------------------------------------------------------------------------ */
|
|
114
114
|
/* our custom "minimal" variant for <Button/> - from Blueprint */
|
|
115
|
-
.mantine-Button-root[data-variant="minimal"]
|
|
115
|
+
.mantine-Button-root[data-variant="minimal"],
|
|
116
|
+
.mantine-Anchor-root[data-variant="minimal"] {
|
|
116
117
|
background-color: transparent;
|
|
117
118
|
color: var(--mantine-color-text);
|
|
118
119
|
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"AdminJobDashboard-BL8gGPDp.js","names":["Flex","Text"],"sources":["../../src/admin/components/jobs/AdminJobDashboard.tsx"],"sourcesContent":["import { ActionButton, Flex, StatCards, Text, useToast } from \"@alepha/ui\";\nimport { AreaChart, BarChart, DonutChart } from \"@mantine/charts\";\nimport { Paper, SimpleGrid, Table } from \"@mantine/core\";\nimport {\n IconAlertTriangle,\n IconCircleCheck,\n IconPlayerPlay,\n IconRefresh,\n IconTerminal2,\n} from \"@tabler/icons-react\";\nimport type {\n AdminJobController,\n JobActivityPoint,\n JobFailure,\n JobQueueDepth,\n JobStats,\n} from \"alepha/api/jobs\";\nimport { useClient } from \"alepha/react\";\nimport { useI18n } from \"alepha/react/i18n\";\nimport { useCallback, useEffect, useState } from \"react\";\n\n// ─────────────────────────────────────────────────────────────────────────────\n\nconst formatDuration = (\n start: Date | string,\n end?: Date | string | null,\n): string => {\n const startTime = new Date(start).getTime();\n const endTime = end ? new Date(end).getTime() : Date.now();\n const duration = endTime - startTime;\n\n if (duration < 1000) return `${duration}ms`;\n if (duration < 60000) return `${(duration / 1000).toFixed(1)}s`;\n if (duration < 3600000)\n return `${Math.floor(duration / 60000)}m ${Math.floor((duration % 60000) / 1000)}s`;\n return `${Math.floor(duration / 3600000)}h ${Math.floor((duration % 3600000) / 60000)}m`;\n};\n\n// ─────────────────────────────────────────────────────────────────────────────\n\ninterface RecentExecution {\n id: string;\n jobName: string;\n status: string;\n startedAt?: string;\n completedAt?: string;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n\nconst AdminJobDashboard = () => {\n const client = useClient<AdminJobController>();\n const { l } = useI18n();\n const toast = useToast();\n\n const [stats, setStats] = useState<JobStats | null>(null);\n const [recent, setRecent] = useState<RecentExecution[]>([]);\n const [failures, setFailures] = useState<JobFailure[]>([]);\n const [activity, setActivity] = useState<JobActivityPoint[]>([]);\n const [queueDepth, setQueueDepth] = useState<JobQueueDepth[]>([]);\n\n const loadData = useCallback(async () => {\n try {\n const [statsData, recentData, failureData, activityData, queueData] =\n await Promise.all([\n client.getJobStats(),\n client.findJobExecutions({ query: { sort: \"-createdAt\", size: 10 } }),\n client.getJobTopFailures(),\n client.getJobActivity({ query: { days: 14 } }),\n client.getJobQueueDepth(),\n ]);\n setStats(statsData);\n setRecent(recentData.content as RecentExecution[]);\n setFailures(failureData);\n setActivity(activityData);\n setQueueDepth(queueData);\n } catch {\n toast.danger(\"Failed to load dashboard data\");\n }\n }, [client, toast]);\n\n useEffect(() => {\n loadData();\n }, [loadData]);\n\n // Prepare chart data\n const activityChartData = activity.map((point) => ({\n date: new Date(point.date).toLocaleDateString(\"en-US\", {\n month: \"short\",\n day: \"numeric\",\n }),\n completed: point.completed,\n failed: point.failed,\n }));\n\n const queueChartData = queueDepth\n .filter(\n (q) => q.pending + q.running + q.scheduled + q.retrying + q.dead > 0,\n )\n .map((q) => ({\n job:\n q.jobName.length > 20\n ? `...${q.jobName.slice(q.jobName.length - 18)}`\n : q.jobName,\n pending: q.pending,\n running: q.running,\n scheduled: q.scheduled,\n retrying: q.retrying,\n dead: q.dead,\n }));\n\n const statusDonutData = stats\n ? [\n { name: \"Running\", value: stats.running, color: \"blue\" },\n { name: \"Pending\", value: stats.pending, color: \"gray\" },\n { name: \"Scheduled\", value: stats.scheduled, color: \"violet\" },\n { name: \"Retrying\", value: stats.retrying, color: \"yellow\" },\n { name: \"Dead\", value: stats.dead, color: \"red\" },\n ].filter((d) => d.value > 0)\n : [];\n\n return (\n <Flex flex={1} direction=\"column\" gap=\"md\" p=\"md\">\n <Flex justify=\"space-between\" align=\"center\">\n <Text size=\"lg\" fw={600}>\n Jobs Dashboard\n </Text>\n <ActionButton\n tooltip=\"Refresh\"\n variant=\"light\"\n size=\"sm\"\n icon={IconRefresh}\n onClick={loadData}\n />\n </Flex>\n\n {/* Stats Cards */}\n {stats && (\n <StatCards\n items={[\n {\n label: \"Registered\",\n value: stats.registered,\n icon: IconTerminal2,\n },\n {\n label: \"Running\",\n value: stats.running,\n icon: IconPlayerPlay,\n },\n {\n label: \"Completed 24h\",\n value: stats.completed24h,\n icon: IconCircleCheck,\n },\n {\n label: \"Failed 24h\",\n value: stats.failed24h,\n icon: IconAlertTriangle,\n },\n ]}\n />\n )}\n\n {/* Charts Row */}\n <SimpleGrid cols={{ base: 1, md: 2 }} spacing=\"md\">\n {/* Activity Timeline */}\n <Paper p=\"md\" radius=\"md\" withBorder>\n <Text size=\"sm\" fw={600} mb=\"sm\">\n Activity (14d)\n </Text>\n {activityChartData.length > 0 ? (\n <AreaChart\n h={220}\n data={activityChartData}\n dataKey=\"date\"\n series={[\n { name: \"completed\", label: \"Completed\", color: \"teal.6\" },\n { name: \"failed\", label: \"Failed\", color: \"red.6\" },\n ]}\n curveType=\"monotone\"\n withGradient\n withTooltip\n withDots={false}\n />\n ) : (\n <Flex h={220} align=\"center\" justify=\"center\">\n <Text size=\"sm\" c=\"dimmed\">\n No activity data\n </Text>\n </Flex>\n )}\n </Paper>\n\n {/* Current Status Donut */}\n <Paper p=\"md\" radius=\"md\" withBorder>\n <Text size=\"sm\" fw={600} mb=\"sm\">\n Active Executions\n </Text>\n {statusDonutData.length > 0 ? (\n <DonutChart\n h={220}\n data={statusDonutData}\n withTooltip\n tooltipDataSource=\"segment\"\n chartLabel={String(\n statusDonutData.reduce((sum, d) => sum + d.value, 0),\n )}\n />\n ) : (\n <Flex h={220} align=\"center\" justify=\"center\">\n <Text size=\"sm\" c=\"dimmed\">\n No active executions\n </Text>\n </Flex>\n )}\n </Paper>\n </SimpleGrid>\n\n {/* Queue Depth Chart */}\n {queueChartData.length > 0 && (\n <Paper p=\"md\" radius=\"md\" withBorder>\n <Text size=\"sm\" fw={600} mb=\"sm\">\n Queue Depth by Job\n </Text>\n <BarChart\n h={200}\n data={queueChartData}\n dataKey=\"job\"\n type=\"stacked\"\n series={[\n { name: \"running\", label: \"Running\", color: \"blue.6\" },\n { name: \"pending\", label: \"Pending\", color: \"gray.5\" },\n { name: \"scheduled\", label: \"Scheduled\", color: \"violet.5\" },\n { name: \"retrying\", label: \"Retrying\", color: \"yellow.5\" },\n { name: \"dead\", label: \"Dead\", color: \"red.6\" },\n ]}\n withTooltip\n withLegend\n />\n </Paper>\n )}\n\n {/* Recent Activity + Top Failures side by side */}\n <SimpleGrid cols={{ base: 1, md: 2 }} spacing=\"md\">\n {/* Recent Activity */}\n <Paper p=\"md\" radius=\"md\" withBorder>\n <Text size=\"sm\" fw={600} mb=\"sm\">\n Recent Executions\n </Text>\n <Table highlightOnHover>\n <Table.Thead>\n <Table.Tr>\n <Table.Th>Job</Table.Th>\n <Table.Th>Status</Table.Th>\n <Table.Th>Duration</Table.Th>\n </Table.Tr>\n </Table.Thead>\n <Table.Tbody>\n {recent.map((exec) => (\n <Table.Tr key={exec.id}>\n <Table.Td>\n <Text size=\"xs\" fw={500} ff=\"monospace\" lineClamp={1}>\n {exec.jobName}\n </Text>\n </Table.Td>\n <Table.Td>\n <Text size=\"xs\" ff=\"monospace\">\n {exec.status}\n </Text>\n </Table.Td>\n <Table.Td>\n <Text size=\"xs\" c=\"dimmed\" ff=\"monospace\">\n {exec.startedAt &&\n (exec.completedAt || exec.status === \"running\")\n ? formatDuration(exec.startedAt, exec.completedAt)\n : \"\\u2014\"}\n </Text>\n </Table.Td>\n </Table.Tr>\n ))}\n {recent.length === 0 && (\n <Table.Tr>\n <Table.Td colSpan={3}>\n <Text size=\"sm\" c=\"dimmed\" ta=\"center\">\n No recent executions\n </Text>\n </Table.Td>\n </Table.Tr>\n )}\n </Table.Tbody>\n </Table>\n </Paper>\n\n {/* Top Failures (7d) */}\n <Paper p=\"md\" radius=\"md\" withBorder>\n <Text size=\"sm\" fw={600} mb=\"sm\">\n Top Failures (7d)\n </Text>\n {failures.length > 0 ? (\n <Table>\n <Table.Thead>\n <Table.Tr>\n <Table.Th>Job</Table.Th>\n <Table.Th>Count</Table.Th>\n <Table.Th>Last Error</Table.Th>\n </Table.Tr>\n </Table.Thead>\n <Table.Tbody>\n {failures.map((f) => (\n <Table.Tr key={f.jobName}>\n <Table.Td>\n <Text size=\"xs\" fw={500} ff=\"monospace\" lineClamp={1}>\n {f.jobName}\n </Text>\n </Table.Td>\n <Table.Td>\n <Text size=\"xs\" fw={600} ff=\"monospace\">\n {f.failures}\n </Text>\n </Table.Td>\n <Table.Td>\n <Text\n size=\"xs\"\n c=\"dimmed\"\n lineClamp={1}\n style={{ maxWidth: 200 }}\n >\n {f.lastError ?? \"\\u2014\"}\n </Text>\n </Table.Td>\n </Table.Tr>\n ))}\n </Table.Tbody>\n </Table>\n ) : (\n <Flex h={100} align=\"center\" justify=\"center\">\n <Text size=\"sm\" c=\"dimmed\">\n No failures in the last 7 days\n </Text>\n </Flex>\n )}\n </Paper>\n </SimpleGrid>\n </Flex>\n );\n};\n\nexport default AdminJobDashboard;\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"AdminLayout-I6TlUMPc.js","names":["Text","SidebarCollapseButton"],"sources":["../../src/admin/components/AdminLayout.tsx"],"sourcesContent":["import {\n ActionButton,\n AlephaMantineProvider,\n DashboardShell,\n type DashboardShellProps,\n SidebarCollapseButton,\n Text,\n ui,\n} from \"@alepha/ui\";\nimport { Flex, Image } from \"@mantine/core\";\nimport { IconArrowLeft } from \"@tabler/icons-react\";\n\nexport interface AdminLayoutProps {\n adminShellProps?: DashboardShellProps;\n}\n\nconst AdminLayout = (props: AdminLayoutProps) => {\n return (\n <AlephaMantineProvider\n mantine={{\n theme: {\n components: {\n Button: {\n defaultProps: {\n fw: 400,\n },\n },\n },\n },\n }}\n >\n <DashboardShell\n layout={\"alt\"}\n navbarHeader={(props) => (\n <Flex gap={\"md\"} flex={1} px={\"lg\"} align={\"center\"}>\n <ActionButton\n href={\"/\"}\n variant={\"default\"}\n bd={0}\n icon={IconArrowLeft}\n />\n {!props.collapsed && (\n <>\n <Image pt={4} src={\"/favicon.svg\"} h={36} w={36} />\n <Flex direction={\"column\"}>\n <Text bold>Blog</Text>\n <Text small muted mt={-4}>\n Admin Panel\n </Text>\n </Flex>\n </>\n )}\n </Flex>\n )}\n footerHeight={48}\n navbarFooter={\n <Flex flex={1} px={\"lg\"} align={\"center\"}>\n <SidebarCollapseButton\n c={\"gray\"}\n size={\"xs\"}\n iconSize={ui.sizes.icon.sm}\n p={8}\n bd={0}\n />\n </Flex>\n }\n sidebarProps={{\n autoPopulateMenu: {\n startsWith: \"/admin\",\n },\n }}\n {...props.adminShellProps}\n />\n </AlephaMantineProvider>\n );\n};\n\nexport default AdminLayout;\n"],"mappings":";;;;;;AAgBA,MAAM,eAAe,UAA4B;AAC/C,QACE,oBAAC;EACC,SAAS,EACP,OAAO,EACL,YAAY,EACV,QAAQ,EACN,cAAc,EACZ,IAAI,KACL,EACF,EACF,EACF,EACF;YAED,oBAAC;GACC,QAAQ;GACR,eAAe,UACb,qBAAC;IAAK,KAAK;IAAM,MAAM;IAAG,IAAI;IAAM,OAAO;eACzC,oBAAC;KACC,MAAM;KACN,SAAS;KACT,IAAI;KACJ,MAAM;MACN,EACD,CAAC,MAAM,aACN,4CACE,oBAAC;KAAM,IAAI;KAAG,KAAK;KAAgB,GAAG;KAAI,GAAG;MAAM,EACnD,qBAAC;KAAK,WAAW;gBACf,oBAACA;MAAK;gBAAK;OAAW,EACtB,oBAACA;MAAK;MAAM;MAAM,IAAI;gBAAI;OAEnB;MACF,IACN;KAEA;GAET,cAAc;GACd,cACE,oBAAC;IAAK,MAAM;IAAG,IAAI;IAAM,OAAO;cAC9B,oBAACC;KACC,GAAG;KACH,MAAM;KACN,UAAU,GAAG,MAAM,KAAK;KACxB,GAAG;KACH,IAAI;MACJ;KACG;GAET,cAAc,EACZ,kBAAkB,EAChB,YAAY,UACb,EACF;GACD,GAAI,MAAM;IACV;GACoB"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"AdminUserLayout-lXT6I0Qq.js","names":["Flex","Text"],"sources":["../../src/admin/components/shared/AdminResourceHeaderMenuItem.tsx","../../src/admin/components/shared/AdminResourceHeader.tsx","../../src/admin/components/shared/AdminResourceTabsItem.tsx","../../src/admin/components/shared/AdminResourceTabs.tsx","../../src/admin/components/users/AdminUserLayout.tsx"],"sourcesContent":["import { Menu } from \"@mantine/core\";\nimport { useRouter } from \"alepha/react/router\";\nimport type { AdminResourceAction } from \"./AdminResourceHeader.tsx\";\n\nexport interface AdminResourceHeaderMenuItemProps {\n /**\n * Action configuration\n */\n action: AdminResourceAction;\n}\n\nconst AdminResourceHeaderMenuItem = (\n props: AdminResourceHeaderMenuItemProps,\n) => {\n const { action } = props;\n const router = useRouter();\n\n const menuItemProps: Record<string, unknown> = {};\n if (action.href) {\n Object.assign(menuItemProps, router.anchor(action.href));\n } else if (action.onClick) {\n menuItemProps.onClick = action.onClick;\n }\n\n return (\n <Menu.Item\n leftSection={action.icon ? <action.icon size={16} /> : undefined}\n color={action.color}\n disabled={action.disabled}\n {...menuItemProps}\n >\n {action.label}\n </Menu.Item>\n );\n};\n\nexport default AdminResourceHeaderMenuItem;\n","import { ActionButton } from \"@alepha/ui\";\nimport {\n ActionIcon,\n Avatar,\n Badge,\n Button,\n Flex,\n Menu,\n Text,\n Tooltip,\n} from \"@mantine/core\";\nimport {\n IconChevronDown,\n IconChevronLeft,\n IconExternalLink,\n} from \"@tabler/icons-react\";\nimport type { ComponentType, ReactNode } from \"react\";\nimport AdminResourceHeaderMenuItem from \"./AdminResourceHeaderMenuItem.tsx\";\n\nexport interface AdminResourceAction {\n label: string;\n icon?: ComponentType<{ size?: number }>;\n onClick?: () => void;\n href?: string;\n color?: string;\n disabled?: boolean;\n loading?: boolean;\n variant?: \"filled\" | \"light\" | \"outline\" | \"subtle\";\n}\n\nexport interface AdminResourceHeaderProps {\n /**\n * Back navigation URL\n */\n backHref?: string;\n\n /**\n * Back navigation label\n */\n backLabel?: string;\n\n /**\n * Avatar content (letter, image URL, or custom node)\n */\n avatar?: string | ReactNode;\n\n /**\n * Avatar color\n */\n avatarColor?: string;\n\n /**\n * Resource title (e.g., user name)\n */\n title: string;\n\n /**\n * Secondary text (e.g., email)\n */\n subtitle?: string;\n\n /**\n * Tertiary identifier to copy (e.g., user ID)\n */\n identifier?: string;\n\n /**\n * Label for the identifier tooltip\n */\n identifierLabel?: string;\n\n /**\n * Status badge\n */\n status?: {\n label: string;\n color: \"green\" | \"red\" | \"yellow\" | \"blue\" | \"gray\";\n };\n\n /**\n * Additional badges (e.g., roles)\n */\n badges?: Array<{\n label: string;\n color?: string;\n variant?: \"filled\" | \"light\" | \"outline\" | \"dot\";\n }>;\n\n /**\n * Primary action button\n */\n primaryAction?: AdminResourceAction;\n\n /**\n * Menu actions (shown in dropdown)\n */\n menuActions?: AdminResourceAction[];\n\n /**\n * External link URL\n */\n externalUrl?: string;\n\n /**\n * Loading state\n */\n loading?: boolean;\n}\n\nconst AdminResourceHeader = (props: AdminResourceHeaderProps) => {\n const {\n backHref,\n backLabel = \"Back\",\n avatar,\n avatarColor = \"blue\",\n title,\n subtitle,\n identifier,\n identifierLabel = \"ID\",\n status,\n badges = [],\n primaryAction,\n menuActions = [],\n externalUrl,\n } = props;\n\n const renderAvatar = () => {\n if (typeof avatar === \"string\") {\n if (avatar.startsWith(\"http\") || avatar.startsWith(\"/\")) {\n return (\n <Avatar src={avatar} size={56} radius=\"md\" color={avatarColor} />\n );\n }\n return (\n <Avatar size={56} radius=\"md\" color={avatarColor}>\n {avatar}\n </Avatar>\n );\n }\n if (avatar) {\n return avatar;\n }\n return (\n <Avatar size={56} radius=\"md\" color={avatarColor}>\n {title.charAt(0).toUpperCase()}\n </Avatar>\n );\n };\n\n return (\n <Flex direction=\"column\" gap=\"xs\">\n {/* Breadcrumb / Back navigation */}\n {backHref && (\n <Flex>\n <ActionButton\n variant=\"subtle\"\n size=\"xs\"\n href={backHref}\n leftSection={<IconChevronLeft size={14} />}\n c=\"dimmed\"\n >\n {backLabel}\n </ActionButton>\n </Flex>\n )}\n\n {/* Main header */}\n <Flex justify=\"space-between\" align=\"flex-start\" wrap=\"nowrap\">\n {/* Left: Avatar + Info */}\n <Flex gap=\"md\" wrap=\"nowrap\">\n {renderAvatar()}\n\n <Flex\n direction=\"column\"\n gap={2}\n justify=\"center\"\n style={{ minHeight: 56 }}\n >\n {/* Title row */}\n <Flex gap=\"xs\" align=\"center\">\n <Text size=\"md\" fw={600} lh={1.2}>\n {title}\n </Text>\n {status && (\n <Badge\n size=\"xs\"\n variant=\"light\"\n color={status.color}\n tt=\"lowercase\"\n >\n {status.label}\n </Badge>\n )}\n </Flex>\n\n {/* Subtitle */}\n {subtitle && (\n <Text size=\"xs\" c=\"dimmed\">\n {subtitle}\n </Text>\n )}\n </Flex>\n </Flex>\n\n {/* Right: Actions */}\n <Flex gap=\"xs\">\n {externalUrl && (\n <Tooltip label=\"Open in new tab\" openDelay={500}>\n <ActionIcon\n variant=\"subtle\"\n color=\"gray\"\n component=\"a\"\n href={externalUrl}\n target=\"_blank\"\n >\n <IconExternalLink size={18} />\n </ActionIcon>\n </Tooltip>\n )}\n\n {primaryAction && (\n <ActionButton\n variant={primaryAction.variant ?? \"light\"}\n color={primaryAction.color}\n onClick={primaryAction.onClick}\n href={primaryAction.href}\n loading={primaryAction.loading}\n disabled={primaryAction.disabled}\n leftSection={\n primaryAction.icon ? (\n <primaryAction.icon size={16} />\n ) : undefined\n }\n >\n {primaryAction.label}\n </ActionButton>\n )}\n\n {menuActions.length > 0 && (\n <Menu position=\"bottom-end\" shadow=\"md\" width={220}>\n <Menu.Target>\n <Button\n variant=\"default\"\n rightSection={<IconChevronDown size={16} />}\n >\n Actions\n </Button>\n </Menu.Target>\n <Menu.Dropdown>\n {menuActions.map((action, index) => (\n <AdminResourceHeaderMenuItem key={index} action={action} />\n ))}\n </Menu.Dropdown>\n </Menu>\n )}\n </Flex>\n </Flex>\n </Flex>\n );\n};\n\nexport default AdminResourceHeader;\n","import { Tabs } from \"@mantine/core\";\nimport { useActive, useRouter } from \"alepha/react/router\";\nimport type { AdminResourceTab } from \"./AdminResourceTabs.tsx\";\n\nexport interface AdminResourceTabsItemProps {\n /**\n * Tab configuration\n */\n tab: AdminResourceTab;\n}\n\nconst AdminResourceTabsItem = (props: AdminResourceTabsItemProps) => {\n const { tab } = props;\n const router = useRouter();\n const { isActive, isPending } = useActive({ href: tab.href });\n const anchorProps = router.anchor(tab.href);\n\n return (\n <Tabs.Tab\n value={tab.value}\n component=\"a\"\n leftSection={tab.icon ? <tab.icon size={16} /> : undefined}\n disabled={tab.disabled}\n data-active={isActive || undefined}\n style={{\n opacity: isPending ? 0.6 : 1,\n }}\n {...anchorProps}\n >\n {tab.label}\n {tab.count !== undefined && tab.count > 0 && ` (${tab.count})`}\n </Tabs.Tab>\n );\n};\n\nexport default AdminResourceTabsItem;\n","import { Tabs } from \"@mantine/core\";\nimport type { ComponentType, ReactNode } from \"react\";\nimport AdminResourceTabsItem from \"./AdminResourceTabsItem.tsx\";\n\nexport interface AdminResourceTab {\n /**\n * Tab key/value\n */\n value: string;\n\n /**\n * Tab label\n */\n label: string;\n\n /**\n * Tab icon\n */\n icon?: ComponentType<{ size?: number }>;\n\n /**\n * Navigation href\n */\n href: string;\n\n /**\n * Whether tab is disabled\n */\n disabled?: boolean;\n\n /**\n * Badge count to show\n */\n count?: number;\n}\n\nexport interface AdminResourceTabsProps {\n /**\n * Array of tab configurations\n */\n tabs: AdminResourceTab[];\n\n /**\n * Currently active tab value\n */\n activeTab?: string;\n\n /**\n * Content to render below tabs\n */\n children?: ReactNode;\n}\n\nconst AdminResourceTabs = (props: AdminResourceTabsProps) => {\n const { tabs, activeTab, children } = props;\n\n return (\n <Tabs value={activeTab} variant=\"default\">\n <Tabs.List>\n {tabs.map((tab) => (\n <AdminResourceTabsItem key={tab.value} tab={tab} />\n ))}\n </Tabs.List>\n\n {children}\n </Tabs>\n );\n};\n\nexport default AdminResourceTabs;\n","import { Flex, Text, useDialog, useToast } from \"@alepha/ui\";\nimport { Loader } from \"@mantine/core\";\nimport { IconBan, IconShieldCheck, IconTrash } from \"@tabler/icons-react\";\nimport { t } from \"alepha\";\nimport type { AdminUserController, UserEntity } from \"alepha/api/users\";\nimport { useClient } from \"alepha/react\";\nimport { NestedView, useRouter, useRouterState } from \"alepha/react/router\";\nimport {\n createContext,\n useCallback,\n useContext,\n useEffect,\n useState,\n} from \"react\";\nimport type { AdminRouter } from \"../../AdminRouter.ts\";\nimport AdminResourceHeader from \"../shared/AdminResourceHeader.tsx\";\nimport AdminResourceTabs from \"../shared/AdminResourceTabs.tsx\";\n\nexport interface AdminUserLayoutProps {\n userRealmName?: string;\n}\n\ninterface UserContextValue {\n user: UserEntity;\n reload: () => void;\n}\n\nconst UserContext = createContext<UserContextValue | null>(null);\n\nexport const useUser = () => {\n const ctx = useContext(UserContext);\n if (!ctx) throw new Error(\"useUser must be used within AdminUserLayout\");\n return ctx;\n};\n\nconst updateUserSchema = t.object({\n email: t.optional(t.email()),\n phoneNumber: t.optional(t.e164()),\n firstName: t.optional(t.string()),\n lastName: t.optional(t.string()),\n roles: t.optional(t.array(t.string())),\n enabled: t.optional(t.boolean()),\n});\n\nconst displayName = (u: UserEntity) =>\n u.firstName || u.lastName\n ? `${u.firstName ?? \"\"} ${u.lastName ?? \"\"}`.trim()\n : u.username || u.email || \"User\";\n\nconst AdminUserLayout = (props: AdminUserLayoutProps) => {\n const router = useRouter<AdminRouter>();\n const state = useRouterState();\n const client = useClient<AdminUserController>();\n const dialog = useDialog();\n const toast = useToast();\n const userId = state.params.userId as string;\n\n const [user, setUser] = useState<UserEntity | null>(null);\n const [loading, setLoading] = useState(true);\n\n const realmQuery = { userRealmName: props.userRealmName };\n\n const loadUser = useCallback(async () => {\n setLoading(true);\n try {\n const data = await client.getUser({\n params: { id: userId },\n query: realmQuery,\n });\n setUser(data);\n } finally {\n setLoading(false);\n }\n }, [userId, client]);\n\n useEffect(() => {\n loadUser();\n }, [loadUser]);\n\n const handleToggleEnabled = async () => {\n if (!user) return;\n const action = user.enabled ? \"disable\" : \"enable\";\n const confirmed = await dialog.confirm({\n title: `${user.enabled ? \"Disable\" : \"Enable\"} User`,\n message: `Are you sure you want to ${action} ${displayName(user)}?`,\n });\n if (confirmed) {\n const updated = await client.updateUser({\n params: { id: user.id },\n query: realmQuery,\n body: { enabled: !user.enabled },\n });\n setUser(updated);\n toast.success({ title: `User ${action}d` });\n }\n };\n\n const handleDelete = async () => {\n if (!user) return;\n const confirmed = await dialog.confirm({\n title: \"Delete User\",\n message: `Are you sure you want to delete ${displayName(user)}? This cannot be undone.`,\n confirmColor: \"red\",\n confirmLabel: \"Delete\",\n });\n if (confirmed) {\n await client.deleteUser({\n params: { id: user.id },\n query: realmQuery,\n });\n toast.success({ title: \"User deleted\" });\n router.push(\"adminUsers\");\n }\n };\n\n if (loading) {\n return (\n <Flex flex={1} justify=\"center\" align=\"center\">\n <Loader />\n </Flex>\n );\n }\n\n if (!user) {\n return (\n <Flex flex={1} justify=\"center\" align=\"center\">\n <Text c=\"dimmed\">User not found</Text>\n </Flex>\n );\n }\n\n return (\n <UserContext.Provider value={{ user, reload: loadUser }}>\n <Flex flex={1} direction=\"column\" gap=\"lg\" p=\"md\">\n <AdminResourceHeader\n backHref={router.path(\"adminUsers\")}\n backLabel=\"Users\"\n title={displayName(user)}\n subtitle={user.email || user.username}\n status={{\n label: user.enabled ? \"Active\" : \"Disabled\",\n color: user.enabled ? \"green\" : \"gray\",\n }}\n menuActions={[\n {\n label: user.enabled ? \"Disable\" : \"Enable\",\n icon: user.enabled ? IconBan : IconShieldCheck,\n onClick: handleToggleEnabled,\n },\n {\n label: \"Delete\",\n icon: IconTrash,\n color: \"red\",\n onClick: handleDelete,\n },\n ]}\n />\n\n <AdminResourceTabs\n tabs={[\n {\n value: \"profile\",\n label: \"Profile\",\n href: router.path(\"adminUserProfile\", {\n params: { userId },\n }),\n },\n {\n value: \"sessions\",\n label: \"Sessions\",\n href: router.path(\"adminUserSessions\", {\n params: { userId },\n }),\n },\n ]}\n />\n\n <NestedView />\n </Flex>\n </UserContext.Provider>\n );\n};\n\nexport default AdminUserLayout;\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"AdminUserProfile-vFBLoJ3h.js","names":["Flex"],"sources":["../../src/admin/components/users/AdminUserProfile.tsx"],"sourcesContent":["import { DetailList, Flex } from \"@alepha/ui\";\nimport { Badge } from \"@mantine/core\";\nimport { useI18n } from \"alepha/react/i18n\";\nimport { useUser } from \"./AdminUserLayout.tsx\";\n\nconst AdminUserProfile = () => {\n const { user } = useUser();\n const { l } = useI18n();\n\n return (\n <DetailList\n items={[\n { label: \"ID\", value: user.id, copyable: user.id },\n { label: \"Username\", value: user.username },\n { label: \"Email\", value: user.email },\n {\n label: \"Email Verified\",\n value: user.emailVerified ? \"Yes\" : \"No\",\n },\n { label: \"Phone\", value: user.phoneNumber },\n { label: \"First Name\", value: user.firstName },\n { label: \"Last Name\", value: user.lastName },\n { label: \"Realm\", value: user.realm },\n {\n label: \"Roles\",\n value:\n user.roles.length > 0 ? (\n <Flex gap={4}>\n {user.roles.map((role) => (\n <Badge key={role} size=\"xs\" variant=\"default\">\n {role}\n </Badge>\n ))}\n </Flex>\n ) : null,\n },\n {\n label: \"Created\",\n value: String(l(user.createdAt, { date: \"lll\" })),\n },\n {\n label: \"Updated\",\n value: String(l(user.updatedAt, { date: \"lll\" })),\n },\n ]}\n />\n );\n};\n\nexport default AdminUserProfile;\n"],"mappings":";;;;;;;AAKA,MAAM,yBAAyB;CAC7B,MAAM,EAAE,SAAS,SAAS;CAC1B,MAAM,EAAE,MAAM,SAAS;AAEvB,QACE,oBAAC,cACC,OAAO;EACL;GAAE,OAAO;GAAM,OAAO,KAAK;GAAI,UAAU,KAAK;GAAI;EAClD;GAAE,OAAO;GAAY,OAAO,KAAK;GAAU;EAC3C;GAAE,OAAO;GAAS,OAAO,KAAK;GAAO;EACrC;GACE,OAAO;GACP,OAAO,KAAK,gBAAgB,QAAQ;GACrC;EACD;GAAE,OAAO;GAAS,OAAO,KAAK;GAAa;EAC3C;GAAE,OAAO;GAAc,OAAO,KAAK;GAAW;EAC9C;GAAE,OAAO;GAAa,OAAO,KAAK;GAAU;EAC5C;GAAE,OAAO;GAAS,OAAO,KAAK;GAAO;EACrC;GACE,OAAO;GACP,OACE,KAAK,MAAM,SAAS,IAClB,oBAACA;IAAK,KAAK;cACR,KAAK,MAAM,KAAK,SACf,oBAAC;KAAiB,MAAK;KAAK,SAAQ;eACjC;OADS,KAEJ,CACR;KACG,GACL;GACP;EACD;GACE,OAAO;GACP,OAAO,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,OAAO,CAAC,CAAC;GAClD;EACD;GACE,OAAO;GACP,OAAO,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,OAAO,CAAC,CAAC;GAClD;EACF,GACD"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"AdminUsers-D1UfGya9.js","names":["Flex","Text"],"sources":["../../src/admin/components/users/AdminUsers.tsx"],"sourcesContent":["import {\n ActionButton,\n DataTable,\n Flex,\n Text,\n useDialog,\n useToast,\n} from \"@alepha/ui\";\nimport { Avatar, Badge } from \"@mantine/core\";\nimport {\n IconEye,\n IconTrash,\n IconUserOff,\n IconUserPlus,\n} from \"@tabler/icons-react\";\nimport { type Page, t } from \"alepha\";\nimport type { AdminUserController, UserEntity } from \"alepha/api/users\";\nimport { useClient } from \"alepha/react\";\nimport { useI18n } from \"alepha/react/i18n\";\nimport { useRouter } from \"alepha/react/router\";\nimport { useState } from \"react\";\nimport type { AdminRouter } from \"../../AdminRouter.tsx\";\n\nexport interface AdminUsersProps {\n userRealmName?: string;\n}\n\nconst filters = t.object({\n query: t.optional(t.text()),\n enabled: t.optional(t.boolean()),\n emailVerified: t.optional(t.boolean()),\n});\n\nconst AdminUsers = (props: AdminUsersProps) => {\n const client = useClient<AdminUserController>();\n const router = useRouter<AdminRouter>();\n const { l } = useI18n();\n const dialog = useDialog();\n const toast = useToast();\n const [refreshKey, setRefreshKey] = useState(0);\n\n const refresh = () => setRefreshKey((k) => k + 1);\n\n const handleToggleEnabled = async (user: UserEntity) => {\n const enabled = !user.enabled;\n const confirmed = await dialog.confirm({\n title: enabled ? \"Enable user\" : \"Disable user\",\n message: enabled\n ? `Enable ${user.email || user.username || \"this user\"}?`\n : `Disable ${user.email || user.username || \"this user\"}? They will no longer be able to sign in.`,\n });\n if (!confirmed) return;\n await client.updateUser({\n params: { id: user.id },\n query: { userRealmName: props.userRealmName },\n body: { enabled },\n });\n toast.success({\n message: enabled ? \"User enabled\" : \"User disabled\",\n });\n refresh();\n };\n\n const handleDelete = async (user: UserEntity) => {\n const confirmed = await dialog.confirm({\n title: \"Delete user\",\n message: `Permanently delete ${user.email || user.username || \"this user\"}? This action cannot be undone.`,\n });\n if (!confirmed) return;\n await client.deleteUser({\n params: { id: user.id },\n query: { userRealmName: props.userRealmName },\n });\n toast.success({ message: \"User deleted\" });\n refresh();\n };\n\n const handleBulkDisable = async (\n items: UserEntity[],\n clearSelection: () => void,\n ) => {\n const enabledUsers = items.filter((u) => u.enabled);\n if (enabledUsers.length === 0) {\n toast.danger({ message: \"No active users in selection\" });\n return;\n }\n const confirmed = await dialog.confirm({\n title: \"Disable users\",\n message: `Disable ${enabledUsers.length} user(s)? They will no longer be able to sign in.`,\n });\n if (!confirmed) return;\n for (const user of enabledUsers) {\n await client.updateUser({\n params: { id: user.id },\n query: { userRealmName: props.userRealmName },\n body: { enabled: false },\n });\n }\n toast.success({\n message: `${enabledUsers.length} user(s) disabled`,\n });\n clearSelection();\n refresh();\n };\n\n return (\n <Flex p={\"md\"} flex={1} direction=\"column\">\n <DataTable<UserEntity, typeof filters>\n withCheckbox\n withExport\n checkboxActions={[\n {\n intent: \"danger\",\n label: \"Disable selected\",\n icon: <IconUserOff />,\n onClick: (ctx) =>\n handleBulkDisable(ctx.selectedItems, ctx.clearSelection),\n },\n ]}\n key={refreshKey}\n submitOnInit\n defaultSize={10}\n defaultFilters={[\"query\"]}\n tableProps={{\n horizontalSpacing: \"xs\",\n verticalSpacing: \"xs\",\n }}\n onFilterChange={(_key, _value, form) => form.submit()}\n filters={filters}\n items={async (filters) => {\n const response = await client.findUsers({\n query: {\n ...filters,\n userRealmName: props.userRealmName,\n },\n });\n return response as Page<UserEntity>;\n }}\n columns={{\n user: {\n label: \"User\",\n value: (item) => {\n const name =\n `${item.firstName || \"\"} ${item.lastName || \"\"}`.trim() ||\n item.username ||\n \"Anonymous\";\n\n return (\n <ActionButton\n variant={\"transparent\"}\n href={`/admin/users/${item.id}`}\n >\n <Flex gap={\"xs\"} centerY>\n <Avatar size={\"sm\"} name={name} />\n <Flex col>\n <Text size={\"sm\"} bold>\n {name}\n </Text>\n <Text size=\"xs\" muted>\n {item.email || \"\\u2014\"}\n </Text>\n </Flex>\n </Flex>\n </ActionButton>\n );\n },\n },\n username: {\n label: \"Username\",\n defaultHidden: true,\n value: (item) => (\n <Text size=\"sm\" ff=\"monospace\">\n {item.username || \"\\u2014\"}\n </Text>\n ),\n },\n roles: {\n label: \"Roles\",\n value: (item) =>\n item.roles.length > 0 ? (\n <Flex gap={4}>\n {item.roles.map((role: string) => (\n <Badge key={role} size=\"xs\" variant=\"default\">\n {role}\n </Badge>\n ))}\n </Flex>\n ) : (\n <Text size=\"xs\" muted>\n No roles\n </Text>\n ),\n },\n enabled: {\n label: \"Status\",\n value: (item) => (\n <Badge\n size=\"sm\"\n variant=\"light\"\n color={item.enabled ? \"green\" : \"red\"}\n >\n {item.enabled ? \"Active\" : \"Disabled\"}\n </Badge>\n ),\n },\n emailVerified: {\n label: \"Email\",\n value: (item) => (\n <Badge\n size=\"sm\"\n variant=\"light\"\n color={item.emailVerified ? \"green\" : \"gray\"}\n >\n {item.emailVerified ? \"Verified\" : \"Unverified\"}\n </Badge>\n ),\n },\n createdAt: {\n label: \"Joined\",\n sortable: true,\n sortKey: \"createdAt\",\n value: (item) => (\n <Text size=\"xs\" muted>\n {l(item.createdAt, { date: \"fromNow\" })}\n </Text>\n ),\n },\n }}\n rowActions={(item) => [\n {\n label: \"View profile\",\n icon: IconEye,\n onClick: () =>\n router.push(\"adminUserProfile\", {\n params: { userId: item.id },\n }),\n },\n {\n label: item.enabled ? \"Disable user\" : \"Enable user\",\n icon: item.enabled ? IconUserOff : IconUserPlus,\n color: item.enabled ? \"red\" : \"green\",\n onClick: () => handleToggleEnabled(item),\n },\n {\n label: \"Delete user\",\n icon: IconTrash,\n color: \"red\",\n onClick: () => handleDelete(item),\n },\n ]}\n />\n </Flex>\n );\n};\n\nexport default AdminUsers;\n"],"mappings":"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"}
|