@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,621 @@
1
+ 'use client';
2
+
3
+ import {
4
+ AlertDialog,
5
+ AlertDialogAction,
6
+ AlertDialogCancel,
7
+ AlertDialogContent,
8
+ AlertDialogDescription,
9
+ AlertDialogFooter,
10
+ AlertDialogHeader,
11
+ AlertDialogTitle,
12
+ } from '@/components/ui/alert-dialog';
13
+ import { Button } from '@/components/ui/button';
14
+ import {
15
+ Dialog,
16
+ DialogContent,
17
+ DialogDescription,
18
+ DialogFooter,
19
+ DialogHeader,
20
+ DialogTitle,
21
+ DialogTrigger,
22
+ } from '@/components/ui/dialog';
23
+ import {
24
+ Form,
25
+ FormControl,
26
+ FormField,
27
+ FormItem,
28
+ FormLabel,
29
+ FormMessage,
30
+ } from '@/components/ui/form';
31
+ import { Input } from '@/components/ui/input';
32
+ import {
33
+ Select,
34
+ SelectContent,
35
+ SelectItem,
36
+ SelectTrigger,
37
+ SelectValue,
38
+ } from '@/components/ui/select';
39
+ import {
40
+ Table,
41
+ TableBody,
42
+ TableCell,
43
+ TableHead,
44
+ TableHeader,
45
+ TableRow,
46
+ } from '@/components/ui/table';
47
+ import { useDebounce } from '@/hooks/use-debounce';
48
+ import { useApp, useQuery } from '@hed-hog/next-app-provider';
49
+ import { zodResolver } from '@hookform/resolvers/zod';
50
+ import {
51
+ IconChevronLeft,
52
+ IconChevronRight,
53
+ IconChevronsLeft,
54
+ IconChevronsRight,
55
+ IconPlus,
56
+ IconTrash,
57
+ } from '@tabler/icons-react';
58
+ import { useTranslations } from 'next-intl';
59
+ import { useState } from 'react';
60
+ import { useForm } from 'react-hook-form';
61
+ import { toast } from 'sonner';
62
+ import { z } from 'zod';
63
+
64
+ type ItemFormData = {
65
+ dashboard_id: number;
66
+ component_id: number;
67
+ width: number;
68
+ height: number;
69
+ x_axis: number;
70
+ y_axis: number;
71
+ };
72
+
73
+ interface DashboardItem {
74
+ id: number;
75
+ dashboard_id: number;
76
+ dashboard_component_id: number;
77
+ dashboard: {
78
+ slug: string;
79
+ dashboard_locale: Array<{
80
+ locale: { code: string };
81
+ name: string;
82
+ }>;
83
+ };
84
+ dashboard_component: {
85
+ slug: string;
86
+ dashboard_component_locale: Array<{
87
+ locale: { code: string };
88
+ name: string;
89
+ }>;
90
+ };
91
+ }
92
+
93
+ interface Dashboard {
94
+ id: number;
95
+ slug: string;
96
+ dashboard_locale: Array<{
97
+ locale: { code: string };
98
+ name: string;
99
+ }>;
100
+ }
101
+
102
+ interface Component {
103
+ id: number;
104
+ slug: string;
105
+ dashboard_component_locale: Array<{
106
+ locale: { code: string };
107
+ name: string;
108
+ }>;
109
+ }
110
+
111
+ export function ItemsTab() {
112
+ const t = useTranslations('core.DashboardManagement');
113
+ const { request, currentLocaleCode } = useApp();
114
+
115
+ const itemSchema = z.object({
116
+ dashboard_id: z.coerce.number().min(1, t('selectDashboardRequired')),
117
+ component_id: z.coerce.number().min(1, t('selectComponentRequired')),
118
+ width: z.coerce.number().min(1).default(3),
119
+ height: z.coerce.number().min(1).default(2),
120
+ x_axis: z.coerce.number().min(0).default(0),
121
+ y_axis: z.coerce.number().min(0).default(0),
122
+ });
123
+
124
+ const [open, setOpen] = useState(false);
125
+ const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);
126
+ const [itemToDelete, setItemToDelete] = useState<number | null>(null);
127
+ const [page, setPage] = useState(1);
128
+ const [pageSize, setPageSize] = useState(10);
129
+ const [searchQuery, setSearchQuery] = useState('');
130
+ const debouncedSearch = useDebounce(searchQuery, 500);
131
+
132
+ const {
133
+ data: paginatedData,
134
+ isLoading,
135
+ refetch,
136
+ } = useQuery<{ data: DashboardItem[]; total: number }>({
137
+ queryKey: [
138
+ 'dashboard-items',
139
+ page,
140
+ pageSize,
141
+ debouncedSearch,
142
+ currentLocaleCode,
143
+ ],
144
+ queryFn: async () => {
145
+ const params = new URLSearchParams();
146
+ params.set('page', String(page));
147
+ params.set('pageSize', String(pageSize));
148
+ if (debouncedSearch) params.set('search', debouncedSearch);
149
+
150
+ const response = await request<{ data: DashboardItem[]; total: number }>({
151
+ url: `/dashboard-item?${params.toString()}`,
152
+ method: 'GET',
153
+ });
154
+ return response.data;
155
+ },
156
+ });
157
+
158
+ const items = paginatedData?.data ?? [];
159
+ const total = paginatedData?.total ?? 0;
160
+ const totalPages = Math.ceil(total / pageSize);
161
+
162
+ const { data: dashboards } = useQuery<any>({
163
+ queryKey: ['dashboards'],
164
+ queryFn: async () => {
165
+ const { data } = await request<Dashboard[]>({
166
+ url: '/dashboard',
167
+ method: 'GET',
168
+ });
169
+ return data;
170
+ },
171
+ });
172
+
173
+ const { data: components } = useQuery<any>({
174
+ queryKey: ['dashboard-components'],
175
+ queryFn: async () => {
176
+ const { data } = await request<Component[]>({
177
+ url: '/dashboard-component',
178
+ method: 'GET',
179
+ });
180
+ return data;
181
+ },
182
+ });
183
+
184
+ const form = useForm<ItemFormData>({
185
+ resolver: zodResolver(itemSchema),
186
+ defaultValues: {
187
+ dashboard_id: 0,
188
+ component_id: 0,
189
+ width: 3,
190
+ height: 2,
191
+ x_axis: 0,
192
+ y_axis: 0,
193
+ },
194
+ });
195
+
196
+ const onSubmit = async (values: ItemFormData) => {
197
+ try {
198
+ await request({
199
+ url: '/dashboard-item',
200
+ method: 'POST',
201
+ data: values,
202
+ });
203
+
204
+ toast.success(t('itemAdded'));
205
+ setOpen(false);
206
+ form.reset();
207
+ refetch();
208
+ } catch (error) {
209
+ console.error('Erro ao adicionar item:', error);
210
+ toast.error('Erro ao adicionar item');
211
+ }
212
+ };
213
+
214
+ const handleDeleteClick = (id: number) => {
215
+ setItemToDelete(id);
216
+ setDeleteDialogOpen(true);
217
+ };
218
+
219
+ const handleDeleteConfirm = async () => {
220
+ if (!itemToDelete) return;
221
+
222
+ try {
223
+ await request({
224
+ url: `/dashboard-item/${itemToDelete}`,
225
+ method: 'DELETE',
226
+ });
227
+ toast.success(t('itemDeleted'));
228
+ refetch();
229
+ } catch (error) {
230
+ console.error('Erro ao excluir item:', error);
231
+ toast.error('Erro ao excluir item');
232
+ } finally {
233
+ setDeleteDialogOpen(false);
234
+ setItemToDelete(null);
235
+ }
236
+ };
237
+
238
+ const handleOpenChange = (newOpen: boolean) => {
239
+ setOpen(newOpen);
240
+ if (!newOpen) {
241
+ form.reset();
242
+ }
243
+ };
244
+
245
+ return (
246
+ <div className="space-y-4">
247
+ <div className="flex justify-between items-center">
248
+ <div>
249
+ <h2 className="text-lg font-semibold">{t('itemsTab')}</h2>
250
+ <p className="text-muted-foreground text-sm">
251
+ {t('relateComponents')}
252
+ </p>
253
+ </div>
254
+ <Dialog open={open} onOpenChange={handleOpenChange}>
255
+ <DialogTrigger asChild>
256
+ <Button size="sm" className="gap-2">
257
+ <IconPlus className="size-4" />
258
+ {t('addItem')}
259
+ </Button>
260
+ </DialogTrigger>
261
+ <DialogContent className="max-w-6xl">
262
+ <DialogHeader>
263
+ <DialogTitle>{t('addItem')}</DialogTitle>
264
+ <DialogDescription>
265
+ {t('selectDashboardAndComponent')}
266
+ </DialogDescription>
267
+ </DialogHeader>
268
+ <Form {...form}>
269
+ <form
270
+ onSubmit={form.handleSubmit(onSubmit)}
271
+ className="space-y-4"
272
+ >
273
+ <FormField
274
+ control={form.control}
275
+ name="dashboard_id"
276
+ render={({ field }) => (
277
+ <FormItem>
278
+ <FormLabel>{t('dashboard')}</FormLabel>
279
+ <Select
280
+ onValueChange={field.onChange}
281
+ defaultValue={field.value?.toString()}
282
+ >
283
+ <FormControl>
284
+ <SelectTrigger className="w-full">
285
+ <SelectValue placeholder={t('selectDashboard')} />
286
+ </SelectTrigger>
287
+ </FormControl>
288
+ <SelectContent>
289
+ {(dashboards.data ?? []).map(
290
+ (dashboard: Dashboard) => {
291
+ const name =
292
+ dashboard.dashboard_locale.find(
293
+ (l) => l.locale.code === currentLocaleCode
294
+ )?.name || dashboard.slug;
295
+ return (
296
+ <SelectItem
297
+ key={dashboard.id}
298
+ value={dashboard.id.toString()}
299
+ >
300
+ {name} ({dashboard.slug})
301
+ </SelectItem>
302
+ );
303
+ }
304
+ )}
305
+ </SelectContent>
306
+ </Select>
307
+ <FormMessage />
308
+ </FormItem>
309
+ )}
310
+ />
311
+ <FormField
312
+ control={form.control}
313
+ name="component_id"
314
+ render={({ field }) => (
315
+ <FormItem>
316
+ <FormLabel>{t('component')}</FormLabel>
317
+ <Select
318
+ onValueChange={field.onChange}
319
+ defaultValue={field.value?.toString()}
320
+ >
321
+ <FormControl>
322
+ <SelectTrigger className="w-full">
323
+ <SelectValue placeholder={t('selectComponent')} />
324
+ </SelectTrigger>
325
+ </FormControl>
326
+ <SelectContent>
327
+ {(components.data ?? []).map(
328
+ (component: Component) => {
329
+ const name =
330
+ component.dashboard_component_locale.find(
331
+ (l) => l.locale.code === currentLocaleCode
332
+ )?.name || component.slug;
333
+ return (
334
+ <SelectItem
335
+ key={component.id}
336
+ value={component.id.toString()}
337
+ >
338
+ {name} ({component.slug})
339
+ </SelectItem>
340
+ );
341
+ }
342
+ )}
343
+ </SelectContent>
344
+ </Select>
345
+ <FormMessage />
346
+ </FormItem>
347
+ )}
348
+ />
349
+ <div className="grid grid-cols-2 gap-4">
350
+ <FormField
351
+ control={form.control}
352
+ name="width"
353
+ render={({ field }) => (
354
+ <FormItem>
355
+ <FormLabel>{t('width')}</FormLabel>
356
+ <FormControl>
357
+ <Input type="number" min="1" max="12" {...field} />
358
+ </FormControl>
359
+ <FormMessage />
360
+ </FormItem>
361
+ )}
362
+ />
363
+ <FormField
364
+ control={form.control}
365
+ name="height"
366
+ render={({ field }) => (
367
+ <FormItem>
368
+ <FormLabel>{t('height')}</FormLabel>
369
+ <FormControl>
370
+ <Input type="number" min="1" {...field} />
371
+ </FormControl>
372
+ <FormMessage />
373
+ </FormItem>
374
+ )}
375
+ />
376
+ </div>
377
+ <div className="grid grid-cols-2 gap-4">
378
+ <FormField
379
+ control={form.control}
380
+ name="x_axis"
381
+ render={({ field }) => (
382
+ <FormItem>
383
+ <FormLabel>{t('positionX')}</FormLabel>
384
+ <FormControl>
385
+ <Input type="number" min="0" {...field} />
386
+ </FormControl>
387
+ <FormMessage />
388
+ </FormItem>
389
+ )}
390
+ />
391
+ <FormField
392
+ control={form.control}
393
+ name="y_axis"
394
+ render={({ field }) => (
395
+ <FormItem>
396
+ <FormLabel>{t('positionY')}</FormLabel>
397
+ <FormControl>
398
+ <Input type="number" min="0" {...field} />
399
+ </FormControl>
400
+ <FormMessage />
401
+ </FormItem>
402
+ )}
403
+ />
404
+ </div>
405
+
406
+ <div className="rounded-md border p-4 bg-muted/50">
407
+ <p className="text-xs text-muted-foreground mb-2">
408
+ {t('positionPreview')}
409
+ </p>
410
+ <div
411
+ className="grid grid-cols-12 gap-1"
412
+ style={{ gridAutoRows: '40px' }}
413
+ >
414
+ {Array.from({ length: 12 * 8 }).map((_, index) => {
415
+ const col = index % 12;
416
+ const row = Math.floor(index / 12);
417
+ const x = form.watch('x_axis') || 0;
418
+ const y = form.watch('y_axis') || 0;
419
+ const w = form.watch('width') || 3;
420
+ const h = form.watch('height') || 2;
421
+
422
+ const isInside =
423
+ col >= x && col < x + w && row >= y && row < y + h;
424
+
425
+ return (
426
+ <div
427
+ key={index}
428
+ className={`border ${
429
+ isInside
430
+ ? 'bg-primary/30 border-primary'
431
+ : 'border-dashed border-muted'
432
+ }`}
433
+ />
434
+ );
435
+ })}
436
+ </div>
437
+ <p className="text-xs text-muted-foreground mt-2">
438
+ {t('willOccupy', {
439
+ x: form.watch('x_axis'),
440
+ y: form.watch('y_axis'),
441
+ w: form.watch('width'),
442
+ h: form.watch('height'),
443
+ })}
444
+ </p>
445
+ </div>
446
+
447
+ <DialogFooter>
448
+ <Button type="submit">{t('save')}</Button>
449
+ </DialogFooter>
450
+ </form>
451
+ </Form>
452
+ </DialogContent>
453
+ </Dialog>
454
+ </div>
455
+
456
+ <div className="rounded-md border">
457
+ <Table>
458
+ <TableHeader>
459
+ <TableRow>
460
+ <TableHead>ID</TableHead>
461
+ <TableHead>{t('dashboard')}</TableHead>
462
+ <TableHead>{t('component')}</TableHead>
463
+ <TableHead className="w-[100px]">{t('actions')}</TableHead>
464
+ </TableRow>
465
+ </TableHeader>
466
+ <TableBody>
467
+ {isLoading ? (
468
+ <TableRow>
469
+ <TableCell colSpan={4} className="text-center">
470
+ {t('loading')}
471
+ </TableCell>
472
+ </TableRow>
473
+ ) : items && items.length > 0 ? (
474
+ items.map((item) => {
475
+ const dashboardName =
476
+ item.dashboard.dashboard_locale.find(
477
+ (l) => l.locale.code === currentLocaleCode
478
+ )?.name || item.dashboard.slug;
479
+ const componentName =
480
+ item.dashboard_component.dashboard_component_locale.find(
481
+ (l) => l.locale.code === currentLocaleCode
482
+ )?.name || item.dashboard_component.slug;
483
+ return (
484
+ <TableRow key={item.id}>
485
+ <TableCell>{item.id}</TableCell>
486
+ <TableCell>
487
+ {dashboardName}
488
+ <span className="text-muted-foreground ml-2 text-xs">
489
+ ({item.dashboard.slug})
490
+ </span>
491
+ </TableCell>
492
+ <TableCell>
493
+ {componentName}
494
+ <span className="text-muted-foreground ml-2 text-xs">
495
+ ({item.dashboard_component.slug})
496
+ </span>
497
+ </TableCell>
498
+ <TableCell>
499
+ <Button
500
+ size="icon"
501
+ variant="ghost"
502
+ onClick={() => handleDeleteClick(item.id)}
503
+ >
504
+ <IconTrash className="size-4" />
505
+ </Button>
506
+ </TableCell>
507
+ </TableRow>
508
+ );
509
+ })
510
+ ) : (
511
+ <TableRow>
512
+ <TableCell colSpan={4} className="text-center">
513
+ {t('noItems')}
514
+ </TableCell>
515
+ </TableRow>
516
+ )}
517
+ </TableBody>
518
+ </Table>
519
+ </div>
520
+
521
+ {/* Paginação */}
522
+ {totalPages > 1 && (
523
+ <div className="flex items-center justify-between px-2">
524
+ <div className="text-muted-foreground text-sm">
525
+ {t('showingResults', {
526
+ from: (page - 1) * pageSize + 1,
527
+ to: Math.min(page * pageSize, total),
528
+ total: total,
529
+ })}
530
+ </div>
531
+ <div className="flex items-center gap-2">
532
+ <div className="flex items-center gap-2">
533
+ <label htmlFor="pageSize" className="text-sm">
534
+ {t('itemsPerPage')}
535
+ </label>
536
+ <Select
537
+ value={String(pageSize)}
538
+ onValueChange={(value) => {
539
+ setPageSize(Number(value));
540
+ setPage(1);
541
+ }}
542
+ >
543
+ <SelectTrigger className="w-20" id="pageSize">
544
+ <SelectValue />
545
+ </SelectTrigger>
546
+ <SelectContent>
547
+ {[10, 20, 30, 50].map((size) => (
548
+ <SelectItem key={size} value={String(size)}>
549
+ {size}
550
+ </SelectItem>
551
+ ))}
552
+ </SelectContent>
553
+ </Select>
554
+ </div>
555
+ <div className="text-sm">
556
+ {t('pageOf', { page: page, total: totalPages })}
557
+ </div>
558
+ <div className="flex gap-1">
559
+ <Button
560
+ variant="outline"
561
+ size="icon"
562
+ className="size-8"
563
+ onClick={() => setPage(1)}
564
+ disabled={page === 1}
565
+ >
566
+ <IconChevronsLeft className="size-4" />
567
+ </Button>
568
+ <Button
569
+ variant="outline"
570
+ size="icon"
571
+ className="size-8"
572
+ onClick={() => setPage((p) => Math.max(1, p - 1))}
573
+ disabled={page === 1}
574
+ >
575
+ <IconChevronLeft className="size-4" />
576
+ </Button>
577
+ <Button
578
+ variant="outline"
579
+ size="icon"
580
+ className="size-8"
581
+ onClick={() => setPage((p) => Math.min(totalPages, p + 1))}
582
+ disabled={page === totalPages}
583
+ >
584
+ <IconChevronRight className="size-4" />
585
+ </Button>
586
+ <Button
587
+ variant="outline"
588
+ size="icon"
589
+ className="size-8"
590
+ onClick={() => setPage(totalPages)}
591
+ disabled={page === totalPages}
592
+ >
593
+ <IconChevronsRight className="size-4" />
594
+ </Button>
595
+ </div>
596
+ </div>
597
+ </div>
598
+ )}
599
+
600
+ <AlertDialog open={deleteDialogOpen} onOpenChange={setDeleteDialogOpen}>
601
+ <AlertDialogContent>
602
+ <AlertDialogHeader>
603
+ <AlertDialogTitle>{t('confirmDelete')}</AlertDialogTitle>
604
+ <AlertDialogDescription>
605
+ {t('deleteDescription')}
606
+ </AlertDialogDescription>
607
+ </AlertDialogHeader>
608
+ <AlertDialogFooter>
609
+ <AlertDialogCancel>{t('cancel')}</AlertDialogCancel>
610
+ <AlertDialogAction
611
+ onClick={handleDeleteConfirm}
612
+ className="bg-destructive text-destructive-foreground hover:bg-destructive/90"
613
+ >
614
+ {t('delete')}
615
+ </AlertDialogAction>
616
+ </AlertDialogFooter>
617
+ </AlertDialogContent>
618
+ </AlertDialog>
619
+ </div>
620
+ );
621
+ }
@@ -0,0 +1,14 @@
1
+ 'use client';
2
+
3
+ import { useRouter } from 'next/navigation';
4
+ import { useEffect } from 'react';
5
+
6
+ export default function DashboardRedirectPage() {
7
+ const router = useRouter();
8
+
9
+ useEffect(() => {
10
+ router.replace('/dashboard/default');
11
+ }, [router]);
12
+
13
+ return null;
14
+ }