@hed-hog/core 0.0.298 → 0.0.299
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 +3 -0
- package/dist/dashboard/dashboard/dashboard.controller.d.ts.map +1 -1
- package/dist/dashboard/dashboard/dashboard.service.d.ts +3 -0
- package/dist/dashboard/dashboard/dashboard.service.d.ts.map +1 -1
- package/dist/dashboard/dashboard-component/dashboard-component.controller.d.ts +12 -0
- package/dist/dashboard/dashboard-component/dashboard-component.controller.d.ts.map +1 -1
- package/dist/dashboard/dashboard-component/dashboard-component.controller.js +22 -0
- package/dist/dashboard/dashboard-component/dashboard-component.controller.js.map +1 -1
- package/dist/dashboard/dashboard-component/dashboard-component.service.d.ts +15 -0
- package/dist/dashboard/dashboard-component/dashboard-component.service.d.ts.map +1 -1
- package/dist/dashboard/dashboard-component/dashboard-component.service.js +110 -3
- 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 +7 -1
- package/dist/dashboard/dashboard-core/dashboard-core.controller.d.ts.map +1 -1
- package/dist/dashboard/dashboard-core/dashboard-core.service.d.ts +7 -1
- package/dist/dashboard/dashboard-core/dashboard-core.service.d.ts.map +1 -1
- package/dist/dashboard/dashboard-core/dashboard-core.service.js +88 -4
- package/dist/dashboard/dashboard-core/dashboard-core.service.js.map +1 -1
- package/dist/dashboard/dashboard-item/dashboard-item.controller.d.ts +1 -0
- package/dist/dashboard/dashboard-item/dashboard-item.controller.d.ts.map +1 -1
- package/dist/dashboard/dashboard-item/dashboard-item.service.d.ts +1 -0
- package/dist/dashboard/dashboard-item/dashboard-item.service.d.ts.map +1 -1
- package/hedhog/data/dashboard_item.yaml +1 -1
- package/hedhog/data/route.yaml +12 -0
- package/hedhog/frontend/app/dashboard/[slug]/dashboard-content.tsx.ejs +141 -24
- package/hedhog/frontend/app/dashboard/[slug]/types.ts.ejs +3 -0
- package/hedhog/frontend/app/dashboard/[slug]/widget-renderer.tsx.ejs +136 -23
- package/hedhog/frontend/app/dashboard/components/add-widget-selector-dialog.tsx.ejs +266 -85
- package/hedhog/frontend/app/dashboard/components/widgets/core..gitkeep.ejs +11 -0
- package/hedhog/frontend/app/dashboard/components/widgets/core.account-security.tsx.ejs +192 -0
- package/hedhog/frontend/app/dashboard/components/widgets/core.user-sessions.tsx.ejs +236 -0
- package/hedhog/frontend/app/dashboard/components/widgets/finance.alerts.tsx.ejs +108 -0
- package/hedhog/frontend/app/dashboard/components/widgets/finance.cash-balance-kpi.tsx.ejs +66 -0
- package/hedhog/frontend/app/dashboard/components/widgets/finance.cash-flow-chart.tsx.ejs +122 -0
- package/hedhog/frontend/app/dashboard/components/widgets/finance.default-kpi.tsx.ejs +63 -0
- package/hedhog/frontend/app/dashboard/components/widgets/finance.payable-30d-kpi.tsx.ejs +73 -0
- package/hedhog/frontend/app/dashboard/components/widgets/finance.receivable-30d-kpi.tsx.ejs +73 -0
- package/hedhog/frontend/app/dashboard/components/widgets/finance.upcoming-payable.tsx.ejs +123 -0
- package/hedhog/frontend/app/dashboard/components/widgets/finance.upcoming-receivable.tsx.ejs +118 -0
- package/hedhog/frontend/messages/en.json +3 -0
- package/hedhog/frontend/messages/pt.json +3 -0
- 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 +33 -29
- package/hedhog/frontend/widgets/active-users-card.tsx.ejs +58 -0
- package/hedhog/frontend/widgets/activity-timeline.tsx.ejs +223 -0
- package/hedhog/frontend/widgets/email-notifications.tsx.ejs +226 -0
- package/hedhog/frontend/widgets/locale-config.tsx.ejs +168 -0
- package/hedhog/frontend/widgets/login-history-chart.tsx.ejs +115 -0
- package/hedhog/frontend/widgets/mail-config.tsx.ejs +199 -0
- package/hedhog/frontend/widgets/mail-sent-card.tsx.ejs +58 -0
- package/hedhog/frontend/widgets/mail-sent-chart.tsx.ejs +149 -0
- package/hedhog/frontend/widgets/menus-card.tsx.ejs +58 -0
- package/hedhog/frontend/widgets/oauth-config.tsx.ejs +175 -0
- package/hedhog/frontend/widgets/permissions-card.tsx.ejs +61 -0
- package/hedhog/frontend/widgets/permissions-chart.tsx.ejs +156 -0
- package/hedhog/frontend/widgets/profile-card.tsx.ejs +186 -0
- package/hedhog/frontend/widgets/routes-card.tsx.ejs +58 -0
- package/hedhog/frontend/widgets/session-activity-chart.tsx.ejs +183 -0
- package/hedhog/frontend/widgets/sessions-today-card.tsx.ejs +62 -0
- package/hedhog/frontend/widgets/stat-access-level.tsx.ejs +57 -0
- package/hedhog/frontend/widgets/stat-actions-today.tsx.ejs +57 -0
- package/hedhog/frontend/widgets/stat-consecutive-days.tsx.ejs +57 -0
- package/hedhog/frontend/widgets/stat-online-time.tsx.ejs +57 -0
- package/hedhog/frontend/widgets/storage-config.tsx.ejs +196 -0
- package/hedhog/frontend/widgets/theme-config.tsx.ejs +213 -0
- package/hedhog/frontend/widgets/user-growth-chart.tsx.ejs +210 -0
- package/hedhog/frontend/widgets/user-roles.tsx.ejs +132 -0
- package/hedhog/frontend/{app/dashboard/components/widgets → widgets}/user-sessions.tsx.ejs +1 -1
- package/hedhog/table/dashboard_component.yaml +7 -0
- package/package.json +4 -4
- package/src/dashboard/dashboard-component/dashboard-component.controller.ts +36 -12
- package/src/dashboard/dashboard-component/dashboard-component.service.ts +150 -3
- 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.service.ts +108 -5
- /package/hedhog/frontend/app/dashboard/components/widgets/{active-users-card.tsx.ejs → core.active-users-card.tsx.ejs} +0 -0
- /package/hedhog/frontend/app/dashboard/components/widgets/{activity-timeline.tsx.ejs → core.activity-timeline.tsx.ejs} +0 -0
- /package/hedhog/frontend/app/dashboard/components/widgets/{email-notifications.tsx.ejs → core.email-notifications.tsx.ejs} +0 -0
- /package/hedhog/frontend/app/dashboard/components/widgets/{locale-config.tsx.ejs → core.locale-config.tsx.ejs} +0 -0
- /package/hedhog/frontend/app/dashboard/components/widgets/{login-history-chart.tsx.ejs → core.login-history-chart.tsx.ejs} +0 -0
- /package/hedhog/frontend/app/dashboard/components/widgets/{mail-config.tsx.ejs → core.mail-config.tsx.ejs} +0 -0
- /package/hedhog/frontend/app/dashboard/components/widgets/{mail-sent-card.tsx.ejs → core.mail-sent-card.tsx.ejs} +0 -0
- /package/hedhog/frontend/app/dashboard/components/widgets/{mail-sent-chart.tsx.ejs → core.mail-sent-chart.tsx.ejs} +0 -0
- /package/hedhog/frontend/app/dashboard/components/widgets/{menus-card.tsx.ejs → core.menus-card.tsx.ejs} +0 -0
- /package/hedhog/frontend/app/dashboard/components/widgets/{oauth-config.tsx.ejs → core.oauth-config.tsx.ejs} +0 -0
- /package/hedhog/frontend/app/dashboard/components/widgets/{permissions-card.tsx.ejs → core.permissions-card.tsx.ejs} +0 -0
- /package/hedhog/frontend/app/dashboard/components/widgets/{permissions-chart.tsx.ejs → core.permissions-chart.tsx.ejs} +0 -0
- /package/hedhog/frontend/app/dashboard/components/widgets/{profile-card.tsx.ejs → core.profile-card.tsx.ejs} +0 -0
- /package/hedhog/frontend/app/dashboard/components/widgets/{routes-card.tsx.ejs → core.routes-card.tsx.ejs} +0 -0
- /package/hedhog/frontend/app/dashboard/components/widgets/{session-activity-chart.tsx.ejs → core.session-activity-chart.tsx.ejs} +0 -0
- /package/hedhog/frontend/app/dashboard/components/widgets/{sessions-today-card.tsx.ejs → core.sessions-today-card.tsx.ejs} +0 -0
- /package/hedhog/frontend/app/dashboard/components/widgets/{stat-access-level.tsx.ejs → core.stat-access-level.tsx.ejs} +0 -0
- /package/hedhog/frontend/app/dashboard/components/widgets/{stat-actions-today.tsx.ejs → core.stat-actions-today.tsx.ejs} +0 -0
- /package/hedhog/frontend/app/dashboard/components/widgets/{stat-consecutive-days.tsx.ejs → core.stat-consecutive-days.tsx.ejs} +0 -0
- /package/hedhog/frontend/app/dashboard/components/widgets/{stat-online-time.tsx.ejs → core.stat-online-time.tsx.ejs} +0 -0
- /package/hedhog/frontend/app/dashboard/components/widgets/{storage-config.tsx.ejs → core.storage-config.tsx.ejs} +0 -0
- /package/hedhog/frontend/app/dashboard/components/widgets/{theme-config.tsx.ejs → core.theme-config.tsx.ejs} +0 -0
- /package/hedhog/frontend/app/dashboard/components/widgets/{user-growth-chart.tsx.ejs → core.user-growth-chart.tsx.ejs} +0 -0
- /package/hedhog/frontend/app/dashboard/components/widgets/{user-roles.tsx.ejs → core.user-roles.tsx.ejs} +0 -0
|
@@ -15,6 +15,7 @@ import { Skeleton } from '@/components/ui/skeleton';
|
|
|
15
15
|
import { useIsMobile } from '@/components/ui/use-mobile';
|
|
16
16
|
import { useApp, useQuery } from '@hed-hog/next-app-provider';
|
|
17
17
|
import { IconDeviceFloppy } from '@tabler/icons-react';
|
|
18
|
+
import { toBlob } from 'html-to-image';
|
|
18
19
|
import { useTranslations } from 'next-intl';
|
|
19
20
|
import { useRouter } from 'next/navigation';
|
|
20
21
|
import { useCallback, useEffect, useState } from 'react';
|
|
@@ -35,6 +36,16 @@ interface DashboardContentProps {
|
|
|
35
36
|
dashboardSlug: string;
|
|
36
37
|
}
|
|
37
38
|
|
|
39
|
+
interface DashboardComponentsPage {
|
|
40
|
+
data: DashboardComponent[];
|
|
41
|
+
total: number;
|
|
42
|
+
lastPage: number;
|
|
43
|
+
page: number;
|
|
44
|
+
pageSize: number;
|
|
45
|
+
prev: number | null;
|
|
46
|
+
next: number | null;
|
|
47
|
+
}
|
|
48
|
+
|
|
38
49
|
const USER_STATS_WIDGETS = new Set([
|
|
39
50
|
'stat-online-time',
|
|
40
51
|
'stat-actions-today',
|
|
@@ -49,7 +60,30 @@ const USER_POST_HISTORY_WIDGETS = new Set([
|
|
|
49
60
|
|
|
50
61
|
const USER_BOTTOM_WIDGETS = new Set(['user-roles', 'activity-timeline']);
|
|
51
62
|
|
|
63
|
+
const getWidgetBaseSlug = (slug: string): string => {
|
|
64
|
+
const parts = slug.split('.');
|
|
65
|
+
return parts[parts.length - 1] || slug;
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
const getWidgetIdentityKey = ({
|
|
69
|
+
slug,
|
|
70
|
+
library_slug,
|
|
71
|
+
}: {
|
|
72
|
+
slug: string;
|
|
73
|
+
library_slug?: string;
|
|
74
|
+
}) => {
|
|
75
|
+
const baseSlug = getWidgetBaseSlug(slug);
|
|
76
|
+
|
|
77
|
+
if (library_slug) {
|
|
78
|
+
return `${library_slug}.${baseSlug}`;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return slug.includes('.') ? slug : baseSlug;
|
|
82
|
+
};
|
|
83
|
+
|
|
52
84
|
const normalizeUserDashboardLayout = (item: WidgetLayout): LayoutItem => {
|
|
85
|
+
const baseSlug = getWidgetBaseSlug(item.slug);
|
|
86
|
+
|
|
53
87
|
const layoutItem: LayoutItem = {
|
|
54
88
|
i: item.i,
|
|
55
89
|
x: item.x,
|
|
@@ -63,26 +97,26 @@ const normalizeUserDashboardLayout = (item: WidgetLayout): LayoutItem => {
|
|
|
63
97
|
static: false,
|
|
64
98
|
};
|
|
65
99
|
|
|
66
|
-
if (
|
|
100
|
+
if (baseSlug === 'profile-card') {
|
|
67
101
|
layoutItem.h = Math.max(item.h, 4);
|
|
68
102
|
}
|
|
69
103
|
|
|
70
|
-
if (USER_STATS_WIDGETS.has(
|
|
104
|
+
if (USER_STATS_WIDGETS.has(baseSlug)) {
|
|
71
105
|
layoutItem.y = Math.max(item.y, 4);
|
|
72
106
|
return layoutItem;
|
|
73
107
|
}
|
|
74
108
|
|
|
75
|
-
if (
|
|
109
|
+
if (baseSlug === 'login-history-chart') {
|
|
76
110
|
layoutItem.y = Math.max(item.y, 5);
|
|
77
111
|
return layoutItem;
|
|
78
112
|
}
|
|
79
113
|
|
|
80
|
-
if (USER_POST_HISTORY_WIDGETS.has(
|
|
114
|
+
if (USER_POST_HISTORY_WIDGETS.has(baseSlug)) {
|
|
81
115
|
layoutItem.y = Math.max(item.y, 9);
|
|
82
116
|
return layoutItem;
|
|
83
117
|
}
|
|
84
118
|
|
|
85
|
-
if (USER_BOTTOM_WIDGETS.has(
|
|
119
|
+
if (USER_BOTTOM_WIDGETS.has(baseSlug)) {
|
|
86
120
|
layoutItem.y = Math.max(item.y, 14);
|
|
87
121
|
layoutItem.h = item.h + 1;
|
|
88
122
|
return layoutItem;
|
|
@@ -100,11 +134,14 @@ export const DashboardContent = ({ dashboardSlug }: DashboardContentProps) => {
|
|
|
100
134
|
const { request } = useApp();
|
|
101
135
|
const router = useRouter();
|
|
102
136
|
const isMobile = useIsMobile();
|
|
137
|
+
const isDevelopment = process.env.NODE_ENV === 'development';
|
|
103
138
|
|
|
104
139
|
const [layout, setLayout] = useState<LayoutItem[]>([]);
|
|
105
140
|
const [widgets, setWidgets] = useState<WidgetLayout[]>([]);
|
|
106
141
|
const [hasChanges, setHasChanges] = useState(false);
|
|
107
142
|
const [isSaving, setIsSaving] = useState(false);
|
|
143
|
+
const [componentsPage, setComponentsPage] = useState(1);
|
|
144
|
+
const [componentsPageSize, setComponentsPageSize] = useState(12);
|
|
108
145
|
|
|
109
146
|
const { data: dashboardAccess, isLoading: isCheckingAccess } =
|
|
110
147
|
useQuery<DashboardAccessResponse>({
|
|
@@ -129,17 +166,26 @@ export const DashboardContent = ({ dashboardSlug }: DashboardContentProps) => {
|
|
|
129
166
|
}, [dashboardAccess, dashboardSlug, router]);
|
|
130
167
|
|
|
131
168
|
const {
|
|
132
|
-
data:
|
|
169
|
+
data: availableComponentsResponse,
|
|
133
170
|
isLoading: isLoadingComponents,
|
|
134
171
|
refetch: refetchComponents,
|
|
135
|
-
} = useQuery<
|
|
136
|
-
queryKey: [
|
|
172
|
+
} = useQuery<DashboardComponentsPage>({
|
|
173
|
+
queryKey: [
|
|
174
|
+
'dashboard-components',
|
|
175
|
+
dashboardSlug,
|
|
176
|
+
componentsPage,
|
|
177
|
+
componentsPageSize,
|
|
178
|
+
],
|
|
137
179
|
queryFn: async () => {
|
|
138
|
-
const { data } = await request<
|
|
180
|
+
const { data } = await request<DashboardComponentsPage>({
|
|
139
181
|
url: '/dashboard-component/user',
|
|
140
182
|
method: 'GET',
|
|
183
|
+
params: {
|
|
184
|
+
page: componentsPage,
|
|
185
|
+
pageSize: componentsPageSize,
|
|
186
|
+
},
|
|
141
187
|
});
|
|
142
|
-
return data
|
|
188
|
+
return data;
|
|
143
189
|
},
|
|
144
190
|
enabled: dashboardAccess?.hasAccess ?? false,
|
|
145
191
|
});
|
|
@@ -188,16 +234,19 @@ export const DashboardContent = ({ dashboardSlug }: DashboardContentProps) => {
|
|
|
188
234
|
}
|
|
189
235
|
setHasChanges(false);
|
|
190
236
|
}
|
|
191
|
-
}, [userLayout]);
|
|
237
|
+
}, [userLayout, dashboardSlug]);
|
|
192
238
|
|
|
193
|
-
const componentsToFilter =
|
|
194
|
-
|
|
195
|
-
|
|
239
|
+
const componentsToFilter = availableComponentsResponse?.data || [];
|
|
240
|
+
const totalAvailableComponents =
|
|
241
|
+
availableComponentsResponse?.total ?? componentsToFilter.length;
|
|
196
242
|
|
|
197
243
|
const filteredComponents =
|
|
198
244
|
componentsToFilter.filter(
|
|
199
245
|
(component: DashboardComponent) =>
|
|
200
|
-
!widgets.some(
|
|
246
|
+
!widgets.some(
|
|
247
|
+
(widget) =>
|
|
248
|
+
getWidgetIdentityKey(widget) === getWidgetIdentityKey(component)
|
|
249
|
+
)
|
|
201
250
|
) || [];
|
|
202
251
|
|
|
203
252
|
const handleLayoutChange = useCallback((newLayout: LayoutItem[]) => {
|
|
@@ -228,19 +277,23 @@ export const DashboardContent = ({ dashboardSlug }: DashboardContentProps) => {
|
|
|
228
277
|
}
|
|
229
278
|
};
|
|
230
279
|
|
|
231
|
-
const handleAddWidget = async (
|
|
280
|
+
const handleAddWidget = async (slugs: string[]) => {
|
|
281
|
+
if (!slugs.length) return;
|
|
282
|
+
|
|
232
283
|
try {
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
284
|
+
for (const slug of slugs) {
|
|
285
|
+
await request({
|
|
286
|
+
url: `/dashboard-core/widget/${dashboardSlug}`,
|
|
287
|
+
method: 'POST',
|
|
288
|
+
data: { componentSlug: slug },
|
|
289
|
+
});
|
|
290
|
+
}
|
|
238
291
|
|
|
239
292
|
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
240
293
|
await Promise.all([refetchLayout(), refetchComponents()]);
|
|
241
294
|
setHasChanges(false);
|
|
242
295
|
} catch (error) {
|
|
243
|
-
console.error('Erro ao adicionar
|
|
296
|
+
console.error('Erro ao adicionar widgets:', error);
|
|
244
297
|
}
|
|
245
298
|
};
|
|
246
299
|
|
|
@@ -257,11 +310,65 @@ export const DashboardContent = ({ dashboardSlug }: DashboardContentProps) => {
|
|
|
257
310
|
}
|
|
258
311
|
};
|
|
259
312
|
|
|
313
|
+
const handleCaptureWidgetPreview = async (
|
|
314
|
+
widgetInstanceId: string,
|
|
315
|
+
componentId: number
|
|
316
|
+
) => {
|
|
317
|
+
if (!isDevelopment) {
|
|
318
|
+
return;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
try {
|
|
322
|
+
const widgetElement = document.querySelector(
|
|
323
|
+
`[data-widget-instance-id="${widgetInstanceId}"]`
|
|
324
|
+
) as HTMLElement | null;
|
|
325
|
+
|
|
326
|
+
if (!widgetElement) {
|
|
327
|
+
throw new Error('Widget element not found for screenshot');
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
const screenshot = await toBlob(widgetElement, {
|
|
331
|
+
cacheBust: true,
|
|
332
|
+
pixelRatio: 2,
|
|
333
|
+
backgroundColor: '#ffffff',
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
if (!screenshot) {
|
|
337
|
+
throw new Error('Failed to generate screenshot blob');
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
const formData = new FormData();
|
|
341
|
+
formData.append(
|
|
342
|
+
'file',
|
|
343
|
+
screenshot,
|
|
344
|
+
`dashboard-widget-${componentId}-${Date.now()}.png`
|
|
345
|
+
);
|
|
346
|
+
|
|
347
|
+
await request({
|
|
348
|
+
url: `/dashboard-component/${componentId}/preview`,
|
|
349
|
+
method: 'POST',
|
|
350
|
+
data: formData,
|
|
351
|
+
headers: {
|
|
352
|
+
'Content-Type': 'multipart/form-data',
|
|
353
|
+
},
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
await refetchComponents();
|
|
357
|
+
} catch (error) {
|
|
358
|
+
console.error('Erro ao capturar preview do widget:', error);
|
|
359
|
+
}
|
|
360
|
+
};
|
|
361
|
+
|
|
260
362
|
const renderWidget = (widget: WidgetLayout) => {
|
|
261
363
|
return (
|
|
262
364
|
<WidgetRenderer
|
|
263
365
|
widget={widget}
|
|
264
366
|
onRemove={() => handleRemoveWidget(widget.i)}
|
|
367
|
+
onCapture={
|
|
368
|
+
isDevelopment
|
|
369
|
+
? () => handleCaptureWidgetPreview(widget.i, widget.component_id)
|
|
370
|
+
: undefined
|
|
371
|
+
}
|
|
265
372
|
/>
|
|
266
373
|
);
|
|
267
374
|
};
|
|
@@ -381,7 +488,17 @@ export const DashboardContent = ({ dashboardSlug }: DashboardContentProps) => {
|
|
|
381
488
|
)}
|
|
382
489
|
<AddWidgetSelectorDialog
|
|
383
490
|
availableComponents={filteredComponents}
|
|
491
|
+
totalItems={totalAvailableComponents}
|
|
492
|
+
currentPage={availableComponentsResponse?.page ?? componentsPage}
|
|
493
|
+
pageSize={
|
|
494
|
+
availableComponentsResponse?.pageSize ?? componentsPageSize
|
|
495
|
+
}
|
|
384
496
|
isLoading={isLoadingComponents}
|
|
497
|
+
onPageChange={setComponentsPage}
|
|
498
|
+
onPageSizeChange={(nextPageSize) => {
|
|
499
|
+
setComponentsPageSize(nextPageSize);
|
|
500
|
+
setComponentsPage(1);
|
|
501
|
+
}}
|
|
385
502
|
onAdd={handleAddWidget}
|
|
386
503
|
currentSlug={dashboardSlug}
|
|
387
504
|
/>
|
|
@@ -390,7 +507,7 @@ export const DashboardContent = ({ dashboardSlug }: DashboardContentProps) => {
|
|
|
390
507
|
</header>
|
|
391
508
|
<div className="flex flex-1 flex-col gap-4 overflow-auto p-4 pt-0">
|
|
392
509
|
{widgets.length > 0 ? (
|
|
393
|
-
<div className="min-h-
|
|
510
|
+
<div className="min-h-105 sm:min-h-130 lg:min-h-150">
|
|
394
511
|
<DraggableGrid
|
|
395
512
|
className="dashboard-grid"
|
|
396
513
|
layout={layout}
|
|
@@ -408,7 +525,7 @@ export const DashboardContent = ({ dashboardSlug }: DashboardContentProps) => {
|
|
|
408
525
|
</DraggableGrid>
|
|
409
526
|
</div>
|
|
410
527
|
) : (
|
|
411
|
-
<div className="flex min-h-
|
|
528
|
+
<div className="flex min-h-100 flex-col items-center justify-center rounded-lg border border-dashed p-8 text-center">
|
|
412
529
|
<h3 className="text-lg font-semibold">{t('noWidgetAdded')}</h3>
|
|
413
530
|
<p className="text-muted-foreground mt-2 text-sm">
|
|
414
531
|
{t('startAddingWidgets')}
|
|
@@ -2,6 +2,7 @@ export interface WidgetLayout {
|
|
|
2
2
|
i: string;
|
|
3
3
|
component_id: number;
|
|
4
4
|
slug: string;
|
|
5
|
+
library_slug?: string;
|
|
5
6
|
name: string;
|
|
6
7
|
description: string;
|
|
7
8
|
x: number;
|
|
@@ -18,6 +19,7 @@ export interface WidgetLayout {
|
|
|
18
19
|
export interface DashboardComponent {
|
|
19
20
|
id: number;
|
|
20
21
|
slug: string;
|
|
22
|
+
library_slug?: string;
|
|
21
23
|
path: string;
|
|
22
24
|
min_width: number;
|
|
23
25
|
max_width?: number;
|
|
@@ -59,4 +61,5 @@ export interface LayoutItem {
|
|
|
59
61
|
export interface WidgetRendererProps {
|
|
60
62
|
widget: WidgetLayout;
|
|
61
63
|
onRemove: () => void;
|
|
64
|
+
onCapture?: () => void;
|
|
62
65
|
}
|
|
@@ -1,45 +1,158 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
+
import { Button } from '@/components/ui/button';
|
|
3
4
|
import { Skeleton } from '@/components/ui/skeleton';
|
|
5
|
+
import { IconCamera } from '@tabler/icons-react';
|
|
4
6
|
import { useEffect, useState } from 'react';
|
|
5
7
|
import { DynamicWidget } from '../components';
|
|
6
8
|
import { WidgetRendererProps } from './types';
|
|
7
9
|
|
|
8
|
-
|
|
10
|
+
const getWidgetBaseSlug = (slug: string): string => {
|
|
11
|
+
const parts = slug.split('.');
|
|
12
|
+
return parts[parts.length - 1] || slug;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const getWidgetLibrarySlug = (
|
|
16
|
+
slug: string,
|
|
17
|
+
librarySlug?: string
|
|
18
|
+
): string | undefined => {
|
|
19
|
+
if (librarySlug) {
|
|
20
|
+
return librarySlug;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const parts = slug.split('.');
|
|
24
|
+
return parts.length > 1 ? parts[0] : 'core';
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const getWidgetImportCandidates = (
|
|
28
|
+
slug: string,
|
|
29
|
+
librarySlug?: string
|
|
30
|
+
): string[] => {
|
|
31
|
+
const baseSlug = getWidgetBaseSlug(slug);
|
|
32
|
+
const effectiveLibrarySlug = getWidgetLibrarySlug(slug, librarySlug);
|
|
33
|
+
const libraryPrefixedSlug = effectiveLibrarySlug
|
|
34
|
+
? `${effectiveLibrarySlug}.${baseSlug}`
|
|
35
|
+
: null;
|
|
36
|
+
|
|
37
|
+
return Array.from(
|
|
38
|
+
new Set(
|
|
39
|
+
[libraryPrefixedSlug, slug, baseSlug].filter(
|
|
40
|
+
(candidate): candidate is string => Boolean(candidate)
|
|
41
|
+
)
|
|
42
|
+
)
|
|
43
|
+
);
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
export const WidgetRenderer = ({
|
|
47
|
+
widget,
|
|
48
|
+
onRemove,
|
|
49
|
+
onCapture,
|
|
50
|
+
}: WidgetRendererProps) => {
|
|
9
51
|
const [Component, setComponent] = useState<any>(null);
|
|
10
52
|
const [useFallback, setUseFallback] = useState(false);
|
|
11
53
|
|
|
12
54
|
useEffect(() => {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
55
|
+
let cancelled = false;
|
|
56
|
+
|
|
57
|
+
setComponent(null);
|
|
58
|
+
setUseFallback(false);
|
|
59
|
+
|
|
60
|
+
const loadWidgetComponent = async () => {
|
|
61
|
+
const candidates = getWidgetImportCandidates(
|
|
62
|
+
widget.slug,
|
|
63
|
+
widget.library_slug
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
for (const candidate of candidates) {
|
|
67
|
+
try {
|
|
68
|
+
const mod = await import(`../components/widgets/${candidate}`);
|
|
69
|
+
if (!cancelled) {
|
|
70
|
+
setComponent(() => mod.default);
|
|
71
|
+
}
|
|
72
|
+
return;
|
|
73
|
+
} catch {
|
|
74
|
+
// Try next candidate.
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (!cancelled) {
|
|
79
|
+
console.warn(`Widget component not found for slug: ${widget.slug}`, {
|
|
80
|
+
candidates,
|
|
81
|
+
});
|
|
22
82
|
setUseFallback(true);
|
|
23
|
-
}
|
|
24
|
-
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
void loadWidgetComponent();
|
|
87
|
+
|
|
88
|
+
return () => {
|
|
89
|
+
cancelled = true;
|
|
90
|
+
};
|
|
91
|
+
}, [widget.slug, widget.library_slug]);
|
|
25
92
|
|
|
26
93
|
if (useFallback) {
|
|
27
94
|
return (
|
|
28
|
-
<
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
95
|
+
<div
|
|
96
|
+
className="group relative h-full w-full"
|
|
97
|
+
data-widget-instance-id={widget.i}
|
|
98
|
+
>
|
|
99
|
+
{onCapture ? (
|
|
100
|
+
<Button
|
|
101
|
+
type="button"
|
|
102
|
+
variant="ghost"
|
|
103
|
+
size="icon"
|
|
104
|
+
className="no-drag absolute right-10 top-3 z-20 size-6 cursor-pointer shrink-0 opacity-0 transition-opacity group-hover:opacity-100"
|
|
105
|
+
onClick={(event) => {
|
|
106
|
+
event.stopPropagation();
|
|
107
|
+
event.preventDefault();
|
|
108
|
+
onCapture();
|
|
109
|
+
}}
|
|
110
|
+
>
|
|
111
|
+
<IconCamera className="size-3" />
|
|
112
|
+
</Button>
|
|
113
|
+
) : null}
|
|
114
|
+
<DynamicWidget
|
|
115
|
+
title={widget.name}
|
|
116
|
+
value={0}
|
|
117
|
+
description={widget.description || ''}
|
|
118
|
+
iconName="settings"
|
|
119
|
+
color="#000000"
|
|
120
|
+
draggable
|
|
121
|
+
onRemove={onRemove}
|
|
122
|
+
/>
|
|
123
|
+
</div>
|
|
37
124
|
);
|
|
38
125
|
}
|
|
39
126
|
|
|
40
127
|
if (!Component) {
|
|
41
|
-
return
|
|
128
|
+
return (
|
|
129
|
+
<div className="h-full w-full" data-widget-instance-id={widget.i}>
|
|
130
|
+
<Skeleton className="h-full w-full" />
|
|
131
|
+
</div>
|
|
132
|
+
);
|
|
42
133
|
}
|
|
43
134
|
|
|
44
|
-
return
|
|
135
|
+
return (
|
|
136
|
+
<div
|
|
137
|
+
className="group relative h-full w-full"
|
|
138
|
+
data-widget-instance-id={widget.i}
|
|
139
|
+
>
|
|
140
|
+
{onCapture ? (
|
|
141
|
+
<Button
|
|
142
|
+
type="button"
|
|
143
|
+
variant="ghost"
|
|
144
|
+
size="icon"
|
|
145
|
+
className="no-drag absolute right-10 top-3 z-20 size-6 cursor-pointer shrink-0 opacity-0 transition-opacity group-hover:opacity-100"
|
|
146
|
+
onClick={(event) => {
|
|
147
|
+
event.stopPropagation();
|
|
148
|
+
event.preventDefault();
|
|
149
|
+
onCapture();
|
|
150
|
+
}}
|
|
151
|
+
>
|
|
152
|
+
<IconCamera className="size-3" />
|
|
153
|
+
</Button>
|
|
154
|
+
) : null}
|
|
155
|
+
<Component widget={widget} onRemove={onRemove} />
|
|
156
|
+
</div>
|
|
157
|
+
);
|
|
45
158
|
};
|