@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
|
@@ -53,12 +53,16 @@ import {
|
|
|
53
53
|
} from '@/components/ui/table';
|
|
54
54
|
import { Textarea } from '@/components/ui/textarea';
|
|
55
55
|
import { useDebounce } from '@/hooks/use-debounce';
|
|
56
|
+
import { useFormDraft } from '@/hooks/use-form-draft';
|
|
57
|
+
import { formatDateTime } from '@/lib/format-date';
|
|
56
58
|
import { useApp, useQuery } from '@hed-hog/next-app-provider';
|
|
57
59
|
import { zodResolver } from '@hookform/resolvers/zod';
|
|
60
|
+
import { formatDistanceToNow } from 'date-fns';
|
|
61
|
+
import { enUS, ptBR } from 'date-fns/locale';
|
|
58
62
|
import { Bot, Pencil, Plus, Trash2 } from 'lucide-react';
|
|
59
63
|
import { useTranslations } from 'next-intl';
|
|
60
|
-
import { useEffect, useState } from 'react';
|
|
61
|
-
import { useForm } from 'react-hook-form';
|
|
64
|
+
import { useEffect, useMemo, useState } from 'react';
|
|
65
|
+
import { useForm, useWatch } from 'react-hook-form';
|
|
62
66
|
import { toast } from 'sonner';
|
|
63
67
|
import { z } from 'zod';
|
|
64
68
|
|
|
@@ -88,8 +92,39 @@ type FormValues = {
|
|
|
88
92
|
instructions?: string;
|
|
89
93
|
};
|
|
90
94
|
|
|
95
|
+
type AgentDraftPayload = {
|
|
96
|
+
mode: 'create' | 'edit';
|
|
97
|
+
agentId: number | null;
|
|
98
|
+
values: FormValues;
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
const AI_AGENT_FORM_DRAFT_STORAGE_KEY = 'core-ai-agent-form-draft';
|
|
102
|
+
|
|
103
|
+
function getErrorMessage(error: unknown, fallback: string) {
|
|
104
|
+
if (error && typeof error === 'object') {
|
|
105
|
+
const record = error as {
|
|
106
|
+
message?: string;
|
|
107
|
+
response?: {
|
|
108
|
+
data?: {
|
|
109
|
+
message?: string;
|
|
110
|
+
error?: string;
|
|
111
|
+
};
|
|
112
|
+
};
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
return (
|
|
116
|
+
record.response?.data?.message ||
|
|
117
|
+
record.response?.data?.error ||
|
|
118
|
+
record.message ||
|
|
119
|
+
fallback
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return fallback;
|
|
124
|
+
}
|
|
125
|
+
|
|
91
126
|
export default function AiAgentPage() {
|
|
92
|
-
const { request } = useApp();
|
|
127
|
+
const { request, currentLocaleCode, getSettingValue } = useApp();
|
|
93
128
|
const t = useTranslations('core.AiAgentPage');
|
|
94
129
|
const formSchema = z.object({
|
|
95
130
|
slug: z.string().min(2, t('validationSlugMinLength')),
|
|
@@ -116,6 +151,62 @@ export default function AiAgentPage() {
|
|
|
116
151
|
},
|
|
117
152
|
});
|
|
118
153
|
|
|
154
|
+
const watchedValues = useWatch({
|
|
155
|
+
control: form.control,
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
const {
|
|
159
|
+
clearDraft,
|
|
160
|
+
loadDraft,
|
|
161
|
+
hasDraft,
|
|
162
|
+
savedAt: draftSavedAt,
|
|
163
|
+
} = useFormDraft<AgentDraftPayload>({
|
|
164
|
+
storageKey: AI_AGENT_FORM_DRAFT_STORAGE_KEY,
|
|
165
|
+
value: {
|
|
166
|
+
mode: editingAgent ? 'edit' : 'create',
|
|
167
|
+
agentId: editingAgent?.id ?? null,
|
|
168
|
+
values: {
|
|
169
|
+
slug: watchedValues.slug ?? '',
|
|
170
|
+
provider: watchedValues.provider ?? 'openai',
|
|
171
|
+
model: watchedValues.model ?? '',
|
|
172
|
+
instructions: watchedValues.instructions ?? '',
|
|
173
|
+
},
|
|
174
|
+
},
|
|
175
|
+
hasData: Boolean(
|
|
176
|
+
(watchedValues.slug ?? '').trim() ||
|
|
177
|
+
(watchedValues.model ?? '').trim() ||
|
|
178
|
+
(watchedValues.instructions ?? '').trim() ||
|
|
179
|
+
watchedValues.provider === 'gemini'
|
|
180
|
+
),
|
|
181
|
+
enabled: isDialogOpen,
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
const draftStatusContent = useMemo(() => {
|
|
185
|
+
if (!hasDraft || !draftSavedAt) {
|
|
186
|
+
return null;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
const savedDate = new Date(draftSavedAt);
|
|
190
|
+
if (Number.isNaN(savedDate.getTime())) {
|
|
191
|
+
return null;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
const locale = currentLocaleCode.startsWith('pt') ? ptBR : enUS;
|
|
195
|
+
const relativeLabel = formatDistanceToNow(savedDate, {
|
|
196
|
+
addSuffix: true,
|
|
197
|
+
locale,
|
|
198
|
+
});
|
|
199
|
+
const absoluteLabel = formatDateTime(
|
|
200
|
+
savedDate,
|
|
201
|
+
getSettingValue,
|
|
202
|
+
currentLocaleCode
|
|
203
|
+
);
|
|
204
|
+
|
|
205
|
+
return currentLocaleCode.startsWith('pt')
|
|
206
|
+
? `Rascunho salvo ${relativeLabel} • Último salvamento: ${absoluteLabel}`
|
|
207
|
+
: `Draft saved ${relativeLabel} • Last saved: ${absoluteLabel}`;
|
|
208
|
+
}, [draftSavedAt, currentLocaleCode, getSettingValue, hasDraft]);
|
|
209
|
+
|
|
119
210
|
const { data, isLoading, refetch } = useQuery<PaginatedResponse<Agent>>({
|
|
120
211
|
queryKey: ['ai-agents', page, pageSize, debouncedSearch],
|
|
121
212
|
queryFn: async () => {
|
|
@@ -141,23 +232,41 @@ export default function AiAgentPage() {
|
|
|
141
232
|
};
|
|
142
233
|
|
|
143
234
|
useEffect(() => {
|
|
235
|
+
if (!isDialogOpen) {
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
const storedDraft = loadDraft();
|
|
240
|
+
const shouldRestoreEditDraft =
|
|
241
|
+
Boolean(editingAgent) &&
|
|
242
|
+
storedDraft?.payload.mode === 'edit' &&
|
|
243
|
+
storedDraft.payload.agentId === editingAgent?.id;
|
|
244
|
+
|
|
144
245
|
if (editingAgent) {
|
|
145
|
-
form.reset(
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
246
|
+
form.reset(
|
|
247
|
+
shouldRestoreEditDraft
|
|
248
|
+
? storedDraft.payload.values
|
|
249
|
+
: {
|
|
250
|
+
slug: editingAgent.slug,
|
|
251
|
+
provider: editingAgent.provider,
|
|
252
|
+
model: editingAgent.model || '',
|
|
253
|
+
instructions: editingAgent.instructions || '',
|
|
254
|
+
}
|
|
255
|
+
);
|
|
151
256
|
return;
|
|
152
257
|
}
|
|
153
258
|
|
|
154
|
-
form.reset(
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
259
|
+
form.reset(
|
|
260
|
+
storedDraft?.payload.mode === 'create'
|
|
261
|
+
? storedDraft.payload.values
|
|
262
|
+
: {
|
|
263
|
+
slug: '',
|
|
264
|
+
provider: 'openai',
|
|
265
|
+
model: '',
|
|
266
|
+
instructions: '',
|
|
267
|
+
}
|
|
268
|
+
);
|
|
269
|
+
}, [editingAgent, form, isDialogOpen, loadDraft]);
|
|
161
270
|
|
|
162
271
|
const handleOpenCreate = () => {
|
|
163
272
|
setEditingAgent(null);
|
|
@@ -197,16 +306,12 @@ export default function AiAgentPage() {
|
|
|
197
306
|
toast.success(t('toastCreatedSuccess'));
|
|
198
307
|
}
|
|
199
308
|
|
|
309
|
+
clearDraft();
|
|
200
310
|
setIsDialogOpen(false);
|
|
201
311
|
setEditingAgent(null);
|
|
202
312
|
await refetch();
|
|
203
|
-
} catch (error:
|
|
204
|
-
|
|
205
|
-
error?.response?.data?.message ||
|
|
206
|
-
error?.response?.data?.error ||
|
|
207
|
-
error?.message ||
|
|
208
|
-
t('toastSaveError');
|
|
209
|
-
toast.error(String(message));
|
|
313
|
+
} catch (error: unknown) {
|
|
314
|
+
toast.error(String(getErrorMessage(error, t('toastSaveError'))));
|
|
210
315
|
}
|
|
211
316
|
};
|
|
212
317
|
|
|
@@ -223,13 +328,8 @@ export default function AiAgentPage() {
|
|
|
223
328
|
toast.success(t('toastDeletedSuccess'));
|
|
224
329
|
setAgentToDelete(null);
|
|
225
330
|
await refetch();
|
|
226
|
-
} catch (error:
|
|
227
|
-
|
|
228
|
-
error?.response?.data?.message ||
|
|
229
|
-
error?.response?.data?.error ||
|
|
230
|
-
error?.message ||
|
|
231
|
-
t('toastDeleteError');
|
|
232
|
-
toast.error(String(message));
|
|
331
|
+
} catch (error: unknown) {
|
|
332
|
+
toast.error(String(getErrorMessage(error, t('toastDeleteError'))));
|
|
233
333
|
}
|
|
234
334
|
};
|
|
235
335
|
|
|
@@ -337,7 +437,15 @@ export default function AiAgentPage() {
|
|
|
337
437
|
}}
|
|
338
438
|
/>
|
|
339
439
|
|
|
340
|
-
<Sheet
|
|
440
|
+
<Sheet
|
|
441
|
+
open={isDialogOpen}
|
|
442
|
+
onOpenChange={(open) => {
|
|
443
|
+
setIsDialogOpen(open);
|
|
444
|
+
if (!open) {
|
|
445
|
+
setEditingAgent(null);
|
|
446
|
+
}
|
|
447
|
+
}}
|
|
448
|
+
>
|
|
341
449
|
<SheetContent side="right" className="sm:max-w-xl overflow-y-auto">
|
|
342
450
|
<SheetHeader>
|
|
343
451
|
<SheetTitle>
|
|
@@ -435,6 +543,12 @@ export default function AiAgentPage() {
|
|
|
435
543
|
)}
|
|
436
544
|
/>
|
|
437
545
|
|
|
546
|
+
{draftStatusContent ? (
|
|
547
|
+
<p className="text-xs text-muted-foreground">
|
|
548
|
+
{draftStatusContent}
|
|
549
|
+
</p>
|
|
550
|
+
) : null}
|
|
551
|
+
|
|
438
552
|
<SheetFooter className="p-0">
|
|
439
553
|
<Button type="submit">
|
|
440
554
|
<Bot className="h-4 w-4" />
|
|
@@ -307,7 +307,7 @@ export default function ConfigurationsLayout({ children }: IProps) {
|
|
|
307
307
|
}
|
|
308
308
|
/>
|
|
309
309
|
|
|
310
|
-
<div className="sticky top-0 z-20 -mx-4 mb-6 border-b border-border bg-background/95 px-4 backdrop-blur supports-
|
|
310
|
+
<div className="sticky top-0 z-20 -mx-4 mb-6 border-b border-border bg-background/95 px-4 backdrop-blur supports-backdrop-filter:bg-background/80">
|
|
311
311
|
<div className="min-w-0 overflow-x-hidden">
|
|
312
312
|
<div className="pb-4 pt-2 md:hidden">
|
|
313
313
|
<Select
|
|
@@ -39,6 +39,13 @@ interface DashboardContentProps {
|
|
|
39
39
|
dashboardSlug: string;
|
|
40
40
|
showHeader?: boolean;
|
|
41
41
|
headerActionsTargetId?: string;
|
|
42
|
+
externalHeaderActions?: boolean;
|
|
43
|
+
requestSaveSignal?: number;
|
|
44
|
+
onRuntimeActionsStateChange?: (state: {
|
|
45
|
+
hasChanges: boolean;
|
|
46
|
+
isSaving: boolean;
|
|
47
|
+
isAutosavePending: boolean;
|
|
48
|
+
}) => void;
|
|
42
49
|
openWidgetPickerSignal?: number;
|
|
43
50
|
onOpenWidgetPickerHandled?: () => void;
|
|
44
51
|
}
|
|
@@ -154,6 +161,9 @@ export const DashboardContent = ({
|
|
|
154
161
|
dashboardSlug,
|
|
155
162
|
showHeader = true,
|
|
156
163
|
headerActionsTargetId,
|
|
164
|
+
externalHeaderActions = false,
|
|
165
|
+
requestSaveSignal,
|
|
166
|
+
onRuntimeActionsStateChange,
|
|
157
167
|
openWidgetPickerSignal,
|
|
158
168
|
onOpenWidgetPickerHandled,
|
|
159
169
|
}: DashboardContentProps) => {
|
|
@@ -180,6 +190,7 @@ export const DashboardContent = ({
|
|
|
180
190
|
useState<HTMLElement | null>(null);
|
|
181
191
|
const lastSavedLayoutSignatureRef = useRef('[]');
|
|
182
192
|
const latestLayoutRef = useRef(layout);
|
|
193
|
+
const lastHandledSaveSignalRef = useRef<number | undefined>(undefined);
|
|
183
194
|
|
|
184
195
|
const debouncedComponentsSearch = useDebounce(componentsSearchQuery, 400);
|
|
185
196
|
const debouncedLayout = useDebounce(layout, LAYOUT_AUTOSAVE_DELAY);
|
|
@@ -316,6 +327,14 @@ export const DashboardContent = ({
|
|
|
316
327
|
const isAutosavePending =
|
|
317
328
|
hasChanges && !isSaving && layoutSignature !== debouncedLayoutSignature;
|
|
318
329
|
|
|
330
|
+
useEffect(() => {
|
|
331
|
+
onRuntimeActionsStateChange?.({
|
|
332
|
+
hasChanges,
|
|
333
|
+
isSaving,
|
|
334
|
+
isAutosavePending,
|
|
335
|
+
});
|
|
336
|
+
}, [hasChanges, isAutosavePending, isSaving, onRuntimeActionsStateChange]);
|
|
337
|
+
|
|
319
338
|
useEffect(() => {
|
|
320
339
|
if (!isAutosavePending && !isSaving) {
|
|
321
340
|
stopProgress(150);
|
|
@@ -394,6 +413,22 @@ export const DashboardContent = ({
|
|
|
394
413
|
[dashboardSlug, isSaving, layout, request]
|
|
395
414
|
);
|
|
396
415
|
|
|
416
|
+
useEffect(() => {
|
|
417
|
+
if (!requestSaveSignal) {
|
|
418
|
+
return;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
if (lastHandledSaveSignalRef.current === requestSaveSignal) {
|
|
422
|
+
return;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
lastHandledSaveSignalRef.current = requestSaveSignal;
|
|
426
|
+
|
|
427
|
+
if (!isSaving && hasChanges) {
|
|
428
|
+
void handleSaveLayout();
|
|
429
|
+
}
|
|
430
|
+
}, [handleSaveLayout, hasChanges, isSaving, requestSaveSignal]);
|
|
431
|
+
|
|
397
432
|
useEffect(() => {
|
|
398
433
|
if (!hasChanges || isSaving) {
|
|
399
434
|
return;
|
|
@@ -646,7 +681,8 @@ export const DashboardContent = ({
|
|
|
646
681
|
</>
|
|
647
682
|
);
|
|
648
683
|
|
|
649
|
-
const hasExternalActionTarget =
|
|
684
|
+
const hasExternalActionTarget =
|
|
685
|
+
!externalHeaderActions && !showHeader && Boolean(headerActionsTarget);
|
|
650
686
|
|
|
651
687
|
return (
|
|
652
688
|
<>
|
|
@@ -678,7 +714,7 @@ export const DashboardContent = ({
|
|
|
678
714
|
</div>
|
|
679
715
|
</div>
|
|
680
716
|
</header>
|
|
681
|
-
) : !hasExternalActionTarget ? (
|
|
717
|
+
) : !externalHeaderActions && !hasExternalActionTarget ? (
|
|
682
718
|
<div className="flex w-full items-center justify-end gap-2 px-4 pt-2">
|
|
683
719
|
{dashboardActions}
|
|
684
720
|
</div>
|
|
@@ -686,6 +722,35 @@ export const DashboardContent = ({
|
|
|
686
722
|
{hasExternalActionTarget && headerActionsTarget
|
|
687
723
|
? createPortal(dashboardActions, headerActionsTarget)
|
|
688
724
|
: null}
|
|
725
|
+
{externalHeaderActions ? (
|
|
726
|
+
<AddWidgetSelectorDialog
|
|
727
|
+
availableComponents={availableComponents}
|
|
728
|
+
totalItems={totalAvailableComponents}
|
|
729
|
+
currentPage={availableComponentsResponse?.page ?? componentsPage}
|
|
730
|
+
pageSize={availableComponentsResponse?.pageSize ?? componentsPageSize}
|
|
731
|
+
isLoading={isLoadingComponents}
|
|
732
|
+
searchQuery={componentsSearchQuery}
|
|
733
|
+
onSearchQueryChange={(value) => {
|
|
734
|
+
setComponentsSearchQuery(value);
|
|
735
|
+
setComponentsPage(1);
|
|
736
|
+
}}
|
|
737
|
+
moduleFilter={componentsModuleFilter}
|
|
738
|
+
modules={availableComponentModules}
|
|
739
|
+
onModuleFilterChange={(value) => {
|
|
740
|
+
setComponentsModuleFilter(value);
|
|
741
|
+
setComponentsPage(1);
|
|
742
|
+
}}
|
|
743
|
+
onPageChange={setComponentsPage}
|
|
744
|
+
onPageSizeChange={(nextPageSize) => {
|
|
745
|
+
setComponentsPageSize(nextPageSize);
|
|
746
|
+
setComponentsPage(1);
|
|
747
|
+
}}
|
|
748
|
+
onAdd={handleAddWidget}
|
|
749
|
+
hideTrigger
|
|
750
|
+
openSignal={openWidgetPickerSignal}
|
|
751
|
+
onOpenSignalHandled={onOpenWidgetPickerHandled}
|
|
752
|
+
/>
|
|
753
|
+
) : null}
|
|
689
754
|
<div className="flex flex-1 flex-col gap-4 overflow-auto pt-0">
|
|
690
755
|
{widgets.length > 0 ? (
|
|
691
756
|
<div className="min-h-105 sm:min-h-130 lg:min-h-150">
|
|
@@ -64,6 +64,7 @@ interface AddWidgetSelectorDialogProps {
|
|
|
64
64
|
onAdd: (slugs: string[]) => Promise<void> | void;
|
|
65
65
|
buttonLabel?: string;
|
|
66
66
|
buttonVariant?: 'default' | 'outline';
|
|
67
|
+
hideTrigger?: boolean;
|
|
67
68
|
openSignal?: number;
|
|
68
69
|
onOpenSignalHandled?: () => void;
|
|
69
70
|
}
|
|
@@ -153,6 +154,7 @@ export function AddWidgetSelectorDialog({
|
|
|
153
154
|
onAdd,
|
|
154
155
|
buttonLabel,
|
|
155
156
|
buttonVariant = 'outline',
|
|
157
|
+
hideTrigger = false,
|
|
156
158
|
openSignal,
|
|
157
159
|
onOpenSignalHandled,
|
|
158
160
|
}: AddWidgetSelectorDialogProps) {
|
|
@@ -210,15 +212,17 @@ export function AddWidgetSelectorDialog({
|
|
|
210
212
|
|
|
211
213
|
return (
|
|
212
214
|
<>
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
215
|
+
{!hideTrigger ? (
|
|
216
|
+
<Button
|
|
217
|
+
size="sm"
|
|
218
|
+
variant={buttonVariant}
|
|
219
|
+
className="cursor-pointer gap-2"
|
|
220
|
+
onClick={() => setOpenWidgets(true)}
|
|
221
|
+
>
|
|
222
|
+
<IconPlus className="size-4" />
|
|
223
|
+
{buttonLabel || tMenu('addWidgets')}
|
|
224
|
+
</Button>
|
|
225
|
+
) : null}
|
|
222
226
|
|
|
223
227
|
<Sheet
|
|
224
228
|
open={openWidgets}
|