@hed-hog/core 0.0.215 → 0.0.216
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/hedhog/frontend/app/dashboard/[slug]/dashboard-content.tsx.ejs +5 -5
- package/hedhog/frontend/app/dashboard/[slug]/widget-renderer.tsx.ejs +1 -1
- package/hedhog/frontend/app/dashboard/components/add-widget-selector-dialog.tsx.ejs +312 -0
- package/hedhog/frontend/app/dashboard/components/dashboard-grid.tsx.ejs +54 -0
- package/hedhog/frontend/app/dashboard/components/draggable-grid.tsx.ejs +132 -0
- package/hedhog/frontend/app/dashboard/components/dynamic-widget.tsx.ejs +88 -0
- package/hedhog/frontend/app/dashboard/components/index.ts.ejs +6 -0
- package/hedhog/frontend/app/dashboard/components/stats.tsx.ejs +93 -0
- package/hedhog/frontend/app/dashboard/components/widget-wrapper.tsx.ejs +150 -0
- package/hedhog/frontend/app/dashboard/components/widgets/account-security.tsx.ejs +184 -0
- package/hedhog/frontend/app/dashboard/components/widgets/active-users-card.tsx.ejs +58 -0
- package/hedhog/frontend/app/dashboard/components/widgets/activity-timeline.tsx.ejs +219 -0
- package/hedhog/frontend/app/dashboard/components/widgets/email-notifications.tsx.ejs +191 -0
- package/hedhog/frontend/app/dashboard/components/widgets/locale-config.tsx.ejs +309 -0
- package/hedhog/frontend/app/dashboard/components/widgets/login-history-chart.tsx.ejs +111 -0
- package/hedhog/frontend/app/dashboard/components/widgets/mail-config.tsx.ejs +445 -0
- package/hedhog/frontend/app/dashboard/components/widgets/mail-sent-card.tsx.ejs +58 -0
- package/hedhog/frontend/app/dashboard/components/widgets/mail-sent-chart.tsx.ejs +149 -0
- package/hedhog/frontend/app/dashboard/components/widgets/oauth-config.tsx.ejs +296 -0
- package/hedhog/frontend/app/dashboard/components/widgets/permissions-card.tsx.ejs +61 -0
- package/hedhog/frontend/app/dashboard/components/widgets/permissions-chart.tsx.ejs +152 -0
- package/hedhog/frontend/app/dashboard/components/widgets/profile-card.tsx.ejs +186 -0
- package/hedhog/frontend/app/dashboard/components/widgets/session-activity-chart.tsx.ejs +183 -0
- package/hedhog/frontend/app/dashboard/components/widgets/sessions-today-card.tsx.ejs +62 -0
- package/hedhog/frontend/app/dashboard/components/widgets/stat-access-level.tsx.ejs +57 -0
- package/hedhog/frontend/app/dashboard/components/widgets/stat-actions-today.tsx.ejs +57 -0
- package/hedhog/frontend/app/dashboard/components/widgets/stat-consecutive-days.tsx.ejs +57 -0
- package/hedhog/frontend/app/dashboard/components/widgets/stat-online-time.tsx.ejs +57 -0
- package/hedhog/frontend/app/dashboard/components/widgets/storage-config.tsx.ejs +340 -0
- package/hedhog/frontend/app/dashboard/components/widgets/theme-config.tsx.ejs +275 -0
- package/hedhog/frontend/app/dashboard/components/widgets/user-growth-chart.tsx.ejs +210 -0
- package/hedhog/frontend/app/dashboard/components/widgets/user-roles.tsx.ejs +130 -0
- package/hedhog/frontend/app/dashboard/components/widgets/user-sessions.tsx.ejs +233 -0
- package/hedhog/frontend/messages/en.json +143 -1
- package/hedhog/frontend/messages/pt.json +143 -1
- package/package.json +3 -3
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { Badge } from '@/components/ui/badge';
|
|
4
|
+
import { Button } from '@/components/ui/button';
|
|
5
|
+
import {
|
|
6
|
+
Card,
|
|
7
|
+
CardContent,
|
|
8
|
+
CardDescription,
|
|
9
|
+
CardHeader,
|
|
10
|
+
CardTitle,
|
|
11
|
+
} from '@/components/ui/card';
|
|
12
|
+
import { Checkbox } from '@/components/ui/checkbox';
|
|
13
|
+
import { Label } from '@/components/ui/label';
|
|
14
|
+
import {
|
|
15
|
+
Select,
|
|
16
|
+
SelectContent,
|
|
17
|
+
SelectItem,
|
|
18
|
+
SelectTrigger,
|
|
19
|
+
SelectValue,
|
|
20
|
+
} from '@/components/ui/select';
|
|
21
|
+
import { Switch } from '@/components/ui/switch';
|
|
22
|
+
import { Calendar, CheckCircle2, Clock, Globe, Type } from 'lucide-react';
|
|
23
|
+
import { useState } from 'react';
|
|
24
|
+
|
|
25
|
+
interface Language {
|
|
26
|
+
code: string;
|
|
27
|
+
label: string;
|
|
28
|
+
flag: string;
|
|
29
|
+
enabled: boolean;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const availableLanguages: Language[] = [
|
|
33
|
+
{ code: 'pt-BR', label: 'Portugues (Brasil)', flag: '🇧🇷', enabled: true },
|
|
34
|
+
{ code: 'en-US', label: 'Ingles (EUA)', flag: '🇺🇸', enabled: true },
|
|
35
|
+
{ code: 'es-ES', label: 'Espanhol (Espanha)', flag: '🇪🇸', enabled: false },
|
|
36
|
+
{ code: 'fr-FR', label: 'Frances (Franca)', flag: '🇫🇷', enabled: false },
|
|
37
|
+
{ code: 'de-DE', label: 'Alemao (Alemanha)', flag: '🇩🇪', enabled: false },
|
|
38
|
+
{ code: 'it-IT', label: 'Italiano (Italia)', flag: '🇮🇹', enabled: false },
|
|
39
|
+
{ code: 'ja-JP', label: 'Japones (Japao)', flag: '🇯🇵', enabled: false },
|
|
40
|
+
{ code: 'zh-CN', label: 'Chines (Simplificado)', flag: '🇨🇳', enabled: false },
|
|
41
|
+
];
|
|
42
|
+
|
|
43
|
+
const timezones = [
|
|
44
|
+
{ value: 'America/Sao_Paulo', label: 'America/Sao_Paulo (GMT-3)' },
|
|
45
|
+
{ value: 'America/New_York', label: 'America/New_York (GMT-5)' },
|
|
46
|
+
{ value: 'America/Chicago', label: 'America/Chicago (GMT-6)' },
|
|
47
|
+
{ value: 'America/Denver', label: 'America/Denver (GMT-7)' },
|
|
48
|
+
{ value: 'America/Los_Angeles', label: 'America/Los_Angeles (GMT-8)' },
|
|
49
|
+
{ value: 'Europe/London', label: 'Europe/London (GMT+0)' },
|
|
50
|
+
{ value: 'Europe/Paris', label: 'Europe/Paris (GMT+1)' },
|
|
51
|
+
{ value: 'Europe/Berlin', label: 'Europe/Berlin (GMT+1)' },
|
|
52
|
+
{ value: 'Asia/Tokyo', label: 'Asia/Tokyo (GMT+9)' },
|
|
53
|
+
{ value: 'Asia/Shanghai', label: 'Asia/Shanghai (GMT+8)' },
|
|
54
|
+
{ value: 'Australia/Sydney', label: 'Australia/Sydney (GMT+11)' },
|
|
55
|
+
];
|
|
56
|
+
|
|
57
|
+
const dateFormats = [
|
|
58
|
+
{ value: 'DD/MM/YYYY', label: 'DD/MM/YYYY', example: '14/02/2026' },
|
|
59
|
+
{ value: 'MM/DD/YYYY', label: 'MM/DD/YYYY', example: '02/14/2026' },
|
|
60
|
+
{ value: 'YYYY-MM-DD', label: 'YYYY-MM-DD', example: '2026-02-14' },
|
|
61
|
+
{ value: 'DD.MM.YYYY', label: 'DD.MM.YYYY', example: '14.02.2026' },
|
|
62
|
+
{ value: 'DD-MM-YYYY', label: 'DD-MM-YYYY', example: '14-02-2026' },
|
|
63
|
+
];
|
|
64
|
+
|
|
65
|
+
const timeFormats = [
|
|
66
|
+
{ value: 'HH:mm', label: '24 horas', example: '14:30' },
|
|
67
|
+
{ value: 'hh:mm A', label: '12 horas', example: '02:30 PM' },
|
|
68
|
+
{ value: 'HH:mm:ss', label: '24 horas com segundos', example: '14:30:45' },
|
|
69
|
+
{
|
|
70
|
+
value: 'hh:mm:ss A',
|
|
71
|
+
label: '12 horas com segundos',
|
|
72
|
+
example: '02:30:45 PM',
|
|
73
|
+
},
|
|
74
|
+
];
|
|
75
|
+
|
|
76
|
+
export default function LocaleConfig() {
|
|
77
|
+
const [languages, setLanguages] = useState<Language[]>(availableLanguages);
|
|
78
|
+
const [defaultLang, setDefaultLang] = useState('pt-BR');
|
|
79
|
+
const [timezone, setTimezone] = useState('America/Sao_Paulo');
|
|
80
|
+
const [dateFormat, setDateFormat] = useState('DD/MM/YYYY');
|
|
81
|
+
const [timeFormat, setTimeFormat] = useState('HH:mm');
|
|
82
|
+
const [autoDetect, setAutoDetect] = useState(true);
|
|
83
|
+
|
|
84
|
+
const enabledLanguages = languages.filter((l) => l.enabled);
|
|
85
|
+
|
|
86
|
+
function toggleLanguage(code: string) {
|
|
87
|
+
setLanguages((prev) =>
|
|
88
|
+
prev.map((l) => {
|
|
89
|
+
if (l.code === code) {
|
|
90
|
+
if (l.enabled && l.code === defaultLang) return l;
|
|
91
|
+
return { ...l, enabled: !l.enabled };
|
|
92
|
+
}
|
|
93
|
+
return l;
|
|
94
|
+
})
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return (
|
|
99
|
+
<Card className="h-full">
|
|
100
|
+
<CardHeader>
|
|
101
|
+
<div className="flex items-center justify-between">
|
|
102
|
+
<div className="flex items-center gap-3">
|
|
103
|
+
<div className="flex h-10 w-10 items-center justify-center rounded-lg bg-indigo-50">
|
|
104
|
+
<Globe className="h-5 w-5 text-indigo-600" />
|
|
105
|
+
</div>
|
|
106
|
+
<div>
|
|
107
|
+
<CardTitle className="text-base">Localizacao</CardTitle>
|
|
108
|
+
<CardDescription>
|
|
109
|
+
Idiomas, fuso horario e formatos regionais
|
|
110
|
+
</CardDescription>
|
|
111
|
+
</div>
|
|
112
|
+
</div>
|
|
113
|
+
<Badge variant="secondary" className="bg-indigo-50 text-indigo-700">
|
|
114
|
+
{enabledLanguages.length} idioma
|
|
115
|
+
{enabledLanguages.length !== 1 ? 's' : ''}
|
|
116
|
+
</Badge>
|
|
117
|
+
</div>
|
|
118
|
+
</CardHeader>
|
|
119
|
+
<CardContent className="space-y-6">
|
|
120
|
+
{/* Languages */}
|
|
121
|
+
<div className="space-y-3">
|
|
122
|
+
<div className="flex items-center gap-2">
|
|
123
|
+
<Type className="h-4 w-4 text-muted-foreground" />
|
|
124
|
+
<Label className="text-sm font-medium">Idiomas habilitados</Label>
|
|
125
|
+
</div>
|
|
126
|
+
<div className="grid grid-cols-2 gap-2">
|
|
127
|
+
{languages.map((lang) => (
|
|
128
|
+
<label
|
|
129
|
+
key={lang.code}
|
|
130
|
+
className={`flex cursor-pointer items-center gap-3 rounded-lg border p-3 transition-colors ${
|
|
131
|
+
lang.enabled
|
|
132
|
+
? 'border-foreground/20 bg-foreground/2'
|
|
133
|
+
: 'border-border hover:border-foreground/10'
|
|
134
|
+
}`}
|
|
135
|
+
>
|
|
136
|
+
<Checkbox
|
|
137
|
+
checked={lang.enabled}
|
|
138
|
+
onCheckedChange={() => toggleLanguage(lang.code)}
|
|
139
|
+
disabled={lang.enabled && lang.code === defaultLang}
|
|
140
|
+
/>
|
|
141
|
+
<span className="text-base leading-none">{lang.flag}</span>
|
|
142
|
+
<div className="flex flex-1 flex-col">
|
|
143
|
+
<span className="text-sm font-medium">{lang.label}</span>
|
|
144
|
+
<span className="text-xs text-muted-foreground">
|
|
145
|
+
{lang.code}
|
|
146
|
+
</span>
|
|
147
|
+
</div>
|
|
148
|
+
{lang.code === defaultLang && (
|
|
149
|
+
<Badge variant="outline" className="text-[10px] h-5">
|
|
150
|
+
Padrao
|
|
151
|
+
</Badge>
|
|
152
|
+
)}
|
|
153
|
+
</label>
|
|
154
|
+
))}
|
|
155
|
+
</div>
|
|
156
|
+
</div>
|
|
157
|
+
|
|
158
|
+
{/* Default Language */}
|
|
159
|
+
<div className="space-y-2">
|
|
160
|
+
<Label>Idioma padrao</Label>
|
|
161
|
+
<Select value={defaultLang} onValueChange={setDefaultLang}>
|
|
162
|
+
<SelectTrigger>
|
|
163
|
+
<SelectValue />
|
|
164
|
+
</SelectTrigger>
|
|
165
|
+
<SelectContent>
|
|
166
|
+
{enabledLanguages.map((lang) => (
|
|
167
|
+
<SelectItem key={lang.code} value={lang.code}>
|
|
168
|
+
<span className="flex items-center gap-2">
|
|
169
|
+
<span>{lang.flag}</span>
|
|
170
|
+
<span>{lang.label}</span>
|
|
171
|
+
</span>
|
|
172
|
+
</SelectItem>
|
|
173
|
+
))}
|
|
174
|
+
</SelectContent>
|
|
175
|
+
</Select>
|
|
176
|
+
<p className="text-xs text-muted-foreground">
|
|
177
|
+
Idioma exibido quando o idioma do usuario nao esta disponivel.
|
|
178
|
+
</p>
|
|
179
|
+
</div>
|
|
180
|
+
|
|
181
|
+
{/* Timezone */}
|
|
182
|
+
<div className="space-y-2">
|
|
183
|
+
<div className="flex items-center gap-2">
|
|
184
|
+
<Clock className="h-4 w-4 text-muted-foreground" />
|
|
185
|
+
<Label>Fuso horario do sistema</Label>
|
|
186
|
+
</div>
|
|
187
|
+
<Select value={timezone} onValueChange={setTimezone}>
|
|
188
|
+
<SelectTrigger>
|
|
189
|
+
<SelectValue />
|
|
190
|
+
</SelectTrigger>
|
|
191
|
+
<SelectContent>
|
|
192
|
+
{timezones.map((tz) => (
|
|
193
|
+
<SelectItem key={tz.value} value={tz.value}>
|
|
194
|
+
{tz.label}
|
|
195
|
+
</SelectItem>
|
|
196
|
+
))}
|
|
197
|
+
</SelectContent>
|
|
198
|
+
</Select>
|
|
199
|
+
</div>
|
|
200
|
+
|
|
201
|
+
{/* Date & Time Formats */}
|
|
202
|
+
<div className="grid gap-4 sm:grid-cols-2">
|
|
203
|
+
<div className="space-y-2">
|
|
204
|
+
<div className="flex items-center gap-2">
|
|
205
|
+
<Calendar className="h-4 w-4 text-muted-foreground" />
|
|
206
|
+
<Label>Formato de data</Label>
|
|
207
|
+
</div>
|
|
208
|
+
<Select value={dateFormat} onValueChange={setDateFormat}>
|
|
209
|
+
<SelectTrigger>
|
|
210
|
+
<SelectValue />
|
|
211
|
+
</SelectTrigger>
|
|
212
|
+
<SelectContent>
|
|
213
|
+
{dateFormats.map((df) => (
|
|
214
|
+
<SelectItem key={df.value} value={df.value}>
|
|
215
|
+
<span className="flex items-center gap-2">
|
|
216
|
+
<span className="font-mono text-xs">{df.value}</span>
|
|
217
|
+
<span className="text-muted-foreground">
|
|
218
|
+
{'('}
|
|
219
|
+
{df.example}
|
|
220
|
+
{')'}
|
|
221
|
+
</span>
|
|
222
|
+
</span>
|
|
223
|
+
</SelectItem>
|
|
224
|
+
))}
|
|
225
|
+
</SelectContent>
|
|
226
|
+
</Select>
|
|
227
|
+
</div>
|
|
228
|
+
<div className="space-y-2">
|
|
229
|
+
<div className="flex items-center gap-2">
|
|
230
|
+
<Clock className="h-4 w-4 text-muted-foreground" />
|
|
231
|
+
<Label>Formato de hora</Label>
|
|
232
|
+
</div>
|
|
233
|
+
<Select value={timeFormat} onValueChange={setTimeFormat}>
|
|
234
|
+
<SelectTrigger>
|
|
235
|
+
<SelectValue />
|
|
236
|
+
</SelectTrigger>
|
|
237
|
+
<SelectContent>
|
|
238
|
+
{timeFormats.map((tf) => (
|
|
239
|
+
<SelectItem key={tf.value} value={tf.value}>
|
|
240
|
+
<span className="flex items-center gap-2">
|
|
241
|
+
<span>{tf.label}</span>
|
|
242
|
+
<span className="text-muted-foreground font-mono text-xs">
|
|
243
|
+
{'('}
|
|
244
|
+
{tf.example}
|
|
245
|
+
{')'}
|
|
246
|
+
</span>
|
|
247
|
+
</span>
|
|
248
|
+
</SelectItem>
|
|
249
|
+
))}
|
|
250
|
+
</SelectContent>
|
|
251
|
+
</Select>
|
|
252
|
+
</div>
|
|
253
|
+
</div>
|
|
254
|
+
|
|
255
|
+
{/* Preview */}
|
|
256
|
+
<div className="rounded-lg border bg-muted/50 p-4">
|
|
257
|
+
<p className="mb-2 text-xs font-medium text-muted-foreground uppercase tracking-wider">
|
|
258
|
+
Pre-visualizacao
|
|
259
|
+
</p>
|
|
260
|
+
<div className="flex flex-wrap gap-x-6 gap-y-1 text-sm">
|
|
261
|
+
<span>
|
|
262
|
+
<span className="text-muted-foreground">Data: </span>
|
|
263
|
+
<span className="font-medium font-mono">
|
|
264
|
+
{dateFormats.find((d) => d.value === dateFormat)?.example}
|
|
265
|
+
</span>
|
|
266
|
+
</span>
|
|
267
|
+
<span>
|
|
268
|
+
<span className="text-muted-foreground">Hora: </span>
|
|
269
|
+
<span className="font-medium font-mono">
|
|
270
|
+
{timeFormats.find((t) => t.value === timeFormat)?.example}
|
|
271
|
+
</span>
|
|
272
|
+
</span>
|
|
273
|
+
<span>
|
|
274
|
+
<span className="text-muted-foreground">Fuso: </span>
|
|
275
|
+
<span className="font-medium font-mono">
|
|
276
|
+
{
|
|
277
|
+
timezones
|
|
278
|
+
.find((t) => t.value === timezone)
|
|
279
|
+
?.label.split(' ')[0]
|
|
280
|
+
}
|
|
281
|
+
</span>
|
|
282
|
+
</span>
|
|
283
|
+
</div>
|
|
284
|
+
</div>
|
|
285
|
+
|
|
286
|
+
{/* Actions */}
|
|
287
|
+
<div className="flex items-center justify-between border-t pt-4">
|
|
288
|
+
<div className="flex items-center gap-2">
|
|
289
|
+
<Switch
|
|
290
|
+
id="auto-detect-lang"
|
|
291
|
+
checked={autoDetect}
|
|
292
|
+
onCheckedChange={setAutoDetect}
|
|
293
|
+
/>
|
|
294
|
+
<Label
|
|
295
|
+
htmlFor="auto-detect-lang"
|
|
296
|
+
className="text-sm text-muted-foreground"
|
|
297
|
+
>
|
|
298
|
+
Detectar idioma do navegador
|
|
299
|
+
</Label>
|
|
300
|
+
</div>
|
|
301
|
+
<Button size="sm">
|
|
302
|
+
<CheckCircle2 className="mr-1.5 h-3.5 w-3.5" />
|
|
303
|
+
Salvar
|
|
304
|
+
</Button>
|
|
305
|
+
</div>
|
|
306
|
+
</CardContent>
|
|
307
|
+
</Card>
|
|
308
|
+
);
|
|
309
|
+
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
Card,
|
|
5
|
+
CardContent,
|
|
6
|
+
CardDescription,
|
|
7
|
+
CardHeader,
|
|
8
|
+
CardTitle,
|
|
9
|
+
} from '@/components/ui/card';
|
|
10
|
+
import {
|
|
11
|
+
ChartContainer,
|
|
12
|
+
ChartTooltip,
|
|
13
|
+
ChartTooltipContent,
|
|
14
|
+
} from '@/components/ui/chart';
|
|
15
|
+
import { useWidgetData } from '@/hooks/use-widget-data';
|
|
16
|
+
import type { AllWidgetsData, LoginDay } from '@/types/widget-data';
|
|
17
|
+
import { LogIn } from 'lucide-react';
|
|
18
|
+
import { useTranslations } from 'next-intl';
|
|
19
|
+
import { Bar, BarChart, CartesianGrid, XAxis, YAxis } from 'recharts';
|
|
20
|
+
import { WidgetWrapper } from '../widget-wrapper';
|
|
21
|
+
|
|
22
|
+
function LoginChart({ data }: { data: LoginDay[] }) {
|
|
23
|
+
const t = useTranslations('core.DashboardPage.loginHistory');
|
|
24
|
+
|
|
25
|
+
const chartConfig = {
|
|
26
|
+
logins: { label: t('logins'), color: 'hsl(221, 83%, 53%)' },
|
|
27
|
+
failed: { label: t('failed'), color: 'hsl(0, 84%, 60%)' },
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
return (
|
|
31
|
+
<Card className="flex h-full flex-col">
|
|
32
|
+
<CardHeader className="shrink-0 pb-2">
|
|
33
|
+
<div className="flex items-center gap-2">
|
|
34
|
+
<LogIn className="h-5 w-5 text-blue-600" />
|
|
35
|
+
<div>
|
|
36
|
+
<CardTitle className="text-base font-semibold">
|
|
37
|
+
{t('title')}
|
|
38
|
+
</CardTitle>
|
|
39
|
+
<CardDescription>{t('description')}</CardDescription>
|
|
40
|
+
</div>
|
|
41
|
+
</div>
|
|
42
|
+
</CardHeader>
|
|
43
|
+
<CardContent className="flex flex-1 flex-col pt-0">
|
|
44
|
+
<ChartContainer
|
|
45
|
+
config={chartConfig}
|
|
46
|
+
className="h-full min-h-[120px] w-full flex-1"
|
|
47
|
+
>
|
|
48
|
+
<BarChart data={data as any} barGap={2}>
|
|
49
|
+
<CartesianGrid vertical={false} strokeDasharray="3 3" />
|
|
50
|
+
<XAxis
|
|
51
|
+
dataKey="day"
|
|
52
|
+
tickLine={false}
|
|
53
|
+
axisLine={false}
|
|
54
|
+
fontSize={12}
|
|
55
|
+
tickMargin={8}
|
|
56
|
+
/>
|
|
57
|
+
<YAxis
|
|
58
|
+
tickLine={false}
|
|
59
|
+
axisLine={false}
|
|
60
|
+
fontSize={12}
|
|
61
|
+
tickMargin={4}
|
|
62
|
+
allowDecimals={false}
|
|
63
|
+
/>
|
|
64
|
+
<ChartTooltip content={<ChartTooltipContent />} />
|
|
65
|
+
<Bar
|
|
66
|
+
dataKey="logins"
|
|
67
|
+
fill="hsl(221, 83%, 53%)"
|
|
68
|
+
radius={[4, 4, 0, 0]}
|
|
69
|
+
/>
|
|
70
|
+
<Bar
|
|
71
|
+
dataKey="failed"
|
|
72
|
+
fill="hsl(0, 84%, 60%)"
|
|
73
|
+
radius={[4, 4, 0, 0]}
|
|
74
|
+
/>
|
|
75
|
+
</BarChart>
|
|
76
|
+
</ChartContainer>
|
|
77
|
+
</CardContent>
|
|
78
|
+
</Card>
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
interface LoginHistoryChartProps {
|
|
83
|
+
widget?: { name?: string };
|
|
84
|
+
onRemove?: () => void;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export default function LoginHistoryChart({
|
|
88
|
+
widget,
|
|
89
|
+
onRemove,
|
|
90
|
+
}: LoginHistoryChartProps) {
|
|
91
|
+
const { data, isLoading, isError, isAccessDenied } = useWidgetData<
|
|
92
|
+
AllWidgetsData,
|
|
93
|
+
LoginDay[]
|
|
94
|
+
>({
|
|
95
|
+
endpoint: '/dashboard-core/widgets/me',
|
|
96
|
+
queryKey: 'widget-me',
|
|
97
|
+
select: (d) => d.loginHistory,
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
return (
|
|
101
|
+
<WidgetWrapper
|
|
102
|
+
isLoading={isLoading}
|
|
103
|
+
isError={isError}
|
|
104
|
+
isAccessDenied={isAccessDenied}
|
|
105
|
+
widgetName={widget?.name ?? 'login-history-chart'}
|
|
106
|
+
onRemove={onRemove}
|
|
107
|
+
>
|
|
108
|
+
{data && <LoginChart data={data} />}
|
|
109
|
+
</WidgetWrapper>
|
|
110
|
+
);
|
|
111
|
+
}
|