@hed-hog/core 0.0.298 → 0.0.300
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/dist/dashboard/dashboard/dashboard.controller.d.ts +9 -0
- package/dist/dashboard/dashboard/dashboard.controller.d.ts.map +1 -1
- package/dist/dashboard/dashboard/dashboard.service.d.ts +9 -0
- package/dist/dashboard/dashboard/dashboard.service.d.ts.map +1 -1
- package/dist/dashboard/dashboard-component/dashboard-component.controller.d.ts +14 -1
- package/dist/dashboard/dashboard-component/dashboard-component.controller.d.ts.map +1 -1
- package/dist/dashboard/dashboard-component/dashboard-component.controller.js +28 -3
- package/dist/dashboard/dashboard-component/dashboard-component.controller.js.map +1 -1
- package/dist/dashboard/dashboard-component/dashboard-component.service.d.ts +22 -1
- package/dist/dashboard/dashboard-component/dashboard-component.service.d.ts.map +1 -1
- package/dist/dashboard/dashboard-component/dashboard-component.service.js +185 -35
- package/dist/dashboard/dashboard-component/dashboard-component.service.js.map +1 -1
- package/dist/dashboard/dashboard-component/dto/create.dto.d.ts +1 -0
- package/dist/dashboard/dashboard-component/dto/create.dto.d.ts.map +1 -1
- package/dist/dashboard/dashboard-component/dto/create.dto.js +5 -0
- package/dist/dashboard/dashboard-component/dto/create.dto.js.map +1 -1
- package/dist/dashboard/dashboard-component/dto/update.dto.d.ts +1 -0
- package/dist/dashboard/dashboard-component/dto/update.dto.d.ts.map +1 -1
- package/dist/dashboard/dashboard-component/dto/update.dto.js +5 -0
- package/dist/dashboard/dashboard-component/dto/update.dto.js.map +1 -1
- package/dist/dashboard/dashboard-component-role/dashboard-component-role.controller.d.ts +1 -0
- package/dist/dashboard/dashboard-component-role/dashboard-component-role.controller.d.ts.map +1 -1
- package/dist/dashboard/dashboard-component-role/dashboard-component-role.service.d.ts +1 -0
- package/dist/dashboard/dashboard-component-role/dashboard-component-role.service.d.ts.map +1 -1
- package/dist/dashboard/dashboard-core/dashboard-core.controller.d.ts +72 -1
- package/dist/dashboard/dashboard-core/dashboard-core.controller.d.ts.map +1 -1
- package/dist/dashboard/dashboard-core/dashboard-core.controller.js +111 -0
- package/dist/dashboard/dashboard-core/dashboard-core.controller.js.map +1 -1
- package/dist/dashboard/dashboard-core/dashboard-core.service.d.ts +76 -1
- package/dist/dashboard/dashboard-core/dashboard-core.service.d.ts.map +1 -1
- package/dist/dashboard/dashboard-core/dashboard-core.service.js +614 -23
- package/dist/dashboard/dashboard-core/dashboard-core.service.js.map +1 -1
- package/dist/dashboard/dashboard-item/dashboard-item.controller.d.ts +3 -0
- package/dist/dashboard/dashboard-item/dashboard-item.controller.d.ts.map +1 -1
- package/dist/dashboard/dashboard-item/dashboard-item.service.d.ts +3 -0
- package/dist/dashboard/dashboard-item/dashboard-item.service.d.ts.map +1 -1
- package/dist/dashboard/dashboard-role/dashboard-role.controller.d.ts +2 -0
- package/dist/dashboard/dashboard-role/dashboard-role.controller.d.ts.map +1 -1
- package/dist/dashboard/dashboard-role/dashboard-role.service.d.ts +2 -0
- package/dist/dashboard/dashboard-role/dashboard-role.service.d.ts.map +1 -1
- package/hedhog/data/dashboard.yaml +12 -6
- package/hedhog/data/dashboard_component_role.yaml +66 -0
- package/hedhog/data/dashboard_item.yaml +1 -1
- package/hedhog/data/dashboard_role.yaml +2 -8
- package/hedhog/data/route.yaml +84 -0
- package/hedhog/frontend/app/dashboard/[slug]/dashboard-content.tsx.ejs +457 -135
- package/hedhog/frontend/app/dashboard/[slug]/types.ts.ejs +3 -0
- package/hedhog/frontend/app/dashboard/[slug]/widget-renderer.tsx.ejs +365 -28
- package/hedhog/frontend/app/dashboard/components/add-widget-selector-dialog.tsx.ejs +376 -247
- package/hedhog/frontend/app/dashboard/components/draggable-grid.tsx.ejs +64 -18
- package/hedhog/frontend/app/dashboard/dashboard-home-tabs.tsx.ejs +1389 -0
- package/hedhog/frontend/app/dashboard/dashboard.css.ejs +37 -0
- package/hedhog/frontend/app/dashboard/management/page.tsx.ejs +1 -1
- package/hedhog/frontend/app/dashboard/management/tabs/components-tab.tsx.ejs +6 -6
- package/hedhog/frontend/app/dashboard/management/tabs/dashboards-tab.tsx.ejs +8 -8
- package/hedhog/frontend/app/dashboard/management/tabs/items-tab.tsx.ejs +3 -3
- package/hedhog/frontend/app/dashboard/page.tsx.ejs +3 -25
- package/hedhog/frontend/messages/en.json +115 -2
- package/hedhog/frontend/messages/pt.json +114 -1
- package/hedhog/frontend/public/dashboard-previews/.gitkeep +12 -0
- package/hedhog/frontend/public/dashboard-previews/account-security.png +0 -0
- package/hedhog/frontend/public/dashboard-previews/active-users-card.png +0 -0
- package/hedhog/frontend/public/dashboard-previews/activity-timeline.png +0 -0
- package/hedhog/frontend/public/dashboard-previews/cash-balance-kpi.png +0 -0
- package/hedhog/frontend/public/dashboard-previews/cash-flow-chart.png +0 -0
- package/hedhog/frontend/public/dashboard-previews/default-kpi.png +0 -0
- package/hedhog/frontend/public/dashboard-previews/email-notifications.png +0 -0
- package/hedhog/frontend/public/dashboard-previews/financial-alerts.png +0 -0
- package/hedhog/frontend/public/dashboard-previews/login-history-chart.png +0 -0
- package/hedhog/frontend/public/dashboard-previews/mail-sent-card.png +0 -0
- package/hedhog/frontend/public/dashboard-previews/mail-sent-chart.png +0 -0
- package/hedhog/frontend/public/dashboard-previews/menus-card.png +0 -0
- package/hedhog/frontend/public/dashboard-previews/payable-30d-kpi.png +0 -0
- package/hedhog/frontend/public/dashboard-previews/permissions-card.png +0 -0
- package/hedhog/frontend/public/dashboard-previews/permissions-chart.png +0 -0
- package/hedhog/frontend/public/dashboard-previews/profile-card.png +0 -0
- package/hedhog/frontend/public/dashboard-previews/receivable-30d-kpi.png +0 -0
- package/hedhog/frontend/public/dashboard-previews/routes-card.png +0 -0
- package/hedhog/frontend/public/dashboard-previews/session-activity-chart.png +0 -0
- package/hedhog/frontend/public/dashboard-previews/sessions-today-card.png +0 -0
- package/hedhog/frontend/public/dashboard-previews/stat-access-level.png +0 -0
- package/hedhog/frontend/public/dashboard-previews/stat-actions-today.png +0 -0
- package/hedhog/frontend/public/dashboard-previews/stat-consecutive-days.png +0 -0
- package/hedhog/frontend/public/dashboard-previews/stat-online-time.png +0 -0
- package/hedhog/frontend/public/dashboard-previews/upcoming-payable.png +0 -0
- package/hedhog/frontend/public/dashboard-previews/upcoming-receivable.png +0 -0
- package/hedhog/frontend/public/dashboard-previews/user-growth-chart.png +0 -0
- package/hedhog/frontend/public/dashboard-previews/user-roles.png +0 -0
- package/hedhog/frontend/{app/dashboard/components/widgets → widgets}/account-security.tsx.ejs +34 -30
- package/hedhog/frontend/{app/dashboard/components/widgets → widgets}/active-users-card.tsx.ejs +2 -2
- package/hedhog/frontend/{app/dashboard/components/widgets → widgets}/activity-timeline.tsx.ejs +1 -1
- package/hedhog/frontend/{app/dashboard/components/widgets → widgets}/email-notifications.tsx.ejs +1 -1
- package/hedhog/frontend/{app/dashboard/components/widgets → widgets}/locale-config.tsx.ejs +1 -1
- package/hedhog/frontend/{app/dashboard/components/widgets → widgets}/login-history-chart.tsx.ejs +1 -1
- package/hedhog/frontend/{app/dashboard/components/widgets → widgets}/mail-config.tsx.ejs +1 -1
- package/hedhog/frontend/{app/dashboard/components/widgets → widgets}/mail-sent-card.tsx.ejs +2 -2
- package/hedhog/frontend/{app/dashboard/components/widgets → widgets}/mail-sent-chart.tsx.ejs +1 -1
- package/hedhog/frontend/{app/dashboard/components/widgets → widgets}/menus-card.tsx.ejs +2 -2
- package/hedhog/frontend/{app/dashboard/components/widgets → widgets}/oauth-config.tsx.ejs +1 -1
- package/hedhog/frontend/{app/dashboard/components/widgets → widgets}/permissions-card.tsx.ejs +2 -2
- package/hedhog/frontend/{app/dashboard/components/widgets → widgets}/permissions-chart.tsx.ejs +1 -1
- package/hedhog/frontend/{app/dashboard/components/widgets → widgets}/profile-card.tsx.ejs +1 -1
- package/hedhog/frontend/{app/dashboard/components/widgets → widgets}/routes-card.tsx.ejs +2 -2
- package/hedhog/frontend/{app/dashboard/components/widgets → widgets}/session-activity-chart.tsx.ejs +1 -1
- package/hedhog/frontend/{app/dashboard/components/widgets → widgets}/sessions-today-card.tsx.ejs +2 -2
- package/hedhog/frontend/{app/dashboard/components/widgets → widgets}/stat-access-level.tsx.ejs +1 -1
- package/hedhog/frontend/{app/dashboard/components/widgets → widgets}/stat-actions-today.tsx.ejs +1 -1
- package/hedhog/frontend/{app/dashboard/components/widgets → widgets}/stat-consecutive-days.tsx.ejs +1 -1
- package/hedhog/frontend/{app/dashboard/components/widgets → widgets}/stat-online-time.tsx.ejs +1 -1
- package/hedhog/frontend/{app/dashboard/components/widgets → widgets}/storage-config.tsx.ejs +1 -1
- package/hedhog/frontend/{app/dashboard/components/widgets → widgets}/theme-config.tsx.ejs +1 -1
- package/hedhog/frontend/{app/dashboard/components/widgets → widgets}/user-growth-chart.tsx.ejs +1 -1
- package/hedhog/frontend/{app/dashboard/components/widgets → widgets}/user-roles.tsx.ejs +1 -1
- package/hedhog/frontend/{app/dashboard/components/widgets → widgets}/user-sessions.tsx.ejs +2 -2
- package/hedhog/table/dashboard.yaml +6 -0
- package/hedhog/table/dashboard_component.yaml +7 -0
- package/package.json +5 -5
- package/src/dashboard/dashboard-component/dashboard-component.controller.ts +51 -14
- package/src/dashboard/dashboard-component/dashboard-component.service.ts +254 -43
- package/src/dashboard/dashboard-component/dto/create.dto.ts +4 -0
- package/src/dashboard/dashboard-component/dto/update.dto.ts +4 -0
- package/src/dashboard/dashboard-core/dashboard-core.controller.ts +112 -1
- package/src/dashboard/dashboard-core/dashboard-core.service.ts +782 -24
|
@@ -13,11 +13,15 @@ import { Separator } from '@/components/ui/separator';
|
|
|
13
13
|
import { SidebarTrigger } from '@/components/ui/sidebar';
|
|
14
14
|
import { Skeleton } from '@/components/ui/skeleton';
|
|
15
15
|
import { useIsMobile } from '@/components/ui/use-mobile';
|
|
16
|
+
import { useDebounce } from '@/hooks/use-debounce';
|
|
17
|
+
import { useProgress } from '@bprogress/next';
|
|
16
18
|
import { useApp, useQuery } from '@hed-hog/next-app-provider';
|
|
17
|
-
import { IconDeviceFloppy } from '@tabler/icons-react';
|
|
19
|
+
import { IconDeviceFloppy, IconLoader2 } from '@tabler/icons-react';
|
|
20
|
+
import { toBlob } from 'html-to-image';
|
|
18
21
|
import { useTranslations } from 'next-intl';
|
|
19
22
|
import { useRouter } from 'next/navigation';
|
|
20
|
-
import { useCallback, useEffect, useState } from 'react';
|
|
23
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
24
|
+
import { createPortal } from 'react-dom';
|
|
21
25
|
import {
|
|
22
26
|
AddWidgetSelectorDialog,
|
|
23
27
|
DraggableGrid,
|
|
@@ -33,6 +37,21 @@ import { WidgetRenderer } from './widget-renderer';
|
|
|
33
37
|
|
|
34
38
|
interface DashboardContentProps {
|
|
35
39
|
dashboardSlug: string;
|
|
40
|
+
showHeader?: boolean;
|
|
41
|
+
headerActionsTargetId?: string;
|
|
42
|
+
openWidgetPickerSignal?: number;
|
|
43
|
+
onOpenWidgetPickerHandled?: () => void;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
interface DashboardComponentsPage {
|
|
47
|
+
data: DashboardComponent[];
|
|
48
|
+
total: number;
|
|
49
|
+
lastPage: number;
|
|
50
|
+
page: number;
|
|
51
|
+
pageSize: number;
|
|
52
|
+
prev: number | null;
|
|
53
|
+
next: number | null;
|
|
54
|
+
modules?: string[];
|
|
36
55
|
}
|
|
37
56
|
|
|
38
57
|
const USER_STATS_WIDGETS = new Set([
|
|
@@ -48,8 +67,44 @@ const USER_POST_HISTORY_WIDGETS = new Set([
|
|
|
48
67
|
]);
|
|
49
68
|
|
|
50
69
|
const USER_BOTTOM_WIDGETS = new Set(['user-roles', 'activity-timeline']);
|
|
70
|
+
const LAYOUT_AUTOSAVE_DELAY = 1000;
|
|
71
|
+
|
|
72
|
+
const normalizeLayoutForSave = (layout: LayoutItem[]) =>
|
|
73
|
+
layout.map(({ i, x, y, w, h }) => ({
|
|
74
|
+
i,
|
|
75
|
+
x,
|
|
76
|
+
y,
|
|
77
|
+
w,
|
|
78
|
+
h,
|
|
79
|
+
}));
|
|
80
|
+
|
|
81
|
+
const getLayoutSignature = (layout: LayoutItem[]) =>
|
|
82
|
+
JSON.stringify(normalizeLayoutForSave(layout));
|
|
83
|
+
|
|
84
|
+
const getWidgetBaseSlug = (slug: string): string => {
|
|
85
|
+
const parts = slug.split('.');
|
|
86
|
+
return parts[parts.length - 1] || slug;
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
const getWidgetIdentityKey = ({
|
|
90
|
+
slug,
|
|
91
|
+
library_slug,
|
|
92
|
+
}: {
|
|
93
|
+
slug: string;
|
|
94
|
+
library_slug?: string;
|
|
95
|
+
}) => {
|
|
96
|
+
const baseSlug = getWidgetBaseSlug(slug);
|
|
97
|
+
|
|
98
|
+
if (library_slug) {
|
|
99
|
+
return `${library_slug}.${baseSlug}`;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return slug.includes('.') ? slug : baseSlug;
|
|
103
|
+
};
|
|
51
104
|
|
|
52
105
|
const normalizeUserDashboardLayout = (item: WidgetLayout): LayoutItem => {
|
|
106
|
+
const baseSlug = getWidgetBaseSlug(item.slug);
|
|
107
|
+
|
|
53
108
|
const layoutItem: LayoutItem = {
|
|
54
109
|
i: item.i,
|
|
55
110
|
x: item.x,
|
|
@@ -63,26 +118,26 @@ const normalizeUserDashboardLayout = (item: WidgetLayout): LayoutItem => {
|
|
|
63
118
|
static: false,
|
|
64
119
|
};
|
|
65
120
|
|
|
66
|
-
if (
|
|
121
|
+
if (baseSlug === 'profile-card') {
|
|
67
122
|
layoutItem.h = Math.max(item.h, 4);
|
|
68
123
|
}
|
|
69
124
|
|
|
70
|
-
if (USER_STATS_WIDGETS.has(
|
|
125
|
+
if (USER_STATS_WIDGETS.has(baseSlug)) {
|
|
71
126
|
layoutItem.y = Math.max(item.y, 4);
|
|
72
127
|
return layoutItem;
|
|
73
128
|
}
|
|
74
129
|
|
|
75
|
-
if (
|
|
130
|
+
if (baseSlug === 'login-history-chart') {
|
|
76
131
|
layoutItem.y = Math.max(item.y, 5);
|
|
77
132
|
return layoutItem;
|
|
78
133
|
}
|
|
79
134
|
|
|
80
|
-
if (USER_POST_HISTORY_WIDGETS.has(
|
|
135
|
+
if (USER_POST_HISTORY_WIDGETS.has(baseSlug)) {
|
|
81
136
|
layoutItem.y = Math.max(item.y, 9);
|
|
82
137
|
return layoutItem;
|
|
83
138
|
}
|
|
84
139
|
|
|
85
|
-
if (USER_BOTTOM_WIDGETS.has(
|
|
140
|
+
if (USER_BOTTOM_WIDGETS.has(baseSlug)) {
|
|
86
141
|
layoutItem.y = Math.max(item.y, 14);
|
|
87
142
|
layoutItem.h = item.h + 1;
|
|
88
143
|
return layoutItem;
|
|
@@ -95,16 +150,47 @@ const normalizeUserDashboardLayout = (item: WidgetLayout): LayoutItem => {
|
|
|
95
150
|
return layoutItem;
|
|
96
151
|
};
|
|
97
152
|
|
|
98
|
-
export const DashboardContent = ({
|
|
153
|
+
export const DashboardContent = ({
|
|
154
|
+
dashboardSlug,
|
|
155
|
+
showHeader = true,
|
|
156
|
+
headerActionsTargetId,
|
|
157
|
+
openWidgetPickerSignal,
|
|
158
|
+
onOpenWidgetPickerHandled,
|
|
159
|
+
}: DashboardContentProps) => {
|
|
99
160
|
const t = useTranslations('core.DashboardPage');
|
|
100
161
|
const { request } = useApp();
|
|
162
|
+
const {
|
|
163
|
+
start: startProgress,
|
|
164
|
+
stop: stopProgress,
|
|
165
|
+
set: setProgress,
|
|
166
|
+
} = useProgress();
|
|
101
167
|
const router = useRouter();
|
|
102
168
|
const isMobile = useIsMobile();
|
|
169
|
+
const isDevelopment = process.env.NODE_ENV === 'development';
|
|
103
170
|
|
|
104
171
|
const [layout, setLayout] = useState<LayoutItem[]>([]);
|
|
105
172
|
const [widgets, setWidgets] = useState<WidgetLayout[]>([]);
|
|
106
173
|
const [hasChanges, setHasChanges] = useState(false);
|
|
107
174
|
const [isSaving, setIsSaving] = useState(false);
|
|
175
|
+
const [componentsPage, setComponentsPage] = useState(1);
|
|
176
|
+
const [componentsPageSize, setComponentsPageSize] = useState(12);
|
|
177
|
+
const [componentsSearchQuery, setComponentsSearchQuery] = useState('');
|
|
178
|
+
const [componentsModuleFilter, setComponentsModuleFilter] = useState('all');
|
|
179
|
+
const [headerActionsTarget, setHeaderActionsTarget] =
|
|
180
|
+
useState<HTMLElement | null>(null);
|
|
181
|
+
const lastSavedLayoutSignatureRef = useRef('[]');
|
|
182
|
+
const latestLayoutRef = useRef(layout);
|
|
183
|
+
|
|
184
|
+
const debouncedComponentsSearch = useDebounce(componentsSearchQuery, 400);
|
|
185
|
+
const debouncedLayout = useDebounce(layout, LAYOUT_AUTOSAVE_DELAY);
|
|
186
|
+
|
|
187
|
+
useEffect(() => {
|
|
188
|
+
latestLayoutRef.current = layout;
|
|
189
|
+
}, [layout]);
|
|
190
|
+
const excludedComponentKeys = useMemo(
|
|
191
|
+
() => widgets.map((widget) => getWidgetIdentityKey(widget)),
|
|
192
|
+
[widgets]
|
|
193
|
+
);
|
|
108
194
|
|
|
109
195
|
const { data: dashboardAccess, isLoading: isCheckingAccess } =
|
|
110
196
|
useQuery<DashboardAccessResponse>({
|
|
@@ -128,18 +214,45 @@ export const DashboardContent = ({ dashboardSlug }: DashboardContentProps) => {
|
|
|
128
214
|
}
|
|
129
215
|
}, [dashboardAccess, dashboardSlug, router]);
|
|
130
216
|
|
|
217
|
+
useEffect(() => {
|
|
218
|
+
setComponentsPage(1);
|
|
219
|
+
setComponentsSearchQuery('');
|
|
220
|
+
setComponentsModuleFilter('all');
|
|
221
|
+
}, [dashboardSlug]);
|
|
222
|
+
|
|
131
223
|
const {
|
|
132
|
-
data:
|
|
224
|
+
data: availableComponentsResponse,
|
|
133
225
|
isLoading: isLoadingComponents,
|
|
134
226
|
refetch: refetchComponents,
|
|
135
|
-
} = useQuery<
|
|
136
|
-
queryKey: [
|
|
227
|
+
} = useQuery<DashboardComponentsPage>({
|
|
228
|
+
queryKey: [
|
|
229
|
+
'dashboard-components',
|
|
230
|
+
dashboardSlug,
|
|
231
|
+
componentsPage,
|
|
232
|
+
componentsPageSize,
|
|
233
|
+
debouncedComponentsSearch,
|
|
234
|
+
componentsModuleFilter,
|
|
235
|
+
excludedComponentKeys.join(','),
|
|
236
|
+
],
|
|
137
237
|
queryFn: async () => {
|
|
138
|
-
const
|
|
238
|
+
const trimmedSearch = debouncedComponentsSearch.trim();
|
|
239
|
+
|
|
240
|
+
const { data } = await request<DashboardComponentsPage>({
|
|
139
241
|
url: '/dashboard-component/user',
|
|
140
242
|
method: 'GET',
|
|
243
|
+
params: {
|
|
244
|
+
page: componentsPage,
|
|
245
|
+
pageSize: componentsPageSize,
|
|
246
|
+
...(trimmedSearch ? { search: trimmedSearch } : {}),
|
|
247
|
+
...(componentsModuleFilter !== 'all'
|
|
248
|
+
? { librarySlug: componentsModuleFilter }
|
|
249
|
+
: {}),
|
|
250
|
+
...(excludedComponentKeys.length > 0
|
|
251
|
+
? { exclude: excludedComponentKeys.join(',') }
|
|
252
|
+
: {}),
|
|
253
|
+
},
|
|
141
254
|
});
|
|
142
|
-
return data
|
|
255
|
+
return data;
|
|
143
256
|
},
|
|
144
257
|
enabled: dashboardAccess?.hasAccess ?? false,
|
|
145
258
|
});
|
|
@@ -180,25 +293,62 @@ export const DashboardContent = ({ dashboardSlug }: DashboardContentProps) => {
|
|
|
180
293
|
}
|
|
181
294
|
);
|
|
182
295
|
|
|
296
|
+
lastSavedLayoutSignatureRef.current = getLayoutSignature(gridLayout);
|
|
183
297
|
setLayout(gridLayout);
|
|
184
298
|
setWidgets(userLayout);
|
|
185
299
|
} else {
|
|
300
|
+
lastSavedLayoutSignatureRef.current = '[]';
|
|
186
301
|
setLayout([]);
|
|
187
302
|
setWidgets([]);
|
|
188
303
|
}
|
|
189
304
|
setHasChanges(false);
|
|
190
305
|
}
|
|
191
|
-
}, [userLayout]);
|
|
306
|
+
}, [userLayout, dashboardSlug]);
|
|
307
|
+
|
|
308
|
+
const availableComponents = availableComponentsResponse?.data ?? [];
|
|
309
|
+
const totalAvailableComponents = availableComponentsResponse?.total ?? 0;
|
|
310
|
+
const availableComponentModules = availableComponentsResponse?.modules ?? [];
|
|
311
|
+
const layoutSignature = useMemo(() => getLayoutSignature(layout), [layout]);
|
|
312
|
+
const debouncedLayoutSignature = useMemo(
|
|
313
|
+
() => getLayoutSignature(debouncedLayout),
|
|
314
|
+
[debouncedLayout]
|
|
315
|
+
);
|
|
316
|
+
const isAutosavePending =
|
|
317
|
+
hasChanges && !isSaving && layoutSignature !== debouncedLayoutSignature;
|
|
318
|
+
|
|
319
|
+
useEffect(() => {
|
|
320
|
+
if (!isAutosavePending && !isSaving) {
|
|
321
|
+
stopProgress(150);
|
|
322
|
+
return;
|
|
323
|
+
}
|
|
192
324
|
|
|
193
|
-
|
|
194
|
-
?
|
|
195
|
-
|
|
325
|
+
startProgress(isSaving ? 0.55 : 0.2, 0, true);
|
|
326
|
+
setProgress(isSaving ? 0.8 : 0.35);
|
|
327
|
+
}, [isAutosavePending, isSaving, setProgress, startProgress, stopProgress]);
|
|
196
328
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
)
|
|
329
|
+
useEffect(() => {
|
|
330
|
+
const lastAvailablePage = Math.max(
|
|
331
|
+
availableComponentsResponse?.lastPage ?? 1,
|
|
332
|
+
1
|
|
333
|
+
);
|
|
334
|
+
|
|
335
|
+
if (componentsPage > lastAvailablePage) {
|
|
336
|
+
setComponentsPage(lastAvailablePage);
|
|
337
|
+
}
|
|
338
|
+
}, [availableComponentsResponse?.lastPage, componentsPage]);
|
|
339
|
+
|
|
340
|
+
useEffect(() => {
|
|
341
|
+
if (
|
|
342
|
+
!headerActionsTargetId ||
|
|
343
|
+
showHeader ||
|
|
344
|
+
typeof document === 'undefined'
|
|
345
|
+
) {
|
|
346
|
+
setHeaderActionsTarget(null);
|
|
347
|
+
return;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
setHeaderActionsTarget(document.getElementById(headerActionsTargetId));
|
|
351
|
+
}, [headerActionsTargetId, showHeader]);
|
|
202
352
|
|
|
203
353
|
const handleLayoutChange = useCallback((newLayout: LayoutItem[]) => {
|
|
204
354
|
setLayout((prevLayout) => {
|
|
@@ -212,35 +362,69 @@ export const DashboardContent = ({ dashboardSlug }: DashboardContentProps) => {
|
|
|
212
362
|
});
|
|
213
363
|
}, []);
|
|
214
364
|
|
|
215
|
-
const handleSaveLayout =
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
setIsSaving(
|
|
365
|
+
const handleSaveLayout = useCallback(
|
|
366
|
+
async (layoutToSave: LayoutItem[] = layout) => {
|
|
367
|
+
const normalizedLayout = normalizeLayoutForSave(layoutToSave);
|
|
368
|
+
const nextLayoutSignature = JSON.stringify(normalizedLayout);
|
|
369
|
+
|
|
370
|
+
if (
|
|
371
|
+
isSaving ||
|
|
372
|
+
nextLayoutSignature === lastSavedLayoutSignatureRef.current
|
|
373
|
+
) {
|
|
374
|
+
return;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
setIsSaving(true);
|
|
378
|
+
try {
|
|
379
|
+
await request({
|
|
380
|
+
url: `/dashboard-core/layout/${dashboardSlug}`,
|
|
381
|
+
method: 'POST',
|
|
382
|
+
data: { layout: normalizedLayout },
|
|
383
|
+
});
|
|
384
|
+
lastSavedLayoutSignatureRef.current = nextLayoutSignature;
|
|
385
|
+
setHasChanges(
|
|
386
|
+
getLayoutSignature(latestLayoutRef.current) !== nextLayoutSignature
|
|
387
|
+
);
|
|
388
|
+
} catch (error) {
|
|
389
|
+
console.error('❌ Erro ao salvar layout:', error);
|
|
390
|
+
} finally {
|
|
391
|
+
setIsSaving(false);
|
|
392
|
+
}
|
|
393
|
+
},
|
|
394
|
+
[dashboardSlug, isSaving, layout, request]
|
|
395
|
+
);
|
|
396
|
+
|
|
397
|
+
useEffect(() => {
|
|
398
|
+
if (!hasChanges || isSaving) {
|
|
399
|
+
return;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
const nextLayoutSignature = getLayoutSignature(debouncedLayout);
|
|
403
|
+
|
|
404
|
+
if (nextLayoutSignature === lastSavedLayoutSignatureRef.current) {
|
|
405
|
+
return;
|
|
228
406
|
}
|
|
229
|
-
};
|
|
230
407
|
|
|
231
|
-
|
|
408
|
+
void handleSaveLayout(debouncedLayout);
|
|
409
|
+
}, [debouncedLayout, handleSaveLayout, hasChanges, isSaving]);
|
|
410
|
+
|
|
411
|
+
const handleAddWidget = async (slugs: string[]) => {
|
|
412
|
+
if (!slugs.length) return;
|
|
413
|
+
|
|
232
414
|
try {
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
415
|
+
for (const slug of slugs) {
|
|
416
|
+
await request({
|
|
417
|
+
url: `/dashboard-core/widget/${dashboardSlug}`,
|
|
418
|
+
method: 'POST',
|
|
419
|
+
data: { componentSlug: slug },
|
|
420
|
+
});
|
|
421
|
+
}
|
|
238
422
|
|
|
239
423
|
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
240
424
|
await Promise.all([refetchLayout(), refetchComponents()]);
|
|
241
425
|
setHasChanges(false);
|
|
242
426
|
} catch (error) {
|
|
243
|
-
console.error('Erro ao adicionar
|
|
427
|
+
console.error('Erro ao adicionar widgets:', error);
|
|
244
428
|
}
|
|
245
429
|
};
|
|
246
430
|
|
|
@@ -257,11 +441,69 @@ export const DashboardContent = ({ dashboardSlug }: DashboardContentProps) => {
|
|
|
257
441
|
}
|
|
258
442
|
};
|
|
259
443
|
|
|
444
|
+
const handleCaptureWidgetPreview = async (
|
|
445
|
+
widgetInstanceId: string,
|
|
446
|
+
componentId: number
|
|
447
|
+
) => {
|
|
448
|
+
if (!isDevelopment) {
|
|
449
|
+
return;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
try {
|
|
453
|
+
const widgetElement = document.querySelector(
|
|
454
|
+
`[data-widget-instance-id="${widgetInstanceId}"]`
|
|
455
|
+
) as HTMLElement | null;
|
|
456
|
+
|
|
457
|
+
if (!widgetElement) {
|
|
458
|
+
throw new Error('Widget element not found for screenshot');
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
const screenshot = await toBlob(widgetElement, {
|
|
462
|
+
cacheBust: true,
|
|
463
|
+
pixelRatio: 2,
|
|
464
|
+
backgroundColor: '#ffffff',
|
|
465
|
+
filter: (node) =>
|
|
466
|
+
!(
|
|
467
|
+
node instanceof HTMLElement && node.dataset.widgetAction === 'true'
|
|
468
|
+
),
|
|
469
|
+
});
|
|
470
|
+
|
|
471
|
+
if (!screenshot) {
|
|
472
|
+
throw new Error('Failed to generate screenshot blob');
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
const formData = new FormData();
|
|
476
|
+
formData.append(
|
|
477
|
+
'file',
|
|
478
|
+
screenshot,
|
|
479
|
+
`dashboard-widget-${componentId}-${Date.now()}.png`
|
|
480
|
+
);
|
|
481
|
+
|
|
482
|
+
await request({
|
|
483
|
+
url: `/dashboard-component/${componentId}/preview`,
|
|
484
|
+
method: 'POST',
|
|
485
|
+
data: formData,
|
|
486
|
+
headers: {
|
|
487
|
+
'Content-Type': 'multipart/form-data',
|
|
488
|
+
},
|
|
489
|
+
});
|
|
490
|
+
|
|
491
|
+
await refetchComponents();
|
|
492
|
+
} catch (error) {
|
|
493
|
+
console.error('Erro ao capturar preview do widget:', error);
|
|
494
|
+
}
|
|
495
|
+
};
|
|
496
|
+
|
|
260
497
|
const renderWidget = (widget: WidgetLayout) => {
|
|
261
498
|
return (
|
|
262
499
|
<WidgetRenderer
|
|
263
500
|
widget={widget}
|
|
264
501
|
onRemove={() => handleRemoveWidget(widget.i)}
|
|
502
|
+
onCapture={
|
|
503
|
+
isDevelopment
|
|
504
|
+
? () => handleCaptureWidgetPreview(widget.i, widget.component_id)
|
|
505
|
+
: undefined
|
|
506
|
+
}
|
|
265
507
|
/>
|
|
266
508
|
);
|
|
267
509
|
};
|
|
@@ -269,28 +511,30 @@ export const DashboardContent = ({ dashboardSlug }: DashboardContentProps) => {
|
|
|
269
511
|
if (isCheckingAccess || isLoadingLayout) {
|
|
270
512
|
return (
|
|
271
513
|
<>
|
|
272
|
-
|
|
273
|
-
<
|
|
274
|
-
<div className="flex items-center gap-2">
|
|
275
|
-
<
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
<
|
|
282
|
-
<
|
|
283
|
-
<
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
<
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
514
|
+
{showHeader ? (
|
|
515
|
+
<header className="flex h-16 shrink-0 items-center gap-2 transition-[width,height] ease-linear group-has-data-[collapsible=icon]/sidebar-wrapper:h-12">
|
|
516
|
+
<div className="flex w-full items-center justify-between gap-2 px-4">
|
|
517
|
+
<div className="flex items-center gap-2">
|
|
518
|
+
<SidebarTrigger className="-ml-1" />
|
|
519
|
+
<Separator
|
|
520
|
+
orientation="vertical"
|
|
521
|
+
className="mr-2 data-[orientation=vertical]:h-4"
|
|
522
|
+
/>
|
|
523
|
+
<Breadcrumb>
|
|
524
|
+
<BreadcrumbList>
|
|
525
|
+
<BreadcrumbItem className="hidden md:block">
|
|
526
|
+
<BreadcrumbLink href="#">{t('dashboard')}</BreadcrumbLink>
|
|
527
|
+
</BreadcrumbItem>
|
|
528
|
+
<BreadcrumbSeparator className="hidden md:block" />
|
|
529
|
+
<BreadcrumbItem>
|
|
530
|
+
<BreadcrumbPage>{t('overview')}</BreadcrumbPage>
|
|
531
|
+
</BreadcrumbItem>
|
|
532
|
+
</BreadcrumbList>
|
|
533
|
+
</Breadcrumb>
|
|
534
|
+
</div>
|
|
291
535
|
</div>
|
|
292
|
-
</
|
|
293
|
-
|
|
536
|
+
</header>
|
|
537
|
+
) : null}
|
|
294
538
|
<div className="flex flex-1 flex-col gap-4 p-4 pt-0">
|
|
295
539
|
<Skeleton className="h-32 w-full" />
|
|
296
540
|
<Skeleton className="h-32 w-full" />
|
|
@@ -303,28 +547,30 @@ export const DashboardContent = ({ dashboardSlug }: DashboardContentProps) => {
|
|
|
303
547
|
if (!dashboardAccess?.hasAccess) {
|
|
304
548
|
return (
|
|
305
549
|
<>
|
|
306
|
-
|
|
307
|
-
<
|
|
308
|
-
<div className="flex items-center gap-2">
|
|
309
|
-
<
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
<
|
|
316
|
-
<
|
|
317
|
-
<
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
<
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
550
|
+
{showHeader ? (
|
|
551
|
+
<header className="flex h-16 shrink-0 items-center gap-2 transition-[width,height] ease-linear group-has-data-[collapsible=icon]/sidebar-wrapper:h-12">
|
|
552
|
+
<div className="flex w-full items-center justify-between gap-2 px-4">
|
|
553
|
+
<div className="flex items-center gap-2">
|
|
554
|
+
<SidebarTrigger className="-ml-1" />
|
|
555
|
+
<Separator
|
|
556
|
+
orientation="vertical"
|
|
557
|
+
className="mr-2 data-[orientation=vertical]:h-4"
|
|
558
|
+
/>
|
|
559
|
+
<Breadcrumb>
|
|
560
|
+
<BreadcrumbList>
|
|
561
|
+
<BreadcrumbItem className="hidden md:block">
|
|
562
|
+
<BreadcrumbLink href="#">{t('dashboard')}</BreadcrumbLink>
|
|
563
|
+
</BreadcrumbItem>
|
|
564
|
+
<BreadcrumbSeparator className="hidden md:block" />
|
|
565
|
+
<BreadcrumbItem>
|
|
566
|
+
<BreadcrumbPage>{t('accessDenied')}</BreadcrumbPage>
|
|
567
|
+
</BreadcrumbItem>
|
|
568
|
+
</BreadcrumbList>
|
|
569
|
+
</Breadcrumb>
|
|
570
|
+
</div>
|
|
325
571
|
</div>
|
|
326
|
-
</
|
|
327
|
-
|
|
572
|
+
</header>
|
|
573
|
+
) : null}
|
|
328
574
|
<div className="flex flex-1 flex-col items-center justify-center gap-4 p-4 pt-0">
|
|
329
575
|
<div className="text-center">
|
|
330
576
|
<h2 className="text-2xl font-bold">{t('accessDenied')}</h2>
|
|
@@ -338,65 +584,118 @@ export const DashboardContent = ({ dashboardSlug }: DashboardContentProps) => {
|
|
|
338
584
|
}
|
|
339
585
|
|
|
340
586
|
const dashboardName = dashboardAccess?.dashboard?.name || dashboardSlug;
|
|
587
|
+
const autosaveStatusLabel = isSaving
|
|
588
|
+
? 'Salvando layout...'
|
|
589
|
+
: isAutosavePending
|
|
590
|
+
? 'Salvando automaticamente...'
|
|
591
|
+
: null;
|
|
592
|
+
|
|
593
|
+
const dashboardActions = (
|
|
594
|
+
<>
|
|
595
|
+
{autosaveStatusLabel ? (
|
|
596
|
+
<div className="text-muted-foreground bg-muted inline-flex items-center gap-2 rounded-md px-2.5 py-1 text-xs">
|
|
597
|
+
<IconLoader2 className="size-3.5 animate-spin" />
|
|
598
|
+
<span>{autosaveStatusLabel}</span>
|
|
599
|
+
</div>
|
|
600
|
+
) : null}
|
|
601
|
+
{hasChanges ? (
|
|
602
|
+
<Button
|
|
603
|
+
size="sm"
|
|
604
|
+
variant="default"
|
|
605
|
+
className="gap-1 px-2 sm:gap-2 sm:px-3"
|
|
606
|
+
onClick={() => void handleSaveLayout()}
|
|
607
|
+
disabled={isSaving}
|
|
608
|
+
aria-label={isSaving ? t('saving') : t('saveLayout')}
|
|
609
|
+
>
|
|
610
|
+
{isSaving ? (
|
|
611
|
+
<IconLoader2 className="size-4 animate-spin" />
|
|
612
|
+
) : (
|
|
613
|
+
<IconDeviceFloppy className="size-4" />
|
|
614
|
+
)}
|
|
615
|
+
<span className="hidden sm:inline">
|
|
616
|
+
{isSaving ? t('saving') : t('saveLayout')}
|
|
617
|
+
</span>
|
|
618
|
+
</Button>
|
|
619
|
+
) : null}
|
|
620
|
+
<AddWidgetSelectorDialog
|
|
621
|
+
availableComponents={availableComponents}
|
|
622
|
+
totalItems={totalAvailableComponents}
|
|
623
|
+
currentPage={availableComponentsResponse?.page ?? componentsPage}
|
|
624
|
+
pageSize={availableComponentsResponse?.pageSize ?? componentsPageSize}
|
|
625
|
+
isLoading={isLoadingComponents}
|
|
626
|
+
searchQuery={componentsSearchQuery}
|
|
627
|
+
onSearchQueryChange={(value) => {
|
|
628
|
+
setComponentsSearchQuery(value);
|
|
629
|
+
setComponentsPage(1);
|
|
630
|
+
}}
|
|
631
|
+
moduleFilter={componentsModuleFilter}
|
|
632
|
+
modules={availableComponentModules}
|
|
633
|
+
onModuleFilterChange={(value) => {
|
|
634
|
+
setComponentsModuleFilter(value);
|
|
635
|
+
setComponentsPage(1);
|
|
636
|
+
}}
|
|
637
|
+
onPageChange={setComponentsPage}
|
|
638
|
+
onPageSizeChange={(nextPageSize) => {
|
|
639
|
+
setComponentsPageSize(nextPageSize);
|
|
640
|
+
setComponentsPage(1);
|
|
641
|
+
}}
|
|
642
|
+
onAdd={handleAddWidget}
|
|
643
|
+
openSignal={openWidgetPickerSignal}
|
|
644
|
+
onOpenSignalHandled={onOpenWidgetPickerHandled}
|
|
645
|
+
/>
|
|
646
|
+
</>
|
|
647
|
+
);
|
|
648
|
+
|
|
649
|
+
const hasExternalActionTarget = !showHeader && Boolean(headerActionsTarget);
|
|
341
650
|
|
|
342
651
|
return (
|
|
343
652
|
<>
|
|
344
|
-
|
|
345
|
-
<
|
|
346
|
-
<div className="flex
|
|
347
|
-
<
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
<
|
|
354
|
-
<
|
|
355
|
-
<
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
<
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
variant="default"
|
|
371
|
-
className="gap-1 px-2 sm:gap-2 sm:px-3"
|
|
372
|
-
onClick={handleSaveLayout}
|
|
373
|
-
disabled={isSaving}
|
|
374
|
-
aria-label={isSaving ? t('saving') : t('saveLayout')}
|
|
375
|
-
>
|
|
376
|
-
<IconDeviceFloppy className="size-4" />
|
|
377
|
-
<span className="hidden sm:inline">
|
|
378
|
-
{isSaving ? t('saving') : t('saveLayout')}
|
|
379
|
-
</span>
|
|
380
|
-
</Button>
|
|
381
|
-
)}
|
|
382
|
-
<AddWidgetSelectorDialog
|
|
383
|
-
availableComponents={filteredComponents}
|
|
384
|
-
isLoading={isLoadingComponents}
|
|
385
|
-
onAdd={handleAddWidget}
|
|
386
|
-
currentSlug={dashboardSlug}
|
|
387
|
-
/>
|
|
653
|
+
{showHeader ? (
|
|
654
|
+
<header className="flex min-h-16 shrink-0 items-center gap-2 py-2 transition-[width,height] ease-linear group-has-data-[collapsible=icon]/sidebar-wrapper:h-12 sm:h-16 sm:py-0">
|
|
655
|
+
<div className="flex w-full flex-col gap-2 px-4 sm:flex-row sm:items-center sm:justify-between">
|
|
656
|
+
<div className="flex min-w-0 items-center gap-2">
|
|
657
|
+
<SidebarTrigger className="-ml-1" />
|
|
658
|
+
<Separator
|
|
659
|
+
orientation="vertical"
|
|
660
|
+
className="mr-2 data-[orientation=vertical]:h-4"
|
|
661
|
+
/>
|
|
662
|
+
<Breadcrumb className="min-w-0">
|
|
663
|
+
<BreadcrumbList>
|
|
664
|
+
<BreadcrumbItem className="hidden md:block">
|
|
665
|
+
<BreadcrumbLink href="#">{t('dashboard')}</BreadcrumbLink>
|
|
666
|
+
</BreadcrumbItem>
|
|
667
|
+
<BreadcrumbSeparator className="hidden md:block" />
|
|
668
|
+
<BreadcrumbItem>
|
|
669
|
+
<BreadcrumbPage className="truncate">
|
|
670
|
+
{dashboardName}
|
|
671
|
+
</BreadcrumbPage>
|
|
672
|
+
</BreadcrumbItem>
|
|
673
|
+
</BreadcrumbList>
|
|
674
|
+
</Breadcrumb>
|
|
675
|
+
</div>
|
|
676
|
+
<div className="flex w-full items-center justify-end gap-2 sm:w-auto">
|
|
677
|
+
{dashboardActions}
|
|
678
|
+
</div>
|
|
388
679
|
</div>
|
|
680
|
+
</header>
|
|
681
|
+
) : !hasExternalActionTarget ? (
|
|
682
|
+
<div className="flex w-full items-center justify-end gap-2 px-4 pt-2">
|
|
683
|
+
{dashboardActions}
|
|
389
684
|
</div>
|
|
390
|
-
|
|
391
|
-
|
|
685
|
+
) : null}
|
|
686
|
+
{hasExternalActionTarget && headerActionsTarget
|
|
687
|
+
? createPortal(dashboardActions, headerActionsTarget)
|
|
688
|
+
: null}
|
|
689
|
+
<div className="flex flex-1 flex-col gap-4 overflow-auto pt-0">
|
|
392
690
|
{widgets.length > 0 ? (
|
|
393
|
-
<div className="min-h-
|
|
691
|
+
<div className="min-h-105 sm:min-h-130 lg:min-h-150">
|
|
394
692
|
<DraggableGrid
|
|
395
693
|
className="dashboard-grid"
|
|
396
694
|
layout={layout}
|
|
397
695
|
onLayoutChange={handleLayoutChange}
|
|
398
696
|
cols={12}
|
|
399
697
|
rowHeight={80}
|
|
698
|
+
compactType={null}
|
|
400
699
|
preventCollision
|
|
401
700
|
isDraggable={!isMobile}
|
|
402
701
|
isResizable={!isMobile}
|
|
@@ -408,17 +707,40 @@ export const DashboardContent = ({ dashboardSlug }: DashboardContentProps) => {
|
|
|
408
707
|
</DraggableGrid>
|
|
409
708
|
</div>
|
|
410
709
|
) : (
|
|
411
|
-
<div className="flex min-h-
|
|
710
|
+
<div className="flex min-h-100 flex-col items-center justify-center rounded-lg border border-dashed p-8 text-center">
|
|
412
711
|
<h3 className="text-lg font-semibold">{t('noWidgetAdded')}</h3>
|
|
413
712
|
<p className="text-muted-foreground mt-2 text-sm">
|
|
414
713
|
{t('startAddingWidgets')}
|
|
415
714
|
</p>
|
|
416
715
|
<div className="mt-4">
|
|
417
716
|
<AddWidgetSelectorDialog
|
|
418
|
-
availableComponents={
|
|
717
|
+
availableComponents={availableComponents}
|
|
718
|
+
totalItems={totalAvailableComponents}
|
|
719
|
+
currentPage={
|
|
720
|
+
availableComponentsResponse?.page ?? componentsPage
|
|
721
|
+
}
|
|
722
|
+
pageSize={
|
|
723
|
+
availableComponentsResponse?.pageSize ?? componentsPageSize
|
|
724
|
+
}
|
|
419
725
|
isLoading={isLoadingComponents}
|
|
726
|
+
searchQuery={componentsSearchQuery}
|
|
727
|
+
onSearchQueryChange={(value) => {
|
|
728
|
+
setComponentsSearchQuery(value);
|
|
729
|
+
setComponentsPage(1);
|
|
730
|
+
}}
|
|
731
|
+
moduleFilter={componentsModuleFilter}
|
|
732
|
+
modules={availableComponentModules}
|
|
733
|
+
onModuleFilterChange={(value) => {
|
|
734
|
+
setComponentsModuleFilter(value);
|
|
735
|
+
setComponentsPage(1);
|
|
736
|
+
}}
|
|
737
|
+
onPageChange={setComponentsPage}
|
|
738
|
+
onPageSizeChange={(nextPageSize) => {
|
|
739
|
+
setComponentsPageSize(nextPageSize);
|
|
740
|
+
setComponentsPage(1);
|
|
741
|
+
}}
|
|
420
742
|
onAdd={handleAddWidget}
|
|
421
|
-
|
|
743
|
+
buttonVariant="default"
|
|
422
744
|
/>
|
|
423
745
|
</div>
|
|
424
746
|
</div>
|