@hed-hog/core 0.0.297 → 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/auth/auth.controller.d.ts +10 -10
- package/dist/auth/auth.service.d.ts +10 -10
- 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 +21 -1
- package/dist/dashboard/dashboard-core/dashboard-core.controller.d.ts.map +1 -1
- package/dist/dashboard/dashboard-core/dashboard-core.controller.js +9 -0
- package/dist/dashboard/dashboard-core/dashboard-core.controller.js.map +1 -1
- package/dist/dashboard/dashboard-core/dashboard-core.module.d.ts.map +1 -1
- package/dist/dashboard/dashboard-core/dashboard-core.module.js +6 -1
- package/dist/dashboard/dashboard-core/dashboard-core.module.js.map +1 -1
- package/dist/dashboard/dashboard-core/dashboard-core.service.d.ts +180 -2
- package/dist/dashboard/dashboard-core/dashboard-core.service.d.ts.map +1 -1
- package/dist/dashboard/dashboard-core/dashboard-core.service.js +619 -9
- 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/dist/file/file.controller.d.ts.map +1 -1
- package/dist/file/file.controller.js +16 -0
- package/dist/file/file.controller.js.map +1 -1
- package/dist/file/file.service.d.ts +7 -1
- package/dist/file/file.service.d.ts.map +1 -1
- package/dist/file/file.service.js +38 -1
- package/dist/file/file.service.js.map +1 -1
- package/dist/file/provider/s3.provider.d.ts +1 -0
- package/dist/file/provider/s3.provider.d.ts.map +1 -1
- package/dist/file/provider/s3.provider.js +38 -29
- package/dist/file/provider/s3.provider.js.map +1 -1
- package/dist/oauth/oauth.service.d.ts.map +1 -1
- package/dist/oauth/oauth.service.js +2 -1
- package/dist/oauth/oauth.service.js.map +1 -1
- package/dist/user/constants/user.constants.d.ts +1 -0
- package/dist/user/constants/user.constants.d.ts.map +1 -1
- package/dist/user/constants/user.constants.js +2 -1
- package/dist/user/constants/user.constants.js.map +1 -1
- package/dist/user/user.controller.d.ts +10 -10
- package/dist/user/user.service.d.ts +30 -30
- package/dist/user/user.service.d.ts.map +1 -1
- package/dist/user/user.service.js +2 -1
- package/dist/user/user.service.js.map +1 -1
- package/hedhog/data/dashboard_item.yaml +10 -10
- package/hedhog/data/route.yaml +20 -0
- package/hedhog/frontend/app/dashboard/[slug]/dashboard-content.tsx.ejs +212 -34
- 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.email-notifications.tsx.ejs +226 -0
- package/hedhog/frontend/app/dashboard/components/widgets/core.locale-config.tsx.ejs +168 -0
- package/hedhog/frontend/app/dashboard/components/widgets/core.mail-config.tsx.ejs +199 -0
- package/hedhog/frontend/app/dashboard/components/widgets/core.oauth-config.tsx.ejs +175 -0
- package/hedhog/frontend/app/dashboard/components/widgets/core.profile-card.tsx.ejs +186 -0
- package/hedhog/frontend/app/dashboard/components/widgets/core.storage-config.tsx.ejs +196 -0
- package/hedhog/frontend/app/dashboard/components/widgets/core.theme-config.tsx.ejs +213 -0
- package/hedhog/frontend/app/dashboard/components/widgets/core.user-roles.tsx.ejs +132 -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 +93 -0
- package/hedhog/frontend/messages/pt.json +93 -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/{app/dashboard/components/widgets → widgets}/email-notifications.tsx.ejs +85 -61
- 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/{app/dashboard/components/widgets → widgets}/profile-card.tsx.ejs +3 -3
- 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/{app/dashboard/components/widgets → widgets}/user-roles.tsx.ejs +12 -14
- package/hedhog/frontend/{app/dashboard/components/widgets → widgets}/user-sessions.tsx.ejs +1 -1
- package/hedhog/table/dashboard_component.yaml +7 -0
- package/hedhog/table/mail_sent_user.yaml +75 -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.controller.ts +5 -0
- package/src/dashboard/dashboard-core/dashboard-core.module.ts +6 -1
- package/src/dashboard/dashboard-core/dashboard-core.service.ts +874 -8
- package/src/file/file.controller.ts +37 -13
- package/src/file/file.service.ts +47 -5
- package/src/file/provider/s3.provider.ts +39 -29
- package/src/oauth/oauth.service.ts +8 -7
- package/src/user/constants/user.constants.ts +1 -0
- package/src/user/user.service.ts +2 -1
- package/hedhog/frontend/app/dashboard/components/widgets/locale-config.tsx.ejs +0 -309
- package/hedhog/frontend/app/dashboard/components/widgets/mail-config.tsx.ejs +0 -445
- package/hedhog/frontend/app/dashboard/components/widgets/oauth-config.tsx.ejs +0 -296
- package/hedhog/frontend/app/dashboard/components/widgets/storage-config.tsx.ejs +0 -340
- package/hedhog/frontend/app/dashboard/components/widgets/theme-config.tsx.ejs +0 -275
- /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/{login-history-chart.tsx.ejs → core.login-history-chart.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/{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/{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/{user-growth-chart.tsx.ejs → core.user-growth-chart.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,16 +36,112 @@ 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
|
+
|
|
49
|
+
const USER_STATS_WIDGETS = new Set([
|
|
50
|
+
'stat-online-time',
|
|
51
|
+
'stat-actions-today',
|
|
52
|
+
'stat-consecutive-days',
|
|
53
|
+
'stat-access-level',
|
|
54
|
+
]);
|
|
55
|
+
|
|
56
|
+
const USER_POST_HISTORY_WIDGETS = new Set([
|
|
57
|
+
'account-security',
|
|
58
|
+
'email-notifications',
|
|
59
|
+
]);
|
|
60
|
+
|
|
61
|
+
const USER_BOTTOM_WIDGETS = new Set(['user-roles', 'activity-timeline']);
|
|
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
|
+
|
|
84
|
+
const normalizeUserDashboardLayout = (item: WidgetLayout): LayoutItem => {
|
|
85
|
+
const baseSlug = getWidgetBaseSlug(item.slug);
|
|
86
|
+
|
|
87
|
+
const layoutItem: LayoutItem = {
|
|
88
|
+
i: item.i,
|
|
89
|
+
x: item.x,
|
|
90
|
+
y: item.y,
|
|
91
|
+
w: item.w,
|
|
92
|
+
h: item.h,
|
|
93
|
+
minW: item.minW || 1,
|
|
94
|
+
maxW: item.maxW || 12,
|
|
95
|
+
minH: item.minH || 1,
|
|
96
|
+
maxH: item.maxH || 10,
|
|
97
|
+
static: false,
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
if (baseSlug === 'profile-card') {
|
|
101
|
+
layoutItem.h = Math.max(item.h, 4);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (USER_STATS_WIDGETS.has(baseSlug)) {
|
|
105
|
+
layoutItem.y = Math.max(item.y, 4);
|
|
106
|
+
return layoutItem;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (baseSlug === 'login-history-chart') {
|
|
110
|
+
layoutItem.y = Math.max(item.y, 5);
|
|
111
|
+
return layoutItem;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (USER_POST_HISTORY_WIDGETS.has(baseSlug)) {
|
|
115
|
+
layoutItem.y = Math.max(item.y, 9);
|
|
116
|
+
return layoutItem;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (USER_BOTTOM_WIDGETS.has(baseSlug)) {
|
|
120
|
+
layoutItem.y = Math.max(item.y, 14);
|
|
121
|
+
layoutItem.h = item.h + 1;
|
|
122
|
+
return layoutItem;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if (layoutItem.y >= 4) {
|
|
126
|
+
layoutItem.y += 1;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return layoutItem;
|
|
130
|
+
};
|
|
131
|
+
|
|
38
132
|
export const DashboardContent = ({ dashboardSlug }: DashboardContentProps) => {
|
|
39
133
|
const t = useTranslations('core.DashboardPage');
|
|
40
134
|
const { request } = useApp();
|
|
41
135
|
const router = useRouter();
|
|
42
136
|
const isMobile = useIsMobile();
|
|
137
|
+
const isDevelopment = process.env.NODE_ENV === 'development';
|
|
43
138
|
|
|
44
139
|
const [layout, setLayout] = useState<LayoutItem[]>([]);
|
|
45
140
|
const [widgets, setWidgets] = useState<WidgetLayout[]>([]);
|
|
46
141
|
const [hasChanges, setHasChanges] = useState(false);
|
|
47
142
|
const [isSaving, setIsSaving] = useState(false);
|
|
143
|
+
const [componentsPage, setComponentsPage] = useState(1);
|
|
144
|
+
const [componentsPageSize, setComponentsPageSize] = useState(12);
|
|
48
145
|
|
|
49
146
|
const { data: dashboardAccess, isLoading: isCheckingAccess } =
|
|
50
147
|
useQuery<DashboardAccessResponse>({
|
|
@@ -69,17 +166,26 @@ export const DashboardContent = ({ dashboardSlug }: DashboardContentProps) => {
|
|
|
69
166
|
}, [dashboardAccess, dashboardSlug, router]);
|
|
70
167
|
|
|
71
168
|
const {
|
|
72
|
-
data:
|
|
169
|
+
data: availableComponentsResponse,
|
|
73
170
|
isLoading: isLoadingComponents,
|
|
74
171
|
refetch: refetchComponents,
|
|
75
|
-
} = useQuery<
|
|
76
|
-
queryKey: [
|
|
172
|
+
} = useQuery<DashboardComponentsPage>({
|
|
173
|
+
queryKey: [
|
|
174
|
+
'dashboard-components',
|
|
175
|
+
dashboardSlug,
|
|
176
|
+
componentsPage,
|
|
177
|
+
componentsPageSize,
|
|
178
|
+
],
|
|
77
179
|
queryFn: async () => {
|
|
78
|
-
const { data } = await request<
|
|
180
|
+
const { data } = await request<DashboardComponentsPage>({
|
|
79
181
|
url: '/dashboard-component/user',
|
|
80
182
|
method: 'GET',
|
|
183
|
+
params: {
|
|
184
|
+
page: componentsPage,
|
|
185
|
+
pageSize: componentsPageSize,
|
|
186
|
+
},
|
|
81
187
|
});
|
|
82
|
-
return data
|
|
188
|
+
return data;
|
|
83
189
|
},
|
|
84
190
|
enabled: dashboardAccess?.hasAccess ?? false,
|
|
85
191
|
});
|
|
@@ -103,21 +209,22 @@ export const DashboardContent = ({ dashboardSlug }: DashboardContentProps) => {
|
|
|
103
209
|
useEffect(() => {
|
|
104
210
|
if (userLayout) {
|
|
105
211
|
if (userLayout.length > 0) {
|
|
106
|
-
const gridLayout = userLayout.map((item) =>
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
212
|
+
const gridLayout = userLayout.map((item) =>
|
|
213
|
+
dashboardSlug === 'user'
|
|
214
|
+
? normalizeUserDashboardLayout(item)
|
|
215
|
+
: {
|
|
216
|
+
i: item.i,
|
|
217
|
+
x: item.x,
|
|
218
|
+
y: item.y,
|
|
219
|
+
w: item.w,
|
|
220
|
+
h: item.h,
|
|
221
|
+
minW: item.minW || 1,
|
|
222
|
+
maxW: item.maxW || 12,
|
|
223
|
+
minH: item.minH || 1,
|
|
224
|
+
maxH: item.maxH || 10,
|
|
225
|
+
static: false,
|
|
226
|
+
}
|
|
227
|
+
);
|
|
121
228
|
|
|
122
229
|
setLayout(gridLayout);
|
|
123
230
|
setWidgets(userLayout);
|
|
@@ -127,16 +234,19 @@ export const DashboardContent = ({ dashboardSlug }: DashboardContentProps) => {
|
|
|
127
234
|
}
|
|
128
235
|
setHasChanges(false);
|
|
129
236
|
}
|
|
130
|
-
}, [userLayout]);
|
|
237
|
+
}, [userLayout, dashboardSlug]);
|
|
131
238
|
|
|
132
|
-
const componentsToFilter =
|
|
133
|
-
|
|
134
|
-
|
|
239
|
+
const componentsToFilter = availableComponentsResponse?.data || [];
|
|
240
|
+
const totalAvailableComponents =
|
|
241
|
+
availableComponentsResponse?.total ?? componentsToFilter.length;
|
|
135
242
|
|
|
136
243
|
const filteredComponents =
|
|
137
244
|
componentsToFilter.filter(
|
|
138
245
|
(component: DashboardComponent) =>
|
|
139
|
-
!widgets.some(
|
|
246
|
+
!widgets.some(
|
|
247
|
+
(widget) =>
|
|
248
|
+
getWidgetIdentityKey(widget) === getWidgetIdentityKey(component)
|
|
249
|
+
)
|
|
140
250
|
) || [];
|
|
141
251
|
|
|
142
252
|
const handleLayoutChange = useCallback((newLayout: LayoutItem[]) => {
|
|
@@ -167,19 +277,23 @@ export const DashboardContent = ({ dashboardSlug }: DashboardContentProps) => {
|
|
|
167
277
|
}
|
|
168
278
|
};
|
|
169
279
|
|
|
170
|
-
const handleAddWidget = async (
|
|
280
|
+
const handleAddWidget = async (slugs: string[]) => {
|
|
281
|
+
if (!slugs.length) return;
|
|
282
|
+
|
|
171
283
|
try {
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
284
|
+
for (const slug of slugs) {
|
|
285
|
+
await request({
|
|
286
|
+
url: `/dashboard-core/widget/${dashboardSlug}`,
|
|
287
|
+
method: 'POST',
|
|
288
|
+
data: { componentSlug: slug },
|
|
289
|
+
});
|
|
290
|
+
}
|
|
177
291
|
|
|
178
292
|
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
179
293
|
await Promise.all([refetchLayout(), refetchComponents()]);
|
|
180
294
|
setHasChanges(false);
|
|
181
295
|
} catch (error) {
|
|
182
|
-
console.error('Erro ao adicionar
|
|
296
|
+
console.error('Erro ao adicionar widgets:', error);
|
|
183
297
|
}
|
|
184
298
|
};
|
|
185
299
|
|
|
@@ -196,11 +310,65 @@ export const DashboardContent = ({ dashboardSlug }: DashboardContentProps) => {
|
|
|
196
310
|
}
|
|
197
311
|
};
|
|
198
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
|
+
|
|
199
362
|
const renderWidget = (widget: WidgetLayout) => {
|
|
200
363
|
return (
|
|
201
364
|
<WidgetRenderer
|
|
202
365
|
widget={widget}
|
|
203
366
|
onRemove={() => handleRemoveWidget(widget.i)}
|
|
367
|
+
onCapture={
|
|
368
|
+
isDevelopment
|
|
369
|
+
? () => handleCaptureWidgetPreview(widget.i, widget.component_id)
|
|
370
|
+
: undefined
|
|
371
|
+
}
|
|
204
372
|
/>
|
|
205
373
|
);
|
|
206
374
|
};
|
|
@@ -320,7 +488,17 @@ export const DashboardContent = ({ dashboardSlug }: DashboardContentProps) => {
|
|
|
320
488
|
)}
|
|
321
489
|
<AddWidgetSelectorDialog
|
|
322
490
|
availableComponents={filteredComponents}
|
|
491
|
+
totalItems={totalAvailableComponents}
|
|
492
|
+
currentPage={availableComponentsResponse?.page ?? componentsPage}
|
|
493
|
+
pageSize={
|
|
494
|
+
availableComponentsResponse?.pageSize ?? componentsPageSize
|
|
495
|
+
}
|
|
323
496
|
isLoading={isLoadingComponents}
|
|
497
|
+
onPageChange={setComponentsPage}
|
|
498
|
+
onPageSizeChange={(nextPageSize) => {
|
|
499
|
+
setComponentsPageSize(nextPageSize);
|
|
500
|
+
setComponentsPage(1);
|
|
501
|
+
}}
|
|
324
502
|
onAdd={handleAddWidget}
|
|
325
503
|
currentSlug={dashboardSlug}
|
|
326
504
|
/>
|
|
@@ -329,7 +507,7 @@ export const DashboardContent = ({ dashboardSlug }: DashboardContentProps) => {
|
|
|
329
507
|
</header>
|
|
330
508
|
<div className="flex flex-1 flex-col gap-4 overflow-auto p-4 pt-0">
|
|
331
509
|
{widgets.length > 0 ? (
|
|
332
|
-
<div className="min-h-
|
|
510
|
+
<div className="min-h-105 sm:min-h-130 lg:min-h-150">
|
|
333
511
|
<DraggableGrid
|
|
334
512
|
className="dashboard-grid"
|
|
335
513
|
layout={layout}
|
|
@@ -347,7 +525,7 @@ export const DashboardContent = ({ dashboardSlug }: DashboardContentProps) => {
|
|
|
347
525
|
</DraggableGrid>
|
|
348
526
|
</div>
|
|
349
527
|
) : (
|
|
350
|
-
<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">
|
|
351
529
|
<h3 className="text-lg font-semibold">{t('noWidgetAdded')}</h3>
|
|
352
530
|
<p className="text-muted-foreground mt-2 text-sm">
|
|
353
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
|
};
|