@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,312 @@
1
+ 'use client';
2
+
3
+ import { PageHeader, PaginationFooter } from '@/components/entity-list';
4
+ import { Badge } from '@/components/ui/badge';
5
+ import { Button } from '@/components/ui/button';
6
+ import { Card, CardContent } from '@/components/ui/card';
7
+ import {
8
+ Dialog,
9
+ DialogContent,
10
+ DialogDescription,
11
+ DialogHeader,
12
+ DialogTitle,
13
+ } from '@/components/ui/dialog';
14
+ import { Input } from '@/components/ui/input';
15
+ import { ScrollArea } from '@/components/ui/scroll-area';
16
+ import { useDebounce } from '@/hooks/use-debounce';
17
+ import { formatDate } from '@/lib/format-date';
18
+ import { useApp, useQuery } from '@hed-hog/next-app-provider';
19
+ import { Clock, Mail, Search, User } from 'lucide-react';
20
+ import { useTranslations } from 'next-intl';
21
+ import { useEffect, useState } from 'react';
22
+
23
+ type PaginationResult<T> = {
24
+ data: T[];
25
+ total: number;
26
+ page: number;
27
+ pageSize: number;
28
+ };
29
+
30
+ type MailSent = {
31
+ id: number;
32
+ mail_id: number;
33
+ subject: string;
34
+ from: string;
35
+ to: string;
36
+ cc?: string;
37
+ bcc?: string;
38
+ body: string;
39
+ created_at: string;
40
+ updated_at: string;
41
+ };
42
+
43
+ export default function MailLogPage() {
44
+ const t = useTranslations('core.MailLog');
45
+ const [logs, setLogs] = useState<MailSent[]>([]);
46
+ const [selectedLog, setSelectedLog] = useState<MailSent | null>(null);
47
+ const [isDetailDialogOpen, setIsDetailDialogOpen] = useState(false);
48
+ const [searchTerm, setSearchTerm] = useState('');
49
+ const debouncedSearch = useDebounce(searchTerm);
50
+ const [page, setPage] = useState(1);
51
+ const [pageSize, setPageSize] = useState(10);
52
+ const { request, getSettingValue } = useApp();
53
+
54
+ const {
55
+ data: { data, total },
56
+ refetch: refetchLogs,
57
+ } = useQuery<PaginationResult<MailSent>>({
58
+ queryKey: ['mail-sent', debouncedSearch, page, pageSize],
59
+ queryFn: async () => {
60
+ const response = await request({
61
+ url: '/mail-sent',
62
+ params: {
63
+ search: debouncedSearch,
64
+ page,
65
+ pageSize,
66
+ },
67
+ });
68
+ return response.data as PaginationResult<MailSent>;
69
+ },
70
+ initialData: {
71
+ data: [],
72
+ total: 0,
73
+ page: 1,
74
+ pageSize: 10,
75
+ },
76
+ });
77
+
78
+ useEffect(() => {
79
+ if (data) {
80
+ setLogs(data);
81
+ }
82
+ }, [data]);
83
+
84
+ const handleViewDetails = (log: MailSent): void => {
85
+ setSelectedLog(log);
86
+ setIsDetailDialogOpen(true);
87
+ };
88
+
89
+ const handleSearchChange = (value: string): void => {
90
+ setSearchTerm(value);
91
+ setPage(1);
92
+ };
93
+
94
+ const truncateText = (text: string, maxLength: number = 100): string => {
95
+ if (!text) return '';
96
+ return text.length > maxLength
97
+ ? text.substring(0, maxLength) + '...'
98
+ : text;
99
+ };
100
+
101
+ useEffect(() => {
102
+ refetchLogs();
103
+ }, [debouncedSearch, page, pageSize]);
104
+
105
+ return (
106
+ <div className="flex flex-col h-screen px-4">
107
+ <PageHeader
108
+ breadcrumbs={[
109
+ { label: t('breadcrumbHome'), href: '/' },
110
+ { label: t('breadcrumbTitle') },
111
+ ]}
112
+ title={t('title')}
113
+ description={t('description')}
114
+ />
115
+
116
+ <div className="grid grid-cols-1 gap-4 md:grid-cols-2">
117
+ <Card className="transition-shadow hover:shadow-md p-0">
118
+ <CardContent className="p-4">
119
+ <div className="flex items-center space-x-3">
120
+ <div className="rounded-full bg-blue-100 p-2 dark:bg-blue-900">
121
+ <Mail className="h-6 w-6 text-blue-600 dark:text-blue-400" />
122
+ </div>
123
+ <div>
124
+ <p className="text-sm font-medium text-muted-foreground">
125
+ {t('totalEmails')}
126
+ </p>
127
+ <p className="text-2xl font-bold">{total || 0}</p>
128
+ </div>
129
+ </div>
130
+ </CardContent>
131
+ </Card>
132
+
133
+ <Card className="transition-shadow hover:shadow-md p-0">
134
+ <CardContent className="p-4">
135
+ <div className="flex items-center space-x-3">
136
+ <div className="rounded-full bg-green-100 p-2 dark:bg-green-900">
137
+ <Clock className="h-6 w-6 text-green-600 dark:text-green-400" />
138
+ </div>
139
+ <div>
140
+ <p className="text-sm font-medium text-muted-foreground">
141
+ {t('onThisPage')}
142
+ </p>
143
+ <p className="text-2xl font-bold">{logs.length}</p>
144
+ </div>
145
+ </div>
146
+ </CardContent>
147
+ </Card>
148
+ </div>
149
+
150
+ <div className="relative my-4">
151
+ <Search className="absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground" />
152
+ <Input
153
+ placeholder={t('searchPlaceholder')}
154
+ value={searchTerm}
155
+ onChange={(e) => handleSearchChange(e.target.value)}
156
+ className="pl-10"
157
+ />
158
+ </div>
159
+
160
+ <div className="space-y-3 mb-4">
161
+ {logs.length === 0 ? (
162
+ <Card>
163
+ <CardContent className="flex flex-col items-center justify-center p-12">
164
+ <Mail className="mb-4 h-12 w-12 text-muted-foreground" />
165
+ <p className="text-lg font-medium text-muted-foreground">
166
+ {t('noLogsFound')}
167
+ </p>
168
+ <p className="text-sm text-muted-foreground">
169
+ {searchTerm ? t('adjustSearch') : t('noEmailsSent')}
170
+ </p>
171
+ </CardContent>
172
+ </Card>
173
+ ) : (
174
+ logs.map((log) => (
175
+ <Card
176
+ key={log.id}
177
+ onDoubleClick={() => handleViewDetails(log)}
178
+ className="cursor-pointer transition-shadow hover:shadow-md"
179
+ >
180
+ <CardContent className="p-6">
181
+ <div className="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
182
+ <div className="flex-1 space-y-2">
183
+ <div className="flex items-center gap-2">
184
+ <h3 className="text-lg font-semibold">{log.subject}</h3>
185
+ <Badge variant="outline">#{log.id}</Badge>
186
+ </div>
187
+ <div className="flex flex-col gap-1 text-sm text-muted-foreground">
188
+ <div className="flex items-center gap-2">
189
+ <User className="h-4 w-4" />
190
+ <span>
191
+ {t('from')}: {log.from}
192
+ </span>
193
+ </div>
194
+ <div className="flex items-center gap-2">
195
+ <Mail className="h-4 w-4" />
196
+ <span>
197
+ {t('to')}: {truncateText(log.to, 50)}
198
+ </span>
199
+ </div>
200
+ <div className="flex items-center gap-2">
201
+ <Clock className="h-4 w-4" />
202
+ <span>
203
+ {formatDate(log.created_at, getSettingValue)}
204
+ </span>
205
+ </div>
206
+ </div>
207
+ </div>
208
+ <Button
209
+ variant="outline"
210
+ size="sm"
211
+ onClick={(e) => {
212
+ e.stopPropagation();
213
+ handleViewDetails(log);
214
+ }}
215
+ >
216
+ {t('viewDetails')}
217
+ </Button>
218
+ </div>
219
+ </CardContent>
220
+ </Card>
221
+ ))
222
+ )}
223
+ </div>
224
+
225
+ <PaginationFooter
226
+ currentPage={page}
227
+ pageSize={pageSize}
228
+ totalItems={total}
229
+ onPageChange={setPage}
230
+ onPageSizeChange={(newSize) => {
231
+ setPageSize(newSize);
232
+ setPage(1);
233
+ }}
234
+ pageSizeOptions={[10, 20, 30, 40, 50]}
235
+ />
236
+
237
+ {selectedLog && (
238
+ <Dialog open={isDetailDialogOpen} onOpenChange={setIsDetailDialogOpen}>
239
+ <DialogContent className="max-w-2xl max-h-[80vh]">
240
+ <DialogHeader>
241
+ <DialogTitle>{t('emailDetails')}</DialogTitle>
242
+ <DialogDescription>{t('detailsDescription')}</DialogDescription>
243
+ </DialogHeader>
244
+ <ScrollArea className="max-h-[60vh]">
245
+ <div className="space-y-4">
246
+ <div>
247
+ <h4 className="mb-2 text-sm font-semibold">{t('subject')}</h4>
248
+ <p className="rounded-md bg-muted p-3 text-sm">
249
+ {selectedLog.subject}
250
+ </p>
251
+ </div>
252
+ <div>
253
+ <h4 className="mb-2 text-sm font-semibold">{t('from')}</h4>
254
+ <p className="rounded-md bg-muted p-3 text-sm">
255
+ {selectedLog.from}
256
+ </p>
257
+ </div>
258
+ <div>
259
+ <h4 className="mb-2 text-sm font-semibold">{t('to')}</h4>
260
+ <p className="rounded-md bg-muted p-3 text-sm">
261
+ {selectedLog.to}
262
+ </p>
263
+ </div>
264
+ {selectedLog.cc && (
265
+ <div>
266
+ <h4 className="mb-2 text-sm font-semibold">{t('cc')}</h4>
267
+ <p className="rounded-md bg-muted p-3 text-sm">
268
+ {selectedLog.cc}
269
+ </p>
270
+ </div>
271
+ )}
272
+ {selectedLog.bcc && (
273
+ <div>
274
+ <h4 className="mb-2 text-sm font-semibold">{t('bcc')}</h4>
275
+ <p className="rounded-md bg-muted p-3 text-sm">
276
+ {selectedLog.bcc}
277
+ </p>
278
+ </div>
279
+ )}
280
+ <div>
281
+ <h4 className="mb-2 text-sm font-semibold">{t('body')}</h4>
282
+ <div
283
+ className="rounded-md bg-muted p-3 text-sm"
284
+ dangerouslySetInnerHTML={{ __html: selectedLog.body }}
285
+ />
286
+ </div>
287
+ <div className="grid grid-cols-2 gap-4">
288
+ <div>
289
+ <h4 className="mb-2 text-sm font-semibold">
290
+ {t('createdAt')}
291
+ </h4>
292
+ <p className="rounded-md bg-muted p-3 text-sm">
293
+ {formatDate(selectedLog.created_at, getSettingValue)}
294
+ </p>
295
+ </div>
296
+ <div>
297
+ <h4 className="mb-2 text-sm font-semibold">
298
+ {t('updatedAt')}
299
+ </h4>
300
+ <p className="rounded-md bg-muted p-3 text-sm">
301
+ {formatDate(selectedLog.updated_at, getSettingValue)}
302
+ </p>
303
+ </div>
304
+ </div>
305
+ </div>
306
+ </ScrollArea>
307
+ </DialogContent>
308
+ </Dialog>
309
+ )}
310
+ </div>
311
+ );
312
+ }