@arch-cadre/panel 0.0.1 → 1.0.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.
Files changed (63) hide show
  1. package/dist/actions/activity-log/index.cjs +19 -0
  2. package/dist/actions/activity-log/index.d.ts +1 -0
  3. package/dist/actions/activity-log/index.mjs +10 -0
  4. package/dist/actions/rbac/index.cjs +130 -0
  5. package/dist/actions/rbac/index.d.ts +35 -0
  6. package/dist/actions/rbac/index.mjs +92 -0
  7. package/dist/actions/session-manager/index.cjs +99 -0
  8. package/dist/actions/session-manager/index.d.ts +21 -0
  9. package/dist/actions/session-manager/index.mjs +73 -0
  10. package/dist/index.cjs +68 -1
  11. package/dist/index.d.ts +6 -0
  12. package/dist/index.mjs +90 -0
  13. package/dist/navigation.cjs +15 -1
  14. package/dist/navigation.mjs +17 -1
  15. package/dist/routes.cjs +31 -6
  16. package/dist/routes.mjs +34 -6
  17. package/dist/schema/activity-log.cjs +23 -0
  18. package/dist/schema/activity-log.d.ts +103 -0
  19. package/dist/schema/activity-log.mjs +15 -0
  20. package/dist/schema.cjs +16 -0
  21. package/dist/schema.d.ts +1 -0
  22. package/dist/schema.mjs +1 -0
  23. package/dist/ui/activity-log/components/ActivityStatsWidget.cjs +35 -0
  24. package/dist/ui/activity-log/components/ActivityStatsWidget.d.ts +2 -0
  25. package/dist/ui/activity-log/components/ActivityStatsWidget.mjs +15 -0
  26. package/dist/ui/activity-log/components/RecentLogsWidget.cjs +50 -0
  27. package/dist/ui/activity-log/components/RecentLogsWidget.d.ts +2 -0
  28. package/dist/ui/activity-log/components/RecentLogsWidget.mjs +29 -0
  29. package/dist/ui/activity-log/pages/log-list.cjs +61 -0
  30. package/dist/ui/activity-log/pages/log-list.d.ts +2 -0
  31. package/dist/ui/activity-log/pages/log-list.mjs +36 -0
  32. package/dist/ui/components/app-user.cjs +4 -5
  33. package/dist/ui/components/app-user.mjs +5 -7
  34. package/dist/ui/components/profile/components.cjs +4 -1
  35. package/dist/ui/components/profile/components.d.ts +4 -3
  36. package/dist/ui/components/profile/components.mjs +1 -0
  37. package/dist/ui/components/profile/link.cjs +10 -1
  38. package/dist/ui/components/profile/link.d.ts +2 -1
  39. package/dist/ui/components/profile/link.mjs +2 -1
  40. package/dist/ui/components/profile/page.cjs +4 -1
  41. package/dist/ui/components/profile/page.d.ts +2 -1
  42. package/dist/ui/components/profile/page.mjs +9 -2
  43. package/dist/ui/dashboard/page.cjs +6 -16
  44. package/dist/ui/dashboard/page.d.ts +1 -1
  45. package/dist/ui/dashboard/page.mjs +14 -6
  46. package/dist/ui/dashboard/widgets/WelcomeBackUserWidget.cjs +43 -0
  47. package/dist/ui/dashboard/widgets/WelcomeBackUserWidget.d.ts +2 -0
  48. package/dist/ui/dashboard/widgets/WelcomeBackUserWidget.mjs +18 -0
  49. package/dist/ui/rbac/pages/rbac-admin.cjs +328 -0
  50. package/dist/ui/rbac/pages/rbac-admin.d.ts +2 -0
  51. package/dist/ui/rbac/pages/rbac-admin.mjs +375 -0
  52. package/dist/ui/session-manager/components/sessions-list.cjs +160 -0
  53. package/dist/ui/session-manager/components/sessions-list.d.ts +13 -0
  54. package/dist/ui/session-manager/components/sessions-list.mjs +193 -0
  55. package/dist/ui/session-manager/pages/sessions-page.cjs +29 -0
  56. package/dist/ui/session-manager/pages/sessions-page.d.ts +2 -0
  57. package/dist/ui/session-manager/pages/sessions-page.mjs +9 -0
  58. package/locales/en/global.json +109 -1
  59. package/locales/pl/global.json +188 -0
  60. package/package.json +7 -6
  61. package/dist/ui/[...catchAll]/page.cjs +0 -127
  62. package/dist/ui/[...catchAll]/page.d.ts +0 -13
  63. package/dist/ui/[...catchAll]/page.mjs +0 -88
@@ -0,0 +1,375 @@
1
+ "use client";
2
+ import { useTranslation } from "@arch-cadre/intl";
3
+ import { Icon, toast } from "@arch-cadre/ui";
4
+ import { Button } from "@arch-cadre/ui/components/button";
5
+ import {
6
+ Card,
7
+ CardContent,
8
+ CardDescription,
9
+ CardHeader,
10
+ CardTitle
11
+ } from "@arch-cadre/ui/components/card";
12
+ import {
13
+ Dialog,
14
+ DialogContent,
15
+ DialogDescription,
16
+ DialogHeader,
17
+ DialogTitle
18
+ } from "@arch-cadre/ui/components/dialog";
19
+ import { Input } from "@arch-cadre/ui/components/input";
20
+ import {
21
+ Table,
22
+ TableBody,
23
+ TableCell,
24
+ TableRow
25
+ } from "@arch-cadre/ui/components/table";
26
+ import * as React from "react";
27
+ import { useCallback, useEffect, useState } from "react";
28
+ import {
29
+ assignPermissionToRole,
30
+ assignPermissionToUser,
31
+ assignRoleToUser,
32
+ createPermission,
33
+ createRole,
34
+ deletePermission,
35
+ deleteRole,
36
+ getPermissions,
37
+ getRolePermissions,
38
+ getRoles,
39
+ getUserRbacData,
40
+ getUsers,
41
+ revokePermissionFromRole,
42
+ revokePermissionFromUser,
43
+ revokeRoleFromUser
44
+ } from "../../../actions/rbac/index.mjs";
45
+ export default function RbacAdminPage() {
46
+ const [roles, setRoles] = useState([]);
47
+ const [permissions, setPermissions] = useState([]);
48
+ const [users, setUsers] = useState([]);
49
+ const [loading, setLoading] = useState(true);
50
+ const { t } = useTranslation();
51
+ const [newRoleName, setNewRoleName] = useState("");
52
+ const [newPermissionName, setNewPermissionName] = useState("");
53
+ const [selectedRole, setSelectedRole] = useState(null);
54
+ const [rolePermissions, setRolePermissions] = useState([]);
55
+ const [selectedUser, setSelectedUser] = useState(null);
56
+ const [userRbacData, setUserRbacData] = useState({
57
+ roles: [],
58
+ directPermissions: [],
59
+ effectivePermissions: []
60
+ });
61
+ const loadData = useCallback(async () => {
62
+ setLoading(true);
63
+ try {
64
+ const [r, p, u] = await Promise.all([
65
+ getRoles(),
66
+ getPermissions(),
67
+ getUsers()
68
+ ]);
69
+ setRoles(r);
70
+ setPermissions(p);
71
+ setUsers(u);
72
+ } catch (e) {
73
+ console.error("[RBAC] Failed to load initial data:", e);
74
+ toast.error(t("Failed to load RBAC data."));
75
+ } finally {
76
+ setLoading(false);
77
+ }
78
+ }, []);
79
+ const loadRolePermissions = useCallback(async (roleId) => {
80
+ try {
81
+ const p = await getRolePermissions(roleId);
82
+ setRolePermissions(p);
83
+ } catch (e) {
84
+ console.error(`[RBAC] Failed to load permissions for role ${roleId}:`, e);
85
+ toast.error(t("Failed to load permissions for role."));
86
+ }
87
+ }, []);
88
+ const loadUserRbacData = useCallback(async (userId) => {
89
+ try {
90
+ const data = await getUserRbacData(userId);
91
+ setUserRbacData(data);
92
+ } catch (e) {
93
+ console.error(`[RBAC] Failed to load RBAC data for user ${userId}:`, e);
94
+ toast.error(t("Failed to load user RBAC data."));
95
+ }
96
+ }, []);
97
+ useEffect(() => {
98
+ loadData();
99
+ }, [loadData]);
100
+ useEffect(() => {
101
+ if (selectedRole?.id) {
102
+ loadRolePermissions(selectedRole.id);
103
+ }
104
+ }, [selectedRole?.id, loadRolePermissions]);
105
+ useEffect(() => {
106
+ if (selectedUser?.id) {
107
+ loadUserRbacData(selectedUser.id);
108
+ }
109
+ }, [selectedUser?.id, loadUserRbacData]);
110
+ const handleCreateRole = useCallback(async () => {
111
+ if (!newRoleName) return;
112
+ try {
113
+ await createRole(newRoleName);
114
+ setNewRoleName("");
115
+ toast.success(t("Role created."));
116
+ loadData();
117
+ } catch (_e) {
118
+ toast.error(t("Failed to create role."));
119
+ }
120
+ }, [newRoleName, loadData]);
121
+ const handleCreatePermission = useCallback(async () => {
122
+ if (!newPermissionName) return;
123
+ try {
124
+ await createPermission(newPermissionName);
125
+ setNewPermissionName("");
126
+ toast.success(t("Permission created."));
127
+ loadData();
128
+ } catch (_e) {
129
+ toast.error(t("Failed to create permission."));
130
+ }
131
+ }, [newPermissionName, loadData]);
132
+ const handleDeleteRole = useCallback(
133
+ async (id) => {
134
+ try {
135
+ await deleteRole(id);
136
+ toast.success(t("Role deleted."));
137
+ if (selectedRole?.id === id) setSelectedRole(null);
138
+ loadData();
139
+ } catch (_e) {
140
+ toast.error(t("Failed to delete role."));
141
+ }
142
+ },
143
+ [selectedRole, loadData]
144
+ );
145
+ const handleDeletePermission = useCallback(
146
+ async (id) => {
147
+ try {
148
+ await deletePermission(id);
149
+ toast.success(t("Permission deleted."));
150
+ loadData();
151
+ } catch (_e) {
152
+ toast.error(t("Failed to delete permission."));
153
+ }
154
+ },
155
+ [loadData]
156
+ );
157
+ const togglePermissionForRole = useCallback(
158
+ async (permId, hasIt) => {
159
+ if (!selectedRole) return;
160
+ try {
161
+ if (hasIt) {
162
+ await revokePermissionFromRole(selectedRole.id, permId);
163
+ } else {
164
+ await assignPermissionToRole(selectedRole.id, permId);
165
+ }
166
+ loadRolePermissions(selectedRole.id);
167
+ } catch (_e) {
168
+ toast.error(t("Action failed."));
169
+ }
170
+ },
171
+ [selectedRole, loadRolePermissions]
172
+ );
173
+ const toggleRoleForUser = useCallback(
174
+ async (roleId, hasIt) => {
175
+ if (!selectedUser) return;
176
+ try {
177
+ if (hasIt) {
178
+ await revokeRoleFromUser(selectedUser.id, roleId);
179
+ } else {
180
+ await assignRoleToUser(selectedUser.id, roleId);
181
+ }
182
+ loadUserRbacData(selectedUser.id);
183
+ } catch (_e) {
184
+ toast.error(t("Action failed."));
185
+ }
186
+ },
187
+ [selectedUser, loadUserRbacData]
188
+ );
189
+ const togglePermissionForUser = useCallback(
190
+ async (permId, hasIt) => {
191
+ if (!selectedUser) return;
192
+ try {
193
+ if (hasIt) {
194
+ await revokePermissionFromUser(selectedUser.id, permId);
195
+ } else {
196
+ await assignPermissionToUser(selectedUser.id, permId);
197
+ }
198
+ loadUserRbacData(selectedUser.id);
199
+ } catch (_e) {
200
+ toast.error(t("Action failed."));
201
+ }
202
+ },
203
+ [selectedUser, loadUserRbacData]
204
+ );
205
+ return /* @__PURE__ */ React.createElement("div", { className: "space-y-6" }, /* @__PURE__ */ React.createElement("div", { className: "space-y-1" }, /* @__PURE__ */ React.createElement("h1", { className: "text-3xl font-black tracking-tight flex items-center gap-2" }, t("Role-Based Access Control Manager")), /* @__PURE__ */ React.createElement("p", { className: "text-muted-foreground" }, t("Manage roles, permissions, and their relationships."))), /* @__PURE__ */ React.createElement("div", { className: "grid gap-6 lg:grid-cols-3" }, /* @__PURE__ */ React.createElement(Card, { className: "border-primary/5 shadow-sm" }, /* @__PURE__ */ React.createElement(CardHeader, null, /* @__PURE__ */ React.createElement(CardTitle, null, t("Roles")), /* @__PURE__ */ React.createElement(CardDescription, null, t("System roles."))), /* @__PURE__ */ React.createElement(CardContent, { className: "space-y-4" }, /* @__PURE__ */ React.createElement("div", { className: "flex gap-2" }, /* @__PURE__ */ React.createElement(
206
+ Input,
207
+ {
208
+ placeholder: t("New role..."),
209
+ value: newRoleName,
210
+ onChange: (e) => setNewRoleName(e.target.value)
211
+ }
212
+ ), /* @__PURE__ */ React.createElement(Button, { onClick: handleCreateRole }, t("Add"))), /* @__PURE__ */ React.createElement("div", { className: "border rounded-md max-h-[300px] overflow-auto" }, /* @__PURE__ */ React.createElement(Table, null, /* @__PURE__ */ React.createElement(TableBody, null, roles.map((role) => /* @__PURE__ */ React.createElement(
213
+ TableRow,
214
+ {
215
+ key: role.id,
216
+ className: selectedRole?.id === role.id ? "bg-primary/5" : ""
217
+ },
218
+ /* @__PURE__ */ React.createElement(
219
+ TableCell,
220
+ {
221
+ className: "font-medium cursor-pointer",
222
+ onClick: () => setSelectedRole(role)
223
+ },
224
+ role.name
225
+ ),
226
+ /* @__PURE__ */ React.createElement(TableCell, { className: "text-right" }, /* @__PURE__ */ React.createElement(
227
+ Button,
228
+ {
229
+ variant: "ghost",
230
+ size: "icon",
231
+ onClick: () => handleDeleteRole(role.id)
232
+ },
233
+ /* @__PURE__ */ React.createElement(
234
+ Icon,
235
+ {
236
+ icon: "solar:trash-bin-trash-bold",
237
+ className: "size-4 text-destructive"
238
+ }
239
+ )
240
+ ))
241
+ ))))))), /* @__PURE__ */ React.createElement(Card, { className: "border-primary/5 shadow-sm" }, /* @__PURE__ */ React.createElement(CardHeader, null, /* @__PURE__ */ React.createElement(CardTitle, null, t("Permissions")), /* @__PURE__ */ React.createElement(CardDescription, null, t("System permissions."))), /* @__PURE__ */ React.createElement(CardContent, { className: "space-y-4" }, /* @__PURE__ */ React.createElement("div", { className: "flex gap-2" }, /* @__PURE__ */ React.createElement(
242
+ Input,
243
+ {
244
+ placeholder: t("New permission..."),
245
+ value: newPermissionName,
246
+ onChange: (e) => setNewPermissionName(e.target.value)
247
+ }
248
+ ), /* @__PURE__ */ React.createElement(Button, { onClick: handleCreatePermission }, t("Add"))), /* @__PURE__ */ React.createElement("div", { className: "border rounded-md max-h-[300px] overflow-auto" }, /* @__PURE__ */ React.createElement(Table, null, /* @__PURE__ */ React.createElement(TableBody, null, permissions.map((perm) => /* @__PURE__ */ React.createElement(TableRow, { key: perm.id }, /* @__PURE__ */ React.createElement(TableCell, { className: "font-medium" }, perm.name), /* @__PURE__ */ React.createElement(TableCell, { className: "text-right" }, /* @__PURE__ */ React.createElement(
249
+ Button,
250
+ {
251
+ variant: "ghost",
252
+ size: "icon",
253
+ onClick: () => handleDeletePermission(perm.id)
254
+ },
255
+ /* @__PURE__ */ React.createElement(
256
+ Icon,
257
+ {
258
+ icon: "solar:trash-bin-trash-bold",
259
+ className: "size-4 text-destructive"
260
+ }
261
+ )
262
+ ))))))))), /* @__PURE__ */ React.createElement(Card, { className: "border-primary/5 shadow-sm" }, /* @__PURE__ */ React.createElement(CardHeader, null, /* @__PURE__ */ React.createElement(CardTitle, null, t("Users")), /* @__PURE__ */ React.createElement(CardDescription, null, t("Manage user roles."))), /* @__PURE__ */ React.createElement(CardContent, { className: "space-y-4" }, /* @__PURE__ */ React.createElement("div", { className: "border rounded-md max-h-[358px] overflow-auto" }, /* @__PURE__ */ React.createElement(Table, null, /* @__PURE__ */ React.createElement(TableBody, null, users.map((user) => /* @__PURE__ */ React.createElement(
263
+ TableRow,
264
+ {
265
+ key: user.id,
266
+ className: selectedUser?.id === user.id ? "bg-primary/5" : ""
267
+ },
268
+ /* @__PURE__ */ React.createElement(
269
+ TableCell,
270
+ {
271
+ className: "font-medium cursor-pointer",
272
+ onClick: () => setSelectedUser(user)
273
+ },
274
+ /* @__PURE__ */ React.createElement("div", null, user.name),
275
+ /* @__PURE__ */ React.createElement("div", { className: "text-[10px] text-muted-foreground" }, user.email)
276
+ )
277
+ )))))))), /* @__PURE__ */ React.createElement(
278
+ Dialog,
279
+ {
280
+ open: !!selectedRole,
281
+ onOpenChange: (open) => !open && setSelectedRole(null)
282
+ },
283
+ /* @__PURE__ */ React.createElement(DialogContent, null, /* @__PURE__ */ React.createElement(DialogHeader, null, /* @__PURE__ */ React.createElement(DialogTitle, null, t("Edit Role: {roleName}", { roleName: selectedRole?.name })), /* @__PURE__ */ React.createElement(DialogDescription, null, t("Manage permissions for role {roleName}", {
284
+ roleName: selectedRole?.name
285
+ })), /* @__PURE__ */ React.createElement("div", { className: "grid grid-cols-1 sm:grid-cols-2 gap-2" }, permissions.map((perm) => {
286
+ const hasIt = rolePermissions.some((rp) => rp.id === perm.id);
287
+ return /* @__PURE__ */ React.createElement(
288
+ Button,
289
+ {
290
+ key: perm.id,
291
+ variant: hasIt ? "default" : "outline",
292
+ size: "sm",
293
+ className: "justify-start gap-2 h-auto py-2 px-3 overflow-hidden",
294
+ onClick: () => togglePermissionForRole(perm.id, hasIt)
295
+ },
296
+ /* @__PURE__ */ React.createElement(
297
+ Icon,
298
+ {
299
+ icon: hasIt ? "solar:check-circle-bold" : "solar:circle-linear",
300
+ className: hasIt ? "text-primary-foreground" : "text-muted-foreground"
301
+ }
302
+ ),
303
+ /* @__PURE__ */ React.createElement("span", { className: "truncate text-xs" }, perm.name)
304
+ );
305
+ }))))
306
+ ), /* @__PURE__ */ React.createElement(
307
+ Dialog,
308
+ {
309
+ open: !!selectedUser,
310
+ onOpenChange: (open) => !open && setSelectedUser(null)
311
+ },
312
+ /* @__PURE__ */ React.createElement(DialogContent, null, /* @__PURE__ */ React.createElement(DialogHeader, null, /* @__PURE__ */ React.createElement(DialogTitle, null, t("Management for {selectedUser}", {
313
+ selectedUser: selectedUser?.name
314
+ })), /* @__PURE__ */ React.createElement(DialogDescription, null, t("Manage roles and permissions for {selectedUser}", {
315
+ selectedUser: selectedUser?.name
316
+ })), /* @__PURE__ */ React.createElement("div", { className: "space-y-6" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("p", { className: "text-[10px] font-black uppercase text-muted-foreground tracking-widest mb-3" }, t("User Roles")), /* @__PURE__ */ React.createElement("div", { className: "grid grid-cols-1 sm:grid-cols-2 gap-2" }, roles.map((role) => {
317
+ const hasIt = userRbacData.roles.some(
318
+ (ur) => ur.id === role.id
319
+ );
320
+ return /* @__PURE__ */ React.createElement(
321
+ Button,
322
+ {
323
+ key: role.id,
324
+ variant: hasIt ? "default" : "outline",
325
+ size: "sm",
326
+ className: "justify-start gap-2 h-auto py-2 px-3 overflow-hidden",
327
+ onClick: () => toggleRoleForUser(role.id, hasIt)
328
+ },
329
+ /* @__PURE__ */ React.createElement(
330
+ Icon,
331
+ {
332
+ icon: hasIt ? "solar:check-circle-bold" : "solar:circle-linear",
333
+ className: hasIt ? "text-primary-foreground" : "text-muted-foreground"
334
+ }
335
+ ),
336
+ /* @__PURE__ */ React.createElement("span", { className: "truncate text-xs" }, role.name)
337
+ );
338
+ }))), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("p", { className: "text-[10px] font-black uppercase text-muted-foreground tracking-widest mb-3" }, t("Direct Permissions (User-specific)")), /* @__PURE__ */ React.createElement("div", { className: "grid grid-cols-1 sm:grid-cols-2 gap-2" }, permissions.map((perm) => {
339
+ const isDirect = userRbacData.directPermissions.some(
340
+ (up) => up.id === perm.id
341
+ );
342
+ return /* @__PURE__ */ React.createElement(
343
+ Button,
344
+ {
345
+ key: perm.id,
346
+ variant: isDirect ? "default" : "outline",
347
+ size: "sm",
348
+ className: "justify-start gap-2 h-auto py-2 px-3 overflow-hidden",
349
+ onClick: () => togglePermissionForUser(perm.id, isDirect)
350
+ },
351
+ /* @__PURE__ */ React.createElement(
352
+ Icon,
353
+ {
354
+ icon: isDirect ? "solar:check-circle-bold" : "solar:circle-linear",
355
+ className: isDirect ? "text-primary-foreground" : "text-muted-foreground"
356
+ }
357
+ ),
358
+ /* @__PURE__ */ React.createElement("span", { className: "truncate text-xs" }, perm.name)
359
+ );
360
+ }))), /* @__PURE__ */ React.createElement("div", { className: "pt-4 border-t" }, /* @__PURE__ */ React.createElement("p", { className: "text-[10px] font-black uppercase text-muted-foreground tracking-widest mb-2" }, t("Effective Permissions (inherited + direct)")), /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap gap-1" }, userRbacData.effectivePermissions.map((p) => /* @__PURE__ */ React.createElement(
361
+ "div",
362
+ {
363
+ key: p.id,
364
+ className: "bg-muted px-2 py-0.5 rounded text-[9px] font-mono"
365
+ },
366
+ p.name
367
+ )), userRbacData.effectivePermissions.length === 0 && /* @__PURE__ */ React.createElement("span", { className: "text-[10px] italic" }, t("No permissions")))))))
368
+ ), loading && /* @__PURE__ */ React.createElement("div", { className: "fixed inset-0 bg-background/50 backdrop-blur-sm flex items-center justify-center z-50" }, /* @__PURE__ */ React.createElement(
369
+ Icon,
370
+ {
371
+ icon: "svg-spinners:18-dots-indicator",
372
+ className: "size-12 text-primary"
373
+ }
374
+ )));
375
+ }
@@ -0,0 +1,160 @@
1
+ "use strict";
2
+ "use client";
3
+
4
+ Object.defineProperty(exports, "__esModule", {
5
+ value: true
6
+ });
7
+ exports.SessionsList = SessionsList;
8
+ var _react = _interopRequireWildcard(require("react"));
9
+ var React = _react;
10
+ var _intl = require("@arch-cadre/intl");
11
+ var _ui = require("@arch-cadre/ui");
12
+ var _alertDialog = require("@arch-cadre/ui/components/alert-dialog");
13
+ var _badge = require("@arch-cadre/ui/components/badge");
14
+ var _button = require("@arch-cadre/ui/components/button");
15
+ var _navigation = require("next/navigation");
16
+ var _sessionManager = require("../../../actions/session-manager/index.cjs");
17
+ function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
18
+ function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
19
+ const CORE_SESSION_KEYS = ["id", "createdAt", "expiresAt", "isCurrent", "userId"];
20
+ function SessionsList({
21
+ sessions
22
+ }) {
23
+ const router = (0, _navigation.useRouter)();
24
+ const [isPending, startTransition] = (0, _react.useTransition)();
25
+ const [revokingSessionId, setRevokingSessionId] = (0, _react.useState)(null);
26
+ const {
27
+ t
28
+ } = (0, _intl.useTranslation)();
29
+ const renderAugmentations = session => {
30
+ return Object.entries(session).filter(([key]) => !CORE_SESSION_KEYS.includes(key)).map(([key, value]) => {
31
+ if (key === "twoFactorVerified" || key === "two_factor_verified") {
32
+ return /* @__PURE__ */React.createElement(_badge.Badge, {
33
+ key,
34
+ variant: "outline",
35
+ className: (0, _ui.cn)(value ? "text-[10px] px-1.5 text-green-600 border-green-200" : "text-[10px] px-1.5 text-red-600 border-red-200")
36
+ }, /* @__PURE__ */React.createElement(_ui.Icon, {
37
+ icon: "solar:shield-check-broken",
38
+ className: "h-2.5 w-2.5 mr-0.5"
39
+ }), t("2FA"));
40
+ }
41
+ if (typeof value === "boolean" && value === false) return null;
42
+ return /* @__PURE__ */React.createElement(_badge.Badge, {
43
+ key,
44
+ variant: "outline",
45
+ className: "text-[10px] px-1.5 text-muted-foreground uppercase font-black tracking-widest"
46
+ }, t(key), typeof value !== "boolean" && `: ${value}`);
47
+ });
48
+ };
49
+ const handleRevokeSession = sessionId => {
50
+ setRevokingSessionId(sessionId);
51
+ startTransition(async () => {
52
+ const result = await (0, _sessionManager.revokeSessionAction)(sessionId);
53
+ if (result.error) {
54
+ _ui.toast.error(result.error);
55
+ } else {
56
+ _ui.toast.success(t("Session revoked successfully"));
57
+ router.refresh();
58
+ }
59
+ setRevokingSessionId(null);
60
+ });
61
+ };
62
+ const handleRevokeAllOther = () => {
63
+ startTransition(async () => {
64
+ const result = await (0, _sessionManager.revokeAllOtherSessionsAction)();
65
+ if (result.error) {
66
+ _ui.toast.error(result.error);
67
+ } else {
68
+ _ui.toast.success(t("All other sessions revoked successfully"));
69
+ router.refresh();
70
+ }
71
+ });
72
+ };
73
+ const isExpiringSoon = expiresAt => {
74
+ const now = /* @__PURE__ */new Date();
75
+ const diffDays = (expiresAt.getTime() - now.getTime()) / (1e3 * 60 * 60 * 24);
76
+ return diffDays < 7;
77
+ };
78
+ const otherSessions = sessions.filter(s => !s.isCurrent);
79
+ return /* @__PURE__ */React.createElement("div", {
80
+ className: "space-y-4"
81
+ }, otherSessions.length > 0 && /* @__PURE__ */React.createElement("div", {
82
+ className: "flex justify-end"
83
+ }, /* @__PURE__ */React.createElement(_alertDialog.AlertDialog, null, /* @__PURE__ */React.createElement(_alertDialog.AlertDialogTrigger, {
84
+ asChild: true
85
+ }, /* @__PURE__ */React.createElement(_button.Button, {
86
+ variant: "outline",
87
+ size: "sm",
88
+ disabled: isPending
89
+ }, /* @__PURE__ */React.createElement(_ui.Icon, {
90
+ icon: "solar:logout-2-broken",
91
+ className: "h-4 w-4 mr-2"
92
+ }), t("Revoke All Other Sessions"))), /* @__PURE__ */React.createElement(_alertDialog.AlertDialogContent, null, /* @__PURE__ */React.createElement(_alertDialog.AlertDialogHeader, null, /* @__PURE__ */React.createElement(_alertDialog.AlertDialogTitle, null, t("Revoke All Other Sessions?")), /* @__PURE__ */React.createElement(_alertDialog.AlertDialogDescription, null, t("This will sign out all other devices. You will remain signed in on this device. This action cannot be undone."))), /* @__PURE__ */React.createElement(_alertDialog.AlertDialogFooter, null, /* @__PURE__ */React.createElement(_alertDialog.AlertDialogCancel, null, t("Cancel")), /* @__PURE__ */React.createElement(_alertDialog.AlertDialogAction, {
93
+ onClick: handleRevokeAllOther,
94
+ disabled: isPending,
95
+ className: "bg-destructive text-destructive-foreground hover:bg-destructive/90"
96
+ }, isPending ? t("Revoking...") : t("Revoke All")))))), /* @__PURE__ */React.createElement("div", {
97
+ className: "space-y-3"
98
+ }, sessions.map(session => /* @__PURE__ */React.createElement("div", {
99
+ key: session.id,
100
+ className: (0, _ui.cn)("flex items-center justify-between p-4 rounded-lg border bg-card", session.isCurrent && "ring-2 ring-primary")
101
+ }, /* @__PURE__ */React.createElement("div", {
102
+ className: "flex items-center gap-4"
103
+ }, /* @__PURE__ */React.createElement("div", {
104
+ className: (0, _ui.cn)("flex h-10 w-10 items-center justify-center rounded-full", session.isCurrent ? "bg-primary/10 text-primary" : "bg-muted text-muted-foreground")
105
+ }, /* @__PURE__ */React.createElement(_ui.Icon, {
106
+ icon: "solar:monitor-broken",
107
+ className: "h-5 w-5"
108
+ })), /* @__PURE__ */React.createElement("div", {
109
+ className: "space-y-1"
110
+ }, /* @__PURE__ */React.createElement("div", {
111
+ className: "flex items-center gap-2"
112
+ }, /* @__PURE__ */React.createElement("span", {
113
+ className: "font-medium text-sm"
114
+ }, session.isCurrent ? t("Current Session") : t("Session")), session.isCurrent && /* @__PURE__ */React.createElement(_badge.Badge, {
115
+ variant: "secondary",
116
+ className: "text-[10px] px-1.5"
117
+ }, t("This device")), renderAugmentations(session)), /* @__PURE__ */React.createElement("div", {
118
+ className: "flex items-center gap-3 text-xs text-muted-foreground"
119
+ }, /* @__PURE__ */React.createElement("span", {
120
+ className: "flex items-center gap-1"
121
+ }, /* @__PURE__ */React.createElement(_ui.Icon, {
122
+ icon: "solar:clock-circle-broken",
123
+ className: "h-3 w-3"
124
+ }), t("Started {{ time }}", {
125
+ time: session.createdAt
126
+ })), /* @__PURE__ */React.createElement("span", {
127
+ className: "flex items-center gap-1"
128
+ }, /* @__PURE__ */React.createElement(_ui.Icon, {
129
+ icon: "solar:hourglass-line-broken",
130
+ className: (0, _ui.cn)("h-3 w-3", isExpiringSoon(session.expiresAt) && "text-orange-500")
131
+ }), t("Expires {{ time }}", {
132
+ time: session.expiresAt
133
+ }))))), !session.isCurrent && /* @__PURE__ */React.createElement(_alertDialog.AlertDialog, null, /* @__PURE__ */React.createElement(_alertDialog.AlertDialogTrigger, {
134
+ asChild: true
135
+ }, /* @__PURE__ */React.createElement(_button.Button, {
136
+ variant: "ghost",
137
+ size: "sm",
138
+ className: "text-muted-foreground hover:text-destructive",
139
+ disabled: isPending && revokingSessionId === session.id
140
+ }, isPending && revokingSessionId === session.id ? /* @__PURE__ */React.createElement(_ui.Icon, {
141
+ icon: "solar:refresh-broken",
142
+ className: "h-4 w-4 animate-spin"
143
+ }) : /* @__PURE__ */React.createElement(React.Fragment, null, /* @__PURE__ */React.createElement(_ui.Icon, {
144
+ icon: "solar:logout-2-broken",
145
+ className: "h-4 w-4 mr-1"
146
+ }), t("Revoke")))), /* @__PURE__ */React.createElement(_alertDialog.AlertDialogContent, null, /* @__PURE__ */React.createElement(_alertDialog.AlertDialogHeader, null, /* @__PURE__ */React.createElement(_alertDialog.AlertDialogTitle, null, t("Revoke Session?")), /* @__PURE__ */React.createElement(_alertDialog.AlertDialogDescription, null, t("This will sign out this device immediately. The session was started {{ time }}.", {
147
+ time: session.createdAt
148
+ }))), /* @__PURE__ */React.createElement(_alertDialog.AlertDialogFooter, null, /* @__PURE__ */React.createElement(_alertDialog.AlertDialogCancel, null, t("Cancel")), /* @__PURE__ */React.createElement(_alertDialog.AlertDialogAction, {
149
+ onClick: () => handleRevokeSession(session.id),
150
+ disabled: isPending,
151
+ className: "bg-destructive text-destructive-foreground hover:bg-destructive/90"
152
+ }, isPending ? t("Revoking...") : t("Revoke")))))))), sessions.length === 0 && /* @__PURE__ */React.createElement("div", {
153
+ className: "text-center py-8 text-muted-foreground"
154
+ }, /* @__PURE__ */React.createElement(_ui.Icon, {
155
+ icon: "solar:devices-broken",
156
+ className: "h-12 w-12 mx-auto mb-3 opacity-50"
157
+ }), /* @__PURE__ */React.createElement("p", null, t("No active sessions found"))), sessions.length === 1 && sessions[0].isCurrent && /* @__PURE__ */React.createElement("p", {
158
+ className: "text-sm text-muted-foreground text-center py-4"
159
+ }, t("You only have one active session (this device).")));
160
+ }
@@ -0,0 +1,13 @@
1
+ import * as React from "react";
2
+ type Session = {
3
+ id: string;
4
+ createdAt: Date;
5
+ expiresAt: Date;
6
+ isCurrent: boolean;
7
+ [key: string]: any;
8
+ };
9
+ interface SessionsListProps {
10
+ sessions: Session[];
11
+ }
12
+ export declare function SessionsList({ sessions }: SessionsListProps): React.JSX.Element;
13
+ export {};