@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.
Files changed (118) hide show
  1. package/README.md +84 -36
  2. package/dist/auth/auth.controller.d.ts +0 -3
  3. package/dist/auth/auth.controller.d.ts.map +1 -1
  4. package/dist/auth/auth.controller.js +4 -7
  5. package/dist/auth/auth.controller.js.map +1 -1
  6. package/dist/auth/auth.service.d.ts.map +1 -1
  7. package/dist/auth/auth.service.js +6 -3
  8. package/dist/auth/auth.service.js.map +1 -1
  9. package/dist/dashboard/dashboard-core/dashboard-core.controller.d.ts +9 -0
  10. package/dist/dashboard/dashboard-core/dashboard-core.controller.d.ts.map +1 -1
  11. package/dist/dashboard/dashboard-core/dashboard-core.controller.js +12 -0
  12. package/dist/dashboard/dashboard-core/dashboard-core.controller.js.map +1 -1
  13. package/dist/dashboard/dashboard-core/dashboard-core.service.d.ts +7 -0
  14. package/dist/dashboard/dashboard-core/dashboard-core.service.d.ts.map +1 -1
  15. package/dist/dashboard/dashboard-core/dashboard-core.service.js +45 -1
  16. package/dist/dashboard/dashboard-core/dashboard-core.service.js.map +1 -1
  17. package/dist/index.d.ts +1 -0
  18. package/dist/index.d.ts.map +1 -1
  19. package/dist/index.js +2 -0
  20. package/dist/index.js.map +1 -1
  21. package/dist/mail-sent/dto/query-mail-sent-list.dto.d.ts +9 -0
  22. package/dist/mail-sent/dto/query-mail-sent-list.dto.d.ts.map +1 -0
  23. package/dist/mail-sent/dto/query-mail-sent-list.dto.js +49 -0
  24. package/dist/mail-sent/dto/query-mail-sent-list.dto.js.map +1 -0
  25. package/dist/mail-sent/mail-sent.controller.d.ts +24 -2
  26. package/dist/mail-sent/mail-sent.controller.d.ts.map +1 -1
  27. package/dist/mail-sent/mail-sent.controller.js +5 -3
  28. package/dist/mail-sent/mail-sent.controller.js.map +1 -1
  29. package/dist/mail-sent/mail-sent.service.d.ts +28 -3
  30. package/dist/mail-sent/mail-sent.service.d.ts.map +1 -1
  31. package/dist/mail-sent/mail-sent.service.js +108 -3
  32. package/dist/mail-sent/mail-sent.service.js.map +1 -1
  33. package/dist/oauth/oauth-callback-coordinator.service.d.ts +18 -0
  34. package/dist/oauth/oauth-callback-coordinator.service.d.ts.map +1 -0
  35. package/dist/oauth/oauth-callback-coordinator.service.js +137 -0
  36. package/dist/oauth/oauth-callback-coordinator.service.js.map +1 -0
  37. package/dist/oauth/oauth.controller.d.ts +4 -2
  38. package/dist/oauth/oauth.controller.d.ts.map +1 -1
  39. package/dist/oauth/oauth.controller.js +39 -11
  40. package/dist/oauth/oauth.controller.js.map +1 -1
  41. package/dist/oauth/oauth.errors.d.ts +16 -0
  42. package/dist/oauth/oauth.errors.d.ts.map +1 -0
  43. package/dist/oauth/oauth.errors.js +30 -0
  44. package/dist/oauth/oauth.errors.js.map +1 -0
  45. package/dist/oauth/oauth.module.d.ts.map +1 -1
  46. package/dist/oauth/oauth.module.js +10 -1
  47. package/dist/oauth/oauth.module.js.map +1 -1
  48. package/dist/oauth/oauth.service.d.ts +9 -1
  49. package/dist/oauth/oauth.service.d.ts.map +1 -1
  50. package/dist/oauth/oauth.service.js +97 -24
  51. package/dist/oauth/oauth.service.js.map +1 -1
  52. package/dist/oauth/providers/abstract.provider.d.ts +6 -0
  53. package/dist/oauth/providers/abstract.provider.d.ts.map +1 -1
  54. package/dist/oauth/providers/abstract.provider.js +34 -0
  55. package/dist/oauth/providers/abstract.provider.js.map +1 -1
  56. package/dist/oauth/providers/microsoft-entra-id.provider.d.ts +3 -0
  57. package/dist/oauth/providers/microsoft-entra-id.provider.d.ts.map +1 -1
  58. package/dist/oauth/providers/microsoft-entra-id.provider.js +40 -22
  59. package/dist/oauth/providers/microsoft-entra-id.provider.js.map +1 -1
  60. package/dist/security/security.service.d.ts +1 -0
  61. package/dist/security/security.service.d.ts.map +1 -1
  62. package/dist/security/security.service.js +9 -10
  63. package/dist/security/security.service.js.map +1 -1
  64. package/dist/session/session.service.d.ts +5 -1
  65. package/dist/session/session.service.d.ts.map +1 -1
  66. package/dist/session/session.service.js +76 -10
  67. package/dist/session/session.service.js.map +1 -1
  68. package/dist/setting/setting.service.js +4 -4
  69. package/dist/setting/setting.service.js.map +1 -1
  70. package/dist/token/token.service.d.ts +1 -0
  71. package/dist/token/token.service.d.ts.map +1 -1
  72. package/dist/token/token.service.js +23 -12
  73. package/dist/token/token.service.js.map +1 -1
  74. package/dist/user/user.service.d.ts +1 -1
  75. package/dist/user/user.service.d.ts.map +1 -1
  76. package/dist/user/user.service.js +3 -0
  77. package/dist/user/user.service.js.map +1 -1
  78. package/hedhog/data/dashboard.yaml +2 -2
  79. package/hedhog/data/mail.yaml +147 -0
  80. package/hedhog/data/route.yaml +8 -0
  81. package/hedhog/frontend/app/account/components/change-email-form.tsx.ejs +87 -8
  82. package/hedhog/frontend/app/account/components/profile-form.tsx.ejs +90 -10
  83. package/hedhog/frontend/app/ai_agent/page.tsx.ejs +145 -31
  84. package/hedhog/frontend/app/configurations/layout.tsx.ejs +1 -1
  85. package/hedhog/frontend/app/dashboard/[slug]/dashboard-content.tsx.ejs +67 -2
  86. package/hedhog/frontend/app/dashboard/components/add-widget-selector-dialog.tsx.ejs +13 -9
  87. package/hedhog/frontend/app/dashboard/dashboard-home-tabs.tsx.ejs +250 -45
  88. package/hedhog/frontend/app/dashboard/management/tabs/component-roles-tab.tsx.ejs +111 -15
  89. package/hedhog/frontend/app/dashboard/management/tabs/dashboard-roles-tab.tsx.ejs +110 -15
  90. package/hedhog/frontend/app/dashboard/management/tabs/items-tab.tsx.ejs +113 -15
  91. package/hedhog/frontend/app/mail/log/page.tsx.ejs +481 -70
  92. package/hedhog/frontend/app/menu/page.tsx.ejs +202 -30
  93. package/hedhog/frontend/app/roles/menus.tsx.ejs +1 -1
  94. package/hedhog/frontend/app/roles/page.tsx.ejs +288 -104
  95. package/hedhog/frontend/app/users/page.tsx.ejs +168 -23
  96. package/hedhog/frontend/messages/en.json +45 -6
  97. package/hedhog/frontend/messages/pt.json +45 -6
  98. package/package.json +4 -4
  99. package/src/auth/auth.controller.ts +15 -19
  100. package/src/auth/auth.service.ts +13 -8
  101. package/src/dashboard/dashboard-core/dashboard-core.controller.ts +22 -9
  102. package/src/dashboard/dashboard-core/dashboard-core.service.ts +75 -1
  103. package/src/index.ts +10 -7
  104. package/src/mail-sent/dto/query-mail-sent-list.dto.ts +30 -0
  105. package/src/mail-sent/mail-sent.controller.ts +14 -2
  106. package/src/mail-sent/mail-sent.service.ts +159 -4
  107. package/src/oauth/oauth-callback-coordinator.service.ts +158 -0
  108. package/src/oauth/oauth.controller.ts +58 -1
  109. package/src/oauth/oauth.errors.ts +32 -0
  110. package/src/oauth/oauth.module.ts +10 -1
  111. package/src/oauth/oauth.service.ts +178 -28
  112. package/src/oauth/providers/abstract.provider.ts +64 -0
  113. package/src/oauth/providers/microsoft-entra-id.provider.ts +92 -56
  114. package/src/security/security.service.ts +12 -10
  115. package/src/session/session.service.ts +84 -9
  116. package/src/setting/setting.service.ts +10 -10
  117. package/src/token/token.service.ts +29 -11
  118. 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 [searchQuery, setSearchQuery] = useState('');
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<any>({
182
+ const { data: dashboards } = useQuery<DashboardListResponse>({
157
183
  queryKey: ['dashboards-list'],
158
184
  queryFn: async () => {
159
- const { data } = await request<Dashboard[]>({
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<any>({
193
+ const { data: availableRoles } = useQuery<RoleListResponse>({
168
194
  queryKey: ['roles'],
169
195
  queryFn: async () => {
170
- const { data } = await request<Role[]>({
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: any) {
288
+ } catch (error: unknown) {
201
289
  console.error('Erro ao adicionar permissão:', error);
202
- if (error?.response?.data?.message?.includes('already exists')) {
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
- onValueChange={field.onChange}
277
- defaultValue={String(field.value)}
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
- onValueChange={field.onChange}
315
- defaultValue={String(field.value)}
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-[100px]">{t('actions')}</TableHead>
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 [searchQuery, setSearchQuery] = useState('');
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<any>({
180
+ const { data: dashboards } = useQuery<DashboardListResponse>({
163
181
  queryKey: ['dashboards'],
164
182
  queryFn: async () => {
165
- const { data } = await request<Dashboard[]>({
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<any>({
191
+ const { data: components } = useQuery<ComponentListResponse>({
174
192
  queryKey: ['dashboard-components'],
175
193
  queryFn: async () => {
176
- const { data } = await request<Component[]>({
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
- onValueChange={field.onChange}
281
- defaultValue={field.value?.toString()}
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.data ?? []).map(
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
- onValueChange={field.onChange}
319
- defaultValue={field.value?.toString()}
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.data ?? []).map(
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-[100px]">{t('actions')}</TableHead>
561
+ <TableHead className="w-25">{t('actions')}</TableHead>
464
562
  </TableRow>
465
563
  </TableHeader>
466
564
  <TableBody>