@hed-hog/core 0.0.279 → 0.0.286

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 (35) hide show
  1. package/dist/auth/auth.controller.d.ts +3 -3
  2. package/dist/auth/auth.service.d.ts +8 -8
  3. package/dist/file/file.controller.d.ts +2 -2
  4. package/dist/file/file.service.d.ts +4 -4
  5. package/dist/role/guards/role.guard.d.ts.map +1 -1
  6. package/dist/role/guards/role.guard.js +1 -1
  7. package/dist/role/guards/role.guard.js.map +1 -1
  8. package/dist/session/session.controller.d.ts +1 -1
  9. package/dist/session/session.service.d.ts +3 -3
  10. package/dist/setting/setting.controller.d.ts +3 -0
  11. package/dist/setting/setting.controller.d.ts.map +1 -1
  12. package/dist/setting/setting.controller.js +10 -0
  13. package/dist/setting/setting.controller.js.map +1 -1
  14. package/dist/setting/setting.service.d.ts +2 -1
  15. package/dist/setting/setting.service.d.ts.map +1 -1
  16. package/dist/setting/setting.service.js +51 -12
  17. package/dist/setting/setting.service.js.map +1 -1
  18. package/dist/user/user.controller.d.ts +2 -2
  19. package/dist/user/user.service.d.ts +6 -6
  20. package/hedhog/data/route.yaml +8 -0
  21. package/hedhog/frontend/app/ai_agent/page.tsx.ejs +72 -65
  22. package/hedhog/frontend/app/configurations/[slug]/components/setting-field.tsx.ejs +1 -92
  23. package/hedhog/frontend/app/dashboard/page.tsx.ejs +2 -2
  24. package/hedhog/frontend/app/mail/log/page.tsx.ejs +37 -42
  25. package/hedhog/frontend/app/mail/template/page.tsx.ejs +176 -126
  26. package/hedhog/frontend/app/menu/page.tsx.ejs +45 -39
  27. package/hedhog/frontend/app/preferences/page.tsx.ejs +34 -121
  28. package/hedhog/frontend/app/roles/page.tsx.ejs +45 -46
  29. package/hedhog/frontend/app/users/page.tsx.ejs +71 -68
  30. package/hedhog/frontend/messages/en.json +7 -0
  31. package/hedhog/frontend/messages/pt.json +7 -0
  32. package/package.json +2 -2
  33. package/src/role/guards/role.guard.ts +9 -8
  34. package/src/setting/setting.controller.ts +21 -16
  35. package/src/setting/setting.service.ts +63 -17
@@ -1,6 +1,7 @@
1
1
  'use client';
2
2
 
3
3
  import {
4
+ EmptyState,
4
5
  Page,
5
6
  PageHeader,
6
7
  PaginationFooter,
@@ -115,7 +116,7 @@ export default function AiAgentPage() {
115
116
  },
116
117
  });
117
118
 
118
- const { data, isLoading, refetch } = useQuery<PaginatedResponse<Agent>>({
119
+ const { data, isLoading, refetch } = useQuery<PaginatedResponse<Agent>>({
119
120
  queryKey: ['ai-agents', page, pageSize, debouncedSearch],
120
121
  queryFn: async () => {
121
122
  const params = new URLSearchParams();
@@ -130,14 +131,14 @@ export default function AiAgentPage() {
130
131
 
131
132
  return response.data;
132
133
  },
133
- });
134
- const agentList = data ?? {
135
- data: [],
136
- total: 0,
137
- page: 1,
138
- pageSize: 10,
139
- totalPages: 1,
140
- };
134
+ });
135
+ const agentList = data ?? {
136
+ data: [],
137
+ total: 0,
138
+ page: 1,
139
+ pageSize: 10,
140
+ totalPages: 1,
141
+ };
141
142
 
142
143
  useEffect(() => {
143
144
  if (editingAgent) {
@@ -261,69 +262,75 @@ export default function AiAgentPage() {
261
262
  placeholder={t('searchPlaceholder')}
262
263
  />
263
264
 
264
- <Card>
265
- <CardContent className="p-0">
266
- <Table>
267
- <TableHeader>
268
- <TableRow>
269
- <TableHead>Slug</TableHead>
270
- <TableHead>{t('columnProvider')}</TableHead>
271
- <TableHead>{t('columnModel')}</TableHead>
272
- <TableHead>{t('columnExternalId')}</TableHead>
273
- <TableHead className="text-right">
274
- {t('columnActions')}
275
- </TableHead>
276
- </TableRow>
277
- </TableHeader>
278
- <TableBody>
279
- {isLoading ? (
265
+ {isLoading || agentList.data.length > 0 ? (
266
+ <Card>
267
+ <CardContent className="p-0">
268
+ <Table>
269
+ <TableHeader>
280
270
  <TableRow>
281
- <TableCell colSpan={5}>{t('loading')}</TableCell>
271
+ <TableHead>Slug</TableHead>
272
+ <TableHead>{t('columnProvider')}</TableHead>
273
+ <TableHead>{t('columnModel')}</TableHead>
274
+ <TableHead>{t('columnExternalId')}</TableHead>
275
+ <TableHead className="text-right">
276
+ {t('columnActions')}
277
+ </TableHead>
282
278
  </TableRow>
283
- ) : agentList.data.length === 0 ? (
284
- <TableRow>
285
- <TableCell colSpan={5}>{t('empty')}</TableCell>
286
- </TableRow>
287
- ) : (
288
- agentList.data.map((agent) => (
289
- <TableRow key={agent.id}>
290
- <TableCell>{agent.slug}</TableCell>
291
- <TableCell className="uppercase">
292
- {agent.provider}
293
- </TableCell>
294
- <TableCell>{agent.model || '-'}</TableCell>
295
- <TableCell>{agent.external_agent_id || '-'}</TableCell>
296
- <TableCell className="text-right">
297
- <div className="flex items-center justify-end gap-2">
298
- <Button
299
- variant="outline"
300
- size="icon"
301
- onClick={() => handleOpenEdit(agent)}
302
- >
303
- <Pencil className="h-4 w-4" />
304
- </Button>
305
- <Button
306
- variant="destructive"
307
- size="icon"
308
- onClick={() => setAgentToDelete(agent)}
309
- >
310
- <Trash2 className="h-4 w-4" />
311
- </Button>
312
- </div>
313
- </TableCell>
279
+ </TableHeader>
280
+ <TableBody>
281
+ {isLoading ? (
282
+ <TableRow>
283
+ <TableCell colSpan={5}>{t('loading')}</TableCell>
314
284
  </TableRow>
315
- ))
316
- )}
317
- </TableBody>
318
- </Table>
319
- </CardContent>
320
- </Card>
285
+ ) : (
286
+ agentList.data.map((agent) => (
287
+ <TableRow key={agent.id}>
288
+ <TableCell>{agent.slug}</TableCell>
289
+ <TableCell className="uppercase">
290
+ {agent.provider}
291
+ </TableCell>
292
+ <TableCell>{agent.model || '-'}</TableCell>
293
+ <TableCell>{agent.external_agent_id || '-'}</TableCell>
294
+ <TableCell className="text-right">
295
+ <div className="flex items-center justify-end gap-2">
296
+ <Button
297
+ variant="outline"
298
+ size="icon"
299
+ onClick={() => handleOpenEdit(agent)}
300
+ >
301
+ <Pencil className="h-4 w-4" />
302
+ </Button>
303
+ <Button
304
+ variant="destructive"
305
+ size="icon"
306
+ onClick={() => setAgentToDelete(agent)}
307
+ >
308
+ <Trash2 className="h-4 w-4" />
309
+ </Button>
310
+ </div>
311
+ </TableCell>
312
+ </TableRow>
313
+ ))
314
+ )}
315
+ </TableBody>
316
+ </Table>
317
+ </CardContent>
318
+ </Card>
319
+ ) : (
320
+ <EmptyState
321
+ icon={<Bot className="h-12 w-12" />}
322
+ title={t('empty')}
323
+ description={t('description')}
324
+ actionLabel={t('newAgent')}
325
+ onAction={handleOpenCreate}
326
+ />
327
+ )}
321
328
 
322
329
  <PaginationFooter
323
330
  currentPage={page}
324
331
  pageSize={pageSize}
325
- totalItems={agentList.total}
326
- onPageChange={setPage}
332
+ totalItems={agentList.total}
333
+ onPageChange={setPage}
327
334
  onPageSizeChange={(value) => {
328
335
  setPageSize(value);
329
336
  setPage(1);
@@ -50,98 +50,7 @@ export const SettingField = ({ setting, className }: SettingFieldProps) => {
50
50
 
51
51
  const newSettings = response.data?.setting || {};
52
52
  localStorage.setItem('settings', JSON.stringify(newSettings));
53
-
54
- const root = document.documentElement;
55
- const themeMode = newSettings['theme-mode'] || 'system';
56
- let currentTheme: 'light' | 'dark' = 'light';
57
-
58
- if (themeMode === 'system') {
59
- const prefersDark = window.matchMedia(
60
- '(prefers-color-scheme: dark)'
61
- ).matches;
62
- currentTheme = prefersDark ? 'dark' : 'light';
63
- } else {
64
- currentTheme = themeMode as 'light' | 'dark';
65
- }
66
-
67
- root.classList.remove('light', 'dark');
68
- root.classList.add(currentTheme);
69
-
70
- const existingStyle = document.getElementById('theme-custom-styles');
71
- if (existingStyle) {
72
- existingStyle.remove();
73
- }
74
-
75
- const styleTag = document.createElement('style');
76
- styleTag.id = 'theme-custom-styles';
77
- let cssRules = '';
78
-
79
- const addRule = (varName: string, settingKey: string) => {
80
- const hexColor = newSettings[settingKey];
81
- if (
82
- hexColor &&
83
- typeof hexColor === 'string' &&
84
- hexColor.startsWith('#')
85
- ) {
86
- cssRules += ` ${varName}: ${hexColor} !important;\n`;
87
- }
88
- };
89
-
90
- if (currentTheme === 'light') {
91
- cssRules += '@supports (color: lab(0% 0 0)) {\n';
92
- cssRules += ' html.light, .light, :root.light {\n';
93
- addRule('--primary', 'theme-primary-light');
94
- addRule('--bprogress-color', 'theme-primary-light');
95
- addRule('--primary-foreground', 'theme-primary-foreground-light');
96
- addRule('--secondary', 'theme-secondary-light');
97
- addRule('--secondary-foreground', 'theme-secondary-foreground-light');
98
- addRule('--accent', 'theme-accent-light');
99
- addRule('--accent-foreground', 'theme-accent-foreground-light');
100
- addRule('--muted', 'theme-muted-light');
101
- addRule('--muted-foreground', 'theme-muted-foreground-light');
102
- addRule('--background', 'theme-background-light');
103
- addRule('--foreground', 'theme-background-foreground-light');
104
- addRule('--card', 'theme-card-light');
105
- addRule('--card-foreground', 'theme-card-foreground-light');
106
- } else {
107
- cssRules += '@supports (color: lab(0% 0 0)) {\n';
108
- cssRules += ' html.dark, .dark, :root.dark {\n';
109
- addRule('--primary', 'theme-primary-dark');
110
- addRule('--bprogress-color', 'theme-primary-dark');
111
- addRule('--primary-foreground', 'theme-primary-foreground-dark');
112
- addRule('--secondary', 'theme-secondary-dark');
113
- addRule('--secondary-foreground', 'theme-secondary-foreground-dark');
114
- addRule('--accent', 'theme-accent-dark');
115
- addRule('--accent-foreground', 'theme-accent-foreground-dark');
116
- addRule('--muted', 'theme-muted-dark');
117
- addRule('--muted-foreground', 'theme-muted-foreground-dark');
118
- addRule('--background', 'theme-background-dark');
119
- addRule('--foreground', 'theme-background-foreground-dark');
120
- addRule('--card', 'theme-card-dark');
121
- addRule('--card-foreground', 'theme-card-foreground-dark');
122
- }
123
-
124
- if (newSettings['theme-radius']) {
125
- cssRules += ` --radius: ${newSettings['theme-radius']}rem !important;\n`;
126
- }
127
- if (newSettings['theme-text-size']) {
128
- cssRules += ` font-size: ${newSettings['theme-text-size']}rem !important;\n`;
129
- }
130
-
131
- cssRules += ' }\n';
132
- cssRules += '}\n';
133
-
134
- if (newSettings['theme-font']) {
135
- cssRules += `:root {\n`;
136
- cssRules += ` --font-sans: ${newSettings['theme-font']} !important;\n`;
137
- cssRules += `}\n`;
138
- cssRules += `html {\n`;
139
- cssRules += ` font-family: ${newSettings['theme-font']} !important;\n`;
140
- cssRules += `}\n`;
141
- }
142
-
143
- styleTag.textContent = cssRules;
144
- document.head.appendChild(styleTag);
53
+ window.dispatchEvent(new CustomEvent('hedhog:settings-change'));
145
54
  } catch (error) {
146
55
  console.error('Error applying theme settings:', error);
147
56
  }
@@ -12,10 +12,10 @@ export default function DashboardRedirectPage() {
12
12
  const { data: dashboardData, isLoading } = useQuery<Dashboard | null>({
13
13
  queryKey: ['dashboard-home-redirect', currentLocaleCode],
14
14
  queryFn: async () => {
15
- const response = await request<Dashboard | null>({
15
+ const response = await request<Dashboard>({
16
16
  url: '/dashboard-core/home',
17
17
  });
18
- return response.data;
18
+ return response.data ?? null;
19
19
  },
20
20
  });
21
21
 
@@ -1,6 +1,12 @@
1
1
  'use client';
2
2
 
3
- import { PageHeader, PaginationFooter } from '@/components/entity-list';
3
+ import {
4
+ EmptyState,
5
+ Page,
6
+ PageHeader,
7
+ PaginationFooter,
8
+ SearchBar,
9
+ } from '@/components/entity-list';
4
10
  import { Badge } from '@/components/ui/badge';
5
11
  import { Button } from '@/components/ui/button';
6
12
  import { Card, CardContent } from '@/components/ui/card';
@@ -11,14 +17,13 @@ import {
11
17
  DialogHeader,
12
18
  DialogTitle,
13
19
  } from '@/components/ui/dialog';
14
- import { Input } from '@/components/ui/input';
15
20
  import { ScrollArea } from '@/components/ui/scroll-area';
16
21
  import { useDebounce } from '@/hooks/use-debounce';
17
22
  import { formatDate } from '@/lib/format-date';
18
23
  import { useApp, useQuery } from '@hed-hog/next-app-provider';
19
- import { Clock, Mail, Search, User } from 'lucide-react';
24
+ import { Clock, Mail, User } from 'lucide-react';
20
25
  import { useTranslations } from 'next-intl';
21
- import { useEffect, useState } from 'react';
26
+ import { useState } from 'react';
22
27
 
23
28
  type PaginationResult<T> = {
24
29
  data: T[];
@@ -42,7 +47,6 @@ type MailSent = {
42
47
 
43
48
  export default function MailLogPage() {
44
49
  const t = useTranslations('core.MailLog');
45
- const [logs, setLogs] = useState<MailSent[]>([]);
46
50
  const [selectedLog, setSelectedLog] = useState<MailSent | null>(null);
47
51
  const [isDetailDialogOpen, setIsDetailDialogOpen] = useState(false);
48
52
  const [searchTerm, setSearchTerm] = useState('');
@@ -51,9 +55,9 @@ export default function MailLogPage() {
51
55
  const [pageSize, setPageSize] = useState(10);
52
56
  const { request, getSettingValue } = useApp();
53
57
 
54
- const { data: logsResult, refetch: refetchLogs } = useQuery<
55
- PaginationResult<MailSent>
56
- >({
58
+ const { data: logsResult, refetch: refetchLogs } = useQuery<
59
+ PaginationResult<MailSent>
60
+ >({
57
61
  queryKey: ['mail-sent', debouncedSearch, page, pageSize],
58
62
  queryFn: async () => {
59
63
  const response = await request({
@@ -66,14 +70,8 @@ export default function MailLogPage() {
66
70
  });
67
71
  return response.data as PaginationResult<MailSent>;
68
72
  },
69
- });
70
- const { data = [], total = 0 } = logsResult ?? {};
71
-
72
- useEffect(() => {
73
- if (data) {
74
- setLogs(data);
75
- }
76
- }, [data]);
73
+ });
74
+ const { data: logs = [], total = 0 } = logsResult ?? {};
77
75
 
78
76
  const handleViewDetails = (log: MailSent): void => {
79
77
  setSelectedLog(log);
@@ -92,12 +90,8 @@ export default function MailLogPage() {
92
90
  : text;
93
91
  };
94
92
 
95
- useEffect(() => {
96
- refetchLogs();
97
- }, [debouncedSearch, page, pageSize]);
98
-
99
93
  return (
100
- <div className="flex flex-col h-screen px-4">
94
+ <Page>
101
95
  <PageHeader
102
96
  breadcrumbs={[
103
97
  { label: t('breadcrumbHome'), href: '/' },
@@ -141,29 +135,30 @@ export default function MailLogPage() {
141
135
  </Card>
142
136
  </div>
143
137
 
144
- <div className="relative my-4">
145
- <Search className="absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground" />
146
- <Input
147
- placeholder={t('searchPlaceholder')}
148
- value={searchTerm}
149
- onChange={(e) => handleSearchChange(e.target.value)}
150
- className="pl-10"
151
- />
152
- </div>
138
+ <SearchBar
139
+ searchQuery={searchTerm}
140
+ onSearchChange={handleSearchChange}
141
+ onSearch={() => setPage(1)}
142
+ placeholder={t('searchPlaceholder')}
143
+ />
153
144
 
154
145
  <div className="space-y-3 mb-4">
155
146
  {logs.length === 0 ? (
156
- <Card>
157
- <CardContent className="flex flex-col items-center justify-center p-12">
158
- <Mail className="mb-4 h-12 w-12 text-muted-foreground" />
159
- <p className="text-lg font-medium text-muted-foreground">
160
- {t('noLogsFound')}
161
- </p>
162
- <p className="text-sm text-muted-foreground">
163
- {searchTerm ? t('adjustSearch') : t('noEmailsSent')}
164
- </p>
165
- </CardContent>
166
- </Card>
147
+ <EmptyState
148
+ icon={<Mail className="h-12 w-12" />}
149
+ title={t('noLogsFound')}
150
+ description={searchTerm ? t('adjustSearch') : t('noEmailsSent')}
151
+ actionLabel={searchTerm ? t('clearSearch') : t('refreshList')}
152
+ onAction={() => {
153
+ if (searchTerm) {
154
+ setSearchTerm('');
155
+ setPage(1);
156
+ return;
157
+ }
158
+
159
+ refetchLogs();
160
+ }}
161
+ />
167
162
  ) : (
168
163
  logs.map((log) => (
169
164
  <Card
@@ -301,6 +296,6 @@ export default function MailLogPage() {
301
296
  </DialogContent>
302
297
  </Dialog>
303
298
  )}
304
- </div>
299
+ </Page>
305
300
  );
306
301
  }