@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,175 @@
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
+ OAuthConfigWidgetData,
15
+ } from '@/types/widget-data';
16
+ import { CheckCircle2, KeyRound, XCircle } from 'lucide-react';
17
+ import { useTranslations } from 'next-intl';
18
+ import { WidgetWrapper } from '../widget-wrapper';
19
+
20
+ const defaultOAuthConfigData: OAuthConfigWidgetData = {
21
+ status: {
22
+ isConfigured: false,
23
+ enabledProviderCount: 0,
24
+ configuredProviderCount: 0,
25
+ connectedAccountCount: 0,
26
+ },
27
+ providers: [],
28
+ };
29
+
30
+ interface OAuthConfigProps {
31
+ widget?: { name?: string };
32
+ onRemove?: () => void;
33
+ }
34
+
35
+ export default function OAuthConfig({ widget, onRemove }: OAuthConfigProps) {
36
+ const t = useTranslations('core.DashboardPage.oauthConfig');
37
+
38
+ const { data, isLoading, isError, isAccessDenied } = useWidgetData<
39
+ DashboardCoreConfigOverviewData,
40
+ OAuthConfigWidgetData
41
+ >({
42
+ endpoint: '/dashboard-core/config/overview',
43
+ queryKey: 'dashboard-core-config-overview',
44
+ select: (d) => d.oauthConfig,
45
+ });
46
+
47
+ const oauthData = data ?? defaultOAuthConfigData;
48
+
49
+ return (
50
+ <WidgetWrapper
51
+ isLoading={isLoading}
52
+ isError={isError}
53
+ isAccessDenied={isAccessDenied}
54
+ widgetName={widget?.name ?? t('title')}
55
+ onRemove={onRemove}
56
+ >
57
+ <Card className="flex h-full min-h-0 flex-col overflow-hidden">
58
+ <CardHeader className="shrink-0">
59
+ <div className="flex items-center justify-between gap-3">
60
+ <div className="flex items-center gap-3">
61
+ <div className="flex h-10 w-10 items-center justify-center rounded-lg bg-amber-50">
62
+ <KeyRound className="h-5 w-5 text-amber-600" />
63
+ </div>
64
+ <div>
65
+ <CardTitle className="text-base">{t('title')}</CardTitle>
66
+ <CardDescription>{t('description')}</CardDescription>
67
+ </div>
68
+ </div>
69
+ <Badge
70
+ variant="secondary"
71
+ className={
72
+ oauthData.status.isConfigured
73
+ ? 'bg-emerald-50 text-emerald-700'
74
+ : 'bg-amber-50 text-amber-700'
75
+ }
76
+ >
77
+ {oauthData.status.isConfigured ? t('configured') : t('pending')}
78
+ </Badge>
79
+ </div>
80
+ </CardHeader>
81
+ <CardContent className="flex min-h-0 flex-1 flex-col gap-4 overflow-hidden pt-0">
82
+ <div className="grid grid-cols-3 gap-2">
83
+ <div className="rounded-lg border bg-muted/30 p-3 text-center">
84
+ <p className="text-[11px] text-muted-foreground">
85
+ {t('enabled')}
86
+ </p>
87
+ <p className="mt-1 text-lg font-semibold">
88
+ {oauthData.status.enabledProviderCount}
89
+ </p>
90
+ </div>
91
+ <div className="rounded-lg border bg-muted/30 p-3 text-center">
92
+ <p className="text-[11px] text-muted-foreground">
93
+ {t('configuredProviders')}
94
+ </p>
95
+ <p className="mt-1 text-lg font-semibold">
96
+ {oauthData.status.configuredProviderCount}
97
+ </p>
98
+ </div>
99
+ <div className="rounded-lg border bg-muted/30 p-3 text-center">
100
+ <p className="text-[11px] text-muted-foreground">
101
+ {t('connectedAccounts')}
102
+ </p>
103
+ <p className="mt-1 text-lg font-semibold">
104
+ {oauthData.status.connectedAccountCount}
105
+ </p>
106
+ </div>
107
+ </div>
108
+
109
+ <div className="min-h-0 flex-1 space-y-2 overflow-auto">
110
+ {oauthData.providers.length > 0 ? (
111
+ oauthData.providers.map((provider) => (
112
+ <div key={provider.id} className="rounded-lg border p-3">
113
+ <div className="flex items-center justify-between gap-2">
114
+ <p className="text-sm font-medium">{provider.label}</p>
115
+ <div className="flex items-center gap-1.5">
116
+ <Badge
117
+ variant="secondary"
118
+ className={
119
+ provider.enabled
120
+ ? 'bg-emerald-50 text-emerald-700'
121
+ : 'bg-muted text-muted-foreground'
122
+ }
123
+ >
124
+ {provider.enabled ? (
125
+ <>
126
+ <CheckCircle2 className="mr-1 h-3 w-3" />
127
+ {t('enabled')}
128
+ </>
129
+ ) : (
130
+ <>
131
+ <XCircle className="mr-1 h-3 w-3" />
132
+ {t('disabled')}
133
+ </>
134
+ )}
135
+ </Badge>
136
+ <Badge
137
+ variant="outline"
138
+ className={
139
+ provider.configured
140
+ ? 'border-emerald-200 text-emerald-700'
141
+ : 'text-muted-foreground'
142
+ }
143
+ >
144
+ {provider.configured ? t('configured') : t('pending')}
145
+ </Badge>
146
+ </div>
147
+ </div>
148
+ <div className="mt-2 flex flex-wrap gap-3 text-[11px] text-muted-foreground">
149
+ <span>
150
+ {t('scopesCount', { count: provider.scopesCount })}
151
+ </span>
152
+ <span>
153
+ {t('connectedUsers', { count: provider.connectedUsers })}
154
+ </span>
155
+ <span>
156
+ {provider.missingKeys.length > 0
157
+ ? t('missingKeys', {
158
+ count: provider.missingKeys.length,
159
+ })
160
+ : t('allKeysPresent')}
161
+ </span>
162
+ </div>
163
+ </div>
164
+ ))
165
+ ) : (
166
+ <p className="text-xs text-muted-foreground">
167
+ {t('noProviders')}
168
+ </p>
169
+ )}
170
+ </div>
171
+ </CardContent>
172
+ </Card>
173
+ </WidgetWrapper>
174
+ );
175
+ }
@@ -0,0 +1,186 @@
1
+ 'use client';
2
+
3
+ import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar';
4
+ import { Badge } from '@/components/ui/badge';
5
+ import { Button } from '@/components/ui/button';
6
+ import { Card, CardContent } from '@/components/ui/card';
7
+ import { Separator } from '@/components/ui/separator';
8
+ import { useWidgetData } from '@/hooks/use-widget-data';
9
+ import { formatDateTime } from '@/lib/format-date';
10
+ import { getPhotoUrl } from '@/lib/get-photo-url';
11
+ import type { AllWidgetsData, ProfileData } from '@/types/widget-data';
12
+ import { useApp } from '@hed-hog/next-app-provider';
13
+ import {
14
+ Calendar,
15
+ CheckCircle2,
16
+ Clock,
17
+ Edit3,
18
+ Globe,
19
+ Mail,
20
+ Shield,
21
+ } from 'lucide-react';
22
+ import { useTranslations } from 'next-intl';
23
+ import { useRouter } from 'next/navigation';
24
+ import { WidgetWrapper } from '../widget-wrapper';
25
+
26
+ function getInitials(name: string): string {
27
+ return name
28
+ .split(' ')
29
+ .filter(Boolean)
30
+ .slice(0, 2)
31
+ .map((n) => n[0]?.toUpperCase() ?? '')
32
+ .join('');
33
+ }
34
+
35
+ function ProfileContent({ profile }: { profile: ProfileData }) {
36
+ const { getSettingValue } = useApp();
37
+ const router = useRouter();
38
+ const t = useTranslations('core.DashboardPage.profileCard');
39
+
40
+ return (
41
+ <Card className="relative flex h-full flex-col overflow-hidden">
42
+ <div className="absolute inset-x-0 top-0 h-28 bg-linear-to-br from-muted to-muted/50" />
43
+ <CardContent className="relative flex flex-1 flex-col overflow-hidden pt-7 pb-12">
44
+ <div className="flex flex-col items-center gap-4 sm:flex-row sm:items-end">
45
+ <div className="relative">
46
+ <Avatar className="h-20 w-20 border-4 border-card shadow-md">
47
+ {profile.photo_id && (
48
+ <AvatarImage
49
+ src={getPhotoUrl(profile.photo_id)}
50
+ alt={profile.name}
51
+ />
52
+ )}
53
+ <AvatarFallback className="bg-foreground text-background text-xl font-bold">
54
+ {getInitials(profile.name)}
55
+ </AvatarFallback>
56
+ </Avatar>
57
+ <div className="absolute -bottom-0.5 -right-0.5 h-5 w-5 rounded-full border-2 border-card bg-emerald-500" />
58
+ </div>
59
+ <div className="flex flex-1 flex-col items-center gap-1 text-center sm:items-start sm:text-left">
60
+ <div className="flex items-center gap-2">
61
+ <h2 className="text-xl font-bold text-foreground">
62
+ {profile.name}
63
+ </h2>
64
+ <CheckCircle2 className="h-4 w-4 text-blue-500" />
65
+ </div>
66
+ <p className="text-sm text-muted-foreground">{profile.role}</p>
67
+ <div className="flex flex-wrap items-center gap-2 pt-1">
68
+ <Badge
69
+ variant="outline"
70
+ className="border-emerald-200 bg-emerald-50 text-xs text-emerald-700 dark:border-emerald-800 dark:bg-emerald-950/40 dark:text-emerald-400"
71
+ >
72
+ {t('online')}
73
+ </Badge>
74
+ </div>
75
+ </div>
76
+ <Button
77
+ onClick={() => router.push('/core/account/profile')}
78
+ variant="outline"
79
+ size="sm"
80
+ className="shrink-0 gap-1.5"
81
+ >
82
+ <Edit3 className="h-3.5 w-3.5" />
83
+ {t('editProfile')}
84
+ </Button>
85
+ </div>
86
+
87
+ <Separator className="my-6" />
88
+
89
+ <div className="grid grid-cols-1 gap-2 sm:grid-cols-2">
90
+ <div className="flex items-center gap-2.5 rounded-lg p-2 transition-colors hover:bg-muted/50">
91
+ <div className="flex h-8 w-8 shrink-0 items-center justify-center rounded-md bg-muted">
92
+ <Mail className="h-4 w-4 text-muted-foreground" />
93
+ </div>
94
+ <div className="flex min-w-0 flex-col">
95
+ <span className="text-[11px] text-muted-foreground">
96
+ {t('email')}
97
+ </span>
98
+ <span className="truncate text-xs font-medium text-foreground">
99
+ {profile.email}
100
+ </span>
101
+ </div>
102
+ </div>
103
+
104
+ <div className="flex items-center gap-2.5 rounded-lg p-2 transition-colors hover:bg-muted/50">
105
+ <div className="flex h-8 w-8 shrink-0 items-center justify-center rounded-md bg-muted">
106
+ <Calendar className="h-4 w-4 text-muted-foreground" />
107
+ </div>
108
+ <div className="flex min-w-0 flex-col">
109
+ <span className="text-[11px] text-muted-foreground">
110
+ {t('memberSinceLabel')}
111
+ </span>
112
+ <span className="truncate text-xs font-medium text-foreground">
113
+ {formatDateTime(profile.memberSince, getSettingValue)}
114
+ </span>
115
+ </div>
116
+ </div>
117
+
118
+ <div className="flex items-center gap-2.5 rounded-lg p-2 transition-colors hover:bg-muted/50">
119
+ <div className="flex h-8 w-8 shrink-0 items-center justify-center rounded-md bg-blue-50 dark:bg-blue-950/40">
120
+ <Globe className="h-4 w-4 text-blue-600 dark:text-blue-400" />
121
+ </div>
122
+ <div className="flex min-w-0 flex-col">
123
+ <span className="text-[11px] text-muted-foreground">
124
+ {t('activeSessions')}
125
+ </span>
126
+ <span className="text-xs font-semibold text-foreground">
127
+ {profile.totalSessions}
128
+ </span>
129
+ </div>
130
+ </div>
131
+
132
+ <div className="flex items-center gap-2.5 rounded-lg p-2 transition-colors hover:bg-muted/50">
133
+ <div className="flex h-8 w-8 shrink-0 items-center justify-center rounded-md bg-amber-50 dark:bg-amber-950/40">
134
+ <Shield className="h-4 w-4 text-amber-600 dark:text-amber-400" />
135
+ </div>
136
+ <div className="flex min-w-0 flex-col">
137
+ <span className="text-[11px] text-muted-foreground">
138
+ {t('roles')}
139
+ </span>
140
+ <span className="text-xs font-semibold text-foreground">
141
+ {profile.totalRoles}
142
+ </span>
143
+ </div>
144
+ </div>
145
+ </div>
146
+ </CardContent>
147
+
148
+ <div className="absolute bottom-0 inset-x-0 flex items-center gap-2 border-t bg-card px-4 py-2">
149
+ <Clock className="h-3.5 w-3.5 shrink-0 text-muted-foreground" />
150
+ <span className="truncate text-xs text-muted-foreground">
151
+ {t('lastLogin', {
152
+ date: formatDateTime(profile.lastLogin, getSettingValue),
153
+ })}
154
+ </span>
155
+ </div>
156
+ </Card>
157
+ );
158
+ }
159
+
160
+ interface ProfileCardProps {
161
+ widget?: { name?: string };
162
+ onRemove?: () => void;
163
+ }
164
+
165
+ export default function ProfileCard({ widget, onRemove }: ProfileCardProps) {
166
+ const { data, isLoading, isError, isAccessDenied } = useWidgetData<
167
+ AllWidgetsData,
168
+ ProfileData
169
+ >({
170
+ endpoint: '/dashboard-core/widgets/me',
171
+ queryKey: 'widget-me',
172
+ select: (d) => d.profile,
173
+ });
174
+
175
+ return (
176
+ <WidgetWrapper
177
+ isLoading={isLoading}
178
+ isError={isError}
179
+ isAccessDenied={isAccessDenied}
180
+ widgetName={widget?.name ?? 'profile-card'}
181
+ onRemove={onRemove}
182
+ >
183
+ {data && <ProfileContent profile={data} />}
184
+ </WidgetWrapper>
185
+ );
186
+ }
@@ -0,0 +1,196 @@
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
+ StorageConfigWidgetData,
15
+ } from '@/types/widget-data';
16
+ import { CheckCircle2, HardDrive } from 'lucide-react';
17
+ import { useTranslations } from 'next-intl';
18
+ import { WidgetWrapper } from '../widget-wrapper';
19
+
20
+ const defaultStorageConfigData: StorageConfigWidgetData = {
21
+ status: {
22
+ isConfigured: false,
23
+ totalProfiles: 0,
24
+ activeProfiles: 0,
25
+ defaultProfileId: null,
26
+ },
27
+ providers: [],
28
+ profiles: [],
29
+ };
30
+
31
+ interface StorageConfigProps {
32
+ widget?: { name?: string };
33
+ onRemove?: () => void;
34
+ }
35
+
36
+ export default function StorageConfig({
37
+ widget,
38
+ onRemove,
39
+ }: StorageConfigProps) {
40
+ const t = useTranslations('core.DashboardPage.storageConfig');
41
+
42
+ const { data, isLoading, isError, isAccessDenied } = useWidgetData<
43
+ DashboardCoreConfigOverviewData,
44
+ StorageConfigWidgetData
45
+ >({
46
+ endpoint: '/dashboard-core/config/overview',
47
+ queryKey: 'dashboard-core-config-overview',
48
+ select: (d) => d.storageConfig,
49
+ });
50
+
51
+ const storageData = data ?? defaultStorageConfigData;
52
+ const defaultProfile = storageData.profiles.find(
53
+ (profile) => profile.id === storageData.status.defaultProfileId
54
+ );
55
+
56
+ return (
57
+ <WidgetWrapper
58
+ isLoading={isLoading}
59
+ isError={isError}
60
+ isAccessDenied={isAccessDenied}
61
+ widgetName={widget?.name ?? t('title')}
62
+ onRemove={onRemove}
63
+ >
64
+ <Card className="flex h-full min-h-0 flex-col overflow-hidden">
65
+ <CardHeader className="shrink-0">
66
+ <div className="flex items-center justify-between gap-3">
67
+ <div className="flex items-center gap-3">
68
+ <div className="flex h-10 w-10 items-center justify-center rounded-lg bg-violet-50">
69
+ <HardDrive className="h-5 w-5 text-violet-600" />
70
+ </div>
71
+ <div>
72
+ <CardTitle className="text-base">{t('title')}</CardTitle>
73
+ <CardDescription>{t('description')}</CardDescription>
74
+ </div>
75
+ </div>
76
+ <Badge
77
+ variant="secondary"
78
+ className={
79
+ storageData.status.isConfigured
80
+ ? 'bg-emerald-50 text-emerald-700'
81
+ : 'bg-amber-50 text-amber-700'
82
+ }
83
+ >
84
+ {storageData.status.isConfigured ? t('configured') : t('pending')}
85
+ </Badge>
86
+ </div>
87
+ </CardHeader>
88
+ <CardContent className="flex min-h-0 flex-1 flex-col gap-4 overflow-hidden pt-0">
89
+ <div className="grid grid-cols-3 gap-2">
90
+ <div className="rounded-lg border bg-muted/30 p-3 text-center">
91
+ <p className="text-[11px] text-muted-foreground">
92
+ {t('profiles')}
93
+ </p>
94
+ <p className="mt-1 text-lg font-semibold">
95
+ {storageData.status.totalProfiles}
96
+ </p>
97
+ </div>
98
+ <div className="rounded-lg border bg-muted/30 p-3 text-center">
99
+ <p className="text-[11px] text-muted-foreground">{t('active')}</p>
100
+ <p className="mt-1 text-lg font-semibold">
101
+ {storageData.status.activeProfiles}
102
+ </p>
103
+ </div>
104
+ <div className="rounded-lg border bg-muted/30 p-3 text-center">
105
+ <p className="text-[11px] text-muted-foreground">
106
+ {t('providers')}
107
+ </p>
108
+ <p className="mt-1 text-lg font-semibold">
109
+ {storageData.providers.length}
110
+ </p>
111
+ </div>
112
+ </div>
113
+
114
+ <div className="rounded-lg border p-3">
115
+ <p className="mb-1 text-xs text-muted-foreground">
116
+ {t('defaultProfile')}
117
+ </p>
118
+ <p className="truncate text-sm font-medium">
119
+ {defaultProfile?.name || t('notSet')}
120
+ </p>
121
+ {defaultProfile && (
122
+ <p className="mt-1 text-[11px] text-muted-foreground">
123
+ {defaultProfile.providerType.toUpperCase()} |{' '}
124
+ {defaultProfile.bucketName}
125
+ </p>
126
+ )}
127
+ </div>
128
+
129
+ <div className="grid gap-2 sm:grid-cols-2">
130
+ {storageData.providers.map((provider) => (
131
+ <div
132
+ key={provider.providerType}
133
+ className="rounded-lg border p-3"
134
+ >
135
+ <div className="flex items-center justify-between">
136
+ <p className="text-sm font-medium">
137
+ {provider.providerType.toUpperCase()}
138
+ </p>
139
+ <Badge variant="outline" className="text-[10px]">
140
+ {provider.defaults} {t('defaults')}
141
+ </Badge>
142
+ </div>
143
+ <p className="mt-1 text-[11px] text-muted-foreground">
144
+ {t('providerSummary', {
145
+ total: provider.total,
146
+ active: provider.active,
147
+ })}
148
+ </p>
149
+ </div>
150
+ ))}
151
+ </div>
152
+
153
+ <div className="min-h-0 flex-1 space-y-2 overflow-auto">
154
+ {storageData.profiles.slice(0, 4).map((profile) => (
155
+ <div key={profile.id} className="rounded-lg border p-3">
156
+ <div className="flex items-center justify-between gap-2">
157
+ <p className="truncate text-sm font-medium">{profile.name}</p>
158
+ <div className="flex items-center gap-1.5">
159
+ {profile.isDefault && (
160
+ <Badge variant="outline" className="text-[10px]">
161
+ {t('defaultTag')}
162
+ </Badge>
163
+ )}
164
+ <Badge
165
+ variant="secondary"
166
+ className={
167
+ profile.isActive
168
+ ? 'bg-emerald-50 text-emerald-700'
169
+ : 'bg-muted text-muted-foreground'
170
+ }
171
+ >
172
+ {profile.isActive ? (
173
+ <>
174
+ <CheckCircle2 className="mr-1 h-3 w-3" />
175
+ {t('active')}
176
+ </>
177
+ ) : (
178
+ t('inactive')
179
+ )}
180
+ </Badge>
181
+ </div>
182
+ </div>
183
+ <p className="mt-1 truncate text-[11px] text-muted-foreground">
184
+ {profile.providerType.toUpperCase()} | {profile.bucketName}
185
+ </p>
186
+ </div>
187
+ ))}
188
+ {storageData.profiles.length === 0 && (
189
+ <p className="text-xs text-muted-foreground">{t('noProfiles')}</p>
190
+ )}
191
+ </div>
192
+ </CardContent>
193
+ </Card>
194
+ </WidgetWrapper>
195
+ );
196
+ }