@hed-hog/core 0.0.298 → 0.0.299
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/dashboard/dashboard/dashboard.controller.d.ts +3 -0
- package/dist/dashboard/dashboard/dashboard.controller.d.ts.map +1 -1
- package/dist/dashboard/dashboard/dashboard.service.d.ts +3 -0
- package/dist/dashboard/dashboard/dashboard.service.d.ts.map +1 -1
- package/dist/dashboard/dashboard-component/dashboard-component.controller.d.ts +12 -0
- package/dist/dashboard/dashboard-component/dashboard-component.controller.d.ts.map +1 -1
- package/dist/dashboard/dashboard-component/dashboard-component.controller.js +22 -0
- package/dist/dashboard/dashboard-component/dashboard-component.controller.js.map +1 -1
- package/dist/dashboard/dashboard-component/dashboard-component.service.d.ts +15 -0
- package/dist/dashboard/dashboard-component/dashboard-component.service.d.ts.map +1 -1
- package/dist/dashboard/dashboard-component/dashboard-component.service.js +110 -3
- package/dist/dashboard/dashboard-component/dashboard-component.service.js.map +1 -1
- package/dist/dashboard/dashboard-component/dto/create.dto.d.ts +1 -0
- package/dist/dashboard/dashboard-component/dto/create.dto.d.ts.map +1 -1
- package/dist/dashboard/dashboard-component/dto/create.dto.js +5 -0
- package/dist/dashboard/dashboard-component/dto/create.dto.js.map +1 -1
- package/dist/dashboard/dashboard-component/dto/update.dto.d.ts +1 -0
- package/dist/dashboard/dashboard-component/dto/update.dto.d.ts.map +1 -1
- package/dist/dashboard/dashboard-component/dto/update.dto.js +5 -0
- package/dist/dashboard/dashboard-component/dto/update.dto.js.map +1 -1
- package/dist/dashboard/dashboard-component-role/dashboard-component-role.controller.d.ts +1 -0
- package/dist/dashboard/dashboard-component-role/dashboard-component-role.controller.d.ts.map +1 -1
- package/dist/dashboard/dashboard-component-role/dashboard-component-role.service.d.ts +1 -0
- package/dist/dashboard/dashboard-component-role/dashboard-component-role.service.d.ts.map +1 -1
- package/dist/dashboard/dashboard-core/dashboard-core.controller.d.ts +7 -1
- package/dist/dashboard/dashboard-core/dashboard-core.controller.d.ts.map +1 -1
- package/dist/dashboard/dashboard-core/dashboard-core.service.d.ts +7 -1
- package/dist/dashboard/dashboard-core/dashboard-core.service.d.ts.map +1 -1
- package/dist/dashboard/dashboard-core/dashboard-core.service.js +88 -4
- package/dist/dashboard/dashboard-core/dashboard-core.service.js.map +1 -1
- package/dist/dashboard/dashboard-item/dashboard-item.controller.d.ts +1 -0
- package/dist/dashboard/dashboard-item/dashboard-item.controller.d.ts.map +1 -1
- package/dist/dashboard/dashboard-item/dashboard-item.service.d.ts +1 -0
- package/dist/dashboard/dashboard-item/dashboard-item.service.d.ts.map +1 -1
- package/hedhog/data/dashboard_item.yaml +1 -1
- package/hedhog/data/route.yaml +12 -0
- package/hedhog/frontend/app/dashboard/[slug]/dashboard-content.tsx.ejs +141 -24
- package/hedhog/frontend/app/dashboard/[slug]/types.ts.ejs +3 -0
- package/hedhog/frontend/app/dashboard/[slug]/widget-renderer.tsx.ejs +136 -23
- package/hedhog/frontend/app/dashboard/components/add-widget-selector-dialog.tsx.ejs +266 -85
- package/hedhog/frontend/app/dashboard/components/widgets/core..gitkeep.ejs +11 -0
- package/hedhog/frontend/app/dashboard/components/widgets/core.account-security.tsx.ejs +192 -0
- package/hedhog/frontend/app/dashboard/components/widgets/core.user-sessions.tsx.ejs +236 -0
- package/hedhog/frontend/app/dashboard/components/widgets/finance.alerts.tsx.ejs +108 -0
- package/hedhog/frontend/app/dashboard/components/widgets/finance.cash-balance-kpi.tsx.ejs +66 -0
- package/hedhog/frontend/app/dashboard/components/widgets/finance.cash-flow-chart.tsx.ejs +122 -0
- package/hedhog/frontend/app/dashboard/components/widgets/finance.default-kpi.tsx.ejs +63 -0
- package/hedhog/frontend/app/dashboard/components/widgets/finance.payable-30d-kpi.tsx.ejs +73 -0
- package/hedhog/frontend/app/dashboard/components/widgets/finance.receivable-30d-kpi.tsx.ejs +73 -0
- package/hedhog/frontend/app/dashboard/components/widgets/finance.upcoming-payable.tsx.ejs +123 -0
- package/hedhog/frontend/app/dashboard/components/widgets/finance.upcoming-receivable.tsx.ejs +118 -0
- package/hedhog/frontend/messages/en.json +3 -0
- package/hedhog/frontend/messages/pt.json +3 -0
- package/hedhog/frontend/public/dashboard-previews/.gitkeep +12 -0
- package/hedhog/frontend/public/dashboard-previews/account-security.png +0 -0
- package/hedhog/frontend/public/dashboard-previews/active-users-card.png +0 -0
- package/hedhog/frontend/public/dashboard-previews/activity-timeline.png +0 -0
- package/hedhog/frontend/public/dashboard-previews/cash-balance-kpi.png +0 -0
- package/hedhog/frontend/public/dashboard-previews/cash-flow-chart.png +0 -0
- package/hedhog/frontend/public/dashboard-previews/default-kpi.png +0 -0
- package/hedhog/frontend/public/dashboard-previews/email-notifications.png +0 -0
- package/hedhog/frontend/public/dashboard-previews/financial-alerts.png +0 -0
- package/hedhog/frontend/public/dashboard-previews/login-history-chart.png +0 -0
- package/hedhog/frontend/public/dashboard-previews/mail-sent-card.png +0 -0
- package/hedhog/frontend/public/dashboard-previews/mail-sent-chart.png +0 -0
- package/hedhog/frontend/public/dashboard-previews/menus-card.png +0 -0
- package/hedhog/frontend/public/dashboard-previews/payable-30d-kpi.png +0 -0
- package/hedhog/frontend/public/dashboard-previews/permissions-card.png +0 -0
- package/hedhog/frontend/public/dashboard-previews/permissions-chart.png +0 -0
- package/hedhog/frontend/public/dashboard-previews/profile-card.png +0 -0
- package/hedhog/frontend/public/dashboard-previews/receivable-30d-kpi.png +0 -0
- package/hedhog/frontend/public/dashboard-previews/routes-card.png +0 -0
- package/hedhog/frontend/public/dashboard-previews/session-activity-chart.png +0 -0
- package/hedhog/frontend/public/dashboard-previews/sessions-today-card.png +0 -0
- package/hedhog/frontend/public/dashboard-previews/stat-access-level.png +0 -0
- package/hedhog/frontend/public/dashboard-previews/stat-actions-today.png +0 -0
- package/hedhog/frontend/public/dashboard-previews/stat-consecutive-days.png +0 -0
- package/hedhog/frontend/public/dashboard-previews/stat-online-time.png +0 -0
- package/hedhog/frontend/public/dashboard-previews/upcoming-payable.png +0 -0
- package/hedhog/frontend/public/dashboard-previews/upcoming-receivable.png +0 -0
- package/hedhog/frontend/public/dashboard-previews/user-growth-chart.png +0 -0
- package/hedhog/frontend/public/dashboard-previews/user-roles.png +0 -0
- package/hedhog/frontend/{app/dashboard/components/widgets → widgets}/account-security.tsx.ejs +33 -29
- package/hedhog/frontend/widgets/active-users-card.tsx.ejs +58 -0
- package/hedhog/frontend/widgets/activity-timeline.tsx.ejs +223 -0
- package/hedhog/frontend/widgets/email-notifications.tsx.ejs +226 -0
- package/hedhog/frontend/widgets/locale-config.tsx.ejs +168 -0
- package/hedhog/frontend/widgets/login-history-chart.tsx.ejs +115 -0
- package/hedhog/frontend/widgets/mail-config.tsx.ejs +199 -0
- package/hedhog/frontend/widgets/mail-sent-card.tsx.ejs +58 -0
- package/hedhog/frontend/widgets/mail-sent-chart.tsx.ejs +149 -0
- package/hedhog/frontend/widgets/menus-card.tsx.ejs +58 -0
- package/hedhog/frontend/widgets/oauth-config.tsx.ejs +175 -0
- package/hedhog/frontend/widgets/permissions-card.tsx.ejs +61 -0
- package/hedhog/frontend/widgets/permissions-chart.tsx.ejs +156 -0
- package/hedhog/frontend/widgets/profile-card.tsx.ejs +186 -0
- package/hedhog/frontend/widgets/routes-card.tsx.ejs +58 -0
- package/hedhog/frontend/widgets/session-activity-chart.tsx.ejs +183 -0
- package/hedhog/frontend/widgets/sessions-today-card.tsx.ejs +62 -0
- package/hedhog/frontend/widgets/stat-access-level.tsx.ejs +57 -0
- package/hedhog/frontend/widgets/stat-actions-today.tsx.ejs +57 -0
- package/hedhog/frontend/widgets/stat-consecutive-days.tsx.ejs +57 -0
- package/hedhog/frontend/widgets/stat-online-time.tsx.ejs +57 -0
- package/hedhog/frontend/widgets/storage-config.tsx.ejs +196 -0
- package/hedhog/frontend/widgets/theme-config.tsx.ejs +213 -0
- package/hedhog/frontend/widgets/user-growth-chart.tsx.ejs +210 -0
- package/hedhog/frontend/widgets/user-roles.tsx.ejs +132 -0
- package/hedhog/frontend/{app/dashboard/components/widgets → widgets}/user-sessions.tsx.ejs +1 -1
- package/hedhog/table/dashboard_component.yaml +7 -0
- package/package.json +4 -4
- package/src/dashboard/dashboard-component/dashboard-component.controller.ts +36 -12
- package/src/dashboard/dashboard-component/dashboard-component.service.ts +150 -3
- package/src/dashboard/dashboard-component/dto/create.dto.ts +4 -0
- package/src/dashboard/dashboard-component/dto/update.dto.ts +4 -0
- package/src/dashboard/dashboard-core/dashboard-core.service.ts +108 -5
- /package/hedhog/frontend/app/dashboard/components/widgets/{active-users-card.tsx.ejs → core.active-users-card.tsx.ejs} +0 -0
- /package/hedhog/frontend/app/dashboard/components/widgets/{activity-timeline.tsx.ejs → core.activity-timeline.tsx.ejs} +0 -0
- /package/hedhog/frontend/app/dashboard/components/widgets/{email-notifications.tsx.ejs → core.email-notifications.tsx.ejs} +0 -0
- /package/hedhog/frontend/app/dashboard/components/widgets/{locale-config.tsx.ejs → core.locale-config.tsx.ejs} +0 -0
- /package/hedhog/frontend/app/dashboard/components/widgets/{login-history-chart.tsx.ejs → core.login-history-chart.tsx.ejs} +0 -0
- /package/hedhog/frontend/app/dashboard/components/widgets/{mail-config.tsx.ejs → core.mail-config.tsx.ejs} +0 -0
- /package/hedhog/frontend/app/dashboard/components/widgets/{mail-sent-card.tsx.ejs → core.mail-sent-card.tsx.ejs} +0 -0
- /package/hedhog/frontend/app/dashboard/components/widgets/{mail-sent-chart.tsx.ejs → core.mail-sent-chart.tsx.ejs} +0 -0
- /package/hedhog/frontend/app/dashboard/components/widgets/{menus-card.tsx.ejs → core.menus-card.tsx.ejs} +0 -0
- /package/hedhog/frontend/app/dashboard/components/widgets/{oauth-config.tsx.ejs → core.oauth-config.tsx.ejs} +0 -0
- /package/hedhog/frontend/app/dashboard/components/widgets/{permissions-card.tsx.ejs → core.permissions-card.tsx.ejs} +0 -0
- /package/hedhog/frontend/app/dashboard/components/widgets/{permissions-chart.tsx.ejs → core.permissions-chart.tsx.ejs} +0 -0
- /package/hedhog/frontend/app/dashboard/components/widgets/{profile-card.tsx.ejs → core.profile-card.tsx.ejs} +0 -0
- /package/hedhog/frontend/app/dashboard/components/widgets/{routes-card.tsx.ejs → core.routes-card.tsx.ejs} +0 -0
- /package/hedhog/frontend/app/dashboard/components/widgets/{session-activity-chart.tsx.ejs → core.session-activity-chart.tsx.ejs} +0 -0
- /package/hedhog/frontend/app/dashboard/components/widgets/{sessions-today-card.tsx.ejs → core.sessions-today-card.tsx.ejs} +0 -0
- /package/hedhog/frontend/app/dashboard/components/widgets/{stat-access-level.tsx.ejs → core.stat-access-level.tsx.ejs} +0 -0
- /package/hedhog/frontend/app/dashboard/components/widgets/{stat-actions-today.tsx.ejs → core.stat-actions-today.tsx.ejs} +0 -0
- /package/hedhog/frontend/app/dashboard/components/widgets/{stat-consecutive-days.tsx.ejs → core.stat-consecutive-days.tsx.ejs} +0 -0
- /package/hedhog/frontend/app/dashboard/components/widgets/{stat-online-time.tsx.ejs → core.stat-online-time.tsx.ejs} +0 -0
- /package/hedhog/frontend/app/dashboard/components/widgets/{storage-config.tsx.ejs → core.storage-config.tsx.ejs} +0 -0
- /package/hedhog/frontend/app/dashboard/components/widgets/{theme-config.tsx.ejs → core.theme-config.tsx.ejs} +0 -0
- /package/hedhog/frontend/app/dashboard/components/widgets/{user-growth-chart.tsx.ejs → core.user-growth-chart.tsx.ejs} +0 -0
- /package/hedhog/frontend/app/dashboard/components/widgets/{user-roles.tsx.ejs → core.user-roles.tsx.ejs} +0 -0
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { Card, CardContent } from '@/components/ui/card';
|
|
4
|
+
import { useWidgetData } from '@/hooks/use-widget-data';
|
|
5
|
+
import { TrendingUp } from 'lucide-react';
|
|
6
|
+
import { useTranslations } from 'next-intl';
|
|
7
|
+
import { WidgetWrapper } from '../widget-wrapper';
|
|
8
|
+
|
|
9
|
+
interface Receivable30dKpiProps {
|
|
10
|
+
widget?: { name?: string };
|
|
11
|
+
onRemove?: () => void;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
interface FinanceData {
|
|
15
|
+
kpis?: {
|
|
16
|
+
aReceber30dias: number;
|
|
17
|
+
aReceber7dias: number;
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export default function Receivable30dKpi({
|
|
22
|
+
widget,
|
|
23
|
+
onRemove,
|
|
24
|
+
}: Receivable30dKpiProps) {
|
|
25
|
+
const t = useTranslations('finance.DashboardPage');
|
|
26
|
+
|
|
27
|
+
const { data, isLoading, isAccessDenied, isError } =
|
|
28
|
+
useWidgetData<FinanceData>({
|
|
29
|
+
endpoint: '/finance/data',
|
|
30
|
+
queryKey: 'finance-kpi-receivable-30d',
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
const value = data?.kpis?.aReceber30dias ?? 0;
|
|
34
|
+
const sevenDays = data?.kpis?.aReceber7dias ?? 0;
|
|
35
|
+
const formatted = new Intl.NumberFormat('pt-BR', {
|
|
36
|
+
style: 'currency',
|
|
37
|
+
currency: 'BRL',
|
|
38
|
+
}).format(value);
|
|
39
|
+
|
|
40
|
+
return (
|
|
41
|
+
<WidgetWrapper
|
|
42
|
+
isLoading={isLoading}
|
|
43
|
+
isAccessDenied={isAccessDenied}
|
|
44
|
+
isError={isError}
|
|
45
|
+
widgetName={widget?.name || t('kpis.receivable30.title')}
|
|
46
|
+
onRemove={onRemove}
|
|
47
|
+
>
|
|
48
|
+
<Card className="h-full overflow-hidden transition-all duration-300 hover:shadow-md">
|
|
49
|
+
<CardContent className="flex h-full items-center gap-3 p-4">
|
|
50
|
+
<div className="flex h-11 w-11 shrink-0 items-center justify-center rounded-xl bg-green-50">
|
|
51
|
+
<TrendingUp className="h-5 w-5 text-green-600" />
|
|
52
|
+
</div>
|
|
53
|
+
<div className="flex min-w-0 flex-col">
|
|
54
|
+
<span className="text-[10px] font-medium uppercase tracking-wider text-muted-foreground">
|
|
55
|
+
{t('kpis.receivable30.title')}
|
|
56
|
+
</span>
|
|
57
|
+
<span className="truncate text-lg font-bold tracking-tight text-foreground">
|
|
58
|
+
{formatted}
|
|
59
|
+
</span>
|
|
60
|
+
<span className="text-[10px] text-muted-foreground">
|
|
61
|
+
{t('kpis.receivable30.sevenDays', {
|
|
62
|
+
value: new Intl.NumberFormat('pt-BR', {
|
|
63
|
+
style: 'currency',
|
|
64
|
+
currency: 'BRL',
|
|
65
|
+
}).format(sevenDays),
|
|
66
|
+
})}
|
|
67
|
+
</span>
|
|
68
|
+
</div>
|
|
69
|
+
</CardContent>
|
|
70
|
+
</Card>
|
|
71
|
+
</WidgetWrapper>
|
|
72
|
+
);
|
|
73
|
+
}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
Card,
|
|
5
|
+
CardContent,
|
|
6
|
+
CardDescription,
|
|
7
|
+
CardHeader,
|
|
8
|
+
CardTitle,
|
|
9
|
+
} from '@/components/ui/card';
|
|
10
|
+
import { Money } from '@/components/ui/money';
|
|
11
|
+
import { StatusBadge } from '@/components/ui/status-badge';
|
|
12
|
+
import { useWidgetData } from '@/hooks/use-widget-data';
|
|
13
|
+
import { ArrowDownRight } from 'lucide-react';
|
|
14
|
+
import { useTranslations } from 'next-intl';
|
|
15
|
+
import Link from 'next/link';
|
|
16
|
+
import { WidgetWrapper } from '../widget-wrapper';
|
|
17
|
+
|
|
18
|
+
interface FinanceData {
|
|
19
|
+
titulosPagar?: Array<{
|
|
20
|
+
id: string;
|
|
21
|
+
status: string;
|
|
22
|
+
documento: string;
|
|
23
|
+
fornecedorId?: string;
|
|
24
|
+
parcelas: Array<{
|
|
25
|
+
status: string;
|
|
26
|
+
vencimento: string;
|
|
27
|
+
valor: number;
|
|
28
|
+
}>;
|
|
29
|
+
}>;
|
|
30
|
+
pessoas?: Array<{
|
|
31
|
+
id: string;
|
|
32
|
+
nome: string;
|
|
33
|
+
}>;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
interface UpcomingPayableProps {
|
|
37
|
+
widget?: { name?: string };
|
|
38
|
+
onRemove?: () => void;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function formatDate(value: string) {
|
|
42
|
+
return new Date(value).toLocaleDateString('pt-BR');
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export default function UpcomingPayable({
|
|
46
|
+
widget,
|
|
47
|
+
onRemove,
|
|
48
|
+
}: UpcomingPayableProps) {
|
|
49
|
+
const t = useTranslations('finance.DashboardPage');
|
|
50
|
+
|
|
51
|
+
const { data, isLoading, isAccessDenied, isError } =
|
|
52
|
+
useWidgetData<FinanceData>({
|
|
53
|
+
endpoint: '/finance/data',
|
|
54
|
+
queryKey: 'finance-upcoming-payable',
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
const getPersonById = (id?: string) =>
|
|
58
|
+
(data?.pessoas || []).find((p) => p.id === id);
|
|
59
|
+
|
|
60
|
+
const approvedPayables = (data?.titulosPagar || []).filter(
|
|
61
|
+
(titulo) => titulo.status !== 'rascunho' && titulo.status !== 'cancelado'
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
const rows = approvedPayables
|
|
65
|
+
.flatMap((titulo) =>
|
|
66
|
+
titulo.parcelas
|
|
67
|
+
.filter((p) => p.status === 'aberto' || p.status === 'vencido')
|
|
68
|
+
.map((parcela) => ({
|
|
69
|
+
tituloId: titulo.id,
|
|
70
|
+
documento: titulo.documento,
|
|
71
|
+
person: getPersonById(titulo.fornecedorId)?.nome || '',
|
|
72
|
+
dueDate: parcela.vencimento,
|
|
73
|
+
value: parcela.valor,
|
|
74
|
+
status: parcela.status,
|
|
75
|
+
}))
|
|
76
|
+
)
|
|
77
|
+
.slice(0, 3);
|
|
78
|
+
|
|
79
|
+
return (
|
|
80
|
+
<WidgetWrapper
|
|
81
|
+
isLoading={isLoading}
|
|
82
|
+
isAccessDenied={isAccessDenied}
|
|
83
|
+
isError={isError}
|
|
84
|
+
widgetName={widget?.name || t('upcoming.payable')}
|
|
85
|
+
onRemove={onRemove}
|
|
86
|
+
>
|
|
87
|
+
<Card className="h-full">
|
|
88
|
+
<CardHeader>
|
|
89
|
+
<CardTitle className="flex items-center gap-2 text-base">
|
|
90
|
+
<ArrowDownRight className="h-4 w-4 text-red-500" />
|
|
91
|
+
{t('upcoming.payable')}
|
|
92
|
+
</CardTitle>
|
|
93
|
+
<CardDescription>{t('upcoming.nextDueDates')}</CardDescription>
|
|
94
|
+
</CardHeader>
|
|
95
|
+
<CardContent>
|
|
96
|
+
<div className="space-y-4">
|
|
97
|
+
{rows.map((item, index) => (
|
|
98
|
+
<Link
|
|
99
|
+
key={index}
|
|
100
|
+
href={`/finance/accounts-payable/installments/${item.tituloId}`}
|
|
101
|
+
className="-m-2 flex cursor-pointer items-center justify-between rounded-md p-2 transition-colors hover:bg-muted/50 active:bg-muted"
|
|
102
|
+
>
|
|
103
|
+
<div className="space-y-1">
|
|
104
|
+
<p className="text-sm font-medium">{item.documento}</p>
|
|
105
|
+
<p className="text-xs text-muted-foreground">{item.person}</p>
|
|
106
|
+
</div>
|
|
107
|
+
<div className="text-right">
|
|
108
|
+
<p className="text-sm font-medium">
|
|
109
|
+
<Money value={item.value} />
|
|
110
|
+
</p>
|
|
111
|
+
<p className="text-xs text-muted-foreground">
|
|
112
|
+
{formatDate(item.dueDate)}
|
|
113
|
+
</p>
|
|
114
|
+
</div>
|
|
115
|
+
<StatusBadge status={item.status as any} />
|
|
116
|
+
</Link>
|
|
117
|
+
))}
|
|
118
|
+
</div>
|
|
119
|
+
</CardContent>
|
|
120
|
+
</Card>
|
|
121
|
+
</WidgetWrapper>
|
|
122
|
+
);
|
|
123
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
Card,
|
|
5
|
+
CardContent,
|
|
6
|
+
CardDescription,
|
|
7
|
+
CardHeader,
|
|
8
|
+
CardTitle,
|
|
9
|
+
} from '@/components/ui/card';
|
|
10
|
+
import { Money } from '@/components/ui/money';
|
|
11
|
+
import { StatusBadge } from '@/components/ui/status-badge';
|
|
12
|
+
import { useWidgetData } from '@/hooks/use-widget-data';
|
|
13
|
+
import { ArrowUpRight } from 'lucide-react';
|
|
14
|
+
import { useTranslations } from 'next-intl';
|
|
15
|
+
import Link from 'next/link';
|
|
16
|
+
import { WidgetWrapper } from '../widget-wrapper';
|
|
17
|
+
|
|
18
|
+
interface FinanceData {
|
|
19
|
+
titulosReceber?: Array<{
|
|
20
|
+
id: string;
|
|
21
|
+
documento: string;
|
|
22
|
+
clienteId?: string;
|
|
23
|
+
parcelas: Array<{
|
|
24
|
+
status: string;
|
|
25
|
+
vencimento: string;
|
|
26
|
+
valor: number;
|
|
27
|
+
}>;
|
|
28
|
+
}>;
|
|
29
|
+
pessoas?: Array<{
|
|
30
|
+
id: string;
|
|
31
|
+
nome: string;
|
|
32
|
+
}>;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
interface UpcomingReceivableProps {
|
|
36
|
+
widget?: { name?: string };
|
|
37
|
+
onRemove?: () => void;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function formatDate(value: string) {
|
|
41
|
+
return new Date(value).toLocaleDateString('pt-BR');
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export default function UpcomingReceivable({
|
|
45
|
+
widget,
|
|
46
|
+
onRemove,
|
|
47
|
+
}: UpcomingReceivableProps) {
|
|
48
|
+
const t = useTranslations('finance.DashboardPage');
|
|
49
|
+
|
|
50
|
+
const { data, isLoading, isAccessDenied, isError } =
|
|
51
|
+
useWidgetData<FinanceData>({
|
|
52
|
+
endpoint: '/finance/data',
|
|
53
|
+
queryKey: 'finance-upcoming-receivable',
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
const getPersonById = (id?: string) =>
|
|
57
|
+
(data?.pessoas || []).find((p) => p.id === id);
|
|
58
|
+
|
|
59
|
+
const rows = (data?.titulosReceber || [])
|
|
60
|
+
.flatMap((titulo) =>
|
|
61
|
+
titulo.parcelas
|
|
62
|
+
.filter((p) => p.status === 'aberto' || p.status === 'vencido')
|
|
63
|
+
.map((parcela) => ({
|
|
64
|
+
tituloId: titulo.id,
|
|
65
|
+
documento: titulo.documento,
|
|
66
|
+
person: getPersonById(titulo.clienteId)?.nome || '',
|
|
67
|
+
dueDate: parcela.vencimento,
|
|
68
|
+
value: parcela.valor,
|
|
69
|
+
status: parcela.status,
|
|
70
|
+
}))
|
|
71
|
+
)
|
|
72
|
+
.slice(0, 3);
|
|
73
|
+
|
|
74
|
+
return (
|
|
75
|
+
<WidgetWrapper
|
|
76
|
+
isLoading={isLoading}
|
|
77
|
+
isAccessDenied={isAccessDenied}
|
|
78
|
+
isError={isError}
|
|
79
|
+
widgetName={widget?.name || t('upcoming.receivable')}
|
|
80
|
+
onRemove={onRemove}
|
|
81
|
+
>
|
|
82
|
+
<Card className="h-full">
|
|
83
|
+
<CardHeader>
|
|
84
|
+
<CardTitle className="flex items-center gap-2 text-base">
|
|
85
|
+
<ArrowUpRight className="h-4 w-4 text-green-500" />
|
|
86
|
+
{t('upcoming.receivable')}
|
|
87
|
+
</CardTitle>
|
|
88
|
+
<CardDescription>{t('upcoming.nextDueDates')}</CardDescription>
|
|
89
|
+
</CardHeader>
|
|
90
|
+
<CardContent>
|
|
91
|
+
<div className="space-y-4">
|
|
92
|
+
{rows.map((item, index) => (
|
|
93
|
+
<Link
|
|
94
|
+
key={index}
|
|
95
|
+
href={`/finance/accounts-receivable/installments/${item.tituloId}`}
|
|
96
|
+
className="-m-2 flex cursor-pointer items-center justify-between rounded-md p-2 transition-colors hover:bg-muted/50 active:bg-muted"
|
|
97
|
+
>
|
|
98
|
+
<div className="space-y-1">
|
|
99
|
+
<p className="text-sm font-medium">{item.documento}</p>
|
|
100
|
+
<p className="text-xs text-muted-foreground">{item.person}</p>
|
|
101
|
+
</div>
|
|
102
|
+
<div className="text-right">
|
|
103
|
+
<p className="text-sm font-medium">
|
|
104
|
+
<Money value={item.value} />
|
|
105
|
+
</p>
|
|
106
|
+
<p className="text-xs text-muted-foreground">
|
|
107
|
+
{formatDate(item.dueDate)}
|
|
108
|
+
</p>
|
|
109
|
+
</div>
|
|
110
|
+
<StatusBadge status={item.status as any} />
|
|
111
|
+
</Link>
|
|
112
|
+
))}
|
|
113
|
+
</div>
|
|
114
|
+
</CardContent>
|
|
115
|
+
</Card>
|
|
116
|
+
</WidgetWrapper>
|
|
117
|
+
);
|
|
118
|
+
}
|
|
@@ -904,10 +904,13 @@
|
|
|
904
904
|
"AddWidgetDialog": {
|
|
905
905
|
"title": "Select Dashboard Components",
|
|
906
906
|
"description": "Choose the components you want to display on your dashboard.",
|
|
907
|
+
"selectedOfTotal": "{selected} selected of {total}",
|
|
907
908
|
"noComponentsAvailable": "No components available at the moment.",
|
|
908
909
|
"dimensions": "Dimensions",
|
|
909
910
|
"resizable": "Resizable",
|
|
910
911
|
"fixedSize": "Fixed size",
|
|
912
|
+
"select": "Select",
|
|
913
|
+
"selected": "Selected",
|
|
911
914
|
"cancel": "Cancel",
|
|
912
915
|
"add": "Add"
|
|
913
916
|
},
|
|
@@ -907,10 +907,13 @@
|
|
|
907
907
|
"AddWidgetDialog": {
|
|
908
908
|
"title": "Selecionar Componentes do Dashboard",
|
|
909
909
|
"description": "Escolha os componentes que deseja exibir no seu dashboard.",
|
|
910
|
+
"selectedOfTotal": "{selected} selecionados de {total}",
|
|
910
911
|
"noComponentsAvailable": "Nenhum componente disponível no momento.",
|
|
911
912
|
"dimensions": "Dimensões",
|
|
912
913
|
"resizable": "Redimensionável",
|
|
913
914
|
"fixedSize": "Tamanho fixo",
|
|
915
|
+
"select": "Selecionar",
|
|
916
|
+
"selected": "Selecionado",
|
|
914
917
|
"cancel": "Cancelar",
|
|
915
918
|
"add": "Adicionar"
|
|
916
919
|
},
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# Dashboard Widget Previews
|
|
2
|
+
#
|
|
3
|
+
# Place one .webp file per dashboard component, named exactly by the component slug.
|
|
4
|
+
# Example: active-users-card.webp, user-sessions.webp
|
|
5
|
+
#
|
|
6
|
+
# These files are copied by `hedhog add` / `hedhog dev apply` to:
|
|
7
|
+
# apps/admin/public/libraries/core/dashboard-previews/<slug>.webp
|
|
8
|
+
#
|
|
9
|
+
# The admin selector loads them as:
|
|
10
|
+
# /libraries/core/dashboard-previews/<slug>.webp
|
|
11
|
+
#
|
|
12
|
+
# If a file is missing, the selector card falls back gracefully (image hidden).
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/hedhog/frontend/{app/dashboard/components/widgets → widgets}/account-security.tsx.ejs
RENAMED
|
@@ -1,6 +1,4 @@
|
|
|
1
1
|
'use client';
|
|
2
|
-
|
|
3
|
-
import { Button } from '@/components/ui/button';
|
|
4
2
|
import {
|
|
5
3
|
Card,
|
|
6
4
|
CardContent,
|
|
@@ -34,6 +32,13 @@ const ICON_MAP: Record<string, React.ElementType> = {
|
|
|
34
32
|
sessions: AlertTriangle,
|
|
35
33
|
};
|
|
36
34
|
|
|
35
|
+
const CHECK_ROUTE_MAP: Record<string, string> = {
|
|
36
|
+
password: '/core/account/password',
|
|
37
|
+
'2fa': '/core/account/2fa',
|
|
38
|
+
email: '/core/account/email',
|
|
39
|
+
sessions: '/core/account/sessions',
|
|
40
|
+
};
|
|
41
|
+
|
|
37
42
|
function AccountSecurityContent({ data }: { data: AccountSecurityData }) {
|
|
38
43
|
const t = useTranslations('core.DashboardPage.accountSecurity');
|
|
39
44
|
const router = useRouter();
|
|
@@ -60,60 +65,66 @@ function AccountSecurityContent({ data }: { data: AccountSecurityData }) {
|
|
|
60
65
|
|
|
61
66
|
return (
|
|
62
67
|
<Card className="flex h-full min-h-0 flex-col overflow-hidden">
|
|
63
|
-
<CardHeader className="shrink-0 pb-
|
|
68
|
+
<CardHeader className="shrink-0 px-4 pb-2 pt-4 sm:px-5">
|
|
64
69
|
<div className="flex items-center gap-2">
|
|
65
70
|
<ShieldCheck className="h-5 w-5 text-emerald-600 dark:text-emerald-400" />
|
|
66
71
|
<div>
|
|
67
72
|
<CardTitle className="text-base font-semibold">
|
|
68
73
|
{t('title')}
|
|
69
74
|
</CardTitle>
|
|
70
|
-
<CardDescription>
|
|
75
|
+
<CardDescription className="text-xs sm:text-sm">
|
|
76
|
+
{t('description')}
|
|
77
|
+
</CardDescription>
|
|
71
78
|
</div>
|
|
72
79
|
</div>
|
|
73
80
|
</CardHeader>
|
|
74
|
-
<CardContent className="flex min-h-0 flex-1 flex-col overflow-auto pt-0">
|
|
75
|
-
<div className="mb-
|
|
81
|
+
<CardContent className="flex min-h-0 flex-1 flex-col overflow-auto px-4 pb-3 pt-0 sm:px-5 sm:pb-4">
|
|
82
|
+
<div className="mb-2 flex flex-col items-center gap-1.5 rounded-xl bg-muted/50 p-2 sm:mb-3 sm:gap-2 sm:p-3">
|
|
76
83
|
<div className="flex items-baseline gap-1">
|
|
77
84
|
<span
|
|
78
|
-
className={`text-
|
|
85
|
+
className={`text-2xl font-bold tracking-tight sm:text-3xl ${scoreColor}`}
|
|
79
86
|
>
|
|
80
87
|
{score}
|
|
81
88
|
</span>
|
|
82
|
-
<span className="text-
|
|
89
|
+
<span className="text-xs text-muted-foreground sm:text-sm">
|
|
83
90
|
/100
|
|
84
91
|
</span>
|
|
85
92
|
</div>
|
|
86
93
|
<Progress
|
|
87
94
|
value={score}
|
|
88
|
-
className="h-
|
|
95
|
+
className="h-1.5 w-full max-w-70"
|
|
89
96
|
style={
|
|
90
97
|
{
|
|
91
98
|
'--progress-foreground': progressColor,
|
|
92
99
|
} as any
|
|
93
100
|
}
|
|
94
101
|
/>
|
|
95
|
-
<p className="text-[
|
|
102
|
+
<p className="text-[10px] text-muted-foreground sm:text-[11px]">
|
|
96
103
|
{score >= 80 ? t('wellProtected') : t('recommendProtections')}
|
|
97
104
|
</p>
|
|
98
105
|
</div>
|
|
99
106
|
|
|
100
|
-
<div className="flex flex-col gap-1
|
|
107
|
+
<div className="flex flex-col gap-1">
|
|
101
108
|
{data.checks.map((item) => {
|
|
102
109
|
const Icon = ICON_MAP[item.id] ?? ShieldCheck;
|
|
103
110
|
return (
|
|
104
|
-
<
|
|
111
|
+
<button
|
|
112
|
+
type="button"
|
|
105
113
|
key={item.id}
|
|
106
|
-
|
|
114
|
+
onClick={() =>
|
|
115
|
+
router.push(CHECK_ROUTE_MAP[item.id] ?? '/core/account')
|
|
116
|
+
}
|
|
117
|
+
className="group flex w-full cursor-pointer flex-wrap items-start gap-2 rounded-lg p-2 text-left transition-colors hover:bg-muted/50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring sm:items-center sm:gap-2.5 sm:p-2.5"
|
|
107
118
|
>
|
|
108
119
|
<div
|
|
109
|
-
className={`flex h-
|
|
120
|
+
className={`flex h-7 w-7 shrink-0 items-center justify-center rounded-lg sm:h-8 sm:w-8 ${
|
|
110
121
|
item.enabled
|
|
111
122
|
? 'bg-emerald-50 dark:bg-emerald-950/40'
|
|
112
123
|
: 'bg-muted'
|
|
113
124
|
}`}
|
|
114
125
|
>
|
|
115
126
|
<Icon
|
|
116
|
-
className={`h-3.5 w-3.5
|
|
127
|
+
className={`h-3.5 w-3.5 ${
|
|
117
128
|
item.enabled
|
|
118
129
|
? 'text-emerald-600 dark:text-emerald-400'
|
|
119
130
|
: 'text-muted-foreground'
|
|
@@ -122,7 +133,7 @@ function AccountSecurityContent({ data }: { data: AccountSecurityData }) {
|
|
|
122
133
|
</div>
|
|
123
134
|
<div className="flex min-w-0 flex-1 flex-col">
|
|
124
135
|
<div className="flex flex-wrap items-center gap-2">
|
|
125
|
-
<span className="wrap-break-word text-[
|
|
136
|
+
<span className="wrap-break-word text-[12px] font-medium text-foreground sm:text-[13px]">
|
|
126
137
|
{t(`labels.${item.labelKey}` as any) || item.labelKey}
|
|
127
138
|
</span>
|
|
128
139
|
{item.enabled ? (
|
|
@@ -131,23 +142,16 @@ function AccountSecurityContent({ data }: { data: AccountSecurityData }) {
|
|
|
131
142
|
<AlertTriangle className="h-3.5 w-3.5 text-amber-500" />
|
|
132
143
|
)}
|
|
133
144
|
</div>
|
|
134
|
-
<span className="text-[
|
|
145
|
+
<span className="text-[10px] text-muted-foreground sm:text-[11px]">
|
|
135
146
|
{t(`descriptions.${item.descriptionKey}` as any) ||
|
|
136
147
|
item.descriptionKey}
|
|
137
148
|
</span>
|
|
138
149
|
</div>
|
|
139
|
-
|
|
140
|
-
<
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
className="mt-1 w-full shrink-0 gap-1 text-xs sm:mt-0 sm:w-auto"
|
|
145
|
-
>
|
|
146
|
-
{t('activate')}
|
|
147
|
-
<ChevronRight className="h-3 w-3" />
|
|
148
|
-
</Button>
|
|
149
|
-
)}
|
|
150
|
-
</div>
|
|
150
|
+
<div className="mt-0.5 flex w-full shrink-0 items-center justify-end gap-1 text-[11px] font-medium text-foreground sm:mt-0 sm:w-auto sm:text-xs">
|
|
151
|
+
{!item.enabled && <span>{t('activate')}</span>}
|
|
152
|
+
<ChevronRight className="h-3 w-3" />
|
|
153
|
+
</div>
|
|
154
|
+
</button>
|
|
151
155
|
);
|
|
152
156
|
})}
|
|
153
157
|
</div>
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { useWidgetData } from '@/hooks/use-widget-data';
|
|
2
|
+
import { Users } from 'lucide-react';
|
|
3
|
+
import { useTranslations } from 'next-intl';
|
|
4
|
+
import StatCard from '../stats';
|
|
5
|
+
import { WidgetWrapper } from '../widget-wrapper';
|
|
6
|
+
|
|
7
|
+
interface ActiveUsersProps {
|
|
8
|
+
widget?: any;
|
|
9
|
+
onRemove?: () => void;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
interface UserStatsData {
|
|
13
|
+
cards?: {
|
|
14
|
+
activeUsers?: {
|
|
15
|
+
value: number;
|
|
16
|
+
change: number | null;
|
|
17
|
+
};
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export default function ActiveUsers({ widget, onRemove }: ActiveUsersProps) {
|
|
22
|
+
const t = useTranslations('core.Dashboard');
|
|
23
|
+
|
|
24
|
+
const { data, isLoading, isAccessDenied, isError } =
|
|
25
|
+
useWidgetData<UserStatsData>({
|
|
26
|
+
endpoint: '/dashboard-core/stats/overview/users',
|
|
27
|
+
queryKey: 'dashboard-stats-users',
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
const value = data?.cards?.activeUsers?.value?.toLocaleString('pt-BR') || '0';
|
|
31
|
+
const change = data?.cards?.activeUsers?.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('activeUsers')}
|
|
41
|
+
onRemove={onRemove}
|
|
42
|
+
>
|
|
43
|
+
<StatCard
|
|
44
|
+
title={t('activeUsers')}
|
|
45
|
+
value={value}
|
|
46
|
+
change={
|
|
47
|
+
change !== null && change !== undefined
|
|
48
|
+
? `${change > 0 ? '+' : ''}${change}%`
|
|
49
|
+
: undefined
|
|
50
|
+
}
|
|
51
|
+
changeType={changeType}
|
|
52
|
+
icon={<Users className="h-6 w-6 text-blue-500" />}
|
|
53
|
+
iconBg="bg-blue-500/10"
|
|
54
|
+
delay={50}
|
|
55
|
+
/>
|
|
56
|
+
</WidgetWrapper>
|
|
57
|
+
);
|
|
58
|
+
}
|