@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
@@ -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
- slug: editingAgent.slug,
147
- provider: editingAgent.provider,
148
- model: editingAgent.model || '',
149
- instructions: editingAgent.instructions || '',
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
- slug: '',
156
- provider: 'openai',
157
- model: '',
158
- instructions: '',
159
- });
160
- }, [editingAgent, form]);
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: any) {
204
- const message =
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: any) {
227
- const message =
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 open={isDialogOpen} onOpenChange={setIsDialogOpen}>
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-[backdrop-filter]:bg-background/80">
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 = !showHeader && Boolean(headerActionsTarget);
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
- <Button
214
- size="sm"
215
- variant={buttonVariant}
216
- className="cursor-pointer gap-2"
217
- onClick={() => setOpenWidgets(true)}
218
- >
219
- <IconPlus className="size-4" />
220
- {buttonLabel || tMenu('addWidgets')}
221
- </Button>
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}