@arch-cadre/panel 1.0.7 → 1.0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (98) hide show
  1. package/dist/actions/activity-log/index.cjs +1 -1
  2. package/dist/actions/activity-log/index.mjs +1 -1
  3. package/dist/actions/index.cjs +2 -2
  4. package/dist/actions/index.d.ts +2 -2
  5. package/dist/actions/index.mjs +2 -2
  6. package/dist/actions/profile.d.ts +1 -1
  7. package/dist/index.cjs +7 -7
  8. package/dist/index.mjs +7 -7
  9. package/dist/routes.cjs +10 -10
  10. package/dist/routes.mjs +10 -10
  11. package/dist/schema.cjs +1 -1
  12. package/dist/schema.d.ts +1 -1
  13. package/dist/schema.mjs +1 -1
  14. package/dist/ui/activity-log/components/ActivityStatsWidget.cjs +1 -1
  15. package/dist/ui/activity-log/components/ActivityStatsWidget.mjs +1 -1
  16. package/dist/ui/activity-log/components/RecentLogsWidget.cjs +1 -1
  17. package/dist/ui/activity-log/components/RecentLogsWidget.mjs +1 -1
  18. package/dist/ui/activity-log/pages/log-list.cjs +2 -2
  19. package/dist/ui/activity-log/pages/log-list.mjs +1 -1
  20. package/dist/ui/components/app-content.cjs +1 -1
  21. package/dist/ui/components/app-content.mjs +1 -1
  22. package/dist/ui/components/app-sidebar.cjs +1 -1
  23. package/dist/ui/components/app-sidebar.mjs +1 -1
  24. package/dist/ui/components/app-user.cjs +1 -1
  25. package/dist/ui/components/app-user.mjs +1 -1
  26. package/dist/ui/components/manager/module-card.cjs +1 -1
  27. package/dist/ui/components/manager/module-card.mjs +1 -1
  28. package/dist/ui/components/manager/module-list.cjs +1 -1
  29. package/dist/ui/components/manager/module-list.mjs +1 -1
  30. package/dist/ui/components/manager/module-upload.cjs +1 -1
  31. package/dist/ui/components/manager/module-upload.mjs +1 -1
  32. package/dist/ui/components/profile/components.cjs +3 -3
  33. package/dist/ui/components/profile/components.d.ts +1 -1
  34. package/dist/ui/components/profile/components.mjs +2 -2
  35. package/dist/ui/components/profile/page.cjs +1 -1
  36. package/dist/ui/components/profile/page.mjs +1 -1
  37. package/dist/ui/components/sidebar-slot.cjs +1 -1
  38. package/dist/ui/components/sidebar-slot.mjs +1 -1
  39. package/dist/ui/layout.cjs +3 -3
  40. package/dist/ui/layout.mjs +3 -3
  41. package/dist/ui/modules/docs/page.cjs +1 -1
  42. package/dist/ui/modules/docs/page.mjs +1 -1
  43. package/dist/ui/modules/page.cjs +3 -3
  44. package/dist/ui/modules/page.mjs +3 -3
  45. package/dist/ui/rbac/pages/rbac-admin.cjs +14 -14
  46. package/dist/ui/rbac/pages/rbac-admin.mjs +1 -1
  47. package/dist/ui/router.cjs +1 -1
  48. package/dist/ui/router.mjs +1 -1
  49. package/dist/ui/session-manager/components/sessions-list.cjs +5 -5
  50. package/dist/ui/session-manager/components/sessions-list.mjs +2 -2
  51. package/dist/ui/session-manager/pages/sessions-page.cjs +4 -4
  52. package/dist/ui/session-manager/pages/sessions-page.mjs +3 -3
  53. package/dist/ui/settings-page.cjs +1 -1
  54. package/dist/ui/settings-page.mjs +1 -1
  55. package/package.json +7 -6
  56. package/src/actions/actions.ts +17 -0
  57. package/src/actions/activity-log/index.ts +17 -0
  58. package/src/actions/index.ts +2 -0
  59. package/src/actions/manager.ts +168 -0
  60. package/src/actions/profile.ts +173 -0
  61. package/src/actions/rbac/index.ts +131 -0
  62. package/src/actions/session-manager/index.ts +87 -0
  63. package/src/actions/settings.ts +34 -0
  64. package/src/index.ts +135 -0
  65. package/src/intl.d.ts +9 -0
  66. package/src/navigation.ts +57 -0
  67. package/src/routes.ts +107 -0
  68. package/src/schema/activity-log.ts +16 -0
  69. package/src/schema.ts +1 -0
  70. package/src/types.ts +18 -0
  71. package/src/ui/activity-log/components/ActivityStatsWidget.tsx +37 -0
  72. package/src/ui/activity-log/components/RecentLogsWidget.tsx +74 -0
  73. package/src/ui/activity-log/pages/log-list.tsx +91 -0
  74. package/src/ui/components/app-content.tsx +51 -0
  75. package/src/ui/components/app-header.tsx +65 -0
  76. package/src/ui/components/app-sidebar.tsx +249 -0
  77. package/src/ui/components/app-user.tsx +126 -0
  78. package/src/ui/components/breadcrumb-slot.tsx +52 -0
  79. package/src/ui/components/manager/module-card.tsx +327 -0
  80. package/src/ui/components/manager/module-list.tsx +59 -0
  81. package/src/ui/components/manager/module-upload.tsx +84 -0
  82. package/src/ui/components/profile/components.tsx +311 -0
  83. package/src/ui/components/profile/link.tsx +36 -0
  84. package/src/ui/components/profile/page.tsx +45 -0
  85. package/src/ui/components/sidebar-slot.tsx +47 -0
  86. package/src/ui/dashboard/page.tsx +17 -0
  87. package/src/ui/dashboard/widgets/WelcomeBackUserWidget.tsx +47 -0
  88. package/src/ui/error.tsx +82 -0
  89. package/src/ui/layout.tsx +54 -0
  90. package/src/ui/modules/docs/page.tsx +105 -0
  91. package/src/ui/modules/page.tsx +30 -0
  92. package/src/ui/page.tsx +15 -0
  93. package/src/ui/rbac/pages/rbac-admin.tsx +551 -0
  94. package/src/ui/router.tsx +69 -0
  95. package/src/ui/session-manager/components/sessions-list.tsx +303 -0
  96. package/src/ui/session-manager/pages/sessions-page.tsx +22 -0
  97. package/src/ui/settings/page.tsx +73 -0
  98. package/src/ui/settings-page.tsx +97 -0
@@ -0,0 +1,173 @@
1
+ "use server";
2
+
3
+ import type { SessionFlags } from "@arch-cadre/core";
4
+ import {
5
+ checkEmailAvailability,
6
+ createEmailVerificationRequest,
7
+ createSession,
8
+ eventBus,
9
+ filesystemService,
10
+ generateSessionToken,
11
+ getCurrentSession,
12
+ getUserPasswordHash,
13
+ invalidateUserSessions,
14
+ sendVerificationEmail,
15
+ setEmailVerificationRequestCookie,
16
+ setSessionTokenCookie,
17
+ updateUserAwatar,
18
+ updateUserName,
19
+ updateUserPassword,
20
+ verifyEmailInput,
21
+ verifyPasswordHash,
22
+ verifyPasswordStrength,
23
+ } from "@arch-cadre/core/server";
24
+ import { getTranslation } from "@arch-cadre/intl/server";
25
+ import { revalidatePath } from "next/cache";
26
+ import { redirect } from "next/navigation";
27
+ import type { ActionResult } from "../types.js";
28
+
29
+ export async function updatePasswordAction(
30
+ _prev: ActionResult,
31
+ formData: FormData,
32
+ ): Promise<ActionResult> {
33
+ const { t } = await getTranslation();
34
+ const { session, user } = await getCurrentSession();
35
+ if (session === null || user === null) {
36
+ return { success: false, message: t("Not authenticated") };
37
+ }
38
+
39
+ if (user.registered2FA && !session.two_factor_verified) {
40
+ return { success: false, message: t("Forbidden") };
41
+ }
42
+
43
+ const password = formData.get("password");
44
+ const newPassword = formData.get("new_password");
45
+
46
+ if (typeof password !== "string" || typeof newPassword !== "string") {
47
+ return { success: false, message: t("Invalid or missing fields") };
48
+ }
49
+
50
+ if (!verifyPasswordStrength(newPassword)) {
51
+ return { success: false, message: t("Weak password") };
52
+ }
53
+
54
+ const passwordHash = await getUserPasswordHash(user.id);
55
+ if (!passwordHash) return { success: false, message: t("Internal error") };
56
+
57
+ const validPassword = await verifyPasswordHash(passwordHash, password);
58
+ if (!validPassword) {
59
+ return { success: false, message: t("Incorrect password") };
60
+ }
61
+
62
+ await invalidateUserSessions(user.id);
63
+ await updateUserPassword(user.id, newPassword);
64
+
65
+ const sessionToken = await generateSessionToken();
66
+ const sessionFlags: SessionFlags = {
67
+ twoFactorVerified: session.two_factor_verified ?? false,
68
+ };
69
+ const newSession = await createSession(sessionToken, user.id, sessionFlags);
70
+ await setSessionTokenCookie(sessionToken, newSession.expiresAt);
71
+
72
+ await eventBus.publish(
73
+ "activity.create",
74
+ {
75
+ action: "profile.updated.password",
76
+ description: `User ${user?.name} updated their password`,
77
+ userId: user?.id,
78
+ metadata: {},
79
+ },
80
+ "profile",
81
+ );
82
+
83
+ return { success: true, message: t("Updated password") };
84
+ }
85
+
86
+ export async function updateEmailAction(
87
+ _prev: ActionResult,
88
+ formData: FormData,
89
+ ): Promise<ActionResult> {
90
+ const { t } = await getTranslation();
91
+ const { session, user } = await getCurrentSession();
92
+ if (session === null || user === null)
93
+ return { success: false, message: t("Not authenticated") };
94
+ if (user.registered2FA && !session.two_factor_verified)
95
+ return { success: false, message: t("Forbidden") };
96
+
97
+ const email = formData.get("email");
98
+ if (typeof email !== "string" || email === "") {
99
+ return { success: false, message: t("Please enter your email") };
100
+ }
101
+ if (!verifyEmailInput(email)) {
102
+ return { success: false, message: t("Please enter a valid email") };
103
+ }
104
+ const emailAvailable = await checkEmailAvailability(email);
105
+ if (!emailAvailable) {
106
+ return { success: false, message: t("This email is already used") };
107
+ }
108
+
109
+ const verificationRequest = await createEmailVerificationRequest(
110
+ user.id,
111
+ email,
112
+ );
113
+ await sendVerificationEmail(
114
+ verificationRequest.email,
115
+ verificationRequest.code,
116
+ );
117
+ await setEmailVerificationRequestCookie(verificationRequest);
118
+
119
+ await eventBus.publish(
120
+ "activity.create",
121
+ {
122
+ action: "profile.updated.email",
123
+ description: `User ${user?.name} updated their email`,
124
+ userId: user?.id,
125
+ metadata: { email },
126
+ },
127
+ "profile",
128
+ );
129
+
130
+ return redirect("/verify-email");
131
+ }
132
+
133
+ export async function updateProfileAction(
134
+ name: string,
135
+ image?: File,
136
+ ): Promise<{ error?: string; success?: boolean; message?: string }> {
137
+ const { session, user } = await getCurrentSession();
138
+
139
+ const { t } = await getTranslation();
140
+
141
+ if (session === null || user === null)
142
+ return { error: t("Not authenticated") };
143
+
144
+ if (!name || name.trim().length === 0)
145
+ return { error: t("Name is required") };
146
+ if (name.trim().length > 100) return { error: t("Name is too long") };
147
+
148
+ if (image instanceof File) {
149
+ const uploaded = await filesystemService.upload(image, "vercel-blob");
150
+
151
+ if ("error" in uploaded) {
152
+ return { error: uploaded.error };
153
+ }
154
+
155
+ await updateUserAwatar(user.id, uploaded.url);
156
+ }
157
+
158
+ await updateUserName(user.id, name.trim());
159
+ revalidatePath("/profile"); // Updated path
160
+
161
+ await eventBus.publish(
162
+ "activity.create",
163
+ {
164
+ action: "profile.updated",
165
+ description: `User ${user?.name} updated their profile`,
166
+ userId: user?.id,
167
+ metadata: { name, imageUrl: image instanceof File ? image.name : null },
168
+ },
169
+ "profile",
170
+ );
171
+
172
+ return { success: true, message: t("Profile updated successfully") };
173
+ }
@@ -0,0 +1,131 @@
1
+ "use server";
2
+
3
+ import * as rbac from "@arch-cadre/core/server";
4
+ import { db, eventBus, userTable } from "@arch-cadre/core/server";
5
+ import { revalidatePath } from "next/cache";
6
+
7
+ /**
8
+ * RBAC Manager Module Actions
9
+ * These actions act as a bridge between the UI and the Core RBAC Logic.
10
+ */
11
+
12
+ export async function getRoles() {
13
+ return await rbac.getRoles();
14
+ }
15
+
16
+ export async function createRole(name: string, description?: string) {
17
+ const result = await rbac.createRole(name, description);
18
+ revalidatePath("/", "layout");
19
+ return JSON.parse(JSON.stringify(result));
20
+ }
21
+
22
+ export async function deleteRole(roleId: string) {
23
+ await rbac.deleteRole(roleId);
24
+ revalidatePath("/", "layout");
25
+ return { success: true };
26
+ }
27
+
28
+ export async function getPermissions() {
29
+ return await rbac.getPermissions();
30
+ }
31
+
32
+ export async function createPermission(name: string, description?: string) {
33
+ const result = await rbac.createPermission(name, description);
34
+ revalidatePath("/", "layout");
35
+ return JSON.parse(JSON.stringify(result));
36
+ }
37
+
38
+ export async function deletePermission(permissionId: string) {
39
+ await rbac.deletePermission(permissionId);
40
+ revalidatePath("/", "layout");
41
+ return { success: true };
42
+ }
43
+
44
+ export async function getRolePermissions(roleId: string) {
45
+ return await rbac.getRolePermissions(roleId);
46
+ }
47
+
48
+ export async function assignPermissionToRole(
49
+ roleId: string,
50
+ permissionId: string,
51
+ ) {
52
+ await rbac.assignPermissionToRole(roleId, permissionId);
53
+ revalidatePath("/", "layout");
54
+ return { success: true };
55
+ }
56
+
57
+ export async function revokePermissionFromRole(
58
+ roleId: string,
59
+ permissionId: string,
60
+ ) {
61
+ await rbac.revokePermissionFromRole(roleId, permissionId);
62
+ revalidatePath("/", "layout");
63
+ return { success: true };
64
+ }
65
+
66
+ // User-specific actions (could also be in a 'user-manager' module)
67
+ export async function getUsers() {
68
+ return await db
69
+ .select({
70
+ id: userTable.id,
71
+ name: userTable.name,
72
+ email: userTable.email,
73
+ image: userTable.image,
74
+ })
75
+ .from(userTable);
76
+ }
77
+ export async function getUserRbacData(userId: string) {
78
+ return await rbac.getUserRbacData(userId);
79
+ }
80
+
81
+ export async function assignRoleToUser(userId: string, roleId: string) {
82
+ const role = await rbac.getRoleById(roleId);
83
+ await rbac.assignRoleToUser(userId, roleId);
84
+
85
+ if (role) {
86
+ await eventBus.publish("notification:send", {
87
+ userId,
88
+ title: "Role assigned",
89
+ content: `You have been assigned the role: ${role.name}`,
90
+ type: "info",
91
+ });
92
+ }
93
+
94
+ revalidatePath("/", "layout");
95
+ return { success: true };
96
+ }
97
+
98
+ export async function revokeRoleFromUser(userId: string, roleId: string) {
99
+ const role = await rbac.getRoleById(roleId);
100
+ await rbac.revokeRoleFromUser(userId, roleId);
101
+
102
+ if (role) {
103
+ await eventBus.publish("notification:send", {
104
+ userId,
105
+ title: "Role revoked",
106
+ content: `The role ${role.name} has been removed from your account`,
107
+ type: "warning",
108
+ });
109
+ }
110
+
111
+ revalidatePath("/", "layout");
112
+ return { success: true };
113
+ }
114
+
115
+ export async function assignPermissionToUser(
116
+ userId: string,
117
+ permissionId: string,
118
+ ) {
119
+ await rbac.assignPermissionToUser(userId, permissionId);
120
+ revalidatePath("/", "layout");
121
+ return { success: true };
122
+ }
123
+
124
+ export async function revokePermissionFromUser(
125
+ userId: string,
126
+ permissionId: string,
127
+ ) {
128
+ await rbac.revokePermissionFromUser(userId, permissionId);
129
+ revalidatePath("/", "layout");
130
+ return { success: true };
131
+ }
@@ -0,0 +1,87 @@
1
+ "use server";
2
+
3
+ import {
4
+ eventBus,
5
+ getCurrentSession,
6
+ getUserSessions,
7
+ invalidateOtherSessions,
8
+ invalidateSession,
9
+ } from "@arch-cadre/core/server";
10
+ import { getTranslation } from "@arch-cadre/intl/server";
11
+ import { revalidatePath } from "next/cache";
12
+
13
+ export async function getSessionsAction() {
14
+ const { t } = await getTranslation();
15
+ const { session, user } = await getCurrentSession();
16
+ if (!session || !user) return { error: t("Not authenticated"), sessions: [] };
17
+
18
+ const sessions = await getUserSessions(user.id, session.id);
19
+ const sortedSessions = sessions.sort((a, b) => {
20
+ if (a.isCurrent) return -1;
21
+ if (b.isCurrent) return 1;
22
+ return b.createdAt.getTime() - a.createdAt.getTime();
23
+ });
24
+
25
+ return {
26
+ sessions: sortedSessions.map((s) => ({
27
+ id: s.id,
28
+ createdAt: s.createdAt,
29
+ expiresAt: s.expiresAt,
30
+ twoFactorVerified: s.twoFactorVerified,
31
+ isCurrent: s.isCurrent,
32
+ })),
33
+ };
34
+ }
35
+
36
+ export async function revokeSessionAction(sessionId: string) {
37
+ const { t } = await getTranslation();
38
+ const { session, user } = await getCurrentSession();
39
+ if (!session || !user) return { error: t("Not authenticated") };
40
+
41
+ if (sessionId === session.id) {
42
+ return { error: t("Cannot revoke current session. Use sign out instead.") };
43
+ }
44
+
45
+ const userSessions = await getUserSessions(user.id, session.id);
46
+ if (!userSessions.find((s) => s.id === sessionId)) {
47
+ return { error: t("Session not found") };
48
+ }
49
+
50
+ await invalidateSession(sessionId);
51
+ revalidatePath("/settings/sessions");
52
+
53
+ await eventBus.publish(
54
+ "activity.create",
55
+ {
56
+ action: "session.revoked",
57
+ description: `User ${user?.name} revoked a session`,
58
+ userId: user?.id,
59
+ metadata: { sessionId },
60
+ },
61
+ "session-manager",
62
+ );
63
+
64
+ return { success: true };
65
+ }
66
+
67
+ export async function revokeAllOtherSessionsAction() {
68
+ const { session, user } = await getCurrentSession();
69
+ const { t } = await getTranslation();
70
+ if (!session || !user) return { error: t("Not authenticated") };
71
+
72
+ await invalidateOtherSessions(user.id, session.id);
73
+ revalidatePath("/settings/sessions");
74
+
75
+ await eventBus.publish(
76
+ "activity.create",
77
+ {
78
+ action: "session.revoked.all-others",
79
+ description: `User ${user?.name} revoked all other sessions`,
80
+ userId: user?.id,
81
+ metadata: {},
82
+ },
83
+ "session-manager",
84
+ );
85
+
86
+ return { success: true };
87
+ }
@@ -0,0 +1,34 @@
1
+ "use server";
2
+
3
+ import {
4
+ getModuleConfig,
5
+ updateModuleConfig,
6
+ } from "@arch-cadre/modules/server";
7
+ import { revalidatePath } from "next/cache";
8
+
9
+ export interface KryoPanelConfig {
10
+ pathPrefix: string;
11
+ }
12
+
13
+ export async function getKryoConfig(): Promise<KryoPanelConfig> {
14
+ const config = await getModuleConfig<KryoPanelConfig>("kryo-panel");
15
+ return (
16
+ config ?? {
17
+ pathPrefix: "/kryo",
18
+ }
19
+ );
20
+ }
21
+
22
+ export async function updateKryoConfig(config: KryoPanelConfig) {
23
+ // Simple validation: must start with / and be at least 2 chars
24
+ if (!config.pathPrefix.startsWith("/") || config.pathPrefix.length < 2) {
25
+ throw new Error("Path prefix must start with '/' and be valid.");
26
+ }
27
+
28
+ await updateModuleConfig("kryo-panel", config);
29
+
30
+ // Revalidate everything since the base path changed
31
+ revalidatePath("/", "layout");
32
+
33
+ return { success: true };
34
+ }
package/src/index.ts ADDED
@@ -0,0 +1,135 @@
1
+ import type { SystemEvent } from "@arch-cadre/core/server";
2
+ import { db, eventBus } from "@arch-cadre/core/server";
3
+ import type { IModule } from "@arch-cadre/modules";
4
+ import manifest from "../manifest.json";
5
+ import { navigation } from "./navigation.js";
6
+ import { apiRoutes, privateRoutes, publicRoutes } from "./routes.js";
7
+ import { activityLogsTable } from "./schema/activity-log.js";
8
+ import ActivityStatsWidget from "./ui/activity-log/components/ActivityStatsWidget.js";
9
+ import RecentLogsWidget from "./ui/activity-log/components/RecentLogsWidget.js";
10
+ import { UserDropdownLink } from "./ui/components/profile/link.js";
11
+ import WelcomeBackUserWidget from "./ui/dashboard/widgets/WelcomeBackUserWidget.js";
12
+
13
+ export interface ActivityCreatePayload {
14
+ action: string;
15
+ description: string;
16
+ userId?: string;
17
+ metadata?: any;
18
+ }
19
+
20
+ const kryoPanelModule: IModule = {
21
+ manifest,
22
+
23
+ init: async () => {
24
+ console.log("[ActivityLog] Registering core system listeners...");
25
+
26
+ // Helper to log any event
27
+ const logEvent = async (
28
+ event: SystemEvent<any>,
29
+ descriptionOverride?: string,
30
+ ) => {
31
+ try {
32
+ const userId = event.payload?.userId || undefined;
33
+ await db.insert(activityLogsTable).values({
34
+ userId,
35
+ action: event.type,
36
+ description: descriptionOverride || `System event: ${event.type}`,
37
+ metadata: event.payload,
38
+ });
39
+ } catch (error) {
40
+ console.error(
41
+ `[ActivityLog] Failed to save log for ${event.type}:`,
42
+ error,
43
+ );
44
+ }
45
+ };
46
+
47
+ // 1. Existing manual activity creation
48
+ eventBus.subscribe(
49
+ "activity.create",
50
+ "activity-log-manual",
51
+ async (event) => {
52
+ const { action, description, userId, metadata } = event.payload as any;
53
+ await db
54
+ .insert(activityLogsTable)
55
+ .values({ userId, action, description, metadata });
56
+ },
57
+ );
58
+
59
+ // 2. Auth Events
60
+ eventBus.subscribe("auth:login", "activity-log-auth", (e) =>
61
+ logEvent(e, `User logged in: ${(e.payload as any).email}`),
62
+ );
63
+ eventBus.subscribe("auth:signup", "activity-log-auth", (e) =>
64
+ logEvent(e, `New user registered: ${(e.payload as any).email}`),
65
+ );
66
+ eventBus.subscribe("auth:logout", "activity-log-auth", (e) =>
67
+ logEvent(e, `User logged out: ${(e.payload as any).email}`),
68
+ );
69
+
70
+ // 3. System Module Events
71
+ eventBus.subscribe("system:module:toggle", "activity-log-system", (e) =>
72
+ logEvent(
73
+ e,
74
+ `Module "${(e.payload as any).moduleId}" ${(e.payload as any).isEnabled ? "enabled" : "disabled"}`,
75
+ ),
76
+ );
77
+ eventBus.subscribe("system:module:upload", "activity-log-system", (e) =>
78
+ logEvent(e, `New module uploaded: "${(e.payload as any).moduleId}"`),
79
+ );
80
+ },
81
+
82
+ onDisable: async () => {
83
+ console.log("[ActivityLog] Unsubscribing listeners...");
84
+ eventBus.unsubscribe("activity.create", "activity-log-manual");
85
+ eventBus.unsubscribe("auth:login", "activity-log-auth");
86
+ eventBus.unsubscribe("auth:signup", "activity-log-auth");
87
+ eventBus.unsubscribe("auth:logout", "activity-log-auth");
88
+ eventBus.unsubscribe("system:module:toggle", "activity-log-system");
89
+ eventBus.unsubscribe("system:module:upload", "activity-log-system");
90
+ },
91
+
92
+ widgets: [
93
+ {
94
+ id: "welcome-back-user",
95
+ name: "Welcome Back User",
96
+ area: "dashboard-stats",
97
+ component: WelcomeBackUserWidget,
98
+ priority: 0,
99
+ },
100
+ {
101
+ id: "recent-activity",
102
+ name: "Recent Activity",
103
+ area: "dashboard-main",
104
+ component: RecentLogsWidget,
105
+ priority: 10,
106
+ },
107
+ {
108
+ id: "activity-stats",
109
+ name: "Activity Stats",
110
+ area: "dashboard-stats",
111
+ component: ActivityStatsWidget,
112
+ priority: 10,
113
+ },
114
+ ],
115
+
116
+ routes: {
117
+ public: publicRoutes,
118
+ private: privateRoutes,
119
+ api: apiRoutes,
120
+ },
121
+
122
+ navigation,
123
+
124
+ extensions: [
125
+ {
126
+ id: "user-settings-link",
127
+ targetModule: "panel",
128
+ point: "app-user:extra-link",
129
+ component: UserDropdownLink,
130
+ priority: 50,
131
+ },
132
+ ],
133
+ };
134
+
135
+ export default kryoPanelModule;
package/src/intl.d.ts ADDED
@@ -0,0 +1,9 @@
1
+ import type messages from "../locales/en/global.json";
2
+
3
+ type JsonDataType = typeof messages;
4
+
5
+ declare module "@arch-cadre/intl" {
6
+ export interface IntlMessages extends JsonDataType {}
7
+ }
8
+
9
+ export {};
@@ -0,0 +1,57 @@
1
+ import { i18n } from "@arch-cadre/intl";
2
+ import type { ModuleNavigation } from "@arch-cadre/modules";
3
+
4
+ export const navigation: ModuleNavigation = {
5
+ admin: {
6
+ [i18n("General")]: [
7
+ {
8
+ id: "dashboard",
9
+ title: i18n("Dashboard"),
10
+ url: "/",
11
+ icon: "solar:widget-bold-duotone",
12
+ },
13
+ {
14
+ id: "settings",
15
+ title: i18n("Settings"),
16
+ url: "/settings",
17
+ icon: "solar:settings-bold-duotone",
18
+ },
19
+ ],
20
+ [i18n("System")]: [
21
+ {
22
+ id: "module-manager-nav",
23
+ title: i18n("Module Manager"),
24
+ url: "/modules",
25
+ icon: "solar:settings-bold-duotone",
26
+ // roles: ["admin"],
27
+ permissions: ["system:modules"],
28
+ },
29
+ {
30
+ id: "activity-logs-nav",
31
+ title: i18n("Activity Logs"),
32
+ url: "/activity-logs",
33
+ icon: "solar:condicioner-2-bold-duotone",
34
+ // roles: ["admin"],
35
+ permissions: ["system:activity-logs"],
36
+ },
37
+ {
38
+ id: "rbac-admin-nav",
39
+ title: i18n("Roles & Permissions"),
40
+ url: "/rbac",
41
+ icon: "solar:shield-keyhole-bold-duotone",
42
+ // roles: ["admin"],
43
+ permissions: ["system:rbac"],
44
+ },
45
+ ],
46
+ },
47
+ settings: {
48
+ [i18n("Panel Core")]: [
49
+ {
50
+ id: "kryo-config",
51
+ title: i18n("Panel Core Settings"),
52
+ url: "/settings/panel/config",
53
+ icon: "solar:tuning-bold-duotone",
54
+ },
55
+ ],
56
+ },
57
+ };