@alepha/ui 0.15.0 → 0.15.2
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/AdminAudits-BlGGKLof.js +3 -0
- package/dist/admin/{AdminAudits-DIrCCPk3.js → AdminAudits-C0DPYw0W.js} +4 -4
- package/dist/admin/AdminAudits-C0DPYw0W.js.map +1 -0
- package/dist/admin/AdminFiles-Bg9feLFH.js +3 -0
- package/dist/admin/{AdminFiles-RsL178Ta.js → AdminFiles-Cu8GHgQ3.js} +5 -5
- package/dist/admin/AdminFiles-Cu8GHgQ3.js.map +1 -0
- package/dist/admin/AdminLayout-BfeFXiul.js +3 -0
- package/dist/admin/{AdminLayout-XiSivwWH.js → AdminLayout-QJLIesuG.js} +1 -1
- package/dist/admin/{AdminLayout-XiSivwWH.js.map → AdminLayout-QJLIesuG.js.map} +1 -1
- package/dist/admin/{AdminNotifications-cIbywWKi.js → AdminNotifications-CgYkBuG_.js} +5 -5
- package/dist/admin/AdminNotifications-CgYkBuG_.js.map +1 -0
- package/dist/admin/AdminNotifications-DmfGPqHe.js +3 -0
- package/dist/admin/{AdminParameters-BKObzzpN.js → AdminParameters-Cl-R0nXt.js} +1 -1
- package/dist/admin/{AdminParameters-D-q3Qmhv.js → AdminParameters-hjNG_KXb.js} +4 -4
- package/dist/admin/AdminParameters-hjNG_KXb.js.map +1 -0
- package/dist/admin/{AdminSessions-vOgkrQ2U.js → AdminSessions-Bey9cuy1.js} +6 -6
- package/dist/admin/AdminSessions-Bey9cuy1.js.map +1 -0
- package/dist/admin/AdminSessions-Cn4_jB04.js +3 -0
- package/dist/admin/{AdminUserAudits-CSsN1fIC.js → AdminUserAudits-C7AN9jx7.js} +4 -4
- package/dist/admin/AdminUserAudits-C7AN9jx7.js.map +1 -0
- package/dist/admin/{AdminUserAudits-DmAnivo3.js → AdminUserAudits-Cp_ERd2g.js} +1 -1
- package/dist/admin/{AdminUserCreate-DpA13zwj.js → AdminUserCreate-Chr-7hLk.js} +1 -1
- package/dist/admin/{AdminUserCreate-B72nu-3W.js → AdminUserCreate-DiXi1EWB.js} +4 -4
- package/dist/admin/AdminUserCreate-DiXi1EWB.js.map +1 -0
- package/dist/admin/{AdminUserDetails-Zib_B6Al.js → AdminUserDetails-Dcn3OwMC.js} +1 -1
- package/dist/admin/{AdminUserDetails-CKM2IEMr.js → AdminUserDetails-yM4x8JE6.js} +5 -5
- package/dist/admin/AdminUserDetails-yM4x8JE6.js.map +1 -0
- package/dist/admin/{AdminUserLayout-BNBOEiAO.js → AdminUserLayout-CfeQHH6e.js} +1 -1
- package/dist/admin/{AdminUserLayout-D7En9UBq.js → AdminUserLayout-D9bqGt6T.js} +3 -3
- package/dist/admin/AdminUserLayout-D9bqGt6T.js.map +1 -0
- package/dist/admin/{AdminUserSessions-DEaGu6n6.js → AdminUserSessions-kmkXG-xf.js} +4 -4
- package/dist/admin/AdminUserSessions-kmkXG-xf.js.map +1 -0
- package/dist/admin/AdminUserSessions-rvA0ztxn.js +3 -0
- package/dist/admin/{AdminUserSettings-Di73D7g2.js → AdminUserSettings-BnzRAcqV.js} +4 -4
- package/dist/admin/AdminUserSettings-BnzRAcqV.js.map +1 -0
- package/dist/admin/AdminUserSettings-CXs-jtRv.js +3 -0
- package/dist/admin/{AdminUsers-BnGIRvmV.js → AdminUsers-CYkcUWCg.js} +6 -6
- package/dist/admin/AdminUsers-CYkcUWCg.js.map +1 -0
- package/dist/admin/AdminUsers-DdFXzrEn.js +3 -0
- package/dist/admin/index.d.ts +33 -18
- package/dist/admin/index.d.ts.map +1 -1
- package/dist/admin/index.js +48 -33
- package/dist/admin/index.js.map +1 -1
- package/dist/auth/{AuthLayout-B1sUB8fB.js → AuthLayout-Dj5K4SIN.js} +2 -2
- package/dist/auth/AuthLayout-Dj5K4SIN.js.map +1 -0
- package/dist/{demo/IconGoogle-DvmFiEDB.js → auth/IconGoogle-DpSlPZ1u.js} +1 -1
- package/dist/auth/{IconGoogle-Cm5d8J3f.js.map → IconGoogle-DpSlPZ1u.js.map} +1 -1
- package/dist/auth/{Login-Cjxv3EDi.js → Login-BAFVcX_J.js} +6 -6
- package/dist/auth/Login-BAFVcX_J.js.map +1 -0
- package/dist/auth/Login-C5PUsp8I.js +4 -0
- package/dist/auth/{Register-CGlbQ50l.js → Register-CZRXEcWy.js} +7 -7
- package/dist/auth/Register-CZRXEcWy.js.map +1 -0
- package/dist/auth/Register-DMTs5ep_.js +4 -0
- package/dist/auth/ResetPassword-D-mhMtmx.js +3 -0
- package/dist/auth/{ResetPassword-DvqD_1SJ.js → ResetPassword-DTYNsBIj.js} +5 -5
- package/dist/auth/ResetPassword-DTYNsBIj.js.map +1 -0
- package/dist/auth/VerifyEmail-BsrCmncc.js +3 -0
- package/dist/auth/{VerifyEmail-VaBruOnO.js → VerifyEmail-DolENWGn.js} +4 -4
- package/dist/auth/VerifyEmail-DolENWGn.js.map +1 -0
- package/dist/auth/index.d.ts +34 -23
- package/dist/auth/index.d.ts.map +1 -1
- package/dist/auth/index.js +25 -14
- package/dist/auth/index.js.map +1 -1
- package/dist/core/index.d.ts +272 -209
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +314 -99
- package/dist/core/index.js.map +1 -1
- package/dist/demo/{DemoDataTable-2mzzf__a.js → DemoDataTable-CguplbR7.js} +2 -2
- package/dist/demo/{DemoDataTable-2mzzf__a.js.map → DemoDataTable-CguplbR7.js.map} +1 -1
- package/dist/demo/{DemoHome-CnuL5WV9.js → DemoHome-Cce2bWmg.js} +1 -1
- package/dist/demo/{DemoHome-CnuL5WV9.js.map → DemoHome-Cce2bWmg.js.map} +1 -1
- package/dist/demo/DemoHome-DC9qkMNe.js +3 -0
- package/dist/demo/DemoJsonViewer-DIssGVlJ.js +4 -0
- package/dist/demo/{DemoJsonViewer-NUGst5wW.js → DemoJsonViewer-Dgdk3Txb.js} +3 -3
- package/dist/demo/{DemoJsonViewer-NUGst5wW.js.map → DemoJsonViewer-Dgdk3Txb.js.map} +1 -1
- package/dist/demo/{DemoLayout-dvbeuBBf.js → DemoLayout-B20TEuhV.js} +2 -2
- package/dist/demo/DemoLayout-B20TEuhV.js.map +1 -0
- package/dist/demo/DemoLayout-DSRyf4qJ.js +3 -0
- package/dist/demo/{DemoLogin-DvltFTER.js → DemoLogin-mtkN6340.js} +7 -7
- package/dist/demo/DemoLogin-mtkN6340.js.map +1 -0
- package/dist/demo/{DemoRegister-Vu6ZPWib.js → DemoRegister-C0MW7anp.js} +8 -8
- package/dist/demo/DemoRegister-C0MW7anp.js.map +1 -0
- package/dist/demo/{DemoResetPassword-BFwmqwec.js → DemoResetPassword-CPTy88iK.js} +6 -6
- package/dist/demo/DemoResetPassword-CPTy88iK.js.map +1 -0
- package/dist/demo/{DemoSidebar-DWnjYHoP.js → DemoSidebar-MVmQKfMt.js} +2 -2
- package/dist/demo/{DemoSidebar-DWnjYHoP.js.map → DemoSidebar-MVmQKfMt.js.map} +1 -1
- package/dist/demo/{DemoTypeForm-P5_VInW2.js → DemoTypeForm-w-qtfRlC.js} +3 -3
- package/dist/demo/DemoTypeForm-w-qtfRlC.js.map +1 -0
- package/dist/demo/{DemoVerifyEmail-C_ooC5u8.js → DemoVerifyEmail-C8FFJT5A.js} +5 -5
- package/dist/demo/DemoVerifyEmail-C8FFJT5A.js.map +1 -0
- package/dist/{auth/IconGoogle-Cm5d8J3f.js → demo/IconGoogle-CbBF8Hqq.js} +1 -1
- package/dist/demo/{IconGoogle-DvmFiEDB.js.map → IconGoogle-CbBF8Hqq.js.map} +1 -1
- package/dist/demo/{Showcase-vemLuO2t.js → Showcase-CQrMWars.js} +4 -4
- package/dist/demo/Showcase-CQrMWars.js.map +1 -0
- package/dist/demo/index.d.ts +37 -27
- package/dist/demo/index.d.ts.map +1 -1
- package/dist/demo/index.js +26 -16
- package/dist/demo/index.js.map +1 -1
- package/dist/json/index.d.ts +17 -17
- package/dist/json/index.d.ts.map +1 -1
- package/package.json +13 -14
- package/src/admin/AdminRouter.ts +2 -2
- package/src/admin/MainRouter.ts +1 -1
- package/src/admin/components/audits/AdminAudits.tsx +3 -3
- package/src/admin/components/files/AdminFiles.tsx +2 -2
- package/src/admin/components/jobs/AdminJobs.tsx +2 -2
- package/src/admin/components/notifications/AdminNotifications.tsx +2 -2
- package/src/admin/components/parameters/AdminParameters.tsx +1 -1
- package/src/admin/components/parameters/ParameterDetails.tsx +2 -2
- package/src/admin/components/parameters/ParameterHistory.tsx +1 -1
- package/src/admin/components/parameters/types.ts +9 -3
- package/src/admin/components/sessions/AdminSessions.tsx +3 -3
- package/src/admin/components/shared/AdminResourceHeader.tsx +1 -1
- package/src/admin/components/shared/AdminResourceTabs.tsx +1 -1
- package/src/admin/components/users/AdminUserAudits.tsx +3 -3
- package/src/admin/components/users/AdminUserCreate.tsx +3 -3
- package/src/admin/components/users/AdminUserDetails.tsx +4 -4
- package/src/admin/components/users/AdminUserLayout.tsx +2 -2
- package/src/admin/components/users/AdminUserSessions.tsx +3 -3
- package/src/admin/components/users/AdminUserSettings.tsx +3 -3
- package/src/admin/components/users/AdminUsers.tsx +3 -3
- package/src/admin/index.ts +16 -1
- package/src/auth/AuthI18n.ts +1 -1
- package/src/auth/AuthRouter.ts +2 -2
- package/src/auth/components/AuthLayout.tsx +1 -1
- package/src/auth/components/Login.tsx +4 -4
- package/src/auth/components/Register.tsx +5 -5
- package/src/auth/components/ResetPassword.tsx +4 -4
- package/src/auth/components/VerifyEmail.tsx +3 -3
- package/src/auth/components/buttons/UserButton.tsx +2 -2
- package/src/auth/index.ts +14 -3
- package/src/core/RootRouter.ts +1 -1
- package/src/core/atoms/alephaSidebarAtom.ts +57 -0
- package/src/core/components/buttons/ActionButton.tsx +9 -9
- package/src/core/components/buttons/BurgerButton.tsx +5 -4
- package/src/core/components/buttons/LanguageButton.tsx +1 -1
- package/src/core/components/buttons/OmnibarButton.tsx +20 -1
- package/src/core/components/buttons/ThemeButton.tsx +1 -1
- package/src/core/components/buttons/ToggleSidebarButton.tsx +33 -23
- package/src/core/components/form/Control.tsx +1 -1
- package/src/core/components/form/ControlArray.tsx +2 -2
- package/src/core/components/form/ControlDate.tsx +1 -1
- package/src/core/components/form/ControlNumber.tsx +2 -2
- package/src/core/components/form/ControlObject.tsx +1 -1
- package/src/core/components/form/ControlQueryBuilder.tsx +1 -1
- package/src/core/components/form/ControlSelect.tsx +1 -1
- package/src/core/components/form/TypeForm.tsx +2 -2
- package/src/core/components/layout/AdminShell.tsx +205 -27
- package/src/core/components/layout/AlephaMantineProvider.tsx +3 -3
- package/src/core/components/layout/Omnibar.tsx +2 -2
- package/src/core/components/layout/Sidebar.tsx +42 -77
- package/src/core/components/table/DataTable.tsx +2 -2
- package/src/core/components/table/DataTableFilters.tsx +1 -1
- package/src/core/components/table/types.ts +1 -1
- package/src/core/hooks/useDialog.ts +1 -1
- package/src/core/hooks/useTheme.ts +1 -1
- package/src/core/hooks/useToast.ts +1 -1
- package/src/core/index.ts +57 -6
- package/src/core/providers/ThemeProvider.ts +1 -1
- package/src/core/styles.css +58 -0
- package/src/core/utils/parseInput.ts +1 -1
- package/src/demo/DemoRouter.ts +1 -1
- package/src/demo/components/DemoLayout.tsx +1 -1
- package/src/demo/components/core/DemoTypeForm.tsx +1 -1
- package/src/demo/components/json/DemoJsonViewer.tsx +1 -1
- package/src/demo/components/shared/Showcase.tsx +1 -1
- package/src/demo/index.ts +11 -1
- package/src/json/index.ts +13 -0
- package/dist/admin/AdminAudits-B3EhKhN7.js +0 -3
- package/dist/admin/AdminAudits-DIrCCPk3.js.map +0 -1
- package/dist/admin/AdminFiles-C8OG4dtD.js +0 -3
- package/dist/admin/AdminFiles-RsL178Ta.js.map +0 -1
- package/dist/admin/AdminLayout-BnSmtA4x.js +0 -3
- package/dist/admin/AdminNotifications-BSL4B2fQ.js +0 -3
- package/dist/admin/AdminNotifications-cIbywWKi.js.map +0 -1
- package/dist/admin/AdminParameters-D-q3Qmhv.js.map +0 -1
- package/dist/admin/AdminSessions-DHG9zPfr.js +0 -3
- package/dist/admin/AdminSessions-vOgkrQ2U.js.map +0 -1
- package/dist/admin/AdminUserAudits-CSsN1fIC.js.map +0 -1
- package/dist/admin/AdminUserCreate-B72nu-3W.js.map +0 -1
- package/dist/admin/AdminUserDetails-CKM2IEMr.js.map +0 -1
- package/dist/admin/AdminUserLayout-D7En9UBq.js.map +0 -1
- package/dist/admin/AdminUserSessions-D9X2_HMA.js +0 -3
- package/dist/admin/AdminUserSessions-DEaGu6n6.js.map +0 -1
- package/dist/admin/AdminUserSettings-Di73D7g2.js.map +0 -1
- package/dist/admin/AdminUserSettings-yI-JECf5.js +0 -3
- package/dist/admin/AdminUsers-BnGIRvmV.js.map +0 -1
- package/dist/admin/AdminUsers-CG9-2Z8W.js +0 -3
- package/dist/auth/AuthLayout-B1sUB8fB.js.map +0 -1
- package/dist/auth/Login-BWi-pPbO.js +0 -4
- package/dist/auth/Login-Cjxv3EDi.js.map +0 -1
- package/dist/auth/Register-CGlbQ50l.js.map +0 -1
- package/dist/auth/Register-CWdkXWkc.js +0 -4
- package/dist/auth/ResetPassword-BUdM7T_R.js +0 -3
- package/dist/auth/ResetPassword-DvqD_1SJ.js.map +0 -1
- package/dist/auth/VerifyEmail-BYmtnkEl.js +0 -3
- package/dist/auth/VerifyEmail-VaBruOnO.js.map +0 -1
- package/dist/demo/DemoHome-D6Z7EE4V.js +0 -3
- package/dist/demo/DemoJsonViewer-CYUggLop.js +0 -4
- package/dist/demo/DemoLayout-ZFDzyvY3.js +0 -3
- package/dist/demo/DemoLayout-dvbeuBBf.js.map +0 -1
- package/dist/demo/DemoLogin-DvltFTER.js.map +0 -1
- package/dist/demo/DemoRegister-Vu6ZPWib.js.map +0 -1
- package/dist/demo/DemoResetPassword-BFwmqwec.js.map +0 -1
- package/dist/demo/DemoTypeForm-P5_VInW2.js.map +0 -1
- package/dist/demo/DemoVerifyEmail-C_ooC5u8.js.map +0 -1
- package/dist/demo/Showcase-vemLuO2t.js.map +0 -1
package/src/core/index.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
1
|
+
import { $context, $module, type Static } from "alepha";
|
|
2
|
+
import { AlephaReactForm } from "alepha/react/form";
|
|
3
|
+
import { AlephaReactHead } from "alepha/react/head";
|
|
4
|
+
import { AlephaReactI18n } from "alepha/react/i18n";
|
|
5
5
|
import type { ComponentType, ReactNode } from "react";
|
|
6
|
+
import { alephaSidebarAtom } from "./atoms/alephaSidebarAtom.ts";
|
|
6
7
|
import { alephaThemeAtom } from "./atoms/alephaThemeAtom.ts";
|
|
7
8
|
import type { ControlProps } from "./components/form/Control.tsx";
|
|
8
9
|
import { ThemeProvider } from "./providers/ThemeProvider.ts";
|
|
@@ -13,6 +14,7 @@ import { ToastService } from "./services/ToastService.tsx";
|
|
|
13
14
|
// ---------------------------------------------------------------------------------------------------------------------
|
|
14
15
|
|
|
15
16
|
export { Flex, Text } from "@mantine/core";
|
|
17
|
+
export * from "./atoms/alephaSidebarAtom.ts";
|
|
16
18
|
export * from "./atoms/alephaThemeAtom.ts";
|
|
17
19
|
export * from "./atoms/alephaThemeListAtom.ts";
|
|
18
20
|
export * from "./atoms/themes/default.ts";
|
|
@@ -35,6 +37,7 @@ export { default as LanguageButton } from "./components/buttons/LanguageButton.t
|
|
|
35
37
|
export { default as OmnibarButton } from "./components/buttons/OmnibarButton.tsx";
|
|
36
38
|
export type { ThemeButtonProps } from "./components/buttons/ThemeButton.tsx";
|
|
37
39
|
export { default as ThemeButton } from "./components/buttons/ThemeButton.tsx";
|
|
40
|
+
export { default as ToggleSidebarButton } from "./components/buttons/ToggleSidebarButton.tsx";
|
|
38
41
|
export { default as AlertDialog } from "./components/dialogs/AlertDialog.tsx";
|
|
39
42
|
export { default as ConfirmDialog } from "./components/dialogs/ConfirmDialog.tsx";
|
|
40
43
|
export { default as PromptDialog } from "./components/dialogs/PromptDialog.tsx";
|
|
@@ -121,11 +124,12 @@ declare module "typebox" {
|
|
|
121
124
|
|
|
122
125
|
declare module "alepha" {
|
|
123
126
|
interface State {
|
|
127
|
+
[alephaSidebarAtom.key]?: Static<typeof alephaSidebarAtom.schema>;
|
|
124
128
|
[alephaThemeAtom.key]?: Static<typeof alephaThemeAtom.schema>;
|
|
125
129
|
}
|
|
126
130
|
}
|
|
127
131
|
|
|
128
|
-
declare module "
|
|
132
|
+
declare module "alepha/react/router" {
|
|
129
133
|
interface PagePrimitiveOptions {
|
|
130
134
|
/**
|
|
131
135
|
* Human-readable title for the page.
|
|
@@ -152,7 +156,25 @@ declare module "@alepha/react/router" {
|
|
|
152
156
|
// ---------------------------------------------------------------------------------------------------------------------
|
|
153
157
|
|
|
154
158
|
/**
|
|
155
|
-
*
|
|
159
|
+
* | type | quality | stability |
|
|
160
|
+
* |------|---------|-----------|
|
|
161
|
+
* | frontend | rare | experimental |
|
|
162
|
+
*
|
|
163
|
+
* Core UI components based on Mantine UI v8.
|
|
164
|
+
*
|
|
165
|
+
* **Features:**
|
|
166
|
+
* - Mantine integration with theme support
|
|
167
|
+
* - ActionButton, BurgerButton, ClipboardButton, DarkModeButton, LanguageButton, ThemeButton
|
|
168
|
+
* - AlertDialog, ConfirmDialog, PromptDialog
|
|
169
|
+
* - Form controls: Control, ControlArray, ControlDate, ControlNumber, ControlObject, ControlSelect, ControlQueryBuilder
|
|
170
|
+
* - TypeForm for automatic form generation from TypeBox schemas
|
|
171
|
+
* - AdminShell layout component
|
|
172
|
+
* - AppBar with configurable elements
|
|
173
|
+
* - Sidebar navigation with sections and menu items
|
|
174
|
+
* - Omnibar for command palette / search
|
|
175
|
+
* - DataTable with filtering, sorting, pagination
|
|
176
|
+
* - Toast notifications
|
|
177
|
+
* - Theme system with dark mode
|
|
156
178
|
*
|
|
157
179
|
* @module alepha.ui
|
|
158
180
|
*/
|
|
@@ -168,3 +190,32 @@ export const AlephaUI = $module({
|
|
|
168
190
|
alepha.with(ToastService);
|
|
169
191
|
},
|
|
170
192
|
});
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Register UI components and get the RootRouter instance.
|
|
196
|
+
*/
|
|
197
|
+
export const $ui = (
|
|
198
|
+
opts: {
|
|
199
|
+
// TODO:
|
|
200
|
+
// theme?: ThemeOptions;
|
|
201
|
+
// root?: string = "/";
|
|
202
|
+
} = {},
|
|
203
|
+
) => {
|
|
204
|
+
const { alepha } = $context();
|
|
205
|
+
|
|
206
|
+
// TODO: Register unique instance ? In order to have multiple ui apps in the same context ?
|
|
207
|
+
// app = $ui();
|
|
208
|
+
// admin = $ui({ root: "/admin", theme: adminTheme });
|
|
209
|
+
// auth = $ui({ root: "/auth", theme: authTheme });
|
|
210
|
+
// etc...
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* If multi ui, should we have N themes ? or one $atom theme but with change based on current ui app ?
|
|
214
|
+
*
|
|
215
|
+
* App (theme=T1) -> Admin (theme=T2) ?
|
|
216
|
+
*
|
|
217
|
+
* > It can be done with onLeave()/onEnter() of the RootRouter to set the theme atom.
|
|
218
|
+
*/
|
|
219
|
+
|
|
220
|
+
return alepha.inject(RootRouter); // Inject as singleton ?
|
|
221
|
+
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { $head } from "@alepha/react/head";
|
|
2
1
|
import { $inject, Alepha, AlephaError } from "alepha";
|
|
2
|
+
import { $head } from "alepha/react/head";
|
|
3
3
|
import { $cookie } from "alepha/server/cookies";
|
|
4
4
|
import { alephaThemeAtom } from "../atoms/alephaThemeAtom.ts";
|
|
5
5
|
import { alephaThemeListAtom } from "../atoms/alephaThemeListAtom.ts";
|
package/src/core/styles.css
CHANGED
|
@@ -53,6 +53,64 @@
|
|
|
53
53
|
scrollbar-width: thin;
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
+
/* Sidebar scroll area - subtle scrollbar that appears on hover */
|
|
57
|
+
.alepha-sidebar-scroll {
|
|
58
|
+
overflow-y: auto;
|
|
59
|
+
overflow-x: hidden;
|
|
60
|
+
scrollbar-width: thin;
|
|
61
|
+
scrollbar-color: transparent transparent;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
.alepha-sidebar-scroll:hover {
|
|
65
|
+
scrollbar-color: var(--mantine-color-gray-4) transparent;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
[data-mantine-color-scheme="dark"] .alepha-sidebar-scroll:hover {
|
|
69
|
+
scrollbar-color: var(--mantine-color-dark-4) transparent;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/* WebKit scrollbar styling */
|
|
73
|
+
.alepha-sidebar-scroll::-webkit-scrollbar {
|
|
74
|
+
width: 6px;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.alepha-sidebar-scroll::-webkit-scrollbar-track {
|
|
78
|
+
background: transparent;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
.alepha-sidebar-scroll::-webkit-scrollbar-thumb {
|
|
82
|
+
background: transparent;
|
|
83
|
+
border-radius: 3px;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
.alepha-sidebar-scroll:hover::-webkit-scrollbar-thumb {
|
|
87
|
+
background: var(--mantine-color-gray-4);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
[data-mantine-color-scheme="dark"]
|
|
91
|
+
.alepha-sidebar-scroll:hover::-webkit-scrollbar-thumb {
|
|
92
|
+
background: var(--mantine-color-dark-4);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/* ------------------------------------------------------------------------------------------------------------------ */
|
|
96
|
+
/* Sidebar Transitions */
|
|
97
|
+
|
|
98
|
+
.alepha-sidebar-navbar {
|
|
99
|
+
transition: width 0.2s ease-in-out;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
.alepha-sidebar-navbar[data-resizing="true"] {
|
|
103
|
+
transition: none;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
.alepha-sidebar-main {
|
|
107
|
+
transition: padding-left 0.2s ease-in-out;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
.alepha-sidebar-main[data-resizing="true"] {
|
|
111
|
+
transition: none;
|
|
112
|
+
}
|
|
113
|
+
|
|
56
114
|
/* ------------------------------------------------------------------------------------------------------------------ */
|
|
57
115
|
/* Modal Customizations */
|
|
58
116
|
|
package/src/demo/DemoRouter.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { $page } from "@alepha/react/router";
|
|
2
1
|
import {
|
|
3
2
|
IconBinaryTree,
|
|
4
3
|
IconBraces,
|
|
@@ -14,6 +13,7 @@ import {
|
|
|
14
13
|
IconUserPlus,
|
|
15
14
|
IconWall,
|
|
16
15
|
} from "@tabler/icons-react";
|
|
16
|
+
import { $page } from "alepha/react/router";
|
|
17
17
|
|
|
18
18
|
export class DemoRouter {
|
|
19
19
|
demoLayout = $page({
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { useRouter } from "@alepha/react/router";
|
|
2
1
|
import { ActionButton, AdminShell, AlephaMantineProvider } from "@alepha/ui";
|
|
3
2
|
import { IconArrowLeft } from "@tabler/icons-react";
|
|
3
|
+
import { useRouter } from "alepha/react/router";
|
|
4
4
|
import type { DemoRouter } from "../DemoRouter.ts";
|
|
5
5
|
|
|
6
6
|
const DemoLayout = () => {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { useForm } from "@alepha/react/form";
|
|
2
1
|
import { TypeForm, ui } from "@alepha/ui";
|
|
3
2
|
import { Box, Card, Flex, Text } from "@mantine/core";
|
|
4
3
|
import type { Static, TObject } from "alepha";
|
|
4
|
+
import { useForm } from "alepha/react/form";
|
|
5
5
|
import { type ReactNode, useState } from "react";
|
|
6
6
|
import MacWindow, { type MacWindowProps } from "./MacWindow.tsx";
|
|
7
7
|
|
package/src/demo/index.ts
CHANGED
|
@@ -20,7 +20,17 @@ export { DemoRouter } from "./DemoRouter.ts";
|
|
|
20
20
|
// ---------------------------------------------------------------------------------------------------------------------
|
|
21
21
|
|
|
22
22
|
/**
|
|
23
|
-
*
|
|
23
|
+
* | type | quality | stability |
|
|
24
|
+
* |------|---------|-----------|
|
|
25
|
+
* | frontend | standard | experimental |
|
|
26
|
+
*
|
|
27
|
+
* Component showcase and documentation.
|
|
28
|
+
*
|
|
29
|
+
* **Features:**
|
|
30
|
+
* - DemoLayout for demo pages
|
|
31
|
+
* - DemoHome landing page
|
|
32
|
+
* - MacWindow component for showcases
|
|
33
|
+
* - Showcase component for component demos
|
|
24
34
|
*
|
|
25
35
|
* @module alepha.ui.demo
|
|
26
36
|
*/
|
package/src/json/index.ts
CHANGED
|
@@ -1,3 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* | type | quality | stability |
|
|
3
|
+
* |------|---------|-----------|
|
|
4
|
+
* | frontend | standard | experimental |
|
|
5
|
+
*
|
|
6
|
+
* JSON viewing components.
|
|
7
|
+
*
|
|
8
|
+
* **Features:**
|
|
9
|
+
* - JsonViewer component for displaying JSON data
|
|
10
|
+
*
|
|
11
|
+
* @module alepha.ui.json
|
|
12
|
+
*/
|
|
13
|
+
|
|
1
14
|
import "./extensions/DialogService.tsx";
|
|
2
15
|
|
|
3
16
|
// ---------------------------------------------------------------------------------------------------------------------
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"AdminAudits-DIrCCPk3.js","names":[],"sources":["../../src/admin/components/audits/AdminAudits.tsx"],"sourcesContent":["import { useClient } from \"@alepha/react\";\nimport { useI18n } from \"@alepha/react/i18n\";\nimport { useRouter } from \"@alepha/react/router\";\nimport { DataTable, Flex, Text } from \"@alepha/ui\";\nimport { Badge, Group, Stack, Tooltip } from \"@mantine/core\";\nimport {\n IconAlertTriangle,\n IconCheck,\n IconInfoCircle,\n IconUser,\n IconX,\n} from \"@tabler/icons-react\";\nimport { type Page, t } from \"alepha\";\nimport type { AdminAuditController, AuditEntity } from \"alepha/api/audits\";\nimport type { AdminRouter } from \"../../AdminRouter.ts\";\n\nexport interface AdminAuditsProps {\n userRealmName?: string;\n}\n\nconst getSeverityColor = (severity: string) => {\n switch (severity) {\n case \"critical\":\n return \"red\";\n case \"warning\":\n return \"yellow\";\n default:\n return \"blue\";\n }\n};\n\nconst getSeverityIcon = (severity: string) => {\n switch (severity) {\n case \"critical\":\n return <IconAlertTriangle size={12} />;\n case \"warning\":\n return <IconAlertTriangle size={12} />;\n default:\n return <IconInfoCircle size={12} />;\n }\n};\n\nconst getTypeColor = (type: string) => {\n switch (type) {\n case \"auth\":\n return \"blue\";\n case \"user\":\n return \"grape\";\n case \"security\":\n return \"red\";\n case \"system\":\n return \"orange\";\n case \"access\":\n return \"cyan\";\n case \"payment\":\n return \"green\";\n case \"order\":\n return \"teal\";\n default:\n return \"gray\";\n }\n};\n\nconst AdminAudits = (props: AdminAuditsProps) => {\n const client = useClient<AdminAuditController>();\n const router = useRouter<AdminRouter>();\n const { l } = useI18n();\n\n const filters = t.object({\n type: t.optional(t.text()),\n action: t.optional(t.text()),\n severity: t.optional(t.enum([\"info\", \"warning\", \"critical\"])),\n success: t.optional(t.boolean()),\n resourceType: t.optional(t.text()),\n search: t.optional(t.text()),\n from: t.optional(t.datetime()),\n to: t.optional(t.datetime()),\n });\n\n return (\n <Flex flex={1} direction=\"column\">\n <DataTable<AuditEntity, typeof filters>\n submitOnInit\n defaultSize={20}\n typeFormProps={{\n skipSubmitButton: true,\n columns: 4,\n }}\n tableProps={{\n horizontalSpacing: \"xs\",\n verticalSpacing: \"xs\",\n striped: false,\n highlightOnHover: true,\n }}\n filters={filters}\n tableTrProps={(item) => ({\n style: {\n cursor: item.userId ? \"pointer\" : \"default\",\n opacity: item.success ? 1 : 0.85,\n },\n onClick: () => {\n if (item.userId) {\n router.go(\"adminUserDetails\", {\n params: { userId: item.userId },\n });\n }\n },\n })}\n items={async (query) => {\n const response = await client.findAudits({\n query: {\n ...query,\n userRealm: props.userRealmName,\n },\n });\n return response as Page<AuditEntity>;\n }}\n columns={{\n type: {\n label: \"Type\",\n fit: true,\n value: (item) => (\n <Badge size=\"sm\" variant=\"light\" color={getTypeColor(item.type)}>\n {item.type}\n </Badge>\n ),\n },\n action: {\n label: \"Action\",\n fit: true,\n value: (item) => (\n <Badge size=\"sm\" variant=\"outline\">\n {item.action}\n </Badge>\n ),\n },\n severity: {\n label: \"Severity\",\n fit: true,\n value: (item) => (\n <Badge\n size=\"xs\"\n variant=\"light\"\n color={getSeverityColor(item.severity)}\n leftSection={getSeverityIcon(item.severity)}\n >\n {item.severity}\n </Badge>\n ),\n },\n user: {\n label: \"User\",\n fit: true,\n value: (item) =>\n item.userId ? (\n <Tooltip\n label={\n <Stack gap={2}>\n <Text size=\"xs\">{item.userEmail || \"No email\"}</Text>\n <Text size=\"xs\" c=\"dimmed\">\n {item.userRealm || \"default\"}\n </Text>\n </Stack>\n }\n >\n <Group gap={4}>\n <IconUser size={12} />\n <Text size=\"xs\" lineClamp={1} maw={100}>\n {item.userEmail?.split(\"@\")[0] || item.userId.slice(0, 8)}\n </Text>\n </Group>\n </Tooltip>\n ) : (\n <Text size=\"xs\" c=\"dimmed\">\n System\n </Text>\n ),\n },\n description: {\n label: \"Description\",\n value: (item) => (\n <Text size=\"sm\" lineClamp={1}>\n {item.description || \"-\"}\n </Text>\n ),\n },\n resource: {\n label: \"Resource\",\n fit: true,\n value: (item) =>\n item.resourceType ? (\n <Tooltip label={`${item.resourceType}: ${item.resourceId}`}>\n <Badge size=\"xs\" variant=\"dot\" color=\"gray\">\n {item.resourceType}\n </Badge>\n </Tooltip>\n ) : (\n <Text size=\"xs\" c=\"dimmed\">\n -\n </Text>\n ),\n },\n success: {\n label: \"Status\",\n fit: true,\n value: (item) =>\n item.success ? (\n <IconCheck size={14} color=\"var(--mantine-color-green-6)\" />\n ) : (\n <Tooltip label={item.errorMessage || \"Failed\"}>\n <IconX size={14} color=\"var(--mantine-color-red-6)\" />\n </Tooltip>\n ),\n },\n ipAddress: {\n label: \"IP\",\n fit: true,\n value: (item) => (\n <Text size=\"xs\" c=\"dimmed\" ff=\"monospace\">\n {item.ipAddress || \"-\"}\n </Text>\n ),\n },\n createdAt: {\n label: \"Time\",\n fit: true,\n value: (item) => (\n <Tooltip label={l(item.createdAt, { date: \"medium\" })}>\n <Text size=\"xs\" c=\"dimmed\">\n {l(item.createdAt, { date: \"fromNow\" })}\n </Text>\n </Tooltip>\n ),\n },\n }}\n />\n </Flex>\n );\n};\n\nexport default AdminAudits;\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"AdminFiles-RsL178Ta.js","names":["filters"],"sources":["../../src/admin/components/files/AdminFiles.tsx"],"sourcesContent":["import { useClient } from \"@alepha/react\";\nimport { useI18n } from \"@alepha/react/i18n\";\nimport { DataTable, Flex, Text } from \"@alepha/ui\";\nimport { Badge } from \"@mantine/core\";\nimport { type Page, t } from \"alepha\";\nimport { type FileController, type FileEntity, files } from \"alepha/api/files\";\n\nconst AdminFiles = () => {\n const client = useClient<FileController>();\n const { l } = useI18n();\n\n const filters = t.object({\n bucket: t.optional(t.string()),\n name: t.optional(\n t.string({\n $control: {\n query: t.pick(files.schema, [\"name\", \"bucket\", \"mimeType\"]),\n },\n }),\n ),\n });\n\n const formatFileSize = (bytes: number) => {\n if (bytes === 0) return \"0 B\";\n const k = 1024;\n const sizes = [\"B\", \"KB\", \"MB\", \"GB\"];\n const i = Math.floor(Math.log(bytes) / Math.log(k));\n return `${Number.parseFloat((bytes / k ** i).toFixed(1))} ${sizes[i]}`;\n };\n\n return (\n <Flex flex={1} direction={\"column\"}>\n <DataTable<FileEntity, typeof filters>\n submitOnInit\n defaultSize={10}\n typeFormProps={{\n skipSubmitButton: true,\n columns: 3,\n }}\n tableProps={{\n horizontalSpacing: \"xs\",\n verticalSpacing: \"xs\",\n }}\n onFilterChange={(key, _value, form) => {\n if (key === \"name\" || key === \"bucket\") {\n return form.submit();\n }\n }}\n filters={filters}\n items={async (filters) => {\n const response = await client.findFiles({\n query: filters,\n });\n\n return response as Page<FileEntity>;\n }}\n columns={{\n name: {\n label: \"Name\",\n value: (item) => (\n <Text size=\"sm\" fw={500} lineClamp={1}>\n {item.name}\n </Text>\n ),\n },\n bucket: {\n label: \"Bucket\",\n fit: true,\n value: (item) => (\n <Badge size=\"sm\" variant=\"light\" color=\"blue\">\n {item.bucket}\n </Badge>\n ),\n },\n mimeType: {\n label: \"Type\",\n fit: true,\n value: (item) => (\n <Text size=\"xs\" c=\"dimmed\">\n {item.mimeType}\n </Text>\n ),\n },\n size: {\n label: \"Size\",\n fit: true,\n value: (item) => (\n <Text size=\"xs\" c=\"dimmed\">\n {formatFileSize(item.size)}\n </Text>\n ),\n },\n creatorName: {\n label: \"Creator\",\n fit: true,\n value: (item) => (\n <Text size=\"xs\" c=\"dimmed\">\n {item.creatorName || \"-\"}\n </Text>\n ),\n },\n createdAt: {\n label: \"Created\",\n fit: true,\n value: (item) => (\n <Text size=\"xs\" c=\"dimmed\">\n {l(item.createdAt, { date: \"fromNow\" })}\n </Text>\n ),\n },\n }}\n />\n </Flex>\n );\n};\n\nexport default AdminFiles;\n"],"mappings":";;;;;;;;;AAOA,MAAM,mBAAmB;CACvB,MAAM,SAAS,WAA2B;CAC1C,MAAM,EAAE,MAAM,SAAS;CAEvB,MAAM,UAAU,EAAE,OAAO;EACvB,QAAQ,EAAE,SAAS,EAAE,QAAQ,CAAC;EAC9B,MAAM,EAAE,SACN,EAAE,OAAO,EACP,UAAU,EACR,OAAO,EAAE,KAAK,MAAM,QAAQ;GAAC;GAAQ;GAAU;GAAW,CAAC,EAC5D,EACF,CAAC,CACH;EACF,CAAC;CAEF,MAAM,kBAAkB,UAAkB;AACxC,MAAI,UAAU,EAAG,QAAO;EACxB,MAAM,IAAI;EACV,MAAM,QAAQ;GAAC;GAAK;GAAM;GAAM;GAAK;EACrC,MAAM,IAAI,KAAK,MAAM,KAAK,IAAI,MAAM,GAAG,KAAK,IAAI,EAAE,CAAC;AACnD,SAAO,GAAG,OAAO,YAAY,QAAQ,KAAK,GAAG,QAAQ,EAAE,CAAC,CAAC,GAAG,MAAM;;AAGpE,QACE,oBAAC;EAAK,MAAM;EAAG,WAAW;YACxB,oBAAC;GACC;GACA,aAAa;GACb,eAAe;IACb,kBAAkB;IAClB,SAAS;IACV;GACD,YAAY;IACV,mBAAmB;IACnB,iBAAiB;IAClB;GACD,iBAAiB,KAAK,QAAQ,SAAS;AACrC,QAAI,QAAQ,UAAU,QAAQ,SAC5B,QAAO,KAAK,QAAQ;;GAGf;GACT,OAAO,OAAO,cAAY;AAKxB,WAJiB,MAAM,OAAO,UAAU,EACtC,OAAOA,WACR,CAAC;;GAIJ,SAAS;IACP,MAAM;KACJ,OAAO;KACP,QAAQ,SACN,oBAAC;MAAK,MAAK;MAAK,IAAI;MAAK,WAAW;gBACjC,KAAK;OACD;KAEV;IACD,QAAQ;KACN,OAAO;KACP,KAAK;KACL,QAAQ,SACN,oBAAC;MAAM,MAAK;MAAK,SAAQ;MAAQ,OAAM;gBACpC,KAAK;OACA;KAEX;IACD,UAAU;KACR,OAAO;KACP,KAAK;KACL,QAAQ,SACN,oBAAC;MAAK,MAAK;MAAK,GAAE;gBACf,KAAK;OACD;KAEV;IACD,MAAM;KACJ,OAAO;KACP,KAAK;KACL,QAAQ,SACN,oBAAC;MAAK,MAAK;MAAK,GAAE;gBACf,eAAe,KAAK,KAAK;OACrB;KAEV;IACD,aAAa;KACX,OAAO;KACP,KAAK;KACL,QAAQ,SACN,oBAAC;MAAK,MAAK;MAAK,GAAE;gBACf,KAAK,eAAe;OAChB;KAEV;IACD,WAAW;KACT,OAAO;KACP,KAAK;KACL,QAAQ,SACN,oBAAC;MAAK,MAAK;MAAK,GAAE;gBACf,EAAE,KAAK,WAAW,EAAE,MAAM,WAAW,CAAC;OAClC;KAEV;IACF;IACD;GACG;;AAIX,yBAAe"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"AdminNotifications-cIbywWKi.js","names":["filters"],"sources":["../../src/admin/components/notifications/AdminNotifications.tsx"],"sourcesContent":["import { useClient } from \"@alepha/react\";\nimport { useI18n } from \"@alepha/react/i18n\";\nimport { DataTable, Flex, Text } from \"@alepha/ui\";\nimport { Badge, Tooltip } from \"@mantine/core\";\nimport {\n IconAlertCircle,\n IconCheck,\n IconClock,\n IconMail,\n IconMessage,\n} from \"@tabler/icons-react\";\nimport { type Page, t } from \"alepha\";\nimport type {\n AdminNotificationController,\n NotificationEntity,\n} from \"alepha/api/notifications\";\n\nconst AdminNotifications = () => {\n const client = useClient<AdminNotificationController>();\n const { l } = useI18n();\n\n const filters = t.object({\n type: t.optional(\n t.enum([\"email\", \"sms\"], {\n title: \"Type\",\n }),\n ),\n status: t.optional(\n t.enum([\"pending\", \"sent\", \"failed\"], {\n title: \"Status\",\n }),\n ),\n template: t.optional(\n t.string({\n title: \"Template\",\n }),\n ),\n contact: t.optional(\n t.string({\n title: \"Contact\",\n }),\n ),\n });\n\n const getStatus = (item: NotificationEntity) => {\n if (item.error) return \"failed\";\n if (item.sentAt) return \"sent\";\n return \"pending\";\n };\n\n const getStatusBadge = (item: NotificationEntity) => {\n const status = getStatus(item);\n switch (status) {\n case \"sent\":\n return (\n <Badge\n size=\"sm\"\n variant=\"light\"\n color=\"green\"\n leftSection={<IconCheck size={12} />}\n >\n Sent\n </Badge>\n );\n case \"failed\":\n return (\n <Tooltip label={item.error?.message} multiline maw={300}>\n <Badge\n size=\"sm\"\n variant=\"light\"\n color=\"red\"\n leftSection={<IconAlertCircle size={12} />}\n >\n Failed\n </Badge>\n </Tooltip>\n );\n default:\n return (\n <Badge\n size=\"sm\"\n variant=\"light\"\n color=\"yellow\"\n leftSection={<IconClock size={12} />}\n >\n Pending\n </Badge>\n );\n }\n };\n\n return (\n <Flex flex={1} direction=\"column\">\n <DataTable<NotificationEntity, typeof filters>\n submitOnInit\n defaultSize={10}\n typeFormProps={{\n skipSubmitButton: true,\n columns: 4,\n }}\n tableProps={{\n horizontalSpacing: \"xs\",\n verticalSpacing: \"xs\",\n }}\n onFilterChange={(_key, _value, form) => {\n return form.submit();\n }}\n filters={filters}\n tableTrProps={(item) => {\n const status = getStatus(item);\n if (status === \"failed\") {\n return {\n bg: \"var(--mantine-color-red-light)\",\n };\n }\n return {};\n }}\n items={async (filters) => {\n const response = await client.findNotifications({\n query: filters,\n });\n\n return response as Page<NotificationEntity>;\n }}\n columns={{\n type: {\n label: \"Type\",\n fit: true,\n value: (item) => (\n <Badge\n size=\"sm\"\n variant=\"outline\"\n leftSection={\n item.type === \"email\" ? (\n <IconMail size={12} />\n ) : (\n <IconMessage size={12} />\n )\n }\n >\n {item.type.toUpperCase()}\n </Badge>\n ),\n },\n template: {\n label: \"Template\",\n value: (item) => (\n <Text size=\"sm\" fw={500}>\n {item.template}\n </Text>\n ),\n },\n contact: {\n label: \"Contact\",\n value: (item) => (\n <Text size=\"sm\" ff=\"monospace\">\n {item.contact}\n </Text>\n ),\n },\n category: {\n label: \"Category\",\n fit: true,\n value: (item) =>\n item.category ? (\n <Badge size=\"xs\" variant=\"light\">\n {item.category}\n </Badge>\n ) : (\n <Text size=\"xs\" c=\"dimmed\">\n -\n </Text>\n ),\n },\n status: {\n label: \"Status\",\n fit: true,\n value: (item) => getStatusBadge(item),\n },\n sentAt: {\n label: \"Sent\",\n fit: true,\n value: (item) => (\n <Text size=\"xs\" c=\"dimmed\">\n {item.sentAt ? l(item.sentAt, { date: \"fromNow\" }) : \"-\"}\n </Text>\n ),\n },\n createdAt: {\n label: \"Created\",\n fit: true,\n value: (item) => (\n <Text size=\"xs\" c=\"dimmed\">\n {l(item.createdAt, { date: \"fromNow\" })}\n </Text>\n ),\n },\n }}\n />\n </Flex>\n );\n};\n\nexport default AdminNotifications;\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"AdminParameters-D-q3Qmhv.js","names":["ParameterTree","ParameterDetails","ParameterHistory"],"sources":["../../src/admin/components/parameters/types.ts","../../src/admin/components/parameters/ParameterDetails.tsx","../../src/admin/components/parameters/ParameterHistory.tsx","../../src/admin/components/parameters/ParameterTree.tsx","../../src/admin/components/parameters/AdminParameters.tsx"],"sourcesContent":["import type { Parameter } from \"alepha/api/parameters\";\n\nexport interface ConfigValue {\n current?: Parameter;\n next?: Parameter;\n /** Default value from the registered $config primitive */\n defaultValue?: unknown;\n /** Current in-memory value (may be default if never saved) */\n currentValue?: unknown;\n /** TypeBox/JSON schema for the configuration (as JSON from API) */\n schema?: Record<string, unknown>;\n}\n\nexport const getStatusColor = (status: string) => {\n switch (status) {\n case \"current\":\n return \"green\";\n case \"next\":\n return \"blue\";\n case \"future\":\n return \"cyan\";\n case \"expired\":\n return \"gray\";\n default:\n return \"gray\";\n }\n};\n\nexport const formatJson = (obj: unknown): string => {\n try {\n return JSON.stringify(obj, null, 2);\n } catch {\n return String(obj);\n }\n};\n","import { useForm } from \"@alepha/react/form\";\nimport { useI18n } from \"@alepha/react/i18n\";\nimport { Flex, Text, TypeForm } from \"@alepha/ui\";\nimport {\n Badge,\n Box,\n Card,\n Code,\n Group,\n Loader,\n ScrollArea,\n Stack,\n} from \"@mantine/core\";\nimport { IconClock, IconSettings } from \"@tabler/icons-react\";\nimport { jsonSchemaToTypeBox, type TObject } from \"alepha\";\nimport { useMemo } from \"react\";\nimport { type ConfigValue, formatJson } from \"./types.ts\";\n\nexport interface ParameterDetailsProps {\n selectedConfig: string | null;\n configValue: ConfigValue | null;\n loading: boolean;\n}\n\nconst ParameterDetails = ({\n selectedConfig,\n configValue,\n loading,\n}: ParameterDetailsProps) => {\n const { l } = useI18n();\n\n // Get the current value to display (from saved version or default)\n const currentContent = useMemo(() => {\n if (configValue?.current?.content) {\n return configValue.current.content;\n }\n if (configValue?.currentValue !== undefined) {\n return configValue.currentValue;\n }\n return null;\n }, [configValue]);\n\n // Convert JSON Schema from API to TypeBox schema\n const schemaForForm = useMemo(() => {\n if (!configValue?.schema) {\n return { type: \"object\", properties: {} } as unknown as TObject;\n }\n return jsonSchemaToTypeBox(configValue.schema) as TObject;\n }, [configValue?.schema]);\n\n const form = useForm(\n {\n schema: schemaForForm,\n initialValues: (currentContent ?? {}) as Record<string, unknown>,\n handler: async () => {\n // Read-only for now\n },\n },\n [selectedConfig, schemaForForm, currentContent],\n );\n\n // Check if we have a valid schema with properties\n const hasValidSchema = useMemo(() => {\n const schema = configValue?.schema;\n return (\n schema &&\n typeof schema === \"object\" &&\n \"properties\" in schema &&\n Object.keys(schema.properties ?? {}).length > 0\n );\n }, [configValue?.schema]);\n\n if (!selectedConfig) {\n return (\n <Card withBorder flex={1} h=\"100%\" style={{ overflow: \"hidden\" }}>\n <Flex flex={1} justify=\"center\" align=\"center\" h=\"100%\">\n <Stack align=\"center\" gap=\"xs\">\n <IconSettings\n size={32}\n stroke={1.5}\n color=\"var(--mantine-color-dimmed)\"\n />\n <Text c=\"dimmed\" size=\"sm\">\n Select a parameter to view its value\n </Text>\n </Stack>\n </Flex>\n </Card>\n );\n }\n\n if (loading) {\n return (\n <Card withBorder flex={1} h=\"100%\" style={{ overflow: \"hidden\" }}>\n <Flex flex={1} justify=\"center\" align=\"center\" h=\"100%\">\n <Loader size=\"sm\" />\n </Flex>\n </Card>\n );\n }\n\n return (\n <Card withBorder flex={1} h=\"100%\" style={{ overflow: \"hidden\" }}>\n <Stack gap=\"md\" h=\"100%\">\n <Group justify=\"space-between\">\n <Stack gap={2}>\n <Text size=\"sm\" fw={500}>\n {selectedConfig}\n </Text>\n {configValue?.current && (\n <Group gap=\"xs\">\n <Badge size=\"xs\" color=\"green\" variant=\"light\">\n v{configValue.current.version}\n </Badge>\n {configValue.next && (\n <Badge size=\"xs\" color=\"blue\" variant=\"light\">\n Next: v{configValue.next.version}\n </Badge>\n )}\n </Group>\n )}\n {!configValue?.current &&\n configValue?.currentValue !== undefined && (\n <Badge size=\"xs\" color=\"yellow\" variant=\"light\">\n Default\n </Badge>\n )}\n </Stack>\n </Group>\n\n <ScrollArea flex={1} offsetScrollbars>\n {currentContent !== null ? (\n <Stack gap=\"md\">\n <Box>\n <Text size=\"xs\" c=\"dimmed\" mb={4}>\n Current Value\n </Text>\n {hasValidSchema ? (\n <TypeForm\n form={form}\n columns={1}\n skipSubmitButton\n skipFormElement\n />\n ) : (\n <Code block style={{ whiteSpace: \"pre-wrap\" }}>\n {formatJson(currentContent)}\n </Code>\n )}\n </Box>\n\n {configValue?.current?.changeDescription && (\n <Box>\n <Text size=\"xs\" c=\"dimmed\" mb={4}>\n Change Description\n </Text>\n <Text size=\"sm\">{configValue.current.changeDescription}</Text>\n </Box>\n )}\n\n {configValue?.current && (\n <Group gap=\"xl\">\n <Box>\n <Text size=\"xs\" c=\"dimmed\" mb={2}>\n Updated\n </Text>\n <Text size=\"sm\">\n {l(configValue.current.updatedAt, { date: \"fromNow\" })}\n </Text>\n </Box>\n {configValue.current.creatorName && (\n <Box>\n <Text size=\"xs\" c=\"dimmed\" mb={2}>\n Updated By\n </Text>\n <Text size=\"sm\">{configValue.current.creatorName}</Text>\n </Box>\n )}\n </Group>\n )}\n\n {!configValue?.current &&\n configValue?.currentValue !== undefined && (\n <Text size=\"xs\" c=\"dimmed\">\n This configuration is using its default value. No versions\n have been saved to the database yet.\n </Text>\n )}\n\n {configValue?.next && (\n <Card withBorder bg=\"blue.0\" p=\"sm\">\n <Stack gap=\"xs\">\n <Group gap=\"xs\">\n <IconClock\n size={14}\n color=\"var(--mantine-color-blue-6)\"\n />\n <Text size=\"xs\" fw={500} c=\"blue.7\">\n Scheduled Update (v{configValue.next.version})\n </Text>\n </Group>\n <Text size=\"xs\" c=\"dimmed\">\n Activates{\" \"}\n {l(configValue.next.activationDate, {\n date: \"fromNow\",\n })}\n </Text>\n <Code block style={{ whiteSpace: \"pre-wrap\" }} fz=\"xs\">\n {formatJson(configValue.next.content)}\n </Code>\n </Stack>\n </Card>\n )}\n </Stack>\n ) : (\n <Flex justify=\"center\" align=\"center\" h={200}>\n <Text c=\"dimmed\" size=\"sm\">\n No current value\n </Text>\n </Flex>\n )}\n </ScrollArea>\n </Stack>\n </Card>\n );\n};\n\nexport default ParameterDetails;\n","import { useI18n } from \"@alepha/react/i18n\";\nimport { ActionButton, Flex, Text } from \"@alepha/ui\";\nimport {\n Badge,\n Card,\n Group,\n Loader,\n ScrollArea,\n Stack,\n Timeline,\n} from \"@mantine/core\";\nimport { IconHistory } from \"@tabler/icons-react\";\nimport type { Parameter } from \"alepha/api/parameters\";\nimport { getStatusColor } from \"./types.ts\";\n\nexport interface ParameterHistoryProps {\n selectedConfig: string | null;\n history: Parameter[];\n loading: boolean;\n onRollback: (version: number) => void;\n}\n\nconst ParameterHistory = ({\n selectedConfig,\n history,\n loading,\n onRollback,\n}: ParameterHistoryProps) => {\n const { l } = useI18n();\n\n const renderContent = () => {\n if (!selectedConfig) {\n return (\n <Flex flex={1} justify=\"center\" align=\"center\">\n <Text c=\"dimmed\" size=\"xs\">\n Select a parameter\n </Text>\n </Flex>\n );\n }\n\n if (loading) {\n return (\n <Flex flex={1} justify=\"center\" align=\"center\">\n <Loader size=\"sm\" />\n </Flex>\n );\n }\n\n if (history.length === 0) {\n return (\n <Flex flex={1} justify=\"center\" align=\"center\">\n <Text c=\"dimmed\" size=\"xs\">\n No history\n </Text>\n </Flex>\n );\n }\n\n return (\n <ScrollArea flex={1} offsetScrollbars>\n <Timeline\n active={history.findIndex((h) => h.status === \"current\")}\n bulletSize={24}\n lineWidth={2}\n >\n {history.map((version) => (\n <Timeline.Item\n key={version.id}\n bullet={\n <Text size=\"xs\" fw={500}>\n {version.version}\n </Text>\n }\n title={\n <Group gap=\"xs\">\n <Text size=\"xs\" fw={500}>\n Version {version.version}\n </Text>\n <Badge\n size=\"xs\"\n variant=\"light\"\n color={getStatusColor(version.status)}\n >\n {version.status}\n </Badge>\n </Group>\n }\n >\n <Stack gap={4} mt={4}>\n <Text size=\"xs\" c=\"dimmed\">\n {l(version.createdAt, { date: \"fromNow\" })}\n </Text>\n {version.changeDescription && (\n <Text size=\"xs\" lineClamp={2}>\n {version.changeDescription}\n </Text>\n )}\n {version.creatorName && (\n <Text size=\"xs\" c=\"dimmed\">\n by {version.creatorName}\n </Text>\n )}\n {version.migrationLog && (\n <Badge size=\"xs\" variant=\"outline\" color=\"orange\">\n Schema Changed\n </Badge>\n )}\n {version.status === \"expired\" && (\n <ActionButton\n size=\"compact-xs\"\n variant=\"subtle\"\n onClick={() => onRollback(version.version)}\n >\n Rollback to this version\n </ActionButton>\n )}\n </Stack>\n </Timeline.Item>\n ))}\n </Timeline>\n </ScrollArea>\n );\n };\n\n return (\n <Card\n withBorder\n w={300}\n h=\"100%\"\n style={{ flexShrink: 0, overflow: \"hidden\" }}\n >\n <Stack gap=\"xs\" h=\"100%\">\n <Group gap=\"xs\">\n <IconHistory size={16} color=\"var(--mantine-color-dimmed)\" />\n <Text size=\"sm\" fw={500}>\n Version History\n </Text>\n </Group>\n {renderContent()}\n </Stack>\n </Card>\n );\n};\n\nexport default ParameterHistory;\n","import { ActionButton, Text } from \"@alepha/ui\";\nimport {\n Box,\n Card,\n Group,\n ScrollArea,\n Stack,\n Tooltip,\n Tree,\n type TreeNodeData,\n useTree,\n} from \"@mantine/core\";\nimport {\n IconChevronDown,\n IconChevronRight,\n IconFolder,\n IconFolderOpen,\n IconRefresh,\n IconSettings,\n} from \"@tabler/icons-react\";\nimport type { ConfigTreeNode } from \"alepha/api/parameters\";\nimport { type HTMLAttributes, useMemo } from \"react\";\n\nexport interface ParameterTreeProps {\n treeData: ConfigTreeNode[];\n selectedConfig: string | null;\n onSelect: (name: string) => void;\n onRefresh: () => void;\n}\n\nconst ParameterTree = ({\n treeData,\n selectedConfig,\n onSelect,\n onRefresh,\n}: ParameterTreeProps) => {\n const tree = useTree({\n initialExpandedState: {},\n });\n\n const mantineTreeData = useMemo((): TreeNodeData[] => {\n const convert = (nodes: ConfigTreeNode[]): TreeNodeData[] => {\n return nodes.map((node) => ({\n value: node.path,\n label: node.name,\n children: node.children.length > 0 ? convert(node.children) : undefined,\n }));\n };\n return convert(treeData);\n }, [treeData]);\n\n const renderNode = ({\n node,\n expanded,\n hasChildren,\n elementProps,\n }: {\n node: TreeNodeData;\n expanded: boolean;\n hasChildren: boolean;\n elementProps: HTMLAttributes<HTMLDivElement>;\n }) => {\n const isLeaf = !hasChildren;\n const isSelected = selectedConfig === node.value;\n\n return (\n <Group\n gap=\"xs\"\n wrap=\"nowrap\"\n {...elementProps}\n onClick={(e) => {\n elementProps.onClick?.(e);\n if (isLeaf) {\n onSelect(node.value);\n }\n }}\n style={{\n ...elementProps.style,\n cursor: isLeaf ? \"pointer\" : \"default\",\n backgroundColor: isSelected\n ? \"var(--mantine-color-blue-light)\"\n : undefined,\n borderRadius: \"var(--mantine-radius-sm)\",\n paddingTop: 4,\n paddingBottom: 4,\n paddingRight: 8,\n }}\n >\n {hasChildren ? (\n <>\n {expanded ? (\n <IconChevronDown size={14} color=\"var(--mantine-color-dimmed)\" />\n ) : (\n <IconChevronRight size={14} color=\"var(--mantine-color-dimmed)\" />\n )}\n {expanded ? (\n <IconFolderOpen size={16} color=\"var(--mantine-color-blue-6)\" />\n ) : (\n <IconFolder size={16} color=\"var(--mantine-color-blue-6)\" />\n )}\n </>\n ) : (\n <>\n <Box w={14} />\n <IconSettings size={16} color=\"var(--mantine-color-gray-6)\" />\n </>\n )}\n <Text size=\"sm\" fw={isSelected ? 500 : 400}>\n {node.label}\n </Text>\n </Group>\n );\n };\n\n return (\n <Card withBorder w={280} h=\"100%\" style={{ flexShrink: 0 }}>\n <Stack gap=\"xs\" h=\"100%\">\n <Group justify=\"space-between\">\n <Text size=\"sm\" fw={500}>\n Parameters\n </Text>\n <Tooltip label=\"Refresh\">\n <ActionButton\n variant=\"subtle\"\n size=\"compact-xs\"\n onClick={onRefresh}\n >\n <IconRefresh size={14} />\n </ActionButton>\n </Tooltip>\n </Group>\n <ScrollArea flex={1} offsetScrollbars>\n <Tree\n data={mantineTreeData}\n tree={tree}\n levelOffset={20}\n expandOnClick\n renderNode={renderNode}\n />\n </ScrollArea>\n </Stack>\n </Card>\n );\n};\n\nexport default ParameterTree;\n","import { useClient } from \"@alepha/react\";\nimport { Flex, Text } from \"@alepha/ui\";\nimport { Loader, Stack } from \"@mantine/core\";\nimport { IconSettings } from \"@tabler/icons-react\";\nimport type {\n AdminConfigController,\n ConfigTreeNode,\n Parameter,\n} from \"alepha/api/parameters\";\nimport { useCallback, useEffect, useState } from \"react\";\nimport ParameterDetails from \"./ParameterDetails.tsx\";\nimport ParameterHistory from \"./ParameterHistory.tsx\";\nimport ParameterTree from \"./ParameterTree.tsx\";\nimport type { ConfigValue } from \"./types.ts\";\n\nconst AdminParameters = () => {\n const client = useClient<AdminConfigController>();\n\n // State\n const [treeData, setTreeData] = useState<ConfigTreeNode[]>([]);\n const [selectedConfig, setSelectedConfig] = useState<string | null>(null);\n const [configValue, setConfigValue] = useState<ConfigValue | null>(null);\n const [history, setHistory] = useState<Parameter[]>([]);\n const [loading, setLoading] = useState(true);\n const [loadingConfig, setLoadingConfig] = useState(false);\n const [loadingHistory, setLoadingHistory] = useState(false);\n\n // Load tree data\n const loadTree = useCallback(async () => {\n try {\n const tree = await client.getConfigTree({});\n setTreeData(tree as ConfigTreeNode[]);\n } finally {\n setLoading(false);\n }\n }, []);\n\n // Load config value and history when selection changes\n const loadConfigDetails = useCallback(async (name: string) => {\n setLoadingConfig(true);\n setLoadingHistory(true);\n\n try {\n const [current, historyData] = await Promise.all([\n client.getCurrent({ params: { name } }),\n client.getHistory({ params: { name } }),\n ]);\n setConfigValue(current);\n setHistory(historyData.versions);\n } finally {\n setLoadingConfig(false);\n setLoadingHistory(false);\n }\n }, []);\n\n // Initial load\n useEffect(() => {\n loadTree();\n }, [loadTree]);\n\n // Load details when selection changes\n useEffect(() => {\n if (selectedConfig) {\n loadConfigDetails(selectedConfig);\n } else {\n setConfigValue(null);\n setHistory([]);\n }\n }, [selectedConfig, loadConfigDetails]);\n\n // Handle rollback\n const handleRollback = async (version: number) => {\n if (!selectedConfig) return;\n\n await client.rollback({\n params: { name: selectedConfig },\n body: { targetVersion: version },\n });\n\n // Reload details\n await loadConfigDetails(selectedConfig);\n };\n\n if (loading) {\n return (\n <Flex flex={1} justify=\"center\" align=\"center\">\n <Loader />\n </Flex>\n );\n }\n\n // Empty state when no configs exist\n if (treeData.length === 0) {\n return (\n <Flex flex={1} justify=\"center\" align=\"center\">\n <Stack align=\"center\" gap=\"xs\">\n <IconSettings\n size={48}\n stroke={1.5}\n color=\"var(--mantine-color-dimmed)\"\n />\n <Text c=\"dimmed\">No Parameters Found</Text>\n <Text size=\"xs\" c=\"dimmed\" ta=\"center\" maw={400}>\n Define parameters using the $config primitive to manage dynamic\n application settings. Parameters will appear here once created.\n </Text>\n </Stack>\n </Flex>\n );\n }\n\n return (\n <Flex flex={1} gap={\"xs\"} h=\"100%\">\n <ParameterTree\n treeData={treeData}\n selectedConfig={selectedConfig}\n onSelect={setSelectedConfig}\n onRefresh={loadTree}\n />\n\n <ParameterDetails\n selectedConfig={selectedConfig}\n configValue={configValue}\n loading={loadingConfig}\n />\n\n <ParameterHistory\n selectedConfig={selectedConfig}\n history={history}\n loading={loadingHistory}\n onRollback={handleRollback}\n />\n </Flex>\n );\n};\n\nexport default AdminParameters;\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"AdminSessions-vOgkrQ2U.js","names":["filters"],"sources":["../../src/admin/components/sessions/AdminSessions.tsx"],"sourcesContent":["import { useClient } from \"@alepha/react\";\nimport { useI18n } from \"@alepha/react/i18n\";\nimport { useRouter } from \"@alepha/react/router\";\nimport { ActionButton, DataTable, Flex, Text } from \"@alepha/ui\";\nimport { Badge, Group } from \"@mantine/core\";\nimport {\n IconDeviceDesktop,\n IconDeviceMobile,\n IconDeviceTablet,\n IconTrash,\n} from \"@tabler/icons-react\";\nimport { type Page, t } from \"alepha\";\nimport {\n type AdminSessionController,\n type SessionEntity,\n sessions,\n} from \"alepha/api/users\";\nimport { useState } from \"react\";\nimport type { AdminRouter } from \"../../AdminRouter.ts\";\n\nexport interface AdminSessionsProps {\n userRealmName?: string;\n}\n\nconst AdminSessions = (props: AdminSessionsProps) => {\n const client = useClient<AdminSessionController>();\n const router = useRouter<AdminRouter>();\n const { l } = useI18n();\n const [refreshKey, setRefreshKey] = useState(0);\n\n const filters = t.object({\n userId: t.optional(\n t.uuid({\n $control: {\n query: t.pick(sessions.schema, [\"userId\"]),\n },\n }),\n ),\n });\n\n const getDeviceIcon = (device?: string) => {\n switch (device) {\n case \"MOBILE\":\n return <IconDeviceMobile size={14} />;\n case \"TABLET\":\n return <IconDeviceTablet size={14} />;\n default:\n return <IconDeviceDesktop size={14} />;\n }\n };\n\n const isExpired = (expiresAt: Date | string) => {\n return new Date(expiresAt) < new Date();\n };\n\n const handleDelete = async (sessionId: string) => {\n await client.deleteSession({\n params: { id: sessionId },\n query: { userRealmName: props.userRealmName },\n });\n setRefreshKey((k) => k + 1);\n };\n\n return (\n <Flex flex={1} direction=\"column\">\n <DataTable<SessionEntity, typeof filters>\n key={refreshKey}\n submitOnInit\n defaultSize={10}\n typeFormProps={{\n skipSubmitButton: true,\n columns: 3,\n }}\n tableProps={{\n horizontalSpacing: \"xs\",\n verticalSpacing: \"xs\",\n }}\n onFilterChange={(key, _value, form) => {\n if (key === \"userId\") {\n return form.submit();\n }\n }}\n filters={filters}\n tableTrProps={(item) => {\n if (isExpired(item.expiresAt)) {\n return {\n opacity: 0.5,\n };\n }\n return {};\n }}\n items={async (filters) => {\n const response = await client.findSessions({\n query: {\n ...filters,\n userRealmName: props.userRealmName,\n },\n });\n\n return response as Page<SessionEntity>;\n }}\n columns={{\n userId: {\n label: \"User\",\n value: (item) => (\n <ActionButton\n variant=\"subtle\"\n size=\"xs\"\n href={router.path(\"adminUserDetails\", {\n params: { userId: item.userId },\n })}\n >\n <Text size=\"xs\" ff=\"monospace\">\n {item.userId.slice(0, 8)}...\n </Text>\n </ActionButton>\n ),\n },\n userAgent: {\n label: \"Device\",\n fit: true,\n value: (item) => (\n <Group gap={4}>\n {item.userAgent ? (\n <>\n <Badge\n size=\"xs\"\n variant=\"light\"\n leftSection={getDeviceIcon(item.userAgent.device)}\n >\n {item.userAgent.device}\n </Badge>\n <Text size=\"xs\" c=\"dimmed\">\n {item.userAgent.browser} / {item.userAgent.os}\n </Text>\n </>\n ) : (\n <Text size=\"xs\" c=\"dimmed\">\n -\n </Text>\n )}\n </Group>\n ),\n },\n ip: {\n label: \"IP\",\n fit: true,\n value: (item) => (\n <Text size=\"xs\" ff=\"monospace\" c=\"dimmed\">\n {item.ip || \"-\"}\n </Text>\n ),\n },\n expiresAt: {\n label: \"Status\",\n fit: true,\n value: (item) => (\n <Badge\n size=\"sm\"\n variant=\"light\"\n color={isExpired(item.expiresAt) ? \"red\" : \"green\"}\n >\n {isExpired(item.expiresAt) ? \"Expired\" : \"Active\"}\n </Badge>\n ),\n },\n createdAt: {\n label: \"Created\",\n fit: true,\n value: (item) => (\n <Text size=\"xs\" c=\"dimmed\">\n {l(item.createdAt, { date: \"fromNow\" })}\n </Text>\n ),\n },\n actions: {\n label: \"\",\n fit: true,\n value: (item) => (\n <ActionButton\n size=\"xs\"\n variant=\"subtle\"\n color=\"red\"\n onClick={() => handleDelete(item.id)}\n >\n <IconTrash size={14} />\n </ActionButton>\n ),\n },\n }}\n />\n </Flex>\n );\n};\n\nexport default AdminSessions;\n"],"mappings":";;;;;;;;;;;;AAwBA,MAAM,iBAAiB,UAA8B;CACnD,MAAM,SAAS,WAAmC;CAClD,MAAM,SAAS,WAAwB;CACvC,MAAM,EAAE,MAAM,SAAS;CACvB,MAAM,CAAC,YAAY,iBAAiB,SAAS,EAAE;CAE/C,MAAM,UAAU,EAAE,OAAO,EACvB,QAAQ,EAAE,SACR,EAAE,KAAK,EACL,UAAU,EACR,OAAO,EAAE,KAAK,SAAS,QAAQ,CAAC,SAAS,CAAC,EAC3C,EACF,CAAC,CACH,EACF,CAAC;CAEF,MAAM,iBAAiB,WAAoB;AACzC,UAAQ,QAAR;GACE,KAAK,SACH,QAAO,oBAAC,oBAAiB,MAAM,KAAM;GACvC,KAAK,SACH,QAAO,oBAAC,oBAAiB,MAAM,KAAM;GACvC,QACE,QAAO,oBAAC,qBAAkB,MAAM,KAAM;;;CAI5C,MAAM,aAAa,cAA6B;AAC9C,SAAO,IAAI,KAAK,UAAU,mBAAG,IAAI,MAAM;;CAGzC,MAAM,eAAe,OAAO,cAAsB;AAChD,QAAM,OAAO,cAAc;GACzB,QAAQ,EAAE,IAAI,WAAW;GACzB,OAAO,EAAE,eAAe,MAAM,eAAe;GAC9C,CAAC;AACF,iBAAe,MAAM,IAAI,EAAE;;AAG7B,QACE,oBAAC;EAAK,MAAM;EAAG,WAAU;YACvB,oBAAC;GAEC;GACA,aAAa;GACb,eAAe;IACb,kBAAkB;IAClB,SAAS;IACV;GACD,YAAY;IACV,mBAAmB;IACnB,iBAAiB;IAClB;GACD,iBAAiB,KAAK,QAAQ,SAAS;AACrC,QAAI,QAAQ,SACV,QAAO,KAAK,QAAQ;;GAGf;GACT,eAAe,SAAS;AACtB,QAAI,UAAU,KAAK,UAAU,CAC3B,QAAO,EACL,SAAS,IACV;AAEH,WAAO,EAAE;;GAEX,OAAO,OAAO,cAAY;AAQxB,WAPiB,MAAM,OAAO,aAAa,EACzC,OAAO;KACL,GAAGA;KACH,eAAe,MAAM;KACtB,EACF,CAAC;;GAIJ,SAAS;IACP,QAAQ;KACN,OAAO;KACP,QAAQ,SACN,oBAAC;MACC,SAAQ;MACR,MAAK;MACL,MAAM,OAAO,KAAK,oBAAoB,EACpC,QAAQ,EAAE,QAAQ,KAAK,QAAQ,EAChC,CAAC;gBAEF,qBAAC;OAAK,MAAK;OAAK,IAAG;kBAChB,KAAK,OAAO,MAAM,GAAG,EAAE,EAAC;QACpB;OACM;KAElB;IACD,WAAW;KACT,OAAO;KACP,KAAK;KACL,QAAQ,SACN,oBAAC;MAAM,KAAK;gBACT,KAAK,YACJ,4CACE,oBAAC;OACC,MAAK;OACL,SAAQ;OACR,aAAa,cAAc,KAAK,UAAU,OAAO;iBAEhD,KAAK,UAAU;QACV,EACR,qBAAC;OAAK,MAAK;OAAK,GAAE;;QACf,KAAK,UAAU;QAAQ;QAAI,KAAK,UAAU;;QACtC,IACN,GAEH,oBAAC;OAAK,MAAK;OAAK,GAAE;iBAAS;QAEpB;OAEH;KAEX;IACD,IAAI;KACF,OAAO;KACP,KAAK;KACL,QAAQ,SACN,oBAAC;MAAK,MAAK;MAAK,IAAG;MAAY,GAAE;gBAC9B,KAAK,MAAM;OACP;KAEV;IACD,WAAW;KACT,OAAO;KACP,KAAK;KACL,QAAQ,SACN,oBAAC;MACC,MAAK;MACL,SAAQ;MACR,OAAO,UAAU,KAAK,UAAU,GAAG,QAAQ;gBAE1C,UAAU,KAAK,UAAU,GAAG,YAAY;OACnC;KAEX;IACD,WAAW;KACT,OAAO;KACP,KAAK;KACL,QAAQ,SACN,oBAAC;MAAK,MAAK;MAAK,GAAE;gBACf,EAAE,KAAK,WAAW,EAAE,MAAM,WAAW,CAAC;OAClC;KAEV;IACD,SAAS;KACP,OAAO;KACP,KAAK;KACL,QAAQ,SACN,oBAAC;MACC,MAAK;MACL,SAAQ;MACR,OAAM;MACN,eAAe,aAAa,KAAK,GAAG;gBAEpC,oBAAC,aAAU,MAAM,KAAM;OACV;KAElB;IACF;KA3HI,WA4HL;GACG;;AAIX,4BAAe"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"AdminUserAudits-CSsN1fIC.js","names":[],"sources":["../../src/admin/components/users/AdminUserAudits.tsx"],"sourcesContent":["import { useClient } from \"@alepha/react\";\nimport { useI18n } from \"@alepha/react/i18n\";\nimport { useRouterState } from \"@alepha/react/router\";\nimport { DataTable, Flex, Text } from \"@alepha/ui\";\nimport { Badge, Group, Tooltip } from \"@mantine/core\";\nimport {\n IconAlertTriangle,\n IconCheck,\n IconInfoCircle,\n IconX,\n} from \"@tabler/icons-react\";\nimport { type Page, t } from \"alepha\";\nimport type { AdminAuditController, AuditEntity } from \"alepha/api/audits\";\n\nexport interface AdminUserAuditsProps {\n userRealmName?: string;\n}\n\nconst getSeverityColor = (severity: string) => {\n switch (severity) {\n case \"critical\":\n return \"red\";\n case \"warning\":\n return \"yellow\";\n default:\n return \"blue\";\n }\n};\n\nconst getSeverityIcon = (severity: string) => {\n switch (severity) {\n case \"critical\":\n return <IconAlertTriangle size={12} />;\n case \"warning\":\n return <IconAlertTriangle size={12} />;\n default:\n return <IconInfoCircle size={12} />;\n }\n};\n\nconst AdminUserAudits = (_props: AdminUserAuditsProps) => {\n const state = useRouterState();\n const client = useClient<AdminAuditController>();\n const { l } = useI18n();\n const userId = state.params.userId as string;\n\n const filters = t.object({\n type: t.optional(t.text()),\n action: t.optional(t.text()),\n severity: t.optional(t.enum([\"info\", \"warning\", \"critical\"])),\n success: t.optional(t.boolean()),\n from: t.optional(t.datetime()),\n to: t.optional(t.datetime()),\n });\n\n return (\n <Flex flex={1} direction=\"column\">\n <DataTable<AuditEntity, typeof filters>\n submitOnInit\n defaultSize={15}\n typeFormProps={{\n skipSubmitButton: true,\n columns: 4,\n }}\n tableProps={{\n horizontalSpacing: \"xs\",\n verticalSpacing: \"xs\",\n striped: false,\n highlightOnHover: true,\n }}\n filters={filters}\n items={async (query) => {\n const response = await client.findByUser({\n params: { userId },\n query,\n });\n return response as Page<AuditEntity>;\n }}\n columns={{\n type: {\n label: \"Type\",\n fit: true,\n value: (item) => (\n <Badge size=\"sm\" variant=\"light\" color=\"grape\">\n {item.type}\n </Badge>\n ),\n },\n action: {\n label: \"Action\",\n fit: true,\n value: (item) => (\n <Badge size=\"sm\" variant=\"outline\">\n {item.action}\n </Badge>\n ),\n },\n severity: {\n label: \"Severity\",\n fit: true,\n value: (item) => (\n <Badge\n size=\"sm\"\n variant=\"light\"\n color={getSeverityColor(item.severity)}\n leftSection={getSeverityIcon(item.severity)}\n >\n {item.severity}\n </Badge>\n ),\n },\n description: {\n label: \"Description\",\n value: (item) => (\n <Text size=\"sm\" lineClamp={1}>\n {item.description || \"-\"}\n </Text>\n ),\n },\n resource: {\n label: \"Resource\",\n fit: true,\n value: (item) =>\n item.resourceType ? (\n <Tooltip label={item.resourceId || \"N/A\"}>\n <Badge size=\"xs\" variant=\"dot\" color=\"gray\">\n {item.resourceType}\n </Badge>\n </Tooltip>\n ) : (\n <Text size=\"xs\" c=\"dimmed\">\n -\n </Text>\n ),\n },\n success: {\n label: \"Status\",\n fit: true,\n value: (item) =>\n item.success ? (\n <Group gap={4}>\n <IconCheck size={14} color=\"var(--mantine-color-green-6)\" />\n <Text size=\"xs\" c=\"green\">\n Success\n </Text>\n </Group>\n ) : (\n <Tooltip label={item.errorMessage || \"Failed\"}>\n <Group gap={4}>\n <IconX size={14} color=\"var(--mantine-color-red-6)\" />\n <Text size=\"xs\" c=\"red\">\n Failed\n </Text>\n </Group>\n </Tooltip>\n ),\n },\n ipAddress: {\n label: \"IP\",\n fit: true,\n value: (item) => (\n <Text size=\"xs\" c=\"dimmed\" ff=\"monospace\">\n {item.ipAddress || \"-\"}\n </Text>\n ),\n },\n createdAt: {\n label: \"Time\",\n fit: true,\n value: (item) => (\n <Tooltip label={l(item.createdAt, { date: \"medium\" })}>\n <Text size=\"xs\" c=\"dimmed\">\n {l(item.createdAt, { date: \"fromNow\" })}\n </Text>\n </Tooltip>\n ),\n },\n }}\n />\n </Flex>\n );\n};\n\nexport default AdminUserAudits;\n"],"mappings":";;;;;;;;;;AAkBA,MAAM,oBAAoB,aAAqB;AAC7C,SAAQ,UAAR;EACE,KAAK,WACH,QAAO;EACT,KAAK,UACH,QAAO;EACT,QACE,QAAO;;;AAIb,MAAM,mBAAmB,aAAqB;AAC5C,SAAQ,UAAR;EACE,KAAK,WACH,QAAO,oBAAC,qBAAkB,MAAM,KAAM;EACxC,KAAK,UACH,QAAO,oBAAC,qBAAkB,MAAM,KAAM;EACxC,QACE,QAAO,oBAAC,kBAAe,MAAM,KAAM;;;AAIzC,MAAM,mBAAmB,WAAiC;CACxD,MAAM,QAAQ,gBAAgB;CAC9B,MAAM,SAAS,WAAiC;CAChD,MAAM,EAAE,MAAM,SAAS;CACvB,MAAM,SAAS,MAAM,OAAO;AAW5B,QACE,oBAAC;EAAK,MAAM;EAAG,WAAU;YACvB,oBAAC;GACC;GACA,aAAa;GACb,eAAe;IACb,kBAAkB;IAClB,SAAS;IACV;GACD,YAAY;IACV,mBAAmB;IACnB,iBAAiB;IACjB,SAAS;IACT,kBAAkB;IACnB;GACD,SAxBU,EAAE,OAAO;IACvB,MAAM,EAAE,SAAS,EAAE,MAAM,CAAC;IAC1B,QAAQ,EAAE,SAAS,EAAE,MAAM,CAAC;IAC5B,UAAU,EAAE,SAAS,EAAE,KAAK;KAAC;KAAQ;KAAW;KAAW,CAAC,CAAC;IAC7D,SAAS,EAAE,SAAS,EAAE,SAAS,CAAC;IAChC,MAAM,EAAE,SAAS,EAAE,UAAU,CAAC;IAC9B,IAAI,EAAE,SAAS,EAAE,UAAU,CAAC;IAC7B,CAAC;GAkBI,OAAO,OAAO,UAAU;AAKtB,WAJiB,MAAM,OAAO,WAAW;KACvC,QAAQ,EAAE,QAAQ;KAClB;KACD,CAAC;;GAGJ,SAAS;IACP,MAAM;KACJ,OAAO;KACP,KAAK;KACL,QAAQ,SACN,oBAAC;MAAM,MAAK;MAAK,SAAQ;MAAQ,OAAM;gBACpC,KAAK;OACA;KAEX;IACD,QAAQ;KACN,OAAO;KACP,KAAK;KACL,QAAQ,SACN,oBAAC;MAAM,MAAK;MAAK,SAAQ;gBACtB,KAAK;OACA;KAEX;IACD,UAAU;KACR,OAAO;KACP,KAAK;KACL,QAAQ,SACN,oBAAC;MACC,MAAK;MACL,SAAQ;MACR,OAAO,iBAAiB,KAAK,SAAS;MACtC,aAAa,gBAAgB,KAAK,SAAS;gBAE1C,KAAK;OACA;KAEX;IACD,aAAa;KACX,OAAO;KACP,QAAQ,SACN,oBAAC;MAAK,MAAK;MAAK,WAAW;gBACxB,KAAK,eAAe;OAChB;KAEV;IACD,UAAU;KACR,OAAO;KACP,KAAK;KACL,QAAQ,SACN,KAAK,eACH,oBAAC;MAAQ,OAAO,KAAK,cAAc;gBACjC,oBAAC;OAAM,MAAK;OAAK,SAAQ;OAAM,OAAM;iBAClC,KAAK;QACA;OACA,GAEV,oBAAC;MAAK,MAAK;MAAK,GAAE;gBAAS;OAEpB;KAEZ;IACD,SAAS;KACP,OAAO;KACP,KAAK;KACL,QAAQ,SACN,KAAK,UACH,qBAAC;MAAM,KAAK;iBACV,oBAAC;OAAU,MAAM;OAAI,OAAM;QAAiC,EAC5D,oBAAC;OAAK,MAAK;OAAK,GAAE;iBAAQ;QAEnB;OACD,GAER,oBAAC;MAAQ,OAAO,KAAK,gBAAgB;gBACnC,qBAAC;OAAM,KAAK;kBACV,oBAAC;QAAM,MAAM;QAAI,OAAM;SAA+B,EACtD,oBAAC;QAAK,MAAK;QAAK,GAAE;kBAAM;SAEjB;QACD;OACA;KAEf;IACD,WAAW;KACT,OAAO;KACP,KAAK;KACL,QAAQ,SACN,oBAAC;MAAK,MAAK;MAAK,GAAE;MAAS,IAAG;gBAC3B,KAAK,aAAa;OACd;KAEV;IACD,WAAW;KACT,OAAO;KACP,KAAK;KACL,QAAQ,SACN,oBAAC;MAAQ,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,UAAU,CAAC;gBACnD,oBAAC;OAAK,MAAK;OAAK,GAAE;iBACf,EAAE,KAAK,WAAW,EAAE,MAAM,WAAW,CAAC;QAClC;OACC;KAEb;IACF;IACD;GACG;;AAIX,8BAAe"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"AdminUserCreate-B72nu-3W.js","names":["Text"],"sources":["../../src/admin/components/users/AdminUserCreate.tsx"],"sourcesContent":["import { useClient } from \"@alepha/react\";\nimport { useForm } from \"@alepha/react/form\";\nimport { useRouter } from \"@alepha/react/router\";\nimport { ActionButton, Control, Flex } from \"@alepha/ui\";\nimport { Card, Stack, Text } from \"@mantine/core\";\nimport { t } from \"alepha\";\nimport type { AdminUserController } from \"alepha/api/users\";\nimport type { AdminRouter } from \"../../AdminRouter.ts\";\n\nexport interface AdminUserCreateProps {\n userRealmName?: string;\n}\n\nconst AdminUserCreate = (props: AdminUserCreateProps) => {\n const client = useClient<AdminUserController>();\n const router = useRouter<AdminRouter>();\n\n const form = useForm({\n schema: t.object({\n username: t.optional(\n t.shortText({\n minLength: 3,\n maxLength: 50,\n pattern: \"^[a-zA-Z0-9._-]+$\",\n }),\n ),\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 password: t.optional(t.string({ minLength: 8 })),\n }),\n handler: async (data) => {\n const user = await client.createUser({\n query: {\n userRealmName: props.userRealmName,\n },\n body: {\n ...data,\n enabled: data.enabled ?? true,\n },\n });\n\n await router.go(\"adminUserDetails\", {\n params: { userId: user.id },\n });\n },\n });\n\n return (\n <Flex flex={1} p=\"md\">\n <Card withBorder p=\"lg\" maw={600} w=\"100%\">\n <form {...form.props}>\n <Stack gap=\"md\">\n <Text size=\"lg\" fw={500}>\n Create New User\n </Text>\n\n <Control title=\"Username\" input={form.input.username} />\n\n <Control title=\"Email\" input={form.input.email} />\n\n <Control title=\"Phone Number\" input={form.input.phoneNumber} />\n\n <Control title=\"First Name\" input={form.input.firstName} />\n\n <Control title=\"Last Name\" input={form.input.lastName} />\n\n <Control title=\"Password\" input={form.input.password} password />\n\n <Control title=\"Roles\" input={form.input.roles} />\n\n <Control title=\"Enabled\" input={form.input.enabled} />\n\n <ActionButton form={form}>Create User</ActionButton>\n </Stack>\n </form>\n </Card>\n </Flex>\n );\n};\n\nexport default AdminUserCreate;\n"],"mappings":";;;;;;;;;AAaA,MAAM,mBAAmB,UAAgC;CACvD,MAAM,SAAS,WAAgC;CAC/C,MAAM,SAAS,WAAwB;CAEvC,MAAM,OAAO,QAAQ;EACnB,QAAQ,EAAE,OAAO;GACf,UAAU,EAAE,SACV,EAAE,UAAU;IACV,WAAW;IACX,WAAW;IACX,SAAS;IACV,CAAC,CACH;GACD,OAAO,EAAE,SAAS,EAAE,OAAO,CAAC;GAC5B,aAAa,EAAE,SAAS,EAAE,MAAM,CAAC;GACjC,WAAW,EAAE,SAAS,EAAE,QAAQ,CAAC;GACjC,UAAU,EAAE,SAAS,EAAE,QAAQ,CAAC;GAChC,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC;GACtC,SAAS,EAAE,SAAS,EAAE,SAAS,CAAC;GAChC,UAAU,EAAE,SAAS,EAAE,OAAO,EAAE,WAAW,GAAG,CAAC,CAAC;GACjD,CAAC;EACF,SAAS,OAAO,SAAS;GACvB,MAAM,OAAO,MAAM,OAAO,WAAW;IACnC,OAAO,EACL,eAAe,MAAM,eACtB;IACD,MAAM;KACJ,GAAG;KACH,SAAS,KAAK,WAAW;KAC1B;IACF,CAAC;AAEF,SAAM,OAAO,GAAG,oBAAoB,EAClC,QAAQ,EAAE,QAAQ,KAAK,IAAI,EAC5B,CAAC;;EAEL,CAAC;AAEF,QACE,oBAAC;EAAK,MAAM;EAAG,GAAE;YACf,oBAAC;GAAK;GAAW,GAAE;GAAK,KAAK;GAAK,GAAE;aAClC,oBAAC;IAAK,GAAI,KAAK;cACb,qBAAC;KAAM,KAAI;;MACT,oBAACA;OAAK,MAAK;OAAK,IAAI;iBAAK;QAElB;MAEP,oBAAC;OAAQ,OAAM;OAAW,OAAO,KAAK,MAAM;QAAY;MAExD,oBAAC;OAAQ,OAAM;OAAQ,OAAO,KAAK,MAAM;QAAS;MAElD,oBAAC;OAAQ,OAAM;OAAe,OAAO,KAAK,MAAM;QAAe;MAE/D,oBAAC;OAAQ,OAAM;OAAa,OAAO,KAAK,MAAM;QAAa;MAE3D,oBAAC;OAAQ,OAAM;OAAY,OAAO,KAAK,MAAM;QAAY;MAEzD,oBAAC;OAAQ,OAAM;OAAW,OAAO,KAAK,MAAM;OAAU;QAAW;MAEjE,oBAAC;OAAQ,OAAM;OAAQ,OAAO,KAAK,MAAM;QAAS;MAElD,oBAAC;OAAQ,OAAM;OAAU,OAAO,KAAK,MAAM;QAAW;MAEtD,oBAAC;OAAmB;iBAAM;QAA0B;;MAC9C;KACH;IACF;GACF;;AAIX,8BAAe"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"AdminUserDetails-CKM2IEMr.js","names":["Text"],"sources":["../../src/admin/components/users/AdminUserDetails.tsx"],"sourcesContent":["import { useClient } from \"@alepha/react\";\nimport { useForm } from \"@alepha/react/form\";\nimport { useI18n } from \"@alepha/react/i18n\";\nimport { useRouterState } from \"@alepha/react/router\";\nimport { ActionButton, ClipboardButton, Control } from \"@alepha/ui\";\nimport {\n Badge,\n Box,\n Card,\n Center,\n Divider,\n Grid,\n Group,\n Loader,\n Paper,\n SimpleGrid,\n Stack,\n Text,\n ThemeIcon,\n} from \"@mantine/core\";\nimport {\n IconActivity,\n IconCalendar,\n IconCheck,\n IconDevices,\n IconKey,\n IconShieldCheck,\n IconUser,\n IconX,\n} from \"@tabler/icons-react\";\nimport { t } from \"alepha\";\nimport type { AdminUserController, UserEntity } from \"alepha/api/users\";\nimport { type ReactNode, useEffect, useState } from \"react\";\n\nexport interface AdminUserDetailsProps {\n userRealmName?: string;\n}\n\ninterface DataRowProps {\n label: string;\n value: ReactNode;\n copyValue?: string;\n}\n\nconst DataRow = ({ label, value, copyValue }: DataRowProps) => (\n <Group\n justify=\"space-between\"\n py={8}\n wrap=\"nowrap\"\n style={{ borderBottom: \"1px solid var(--mantine-color-default-border)\" }}\n >\n <Text size=\"sm\" c=\"dimmed\" style={{ flexShrink: 0 }}>\n {label}\n </Text>\n <Group gap={6} wrap=\"nowrap\" style={{ minWidth: 0 }}>\n {typeof value === \"string\" ? (\n <Text size=\"sm\" fw={500} truncate style={{ maxWidth: 220 }}>\n {value || \"—\"}\n </Text>\n ) : (\n value\n )}\n {copyValue && (\n <ClipboardButton\n value={copyValue}\n size=\"xs\"\n variant=\"subtle\"\n c=\"dimmed\"\n />\n )}\n </Group>\n </Group>\n);\n\ninterface StatCardProps {\n icon: ReactNode;\n label: string;\n value: string | number;\n color: string;\n}\n\nconst StatCard = ({ icon, label, value, color }: StatCardProps) => (\n <Paper p=\"md\" radius=\"md\" withBorder>\n <Group gap=\"sm\">\n <ThemeIcon size=\"lg\" radius=\"md\" variant=\"light\" color={color}>\n {icon}\n </ThemeIcon>\n <Box>\n <Text size=\"xl\" fw={700} lh={1}>\n {value}\n </Text>\n <Text size=\"xs\" c=\"dimmed\">\n {label}\n </Text>\n </Box>\n </Group>\n </Paper>\n);\n\nconst AdminUserDetails = (props: AdminUserDetailsProps) => {\n const state = useRouterState();\n const client = useClient<AdminUserController>();\n const { l } = useI18n();\n const userId = state.params.userId as string;\n\n const [user, setUser] = useState<UserEntity | null>(null);\n const [loading, setLoading] = useState(true);\n const [editing, setEditing] = useState(false);\n\n useEffect(() => {\n const loadUser = async () => {\n try {\n const data = await client.getUser({\n params: { id: userId },\n query: { userRealmName: props.userRealmName },\n });\n setUser(data);\n } finally {\n setLoading(false);\n }\n };\n\n loadUser();\n }, [userId]);\n\n const form = useForm({\n schema: 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 handler: async (data) => {\n const updated = await client.updateUser({\n params: { id: userId },\n query: { userRealmName: props.userRealmName },\n body: data,\n });\n setUser(updated);\n setEditing(false);\n },\n });\n\n useEffect(() => {\n if (user) {\n form.input.email?.set(user.email ?? \"\");\n form.input.phoneNumber?.set(user.phoneNumber ?? \"\");\n form.input.firstName?.set(user.firstName ?? \"\");\n form.input.lastName?.set(user.lastName ?? \"\");\n form.input.roles?.set(user.roles ?? []);\n form.input.enabled?.set(user.enabled);\n }\n }, [user]);\n\n if (loading) {\n return (\n <Center flex={1} py=\"xl\">\n <Loader />\n </Center>\n );\n }\n\n if (!user) {\n return (\n <Center flex={1} py=\"xl\">\n <Stack align=\"center\" gap=\"xs\">\n <IconUser size={48} opacity={0.3} />\n <Text c=\"dimmed\">User not found</Text>\n </Stack>\n </Center>\n );\n }\n\n const displayName =\n user.firstName && user.lastName\n ? `${user.firstName} ${user.lastName}`\n : user.firstName || user.lastName || null;\n\n return (\n <Stack gap=\"md\">\n {/* Stats Overview */}\n <SimpleGrid cols={{ base: 2, sm: 4 }}>\n <StatCard\n icon={<IconDevices size={18} />}\n label=\"Sessions\"\n value={0}\n color=\"blue\"\n />\n <StatCard\n icon={<IconActivity size={18} />}\n label=\"API Calls\"\n value={0}\n color=\"green\"\n />\n <StatCard\n icon={<IconKey size={18} />}\n label=\"Failed Logins\"\n value={0}\n color=\"orange\"\n />\n <StatCard\n icon={<IconShieldCheck size={18} />}\n label=\"Roles\"\n value={user.roles.length}\n color=\"violet\"\n />\n </SimpleGrid>\n\n <Grid>\n {/* Left Column - Account Details */}\n <Grid.Col span={{ base: 12, md: 6 }}>\n <Card padding={0} radius=\"md\" withBorder h=\"100%\">\n <Group justify=\"space-between\" p=\"md\" pb={0}>\n <Text fw={600} size=\"sm\">\n Account Details\n </Text>\n </Group>\n <Box px=\"md\" pb=\"md\">\n <DataRow label=\"User ID\" value={user.id} copyValue={user.id} />\n <DataRow\n label=\"Username\"\n value={user.username || \"—\"}\n copyValue={user.username}\n />\n <DataRow\n label=\"Email\"\n value={\n <Group gap={6}>\n <Text size=\"sm\" fw={500}>\n {user.email || \"—\"}\n </Text>\n {user.email && (\n <Badge\n size=\"xs\"\n variant=\"light\"\n color={user.emailVerified ? \"green\" : \"orange\"}\n >\n {user.emailVerified ? \"verified\" : \"unverified\"}\n </Badge>\n )}\n </Group>\n }\n copyValue={user.email}\n />\n <DataRow\n label=\"Phone\"\n value={user.phoneNumber || \"—\"}\n copyValue={user.phoneNumber}\n />\n <DataRow label=\"Realm\" value={user.realm} />\n <DataRow\n label=\"Status\"\n value={\n <Group gap={4}>\n <ThemeIcon\n size={16}\n radius=\"xl\"\n color={user.enabled ? \"green\" : \"red\"}\n variant=\"filled\"\n >\n {user.enabled ? (\n <IconCheck size={10} />\n ) : (\n <IconX size={10} />\n )}\n </ThemeIcon>\n <Text size=\"sm\" fw={500}>\n {user.enabled ? \"Active\" : \"Disabled\"}\n </Text>\n </Group>\n }\n />\n </Box>\n </Card>\n </Grid.Col>\n\n {/* Right Column - Personal Info */}\n <Grid.Col span={{ base: 12, md: 6 }}>\n <Card padding={0} radius=\"md\" withBorder h=\"100%\">\n <Group justify=\"space-between\" p=\"md\" pb={0}>\n <Text fw={600} size=\"sm\">\n Personal Information\n </Text>\n {!editing && (\n <ActionButton\n variant=\"subtle\"\n size=\"xs\"\n onClick={() => setEditing(true)}\n >\n Edit\n </ActionButton>\n )}\n </Group>\n\n {editing ? (\n <Box p=\"md\">\n <form {...form.props}>\n <Stack gap=\"sm\">\n <SimpleGrid cols={2}>\n <Control\n title=\"First Name\"\n input={form.input.firstName}\n />\n <Control title=\"Last Name\" input={form.input.lastName} />\n </SimpleGrid>\n <SimpleGrid cols={2}>\n <Control title=\"Email\" input={form.input.email} />\n <Control title=\"Phone\" input={form.input.phoneNumber} />\n </SimpleGrid>\n <Control title=\"Roles\" input={form.input.roles} />\n <Divider />\n <Group justify=\"flex-end\" gap=\"xs\">\n <ActionButton\n variant=\"subtle\"\n size=\"xs\"\n onClick={() => setEditing(false)}\n >\n Cancel\n </ActionButton>\n <ActionButton size=\"xs\" form={form}>\n Save\n </ActionButton>\n </Group>\n </Stack>\n </form>\n </Box>\n ) : (\n <Box px=\"md\" pb=\"md\">\n <DataRow label=\"First Name\" value={user.firstName || \"—\"} />\n <DataRow label=\"Last Name\" value={user.lastName || \"—\"} />\n <DataRow label=\"Display Name\" value={displayName || \"—\"} />\n <DataRow\n label=\"Roles\"\n value={\n user.roles.length > 0 ? (\n <Group gap={4}>\n {user.roles.map((role) => (\n <Badge key={role} size=\"xs\" variant=\"light\">\n {role}\n </Badge>\n ))}\n </Group>\n ) : (\n <Text size=\"sm\" c=\"dimmed\">\n No roles\n </Text>\n )\n }\n />\n </Box>\n )}\n </Card>\n </Grid.Col>\n </Grid>\n\n {/* Timeline */}\n <Card padding={0} radius=\"md\" withBorder>\n <Group justify=\"space-between\" p=\"md\" pb={0}>\n <Text fw={600} size=\"sm\">\n Activity Timeline\n </Text>\n </Group>\n <SimpleGrid cols={{ base: 2, sm: 4 }} p=\"md\">\n <Box>\n <Group gap={6} mb={4}>\n <IconCalendar size={14} style={{ opacity: 0.5 }} />\n <Text size=\"xs\" c=\"dimmed\">\n Created\n </Text>\n </Group>\n <Text size=\"sm\" fw={500}>\n {l(user.createdAt, { date: \"ll\" })}\n </Text>\n <Text size=\"xs\" c=\"dimmed\">\n {l(user.createdAt, { date: \"fromNow\" })}\n </Text>\n </Box>\n <Box>\n <Group gap={6} mb={4}>\n <IconCalendar size={14} style={{ opacity: 0.5 }} />\n <Text size=\"xs\" c=\"dimmed\">\n Updated\n </Text>\n </Group>\n <Text size=\"sm\" fw={500}>\n {l(user.updatedAt, { date: \"ll\" })}\n </Text>\n <Text size=\"xs\" c=\"dimmed\">\n {l(user.updatedAt, { date: \"fromNow\" })}\n </Text>\n </Box>\n <Box>\n <Group gap={6} mb={4}>\n <IconDevices size={14} style={{ opacity: 0.5 }} />\n <Text size=\"xs\" c=\"dimmed\">\n Last Login\n </Text>\n </Group>\n <Text size=\"sm\" c=\"dimmed\">\n —\n </Text>\n </Box>\n <Box>\n <Group gap={6} mb={4}>\n <IconCheck size={14} style={{ opacity: 0.5 }} />\n <Text size=\"xs\" c=\"dimmed\">\n Email Verified\n </Text>\n </Group>\n {user.emailVerified ? (\n <>\n <Text size=\"sm\" fw={500}>\n {l(user.updatedAt, { date: \"ll\" })}\n </Text>\n <Text size=\"xs\" c=\"dimmed\">\n {l(user.updatedAt, { date: \"fromNow\" })}\n </Text>\n </>\n ) : (\n <Text size=\"sm\" c=\"dimmed\">\n Not verified\n </Text>\n )}\n </Box>\n </SimpleGrid>\n </Card>\n </Stack>\n );\n};\n\nexport default AdminUserDetails;\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"AdminUserLayout-D7En9UBq.js","names":["Text","Text","AdminResourceHeader","AdminResourceTabs"],"sources":["../../src/admin/components/shared/AdminResourceHeader.tsx","../../src/admin/components/shared/AdminResourceTabs.tsx","../../src/admin/components/users/AdminUserLayout.tsx"],"sourcesContent":["import { useRouter } from \"@alepha/react/router\";\nimport { ActionButton } from \"@alepha/ui\";\nimport {\n ActionIcon,\n Avatar,\n Badge,\n Button,\n Group,\n Menu,\n Stack,\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\";\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 ActionMenuItem = (props: { action: AdminResourceAction }) => {\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\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 <Stack gap=\"xs\">\n {/* Breadcrumb / Back navigation */}\n {backHref && (\n <Group>\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 </Group>\n )}\n\n {/* Main header */}\n <Group justify=\"space-between\" align=\"flex-start\" wrap=\"nowrap\">\n {/* Left: Avatar + Info */}\n <Group gap=\"md\" wrap=\"nowrap\">\n {renderAvatar()}\n\n <Stack gap={2} justify=\"center\" style={{ minHeight: 56 }}>\n {/* Title row */}\n <Group 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 </Group>\n\n {/* Subtitle */}\n {subtitle && (\n <Text size=\"xs\" c=\"dimmed\">\n {subtitle}\n </Text>\n )}\n </Stack>\n </Group>\n\n {/* Right: Actions */}\n <Group 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 <ActionMenuItem key={index} action={action} />\n ))}\n </Menu.Dropdown>\n </Menu>\n )}\n </Group>\n </Group>\n </Stack>\n );\n};\n\nexport default AdminResourceHeader;\n","import { useActive, useRouter } from \"@alepha/react/router\";\nimport { Tabs } from \"@mantine/core\";\nimport type { ComponentType, ReactNode } from \"react\";\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 TabItem = (props: { tab: AdminResourceTab }) => {\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\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 <TabItem key={tab.value} tab={tab} />\n ))}\n </Tabs.List>\n\n {children}\n </Tabs>\n );\n};\n\nexport default AdminResourceTabs;\n","import { useClient } from \"@alepha/react\";\nimport { NestedView, useRouter, useRouterState } from \"@alepha/react/router\";\nimport { Box, Center, Loader, Stack, Text } from \"@mantine/core\";\nimport {\n IconBan,\n IconDevices,\n IconHistory,\n IconLock,\n IconMail,\n IconPencil,\n IconSettings,\n IconShieldCheck,\n IconTrash,\n IconUser,\n} from \"@tabler/icons-react\";\nimport type { AdminUserController, UserEntity } from \"alepha/api/users\";\nimport { useEffect, useState } 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\nconst AdminUserLayout = (props: AdminUserLayoutProps) => {\n const router = useRouter<AdminRouter>();\n const state = useRouterState();\n const client = useClient<AdminUserController>();\n const userId = state.params.userId as string;\n\n const [user, setUser] = useState<UserEntity | null>(null);\n const [loading, setLoading] = useState(true);\n const [actionLoading, setActionLoading] = useState<string | null>(null);\n\n useEffect(() => {\n const loadUser = async () => {\n try {\n const data = await client.getUser({\n params: { id: userId },\n query: { userRealmName: props.userRealmName },\n });\n setUser(data);\n } finally {\n setLoading(false);\n }\n };\n\n loadUser();\n }, [userId]);\n\n if (loading) {\n return (\n <Center flex={1}>\n <Loader />\n </Center>\n );\n }\n\n if (!user) {\n return (\n <Center flex={1}>\n <Stack align=\"center\" gap=\"xs\">\n <IconUser size={48} opacity={0.3} />\n <Text c=\"dimmed\">User not found</Text>\n </Stack>\n </Center>\n );\n }\n\n const displayName =\n user.firstName || user.lastName\n ? `${user.firstName ?? \"\"} ${user.lastName ?? \"\"}`.trim()\n : user.username || user.email || \"User\";\n\n const currentPath = state.url.pathname;\n const getActiveTab = () => {\n if (currentPath.endsWith(\"/sessions\")) return \"sessions\";\n if (currentPath.endsWith(\"/settings\")) return \"settings\";\n if (currentPath.endsWith(\"/audits\")) return \"audits\";\n return \"profile\";\n };\n\n const handleBlockUser = async () => {\n setActionLoading(\"block\");\n try {\n const updated = await client.updateUser({\n params: { id: userId },\n query: { userRealmName: props.userRealmName },\n body: { enabled: !user.enabled },\n });\n setUser(updated);\n } finally {\n setActionLoading(null);\n }\n };\n\n const handleSendVerification = async () => {\n setActionLoading(\"verify\");\n // TODO: Implement send verification\n await new Promise((resolve) => setTimeout(resolve, 1000));\n setActionLoading(null);\n };\n\n const handleResetPassword = async () => {\n setActionLoading(\"reset\");\n // TODO: Implement reset password\n await new Promise((resolve) => setTimeout(resolve, 1000));\n setActionLoading(null);\n };\n\n const handleDeleteUser = async () => {\n if (\n !confirm(\n \"Are you sure you want to delete this user? This action cannot be undone.\",\n )\n ) {\n return;\n }\n setActionLoading(\"delete\");\n try {\n await client.deleteUser({\n params: { id: userId },\n query: { userRealmName: props.userRealmName },\n });\n await router.go(\"adminUsers\");\n } finally {\n setActionLoading(null);\n }\n };\n\n return (\n <Box py=\"xl\" px=\"xl\" flex={1}>\n <Stack gap=\"lg\">\n <AdminResourceHeader\n backHref={router.path(\"adminUsers\")}\n backLabel=\"Users\"\n avatar={user.picture || displayName.charAt(0).toUpperCase()}\n avatarColor={user.enabled ? \"blue\" : \"gray\"}\n title={displayName}\n subtitle={user.email || user.username || undefined}\n status={{\n label: user.enabled ? \"Active\" : \"Disabled\",\n color: user.enabled ? \"green\" : \"red\",\n }}\n menuActions={[\n {\n label: \"Edit Profile\",\n icon: IconPencil,\n href: router.path(\"adminUserDetails\", { params: { userId } }),\n },\n {\n label: user.enabled ? \"Disable User\" : \"Enable User\",\n icon: user.enabled ? IconBan : IconShieldCheck,\n color: user.enabled ? \"orange\" : \"green\",\n onClick: handleBlockUser,\n loading: actionLoading === \"block\",\n },\n ...(user.email && !user.emailVerified\n ? [\n {\n label: \"Send Verification Email\",\n icon: IconMail,\n onClick: handleSendVerification,\n loading: actionLoading === \"verify\",\n },\n ]\n : []),\n {\n label: \"Reset Password\",\n icon: IconLock,\n onClick: handleResetPassword,\n loading: actionLoading === \"reset\",\n },\n {\n label: \"Delete User\",\n icon: IconTrash,\n color: \"red\",\n onClick: handleDeleteUser,\n loading: actionLoading === \"delete\",\n },\n ]}\n />\n\n <AdminResourceTabs\n activeTab={getActiveTab()}\n tabs={[\n {\n value: \"profile\",\n label: \"Profile\",\n icon: IconUser,\n href: router.path(\"adminUserDetails\", { params: { userId } }),\n },\n {\n value: \"sessions\",\n label: \"Sessions\",\n icon: IconDevices,\n href: router.path(\"adminUserSessions\", { params: { userId } }),\n },\n {\n value: \"audits\",\n label: \"Activity\",\n icon: IconHistory,\n href: router.path(\"adminUserAudits\", { params: { userId } }),\n },\n {\n value: \"settings\",\n label: \"Settings\",\n icon: IconSettings,\n href: router.path(\"adminUserSettings\", { params: { userId } }),\n },\n ]}\n />\n\n <NestedView />\n </Stack>\n </Box>\n );\n};\n\nexport default AdminUserLayout;\n"],"mappings":"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"}
|