@hed-hog/core 0.0.303 → 0.0.305
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.
- package/README.md +84 -36
- package/dist/auth/auth.controller.d.ts +0 -3
- package/dist/auth/auth.controller.d.ts.map +1 -1
- package/dist/auth/auth.controller.js +4 -7
- package/dist/auth/auth.controller.js.map +1 -1
- package/dist/auth/auth.service.d.ts.map +1 -1
- package/dist/auth/auth.service.js +6 -3
- package/dist/auth/auth.service.js.map +1 -1
- package/dist/dashboard/dashboard-core/dashboard-core.controller.d.ts +9 -0
- package/dist/dashboard/dashboard-core/dashboard-core.controller.d.ts.map +1 -1
- package/dist/dashboard/dashboard-core/dashboard-core.controller.js +12 -0
- package/dist/dashboard/dashboard-core/dashboard-core.controller.js.map +1 -1
- package/dist/dashboard/dashboard-core/dashboard-core.service.d.ts +7 -0
- package/dist/dashboard/dashboard-core/dashboard-core.service.d.ts.map +1 -1
- package/dist/dashboard/dashboard-core/dashboard-core.service.js +45 -1
- package/dist/dashboard/dashboard-core/dashboard-core.service.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/mail-sent/dto/query-mail-sent-list.dto.d.ts +9 -0
- package/dist/mail-sent/dto/query-mail-sent-list.dto.d.ts.map +1 -0
- package/dist/mail-sent/dto/query-mail-sent-list.dto.js +49 -0
- package/dist/mail-sent/dto/query-mail-sent-list.dto.js.map +1 -0
- package/dist/mail-sent/mail-sent.controller.d.ts +24 -2
- package/dist/mail-sent/mail-sent.controller.d.ts.map +1 -1
- package/dist/mail-sent/mail-sent.controller.js +5 -3
- package/dist/mail-sent/mail-sent.controller.js.map +1 -1
- package/dist/mail-sent/mail-sent.service.d.ts +28 -3
- package/dist/mail-sent/mail-sent.service.d.ts.map +1 -1
- package/dist/mail-sent/mail-sent.service.js +108 -3
- package/dist/mail-sent/mail-sent.service.js.map +1 -1
- package/dist/oauth/oauth-callback-coordinator.service.d.ts +18 -0
- package/dist/oauth/oauth-callback-coordinator.service.d.ts.map +1 -0
- package/dist/oauth/oauth-callback-coordinator.service.js +137 -0
- package/dist/oauth/oauth-callback-coordinator.service.js.map +1 -0
- package/dist/oauth/oauth.controller.d.ts +4 -2
- package/dist/oauth/oauth.controller.d.ts.map +1 -1
- package/dist/oauth/oauth.controller.js +39 -11
- package/dist/oauth/oauth.controller.js.map +1 -1
- package/dist/oauth/oauth.errors.d.ts +16 -0
- package/dist/oauth/oauth.errors.d.ts.map +1 -0
- package/dist/oauth/oauth.errors.js +30 -0
- package/dist/oauth/oauth.errors.js.map +1 -0
- package/dist/oauth/oauth.module.d.ts.map +1 -1
- package/dist/oauth/oauth.module.js +10 -1
- package/dist/oauth/oauth.module.js.map +1 -1
- package/dist/oauth/oauth.service.d.ts +9 -1
- package/dist/oauth/oauth.service.d.ts.map +1 -1
- package/dist/oauth/oauth.service.js +97 -24
- package/dist/oauth/oauth.service.js.map +1 -1
- package/dist/oauth/providers/abstract.provider.d.ts +6 -0
- package/dist/oauth/providers/abstract.provider.d.ts.map +1 -1
- package/dist/oauth/providers/abstract.provider.js +34 -0
- package/dist/oauth/providers/abstract.provider.js.map +1 -1
- package/dist/oauth/providers/microsoft-entra-id.provider.d.ts +3 -0
- package/dist/oauth/providers/microsoft-entra-id.provider.d.ts.map +1 -1
- package/dist/oauth/providers/microsoft-entra-id.provider.js +40 -22
- package/dist/oauth/providers/microsoft-entra-id.provider.js.map +1 -1
- package/dist/security/security.service.d.ts +1 -0
- package/dist/security/security.service.d.ts.map +1 -1
- package/dist/security/security.service.js +9 -10
- package/dist/security/security.service.js.map +1 -1
- package/dist/session/session.service.d.ts +5 -1
- package/dist/session/session.service.d.ts.map +1 -1
- package/dist/session/session.service.js +76 -10
- package/dist/session/session.service.js.map +1 -1
- package/dist/setting/setting.service.js +4 -4
- package/dist/setting/setting.service.js.map +1 -1
- package/dist/token/token.service.d.ts +1 -0
- package/dist/token/token.service.d.ts.map +1 -1
- package/dist/token/token.service.js +23 -12
- package/dist/token/token.service.js.map +1 -1
- package/dist/user/user.service.d.ts +1 -1
- package/dist/user/user.service.d.ts.map +1 -1
- package/dist/user/user.service.js +3 -0
- package/dist/user/user.service.js.map +1 -1
- package/hedhog/data/dashboard.yaml +2 -2
- package/hedhog/data/mail.yaml +147 -0
- package/hedhog/data/route.yaml +8 -0
- package/hedhog/frontend/app/account/components/change-email-form.tsx.ejs +87 -8
- package/hedhog/frontend/app/account/components/profile-form.tsx.ejs +90 -10
- package/hedhog/frontend/app/ai_agent/page.tsx.ejs +145 -31
- package/hedhog/frontend/app/configurations/layout.tsx.ejs +1 -1
- package/hedhog/frontend/app/dashboard/[slug]/dashboard-content.tsx.ejs +67 -2
- package/hedhog/frontend/app/dashboard/components/add-widget-selector-dialog.tsx.ejs +13 -9
- package/hedhog/frontend/app/dashboard/dashboard-home-tabs.tsx.ejs +250 -45
- package/hedhog/frontend/app/dashboard/management/tabs/component-roles-tab.tsx.ejs +111 -15
- package/hedhog/frontend/app/dashboard/management/tabs/dashboard-roles-tab.tsx.ejs +110 -15
- package/hedhog/frontend/app/dashboard/management/tabs/items-tab.tsx.ejs +113 -15
- package/hedhog/frontend/app/mail/log/page.tsx.ejs +481 -70
- package/hedhog/frontend/app/menu/page.tsx.ejs +202 -30
- package/hedhog/frontend/app/roles/menus.tsx.ejs +1 -1
- package/hedhog/frontend/app/roles/page.tsx.ejs +288 -104
- package/hedhog/frontend/app/users/page.tsx.ejs +168 -23
- package/hedhog/frontend/messages/en.json +45 -6
- package/hedhog/frontend/messages/pt.json +45 -6
- package/package.json +4 -4
- package/src/auth/auth.controller.ts +15 -19
- package/src/auth/auth.service.ts +13 -8
- package/src/dashboard/dashboard-core/dashboard-core.controller.ts +22 -9
- package/src/dashboard/dashboard-core/dashboard-core.service.ts +75 -1
- package/src/index.ts +10 -7
- package/src/mail-sent/dto/query-mail-sent-list.dto.ts +30 -0
- package/src/mail-sent/mail-sent.controller.ts +14 -2
- package/src/mail-sent/mail-sent.service.ts +159 -4
- package/src/oauth/oauth-callback-coordinator.service.ts +158 -0
- package/src/oauth/oauth.controller.ts +58 -1
- package/src/oauth/oauth.errors.ts +32 -0
- package/src/oauth/oauth.module.ts +10 -1
- package/src/oauth/oauth.service.ts +178 -28
- package/src/oauth/providers/abstract.provider.ts +64 -0
- package/src/oauth/providers/microsoft-entra-id.provider.ts +92 -56
- package/src/security/security.service.ts +12 -10
- package/src/session/session.service.ts +84 -9
- package/src/setting/setting.service.ts +10 -10
- package/src/token/token.service.ts +29 -11
- package/src/user/user.service.ts +10 -6
|
@@ -45,6 +45,8 @@ import {
|
|
|
45
45
|
TableRow,
|
|
46
46
|
} from '@/components/ui/table';
|
|
47
47
|
import { useDebounce } from '@/hooks/use-debounce';
|
|
48
|
+
import { useFormDraft } from '@/hooks/use-form-draft';
|
|
49
|
+
import { formatDateTime } from '@/lib/format-date';
|
|
48
50
|
import { useApp, useQuery } from '@hed-hog/next-app-provider';
|
|
49
51
|
import { zodResolver } from '@hookform/resolvers/zod';
|
|
50
52
|
import {
|
|
@@ -55,9 +57,11 @@ import {
|
|
|
55
57
|
IconPlus,
|
|
56
58
|
IconTrash,
|
|
57
59
|
} from '@tabler/icons-react';
|
|
60
|
+
import { formatDistanceToNow } from 'date-fns';
|
|
61
|
+
import { enUS, ptBR } from 'date-fns/locale';
|
|
58
62
|
import { useTranslations } from 'next-intl';
|
|
59
|
-
import { useState } from 'react';
|
|
60
|
-
import { useForm } from 'react-hook-form';
|
|
63
|
+
import { useEffect, useMemo, useState } from 'react';
|
|
64
|
+
import { useForm, useWatch } from 'react-hook-form';
|
|
61
65
|
import { toast } from 'sonner';
|
|
62
66
|
import { z } from 'zod';
|
|
63
67
|
|
|
@@ -66,6 +70,12 @@ type RoleFormData = {
|
|
|
66
70
|
role_id: number;
|
|
67
71
|
};
|
|
68
72
|
|
|
73
|
+
type DashboardRoleDraftPayload = {
|
|
74
|
+
values: RoleFormData;
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
const DASHBOARD_ROLE_DRAFT_STORAGE_KEY = 'core-dashboard-role-form-draft';
|
|
78
|
+
|
|
69
79
|
interface DashboardRole {
|
|
70
80
|
id: number;
|
|
71
81
|
dashboard_id: number;
|
|
@@ -106,9 +116,25 @@ interface Role {
|
|
|
106
116
|
}>;
|
|
107
117
|
}
|
|
108
118
|
|
|
119
|
+
type DashboardListResponse = {
|
|
120
|
+
data?: Dashboard[];
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
type RoleListResponse = {
|
|
124
|
+
data?: Role[];
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
type RequestError = {
|
|
128
|
+
response?: {
|
|
129
|
+
data?: {
|
|
130
|
+
message?: string;
|
|
131
|
+
};
|
|
132
|
+
};
|
|
133
|
+
};
|
|
134
|
+
|
|
109
135
|
export function DashboardRolesTab() {
|
|
110
136
|
const t = useTranslations('core.DashboardManagement');
|
|
111
|
-
const { request, currentLocaleCode } = useApp();
|
|
137
|
+
const { request, currentLocaleCode, getSettingValue } = useApp();
|
|
112
138
|
|
|
113
139
|
const roleSchema = z.object({
|
|
114
140
|
dashboard_id: z.coerce.number().min(1, t('selectDashboardRequired')),
|
|
@@ -120,7 +146,7 @@ export function DashboardRolesTab() {
|
|
|
120
146
|
const [roleToDelete, setRoleToDelete] = useState<number | null>(null);
|
|
121
147
|
const [page, setPage] = useState(1);
|
|
122
148
|
const [pageSize, setPageSize] = useState(10);
|
|
123
|
-
const
|
|
149
|
+
const searchQuery = '';
|
|
124
150
|
const debouncedSearch = useDebounce(searchQuery, 500);
|
|
125
151
|
|
|
126
152
|
const {
|
|
@@ -153,10 +179,10 @@ export function DashboardRolesTab() {
|
|
|
153
179
|
const total = paginatedData?.total ?? 0;
|
|
154
180
|
const totalPages = Math.ceil(total / pageSize);
|
|
155
181
|
|
|
156
|
-
const { data: dashboards } = useQuery<
|
|
182
|
+
const { data: dashboards } = useQuery<DashboardListResponse>({
|
|
157
183
|
queryKey: ['dashboards-list'],
|
|
158
184
|
queryFn: async () => {
|
|
159
|
-
const { data } = await request<
|
|
185
|
+
const { data } = await request<DashboardListResponse>({
|
|
160
186
|
url: '/dashboard',
|
|
161
187
|
method: 'GET',
|
|
162
188
|
});
|
|
@@ -164,10 +190,10 @@ export function DashboardRolesTab() {
|
|
|
164
190
|
},
|
|
165
191
|
});
|
|
166
192
|
|
|
167
|
-
const { data: availableRoles } = useQuery<
|
|
193
|
+
const { data: availableRoles } = useQuery<RoleListResponse>({
|
|
168
194
|
queryKey: ['roles'],
|
|
169
195
|
queryFn: async () => {
|
|
170
|
-
const { data } = await request<
|
|
196
|
+
const { data } = await request<RoleListResponse>({
|
|
171
197
|
url: '/role',
|
|
172
198
|
method: 'GET',
|
|
173
199
|
});
|
|
@@ -183,6 +209,67 @@ export function DashboardRolesTab() {
|
|
|
183
209
|
},
|
|
184
210
|
});
|
|
185
211
|
|
|
212
|
+
const watchedValues = useWatch({
|
|
213
|
+
control: form.control,
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
const {
|
|
217
|
+
clearDraft,
|
|
218
|
+
loadDraft,
|
|
219
|
+
hasDraft,
|
|
220
|
+
savedAt: draftSavedAt,
|
|
221
|
+
} = useFormDraft<DashboardRoleDraftPayload>({
|
|
222
|
+
storageKey: DASHBOARD_ROLE_DRAFT_STORAGE_KEY,
|
|
223
|
+
value: {
|
|
224
|
+
values: {
|
|
225
|
+
dashboard_id: Number(watchedValues.dashboard_id ?? 0),
|
|
226
|
+
role_id: Number(watchedValues.role_id ?? 0),
|
|
227
|
+
},
|
|
228
|
+
},
|
|
229
|
+
hasData:
|
|
230
|
+
Number(watchedValues.dashboard_id ?? 0) > 0 ||
|
|
231
|
+
Number(watchedValues.role_id ?? 0) > 0,
|
|
232
|
+
enabled: open,
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
const draftStatusContent = useMemo(() => {
|
|
236
|
+
if (!hasDraft || !draftSavedAt) {
|
|
237
|
+
return null;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
const savedDate = new Date(draftSavedAt);
|
|
241
|
+
if (Number.isNaN(savedDate.getTime())) {
|
|
242
|
+
return null;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
const locale = currentLocaleCode.startsWith('pt') ? ptBR : enUS;
|
|
246
|
+
const relativeLabel = formatDistanceToNow(savedDate, {
|
|
247
|
+
addSuffix: true,
|
|
248
|
+
locale,
|
|
249
|
+
});
|
|
250
|
+
const absoluteLabel = formatDateTime(
|
|
251
|
+
savedDate,
|
|
252
|
+
getSettingValue,
|
|
253
|
+
currentLocaleCode
|
|
254
|
+
);
|
|
255
|
+
|
|
256
|
+
return currentLocaleCode.startsWith('pt')
|
|
257
|
+
? `Rascunho salvo ${relativeLabel} • Último salvamento: ${absoluteLabel}`
|
|
258
|
+
: `Draft saved ${relativeLabel} • Last saved: ${absoluteLabel}`;
|
|
259
|
+
}, [draftSavedAt, currentLocaleCode, getSettingValue, hasDraft]);
|
|
260
|
+
|
|
261
|
+
useEffect(() => {
|
|
262
|
+
if (!open) {
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
const storedDraft = loadDraft();
|
|
267
|
+
form.reset({
|
|
268
|
+
dashboard_id: storedDraft?.payload.values.dashboard_id ?? 0,
|
|
269
|
+
role_id: storedDraft?.payload.values.role_id ?? 0,
|
|
270
|
+
});
|
|
271
|
+
}, [form, loadDraft, open]);
|
|
272
|
+
|
|
186
273
|
const onSubmit = async (values: RoleFormData) => {
|
|
187
274
|
try {
|
|
188
275
|
await request({
|
|
@@ -194,12 +281,14 @@ export function DashboardRolesTab() {
|
|
|
194
281
|
},
|
|
195
282
|
});
|
|
196
283
|
toast.success(t('roleAdded'));
|
|
284
|
+
clearDraft();
|
|
197
285
|
setOpen(false);
|
|
198
286
|
form.reset();
|
|
199
287
|
refetch();
|
|
200
|
-
} catch (error:
|
|
288
|
+
} catch (error: unknown) {
|
|
201
289
|
console.error('Erro ao adicionar permissão:', error);
|
|
202
|
-
|
|
290
|
+
const message = (error as RequestError)?.response?.data?.message ?? '';
|
|
291
|
+
if (message.includes('already exists')) {
|
|
203
292
|
toast.error(t('roleAlreadyExists'));
|
|
204
293
|
} else {
|
|
205
294
|
toast.error(t('errorAddingRole'));
|
|
@@ -273,8 +362,8 @@ export function DashboardRolesTab() {
|
|
|
273
362
|
<FormItem>
|
|
274
363
|
<FormLabel>{t('dashboard')}</FormLabel>
|
|
275
364
|
<Select
|
|
276
|
-
|
|
277
|
-
|
|
365
|
+
value={field.value ? String(field.value) : undefined}
|
|
366
|
+
onValueChange={(value) => field.onChange(Number(value))}
|
|
278
367
|
>
|
|
279
368
|
<FormControl>
|
|
280
369
|
<SelectTrigger className="w-full">
|
|
@@ -311,8 +400,8 @@ export function DashboardRolesTab() {
|
|
|
311
400
|
<FormItem>
|
|
312
401
|
<FormLabel>{t('role')}</FormLabel>
|
|
313
402
|
<Select
|
|
314
|
-
|
|
315
|
-
|
|
403
|
+
value={field.value ? String(field.value) : undefined}
|
|
404
|
+
onValueChange={(value) => field.onChange(Number(value))}
|
|
316
405
|
>
|
|
317
406
|
<FormControl>
|
|
318
407
|
<SelectTrigger className="w-full">
|
|
@@ -337,6 +426,12 @@ export function DashboardRolesTab() {
|
|
|
337
426
|
)}
|
|
338
427
|
/>
|
|
339
428
|
|
|
429
|
+
{draftStatusContent ? (
|
|
430
|
+
<p className="text-xs text-muted-foreground">
|
|
431
|
+
{draftStatusContent}
|
|
432
|
+
</p>
|
|
433
|
+
) : null}
|
|
434
|
+
|
|
340
435
|
<DialogFooter>
|
|
341
436
|
<Button type="submit">{t('save')}</Button>
|
|
342
437
|
</DialogFooter>
|
|
@@ -353,7 +448,7 @@ export function DashboardRolesTab() {
|
|
|
353
448
|
<TableHead>ID</TableHead>
|
|
354
449
|
<TableHead>{t('dashboard')}</TableHead>
|
|
355
450
|
<TableHead>{t('role')}</TableHead>
|
|
356
|
-
<TableHead className="w-
|
|
451
|
+
<TableHead className="w-25">{t('actions')}</TableHead>
|
|
357
452
|
</TableRow>
|
|
358
453
|
</TableHeader>
|
|
359
454
|
<TableBody>
|
|
@@ -45,6 +45,8 @@ import {
|
|
|
45
45
|
TableRow,
|
|
46
46
|
} from '@/components/ui/table';
|
|
47
47
|
import { useDebounce } from '@/hooks/use-debounce';
|
|
48
|
+
import { useFormDraft } from '@/hooks/use-form-draft';
|
|
49
|
+
import { formatDateTime } from '@/lib/format-date';
|
|
48
50
|
import { useApp, useQuery } from '@hed-hog/next-app-provider';
|
|
49
51
|
import { zodResolver } from '@hookform/resolvers/zod';
|
|
50
52
|
import {
|
|
@@ -55,9 +57,11 @@ import {
|
|
|
55
57
|
IconPlus,
|
|
56
58
|
IconTrash,
|
|
57
59
|
} from '@tabler/icons-react';
|
|
60
|
+
import { formatDistanceToNow } from 'date-fns';
|
|
61
|
+
import { enUS, ptBR } from 'date-fns/locale';
|
|
58
62
|
import { useTranslations } from 'next-intl';
|
|
59
|
-
import { useState } from 'react';
|
|
60
|
-
import { useForm } from 'react-hook-form';
|
|
63
|
+
import { useEffect, useMemo, useState } from 'react';
|
|
64
|
+
import { useForm, useWatch } from 'react-hook-form';
|
|
61
65
|
import { toast } from 'sonner';
|
|
62
66
|
import { z } from 'zod';
|
|
63
67
|
|
|
@@ -70,6 +74,12 @@ type ItemFormData = {
|
|
|
70
74
|
y_axis: number;
|
|
71
75
|
};
|
|
72
76
|
|
|
77
|
+
type DashboardItemDraftPayload = {
|
|
78
|
+
values: ItemFormData;
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
const DASHBOARD_ITEM_DRAFT_STORAGE_KEY = 'core-dashboard-item-form-draft';
|
|
82
|
+
|
|
73
83
|
interface DashboardItem {
|
|
74
84
|
id: number;
|
|
75
85
|
dashboard_id: number;
|
|
@@ -108,9 +118,17 @@ interface Component {
|
|
|
108
118
|
}>;
|
|
109
119
|
}
|
|
110
120
|
|
|
121
|
+
type DashboardListResponse = {
|
|
122
|
+
data?: Dashboard[];
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
type ComponentListResponse = {
|
|
126
|
+
data?: Component[];
|
|
127
|
+
};
|
|
128
|
+
|
|
111
129
|
export function ItemsTab() {
|
|
112
130
|
const t = useTranslations('core.DashboardManagement');
|
|
113
|
-
const { request, currentLocaleCode } = useApp();
|
|
131
|
+
const { request, currentLocaleCode, getSettingValue } = useApp();
|
|
114
132
|
|
|
115
133
|
const itemSchema = z.object({
|
|
116
134
|
dashboard_id: z.coerce.number().min(1, t('selectDashboardRequired')),
|
|
@@ -126,7 +144,7 @@ export function ItemsTab() {
|
|
|
126
144
|
const [itemToDelete, setItemToDelete] = useState<number | null>(null);
|
|
127
145
|
const [page, setPage] = useState(1);
|
|
128
146
|
const [pageSize, setPageSize] = useState(10);
|
|
129
|
-
const
|
|
147
|
+
const searchQuery = '';
|
|
130
148
|
const debouncedSearch = useDebounce(searchQuery, 500);
|
|
131
149
|
|
|
132
150
|
const {
|
|
@@ -159,10 +177,10 @@ export function ItemsTab() {
|
|
|
159
177
|
const total = paginatedData?.total ?? 0;
|
|
160
178
|
const totalPages = Math.ceil(total / pageSize);
|
|
161
179
|
|
|
162
|
-
const { data: dashboards } = useQuery<
|
|
180
|
+
const { data: dashboards } = useQuery<DashboardListResponse>({
|
|
163
181
|
queryKey: ['dashboards'],
|
|
164
182
|
queryFn: async () => {
|
|
165
|
-
const { data } = await request<
|
|
183
|
+
const { data } = await request<DashboardListResponse>({
|
|
166
184
|
url: '/dashboard',
|
|
167
185
|
method: 'GET',
|
|
168
186
|
});
|
|
@@ -170,10 +188,10 @@ export function ItemsTab() {
|
|
|
170
188
|
},
|
|
171
189
|
});
|
|
172
190
|
|
|
173
|
-
const { data: components } = useQuery<
|
|
191
|
+
const { data: components } = useQuery<ComponentListResponse>({
|
|
174
192
|
queryKey: ['dashboard-components'],
|
|
175
193
|
queryFn: async () => {
|
|
176
|
-
const { data } = await request<
|
|
194
|
+
const { data } = await request<ComponentListResponse>({
|
|
177
195
|
url: '/dashboard-component',
|
|
178
196
|
method: 'GET',
|
|
179
197
|
});
|
|
@@ -193,6 +211,79 @@ export function ItemsTab() {
|
|
|
193
211
|
},
|
|
194
212
|
});
|
|
195
213
|
|
|
214
|
+
const watchedValues = useWatch({
|
|
215
|
+
control: form.control,
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
const {
|
|
219
|
+
clearDraft,
|
|
220
|
+
loadDraft,
|
|
221
|
+
hasDraft,
|
|
222
|
+
savedAt: draftSavedAt,
|
|
223
|
+
} = useFormDraft<DashboardItemDraftPayload>({
|
|
224
|
+
storageKey: DASHBOARD_ITEM_DRAFT_STORAGE_KEY,
|
|
225
|
+
value: {
|
|
226
|
+
values: {
|
|
227
|
+
dashboard_id: Number(watchedValues.dashboard_id ?? 0),
|
|
228
|
+
component_id: Number(watchedValues.component_id ?? 0),
|
|
229
|
+
width: Number(watchedValues.width ?? 3),
|
|
230
|
+
height: Number(watchedValues.height ?? 2),
|
|
231
|
+
x_axis: Number(watchedValues.x_axis ?? 0),
|
|
232
|
+
y_axis: Number(watchedValues.y_axis ?? 0),
|
|
233
|
+
},
|
|
234
|
+
},
|
|
235
|
+
hasData:
|
|
236
|
+
Number(watchedValues.dashboard_id ?? 0) > 0 ||
|
|
237
|
+
Number(watchedValues.component_id ?? 0) > 0 ||
|
|
238
|
+
Number(watchedValues.width ?? 3) !== 3 ||
|
|
239
|
+
Number(watchedValues.height ?? 2) !== 2 ||
|
|
240
|
+
Number(watchedValues.x_axis ?? 0) !== 0 ||
|
|
241
|
+
Number(watchedValues.y_axis ?? 0) !== 0,
|
|
242
|
+
enabled: open,
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
const draftStatusContent = useMemo(() => {
|
|
246
|
+
if (!hasDraft || !draftSavedAt) {
|
|
247
|
+
return null;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
const savedDate = new Date(draftSavedAt);
|
|
251
|
+
if (Number.isNaN(savedDate.getTime())) {
|
|
252
|
+
return null;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
const locale = currentLocaleCode.startsWith('pt') ? ptBR : enUS;
|
|
256
|
+
const relativeLabel = formatDistanceToNow(savedDate, {
|
|
257
|
+
addSuffix: true,
|
|
258
|
+
locale,
|
|
259
|
+
});
|
|
260
|
+
const absoluteLabel = formatDateTime(
|
|
261
|
+
savedDate,
|
|
262
|
+
getSettingValue,
|
|
263
|
+
currentLocaleCode
|
|
264
|
+
);
|
|
265
|
+
|
|
266
|
+
return currentLocaleCode.startsWith('pt')
|
|
267
|
+
? `Rascunho salvo ${relativeLabel} • Último salvamento: ${absoluteLabel}`
|
|
268
|
+
: `Draft saved ${relativeLabel} • Last saved: ${absoluteLabel}`;
|
|
269
|
+
}, [draftSavedAt, currentLocaleCode, getSettingValue, hasDraft]);
|
|
270
|
+
|
|
271
|
+
useEffect(() => {
|
|
272
|
+
if (!open) {
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
const storedDraft = loadDraft();
|
|
277
|
+
form.reset({
|
|
278
|
+
dashboard_id: storedDraft?.payload.values.dashboard_id ?? 0,
|
|
279
|
+
component_id: storedDraft?.payload.values.component_id ?? 0,
|
|
280
|
+
width: storedDraft?.payload.values.width ?? 3,
|
|
281
|
+
height: storedDraft?.payload.values.height ?? 2,
|
|
282
|
+
x_axis: storedDraft?.payload.values.x_axis ?? 0,
|
|
283
|
+
y_axis: storedDraft?.payload.values.y_axis ?? 0,
|
|
284
|
+
});
|
|
285
|
+
}, [form, loadDraft, open]);
|
|
286
|
+
|
|
196
287
|
const onSubmit = async (values: ItemFormData) => {
|
|
197
288
|
try {
|
|
198
289
|
await request({
|
|
@@ -202,6 +293,7 @@ export function ItemsTab() {
|
|
|
202
293
|
});
|
|
203
294
|
|
|
204
295
|
toast.success(t('itemAdded'));
|
|
296
|
+
clearDraft();
|
|
205
297
|
setOpen(false);
|
|
206
298
|
form.reset();
|
|
207
299
|
refetch();
|
|
@@ -277,8 +369,8 @@ export function ItemsTab() {
|
|
|
277
369
|
<FormItem>
|
|
278
370
|
<FormLabel>{t('dashboard')}</FormLabel>
|
|
279
371
|
<Select
|
|
280
|
-
|
|
281
|
-
|
|
372
|
+
value={field.value ? field.value.toString() : undefined}
|
|
373
|
+
onValueChange={(value) => field.onChange(Number(value))}
|
|
282
374
|
>
|
|
283
375
|
<FormControl>
|
|
284
376
|
<SelectTrigger className="w-full">
|
|
@@ -286,7 +378,7 @@ export function ItemsTab() {
|
|
|
286
378
|
</SelectTrigger>
|
|
287
379
|
</FormControl>
|
|
288
380
|
<SelectContent>
|
|
289
|
-
{(dashboards
|
|
381
|
+
{(dashboards?.data ?? []).map(
|
|
290
382
|
(dashboard: Dashboard) => {
|
|
291
383
|
const name =
|
|
292
384
|
dashboard.dashboard_locale.find(
|
|
@@ -315,8 +407,8 @@ export function ItemsTab() {
|
|
|
315
407
|
<FormItem>
|
|
316
408
|
<FormLabel>{t('component')}</FormLabel>
|
|
317
409
|
<Select
|
|
318
|
-
|
|
319
|
-
|
|
410
|
+
value={field.value ? field.value.toString() : undefined}
|
|
411
|
+
onValueChange={(value) => field.onChange(Number(value))}
|
|
320
412
|
>
|
|
321
413
|
<FormControl>
|
|
322
414
|
<SelectTrigger className="w-full">
|
|
@@ -324,7 +416,7 @@ export function ItemsTab() {
|
|
|
324
416
|
</SelectTrigger>
|
|
325
417
|
</FormControl>
|
|
326
418
|
<SelectContent>
|
|
327
|
-
{(components
|
|
419
|
+
{(components?.data ?? []).map(
|
|
328
420
|
(component: Component) => {
|
|
329
421
|
const name =
|
|
330
422
|
component.dashboard_component_locale.find(
|
|
@@ -444,6 +536,12 @@ export function ItemsTab() {
|
|
|
444
536
|
</p>
|
|
445
537
|
</div>
|
|
446
538
|
|
|
539
|
+
{draftStatusContent ? (
|
|
540
|
+
<p className="text-xs text-muted-foreground">
|
|
541
|
+
{draftStatusContent}
|
|
542
|
+
</p>
|
|
543
|
+
) : null}
|
|
544
|
+
|
|
447
545
|
<DialogFooter>
|
|
448
546
|
<Button type="submit">{t('save')}</Button>
|
|
449
547
|
</DialogFooter>
|
|
@@ -460,7 +558,7 @@ export function ItemsTab() {
|
|
|
460
558
|
<TableHead>{t('id')}</TableHead>
|
|
461
559
|
<TableHead>{t('dashboard')}</TableHead>
|
|
462
560
|
<TableHead>{t('component')}</TableHead>
|
|
463
|
-
<TableHead className="w-
|
|
561
|
+
<TableHead className="w-25">{t('actions')}</TableHead>
|
|
464
562
|
</TableRow>
|
|
465
563
|
</TableHeader>
|
|
466
564
|
<TableBody>
|