@hed-hog/core 0.0.299 → 0.0.300
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/dashboard/dashboard/dashboard.controller.d.ts +6 -0
- package/dist/dashboard/dashboard/dashboard.controller.d.ts.map +1 -1
- package/dist/dashboard/dashboard/dashboard.service.d.ts +6 -0
- package/dist/dashboard/dashboard/dashboard.service.d.ts.map +1 -1
- package/dist/dashboard/dashboard-component/dashboard-component.controller.d.ts +2 -1
- package/dist/dashboard/dashboard-component/dashboard-component.controller.d.ts.map +1 -1
- package/dist/dashboard/dashboard-component/dashboard-component.controller.js +6 -3
- package/dist/dashboard/dashboard-component/dashboard-component.controller.js.map +1 -1
- package/dist/dashboard/dashboard-component/dashboard-component.service.d.ts +7 -1
- package/dist/dashboard/dashboard-component/dashboard-component.service.d.ts.map +1 -1
- package/dist/dashboard/dashboard-component/dashboard-component.service.js +76 -33
- package/dist/dashboard/dashboard-component/dashboard-component.service.js.map +1 -1
- package/dist/dashboard/dashboard-core/dashboard-core.controller.d.ts +65 -0
- package/dist/dashboard/dashboard-core/dashboard-core.controller.d.ts.map +1 -1
- package/dist/dashboard/dashboard-core/dashboard-core.controller.js +111 -0
- package/dist/dashboard/dashboard-core/dashboard-core.controller.js.map +1 -1
- package/dist/dashboard/dashboard-core/dashboard-core.service.d.ts +69 -0
- package/dist/dashboard/dashboard-core/dashboard-core.service.d.ts.map +1 -1
- package/dist/dashboard/dashboard-core/dashboard-core.service.js +526 -19
- package/dist/dashboard/dashboard-core/dashboard-core.service.js.map +1 -1
- package/dist/dashboard/dashboard-item/dashboard-item.controller.d.ts +2 -0
- package/dist/dashboard/dashboard-item/dashboard-item.controller.d.ts.map +1 -1
- package/dist/dashboard/dashboard-item/dashboard-item.service.d.ts +2 -0
- package/dist/dashboard/dashboard-item/dashboard-item.service.d.ts.map +1 -1
- package/dist/dashboard/dashboard-role/dashboard-role.controller.d.ts +2 -0
- package/dist/dashboard/dashboard-role/dashboard-role.controller.d.ts.map +1 -1
- package/dist/dashboard/dashboard-role/dashboard-role.service.d.ts +2 -0
- package/dist/dashboard/dashboard-role/dashboard-role.service.d.ts.map +1 -1
- package/hedhog/data/dashboard.yaml +12 -6
- package/hedhog/data/dashboard_component_role.yaml +66 -0
- package/hedhog/data/dashboard_role.yaml +2 -8
- package/hedhog/data/route.yaml +72 -0
- package/hedhog/frontend/app/dashboard/[slug]/dashboard-content.tsx.ejs +333 -128
- package/hedhog/frontend/app/dashboard/[slug]/widget-renderer.tsx.ejs +277 -53
- package/hedhog/frontend/app/dashboard/components/add-widget-selector-dialog.tsx.ejs +179 -231
- package/hedhog/frontend/app/dashboard/components/draggable-grid.tsx.ejs +64 -18
- package/hedhog/frontend/app/dashboard/dashboard-home-tabs.tsx.ejs +1389 -0
- package/hedhog/frontend/app/dashboard/dashboard.css.ejs +37 -0
- package/hedhog/frontend/app/dashboard/management/page.tsx.ejs +1 -1
- package/hedhog/frontend/app/dashboard/management/tabs/components-tab.tsx.ejs +6 -6
- package/hedhog/frontend/app/dashboard/management/tabs/dashboards-tab.tsx.ejs +8 -8
- package/hedhog/frontend/app/dashboard/management/tabs/items-tab.tsx.ejs +3 -3
- package/hedhog/frontend/app/dashboard/page.tsx.ejs +3 -25
- package/hedhog/frontend/messages/en.json +112 -2
- package/hedhog/frontend/messages/pt.json +111 -1
- package/hedhog/frontend/widgets/account-security.tsx.ejs +1 -1
- package/hedhog/frontend/widgets/active-users-card.tsx.ejs +2 -2
- package/hedhog/frontend/widgets/activity-timeline.tsx.ejs +1 -1
- package/hedhog/frontend/widgets/email-notifications.tsx.ejs +1 -1
- package/hedhog/frontend/widgets/locale-config.tsx.ejs +1 -1
- package/hedhog/frontend/widgets/login-history-chart.tsx.ejs +1 -1
- package/hedhog/frontend/widgets/mail-config.tsx.ejs +1 -1
- package/hedhog/frontend/widgets/mail-sent-card.tsx.ejs +2 -2
- package/hedhog/frontend/widgets/mail-sent-chart.tsx.ejs +1 -1
- package/hedhog/frontend/widgets/menus-card.tsx.ejs +2 -2
- package/hedhog/frontend/widgets/oauth-config.tsx.ejs +1 -1
- package/hedhog/frontend/widgets/permissions-card.tsx.ejs +2 -2
- package/hedhog/frontend/widgets/permissions-chart.tsx.ejs +1 -1
- package/hedhog/frontend/widgets/profile-card.tsx.ejs +1 -1
- package/hedhog/frontend/widgets/routes-card.tsx.ejs +2 -2
- package/hedhog/frontend/widgets/session-activity-chart.tsx.ejs +1 -1
- package/hedhog/frontend/widgets/sessions-today-card.tsx.ejs +2 -2
- package/hedhog/frontend/widgets/stat-access-level.tsx.ejs +1 -1
- package/hedhog/frontend/widgets/stat-actions-today.tsx.ejs +1 -1
- package/hedhog/frontend/widgets/stat-consecutive-days.tsx.ejs +1 -1
- package/hedhog/frontend/widgets/stat-online-time.tsx.ejs +1 -1
- package/hedhog/frontend/widgets/storage-config.tsx.ejs +1 -1
- package/hedhog/frontend/widgets/theme-config.tsx.ejs +1 -1
- package/hedhog/frontend/widgets/user-growth-chart.tsx.ejs +1 -1
- package/hedhog/frontend/widgets/user-roles.tsx.ejs +1 -1
- package/hedhog/frontend/widgets/user-sessions.tsx.ejs +1 -1
- package/hedhog/table/dashboard.yaml +6 -0
- package/package.json +5 -5
- package/src/dashboard/dashboard-component/dashboard-component.controller.ts +15 -2
- package/src/dashboard/dashboard-component/dashboard-component.service.ts +107 -43
- package/src/dashboard/dashboard-core/dashboard-core.controller.ts +112 -1
- package/src/dashboard/dashboard-core/dashboard-core.service.ts +674 -19
- package/hedhog/frontend/app/dashboard/components/widgets/core..gitkeep.ejs +0 -11
- package/hedhog/frontend/app/dashboard/components/widgets/core.account-security.tsx.ejs +0 -192
- package/hedhog/frontend/app/dashboard/components/widgets/core.active-users-card.tsx.ejs +0 -58
- package/hedhog/frontend/app/dashboard/components/widgets/core.activity-timeline.tsx.ejs +0 -223
- package/hedhog/frontend/app/dashboard/components/widgets/core.email-notifications.tsx.ejs +0 -226
- package/hedhog/frontend/app/dashboard/components/widgets/core.locale-config.tsx.ejs +0 -168
- package/hedhog/frontend/app/dashboard/components/widgets/core.login-history-chart.tsx.ejs +0 -115
- package/hedhog/frontend/app/dashboard/components/widgets/core.mail-config.tsx.ejs +0 -199
- package/hedhog/frontend/app/dashboard/components/widgets/core.mail-sent-card.tsx.ejs +0 -58
- package/hedhog/frontend/app/dashboard/components/widgets/core.mail-sent-chart.tsx.ejs +0 -149
- package/hedhog/frontend/app/dashboard/components/widgets/core.menus-card.tsx.ejs +0 -58
- package/hedhog/frontend/app/dashboard/components/widgets/core.oauth-config.tsx.ejs +0 -175
- package/hedhog/frontend/app/dashboard/components/widgets/core.permissions-card.tsx.ejs +0 -61
- package/hedhog/frontend/app/dashboard/components/widgets/core.permissions-chart.tsx.ejs +0 -156
- package/hedhog/frontend/app/dashboard/components/widgets/core.profile-card.tsx.ejs +0 -186
- package/hedhog/frontend/app/dashboard/components/widgets/core.routes-card.tsx.ejs +0 -58
- package/hedhog/frontend/app/dashboard/components/widgets/core.session-activity-chart.tsx.ejs +0 -183
- package/hedhog/frontend/app/dashboard/components/widgets/core.sessions-today-card.tsx.ejs +0 -62
- package/hedhog/frontend/app/dashboard/components/widgets/core.stat-access-level.tsx.ejs +0 -57
- package/hedhog/frontend/app/dashboard/components/widgets/core.stat-actions-today.tsx.ejs +0 -57
- package/hedhog/frontend/app/dashboard/components/widgets/core.stat-consecutive-days.tsx.ejs +0 -57
- package/hedhog/frontend/app/dashboard/components/widgets/core.stat-online-time.tsx.ejs +0 -57
- package/hedhog/frontend/app/dashboard/components/widgets/core.storage-config.tsx.ejs +0 -196
- package/hedhog/frontend/app/dashboard/components/widgets/core.theme-config.tsx.ejs +0 -213
- package/hedhog/frontend/app/dashboard/components/widgets/core.user-growth-chart.tsx.ejs +0 -210
- package/hedhog/frontend/app/dashboard/components/widgets/core.user-roles.tsx.ejs +0 -132
- package/hedhog/frontend/app/dashboard/components/widgets/core.user-sessions.tsx.ejs +0 -236
- package/hedhog/frontend/app/dashboard/components/widgets/finance.alerts.tsx.ejs +0 -108
- package/hedhog/frontend/app/dashboard/components/widgets/finance.cash-balance-kpi.tsx.ejs +0 -66
- package/hedhog/frontend/app/dashboard/components/widgets/finance.cash-flow-chart.tsx.ejs +0 -122
- package/hedhog/frontend/app/dashboard/components/widgets/finance.default-kpi.tsx.ejs +0 -63
- package/hedhog/frontend/app/dashboard/components/widgets/finance.payable-30d-kpi.tsx.ejs +0 -73
- package/hedhog/frontend/app/dashboard/components/widgets/finance.receivable-30d-kpi.tsx.ejs +0 -73
- package/hedhog/frontend/app/dashboard/components/widgets/finance.upcoming-payable.tsx.ejs +0 -123
- package/hedhog/frontend/app/dashboard/components/widgets/finance.upcoming-receivable.tsx.ejs +0 -118
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
import { useWidgetData } from '@/hooks/use-widget-data';
|
|
2
|
-
import { Shield } from 'lucide-react';
|
|
3
|
-
import { useTranslations } from 'next-intl';
|
|
4
|
-
import StatCard from '../stats';
|
|
5
|
-
import { WidgetWrapper } from '../widget-wrapper';
|
|
6
|
-
|
|
7
|
-
interface PermissionsCardProps {
|
|
8
|
-
widget?: any;
|
|
9
|
-
onRemove?: () => void;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
interface UserStatsData {
|
|
13
|
-
cards?: {
|
|
14
|
-
roles?: {
|
|
15
|
-
value: number;
|
|
16
|
-
change: number | null;
|
|
17
|
-
};
|
|
18
|
-
};
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export default function PermissionsCard({
|
|
22
|
-
widget,
|
|
23
|
-
onRemove,
|
|
24
|
-
}: PermissionsCardProps) {
|
|
25
|
-
const t = useTranslations('core.Dashboard');
|
|
26
|
-
|
|
27
|
-
const { data, isLoading, isAccessDenied, isError } =
|
|
28
|
-
useWidgetData<UserStatsData>({
|
|
29
|
-
endpoint: '/dashboard-core/stats/overview/users',
|
|
30
|
-
queryKey: 'dashboard-stats-users',
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
const value = data?.cards?.roles?.value?.toLocaleString('pt-BR') || '0';
|
|
34
|
-
const change = data?.cards?.roles?.change;
|
|
35
|
-
const changeType =
|
|
36
|
-
change !== null && change !== undefined && change >= 0 ? 'up' : 'down';
|
|
37
|
-
|
|
38
|
-
return (
|
|
39
|
-
<WidgetWrapper
|
|
40
|
-
isLoading={isLoading}
|
|
41
|
-
isAccessDenied={isAccessDenied}
|
|
42
|
-
isError={isError}
|
|
43
|
-
widgetName={widget?.name || t('permissions')}
|
|
44
|
-
onRemove={onRemove}
|
|
45
|
-
>
|
|
46
|
-
<StatCard
|
|
47
|
-
title={t('permissions')}
|
|
48
|
-
value={value}
|
|
49
|
-
change={
|
|
50
|
-
change !== null && change !== undefined
|
|
51
|
-
? `${change > 0 ? '+' : ''}${change}%`
|
|
52
|
-
: undefined
|
|
53
|
-
}
|
|
54
|
-
changeType={changeType}
|
|
55
|
-
icon={<Shield className="h-6 w-6 text-rose-500" />}
|
|
56
|
-
iconBg="bg-rose-500/10"
|
|
57
|
-
delay={200}
|
|
58
|
-
/>
|
|
59
|
-
</WidgetWrapper>
|
|
60
|
-
);
|
|
61
|
-
}
|
|
@@ -1,156 +0,0 @@
|
|
|
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 { Cell, Pie, PieChart, ResponsiveContainer, Tooltip } from 'recharts';
|
|
14
|
-
import { WidgetWrapper } from '../widget-wrapper';
|
|
15
|
-
|
|
16
|
-
function CustomTooltip({
|
|
17
|
-
active,
|
|
18
|
-
payload,
|
|
19
|
-
}: {
|
|
20
|
-
active?: boolean;
|
|
21
|
-
payload?: Array<{ name: string; value: number; payload: { color: string } }>;
|
|
22
|
-
}) {
|
|
23
|
-
const t = useTranslations('core.Dashboard');
|
|
24
|
-
if (!active || !payload?.length) return null;
|
|
25
|
-
|
|
26
|
-
const entry = payload[0];
|
|
27
|
-
return (
|
|
28
|
-
<div className="rounded-lg border bg-card px-3 py-2 shadow-xl">
|
|
29
|
-
<p className="text-xs text-muted-foreground">
|
|
30
|
-
<span
|
|
31
|
-
className="mr-1.5 inline-block h-2 w-2 rounded-full"
|
|
32
|
-
style={{ backgroundColor: entry?.payload.color }}
|
|
33
|
-
/>
|
|
34
|
-
{entry?.name}: {entry?.value} {t('permissionsLowercase')}
|
|
35
|
-
</p>
|
|
36
|
-
</div>
|
|
37
|
-
);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
interface PermissionsChartProps {
|
|
41
|
-
widget?: {
|
|
42
|
-
name?: string;
|
|
43
|
-
} | null;
|
|
44
|
-
onRemove?: () => void;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
interface PermissionDistributionItem {
|
|
48
|
-
name: string;
|
|
49
|
-
value: number;
|
|
50
|
-
color: string;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
interface UserStatsData {
|
|
54
|
-
charts?: {
|
|
55
|
-
permissionDistribution?: PermissionDistributionItem[];
|
|
56
|
-
};
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
export default function PermissionsChart({
|
|
60
|
-
widget,
|
|
61
|
-
onRemove,
|
|
62
|
-
}: PermissionsChartProps) {
|
|
63
|
-
const t = useTranslations('core.Dashboard');
|
|
64
|
-
|
|
65
|
-
const {
|
|
66
|
-
data: statsData,
|
|
67
|
-
isLoading,
|
|
68
|
-
isAccessDenied,
|
|
69
|
-
isError,
|
|
70
|
-
} = useWidgetData<UserStatsData>({
|
|
71
|
-
endpoint: '/dashboard-core/stats/overview/users',
|
|
72
|
-
queryKey: 'dashboard-stats-users',
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
const data = statsData?.charts?.permissionDistribution || [];
|
|
76
|
-
const total = data.reduce((sum, item) => sum + item.value, 0);
|
|
77
|
-
|
|
78
|
-
return (
|
|
79
|
-
<WidgetWrapper
|
|
80
|
-
isLoading={isLoading}
|
|
81
|
-
isAccessDenied={isAccessDenied}
|
|
82
|
-
isError={isError}
|
|
83
|
-
widgetName={widget?.name || t('permissionsDistributionTitle')}
|
|
84
|
-
onRemove={onRemove}
|
|
85
|
-
>
|
|
86
|
-
<Card className="h-full flex flex-col group">
|
|
87
|
-
<div
|
|
88
|
-
className="drag-handle absolute top-3 left-4 z-10"
|
|
89
|
-
style={{ cursor: 'grab' }}
|
|
90
|
-
>
|
|
91
|
-
<IconGripVertical className="text-muted-foreground/50 size-4 shrink-0" />
|
|
92
|
-
</div>
|
|
93
|
-
<CardHeader className="pb-2 pt-4 pl-10">
|
|
94
|
-
<CardTitle className="text-base font-semibold">
|
|
95
|
-
{t('permissionsDistributionTitle')}
|
|
96
|
-
</CardTitle>
|
|
97
|
-
<CardDescription>
|
|
98
|
-
{t('permissionsDistributionDescription')}
|
|
99
|
-
</CardDescription>
|
|
100
|
-
</CardHeader>
|
|
101
|
-
<CardContent className="flex-1 pt-0">
|
|
102
|
-
<div className="flex h-full items-center justify-between gap-4 overflow-hidden">
|
|
103
|
-
<div className="relative h-[280px] w-[280px] shrink-0">
|
|
104
|
-
<ResponsiveContainer width="100%" height="100%">
|
|
105
|
-
<PieChart>
|
|
106
|
-
<Pie
|
|
107
|
-
data={data}
|
|
108
|
-
cx="50%"
|
|
109
|
-
cy="50%"
|
|
110
|
-
innerRadius={70}
|
|
111
|
-
outerRadius={110}
|
|
112
|
-
paddingAngle={4}
|
|
113
|
-
dataKey="value"
|
|
114
|
-
animationDuration={1200}
|
|
115
|
-
strokeWidth={0}
|
|
116
|
-
>
|
|
117
|
-
{data.map((entry, index: number) => (
|
|
118
|
-
<Cell key={`cell-${index}`} fill={entry.color} />
|
|
119
|
-
))}
|
|
120
|
-
</Pie>
|
|
121
|
-
<Tooltip content={<CustomTooltip />} />
|
|
122
|
-
</PieChart>
|
|
123
|
-
</ResponsiveContainer>
|
|
124
|
-
<div className="absolute inset-0 flex flex-col items-center justify-center">
|
|
125
|
-
<span className="text-2xl font-bold text-foreground">
|
|
126
|
-
{total}
|
|
127
|
-
</span>
|
|
128
|
-
<span className="text-[10px] text-muted-foreground">
|
|
129
|
-
{t('total')}
|
|
130
|
-
</span>
|
|
131
|
-
</div>
|
|
132
|
-
</div>
|
|
133
|
-
<div className="grid max-h-[280px] min-w-0 flex-1 grid-cols-2 content-start gap-x-4 gap-y-2 overflow-y-auto pr-2">
|
|
134
|
-
{data.map((item) => (
|
|
135
|
-
<div key={item.name} className="flex min-w-0 items-start gap-2.5">
|
|
136
|
-
<div
|
|
137
|
-
className="mt-1 h-2.5 w-2.5 shrink-0 rounded-sm"
|
|
138
|
-
style={{ backgroundColor: item.color }}
|
|
139
|
-
/>
|
|
140
|
-
<div className="flex min-w-0 flex-col">
|
|
141
|
-
<span className="break-words text-xs font-medium leading-tight text-foreground">
|
|
142
|
-
{item.name}
|
|
143
|
-
</span>
|
|
144
|
-
<span className="text-[11px] text-muted-foreground">
|
|
145
|
-
{item.value} ({total ? Math.round((item.value / total) * 100) : 0}%)
|
|
146
|
-
</span>
|
|
147
|
-
</div>
|
|
148
|
-
</div>
|
|
149
|
-
))}
|
|
150
|
-
</div>
|
|
151
|
-
</div>
|
|
152
|
-
</CardContent>
|
|
153
|
-
</Card>
|
|
154
|
-
</WidgetWrapper>
|
|
155
|
-
);
|
|
156
|
-
}
|
|
@@ -1,186 +0,0 @@
|
|
|
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
|
-
}
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
import { useWidgetData } from '@/hooks/use-widget-data';
|
|
2
|
-
import { Route } from 'lucide-react';
|
|
3
|
-
import { useTranslations } from 'next-intl';
|
|
4
|
-
import StatCard from '../stats';
|
|
5
|
-
import { WidgetWrapper } from '../widget-wrapper';
|
|
6
|
-
|
|
7
|
-
interface RoutesCardProps {
|
|
8
|
-
widget?: any;
|
|
9
|
-
onRemove?: () => void;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
interface SystemStatsData {
|
|
13
|
-
cards?: {
|
|
14
|
-
routes?: {
|
|
15
|
-
value: number;
|
|
16
|
-
change: number | null;
|
|
17
|
-
};
|
|
18
|
-
};
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export default function RoutesCard({ widget, onRemove }: RoutesCardProps) {
|
|
22
|
-
const t = useTranslations('core.Dashboard');
|
|
23
|
-
|
|
24
|
-
const { data, isLoading, isAccessDenied, isError } =
|
|
25
|
-
useWidgetData<SystemStatsData>({
|
|
26
|
-
endpoint: '/dashboard-core/stats/overview/system',
|
|
27
|
-
queryKey: 'dashboard-stats-system',
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
const value = data?.cards?.routes?.value?.toLocaleString('pt-BR') || '0';
|
|
31
|
-
const change = data?.cards?.routes?.change;
|
|
32
|
-
const changeType =
|
|
33
|
-
change !== null && change !== undefined && change >= 0 ? 'up' : 'down';
|
|
34
|
-
|
|
35
|
-
return (
|
|
36
|
-
<WidgetWrapper
|
|
37
|
-
isLoading={isLoading}
|
|
38
|
-
isAccessDenied={isAccessDenied}
|
|
39
|
-
isError={isError}
|
|
40
|
-
widgetName={widget?.name || t('routes')}
|
|
41
|
-
onRemove={onRemove}
|
|
42
|
-
>
|
|
43
|
-
<StatCard
|
|
44
|
-
title={t('routes')}
|
|
45
|
-
value={value}
|
|
46
|
-
change={
|
|
47
|
-
change !== null && change !== undefined
|
|
48
|
-
? `${change > 0 ? '+' : ''}${change}%`
|
|
49
|
-
: undefined
|
|
50
|
-
}
|
|
51
|
-
changeType={changeType}
|
|
52
|
-
icon={<Route className="h-6 w-6 text-orange-500" />}
|
|
53
|
-
iconBg="bg-orange-500/10"
|
|
54
|
-
delay={50}
|
|
55
|
-
/>
|
|
56
|
-
</WidgetWrapper>
|
|
57
|
-
);
|
|
58
|
-
}
|
package/hedhog/frontend/app/dashboard/components/widgets/core.session-activity-chart.tsx.ejs
DELETED
|
@@ -1,183 +0,0 @@
|
|
|
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
|
-
CartesianGrid,
|
|
15
|
-
Line,
|
|
16
|
-
LineChart,
|
|
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 === 'active' ? '#10b981' : '#f59e0b',
|
|
46
|
-
}}
|
|
47
|
-
/>
|
|
48
|
-
{entry.dataKey === 'active' ? t('active') : t('expired')}:{' '}
|
|
49
|
-
{entry.value}
|
|
50
|
-
</p>
|
|
51
|
-
))}
|
|
52
|
-
</div>
|
|
53
|
-
);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
interface SessionActivityChartProps {
|
|
57
|
-
widget?: any;
|
|
58
|
-
onRemove?: () => void;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
interface UserStatsData {
|
|
62
|
-
charts?: {
|
|
63
|
-
sessionActivity?: Array<{
|
|
64
|
-
hour: string;
|
|
65
|
-
active: number;
|
|
66
|
-
expired: number;
|
|
67
|
-
}>;
|
|
68
|
-
};
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
export default function SessionActivityChart({
|
|
72
|
-
widget,
|
|
73
|
-
onRemove,
|
|
74
|
-
}: SessionActivityChartProps) {
|
|
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?.sessionActivity || [];
|
|
88
|
-
|
|
89
|
-
return (
|
|
90
|
-
<WidgetWrapper
|
|
91
|
-
isLoading={isLoading}
|
|
92
|
-
isAccessDenied={isAccessDenied}
|
|
93
|
-
isError={isError}
|
|
94
|
-
widgetName={widget?.name || t('sessionActivityTitle')}
|
|
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('sessionActivityTitle')}
|
|
108
|
-
</CardTitle>
|
|
109
|
-
<CardDescription>{t('sessionActivityDescription')}</CardDescription>
|
|
110
|
-
</div>
|
|
111
|
-
</CardHeader>
|
|
112
|
-
<CardContent className="flex-1 pt-0">
|
|
113
|
-
<div className="h-[200px] w-full">
|
|
114
|
-
<ResponsiveContainer width="100%" height="100%">
|
|
115
|
-
<LineChart
|
|
116
|
-
data={data}
|
|
117
|
-
margin={{ top: 5, right: 10, left: -20, bottom: 0 }}
|
|
118
|
-
>
|
|
119
|
-
<CartesianGrid
|
|
120
|
-
strokeDasharray="3 3"
|
|
121
|
-
stroke="currentColor"
|
|
122
|
-
opacity={0.1}
|
|
123
|
-
/>
|
|
124
|
-
<XAxis
|
|
125
|
-
dataKey="hour"
|
|
126
|
-
tick={{ fill: 'hsl(var(--muted-foreground))', fontSize: 11 }}
|
|
127
|
-
axisLine={false}
|
|
128
|
-
tickLine={false}
|
|
129
|
-
/>
|
|
130
|
-
<YAxis
|
|
131
|
-
tick={{ fill: 'hsl(var(--muted-foreground))', fontSize: 11 }}
|
|
132
|
-
axisLine={false}
|
|
133
|
-
tickLine={false}
|
|
134
|
-
/>
|
|
135
|
-
<Tooltip content={<CustomTooltip />} />
|
|
136
|
-
<Line
|
|
137
|
-
type="monotone"
|
|
138
|
-
dataKey="active"
|
|
139
|
-
stroke="#10b981"
|
|
140
|
-
strokeWidth={3}
|
|
141
|
-
dot={false}
|
|
142
|
-
activeDot={{ r: 5, fill: '#10b981' }}
|
|
143
|
-
animationDuration={1500}
|
|
144
|
-
/>
|
|
145
|
-
<Line
|
|
146
|
-
type="monotone"
|
|
147
|
-
dataKey="expired"
|
|
148
|
-
stroke="#f59e0b"
|
|
149
|
-
strokeWidth={2.5}
|
|
150
|
-
dot={false}
|
|
151
|
-
strokeDasharray="5 5"
|
|
152
|
-
activeDot={{ r: 4, fill: '#f59e0b' }}
|
|
153
|
-
animationDuration={1500}
|
|
154
|
-
animationBegin={300}
|
|
155
|
-
/>
|
|
156
|
-
</LineChart>
|
|
157
|
-
</ResponsiveContainer>
|
|
158
|
-
</div>
|
|
159
|
-
<div className="mt-2 flex items-center justify-center gap-6">
|
|
160
|
-
<div className="flex items-center gap-2">
|
|
161
|
-
<div
|
|
162
|
-
className="h-0.5 w-4 rounded-full"
|
|
163
|
-
style={{ backgroundColor: '#10b981' }}
|
|
164
|
-
/>
|
|
165
|
-
<span className="text-xs text-muted-foreground">
|
|
166
|
-
{t('active')}
|
|
167
|
-
</span>
|
|
168
|
-
</div>
|
|
169
|
-
<div className="flex items-center gap-2">
|
|
170
|
-
<div
|
|
171
|
-
className="h-0.5 w-4 rounded-full"
|
|
172
|
-
style={{ backgroundColor: '#f59e0b' }}
|
|
173
|
-
/>
|
|
174
|
-
<span className="text-xs text-muted-foreground">
|
|
175
|
-
{t('expired')}
|
|
176
|
-
</span>
|
|
177
|
-
</div>
|
|
178
|
-
</div>
|
|
179
|
-
</CardContent>
|
|
180
|
-
</Card>
|
|
181
|
-
</WidgetWrapper>
|
|
182
|
-
);
|
|
183
|
-
}
|