@alepha/ui 0.11.12 → 0.12.1
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/README.md +2 -30
- package/dist/admin/AdminFiles-BM6_7_5A.cjs +4 -0
- package/dist/admin/AdminFiles-BaCIMeNt.js +4 -0
- package/dist/admin/AdminFiles-CllAxb1B.js +117 -0
- package/dist/admin/AdminFiles-CllAxb1B.js.map +1 -0
- package/dist/admin/AdminFiles-DC3T8uWZ.cjs +122 -0
- package/dist/admin/AdminFiles-DC3T8uWZ.cjs.map +1 -0
- package/dist/admin/AdminJobs-BXkFtlVo.js +125 -0
- package/dist/admin/AdminJobs-BXkFtlVo.js.map +1 -0
- package/dist/admin/AdminJobs-C428qrNQ.cjs +130 -0
- package/dist/admin/AdminJobs-C428qrNQ.cjs.map +1 -0
- package/dist/admin/AdminJobs-DCPPaJ4i.cjs +4 -0
- package/dist/admin/AdminJobs-yC6DarGO.js +4 -0
- package/dist/admin/AdminLayout-Bqo4cd33.cjs +4 -0
- package/dist/admin/AdminLayout-CQpxfko6.js +4 -0
- package/dist/admin/AdminLayout-CiLlywAQ.cjs +93 -0
- package/dist/admin/AdminLayout-CiLlywAQ.cjs.map +1 -0
- package/dist/admin/AdminLayout-CtkVYk-u.js +88 -0
- package/dist/admin/AdminLayout-CtkVYk-u.js.map +1 -0
- package/dist/admin/AdminNotifications-DNUeJ-PW.cjs +44 -0
- package/dist/admin/AdminNotifications-DNUeJ-PW.cjs.map +1 -0
- package/dist/admin/AdminNotifications-DaMu1AQ4.js +4 -0
- package/dist/admin/AdminNotifications-DnnulNNV.js +40 -0
- package/dist/admin/AdminNotifications-DnnulNNV.js.map +1 -0
- package/dist/admin/AdminNotifications-ihgbKVCx.cjs +4 -0
- package/dist/admin/AdminParameters-B3hvpLpu.js +40 -0
- package/dist/admin/AdminParameters-B3hvpLpu.js.map +1 -0
- package/dist/admin/AdminParameters-U4lU1rUF.cjs +4 -0
- package/dist/admin/AdminParameters-gdf7036N.cjs +44 -0
- package/dist/admin/AdminParameters-gdf7036N.cjs.map +1 -0
- package/dist/admin/AdminParameters-prMcCgxf.js +4 -0
- package/dist/admin/AdminSessions-BF_P4lHs.cjs +128 -0
- package/dist/admin/AdminSessions-BF_P4lHs.cjs.map +1 -0
- package/dist/admin/AdminSessions-CATIU61I.cjs +4 -0
- package/dist/admin/AdminSessions-DqOXOpYR.js +4 -0
- package/dist/admin/AdminSessions-Pjdz-iZx.js +123 -0
- package/dist/admin/AdminSessions-Pjdz-iZx.js.map +1 -0
- package/dist/admin/AdminUsers-BgTL-zSY.js +4 -0
- package/dist/admin/AdminUsers-C1HsrRxn.js +104 -0
- package/dist/admin/AdminUsers-C1HsrRxn.js.map +1 -0
- package/dist/admin/AdminUsers-HqvxwNGZ.cjs +4 -0
- package/dist/admin/AdminUsers-M2uEQbp5.cjs +109 -0
- package/dist/admin/AdminUsers-M2uEQbp5.cjs.map +1 -0
- package/dist/admin/AdminVerifications-BVssbtfU.cjs +44 -0
- package/dist/admin/AdminVerifications-BVssbtfU.cjs.map +1 -0
- package/dist/admin/AdminVerifications-Df6DRgNo.js +4 -0
- package/dist/admin/AdminVerifications-DxAtcYUR.cjs +4 -0
- package/dist/admin/AdminVerifications-VMpm30mS.js +40 -0
- package/dist/admin/AdminVerifications-VMpm30mS.js.map +1 -0
- package/dist/admin/core-CzO6aavT.js +2507 -0
- package/dist/admin/core-CzO6aavT.js.map +1 -0
- package/dist/{index.cjs → admin/core-aFtK4l9I.cjs} +287 -204
- package/dist/admin/core-aFtK4l9I.cjs.map +1 -0
- package/dist/admin/index.cjs +87 -0
- package/dist/admin/index.cjs.map +1 -0
- package/dist/admin/index.d.cts +1739 -0
- package/dist/admin/index.d.ts +1745 -0
- package/dist/admin/index.js +78 -0
- package/dist/admin/index.js.map +1 -0
- package/dist/auth/IconGoogle-B17BTQyD.cjs +69 -0
- package/dist/auth/IconGoogle-B17BTQyD.cjs.map +1 -0
- package/dist/auth/IconGoogle-Bfmuv9Rv.js +58 -0
- package/dist/auth/IconGoogle-Bfmuv9Rv.js.map +1 -0
- package/dist/auth/Login-BTBmbnWl.cjs +181 -0
- package/dist/auth/Login-BTBmbnWl.cjs.map +1 -0
- package/dist/auth/Login-BcQOtG3v.js +5 -0
- package/dist/auth/Login-Btmd70Um.cjs +5 -0
- package/dist/auth/Login-JeXFsUf5.js +176 -0
- package/dist/auth/Login-JeXFsUf5.js.map +1 -0
- package/dist/auth/Register-CPQnvXCZ.js +318 -0
- package/dist/auth/Register-CPQnvXCZ.js.map +1 -0
- package/dist/auth/Register-CbesZal3.cjs +5 -0
- package/dist/auth/Register-DpI_JdyO.js +5 -0
- package/dist/auth/Register-HP3rP71B.cjs +323 -0
- package/dist/auth/Register-HP3rP71B.cjs.map +1 -0
- package/dist/auth/ResetPassword-B-tkzV7g.cjs +248 -0
- package/dist/auth/ResetPassword-B-tkzV7g.cjs.map +1 -0
- package/dist/auth/ResetPassword-BlK3xEpU.js +4 -0
- package/dist/auth/ResetPassword-BzUjGG_-.js +243 -0
- package/dist/auth/ResetPassword-BzUjGG_-.js.map +1 -0
- package/dist/auth/ResetPassword-W3xjOnWy.cjs +4 -0
- package/dist/auth/chunk-DhGyd7sr.js +28 -0
- package/dist/auth/core-D1MHij1j.js +1795 -0
- package/dist/auth/core-D1MHij1j.js.map +1 -0
- package/dist/auth/core-rDZ9d92K.cjs +1824 -0
- package/dist/auth/core-rDZ9d92K.cjs.map +1 -0
- package/dist/auth/index.cjs +211 -0
- package/dist/auth/index.cjs.map +1 -0
- package/dist/auth/index.d.cts +6265 -0
- package/dist/auth/index.d.ts +6274 -0
- package/dist/auth/index.js +206 -0
- package/dist/auth/index.js.map +1 -0
- package/dist/core/index.cjs +2620 -0
- package/dist/core/index.cjs.map +1 -0
- package/dist/core/index.d.cts +2737 -0
- package/dist/core/index.d.ts +2743 -0
- package/dist/{index.js → core/index.js} +298 -126
- package/dist/core/index.js.map +1 -0
- package/package.json +32 -14
- package/src/admin/AdminRouter.ts +58 -0
- package/src/admin/components/AdminFiles.tsx +117 -0
- package/src/admin/components/AdminJobs.tsx +158 -0
- package/src/admin/components/AdminLayout.tsx +114 -0
- package/src/admin/components/AdminNotifications.tsx +20 -0
- package/src/admin/components/AdminParameters.tsx +24 -0
- package/src/admin/components/AdminSessions.tsx +159 -0
- package/src/admin/components/AdminUsers.tsx +137 -0
- package/src/admin/components/AdminVerifications.tsx +25 -0
- package/src/admin/index.ts +29 -0
- package/src/auth/AuthI18n.ts +118 -0
- package/src/auth/AuthRouter.ts +53 -0
- package/src/auth/components/Login.tsx +193 -0
- package/src/auth/components/Register.tsx +421 -0
- package/src/auth/components/ResetPassword.tsx +259 -0
- package/src/auth/components/buttons/UserButton.tsx +118 -0
- package/src/auth/components/icons/IconGithub.tsx +21 -0
- package/src/auth/components/icons/IconGoogle.tsx +30 -0
- package/src/auth/index.ts +27 -0
- package/src/{RootRouter.ts → core/RootRouter.ts} +2 -1
- package/src/{components → core/components}/buttons/ActionButton.tsx +49 -6
- package/src/core/components/buttons/ClipboardButton.tsx +56 -0
- package/src/{components → core/components}/buttons/DarkModeButton.tsx +7 -8
- package/src/{components → core/components}/buttons/LanguageButton.tsx +2 -2
- package/src/{components → core/components}/buttons/OmnibarButton.tsx +1 -1
- package/src/{components → core/components}/dialogs/AlertDialog.tsx +1 -1
- package/src/{components → core/components}/dialogs/ConfirmDialog.tsx +1 -1
- package/src/{components → core/components}/dialogs/PromptDialog.tsx +1 -1
- package/src/{components → core/components}/form/Control.tsx +1 -0
- package/src/{components → core/components}/layout/AdminShell.tsx +38 -7
- package/src/{components → core/components}/layout/AlephaMantineProvider.tsx +12 -8
- package/src/{components → core/components}/layout/AppBar.tsx +1 -1
- package/src/{components → core/components}/layout/Omnibar.tsx +1 -1
- package/src/{components → core/components}/layout/Sidebar.tsx +29 -26
- package/src/{components → core/components}/table/DataTable.tsx +1 -1
- package/src/{constants → core/constants}/ui.ts +9 -0
- package/src/{index.ts → core/index.ts} +3 -0
- package/src/{services → core/services}/DialogService.tsx +3 -3
- package/src/{services → core/services}/ToastService.tsx +3 -1
- package/src/{utils → core/utils}/extractSchemaFields.ts +2 -8
- package/src/{utils → core/utils}/icons.tsx +5 -15
- package/src/{utils → core/utils}/parseInput.ts +34 -26
- package/dist/AlephaMantineProvider-CGpgWDt8.cjs +0 -3
- package/dist/AlephaMantineProvider-D8cHYAge.js +0 -152
- package/dist/AlephaMantineProvider-D8cHYAge.js.map +0 -1
- package/dist/AlephaMantineProvider-DuvZFAuk.cjs +0 -175
- package/dist/AlephaMantineProvider-DuvZFAuk.cjs.map +0 -1
- package/dist/AlephaMantineProvider-twBqV4IO.js +0 -3
- package/dist/index.cjs.map +0 -1
- package/dist/index.d.cts +0 -821
- package/dist/index.d.cts.map +0 -1
- package/dist/index.d.ts +0 -821
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- /package/src/{components → core/components}/buttons/BurgerButton.tsx +0 -0
- /package/src/{components → core/components}/buttons/ToggleSidebarButton.tsx +0 -0
- /package/src/{components → core/components}/data/JsonViewer.tsx +0 -0
- /package/src/{components → core/components}/form/ControlDate.tsx +0 -0
- /package/src/{components → core/components}/form/ControlNumber.tsx +0 -0
- /package/src/{components → core/components}/form/ControlQueryBuilder.tsx +0 -0
- /package/src/{components → core/components}/form/ControlSelect.tsx +0 -0
- /package/src/{components → core/components}/form/TypeForm.tsx +0 -0
- /package/src/{hooks → core/hooks}/useDialog.ts +0 -0
- /package/src/{hooks → core/hooks}/useToast.ts +0 -0
- /package/src/{utils → core/utils}/string.ts +0 -0
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { useAuth } from "@alepha/react/auth";
|
|
2
|
+
import {
|
|
3
|
+
ActionButton,
|
|
4
|
+
type ActionMenuConfig,
|
|
5
|
+
type ActionMenuItem,
|
|
6
|
+
type ActionProps,
|
|
7
|
+
ui,
|
|
8
|
+
} from "@alepha/ui";
|
|
9
|
+
import { Avatar } from "@mantine/core";
|
|
10
|
+
import { IconLogout, IconUser } from "@tabler/icons-react";
|
|
11
|
+
import type { ReactNode } from "react";
|
|
12
|
+
|
|
13
|
+
export interface UserButtonProps
|
|
14
|
+
extends Omit<ActionProps, "menu" | "icon" | "onClick"> {
|
|
15
|
+
/**
|
|
16
|
+
* Additional menu items to display before the logout option
|
|
17
|
+
*/
|
|
18
|
+
menuItems?: ActionMenuItem[];
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Custom logout label (default: "Sign out")
|
|
22
|
+
*/
|
|
23
|
+
logoutLabel?: string;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Menu configuration overrides
|
|
27
|
+
*/
|
|
28
|
+
menuConfig?: Partial<Omit<ActionMenuConfig, "items">>;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Whether to show a divider before logout (default: true when menuItems provided)
|
|
32
|
+
*/
|
|
33
|
+
showLogoutDivider?: boolean;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Custom icon to use instead of user avatar (default: IconUser)
|
|
37
|
+
*/
|
|
38
|
+
icon?: ReactNode;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const UserButton = (props: UserButtonProps) => {
|
|
42
|
+
const {
|
|
43
|
+
menuItems = [],
|
|
44
|
+
logoutLabel = "Sign out",
|
|
45
|
+
menuConfig,
|
|
46
|
+
showLogoutDivider = menuItems.length > 0,
|
|
47
|
+
icon,
|
|
48
|
+
children,
|
|
49
|
+
...buttonProps
|
|
50
|
+
} = props;
|
|
51
|
+
|
|
52
|
+
const auth = useAuth<{
|
|
53
|
+
username?: string;
|
|
54
|
+
email?: string;
|
|
55
|
+
picture?: string;
|
|
56
|
+
}>();
|
|
57
|
+
|
|
58
|
+
if (!auth.user) {
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const userLabel = auth.user.username || auth.user.email;
|
|
63
|
+
|
|
64
|
+
const items: ActionMenuItem[] = [];
|
|
65
|
+
|
|
66
|
+
// Add user info label if available
|
|
67
|
+
if (auth.user.email && auth.user.username) {
|
|
68
|
+
items.push({
|
|
69
|
+
type: "label",
|
|
70
|
+
label: auth.user.email,
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Add custom menu items
|
|
75
|
+
items.push(...menuItems);
|
|
76
|
+
|
|
77
|
+
// Add divider before logout if needed
|
|
78
|
+
if (showLogoutDivider && items.length > 0) {
|
|
79
|
+
items.push({ type: "divider" });
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Add logout item
|
|
83
|
+
items.push({
|
|
84
|
+
label: logoutLabel,
|
|
85
|
+
icon: <IconLogout size={ui.sizes.icon.md} />,
|
|
86
|
+
color: "red",
|
|
87
|
+
onClick: () => auth.logout(),
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
// Use leftSection for Avatar (JSX element), icon prop for component types
|
|
91
|
+
const hasAvatar = !icon && auth.user.picture;
|
|
92
|
+
|
|
93
|
+
return (
|
|
94
|
+
<ActionButton
|
|
95
|
+
{...buttonProps}
|
|
96
|
+
icon={hasAvatar ? undefined : (icon ?? IconUser)}
|
|
97
|
+
leftSection={
|
|
98
|
+
hasAvatar ? (
|
|
99
|
+
<Avatar
|
|
100
|
+
src={`/api/files/${auth.user.picture}`}
|
|
101
|
+
size={24}
|
|
102
|
+
radius="xl"
|
|
103
|
+
/>
|
|
104
|
+
) : undefined
|
|
105
|
+
}
|
|
106
|
+
menu={{
|
|
107
|
+
position: "bottom-end",
|
|
108
|
+
width: 200,
|
|
109
|
+
...menuConfig,
|
|
110
|
+
items,
|
|
111
|
+
}}
|
|
112
|
+
>
|
|
113
|
+
{children ?? userLabel}
|
|
114
|
+
</ActionButton>
|
|
115
|
+
);
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
export default UserButton;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
const IconGithub = () => {
|
|
2
|
+
return (
|
|
3
|
+
<svg
|
|
4
|
+
width="24"
|
|
5
|
+
height="24"
|
|
6
|
+
viewBox="0 0 1024 1024"
|
|
7
|
+
fill="none"
|
|
8
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
9
|
+
>
|
|
10
|
+
<path
|
|
11
|
+
fillRule="evenodd"
|
|
12
|
+
clipRule="evenodd"
|
|
13
|
+
d="M8 0C3.58 0 0 3.58 0 8C0 11.54 2.29 14.53 5.47 15.59C5.87 15.66 6.02 15.42 6.02 15.21C6.02 15.02 6.01 14.39 6.01 13.72C4 14.09 3.48 13.23 3.32 12.78C3.23 12.55 2.84 11.84 2.5 11.65C2.22 11.5 1.82 11.13 2.49 11.12C3.12 11.11 3.57 11.7 3.72 11.94C4.44 13.15 5.59 12.81 6.05 12.6C6.12 12.08 6.33 11.73 6.56 11.53C4.78 11.33 2.92 10.64 2.92 7.58C2.92 6.71 3.23 5.99 3.74 5.43C3.66 5.23 3.38 4.41 3.82 3.31C3.82 3.31 4.49 3.1 6.02 4.13C6.66 3.95 7.34 3.86 8.02 3.86C8.7 3.86 9.38 3.95 10.02 4.13C11.55 3.09 12.22 3.31 12.22 3.31C12.66 4.41 12.38 5.23 12.3 5.43C12.81 5.99 13.12 6.7 13.12 7.58C13.12 10.65 11.25 11.33 9.47 11.53C9.76 11.78 10.01 12.26 10.01 13.01C10.01 14.08 10 14.94 10 15.21C10 15.42 10.15 15.67 10.55 15.59C13.71 14.53 16 11.53 16 8C16 3.58 12.42 0 8 0Z"
|
|
14
|
+
transform="scale(64)"
|
|
15
|
+
fill={"var(--alepha-text)"}
|
|
16
|
+
/>
|
|
17
|
+
</svg>
|
|
18
|
+
);
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export default IconGithub;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
const IconGoogle = () => {
|
|
2
|
+
return (
|
|
3
|
+
<svg
|
|
4
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
5
|
+
height="24"
|
|
6
|
+
viewBox="0 0 24 24"
|
|
7
|
+
width="24"
|
|
8
|
+
>
|
|
9
|
+
<path
|
|
10
|
+
d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z"
|
|
11
|
+
fill="#4285F4"
|
|
12
|
+
/>
|
|
13
|
+
<path
|
|
14
|
+
d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z"
|
|
15
|
+
fill="#34A853"
|
|
16
|
+
/>
|
|
17
|
+
<path
|
|
18
|
+
d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z"
|
|
19
|
+
fill="#FBBC05"
|
|
20
|
+
/>
|
|
21
|
+
<path
|
|
22
|
+
d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z"
|
|
23
|
+
fill="#EA4335"
|
|
24
|
+
/>
|
|
25
|
+
<path d="M1 1h22v22H1z" fill="none" />
|
|
26
|
+
</svg>
|
|
27
|
+
);
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export default IconGoogle;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { AlephaReactAuth } from "@alepha/react/auth";
|
|
2
|
+
import { AlephaReactI18n } from "@alepha/react/i18n";
|
|
3
|
+
import { AlephaUI } from "@alepha/ui";
|
|
4
|
+
import { $module } from "alepha";
|
|
5
|
+
import { AuthI18n } from "./AuthI18n.ts";
|
|
6
|
+
import { AuthRouter } from "./AuthRouter.ts";
|
|
7
|
+
|
|
8
|
+
// ---------------------------------------------------------------------------------------------------------------------
|
|
9
|
+
|
|
10
|
+
export * from "./AuthRouter.ts";
|
|
11
|
+
export type { UserButtonProps } from "./components/buttons/UserButton.tsx";
|
|
12
|
+
export { default as UserButton } from "./components/buttons/UserButton.tsx";
|
|
13
|
+
export { default as Login } from "./components/Login.tsx";
|
|
14
|
+
export { default as Register } from "./components/Register.tsx";
|
|
15
|
+
export { default as ResetPassword } from "./components/ResetPassword.tsx";
|
|
16
|
+
|
|
17
|
+
// ---------------------------------------------------------------------------------------------------------------------
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Login UI Module
|
|
21
|
+
*
|
|
22
|
+
* @module alepha.ui.auth
|
|
23
|
+
*/
|
|
24
|
+
export const AlephaUIAuth = $module({
|
|
25
|
+
name: "alepha.ui.auth",
|
|
26
|
+
services: [AlephaUI, AlephaReactAuth, AlephaReactI18n, AuthRouter, AuthI18n],
|
|
27
|
+
});
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { $page } from "@alepha/react";
|
|
2
|
+
import AlephaMantineProvider from "./components/layout/AlephaMantineProvider.tsx";
|
|
2
3
|
|
|
3
4
|
export class RootRouter {
|
|
4
5
|
public readonly root = $page({
|
|
5
6
|
path: "/",
|
|
6
|
-
|
|
7
|
+
component: AlephaMantineProvider,
|
|
7
8
|
});
|
|
8
9
|
}
|
|
@@ -8,6 +8,8 @@ import {
|
|
|
8
8
|
} from "@alepha/react";
|
|
9
9
|
import { type FormModel, useFormState } from "@alepha/react/form";
|
|
10
10
|
import {
|
|
11
|
+
Anchor,
|
|
12
|
+
type AnchorProps,
|
|
11
13
|
Button,
|
|
12
14
|
type ButtonProps,
|
|
13
15
|
Flex,
|
|
@@ -21,7 +23,14 @@ import {
|
|
|
21
23
|
type TooltipProps,
|
|
22
24
|
} from "@mantine/core";
|
|
23
25
|
import { IconCheck, IconChevronRight } from "@tabler/icons-react";
|
|
24
|
-
import
|
|
26
|
+
import {
|
|
27
|
+
type ButtonHTMLAttributes,
|
|
28
|
+
Children,
|
|
29
|
+
type ComponentType,
|
|
30
|
+
isValidElement,
|
|
31
|
+
type ReactNode,
|
|
32
|
+
} from "react";
|
|
33
|
+
import { ui } from "../../constants/ui.ts";
|
|
25
34
|
|
|
26
35
|
export interface ActionMenuItem {
|
|
27
36
|
/**
|
|
@@ -97,11 +106,13 @@ export interface ActionMenuConfig {
|
|
|
97
106
|
on?: "hover" | "click";
|
|
98
107
|
|
|
99
108
|
targetProps?: MenuTargetProps;
|
|
109
|
+
|
|
100
110
|
menuProps?: MenuProps;
|
|
101
111
|
}
|
|
102
112
|
|
|
103
113
|
export interface ActionCommonProps extends ButtonProps {
|
|
104
114
|
children?: ReactNode;
|
|
115
|
+
|
|
105
116
|
textVisibleFrom?: "xs" | "sm" | "md" | "lg" | "xl";
|
|
106
117
|
|
|
107
118
|
/**
|
|
@@ -127,7 +138,7 @@ export interface ActionCommonProps extends ButtonProps {
|
|
|
127
138
|
* Icon to display on the left side of the button.
|
|
128
139
|
* If no children are provided, the button will be styled as an icon-only button.
|
|
129
140
|
*/
|
|
130
|
-
icon?: ReactNode;
|
|
141
|
+
icon?: ReactNode | ComponentType;
|
|
131
142
|
|
|
132
143
|
/**
|
|
133
144
|
* Additional props to pass to the ThemeIcon wrapping the icon.
|
|
@@ -223,11 +234,13 @@ const ActionMenuItem = (props: {
|
|
|
223
234
|
};
|
|
224
235
|
|
|
225
236
|
const ActionButton = (_props: ActionProps) => {
|
|
226
|
-
const props = { variant: "
|
|
237
|
+
const props = { variant: "default", ..._props };
|
|
227
238
|
const { tooltip, menu, icon, ...restProps } = props;
|
|
228
239
|
|
|
229
240
|
if (props.icon) {
|
|
230
|
-
const icon = (
|
|
241
|
+
const icon = isComponentType(props.icon) ? (
|
|
242
|
+
<props.icon size={ui.sizes.icon.md} />
|
|
243
|
+
) : (
|
|
231
244
|
<ThemeIcon
|
|
232
245
|
w={24} // TODO: make size configurable
|
|
233
246
|
variant={"transparent"}
|
|
@@ -235,11 +248,12 @@ const ActionButton = (_props: ActionProps) => {
|
|
|
235
248
|
c={"var(--mantine-color-text)"}
|
|
236
249
|
{...props.themeIconProps}
|
|
237
250
|
>
|
|
238
|
-
{props.icon}
|
|
251
|
+
{props.icon as ReactNode}
|
|
239
252
|
</ThemeIcon>
|
|
240
253
|
);
|
|
254
|
+
|
|
241
255
|
if (!props.children) {
|
|
242
|
-
restProps.children = icon;
|
|
256
|
+
restProps.children = Children.only(icon);
|
|
243
257
|
restProps.p ??= "xs";
|
|
244
258
|
} else {
|
|
245
259
|
restProps.leftSection = icon;
|
|
@@ -509,6 +523,7 @@ export interface ActionNavigationButtonProps extends ButtonProps {
|
|
|
509
523
|
classNameActive?: string;
|
|
510
524
|
variantActive?: ButtonProps["variant"];
|
|
511
525
|
target?: string;
|
|
526
|
+
anchorProps?: AnchorProps;
|
|
512
527
|
}
|
|
513
528
|
|
|
514
529
|
/**
|
|
@@ -533,6 +548,14 @@ const ActionNavigationButton = (props: ActionNavigationButtonProps) => {
|
|
|
533
548
|
buttonProps.className = `${className} ${classNameActive}`.trim();
|
|
534
549
|
}
|
|
535
550
|
|
|
551
|
+
if (props.anchorProps) {
|
|
552
|
+
return (
|
|
553
|
+
<Anchor component={"a"} {...anchorProps} {...props.anchorProps}>
|
|
554
|
+
{props.children}
|
|
555
|
+
</Anchor>
|
|
556
|
+
);
|
|
557
|
+
}
|
|
558
|
+
|
|
536
559
|
return (
|
|
537
560
|
<Button
|
|
538
561
|
component={"a"}
|
|
@@ -566,3 +589,23 @@ const ActionHrefButton = (props: ActionNavigationButtonProps) => {
|
|
|
566
589
|
</Button>
|
|
567
590
|
);
|
|
568
591
|
};
|
|
592
|
+
|
|
593
|
+
// ---------------------------------------------------------------------------------------------------------------------
|
|
594
|
+
|
|
595
|
+
export function isComponentType(param: any): param is ComponentType<any> {
|
|
596
|
+
if (isValidElement(param)) return false;
|
|
597
|
+
return (
|
|
598
|
+
typeof param === "function" ||
|
|
599
|
+
(typeof param === "object" && param !== null && "$$typeof" in param)
|
|
600
|
+
);
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
export const renderIcon = (icon: ReactNode | ComponentType): ReactNode => {
|
|
604
|
+
if (!icon) return null;
|
|
605
|
+
if (isValidElement(icon)) return icon;
|
|
606
|
+
if (isComponentType(icon)) {
|
|
607
|
+
const IconComponent = icon;
|
|
608
|
+
return <IconComponent size={ui.sizes.icon.md} />;
|
|
609
|
+
}
|
|
610
|
+
return icon as ReactNode;
|
|
611
|
+
};
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { CopyButton, Tooltip } from "@mantine/core";
|
|
2
|
+
import { IconCheck, IconCopy } from "@tabler/icons-react";
|
|
3
|
+
import ActionButton, { type ActionCommonProps } from "./ActionButton.tsx";
|
|
4
|
+
|
|
5
|
+
export interface ClipboardButtonProps
|
|
6
|
+
extends Omit<ActionCommonProps, "onClick" | "icon"> {
|
|
7
|
+
/**
|
|
8
|
+
* The value to copy to the clipboard
|
|
9
|
+
*/
|
|
10
|
+
value: string;
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Timeout in ms to show the "Copied" state (default: 2000)
|
|
14
|
+
*/
|
|
15
|
+
timeout?: number;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Label to show in tooltip when not copied (default: "Copy")
|
|
19
|
+
*/
|
|
20
|
+
copyLabel?: string;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Label to show in tooltip when copied (default: "Copied")
|
|
24
|
+
*/
|
|
25
|
+
copiedLabel?: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const ClipboardButton = (props: ClipboardButtonProps) => {
|
|
29
|
+
const {
|
|
30
|
+
value,
|
|
31
|
+
timeout = 2000,
|
|
32
|
+
copyLabel = "Copy",
|
|
33
|
+
copiedLabel = "Copied",
|
|
34
|
+
children,
|
|
35
|
+
...buttonProps
|
|
36
|
+
} = props;
|
|
37
|
+
|
|
38
|
+
return (
|
|
39
|
+
<CopyButton value={value} timeout={timeout}>
|
|
40
|
+
{({ copied, copy }) => (
|
|
41
|
+
<Tooltip label={copied ? copiedLabel : copyLabel} openDelay={500}>
|
|
42
|
+
<ActionButton
|
|
43
|
+
color={copied ? "teal" : undefined}
|
|
44
|
+
onClick={copy}
|
|
45
|
+
icon={copied ? IconCheck : IconCopy}
|
|
46
|
+
{...buttonProps}
|
|
47
|
+
>
|
|
48
|
+
{children}
|
|
49
|
+
</ActionButton>
|
|
50
|
+
</Tooltip>
|
|
51
|
+
)}
|
|
52
|
+
</CopyButton>
|
|
53
|
+
);
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
export default ClipboardButton;
|
|
@@ -73,19 +73,18 @@ const DarkModeButton = (props: DarkModeButtonProps) => {
|
|
|
73
73
|
return (
|
|
74
74
|
<ActionButton
|
|
75
75
|
onClick={toggleColorScheme}
|
|
76
|
-
variant={props.variant ?? "
|
|
76
|
+
variant={props.variant ?? "default"}
|
|
77
77
|
size={props.size ?? "sm"}
|
|
78
78
|
aria-label="Toggle color scheme"
|
|
79
79
|
px={"xs"}
|
|
80
|
+
c={colorScheme !== "default" ? undefined : "transparent"}
|
|
80
81
|
fullWidth={props.fullWidth ?? false}
|
|
81
82
|
icon={
|
|
82
|
-
colorScheme === "dark"
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
<Flex h={20} w={20} />
|
|
88
|
-
)
|
|
83
|
+
colorScheme === "dark"
|
|
84
|
+
? IconSun
|
|
85
|
+
: colorScheme === "light"
|
|
86
|
+
? IconMoon
|
|
87
|
+
: IconSun
|
|
89
88
|
}
|
|
90
89
|
{...props.actionProps}
|
|
91
90
|
/>
|
|
@@ -11,8 +11,8 @@ const LanguageButton = (props: LanguageButtonProps) => {
|
|
|
11
11
|
const i18n = useI18n();
|
|
12
12
|
return (
|
|
13
13
|
<ActionButton
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
variant={"default"}
|
|
15
|
+
icon={IconLanguage}
|
|
16
16
|
menu={{
|
|
17
17
|
items: i18n.languages.map((lang) => ({
|
|
18
18
|
label: i18n.tr(lang),
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Button, Group, Text } from "@mantine/core";
|
|
2
|
-
import type { AlertDialogProps } from "../../services/DialogService";
|
|
2
|
+
import type { AlertDialogProps } from "../../services/DialogService.tsx";
|
|
3
3
|
|
|
4
4
|
const AlertDialog = ({ options, onClose }: AlertDialogProps) => (
|
|
5
5
|
<>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Button, Group, Text } from "@mantine/core";
|
|
2
|
-
import type { ConfirmDialogProps } from "../../services/DialogService";
|
|
2
|
+
import type { ConfirmDialogProps } from "../../services/DialogService.tsx";
|
|
3
3
|
|
|
4
4
|
const ConfirmDialog = ({ options, onConfirm }: ConfirmDialogProps) => (
|
|
5
5
|
<>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Button, Group, Text, TextInput } from "@mantine/core";
|
|
2
2
|
import { useEffect, useRef, useState } from "react";
|
|
3
|
-
import type { PromptDialogProps } from "../../services/DialogService";
|
|
3
|
+
import type { PromptDialogProps } from "../../services/DialogService.tsx";
|
|
4
4
|
|
|
5
5
|
const PromptDialog = ({ options, onSubmit }: PromptDialogProps) => {
|
|
6
6
|
const [value, setValue] = useState(options?.defaultValue || "");
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { NestedView, useEvents, useStore } from "@alepha/react";
|
|
1
|
+
import { NestedView, useEvents, useRouter, useStore } from "@alepha/react";
|
|
2
2
|
import {
|
|
3
3
|
AppShell,
|
|
4
4
|
type AppShellFooterProps,
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
type AppShellNavbarProps,
|
|
8
8
|
type AppShellProps,
|
|
9
9
|
} from "@mantine/core";
|
|
10
|
-
import type
|
|
10
|
+
import { type ReactNode, useState } from "react";
|
|
11
11
|
import { ui } from "../../constants/ui.ts";
|
|
12
12
|
import AppBar, { type AppBarProps } from "./AppBar.tsx";
|
|
13
13
|
import { Sidebar, type SidebarProps } from "./Sidebar.tsx";
|
|
@@ -23,6 +23,13 @@ export interface AdminShellProps {
|
|
|
23
23
|
header?: ReactNode;
|
|
24
24
|
footer?: ReactNode;
|
|
25
25
|
children?: ReactNode;
|
|
26
|
+
|
|
27
|
+
noSidebarWhen?: {
|
|
28
|
+
/**
|
|
29
|
+
* Paths where the sidebar should be hidden.
|
|
30
|
+
*/
|
|
31
|
+
paths?: string[];
|
|
32
|
+
};
|
|
26
33
|
}
|
|
27
34
|
|
|
28
35
|
declare module "alepha" {
|
|
@@ -40,14 +47,35 @@ declare module "alepha" {
|
|
|
40
47
|
}
|
|
41
48
|
|
|
42
49
|
const AdminShell = (props: AdminShellProps) => {
|
|
50
|
+
const router = useRouter();
|
|
43
51
|
const [opened, setOpened] = useStore("alepha.ui.sidebar.opened");
|
|
44
52
|
const [collapsed] = useStore(
|
|
45
53
|
"alepha.ui.sidebar.collapsed",
|
|
46
54
|
props.sidebarProps?.collapsed,
|
|
47
55
|
);
|
|
48
56
|
|
|
57
|
+
const shouldShowSidebar = () => {
|
|
58
|
+
if (props.noSidebarWhen?.paths) {
|
|
59
|
+
for (const path of props.noSidebarWhen.paths) {
|
|
60
|
+
if (
|
|
61
|
+
router.isActive(path, {
|
|
62
|
+
startWith: true,
|
|
63
|
+
})
|
|
64
|
+
) {
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return true;
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
const [showSidebar, setShowSidebar] = useState(shouldShowSidebar());
|
|
73
|
+
|
|
49
74
|
useEvents(
|
|
50
75
|
{
|
|
76
|
+
"react:transition:end": () => {
|
|
77
|
+
setShowSidebar(shouldShowSidebar());
|
|
78
|
+
},
|
|
51
79
|
"react:transition:begin": () => {
|
|
52
80
|
setOpened(false);
|
|
53
81
|
},
|
|
@@ -60,12 +88,15 @@ const AdminShell = (props: AdminShellProps) => {
|
|
|
60
88
|
{ position: "left" as const, type: "burger" as const },
|
|
61
89
|
];
|
|
62
90
|
|
|
91
|
+
const hasSidebar = showSidebar && props.sidebarProps !== undefined;
|
|
92
|
+
const hasAppBar = hasSidebar || props.appBarProps || props.header;
|
|
93
|
+
|
|
63
94
|
return (
|
|
64
95
|
<AppShell
|
|
65
96
|
padding="md"
|
|
66
|
-
header={{ height: 60 }}
|
|
97
|
+
header={hasAppBar ? { height: 60 } : undefined}
|
|
67
98
|
navbar={
|
|
68
|
-
|
|
99
|
+
hasSidebar
|
|
69
100
|
? {
|
|
70
101
|
width: collapsed ? { base: 72 } : { base: 300 },
|
|
71
102
|
breakpoint: "sm",
|
|
@@ -82,13 +113,13 @@ const AdminShell = (props: AdminShellProps) => {
|
|
|
82
113
|
)}
|
|
83
114
|
</AppShell.Header>
|
|
84
115
|
|
|
85
|
-
{
|
|
116
|
+
{hasSidebar && (
|
|
86
117
|
<AppShell.Navbar bg={ui.colors.surface} {...props.appShellNavbarProps}>
|
|
87
|
-
<Sidebar collapsed={collapsed} {...props.sidebarProps} />
|
|
118
|
+
<Sidebar collapsed={collapsed} {...(props.sidebarProps ?? {})} />
|
|
88
119
|
</AppShell.Navbar>
|
|
89
120
|
)}
|
|
90
121
|
|
|
91
|
-
<AppShell.Main {...props.appShellMainProps}>
|
|
122
|
+
<AppShell.Main display={"flex"} flex={1} {...props.appShellMainProps}>
|
|
92
123
|
{props.children ?? <NestedView />}
|
|
93
124
|
</AppShell.Main>
|
|
94
125
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { NestedView, useEvents } from "@alepha/react";
|
|
2
|
+
import { FormValidationError } from "@alepha/react/form";
|
|
2
3
|
import type {
|
|
3
4
|
ColorSchemeScriptProps,
|
|
4
5
|
MantineProviderProps,
|
|
@@ -33,8 +34,17 @@ const AlephaMantineProvider = (props: AlephaMantineProviderProps) => {
|
|
|
33
34
|
"react:transition:end": () => {
|
|
34
35
|
nprogress.complete();
|
|
35
36
|
},
|
|
36
|
-
"react:action:error": () => {
|
|
37
|
-
|
|
37
|
+
"react:action:error": ({ error }) => {
|
|
38
|
+
if (error instanceof FormValidationError) {
|
|
39
|
+
// Validation errors are handled by the form component
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
toast.danger({
|
|
44
|
+
title: error.name || "Error",
|
|
45
|
+
message:
|
|
46
|
+
error.message ?? "An error occurred while processing your action.",
|
|
47
|
+
});
|
|
38
48
|
},
|
|
39
49
|
},
|
|
40
50
|
[],
|
|
@@ -49,12 +59,6 @@ const AlephaMantineProvider = (props: AlephaMantineProviderProps) => {
|
|
|
49
59
|
<MantineProvider
|
|
50
60
|
{...props.mantine}
|
|
51
61
|
theme={{
|
|
52
|
-
primaryColor: "gray",
|
|
53
|
-
primaryShade: {
|
|
54
|
-
light: 9,
|
|
55
|
-
dark: 8,
|
|
56
|
-
},
|
|
57
|
-
cursorType: "pointer",
|
|
58
62
|
...props.mantine?.theme,
|
|
59
63
|
}}
|
|
60
64
|
>
|
|
@@ -119,7 +119,7 @@ const AppBar = (props: AppBarProps) => {
|
|
|
119
119
|
</Flex>
|
|
120
120
|
))}
|
|
121
121
|
</Flex>
|
|
122
|
-
<Flex flex={1}
|
|
122
|
+
<Flex flex={1} align={"center"} justify={"end"}>
|
|
123
123
|
{rightItems.map((item, index) => (
|
|
124
124
|
<Flex key={index} ml={index === 0 ? 0 : "md"} align="center">
|
|
125
125
|
{renderItem(item, index)}
|
|
@@ -20,7 +20,7 @@ const Omnibar = (props: OmnibarProps) => {
|
|
|
20
20
|
id: page.name,
|
|
21
21
|
label: page.label ?? page.name,
|
|
22
22
|
description: page.description,
|
|
23
|
-
onClick: () => router.go(page.
|
|
23
|
+
onClick: () => router.go(page.name),
|
|
24
24
|
leftSection: page.icon,
|
|
25
25
|
})),
|
|
26
26
|
[],
|