@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.
Files changed (139) hide show
  1. package/dist/dashboard/dashboard/dashboard.controller.d.ts +3 -0
  2. package/dist/dashboard/dashboard/dashboard.controller.d.ts.map +1 -1
  3. package/dist/dashboard/dashboard/dashboard.service.d.ts +3 -0
  4. package/dist/dashboard/dashboard/dashboard.service.d.ts.map +1 -1
  5. package/dist/dashboard/dashboard-component/dashboard-component.controller.d.ts +12 -0
  6. package/dist/dashboard/dashboard-component/dashboard-component.controller.d.ts.map +1 -1
  7. package/dist/dashboard/dashboard-component/dashboard-component.controller.js +22 -0
  8. package/dist/dashboard/dashboard-component/dashboard-component.controller.js.map +1 -1
  9. package/dist/dashboard/dashboard-component/dashboard-component.service.d.ts +15 -0
  10. package/dist/dashboard/dashboard-component/dashboard-component.service.d.ts.map +1 -1
  11. package/dist/dashboard/dashboard-component/dashboard-component.service.js +110 -3
  12. package/dist/dashboard/dashboard-component/dashboard-component.service.js.map +1 -1
  13. package/dist/dashboard/dashboard-component/dto/create.dto.d.ts +1 -0
  14. package/dist/dashboard/dashboard-component/dto/create.dto.d.ts.map +1 -1
  15. package/dist/dashboard/dashboard-component/dto/create.dto.js +5 -0
  16. package/dist/dashboard/dashboard-component/dto/create.dto.js.map +1 -1
  17. package/dist/dashboard/dashboard-component/dto/update.dto.d.ts +1 -0
  18. package/dist/dashboard/dashboard-component/dto/update.dto.d.ts.map +1 -1
  19. package/dist/dashboard/dashboard-component/dto/update.dto.js +5 -0
  20. package/dist/dashboard/dashboard-component/dto/update.dto.js.map +1 -1
  21. package/dist/dashboard/dashboard-component-role/dashboard-component-role.controller.d.ts +1 -0
  22. package/dist/dashboard/dashboard-component-role/dashboard-component-role.controller.d.ts.map +1 -1
  23. package/dist/dashboard/dashboard-component-role/dashboard-component-role.service.d.ts +1 -0
  24. package/dist/dashboard/dashboard-component-role/dashboard-component-role.service.d.ts.map +1 -1
  25. package/dist/dashboard/dashboard-core/dashboard-core.controller.d.ts +7 -1
  26. package/dist/dashboard/dashboard-core/dashboard-core.controller.d.ts.map +1 -1
  27. package/dist/dashboard/dashboard-core/dashboard-core.service.d.ts +7 -1
  28. package/dist/dashboard/dashboard-core/dashboard-core.service.d.ts.map +1 -1
  29. package/dist/dashboard/dashboard-core/dashboard-core.service.js +88 -4
  30. package/dist/dashboard/dashboard-core/dashboard-core.service.js.map +1 -1
  31. package/dist/dashboard/dashboard-item/dashboard-item.controller.d.ts +1 -0
  32. package/dist/dashboard/dashboard-item/dashboard-item.controller.d.ts.map +1 -1
  33. package/dist/dashboard/dashboard-item/dashboard-item.service.d.ts +1 -0
  34. package/dist/dashboard/dashboard-item/dashboard-item.service.d.ts.map +1 -1
  35. package/hedhog/data/dashboard_item.yaml +1 -1
  36. package/hedhog/data/route.yaml +12 -0
  37. package/hedhog/frontend/app/dashboard/[slug]/dashboard-content.tsx.ejs +141 -24
  38. package/hedhog/frontend/app/dashboard/[slug]/types.ts.ejs +3 -0
  39. package/hedhog/frontend/app/dashboard/[slug]/widget-renderer.tsx.ejs +136 -23
  40. package/hedhog/frontend/app/dashboard/components/add-widget-selector-dialog.tsx.ejs +266 -85
  41. package/hedhog/frontend/app/dashboard/components/widgets/core..gitkeep.ejs +11 -0
  42. package/hedhog/frontend/app/dashboard/components/widgets/core.account-security.tsx.ejs +192 -0
  43. package/hedhog/frontend/app/dashboard/components/widgets/core.user-sessions.tsx.ejs +236 -0
  44. package/hedhog/frontend/app/dashboard/components/widgets/finance.alerts.tsx.ejs +108 -0
  45. package/hedhog/frontend/app/dashboard/components/widgets/finance.cash-balance-kpi.tsx.ejs +66 -0
  46. package/hedhog/frontend/app/dashboard/components/widgets/finance.cash-flow-chart.tsx.ejs +122 -0
  47. package/hedhog/frontend/app/dashboard/components/widgets/finance.default-kpi.tsx.ejs +63 -0
  48. package/hedhog/frontend/app/dashboard/components/widgets/finance.payable-30d-kpi.tsx.ejs +73 -0
  49. package/hedhog/frontend/app/dashboard/components/widgets/finance.receivable-30d-kpi.tsx.ejs +73 -0
  50. package/hedhog/frontend/app/dashboard/components/widgets/finance.upcoming-payable.tsx.ejs +123 -0
  51. package/hedhog/frontend/app/dashboard/components/widgets/finance.upcoming-receivable.tsx.ejs +118 -0
  52. package/hedhog/frontend/messages/en.json +3 -0
  53. package/hedhog/frontend/messages/pt.json +3 -0
  54. package/hedhog/frontend/public/dashboard-previews/.gitkeep +12 -0
  55. package/hedhog/frontend/public/dashboard-previews/account-security.png +0 -0
  56. package/hedhog/frontend/public/dashboard-previews/active-users-card.png +0 -0
  57. package/hedhog/frontend/public/dashboard-previews/activity-timeline.png +0 -0
  58. package/hedhog/frontend/public/dashboard-previews/cash-balance-kpi.png +0 -0
  59. package/hedhog/frontend/public/dashboard-previews/cash-flow-chart.png +0 -0
  60. package/hedhog/frontend/public/dashboard-previews/default-kpi.png +0 -0
  61. package/hedhog/frontend/public/dashboard-previews/email-notifications.png +0 -0
  62. package/hedhog/frontend/public/dashboard-previews/financial-alerts.png +0 -0
  63. package/hedhog/frontend/public/dashboard-previews/login-history-chart.png +0 -0
  64. package/hedhog/frontend/public/dashboard-previews/mail-sent-card.png +0 -0
  65. package/hedhog/frontend/public/dashboard-previews/mail-sent-chart.png +0 -0
  66. package/hedhog/frontend/public/dashboard-previews/menus-card.png +0 -0
  67. package/hedhog/frontend/public/dashboard-previews/payable-30d-kpi.png +0 -0
  68. package/hedhog/frontend/public/dashboard-previews/permissions-card.png +0 -0
  69. package/hedhog/frontend/public/dashboard-previews/permissions-chart.png +0 -0
  70. package/hedhog/frontend/public/dashboard-previews/profile-card.png +0 -0
  71. package/hedhog/frontend/public/dashboard-previews/receivable-30d-kpi.png +0 -0
  72. package/hedhog/frontend/public/dashboard-previews/routes-card.png +0 -0
  73. package/hedhog/frontend/public/dashboard-previews/session-activity-chart.png +0 -0
  74. package/hedhog/frontend/public/dashboard-previews/sessions-today-card.png +0 -0
  75. package/hedhog/frontend/public/dashboard-previews/stat-access-level.png +0 -0
  76. package/hedhog/frontend/public/dashboard-previews/stat-actions-today.png +0 -0
  77. package/hedhog/frontend/public/dashboard-previews/stat-consecutive-days.png +0 -0
  78. package/hedhog/frontend/public/dashboard-previews/stat-online-time.png +0 -0
  79. package/hedhog/frontend/public/dashboard-previews/upcoming-payable.png +0 -0
  80. package/hedhog/frontend/public/dashboard-previews/upcoming-receivable.png +0 -0
  81. package/hedhog/frontend/public/dashboard-previews/user-growth-chart.png +0 -0
  82. package/hedhog/frontend/public/dashboard-previews/user-roles.png +0 -0
  83. package/hedhog/frontend/{app/dashboard/components/widgets → widgets}/account-security.tsx.ejs +33 -29
  84. package/hedhog/frontend/widgets/active-users-card.tsx.ejs +58 -0
  85. package/hedhog/frontend/widgets/activity-timeline.tsx.ejs +223 -0
  86. package/hedhog/frontend/widgets/email-notifications.tsx.ejs +226 -0
  87. package/hedhog/frontend/widgets/locale-config.tsx.ejs +168 -0
  88. package/hedhog/frontend/widgets/login-history-chart.tsx.ejs +115 -0
  89. package/hedhog/frontend/widgets/mail-config.tsx.ejs +199 -0
  90. package/hedhog/frontend/widgets/mail-sent-card.tsx.ejs +58 -0
  91. package/hedhog/frontend/widgets/mail-sent-chart.tsx.ejs +149 -0
  92. package/hedhog/frontend/widgets/menus-card.tsx.ejs +58 -0
  93. package/hedhog/frontend/widgets/oauth-config.tsx.ejs +175 -0
  94. package/hedhog/frontend/widgets/permissions-card.tsx.ejs +61 -0
  95. package/hedhog/frontend/widgets/permissions-chart.tsx.ejs +156 -0
  96. package/hedhog/frontend/widgets/profile-card.tsx.ejs +186 -0
  97. package/hedhog/frontend/widgets/routes-card.tsx.ejs +58 -0
  98. package/hedhog/frontend/widgets/session-activity-chart.tsx.ejs +183 -0
  99. package/hedhog/frontend/widgets/sessions-today-card.tsx.ejs +62 -0
  100. package/hedhog/frontend/widgets/stat-access-level.tsx.ejs +57 -0
  101. package/hedhog/frontend/widgets/stat-actions-today.tsx.ejs +57 -0
  102. package/hedhog/frontend/widgets/stat-consecutive-days.tsx.ejs +57 -0
  103. package/hedhog/frontend/widgets/stat-online-time.tsx.ejs +57 -0
  104. package/hedhog/frontend/widgets/storage-config.tsx.ejs +196 -0
  105. package/hedhog/frontend/widgets/theme-config.tsx.ejs +213 -0
  106. package/hedhog/frontend/widgets/user-growth-chart.tsx.ejs +210 -0
  107. package/hedhog/frontend/widgets/user-roles.tsx.ejs +132 -0
  108. package/hedhog/frontend/{app/dashboard/components/widgets → widgets}/user-sessions.tsx.ejs +1 -1
  109. package/hedhog/table/dashboard_component.yaml +7 -0
  110. package/package.json +4 -4
  111. package/src/dashboard/dashboard-component/dashboard-component.controller.ts +36 -12
  112. package/src/dashboard/dashboard-component/dashboard-component.service.ts +150 -3
  113. package/src/dashboard/dashboard-component/dto/create.dto.ts +4 -0
  114. package/src/dashboard/dashboard-component/dto/update.dto.ts +4 -0
  115. package/src/dashboard/dashboard-core/dashboard-core.service.ts +108 -5
  116. /package/hedhog/frontend/app/dashboard/components/widgets/{active-users-card.tsx.ejs → core.active-users-card.tsx.ejs} +0 -0
  117. /package/hedhog/frontend/app/dashboard/components/widgets/{activity-timeline.tsx.ejs → core.activity-timeline.tsx.ejs} +0 -0
  118. /package/hedhog/frontend/app/dashboard/components/widgets/{email-notifications.tsx.ejs → core.email-notifications.tsx.ejs} +0 -0
  119. /package/hedhog/frontend/app/dashboard/components/widgets/{locale-config.tsx.ejs → core.locale-config.tsx.ejs} +0 -0
  120. /package/hedhog/frontend/app/dashboard/components/widgets/{login-history-chart.tsx.ejs → core.login-history-chart.tsx.ejs} +0 -0
  121. /package/hedhog/frontend/app/dashboard/components/widgets/{mail-config.tsx.ejs → core.mail-config.tsx.ejs} +0 -0
  122. /package/hedhog/frontend/app/dashboard/components/widgets/{mail-sent-card.tsx.ejs → core.mail-sent-card.tsx.ejs} +0 -0
  123. /package/hedhog/frontend/app/dashboard/components/widgets/{mail-sent-chart.tsx.ejs → core.mail-sent-chart.tsx.ejs} +0 -0
  124. /package/hedhog/frontend/app/dashboard/components/widgets/{menus-card.tsx.ejs → core.menus-card.tsx.ejs} +0 -0
  125. /package/hedhog/frontend/app/dashboard/components/widgets/{oauth-config.tsx.ejs → core.oauth-config.tsx.ejs} +0 -0
  126. /package/hedhog/frontend/app/dashboard/components/widgets/{permissions-card.tsx.ejs → core.permissions-card.tsx.ejs} +0 -0
  127. /package/hedhog/frontend/app/dashboard/components/widgets/{permissions-chart.tsx.ejs → core.permissions-chart.tsx.ejs} +0 -0
  128. /package/hedhog/frontend/app/dashboard/components/widgets/{profile-card.tsx.ejs → core.profile-card.tsx.ejs} +0 -0
  129. /package/hedhog/frontend/app/dashboard/components/widgets/{routes-card.tsx.ejs → core.routes-card.tsx.ejs} +0 -0
  130. /package/hedhog/frontend/app/dashboard/components/widgets/{session-activity-chart.tsx.ejs → core.session-activity-chart.tsx.ejs} +0 -0
  131. /package/hedhog/frontend/app/dashboard/components/widgets/{sessions-today-card.tsx.ejs → core.sessions-today-card.tsx.ejs} +0 -0
  132. /package/hedhog/frontend/app/dashboard/components/widgets/{stat-access-level.tsx.ejs → core.stat-access-level.tsx.ejs} +0 -0
  133. /package/hedhog/frontend/app/dashboard/components/widgets/{stat-actions-today.tsx.ejs → core.stat-actions-today.tsx.ejs} +0 -0
  134. /package/hedhog/frontend/app/dashboard/components/widgets/{stat-consecutive-days.tsx.ejs → core.stat-consecutive-days.tsx.ejs} +0 -0
  135. /package/hedhog/frontend/app/dashboard/components/widgets/{stat-online-time.tsx.ejs → core.stat-online-time.tsx.ejs} +0 -0
  136. /package/hedhog/frontend/app/dashboard/components/widgets/{storage-config.tsx.ejs → core.storage-config.tsx.ejs} +0 -0
  137. /package/hedhog/frontend/app/dashboard/components/widgets/{theme-config.tsx.ejs → core.theme-config.tsx.ejs} +0 -0
  138. /package/hedhog/frontend/app/dashboard/components/widgets/{user-growth-chart.tsx.ejs → core.user-growth-chart.tsx.ejs} +0 -0
  139. /package/hedhog/frontend/app/dashboard/components/widgets/{user-roles.tsx.ejs → core.user-roles.tsx.ejs} +0 -0
@@ -0,0 +1,213 @@
1
+ 'use client';
2
+
3
+ import { Badge } from '@/components/ui/badge';
4
+ import {
5
+ Card,
6
+ CardContent,
7
+ CardDescription,
8
+ CardHeader,
9
+ CardTitle,
10
+ } from '@/components/ui/card';
11
+ import { useWidgetData } from '@/hooks/use-widget-data';
12
+ import type {
13
+ DashboardCoreConfigOverviewData,
14
+ ThemeConfigWidgetData,
15
+ } from '@/types/widget-data';
16
+ import { Palette } from 'lucide-react';
17
+ import { useTranslations } from 'next-intl';
18
+ import { WidgetWrapper } from '../widget-wrapper';
19
+
20
+ const emptyPaletteMode = {
21
+ primary: null,
22
+ primaryForeground: null,
23
+ secondary: null,
24
+ secondaryForeground: null,
25
+ accent: null,
26
+ accentForeground: null,
27
+ muted: null,
28
+ mutedForeground: null,
29
+ background: null,
30
+ backgroundForeground: null,
31
+ card: null,
32
+ cardForeground: null,
33
+ };
34
+
35
+ const defaultThemeConfigData: ThemeConfigWidgetData = {
36
+ status: {
37
+ isConfigured: false,
38
+ configuredTokenCount: 0,
39
+ },
40
+ branding: {
41
+ systemName: null,
42
+ systemSlogan: null,
43
+ iconUrl: null,
44
+ imageUrl: null,
45
+ },
46
+ presentation: {
47
+ mode: null,
48
+ font: null,
49
+ textSize: null,
50
+ radius: null,
51
+ },
52
+ palette: {
53
+ light: emptyPaletteMode,
54
+ dark: emptyPaletteMode,
55
+ },
56
+ };
57
+
58
+ interface ThemeConfigProps {
59
+ widget?: { name?: string };
60
+ onRemove?: () => void;
61
+ }
62
+
63
+ export default function ThemeConfig({ widget, onRemove }: ThemeConfigProps) {
64
+ const t = useTranslations('core.DashboardPage.themeConfig');
65
+
66
+ const { data, isLoading, isError, isAccessDenied } = useWidgetData<
67
+ DashboardCoreConfigOverviewData,
68
+ ThemeConfigWidgetData
69
+ >({
70
+ endpoint: '/dashboard-core/config/overview',
71
+ queryKey: 'dashboard-core-config-overview',
72
+ select: (d) => d.themeConfig,
73
+ });
74
+
75
+ const themeData = data ?? defaultThemeConfigData;
76
+
77
+ return (
78
+ <WidgetWrapper
79
+ isLoading={isLoading}
80
+ isError={isError}
81
+ isAccessDenied={isAccessDenied}
82
+ widgetName={widget?.name ?? t('title')}
83
+ onRemove={onRemove}
84
+ >
85
+ <Card className="flex h-full min-h-0 flex-col overflow-hidden">
86
+ <CardHeader className="shrink-0">
87
+ <div className="flex items-center justify-between gap-3">
88
+ <div className="flex items-center gap-3">
89
+ <div className="flex h-10 w-10 items-center justify-center rounded-lg bg-rose-50">
90
+ <Palette className="h-5 w-5 text-rose-600" />
91
+ </div>
92
+ <div>
93
+ <CardTitle className="text-base">{t('title')}</CardTitle>
94
+ <CardDescription>{t('description')}</CardDescription>
95
+ </div>
96
+ </div>
97
+ <Badge
98
+ variant="secondary"
99
+ className={
100
+ themeData.status.isConfigured
101
+ ? 'bg-emerald-50 text-emerald-700'
102
+ : 'bg-amber-50 text-amber-700'
103
+ }
104
+ >
105
+ {themeData.status.isConfigured ? t('configured') : t('pending')}
106
+ </Badge>
107
+ </div>
108
+ </CardHeader>
109
+ <CardContent className="flex min-h-0 flex-1 flex-col gap-4 overflow-hidden pt-0">
110
+ <div className="rounded-lg border bg-muted/30 p-4">
111
+ <div className="flex items-center gap-3 rounded-lg border bg-background p-3">
112
+ <div
113
+ className="flex h-10 w-10 items-center justify-center rounded-lg text-sm font-bold text-white"
114
+ style={{
115
+ backgroundColor: themeData.palette.light.primary || '#64748b',
116
+ }}
117
+ >
118
+ {(themeData.branding.systemName || 'S').charAt(0).toUpperCase()}
119
+ </div>
120
+ <div className="min-w-0">
121
+ <p className="truncate text-sm font-semibold">
122
+ {themeData.branding.systemName || t('notSet')}
123
+ </p>
124
+ <p className="truncate text-xs text-muted-foreground">
125
+ {themeData.branding.systemSlogan || t('notSet')}
126
+ </p>
127
+ </div>
128
+ <Badge variant="outline" className="ml-auto text-[10px]">
129
+ {themeData.presentation.mode || t('notSet')}
130
+ </Badge>
131
+ </div>
132
+ </div>
133
+
134
+ <div className="grid grid-cols-2 gap-2 sm:grid-cols-4">
135
+ <div className="rounded-lg border p-3 text-center">
136
+ <p className="text-[11px] text-muted-foreground">{t('tokens')}</p>
137
+ <p className="mt-1 text-lg font-semibold">
138
+ {themeData.status.configuredTokenCount}
139
+ </p>
140
+ </div>
141
+ <div className="rounded-lg border p-3 text-center">
142
+ <p className="text-[11px] text-muted-foreground">{t('font')}</p>
143
+ <p className="mt-1 truncate text-sm font-medium">
144
+ {themeData.presentation.font || t('notSet')}
145
+ </p>
146
+ </div>
147
+ <div className="rounded-lg border p-3 text-center">
148
+ <p className="text-[11px] text-muted-foreground">
149
+ {t('textSize')}
150
+ </p>
151
+ <p className="mt-1 truncate text-sm font-medium">
152
+ {themeData.presentation.textSize || t('notSet')}
153
+ </p>
154
+ </div>
155
+ <div className="rounded-lg border p-3 text-center">
156
+ <p className="text-[11px] text-muted-foreground">{t('radius')}</p>
157
+ <p className="mt-1 truncate text-sm font-medium">
158
+ {themeData.presentation.radius || t('notSet')}
159
+ </p>
160
+ </div>
161
+ </div>
162
+
163
+ <div className="grid gap-2 sm:grid-cols-2">
164
+ <div className="rounded-lg border p-3">
165
+ <div className="mb-2 flex items-center justify-between">
166
+ <p className="text-xs text-muted-foreground">
167
+ {t('lightPalette')}
168
+ </p>
169
+ <Badge variant="outline" className="text-[10px]">
170
+ {themeData.palette.light.primary || t('notSet')}
171
+ </Badge>
172
+ </div>
173
+ <div className="flex items-center gap-2">
174
+ <span
175
+ className="h-5 w-5 rounded-full border"
176
+ style={{
177
+ backgroundColor:
178
+ themeData.palette.light.primary || 'transparent',
179
+ }}
180
+ />
181
+ <span className="text-xs text-muted-foreground">
182
+ {t('primary')}
183
+ </span>
184
+ </div>
185
+ </div>
186
+ <div className="rounded-lg border p-3">
187
+ <div className="mb-2 flex items-center justify-between">
188
+ <p className="text-xs text-muted-foreground">
189
+ {t('darkPalette')}
190
+ </p>
191
+ <Badge variant="outline" className="text-[10px]">
192
+ {themeData.palette.dark.primary || t('notSet')}
193
+ </Badge>
194
+ </div>
195
+ <div className="flex items-center gap-2">
196
+ <span
197
+ className="h-5 w-5 rounded-full border"
198
+ style={{
199
+ backgroundColor:
200
+ themeData.palette.dark.primary || 'transparent',
201
+ }}
202
+ />
203
+ <span className="text-xs text-muted-foreground">
204
+ {t('primary')}
205
+ </span>
206
+ </div>
207
+ </div>
208
+ </div>
209
+ </CardContent>
210
+ </Card>
211
+ </WidgetWrapper>
212
+ );
213
+ }
@@ -0,0 +1,210 @@
1
+ 'use client';
2
+
3
+ import {
4
+ Card,
5
+ CardContent,
6
+ CardDescription,
7
+ CardHeader,
8
+ CardTitle,
9
+ } from '@/components/ui/card';
10
+ import { useWidgetData } from '@/hooks/use-widget-data';
11
+ import { IconGripVertical } from '@tabler/icons-react';
12
+ import { useTranslations } from 'next-intl';
13
+ import {
14
+ Area,
15
+ AreaChart,
16
+ CartesianGrid,
17
+ ResponsiveContainer,
18
+ Tooltip,
19
+ XAxis,
20
+ YAxis,
21
+ } from 'recharts';
22
+ import { WidgetWrapper } from '../widget-wrapper';
23
+
24
+ function CustomTooltip({
25
+ active,
26
+ payload,
27
+ label,
28
+ }: {
29
+ active?: boolean;
30
+ payload?: Array<{ value: number; dataKey: string }>;
31
+ label?: string;
32
+ }) {
33
+ const t = useTranslations('core.Dashboard');
34
+ if (!active || !payload?.length) return null;
35
+
36
+ return (
37
+ <div className="rounded-lg border bg-card px-3 py-2 shadow-xl">
38
+ <p className="mb-1 text-xs font-medium text-card-foreground">{label}</p>
39
+ {payload.map((entry) => (
40
+ <p key={entry.dataKey} className="text-xs text-muted-foreground">
41
+ <span
42
+ className="mr-1.5 inline-block h-2 w-2 rounded-full"
43
+ style={{
44
+ backgroundColor:
45
+ entry.dataKey === 'users' ? '#6366f1' : '#10b981',
46
+ }}
47
+ />
48
+ {entry.dataKey === 'users' ? t('users') : t('sessions')}:{' '}
49
+ {entry.value.toLocaleString('pt-BR')}
50
+ </p>
51
+ ))}
52
+ </div>
53
+ );
54
+ }
55
+
56
+ interface UserGrowthChartProps {
57
+ widget?: any;
58
+ onRemove?: () => void;
59
+ }
60
+
61
+ interface UserStatsData {
62
+ charts?: {
63
+ userGrowth?: Array<{
64
+ month: string;
65
+ users: number;
66
+ sessions: number;
67
+ }>;
68
+ };
69
+ }
70
+
71
+ export default function UserGrowthChart({
72
+ widget,
73
+ onRemove,
74
+ }: UserGrowthChartProps) {
75
+ const t = useTranslations('core.Dashboard');
76
+
77
+ const {
78
+ data: statsData,
79
+ isLoading,
80
+ isAccessDenied,
81
+ isError,
82
+ } = useWidgetData<UserStatsData>({
83
+ endpoint: '/dashboard-core/stats/overview/users',
84
+ queryKey: 'dashboard-stats-users',
85
+ });
86
+
87
+ const data = statsData?.charts?.userGrowth || [];
88
+
89
+ return (
90
+ <WidgetWrapper
91
+ isLoading={isLoading}
92
+ isAccessDenied={isAccessDenied}
93
+ isError={isError}
94
+ widgetName={widget?.name || t('userGrowthTitle')}
95
+ onRemove={onRemove}
96
+ >
97
+ <Card className="h-full flex flex-col group">
98
+ <div
99
+ className="drag-handle absolute top-3 left-4 z-10"
100
+ style={{ cursor: 'grab' }}
101
+ >
102
+ <IconGripVertical className="text-muted-foreground/50 size-4 shrink-0" />
103
+ </div>
104
+ <CardHeader className="flex flex-row items-center justify-between pb-2 pt-4">
105
+ <div className="pl-6">
106
+ <CardTitle className="text-base font-semibold">
107
+ {t('userGrowthTitle')}
108
+ </CardTitle>
109
+ <CardDescription>{t('userGrowthDescription')}</CardDescription>
110
+ </div>
111
+ </CardHeader>
112
+ <CardContent className="flex-1 pt-0">
113
+ <div className="h-[280px] w-full">
114
+ <ResponsiveContainer width="100%" height="100%">
115
+ <AreaChart
116
+ data={data}
117
+ margin={{ top: 5, right: 10, left: -20, bottom: 0 }}
118
+ >
119
+ <defs>
120
+ <linearGradient
121
+ id="gradientUsuarios"
122
+ x1="0"
123
+ y1="0"
124
+ x2="0"
125
+ y2="1"
126
+ >
127
+ <stop offset="0%" stopColor="#6366f1" stopOpacity={0.5} />
128
+ <stop
129
+ offset="100%"
130
+ stopColor="#6366f1"
131
+ stopOpacity={0.05}
132
+ />
133
+ </linearGradient>
134
+ <linearGradient
135
+ id="gradientSessoes"
136
+ x1="0"
137
+ y1="0"
138
+ x2="0"
139
+ y2="1"
140
+ >
141
+ <stop offset="0%" stopColor="#10b981" stopOpacity={0.4} />
142
+ <stop
143
+ offset="100%"
144
+ stopColor="#10b981"
145
+ stopOpacity={0.05}
146
+ />
147
+ </linearGradient>
148
+ </defs>
149
+ <CartesianGrid
150
+ strokeDasharray="3 3"
151
+ stroke="currentColor"
152
+ opacity={0.1}
153
+ />
154
+ <XAxis
155
+ dataKey="month"
156
+ tick={{ fill: 'hsl(var(--muted-foreground))', fontSize: 12 }}
157
+ axisLine={false}
158
+ tickLine={false}
159
+ />
160
+ <YAxis
161
+ tick={{ fill: 'hsl(var(--muted-foreground))', fontSize: 12 }}
162
+ axisLine={false}
163
+ tickLine={false}
164
+ />
165
+ <Tooltip content={<CustomTooltip />} />
166
+ <Area
167
+ type="monotone"
168
+ dataKey="sessions"
169
+ stroke="#10b981"
170
+ strokeWidth={2.5}
171
+ fill="url(#gradientSessoes)"
172
+ animationDuration={1500}
173
+ />
174
+ <Area
175
+ type="monotone"
176
+ dataKey="users"
177
+ stroke="#6366f1"
178
+ strokeWidth={2.5}
179
+ fill="url(#gradientUsuarios)"
180
+ animationDuration={1500}
181
+ animationBegin={200}
182
+ />
183
+ </AreaChart>
184
+ </ResponsiveContainer>
185
+ </div>
186
+ <div className="mt-3 flex items-center justify-center gap-6">
187
+ <div className="flex items-center gap-2">
188
+ <div
189
+ className="h-2.5 w-2.5 rounded-full"
190
+ style={{ backgroundColor: '#6366f1' }}
191
+ />
192
+ <span className="text-xs text-muted-foreground">
193
+ {t('users')}
194
+ </span>
195
+ </div>
196
+ <div className="flex items-center gap-2">
197
+ <div
198
+ className="h-2.5 w-2.5 rounded-full"
199
+ style={{ backgroundColor: '#10b981' }}
200
+ />
201
+ <span className="text-xs text-muted-foreground">
202
+ {t('sessions')}
203
+ </span>
204
+ </div>
205
+ </div>
206
+ </CardContent>
207
+ </Card>
208
+ </WidgetWrapper>
209
+ );
210
+ }
@@ -0,0 +1,132 @@
1
+ 'use client';
2
+
3
+ import { Badge } from '@/components/ui/badge';
4
+ import {
5
+ Card,
6
+ CardContent,
7
+ CardDescription,
8
+ CardHeader,
9
+ CardTitle,
10
+ } from '@/components/ui/card';
11
+ import { useWidgetData } from '@/hooks/use-widget-data';
12
+ import type { AllWidgetsData, RoleData } from '@/types/widget-data';
13
+ import { Crown, ShieldCheck } from 'lucide-react';
14
+ import { useTranslations } from 'next-intl';
15
+ import { WidgetWrapper } from '../widget-wrapper';
16
+
17
+ const levelStyles = [
18
+ {
19
+ border: 'border-blue-200 dark:border-blue-800',
20
+ bg: 'bg-blue-50/50 dark:bg-blue-950/30',
21
+ iconBg: 'bg-blue-100 dark:bg-blue-900/50',
22
+ iconColor: 'text-blue-600 dark:text-blue-400',
23
+ badge: 'bg-blue-50 text-blue-700 dark:bg-blue-950/40 dark:text-blue-400',
24
+ },
25
+ {
26
+ border: 'border-indigo-200 dark:border-indigo-800',
27
+ bg: 'bg-indigo-50/30 dark:bg-indigo-950/20',
28
+ iconBg: 'bg-indigo-100 dark:bg-indigo-900/50',
29
+ iconColor: 'text-indigo-600 dark:text-indigo-400',
30
+ badge:
31
+ 'bg-indigo-50 text-indigo-700 dark:bg-indigo-950/40 dark:text-indigo-400',
32
+ },
33
+ {
34
+ border: 'border-emerald-200 dark:border-emerald-800',
35
+ bg: 'bg-emerald-50/30 dark:bg-emerald-950/20',
36
+ iconBg: 'bg-emerald-100 dark:bg-emerald-900/50',
37
+ iconColor: 'text-emerald-600 dark:text-emerald-400',
38
+ badge:
39
+ 'bg-emerald-50 text-emerald-700 dark:bg-emerald-950/40 dark:text-emerald-400',
40
+ },
41
+ {
42
+ border: 'border-amber-200 dark:border-amber-800',
43
+ bg: 'bg-amber-50/30 dark:bg-amber-950/20',
44
+ iconBg: 'bg-amber-100 dark:bg-amber-900/50',
45
+ iconColor: 'text-amber-600 dark:text-amber-400',
46
+ badge:
47
+ 'bg-amber-50 text-amber-700 dark:bg-amber-950/40 dark:text-amber-400',
48
+ },
49
+ ];
50
+
51
+ function RolesContent({ roles }: { roles: RoleData[] }) {
52
+ const t = useTranslations('core.DashboardPage.userRoles');
53
+
54
+ return (
55
+ <Card className="flex h-full min-h-0 flex-col overflow-hidden">
56
+ <CardHeader className="shrink-0 space-y-1 pb-2">
57
+ <div className="flex items-center gap-1.5">
58
+ <Crown className="h-4 w-4 text-amber-600 dark:text-amber-400" />
59
+ <div>
60
+ <CardTitle className="text-sm font-semibold">
61
+ {t('title')}
62
+ </CardTitle>
63
+ <CardDescription className="text-xs leading-tight">
64
+ {t('description')}
65
+ </CardDescription>
66
+ </div>
67
+ </div>
68
+ </CardHeader>
69
+ <CardContent className="flex min-h-0 flex-1 overflow-auto pt-0">
70
+ <div className="grid content-start items-start grid-cols-1 gap-1.5 md:grid-cols-2">
71
+ {roles.map((role, index) => {
72
+ const style = levelStyles[index % levelStyles.length]!;
73
+ return (
74
+ <div
75
+ key={role.id}
76
+ className={`flex h-fit self-start items-center gap-2 rounded-lg border p-2 transition-all duration-200 hover:shadow-sm ${style.border} ${style.bg}`}
77
+ >
78
+ <div
79
+ className={`flex h-7 w-7 shrink-0 items-center justify-center rounded-md ${style.iconBg}`}
80
+ >
81
+ <ShieldCheck className={`h-3.5 w-3.5 ${style.iconColor}`} />
82
+ </div>
83
+ <div className="flex min-w-0 flex-1 flex-col gap-0.5">
84
+ <span className="truncate text-xs font-medium text-foreground sm:text-[13px]">
85
+ {role.name}
86
+ </span>
87
+ <span className="truncate text-[10px] text-muted-foreground sm:text-[11px]">
88
+ {role.slug}
89
+ </span>
90
+ </div>
91
+ <Badge
92
+ variant="secondary"
93
+ className={`shrink-0 px-1.5 py-0 text-[10px] ${style.badge}`}
94
+ >
95
+ {t('active')}
96
+ </Badge>
97
+ </div>
98
+ );
99
+ })}
100
+ </div>
101
+ </CardContent>
102
+ </Card>
103
+ );
104
+ }
105
+
106
+ interface UserRolesProps {
107
+ widget?: { name?: string };
108
+ onRemove?: () => void;
109
+ }
110
+
111
+ export default function UserRoles({ widget, onRemove }: UserRolesProps) {
112
+ const { data, isLoading, isError, isAccessDenied } = useWidgetData<
113
+ AllWidgetsData,
114
+ RoleData[]
115
+ >({
116
+ endpoint: '/dashboard-core/widgets/me',
117
+ queryKey: 'widget-me',
118
+ select: (d) => d.userRoles,
119
+ });
120
+
121
+ return (
122
+ <WidgetWrapper
123
+ isLoading={isLoading}
124
+ isError={isError}
125
+ isAccessDenied={isAccessDenied}
126
+ widgetName={widget?.name ?? 'user-roles'}
127
+ onRemove={onRemove}
128
+ >
129
+ {data && <RolesContent roles={data} />}
130
+ </WidgetWrapper>
131
+ );
132
+ }
@@ -112,7 +112,7 @@ function SessionsContent({ sessions }: { sessions: SessionData[] }) {
112
112
  </div>
113
113
  </CardHeader>
114
114
  <CardContent className="flex min-h-0 flex-1 overflow-auto pt-0">
115
- <div className="flex flex-col gap-2">
115
+ <div className="grid w-full items-start grid-cols-[repeat(auto-fill,minmax(260px,1fr))] gap-2 sm:gap-3">
116
116
  {sessions.map((session, index) => {
117
117
  const ua = session.user_agent ?? '';
118
118
  const deviceType = detectDeviceType(ua);
@@ -4,6 +4,13 @@ columns:
4
4
  locale:
5
5
  en: Slug
6
6
  pt: Slug
7
+ - name: library_slug
8
+ type: varchar
9
+ length: 120
10
+ default: core
11
+ locale:
12
+ en: Library Slug
13
+ pt: Slug da Library
7
14
  - name: min_width
8
15
  type: int
9
16
  default: 1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hed-hog/core",
3
- "version": "0.0.298",
3
+ "version": "0.0.299",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "dependencies": {
@@ -31,11 +31,11 @@
31
31
  "speakeasy": "^2.0.0",
32
32
  "uuid": "^11.1.0",
33
33
  "@hed-hog/api": "0.0.6",
34
- "@hed-hog/api-prisma": "0.0.6",
35
34
  "@hed-hog/api-types": "0.0.1",
36
- "@hed-hog/api-locale": "0.0.14",
37
35
  "@hed-hog/api-pagination": "0.0.7",
38
- "@hed-hog/api-mail": "0.0.9"
36
+ "@hed-hog/api-locale": "0.0.14",
37
+ "@hed-hog/api-mail": "0.0.9",
38
+ "@hed-hog/api-prisma": "0.0.6"
39
39
  },
40
40
  "exports": {
41
41
  ".": {
@@ -2,21 +2,25 @@ import { Role, User } from '@hed-hog/api';
2
2
  import { Locale } from '@hed-hog/api-locale';
3
3
  import { Pagination } from '@hed-hog/api-pagination';
4
4
  import {
5
- Body,
6
- Controller,
7
- Delete,
8
- Get,
9
- Inject,
10
- Param,
11
- ParseIntPipe,
12
- Patch,
13
- Post,
14
- forwardRef
5
+ BadRequestException,
6
+ Body,
7
+ Controller,
8
+ Delete,
9
+ Get,
10
+ Inject,
11
+ Param,
12
+ ParseIntPipe,
13
+ Patch,
14
+ Post,
15
+ UploadedFile,
16
+ UseInterceptors,
17
+ forwardRef
15
18
  } from '@nestjs/common';
19
+ import { FileInterceptor } from '@nestjs/platform-express';
16
20
  import { DashboardComponentService } from './dashboard-component.service';
17
21
  import {
18
- CreateDashboardComponentDTO,
19
- UpdateDashboardComponentDTO,
22
+ CreateDashboardComponentDTO,
23
+ UpdateDashboardComponentDTO,
20
24
  } from './dto';
21
25
 
22
26
  @Role()
@@ -64,4 +68,24 @@ export class DashboardComponentController {
64
68
  deleteComponent(@Param('id', ParseIntPipe) id: number, @Locale() locale: string) {
65
69
  return this.dashboardComponentService.deleteComponent(id, locale);
66
70
  }
71
+
72
+ @Post(':id/preview')
73
+ @UseInterceptors(
74
+ FileInterceptor('file', {
75
+ fileFilter: (req, file, cb) => {
76
+ if (!file?.mimetype?.startsWith('image/')) {
77
+ return cb(new BadRequestException('Invalid image file'), false);
78
+ }
79
+
80
+ cb(null, true);
81
+ },
82
+ }),
83
+ )
84
+ savePreview(
85
+ @Param('id', ParseIntPipe) id: number,
86
+ @UploadedFile() file: MulterFile,
87
+ @Locale() locale: string,
88
+ ) {
89
+ return this.dashboardComponentService.savePreview(id, file, locale);
90
+ }
67
91
  }