@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.
Files changed (183) hide show
  1. package/dist/auth/auth.controller.d.ts +10 -10
  2. package/dist/auth/auth.service.d.ts +10 -10
  3. package/dist/dashboard/dashboard/dashboard.controller.d.ts +3 -0
  4. package/dist/dashboard/dashboard/dashboard.controller.d.ts.map +1 -1
  5. package/dist/dashboard/dashboard/dashboard.service.d.ts +3 -0
  6. package/dist/dashboard/dashboard/dashboard.service.d.ts.map +1 -1
  7. package/dist/dashboard/dashboard-component/dashboard-component.controller.d.ts +12 -0
  8. package/dist/dashboard/dashboard-component/dashboard-component.controller.d.ts.map +1 -1
  9. package/dist/dashboard/dashboard-component/dashboard-component.controller.js +22 -0
  10. package/dist/dashboard/dashboard-component/dashboard-component.controller.js.map +1 -1
  11. package/dist/dashboard/dashboard-component/dashboard-component.service.d.ts +15 -0
  12. package/dist/dashboard/dashboard-component/dashboard-component.service.d.ts.map +1 -1
  13. package/dist/dashboard/dashboard-component/dashboard-component.service.js +110 -3
  14. package/dist/dashboard/dashboard-component/dashboard-component.service.js.map +1 -1
  15. package/dist/dashboard/dashboard-component/dto/create.dto.d.ts +1 -0
  16. package/dist/dashboard/dashboard-component/dto/create.dto.d.ts.map +1 -1
  17. package/dist/dashboard/dashboard-component/dto/create.dto.js +5 -0
  18. package/dist/dashboard/dashboard-component/dto/create.dto.js.map +1 -1
  19. package/dist/dashboard/dashboard-component/dto/update.dto.d.ts +1 -0
  20. package/dist/dashboard/dashboard-component/dto/update.dto.d.ts.map +1 -1
  21. package/dist/dashboard/dashboard-component/dto/update.dto.js +5 -0
  22. package/dist/dashboard/dashboard-component/dto/update.dto.js.map +1 -1
  23. package/dist/dashboard/dashboard-component-role/dashboard-component-role.controller.d.ts +1 -0
  24. package/dist/dashboard/dashboard-component-role/dashboard-component-role.controller.d.ts.map +1 -1
  25. package/dist/dashboard/dashboard-component-role/dashboard-component-role.service.d.ts +1 -0
  26. package/dist/dashboard/dashboard-component-role/dashboard-component-role.service.d.ts.map +1 -1
  27. package/dist/dashboard/dashboard-core/dashboard-core.controller.d.ts +21 -1
  28. package/dist/dashboard/dashboard-core/dashboard-core.controller.d.ts.map +1 -1
  29. package/dist/dashboard/dashboard-core/dashboard-core.controller.js +9 -0
  30. package/dist/dashboard/dashboard-core/dashboard-core.controller.js.map +1 -1
  31. package/dist/dashboard/dashboard-core/dashboard-core.module.d.ts.map +1 -1
  32. package/dist/dashboard/dashboard-core/dashboard-core.module.js +6 -1
  33. package/dist/dashboard/dashboard-core/dashboard-core.module.js.map +1 -1
  34. package/dist/dashboard/dashboard-core/dashboard-core.service.d.ts +180 -2
  35. package/dist/dashboard/dashboard-core/dashboard-core.service.d.ts.map +1 -1
  36. package/dist/dashboard/dashboard-core/dashboard-core.service.js +619 -9
  37. package/dist/dashboard/dashboard-core/dashboard-core.service.js.map +1 -1
  38. package/dist/dashboard/dashboard-item/dashboard-item.controller.d.ts +1 -0
  39. package/dist/dashboard/dashboard-item/dashboard-item.controller.d.ts.map +1 -1
  40. package/dist/dashboard/dashboard-item/dashboard-item.service.d.ts +1 -0
  41. package/dist/dashboard/dashboard-item/dashboard-item.service.d.ts.map +1 -1
  42. package/dist/file/file.controller.d.ts.map +1 -1
  43. package/dist/file/file.controller.js +16 -0
  44. package/dist/file/file.controller.js.map +1 -1
  45. package/dist/file/file.service.d.ts +7 -1
  46. package/dist/file/file.service.d.ts.map +1 -1
  47. package/dist/file/file.service.js +38 -1
  48. package/dist/file/file.service.js.map +1 -1
  49. package/dist/file/provider/s3.provider.d.ts +1 -0
  50. package/dist/file/provider/s3.provider.d.ts.map +1 -1
  51. package/dist/file/provider/s3.provider.js +38 -29
  52. package/dist/file/provider/s3.provider.js.map +1 -1
  53. package/dist/oauth/oauth.service.d.ts.map +1 -1
  54. package/dist/oauth/oauth.service.js +2 -1
  55. package/dist/oauth/oauth.service.js.map +1 -1
  56. package/dist/user/constants/user.constants.d.ts +1 -0
  57. package/dist/user/constants/user.constants.d.ts.map +1 -1
  58. package/dist/user/constants/user.constants.js +2 -1
  59. package/dist/user/constants/user.constants.js.map +1 -1
  60. package/dist/user/user.controller.d.ts +10 -10
  61. package/dist/user/user.service.d.ts +30 -30
  62. package/dist/user/user.service.d.ts.map +1 -1
  63. package/dist/user/user.service.js +2 -1
  64. package/dist/user/user.service.js.map +1 -1
  65. package/hedhog/data/dashboard_item.yaml +10 -10
  66. package/hedhog/data/route.yaml +20 -0
  67. package/hedhog/frontend/app/dashboard/[slug]/dashboard-content.tsx.ejs +212 -34
  68. package/hedhog/frontend/app/dashboard/[slug]/types.ts.ejs +3 -0
  69. package/hedhog/frontend/app/dashboard/[slug]/widget-renderer.tsx.ejs +136 -23
  70. package/hedhog/frontend/app/dashboard/components/add-widget-selector-dialog.tsx.ejs +266 -85
  71. package/hedhog/frontend/app/dashboard/components/widgets/core..gitkeep.ejs +11 -0
  72. package/hedhog/frontend/app/dashboard/components/widgets/core.account-security.tsx.ejs +192 -0
  73. package/hedhog/frontend/app/dashboard/components/widgets/core.email-notifications.tsx.ejs +226 -0
  74. package/hedhog/frontend/app/dashboard/components/widgets/core.locale-config.tsx.ejs +168 -0
  75. package/hedhog/frontend/app/dashboard/components/widgets/core.mail-config.tsx.ejs +199 -0
  76. package/hedhog/frontend/app/dashboard/components/widgets/core.oauth-config.tsx.ejs +175 -0
  77. package/hedhog/frontend/app/dashboard/components/widgets/core.profile-card.tsx.ejs +186 -0
  78. package/hedhog/frontend/app/dashboard/components/widgets/core.storage-config.tsx.ejs +196 -0
  79. package/hedhog/frontend/app/dashboard/components/widgets/core.theme-config.tsx.ejs +213 -0
  80. package/hedhog/frontend/app/dashboard/components/widgets/core.user-roles.tsx.ejs +132 -0
  81. package/hedhog/frontend/app/dashboard/components/widgets/core.user-sessions.tsx.ejs +236 -0
  82. package/hedhog/frontend/app/dashboard/components/widgets/finance.alerts.tsx.ejs +108 -0
  83. package/hedhog/frontend/app/dashboard/components/widgets/finance.cash-balance-kpi.tsx.ejs +66 -0
  84. package/hedhog/frontend/app/dashboard/components/widgets/finance.cash-flow-chart.tsx.ejs +122 -0
  85. package/hedhog/frontend/app/dashboard/components/widgets/finance.default-kpi.tsx.ejs +63 -0
  86. package/hedhog/frontend/app/dashboard/components/widgets/finance.payable-30d-kpi.tsx.ejs +73 -0
  87. package/hedhog/frontend/app/dashboard/components/widgets/finance.receivable-30d-kpi.tsx.ejs +73 -0
  88. package/hedhog/frontend/app/dashboard/components/widgets/finance.upcoming-payable.tsx.ejs +123 -0
  89. package/hedhog/frontend/app/dashboard/components/widgets/finance.upcoming-receivable.tsx.ejs +118 -0
  90. package/hedhog/frontend/messages/en.json +93 -0
  91. package/hedhog/frontend/messages/pt.json +93 -0
  92. package/hedhog/frontend/public/dashboard-previews/.gitkeep +12 -0
  93. package/hedhog/frontend/public/dashboard-previews/account-security.png +0 -0
  94. package/hedhog/frontend/public/dashboard-previews/active-users-card.png +0 -0
  95. package/hedhog/frontend/public/dashboard-previews/activity-timeline.png +0 -0
  96. package/hedhog/frontend/public/dashboard-previews/cash-balance-kpi.png +0 -0
  97. package/hedhog/frontend/public/dashboard-previews/cash-flow-chart.png +0 -0
  98. package/hedhog/frontend/public/dashboard-previews/default-kpi.png +0 -0
  99. package/hedhog/frontend/public/dashboard-previews/email-notifications.png +0 -0
  100. package/hedhog/frontend/public/dashboard-previews/financial-alerts.png +0 -0
  101. package/hedhog/frontend/public/dashboard-previews/login-history-chart.png +0 -0
  102. package/hedhog/frontend/public/dashboard-previews/mail-sent-card.png +0 -0
  103. package/hedhog/frontend/public/dashboard-previews/mail-sent-chart.png +0 -0
  104. package/hedhog/frontend/public/dashboard-previews/menus-card.png +0 -0
  105. package/hedhog/frontend/public/dashboard-previews/payable-30d-kpi.png +0 -0
  106. package/hedhog/frontend/public/dashboard-previews/permissions-card.png +0 -0
  107. package/hedhog/frontend/public/dashboard-previews/permissions-chart.png +0 -0
  108. package/hedhog/frontend/public/dashboard-previews/profile-card.png +0 -0
  109. package/hedhog/frontend/public/dashboard-previews/receivable-30d-kpi.png +0 -0
  110. package/hedhog/frontend/public/dashboard-previews/routes-card.png +0 -0
  111. package/hedhog/frontend/public/dashboard-previews/session-activity-chart.png +0 -0
  112. package/hedhog/frontend/public/dashboard-previews/sessions-today-card.png +0 -0
  113. package/hedhog/frontend/public/dashboard-previews/stat-access-level.png +0 -0
  114. package/hedhog/frontend/public/dashboard-previews/stat-actions-today.png +0 -0
  115. package/hedhog/frontend/public/dashboard-previews/stat-consecutive-days.png +0 -0
  116. package/hedhog/frontend/public/dashboard-previews/stat-online-time.png +0 -0
  117. package/hedhog/frontend/public/dashboard-previews/upcoming-payable.png +0 -0
  118. package/hedhog/frontend/public/dashboard-previews/upcoming-receivable.png +0 -0
  119. package/hedhog/frontend/public/dashboard-previews/user-growth-chart.png +0 -0
  120. package/hedhog/frontend/public/dashboard-previews/user-roles.png +0 -0
  121. package/hedhog/frontend/{app/dashboard/components/widgets → widgets}/account-security.tsx.ejs +33 -29
  122. package/hedhog/frontend/widgets/active-users-card.tsx.ejs +58 -0
  123. package/hedhog/frontend/widgets/activity-timeline.tsx.ejs +223 -0
  124. package/hedhog/frontend/{app/dashboard/components/widgets → widgets}/email-notifications.tsx.ejs +85 -61
  125. package/hedhog/frontend/widgets/locale-config.tsx.ejs +168 -0
  126. package/hedhog/frontend/widgets/login-history-chart.tsx.ejs +115 -0
  127. package/hedhog/frontend/widgets/mail-config.tsx.ejs +199 -0
  128. package/hedhog/frontend/widgets/mail-sent-card.tsx.ejs +58 -0
  129. package/hedhog/frontend/widgets/mail-sent-chart.tsx.ejs +149 -0
  130. package/hedhog/frontend/widgets/menus-card.tsx.ejs +58 -0
  131. package/hedhog/frontend/widgets/oauth-config.tsx.ejs +175 -0
  132. package/hedhog/frontend/widgets/permissions-card.tsx.ejs +61 -0
  133. package/hedhog/frontend/widgets/permissions-chart.tsx.ejs +156 -0
  134. package/hedhog/frontend/{app/dashboard/components/widgets → widgets}/profile-card.tsx.ejs +3 -3
  135. package/hedhog/frontend/widgets/routes-card.tsx.ejs +58 -0
  136. package/hedhog/frontend/widgets/session-activity-chart.tsx.ejs +183 -0
  137. package/hedhog/frontend/widgets/sessions-today-card.tsx.ejs +62 -0
  138. package/hedhog/frontend/widgets/stat-access-level.tsx.ejs +57 -0
  139. package/hedhog/frontend/widgets/stat-actions-today.tsx.ejs +57 -0
  140. package/hedhog/frontend/widgets/stat-consecutive-days.tsx.ejs +57 -0
  141. package/hedhog/frontend/widgets/stat-online-time.tsx.ejs +57 -0
  142. package/hedhog/frontend/widgets/storage-config.tsx.ejs +196 -0
  143. package/hedhog/frontend/widgets/theme-config.tsx.ejs +213 -0
  144. package/hedhog/frontend/widgets/user-growth-chart.tsx.ejs +210 -0
  145. package/hedhog/frontend/{app/dashboard/components/widgets → widgets}/user-roles.tsx.ejs +12 -14
  146. package/hedhog/frontend/{app/dashboard/components/widgets → widgets}/user-sessions.tsx.ejs +1 -1
  147. package/hedhog/table/dashboard_component.yaml +7 -0
  148. package/hedhog/table/mail_sent_user.yaml +75 -0
  149. package/package.json +4 -4
  150. package/src/dashboard/dashboard-component/dashboard-component.controller.ts +36 -12
  151. package/src/dashboard/dashboard-component/dashboard-component.service.ts +150 -3
  152. package/src/dashboard/dashboard-component/dto/create.dto.ts +4 -0
  153. package/src/dashboard/dashboard-component/dto/update.dto.ts +4 -0
  154. package/src/dashboard/dashboard-core/dashboard-core.controller.ts +5 -0
  155. package/src/dashboard/dashboard-core/dashboard-core.module.ts +6 -1
  156. package/src/dashboard/dashboard-core/dashboard-core.service.ts +874 -8
  157. package/src/file/file.controller.ts +37 -13
  158. package/src/file/file.service.ts +47 -5
  159. package/src/file/provider/s3.provider.ts +39 -29
  160. package/src/oauth/oauth.service.ts +8 -7
  161. package/src/user/constants/user.constants.ts +1 -0
  162. package/src/user/user.service.ts +2 -1
  163. package/hedhog/frontend/app/dashboard/components/widgets/locale-config.tsx.ejs +0 -309
  164. package/hedhog/frontend/app/dashboard/components/widgets/mail-config.tsx.ejs +0 -445
  165. package/hedhog/frontend/app/dashboard/components/widgets/oauth-config.tsx.ejs +0 -296
  166. package/hedhog/frontend/app/dashboard/components/widgets/storage-config.tsx.ejs +0 -340
  167. package/hedhog/frontend/app/dashboard/components/widgets/theme-config.tsx.ejs +0 -275
  168. /package/hedhog/frontend/app/dashboard/components/widgets/{active-users-card.tsx.ejs → core.active-users-card.tsx.ejs} +0 -0
  169. /package/hedhog/frontend/app/dashboard/components/widgets/{activity-timeline.tsx.ejs → core.activity-timeline.tsx.ejs} +0 -0
  170. /package/hedhog/frontend/app/dashboard/components/widgets/{login-history-chart.tsx.ejs → core.login-history-chart.tsx.ejs} +0 -0
  171. /package/hedhog/frontend/app/dashboard/components/widgets/{mail-sent-card.tsx.ejs → core.mail-sent-card.tsx.ejs} +0 -0
  172. /package/hedhog/frontend/app/dashboard/components/widgets/{mail-sent-chart.tsx.ejs → core.mail-sent-chart.tsx.ejs} +0 -0
  173. /package/hedhog/frontend/app/dashboard/components/widgets/{menus-card.tsx.ejs → core.menus-card.tsx.ejs} +0 -0
  174. /package/hedhog/frontend/app/dashboard/components/widgets/{permissions-card.tsx.ejs → core.permissions-card.tsx.ejs} +0 -0
  175. /package/hedhog/frontend/app/dashboard/components/widgets/{permissions-chart.tsx.ejs → core.permissions-chart.tsx.ejs} +0 -0
  176. /package/hedhog/frontend/app/dashboard/components/widgets/{routes-card.tsx.ejs → core.routes-card.tsx.ejs} +0 -0
  177. /package/hedhog/frontend/app/dashboard/components/widgets/{session-activity-chart.tsx.ejs → core.session-activity-chart.tsx.ejs} +0 -0
  178. /package/hedhog/frontend/app/dashboard/components/widgets/{sessions-today-card.tsx.ejs → core.sessions-today-card.tsx.ejs} +0 -0
  179. /package/hedhog/frontend/app/dashboard/components/widgets/{stat-access-level.tsx.ejs → core.stat-access-level.tsx.ejs} +0 -0
  180. /package/hedhog/frontend/app/dashboard/components/widgets/{stat-actions-today.tsx.ejs → core.stat-actions-today.tsx.ejs} +0 -0
  181. /package/hedhog/frontend/app/dashboard/components/widgets/{stat-consecutive-days.tsx.ejs → core.stat-consecutive-days.tsx.ejs} +0 -0
  182. /package/hedhog/frontend/app/dashboard/components/widgets/{stat-online-time.tsx.ejs → core.stat-online-time.tsx.ejs} +0 -0
  183. /package/hedhog/frontend/app/dashboard/components/widgets/{user-growth-chart.tsx.ejs → core.user-growth-chart.tsx.ejs} +0 -0
@@ -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
+ }
@@ -53,46 +53,44 @@ function RolesContent({ roles }: { roles: RoleData[] }) {
53
53
 
54
54
  return (
55
55
  <Card className="flex h-full min-h-0 flex-col overflow-hidden">
56
- <CardHeader className="shrink-0 pb-3">
57
- <div className="flex items-center gap-2">
58
- <Crown className="h-4 w-4 text-amber-600 dark:text-amber-400 sm:h-5 sm:w-5" />
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
59
  <div>
60
- <CardTitle className="text-sm font-semibold sm:text-base">
60
+ <CardTitle className="text-sm font-semibold">
61
61
  {t('title')}
62
62
  </CardTitle>
63
- <CardDescription className="text-xs sm:text-sm">
63
+ <CardDescription className="text-xs leading-tight">
64
64
  {t('description')}
65
65
  </CardDescription>
66
66
  </div>
67
67
  </div>
68
68
  </CardHeader>
69
69
  <CardContent className="flex min-h-0 flex-1 overflow-auto pt-0">
70
- <div className="grid grid-cols-1 gap-2 md:grid-cols-2">
70
+ <div className="grid content-start items-start grid-cols-1 gap-1.5 md:grid-cols-2">
71
71
  {roles.map((role, index) => {
72
72
  const style = levelStyles[index % levelStyles.length]!;
73
73
  return (
74
74
  <div
75
75
  key={role.id}
76
- className={`flex items-center gap-2.5 rounded-xl border p-2.5 transition-all duration-200 hover:shadow-sm sm:gap-3 sm:p-3 ${style.border} ${style.bg}`}
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
77
  >
78
78
  <div
79
- className={`flex h-8 w-8 shrink-0 items-center justify-center rounded-lg sm:h-9 sm:w-9 ${style.iconBg}`}
79
+ className={`flex h-7 w-7 shrink-0 items-center justify-center rounded-md ${style.iconBg}`}
80
80
  >
81
- <ShieldCheck
82
- className={`h-3.5 w-3.5 sm:h-4 sm:w-4 ${style.iconColor}`}
83
- />
81
+ <ShieldCheck className={`h-3.5 w-3.5 ${style.iconColor}`} />
84
82
  </div>
85
83
  <div className="flex min-w-0 flex-1 flex-col gap-0.5">
86
- <span className="truncate text-[13px] font-medium text-foreground sm:text-sm">
84
+ <span className="truncate text-xs font-medium text-foreground sm:text-[13px]">
87
85
  {role.name}
88
86
  </span>
89
- <span className="truncate text-[11px] text-muted-foreground sm:text-xs">
87
+ <span className="truncate text-[10px] text-muted-foreground sm:text-[11px]">
90
88
  {role.slug}
91
89
  </span>
92
90
  </div>
93
91
  <Badge
94
92
  variant="secondary"
95
- className={`shrink-0 text-[10px] ${style.badge}`}
93
+ className={`shrink-0 px-1.5 py-0 text-[10px] ${style.badge}`}
96
94
  >
97
95
  {t('active')}
98
96
  </Badge>
@@ -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
@@ -0,0 +1,75 @@
1
+ columns:
2
+ - type: pk
3
+ - name: mail_sent_id
4
+ type: fk
5
+ isNullable: true
6
+ references:
7
+ table: mail_sent
8
+ column: id
9
+ onDelete: SET NULL
10
+ onUpdate: CASCADE
11
+ - name: mail_id
12
+ type: fk
13
+ references:
14
+ table: mail
15
+ column: id
16
+ onDelete: CASCADE
17
+ onUpdate: CASCADE
18
+ - name: user_id
19
+ type: fk
20
+ references:
21
+ table: user
22
+ column: id
23
+ onDelete: CASCADE
24
+ onUpdate: CASCADE
25
+ - name: user_identifier_id
26
+ type: fk
27
+ isNullable: true
28
+ references:
29
+ table: user_identifier
30
+ column: id
31
+ onDelete: SET NULL
32
+ onUpdate: CASCADE
33
+ - name: recipient_email
34
+ type: varchar
35
+ length: 255
36
+ - name: status
37
+ type: enum
38
+ values:
39
+ - received
40
+ - read
41
+ - error
42
+ default: received
43
+ - name: read_at
44
+ type: datetime
45
+ isNullable: true
46
+ - name: error_code
47
+ type: varchar
48
+ length: 128
49
+ isNullable: true
50
+ - name: error_message
51
+ type: text
52
+ isNullable: true
53
+ - type: created_at
54
+ - type: updated_at
55
+ indices:
56
+ - columns:
57
+ - mail_sent_id
58
+ - user_id
59
+ isUnique: true
60
+ - columns:
61
+ - user_id
62
+ - created_at
63
+ - columns:
64
+ - user_id
65
+ - status
66
+ - created_at
67
+ - columns:
68
+ - user_id
69
+ - read_at
70
+ - columns:
71
+ - mail_id
72
+ - columns:
73
+ - user_identifier_id
74
+ - columns:
75
+ - status
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hed-hog/core",
3
- "version": "0.0.297",
3
+ "version": "0.0.299",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "dependencies": {
@@ -30,12 +30,12 @@
30
30
  "sharp": "^0.34.2",
31
31
  "speakeasy": "^2.0.0",
32
32
  "uuid": "^11.1.0",
33
- "@hed-hog/api-locale": "0.0.14",
34
33
  "@hed-hog/api": "0.0.6",
34
+ "@hed-hog/api-types": "0.0.1",
35
35
  "@hed-hog/api-pagination": "0.0.7",
36
+ "@hed-hog/api-locale": "0.0.14",
36
37
  "@hed-hog/api-mail": "0.0.9",
37
- "@hed-hog/api-prisma": "0.0.6",
38
- "@hed-hog/api-types": "0.0.1"
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
  }
@@ -2,11 +2,15 @@ import { getLocaleText } from '@hed-hog/api-locale';
2
2
  import { PaginationDTO, PaginationService } from '@hed-hog/api-pagination';
3
3
  import { PrismaService } from '@hed-hog/api-prisma';
4
4
  import {
5
+ BadRequestException,
6
+ ForbiddenException,
5
7
  forwardRef,
6
8
  Inject,
7
9
  Injectable,
8
10
  NotFoundException
9
11
  } from '@nestjs/common';
12
+ import { existsSync, promises as fs } from 'fs';
13
+ import { join, resolve } from 'path';
10
14
  import {
11
15
  CreateDashboardComponentDTO,
12
16
  UpdateDashboardComponentDTO,
@@ -21,7 +25,7 @@ export class DashboardComponentService {
21
25
  ) {}
22
26
 
23
27
  async getAllComponents(paginationParams: PaginationDTO) {
24
- const fields = ['slug']
28
+ const fields = ['slug', 'library_slug']
25
29
  const OR = this.prismaService.createInsensitiveSearch(
26
30
  fields,
27
31
  paginationParams,
@@ -83,6 +87,42 @@ export class DashboardComponentService {
83
87
 
84
88
  const userRoleIds = userRoles.map((ur) => ur.role_id);
85
89
 
90
+ if (userRoleIds.length === 0) {
91
+ return this.paginationService.paginate(
92
+ this.prismaService.dashboard_component,
93
+ paginationParams,
94
+ {
95
+ include: {
96
+ dashboard_component_locale: {
97
+ include: {
98
+ locale: true,
99
+ },
100
+ },
101
+ dashboard_component_role: {
102
+ include: {
103
+ role: {
104
+ include: {
105
+ role_locale: {
106
+ include: {
107
+ locale: true,
108
+ },
109
+ },
110
+ },
111
+ },
112
+ },
113
+ },
114
+ },
115
+ where: {
116
+ id: -1,
117
+ },
118
+ orderBy: {
119
+ created_at: 'desc',
120
+ },
121
+ },
122
+ 'dashboardComponent',
123
+ );
124
+ }
125
+
86
126
  const fields = ['slug']
87
127
  const OR = this.prismaService.createInsensitiveSearch(
88
128
  fields,
@@ -221,9 +261,15 @@ export class DashboardComponentService {
221
261
  }
222
262
 
223
263
  async createComponent(data: CreateDashboardComponentDTO, locale: string) {
264
+ const normalized = this.normalizeComponentIdentity(
265
+ data.slug,
266
+ data.library_slug,
267
+ );
268
+
224
269
  const component = await this.prismaService.dashboard_component.create({
225
270
  data: {
226
- slug: data.slug,
271
+ slug: normalized.slug,
272
+ library_slug: normalized.library_slug,
227
273
  min_width: data.min_width,
228
274
  max_width: data.max_width,
229
275
  min_height: data.min_height,
@@ -257,10 +303,18 @@ export class DashboardComponentService {
257
303
  }
258
304
 
259
305
  async updateComponent(id: number, data: UpdateDashboardComponentDTO, locale: string) {
306
+ const normalized = data.slug
307
+ ? this.normalizeComponentIdentity(data.slug, data.library_slug)
308
+ : {
309
+ slug: undefined,
310
+ library_slug: data.library_slug,
311
+ };
312
+
260
313
  await this.prismaService.dashboard_component.update({
261
314
  where: { id },
262
315
  data: {
263
- slug: data.slug,
316
+ slug: normalized.slug,
317
+ library_slug: normalized.library_slug,
264
318
  min_width: data.min_width,
265
319
  max_width: data.max_width,
266
320
  min_height: data.min_height,
@@ -312,4 +366,97 @@ export class DashboardComponentService {
312
366
  where: { id },
313
367
  });
314
368
  }
369
+
370
+ private normalizeComponentIdentity(slug: string, librarySlug?: string | null) {
371
+ const slugParts = slug.split('.').filter(Boolean);
372
+ const baseSlug =
373
+ slugParts.length > 0 ? slugParts[slugParts.length - 1]! : slug;
374
+ const resolvedLibrarySlug =
375
+ librarySlug || (slugParts.length > 1 ? slugParts[0]! : 'core');
376
+
377
+ return {
378
+ slug: baseSlug,
379
+ library_slug: resolvedLibrarySlug,
380
+ };
381
+ }
382
+
383
+ private buildWidgetAssetSlug(slug: string, librarySlug?: string | null) {
384
+ const normalized = this.normalizeComponentIdentity(slug, librarySlug);
385
+
386
+ return normalized.slug;
387
+ }
388
+
389
+ private resolvePreviewDirectory(): string {
390
+ const cwd = process.cwd();
391
+ const candidates = [
392
+ resolve(cwd, 'libraries/core/hedhog/frontend/public/dashboard-previews'),
393
+ resolve(cwd, '../libraries/core/hedhog/frontend/public/dashboard-previews'),
394
+ resolve(cwd, '../../libraries/core/hedhog/frontend/public/dashboard-previews'),
395
+ ];
396
+
397
+ for (const candidate of candidates) {
398
+ const parentDirectory = resolve(candidate, '..');
399
+ if (existsSync(parentDirectory)) {
400
+ return candidate;
401
+ }
402
+ }
403
+
404
+ return candidates[0];
405
+ }
406
+
407
+ async savePreview(id: number, file: MulterFile, locale: string) {
408
+ const isDevelopment = process.env.NODE_ENV !== 'production';
409
+ if (!isDevelopment) {
410
+ throw new ForbiddenException(
411
+ getLocaleText('dashboardPreviewOnlyInDevelopment', locale, 'Preview capture is only available in development environment'),
412
+ );
413
+ }
414
+
415
+ if (!file || !file.buffer) {
416
+ throw new BadRequestException(
417
+ getLocaleText('dashboardPreviewFileRequired', locale, 'Preview image file is required'),
418
+ );
419
+ }
420
+
421
+ if (!file.mimetype?.startsWith('image/')) {
422
+ throw new BadRequestException(
423
+ getLocaleText('dashboardPreviewInvalidFileType', locale, 'Preview file must be an image'),
424
+ );
425
+ }
426
+
427
+ const component = await this.prismaService.dashboard_component.findUnique({
428
+ where: { id },
429
+ select: {
430
+ id: true,
431
+ slug: true,
432
+ library_slug: true,
433
+ },
434
+ });
435
+
436
+ if (!component) {
437
+ throw new NotFoundException(
438
+ getLocaleText('dashboardComponentNotFound', locale, 'Dashboard component not found'),
439
+ );
440
+ }
441
+
442
+ const previewDirectory = this.resolvePreviewDirectory();
443
+ await fs.mkdir(previewDirectory, { recursive: true });
444
+
445
+ const assetSlug = this.buildWidgetAssetSlug(
446
+ component.slug,
447
+ component.library_slug,
448
+ );
449
+ const fileName = `${assetSlug}.png`;
450
+ const outputPath = join(previewDirectory, fileName);
451
+ await fs.writeFile(outputPath, file.buffer);
452
+
453
+ return {
454
+ success: true,
455
+ componentId: component.id,
456
+ slug: component.slug,
457
+ library_slug: component.library_slug,
458
+ fileName,
459
+ relativeUrl: `/libraries/${component.library_slug}/dashboard-previews/${fileName}`,
460
+ };
461
+ }
315
462
  }
@@ -5,6 +5,10 @@ export class CreateDTO {
5
5
  @IsString({ message: (args) => getLocaleText('validation.stringRequired', args.value) })
6
6
  slug: string;
7
7
 
8
+ @IsOptional()
9
+ @IsString({ message: (args) => getLocaleText('validation.stringRequired', args.value) })
10
+ library_slug?: string;
11
+
8
12
  @IsOptional()
9
13
  @IsNumber({}, { message: (args) => getLocaleText('validation.numberRequired', args.value) })
10
14
  min_width?: number;
@@ -6,6 +6,10 @@ export class UpdateDTO {
6
6
  @IsString({ message: (args) => getLocaleText('validation.stringRequired', args.value) })
7
7
  slug?: string;
8
8
 
9
+ @IsOptional()
10
+ @IsString({ message: (args) => getLocaleText('validation.stringRequired', args.value) })
11
+ library_slug?: string;
12
+
9
13
  @IsOptional()
10
14
  @IsNumber({}, { message: (args) => getLocaleText('validation.numberRequired', args.value) })
11
15
  min_width?: number;
@@ -28,6 +28,11 @@ export class DashboardCoreController {
28
28
  return this.dashboardCoreService.getSystemStatistics();
29
29
  }
30
30
 
31
+ @Get('config/overview')
32
+ getConfigOverview() {
33
+ return this.dashboardCoreService.getConfigOverview();
34
+ }
35
+
31
36
  @Get('widgets/me')
32
37
  getWidgetsData(@User() user, @Locale() locale: string) {
33
38
  return this.dashboardCoreService.getWidgetsData(user.id, locale);