@alepha/ui 0.13.2 → 0.13.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/admin/AdminLayout-JakF7ESb.js +388 -0
- package/dist/admin/AdminLayout-JakF7ESb.js.map +1 -0
- package/dist/admin/AdminLayout-qNsIyl30.js +3 -0
- package/dist/admin/AdminNotifications-BPrxALdS.js +154 -0
- package/dist/admin/AdminNotifications-BPrxALdS.js.map +1 -0
- package/dist/admin/AdminNotifications-DV-35Fi3.js +3 -0
- package/dist/admin/{AdminSessions-CmDVneE2.js → AdminSessions-CMmBtbSw.js} +36 -9
- package/dist/admin/AdminSessions-CMmBtbSw.js.map +1 -0
- package/dist/admin/AdminSessions-Df2VYzlE.js +3 -0
- package/dist/admin/AdminUserCreate-Coa_yi6m.js +103 -0
- package/dist/admin/AdminUserCreate-Coa_yi6m.js.map +1 -0
- package/dist/admin/AdminUserCreate-DjiCcAk0.js +3 -0
- package/dist/admin/AdminUserDetails-BCFwOm9w.js +221 -0
- package/dist/admin/AdminUserDetails-BCFwOm9w.js.map +1 -0
- package/dist/admin/AdminUserDetails-C5yeJNa3.js +3 -0
- package/dist/admin/AdminUserLayout-B8ga5QvP.js +3 -0
- package/dist/admin/AdminUserLayout-CR2OqV9Z.js +153 -0
- package/dist/admin/AdminUserLayout-CR2OqV9Z.js.map +1 -0
- package/dist/admin/AdminUserSessions-A_5KkqTY.js +3 -0
- package/dist/admin/AdminUserSessions-Bcf6-rjG.js +129 -0
- package/dist/admin/AdminUserSessions-Bcf6-rjG.js.map +1 -0
- package/dist/admin/AdminUserSettings-DAsAhFjX.js +3 -0
- package/dist/admin/AdminUserSettings-DRYVdW6S.js +164 -0
- package/dist/admin/AdminUserSettings-DRYVdW6S.js.map +1 -0
- package/dist/admin/AdminUsers-Dd9a5UqO.js +3 -0
- package/dist/admin/{AdminUsers-88De5pev.js → AdminUsers-IN_2yHKt.js} +32 -14
- package/dist/admin/AdminUsers-IN_2yHKt.js.map +1 -0
- package/dist/admin/index.d.ts +5560 -416
- package/dist/admin/index.js +299 -41
- package/dist/admin/index.js.map +1 -1
- package/dist/auth/AuthLayout-BSL8ZHgr.js +19 -0
- package/dist/auth/AuthLayout-BSL8ZHgr.js.map +1 -0
- package/dist/auth/Login-DDsyCNAA.js +4 -0
- package/dist/auth/{Login-OCrvjs9U.js → Login-kBfaRgKG.js} +5 -4
- package/dist/auth/Login-kBfaRgKG.js.map +1 -0
- package/dist/auth/{Register-Ei34GSba.js → Register-BxJmOqpF.js} +9 -6
- package/dist/auth/Register-BxJmOqpF.js.map +1 -0
- package/dist/auth/Register-D10MnlQc.js +4 -0
- package/dist/auth/{ResetPassword-tO0oMzfo.js → ResetPassword-BhyZ9ek4.js} +3 -3
- package/dist/auth/ResetPassword-BhyZ9ek4.js.map +1 -0
- package/dist/auth/ResetPassword-llBG-STp.js +3 -0
- package/dist/auth/VerifyEmail-BvOG-IUC.js +3 -0
- package/dist/auth/VerifyEmail-DeLct3oQ.js +131 -0
- package/dist/auth/VerifyEmail-DeLct3oQ.js.map +1 -0
- package/dist/auth/index.d.ts +2412 -2254
- package/dist/auth/index.js +96 -20
- package/dist/auth/index.js.map +1 -1
- package/dist/core/index.d.ts +280 -95
- package/dist/core/index.js +1381 -392
- package/dist/core/index.js.map +1 -1
- package/package.json +5 -5
- package/src/admin/AdminRouter.ts +116 -29
- package/src/admin/MainRouter.ts +23 -0
- package/src/admin/components/AdminLayout.tsx +86 -103
- package/src/admin/components/AdminNotifications.tsx +196 -12
- package/src/admin/components/AdminSessions.tsx +43 -7
- package/src/admin/components/AdminUserCreate.tsx +84 -0
- package/src/admin/components/AdminUserDetails.tsx +180 -0
- package/src/admin/components/AdminUserLayout.tsx +172 -0
- package/src/admin/components/AdminUserSessions.tsx +158 -0
- package/src/admin/components/AdminUserSettings.tsx +165 -0
- package/src/admin/components/AdminUsers.tsx +29 -9
- package/src/admin/index.ts +12 -3
- package/src/auth/AuthI18n.ts +22 -0
- package/src/auth/AuthRouter.ts +82 -8
- package/src/auth/components/AuthLayout.tsx +12 -0
- package/src/auth/components/Login.tsx +13 -11
- package/src/auth/components/Register.tsx +6 -5
- package/src/auth/components/ResetPassword.tsx +1 -1
- package/src/auth/components/VerifyEmail.tsx +102 -0
- package/src/auth/components/buttons/UserButton.tsx +6 -2
- package/src/auth/index.ts +1 -0
- package/src/core/components/buttons/ActionButton.tsx +11 -4
- package/src/core/components/buttons/DarkModeButton.tsx +1 -1
- package/src/core/components/buttons/ThemeButton.tsx +31 -0
- package/src/core/components/layout/AdminShell.tsx +4 -2
- package/src/core/components/layout/AlephaMantineProvider.tsx +10 -4
- package/src/core/components/layout/Omnibar.tsx +27 -15
- package/src/core/components/layout/Sidebar.tsx +33 -15
- package/src/core/components/table/DataTable.tsx +9 -5
- package/src/core/hooks/useTheme.ts +25 -0
- package/src/core/index.ts +8 -3
- package/src/core/providers/ThemeProvider.ts +87 -0
- package/src/core/themes/aurora.ts +107 -0
- package/src/core/themes/crystal.ts +107 -0
- package/src/core/themes/default.ts +7 -0
- package/src/core/themes/ember.ts +107 -0
- package/src/core/themes/index.ts +7 -0
- package/src/core/themes/midnight.ts +104 -0
- package/src/core/themes/remoraid.ts +278 -0
- package/src/core/themes/slate.ts +81 -0
- package/dist/admin/AdminJobs-BOq6AZOW.js +0 -3
- package/dist/admin/AdminJobs-CDnVxEv6.js +0 -125
- package/dist/admin/AdminJobs-CDnVxEv6.js.map +0 -1
- package/dist/admin/AdminLayout-Bgx25J8m.js +0 -3
- package/dist/admin/AdminLayout-CervL8LV.js +0 -88
- package/dist/admin/AdminLayout-CervL8LV.js.map +0 -1
- package/dist/admin/AdminNotifications-BDQXt3-e.js +0 -3
- package/dist/admin/AdminNotifications-DvI2989x.js +0 -40
- package/dist/admin/AdminNotifications-DvI2989x.js.map +0 -1
- package/dist/admin/AdminParameters-D_v0GAvI.js +0 -3
- package/dist/admin/AdminParameters-P1LB6ZI1.js +0 -40
- package/dist/admin/AdminParameters-P1LB6ZI1.js.map +0 -1
- package/dist/admin/AdminSessions-CmDVneE2.js.map +0 -1
- package/dist/admin/AdminSessions-Dkk_fzWK.js +0 -3
- package/dist/admin/AdminUsers-88De5pev.js.map +0 -1
- package/dist/admin/AdminUsers-oyAXqZ5l.js +0 -3
- package/dist/admin/AdminVerifications-D93TKymL.js +0 -3
- package/dist/admin/AdminVerifications-DBVEoqJe.js +0 -40
- package/dist/admin/AdminVerifications-DBVEoqJe.js.map +0 -1
- package/dist/auth/Login-BC2jTczq.js +0 -4
- package/dist/auth/Login-OCrvjs9U.js.map +0 -1
- package/dist/auth/Register-Dh0lsQmI.js +0 -4
- package/dist/auth/Register-Ei34GSba.js.map +0 -1
- package/dist/auth/ResetPassword-BnlAQAOE.js +0 -3
- package/dist/auth/ResetPassword-tO0oMzfo.js.map +0 -1
package/package.json
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
"mantine"
|
|
8
8
|
],
|
|
9
9
|
"author": "Nicolas Foures",
|
|
10
|
-
"version": "0.13.
|
|
10
|
+
"version": "0.13.3",
|
|
11
11
|
"type": "module",
|
|
12
12
|
"engines": {
|
|
13
13
|
"node": ">=22.0.0"
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"src"
|
|
22
22
|
],
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@alepha/react": "0.13.
|
|
24
|
+
"@alepha/react": "0.13.3",
|
|
25
25
|
"@mantine/core": "^8.3.9",
|
|
26
26
|
"@mantine/dates": "^8.3.9",
|
|
27
27
|
"@mantine/hooks": "^8.3.9",
|
|
@@ -30,12 +30,12 @@
|
|
|
30
30
|
"@mantine/nprogress": "^8.3.9",
|
|
31
31
|
"@mantine/spotlight": "^8.3.9",
|
|
32
32
|
"@tabler/icons-react": "^3.35.0",
|
|
33
|
-
"alepha": "0.13.
|
|
33
|
+
"alepha": "0.13.3",
|
|
34
34
|
"dayjs": "^1.11.19"
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|
|
37
|
-
"react": "^19.2.
|
|
38
|
-
"react-dom": "^19.2.
|
|
37
|
+
"react": "^19.2.1",
|
|
38
|
+
"react-dom": "^19.2.1"
|
|
39
39
|
},
|
|
40
40
|
"peerDependencies": {
|
|
41
41
|
"react": "*",
|
package/src/admin/AdminRouter.ts
CHANGED
|
@@ -1,58 +1,145 @@
|
|
|
1
|
-
import { $page } from "@alepha/react";
|
|
1
|
+
import { $page, ReactRouter, Redirection } from "@alepha/react";
|
|
2
|
+
import { ReactAuth } from "@alepha/react/auth";
|
|
3
|
+
import { AuthRouter } from "@alepha/ui/auth";
|
|
4
|
+
import {
|
|
5
|
+
IconBell,
|
|
6
|
+
IconDevices,
|
|
7
|
+
IconFile,
|
|
8
|
+
IconPlus,
|
|
9
|
+
IconUser,
|
|
10
|
+
IconUsers,
|
|
11
|
+
} from "@tabler/icons-react";
|
|
12
|
+
import { $inject } from "alepha";
|
|
13
|
+
import type { FileController } from "alepha/api/files";
|
|
14
|
+
import type { NotificationController } from "alepha/api/notifications";
|
|
15
|
+
import type { SessionController, UserController } from "alepha/api/users";
|
|
16
|
+
import { $client } from "alepha/server/links";
|
|
2
17
|
|
|
3
18
|
export class AdminRouter {
|
|
4
|
-
|
|
19
|
+
protected readonly router = $inject(ReactRouter);
|
|
20
|
+
protected readonly authRouter = $inject(AuthRouter);
|
|
21
|
+
protected readonly auth = $inject(ReactAuth);
|
|
22
|
+
protected readonly userCtrl = $client<UserController>();
|
|
23
|
+
protected readonly sessionCtrl = $client<SessionController>();
|
|
24
|
+
protected readonly notificationCtrl = $client<NotificationController>();
|
|
25
|
+
protected readonly fileCtrl = $client<FileController>();
|
|
26
|
+
|
|
27
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
28
|
+
// Layout
|
|
29
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
30
|
+
|
|
31
|
+
public readonly layout = $page({
|
|
32
|
+
name: "AdminLayout",
|
|
5
33
|
path: "/admin",
|
|
6
34
|
label: "Admin",
|
|
7
35
|
lazy: () => import("./components/AdminLayout.tsx"),
|
|
36
|
+
resolve: ({ user, url }) => {
|
|
37
|
+
if (!user) {
|
|
38
|
+
throw new Redirection(
|
|
39
|
+
this.router.path(this.authRouter.login.name, {
|
|
40
|
+
query: {
|
|
41
|
+
r: url.pathname,
|
|
42
|
+
},
|
|
43
|
+
}),
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
return {};
|
|
47
|
+
},
|
|
8
48
|
});
|
|
9
49
|
|
|
10
|
-
|
|
11
|
-
|
|
50
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
51
|
+
// Users
|
|
52
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
53
|
+
|
|
54
|
+
public readonly adminUsers = $page({
|
|
55
|
+
icon: IconUsers,
|
|
56
|
+
parent: this.layout,
|
|
12
57
|
path: "/users",
|
|
13
58
|
label: "Users",
|
|
59
|
+
description: "Manage application users and their roles.",
|
|
14
60
|
lazy: () => import("./components/AdminUsers.tsx"),
|
|
61
|
+
can: () => this.userCtrl.findUsers.can(),
|
|
15
62
|
});
|
|
16
63
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
64
|
+
public readonly adminUserCreate = $page({
|
|
65
|
+
icon: IconPlus,
|
|
66
|
+
parent: this.layout,
|
|
67
|
+
path: "/users/create",
|
|
68
|
+
label: "Create User",
|
|
69
|
+
description: "Create a new user account.",
|
|
70
|
+
lazy: () => import("./components/AdminUserCreate.tsx"),
|
|
71
|
+
can: () => this.userCtrl.createUser.can(),
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
public readonly adminUserLayout = $page({
|
|
75
|
+
icon: IconUser,
|
|
76
|
+
parent: this.layout,
|
|
77
|
+
path: "/users/:userId",
|
|
78
|
+
label: "User",
|
|
79
|
+
lazy: () => import("./components/AdminUserLayout.tsx"),
|
|
80
|
+
can: () => this.userCtrl.getUser.can(),
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
public readonly adminUserDetails = $page({
|
|
84
|
+
parent: this.adminUserLayout,
|
|
85
|
+
path: "/details",
|
|
86
|
+
label: "Details",
|
|
87
|
+
lazy: () => import("./components/AdminUserDetails.tsx"),
|
|
22
88
|
});
|
|
23
89
|
|
|
24
|
-
|
|
25
|
-
parent: this.
|
|
90
|
+
public readonly adminUserSessions = $page({
|
|
91
|
+
parent: this.adminUserLayout,
|
|
26
92
|
path: "/sessions",
|
|
27
93
|
label: "Sessions",
|
|
28
|
-
lazy: () => import("./components/
|
|
94
|
+
lazy: () => import("./components/AdminUserSessions.tsx"),
|
|
29
95
|
});
|
|
30
96
|
|
|
31
|
-
|
|
32
|
-
parent: this.
|
|
33
|
-
path: "/
|
|
34
|
-
label: "
|
|
35
|
-
lazy: () => import("./components/
|
|
97
|
+
public readonly adminUserSettings = $page({
|
|
98
|
+
parent: this.adminUserLayout,
|
|
99
|
+
path: "/settings",
|
|
100
|
+
label: "Settings",
|
|
101
|
+
lazy: () => import("./components/AdminUserSettings.tsx"),
|
|
36
102
|
});
|
|
37
103
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
104
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
105
|
+
// Sessions
|
|
106
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
107
|
+
|
|
108
|
+
public readonly adminSessions = $page({
|
|
109
|
+
icon: IconDevices,
|
|
110
|
+
parent: this.layout,
|
|
111
|
+
path: "/sessions",
|
|
112
|
+
label: "Sessions",
|
|
113
|
+
description: "View and manage all active sessions.",
|
|
114
|
+
lazy: () => import("./components/AdminSessions.tsx"),
|
|
115
|
+
can: () => this.sessionCtrl.findSessions.can(),
|
|
43
116
|
});
|
|
44
117
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
118
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
119
|
+
// Notifications
|
|
120
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
121
|
+
|
|
122
|
+
public readonly adminNotifications = $page({
|
|
123
|
+
icon: IconBell,
|
|
124
|
+
parent: this.layout,
|
|
125
|
+
path: "/notifications",
|
|
126
|
+
label: "Notifications",
|
|
127
|
+
description: "View notification history and status.",
|
|
128
|
+
lazy: () => import("./components/AdminNotifications.tsx"),
|
|
129
|
+
can: () => this.notificationCtrl.findNotifications.can(),
|
|
50
130
|
});
|
|
51
131
|
|
|
52
|
-
|
|
53
|
-
|
|
132
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
133
|
+
// Files
|
|
134
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
135
|
+
|
|
136
|
+
public readonly adminFiles = $page({
|
|
137
|
+
icon: IconFile,
|
|
138
|
+
parent: this.layout,
|
|
54
139
|
path: "/files",
|
|
55
140
|
label: "Files",
|
|
141
|
+
description: "Manage uploaded files and storage.",
|
|
56
142
|
lazy: () => import("./components/AdminFiles.tsx"),
|
|
143
|
+
can: () => this.fileCtrl.findFiles.can(),
|
|
57
144
|
});
|
|
58
145
|
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { $page } from "@alepha/react";
|
|
2
|
+
import { AlephaMantineProvider } from "@alepha/ui";
|
|
3
|
+
import { AuthRouter } from "@alepha/ui/auth";
|
|
4
|
+
import { $inject } from "alepha";
|
|
5
|
+
import { AdminRouter } from "./AdminRouter.ts";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Main application router that combines Auth and Admin routers.
|
|
9
|
+
*
|
|
10
|
+
* We assume that the main application router will always have Admin and Auth routers.
|
|
11
|
+
*
|
|
12
|
+
* This is basically a convenience class to avoid having to inject these routers everywhere.
|
|
13
|
+
* Code is lightweight enough that we can just copy it if needed.
|
|
14
|
+
*/
|
|
15
|
+
export class MainRouter {
|
|
16
|
+
auth = $inject(AuthRouter);
|
|
17
|
+
admin = $inject(AdminRouter);
|
|
18
|
+
|
|
19
|
+
layout = $page({
|
|
20
|
+
component: AlephaMantineProvider,
|
|
21
|
+
children: () => [this.auth.layout, this.admin.layout],
|
|
22
|
+
});
|
|
23
|
+
}
|
|
@@ -1,113 +1,96 @@
|
|
|
1
1
|
import { NestedView, useRouter } from "@alepha/react";
|
|
2
|
-
import { AdminShell, AlephaMantineProvider } from "@alepha/ui";
|
|
3
|
-
import { Flex } from "@mantine/core";
|
|
4
2
|
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
3
|
+
ActionButton,
|
|
4
|
+
AdminShell,
|
|
5
|
+
OmnibarButton,
|
|
6
|
+
ThemeButton,
|
|
7
|
+
} from "@alepha/ui";
|
|
8
|
+
import { UserButton } from "@alepha/ui/auth";
|
|
9
|
+
import { Flex } from "@mantine/core";
|
|
10
|
+
import { IconArrowLeft } from "@tabler/icons-react";
|
|
11
|
+
import ToggleSidebarButton from "../../core/components/buttons/ToggleSidebarButton.tsx";
|
|
14
12
|
import type { AdminRouter } from "../AdminRouter.ts";
|
|
15
13
|
|
|
16
14
|
const AdminLayout = () => {
|
|
17
15
|
const router = useRouter<AdminRouter>();
|
|
18
16
|
return (
|
|
19
|
-
<
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
bg: "var(--alepha-background)",
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
{
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
}}
|
|
97
|
-
>
|
|
98
|
-
<Flex
|
|
99
|
-
flex={1}
|
|
100
|
-
p={"lg"}
|
|
101
|
-
mt={-16}
|
|
102
|
-
ml={-16}
|
|
103
|
-
bg={"var(--alepha-surface)"}
|
|
104
|
-
bdrs={"lg"}
|
|
105
|
-
bd={"1px solid var(--alepha-border)"}
|
|
106
|
-
>
|
|
107
|
-
<NestedView />
|
|
108
|
-
</Flex>
|
|
109
|
-
</AdminShell>
|
|
110
|
-
</AlephaMantineProvider>
|
|
17
|
+
<AdminShell
|
|
18
|
+
appShellMainProps={{
|
|
19
|
+
bg: "var(--alepha-surface)",
|
|
20
|
+
}}
|
|
21
|
+
appShellHeaderProps={{
|
|
22
|
+
bg: "var(--alepha-background)",
|
|
23
|
+
}}
|
|
24
|
+
appShellNavbarProps={
|
|
25
|
+
{
|
|
26
|
+
// bg: "var(--alepha-background)",
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
appShellProps={
|
|
30
|
+
{
|
|
31
|
+
// withBorder: false,
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
appShellFooterProps={{
|
|
35
|
+
bg: "var(--alepha-background)",
|
|
36
|
+
}}
|
|
37
|
+
footer={<Flex h={12} />}
|
|
38
|
+
appBarProps={{
|
|
39
|
+
items: [
|
|
40
|
+
{
|
|
41
|
+
element: <ActionButton icon={IconArrowLeft} href={"/"} />,
|
|
42
|
+
position: "left",
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
element: <OmnibarButton actionProps={{ variant: "outline" }} />,
|
|
46
|
+
position: "right",
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
element: <UserButton />,
|
|
50
|
+
position: "right",
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
element: <ThemeButton />,
|
|
54
|
+
position: "right",
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
type: "dark",
|
|
58
|
+
position: "right",
|
|
59
|
+
},
|
|
60
|
+
],
|
|
61
|
+
}}
|
|
62
|
+
sidebarProps={{
|
|
63
|
+
gap: "xs",
|
|
64
|
+
menu: [
|
|
65
|
+
{
|
|
66
|
+
element: <ToggleSidebarButton />,
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
type: "spacer",
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
...router.node("adminUsers"),
|
|
73
|
+
description: undefined,
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
...router.node("adminSessions"),
|
|
77
|
+
description: undefined,
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
...router.node("adminNotifications"),
|
|
81
|
+
description: undefined,
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
...router.node("adminFiles"),
|
|
85
|
+
description: undefined,
|
|
86
|
+
},
|
|
87
|
+
],
|
|
88
|
+
}}
|
|
89
|
+
>
|
|
90
|
+
<Flex flex={1} bg={"var(--alepha-surface)"}>
|
|
91
|
+
<NestedView />
|
|
92
|
+
</Flex>
|
|
93
|
+
</AdminShell>
|
|
111
94
|
);
|
|
112
95
|
};
|
|
113
96
|
|
|
@@ -1,18 +1,202 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import { useClient } from "@alepha/react";
|
|
2
|
+
import { useI18n } from "@alepha/react/i18n";
|
|
3
|
+
import { DataTable, Flex, Text } from "@alepha/ui";
|
|
4
|
+
import { Badge, Tooltip } from "@mantine/core";
|
|
5
|
+
import {
|
|
6
|
+
IconAlertCircle,
|
|
7
|
+
IconCheck,
|
|
8
|
+
IconClock,
|
|
9
|
+
IconMail,
|
|
10
|
+
IconMessage,
|
|
11
|
+
} from "@tabler/icons-react";
|
|
12
|
+
import { type Page, t } from "alepha";
|
|
13
|
+
import type {
|
|
14
|
+
NotificationController,
|
|
15
|
+
NotificationEntity,
|
|
16
|
+
} from "alepha/api/notifications";
|
|
4
17
|
|
|
5
18
|
const AdminNotifications = () => {
|
|
19
|
+
const client = useClient<NotificationController>();
|
|
20
|
+
const { l } = useI18n();
|
|
21
|
+
|
|
22
|
+
const filters = t.object({
|
|
23
|
+
type: t.optional(
|
|
24
|
+
t.enum(["email", "sms"], {
|
|
25
|
+
title: "Type",
|
|
26
|
+
}),
|
|
27
|
+
),
|
|
28
|
+
status: t.optional(
|
|
29
|
+
t.enum(["pending", "sent", "failed"], {
|
|
30
|
+
title: "Status",
|
|
31
|
+
}),
|
|
32
|
+
),
|
|
33
|
+
template: t.optional(
|
|
34
|
+
t.string({
|
|
35
|
+
title: "Template",
|
|
36
|
+
}),
|
|
37
|
+
),
|
|
38
|
+
contact: t.optional(
|
|
39
|
+
t.string({
|
|
40
|
+
title: "Contact",
|
|
41
|
+
}),
|
|
42
|
+
),
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
const getStatus = (item: NotificationEntity) => {
|
|
46
|
+
if (item.error) return "failed";
|
|
47
|
+
if (item.sentAt) return "sent";
|
|
48
|
+
return "pending";
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const getStatusBadge = (item: NotificationEntity) => {
|
|
52
|
+
const status = getStatus(item);
|
|
53
|
+
switch (status) {
|
|
54
|
+
case "sent":
|
|
55
|
+
return (
|
|
56
|
+
<Badge
|
|
57
|
+
size="sm"
|
|
58
|
+
variant="light"
|
|
59
|
+
color="green"
|
|
60
|
+
leftSection={<IconCheck size={12} />}
|
|
61
|
+
>
|
|
62
|
+
Sent
|
|
63
|
+
</Badge>
|
|
64
|
+
);
|
|
65
|
+
case "failed":
|
|
66
|
+
return (
|
|
67
|
+
<Tooltip label={item.error?.message} multiline maw={300}>
|
|
68
|
+
<Badge
|
|
69
|
+
size="sm"
|
|
70
|
+
variant="light"
|
|
71
|
+
color="red"
|
|
72
|
+
leftSection={<IconAlertCircle size={12} />}
|
|
73
|
+
>
|
|
74
|
+
Failed
|
|
75
|
+
</Badge>
|
|
76
|
+
</Tooltip>
|
|
77
|
+
);
|
|
78
|
+
default:
|
|
79
|
+
return (
|
|
80
|
+
<Badge
|
|
81
|
+
size="sm"
|
|
82
|
+
variant="light"
|
|
83
|
+
color="yellow"
|
|
84
|
+
leftSection={<IconClock size={12} />}
|
|
85
|
+
>
|
|
86
|
+
Pending
|
|
87
|
+
</Badge>
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
|
|
6
92
|
return (
|
|
7
|
-
<Flex flex={1}
|
|
8
|
-
<
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
93
|
+
<Flex flex={1} direction="column">
|
|
94
|
+
<DataTable<NotificationEntity, typeof filters>
|
|
95
|
+
submitOnInit
|
|
96
|
+
defaultSize={10}
|
|
97
|
+
typeFormProps={{
|
|
98
|
+
skipSubmitButton: true,
|
|
99
|
+
columns: 4,
|
|
100
|
+
}}
|
|
101
|
+
tableProps={{
|
|
102
|
+
horizontalSpacing: "xs",
|
|
103
|
+
verticalSpacing: "xs",
|
|
104
|
+
}}
|
|
105
|
+
onFilterChange={(_key, _value, form) => {
|
|
106
|
+
return form.submit();
|
|
107
|
+
}}
|
|
108
|
+
filters={filters}
|
|
109
|
+
tableTrProps={(item) => {
|
|
110
|
+
const status = getStatus(item);
|
|
111
|
+
if (status === "failed") {
|
|
112
|
+
return {
|
|
113
|
+
bg: "var(--mantine-color-red-light)",
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
return {};
|
|
117
|
+
}}
|
|
118
|
+
items={async (filters) => {
|
|
119
|
+
const response = await client.findNotifications({
|
|
120
|
+
query: filters,
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
return response as Page<NotificationEntity>;
|
|
124
|
+
}}
|
|
125
|
+
columns={{
|
|
126
|
+
type: {
|
|
127
|
+
label: "Type",
|
|
128
|
+
fit: true,
|
|
129
|
+
value: (item) => (
|
|
130
|
+
<Badge
|
|
131
|
+
size="sm"
|
|
132
|
+
variant="outline"
|
|
133
|
+
leftSection={
|
|
134
|
+
item.type === "email" ? (
|
|
135
|
+
<IconMail size={12} />
|
|
136
|
+
) : (
|
|
137
|
+
<IconMessage size={12} />
|
|
138
|
+
)
|
|
139
|
+
}
|
|
140
|
+
>
|
|
141
|
+
{item.type.toUpperCase()}
|
|
142
|
+
</Badge>
|
|
143
|
+
),
|
|
144
|
+
},
|
|
145
|
+
template: {
|
|
146
|
+
label: "Template",
|
|
147
|
+
value: (item) => (
|
|
148
|
+
<Text size="sm" fw={500}>
|
|
149
|
+
{item.template}
|
|
150
|
+
</Text>
|
|
151
|
+
),
|
|
152
|
+
},
|
|
153
|
+
contact: {
|
|
154
|
+
label: "Contact",
|
|
155
|
+
value: (item) => (
|
|
156
|
+
<Text size="sm" ff="monospace">
|
|
157
|
+
{item.contact}
|
|
158
|
+
</Text>
|
|
159
|
+
),
|
|
160
|
+
},
|
|
161
|
+
category: {
|
|
162
|
+
label: "Category",
|
|
163
|
+
fit: true,
|
|
164
|
+
value: (item) =>
|
|
165
|
+
item.category ? (
|
|
166
|
+
<Badge size="xs" variant="light">
|
|
167
|
+
{item.category}
|
|
168
|
+
</Badge>
|
|
169
|
+
) : (
|
|
170
|
+
<Text size="xs" c="dimmed">
|
|
171
|
+
-
|
|
172
|
+
</Text>
|
|
173
|
+
),
|
|
174
|
+
},
|
|
175
|
+
status: {
|
|
176
|
+
label: "Status",
|
|
177
|
+
fit: true,
|
|
178
|
+
value: (item) => getStatusBadge(item),
|
|
179
|
+
},
|
|
180
|
+
sentAt: {
|
|
181
|
+
label: "Sent",
|
|
182
|
+
fit: true,
|
|
183
|
+
value: (item) => (
|
|
184
|
+
<Text size="xs" c="dimmed">
|
|
185
|
+
{item.sentAt ? l(item.sentAt, { date: "fromNow" }) : "-"}
|
|
186
|
+
</Text>
|
|
187
|
+
),
|
|
188
|
+
},
|
|
189
|
+
createdAt: {
|
|
190
|
+
label: "Created",
|
|
191
|
+
fit: true,
|
|
192
|
+
value: (item) => (
|
|
193
|
+
<Text size="xs" c="dimmed">
|
|
194
|
+
{l(item.createdAt, { date: "fromNow" })}
|
|
195
|
+
</Text>
|
|
196
|
+
),
|
|
197
|
+
},
|
|
198
|
+
}}
|
|
199
|
+
/>
|
|
16
200
|
</Flex>
|
|
17
201
|
);
|
|
18
202
|
};
|