@hed-hog/core 0.0.185 → 0.0.190

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 (55) hide show
  1. package/hedhog/frontend/app/account/2fa/page.tsx.ejs +5 -0
  2. package/hedhog/frontend/app/account/accounts/page.tsx.ejs +5 -0
  3. package/hedhog/frontend/app/account/components/active-sessions.tsx.ejs +356 -0
  4. package/hedhog/frontend/app/account/components/change-email-form.tsx.ejs +379 -0
  5. package/hedhog/frontend/app/account/components/change-password-form.tsx.ejs +184 -0
  6. package/hedhog/frontend/app/account/components/connected-accounts.tsx.ejs +144 -0
  7. package/hedhog/frontend/app/account/components/email-request-dialog.tsx.ejs +96 -0
  8. package/hedhog/frontend/app/account/components/mfa-add-buttons.tsx.ejs +43 -0
  9. package/hedhog/frontend/app/account/components/mfa-method-card.tsx.ejs +115 -0
  10. package/hedhog/frontend/app/account/components/mfa-setup-dialog.tsx.ejs +236 -0
  11. package/hedhog/frontend/app/account/components/profile-form.tsx.ejs +209 -0
  12. package/hedhog/frontend/app/account/components/recovery-codes-dialog.tsx.ejs +192 -0
  13. package/hedhog/frontend/app/account/components/regenerate-codes-dialog.tsx.ejs +372 -0
  14. package/hedhog/frontend/app/account/components/remove-mfa-dialog.tsx.ejs +337 -0
  15. package/hedhog/frontend/app/account/components/two-factor-auth.tsx.ejs +393 -0
  16. package/hedhog/frontend/app/account/components/verify-before-add-dialog.tsx.ejs +332 -0
  17. package/hedhog/frontend/app/account/email/page.tsx.ejs +5 -0
  18. package/hedhog/frontend/app/account/hooks/use-mfa-methods.ts.ejs +27 -0
  19. package/hedhog/frontend/app/account/hooks/use-mfa-setup.ts.ejs +461 -0
  20. package/hedhog/frontend/app/account/layout.tsx.ejs +105 -0
  21. package/hedhog/frontend/app/account/lib/mfa-utils.tsx.ejs +37 -0
  22. package/hedhog/frontend/app/account/page.tsx.ejs +5 -0
  23. package/hedhog/frontend/app/account/password/page.tsx.ejs +5 -0
  24. package/hedhog/frontend/app/account/profile/page.tsx.ejs +5 -0
  25. package/hedhog/frontend/app/account/sessions/page.tsx.ejs +5 -0
  26. package/hedhog/frontend/app/configurations/[slug]/components/setting-field.tsx.ejs +490 -0
  27. package/hedhog/frontend/app/configurations/[slug]/page.tsx.ejs +62 -0
  28. package/hedhog/frontend/app/configurations/layout.tsx.ejs +316 -0
  29. package/hedhog/frontend/app/configurations/page.tsx.ejs +35 -0
  30. package/hedhog/frontend/app/dashboard/[slug]/dashboard-content.tsx.ejs +351 -0
  31. package/hedhog/frontend/app/dashboard/[slug]/page.tsx.ejs +11 -0
  32. package/hedhog/frontend/app/dashboard/[slug]/types.ts.ejs +62 -0
  33. package/hedhog/frontend/app/dashboard/[slug]/widget-renderer.tsx.ejs +45 -0
  34. package/hedhog/frontend/app/dashboard/dashboard.css.ejs +196 -0
  35. package/hedhog/frontend/app/dashboard/management/page.tsx.ejs +63 -0
  36. package/hedhog/frontend/app/dashboard/management/tabs/component-roles-tab.tsx.ejs +516 -0
  37. package/hedhog/frontend/app/dashboard/management/tabs/components-tab.tsx.ejs +753 -0
  38. package/hedhog/frontend/app/dashboard/management/tabs/dashboard-roles-tab.tsx.ejs +516 -0
  39. package/hedhog/frontend/app/dashboard/management/tabs/dashboards-tab.tsx.ejs +489 -0
  40. package/hedhog/frontend/app/dashboard/management/tabs/items-tab.tsx.ejs +621 -0
  41. package/hedhog/frontend/app/dashboard/page.tsx.ejs +14 -0
  42. package/hedhog/frontend/app/mail/log/page.tsx.ejs +312 -0
  43. package/hedhog/frontend/app/mail/template/page.tsx.ejs +1177 -0
  44. package/hedhog/frontend/app/preferences/page.tsx.ejs +448 -0
  45. package/hedhog/frontend/app/roles/menus.tsx.ejs +504 -0
  46. package/hedhog/frontend/app/roles/page.tsx.ejs +814 -0
  47. package/hedhog/frontend/app/roles/routes.tsx.ejs +397 -0
  48. package/hedhog/frontend/app/roles/users.tsx.ejs +306 -0
  49. package/hedhog/frontend/app/users/active-session.tsx.ejs +159 -0
  50. package/hedhog/frontend/app/users/identifiers.tsx.ejs +279 -0
  51. package/hedhog/frontend/app/users/page.tsx.ejs +1257 -0
  52. package/hedhog/frontend/app/users/permissions.tsx.ejs +155 -0
  53. package/hedhog/frontend/messages/en.json +1080 -0
  54. package/hedhog/frontend/messages/pt.json +1135 -0
  55. package/package.json +4 -4
@@ -0,0 +1,5 @@
1
+ import { TwoFactorAuth } from '../components/two-factor-auth';
2
+
3
+ export default function TwoFactorPage() {
4
+ return <TwoFactorAuth />;
5
+ }
@@ -0,0 +1,5 @@
1
+ import { ConnectedAccounts } from '../components/connected-accounts';
2
+
3
+ export default function AccountsPage() {
4
+ return <ConnectedAccounts />;
5
+ }
@@ -0,0 +1,356 @@
1
+ 'use client';
2
+
3
+ import { PaginationFooter } from '@/components/entity-list/pagination-footer';
4
+ import {
5
+ AlertDialog,
6
+ AlertDialogAction,
7
+ AlertDialogCancel,
8
+ AlertDialogContent,
9
+ AlertDialogDescription,
10
+ AlertDialogFooter,
11
+ AlertDialogHeader,
12
+ AlertDialogTitle,
13
+ } from '@/components/ui/alert-dialog';
14
+ import { Badge } from '@/components/ui/badge';
15
+ import { Button } from '@/components/ui/button';
16
+ import {
17
+ Card,
18
+ CardContent,
19
+ CardDescription,
20
+ CardHeader,
21
+ CardTitle,
22
+ } from '@/components/ui/card';
23
+ import {
24
+ Table,
25
+ TableBody,
26
+ TableCell,
27
+ TableHead,
28
+ TableHeader,
29
+ TableRow,
30
+ } from '@/components/ui/table';
31
+ import { usePagination } from '@/hooks/use-pagination';
32
+ import { formatDate, formatDateTime } from '@/lib/format-date';
33
+ import { parseBrowser, parseDeviceType, parseOS } from '@/lib/session';
34
+ import { UserSession } from '@hed-hog/api-types';
35
+ import { useApp } from '@hed-hog/next-app-provider';
36
+ import { Calendar, Clock, LogOut, MapPin } from 'lucide-react';
37
+ import { useTranslations } from 'next-intl';
38
+ import { useState } from 'react';
39
+ import { toast } from 'sonner';
40
+
41
+ export function ActiveSessions() {
42
+ const { request, logout, currentLocaleCode, getSettingValue } = useApp();
43
+ const t = useTranslations('core.ActiveSessions');
44
+ const [sessionToRevoke, setSessionToRevoke] = useState<number | null>(null);
45
+ const [revokeAllSessions, setRevokeAllSessions] = useState(false);
46
+ const [revokeAllOtherSessions, setRevokeAllOtherSessions] = useState(false);
47
+
48
+ const {
49
+ items: sessions,
50
+ refetch,
51
+ page,
52
+ setPage,
53
+ totalItems,
54
+ pageSize,
55
+ setPageSize,
56
+ } = usePagination({
57
+ url: `/sessions/user`,
58
+ });
59
+
60
+ const handleRevokeSession = async (sessionId: number) => {
61
+ try {
62
+ await request({ url: `/sessions/${sessionId}/revoke`, method: 'DELETE' });
63
+ await refetch();
64
+ setSessionToRevoke(null);
65
+ toast.success(t('revokeSuccess'));
66
+ } catch (error) {
67
+ toast.error(t('revokeFailure'));
68
+ }
69
+ };
70
+
71
+ const handleRevokeAllOtherSessions = async () => {
72
+ try {
73
+ await request({ url: '/sessions/revoke-all-other', method: 'DELETE' });
74
+ await refetch();
75
+ toast.success(t('revokeAllSuccess'));
76
+ } catch (error) {
77
+ toast.error(t('revokeAllFailure'));
78
+ }
79
+ };
80
+
81
+ const handleRevokeAllSessions = async () => {
82
+ try {
83
+ await request({ url: '/sessions/revoke-all', method: 'DELETE' });
84
+ await logout();
85
+ toast.success(t('revokeAllSuccess'));
86
+ } catch (error) {
87
+ toast.error(t('revokeAllFailure'));
88
+ }
89
+ };
90
+
91
+ return (
92
+ <>
93
+ <Card>
94
+ <CardHeader className="pb-4">
95
+ <div className="flex items-center justify-between">
96
+ <div>
97
+ <CardTitle>{t('title')}</CardTitle>
98
+ <CardDescription>{t('description')}</CardDescription>
99
+ </div>
100
+ {sessions.length > 1 && (
101
+ <div className="flex flex-row">
102
+ <Button
103
+ variant="outline"
104
+ size="sm"
105
+ onClick={() => setRevokeAllSessions(true)}
106
+ >
107
+ {t('revokeAllSessions')}
108
+ </Button>
109
+ <Button
110
+ variant="outline"
111
+ size="sm"
112
+ onClick={() => setRevokeAllOtherSessions(true)}
113
+ >
114
+ {t('revokeAllOtherSessions')}
115
+ </Button>
116
+ </div>
117
+ )}
118
+ </div>
119
+ </CardHeader>
120
+
121
+ <CardContent className="px-6 pb-6">
122
+ {sessions.length > 0 ? (
123
+ <>
124
+ <div className="rounded-md border">
125
+ <Table>
126
+ <TableHeader>
127
+ <TableRow>
128
+ <TableHead>{t('device')}</TableHead>
129
+ <TableHead>{t('browser')}</TableHead>
130
+ <TableHead>{t('location')}</TableHead>
131
+ <TableHead>{t('createdAt')}</TableHead>
132
+ <TableHead>{t('expiredAt')}</TableHead>
133
+ <TableHead className="text-right">
134
+ {t('action')}
135
+ </TableHead>
136
+ </TableRow>
137
+ </TableHeader>
138
+ <TableBody>
139
+ {sessions.map((session) => (
140
+ <SessionRow
141
+ key={session.id}
142
+ session={session}
143
+ onRequestRevoke={(id) => setSessionToRevoke(id)}
144
+ currentLocaleCode={currentLocaleCode}
145
+ getSettingValue={getSettingValue}
146
+ t={t}
147
+ />
148
+ ))}
149
+ </TableBody>
150
+ </Table>
151
+ </div>
152
+ <div className="mt-4">
153
+ <PaginationFooter
154
+ currentPage={page}
155
+ pageSize={pageSize}
156
+ totalItems={totalItems}
157
+ onPageChange={setPage}
158
+ onPageSizeChange={(newSize) => {
159
+ setPageSize(newSize);
160
+ setPage(1);
161
+ }}
162
+ pageSizeOptions={[10, 20, 30, 40, 50]}
163
+ />
164
+ </div>
165
+ </>
166
+ ) : (
167
+ <div className="text-center text-sm text-muted-foreground py-8">
168
+ {t('noActiveSessions')}
169
+ </div>
170
+ )}
171
+ </CardContent>
172
+ </Card>
173
+
174
+ <AlertDialog
175
+ open={sessionToRevoke !== null}
176
+ onOpenChange={(open) => !open && setSessionToRevoke(null)}
177
+ >
178
+ <AlertDialogContent>
179
+ <AlertDialogHeader>
180
+ <AlertDialogTitle>{t('revokeSessionTitle')}</AlertDialogTitle>
181
+ <AlertDialogDescription>
182
+ {t('revokeSessionDescription')}
183
+ </AlertDialogDescription>
184
+ </AlertDialogHeader>
185
+ <AlertDialogFooter>
186
+ <AlertDialogCancel>{t('cancel')}</AlertDialogCancel>
187
+ <AlertDialogAction
188
+ onClick={() =>
189
+ sessionToRevoke && handleRevokeSession(sessionToRevoke)
190
+ }
191
+ >
192
+ {t('revokeSession')}
193
+ </AlertDialogAction>
194
+ </AlertDialogFooter>
195
+ </AlertDialogContent>
196
+ </AlertDialog>
197
+
198
+ <AlertDialog open={revokeAllSessions} onOpenChange={setRevokeAllSessions}>
199
+ <AlertDialogContent>
200
+ <AlertDialogHeader>
201
+ <AlertDialogTitle>{t('revokeAllSessionsTitle')}</AlertDialogTitle>
202
+ <AlertDialogDescription>
203
+ {t('revokeAllSessionsDescription')}
204
+ </AlertDialogDescription>
205
+ </AlertDialogHeader>
206
+ <AlertDialogFooter>
207
+ <AlertDialogCancel>{t('cancel')}</AlertDialogCancel>
208
+ <AlertDialogAction onClick={handleRevokeAllSessions}>
209
+ {t('revokeSessions')}
210
+ </AlertDialogAction>
211
+ </AlertDialogFooter>
212
+ </AlertDialogContent>
213
+ </AlertDialog>
214
+
215
+ <AlertDialog
216
+ open={revokeAllOtherSessions}
217
+ onOpenChange={setRevokeAllOtherSessions}
218
+ >
219
+ <AlertDialogContent>
220
+ <AlertDialogHeader>
221
+ <AlertDialogTitle>
222
+ {t('revokeAllOtherSessionsTitle')}
223
+ </AlertDialogTitle>
224
+ <AlertDialogDescription>
225
+ {t('revokeAllOtherSessionsDescription')}
226
+ </AlertDialogDescription>
227
+ </AlertDialogHeader>
228
+ <AlertDialogFooter>
229
+ <AlertDialogCancel>{t('cancel')}</AlertDialogCancel>
230
+ <AlertDialogAction onClick={handleRevokeAllOtherSessions}>
231
+ {t('revokeSessions')}
232
+ </AlertDialogAction>
233
+ </AlertDialogFooter>
234
+ </AlertDialogContent>
235
+ </AlertDialog>
236
+ </>
237
+ );
238
+ }
239
+
240
+ function SessionRow({
241
+ session,
242
+ onRequestRevoke,
243
+ currentLocaleCode,
244
+ getSettingValue,
245
+ t,
246
+ }: {
247
+ session: UserSession;
248
+ onRequestRevoke: (id: number) => void;
249
+ currentLocaleCode: string;
250
+ getSettingValue: (key: string) => any;
251
+ t: any;
252
+ }) {
253
+ return (
254
+ <TableRow key={session.id}>
255
+ <TableCell>
256
+ <div className="flex items-center gap-3">
257
+ <div>
258
+ <p className="font-medium capitalize">
259
+ {parseDeviceType(session.user_agent)}
260
+ </p>
261
+ <p className="text-xs text-muted-foreground">
262
+ {parseOS(session.user_agent)}
263
+ </p>
264
+ </div>
265
+ </div>
266
+ </TableCell>
267
+ <TableCell>
268
+ <div>{parseBrowser(session.user_agent)}</div>
269
+
270
+ {session.revoked_at === null && (
271
+ <Badge variant="default" className="mt-1 text-xs">
272
+ {t('currentSession')}
273
+ </Badge>
274
+ )}
275
+ </TableCell>
276
+ <TableCell>
277
+ <div className="flex items-center gap-2">
278
+ <MapPin className="size-4 text-muted-foreground" />
279
+ <div>
280
+ <p className="text-md">
281
+ {`${session.location?.city || ''} `}
282
+ {session.location?.country}
283
+ </p>
284
+ <p className="text-xs text-muted-foreground">
285
+ {session.ip_address === '::1' ? '127.0.0.1' : session.ip_address}
286
+ </p>
287
+ </div>
288
+ </div>
289
+ </TableCell>
290
+ <TableCell>
291
+ <div className="flex items-center gap-2">
292
+ <Calendar className="size-4 text-muted-foreground" />
293
+ <div>
294
+ <p className="text-md">
295
+ {formatDate(
296
+ String(session.created_at),
297
+ getSettingValue,
298
+ currentLocaleCode
299
+ )}
300
+ </p>
301
+ <p className="text-xs text-muted-foreground">
302
+ {
303
+ formatDateTime(
304
+ String(session.created_at),
305
+ getSettingValue,
306
+ currentLocaleCode
307
+ ).split(' ')[1]
308
+ }
309
+ </p>
310
+ </div>
311
+ </div>
312
+ </TableCell>
313
+ <TableCell>
314
+ <div className="flex items-center gap-2">
315
+ <Clock className="size-4 text-muted-foreground" />
316
+ <div>
317
+ {session.revoked_at ? (
318
+ <>
319
+ <p className="text-md">
320
+ {formatDate(
321
+ String(session.revoked_at),
322
+ getSettingValue,
323
+ currentLocaleCode
324
+ )}
325
+ </p>
326
+ <p className="text-xs text-muted-foreground">
327
+ {
328
+ formatDateTime(
329
+ String(session.revoked_at),
330
+ getSettingValue,
331
+ currentLocaleCode
332
+ ).split(' ')[1]
333
+ }
334
+ </p>
335
+ </>
336
+ ) : (
337
+ <p className="text-xs text-muted-foreground">&nbsp;</p>
338
+ )}
339
+ </div>
340
+ </div>
341
+ </TableCell>
342
+ <TableCell className="text-right">
343
+ {session.revoked_at === null && (
344
+ <Button
345
+ variant="ghost"
346
+ size="sm"
347
+ onClick={() => onRequestRevoke(Number(session.id))}
348
+ >
349
+ <LogOut className="mr-2 size-4" />
350
+ {t('revoke')}
351
+ </Button>
352
+ )}
353
+ </TableCell>
354
+ </TableRow>
355
+ );
356
+ }