@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.
Files changed (139) hide show
  1. package/dist/dashboard/dashboard/dashboard.controller.d.ts +3 -0
  2. package/dist/dashboard/dashboard/dashboard.controller.d.ts.map +1 -1
  3. package/dist/dashboard/dashboard/dashboard.service.d.ts +3 -0
  4. package/dist/dashboard/dashboard/dashboard.service.d.ts.map +1 -1
  5. package/dist/dashboard/dashboard-component/dashboard-component.controller.d.ts +12 -0
  6. package/dist/dashboard/dashboard-component/dashboard-component.controller.d.ts.map +1 -1
  7. package/dist/dashboard/dashboard-component/dashboard-component.controller.js +22 -0
  8. package/dist/dashboard/dashboard-component/dashboard-component.controller.js.map +1 -1
  9. package/dist/dashboard/dashboard-component/dashboard-component.service.d.ts +15 -0
  10. package/dist/dashboard/dashboard-component/dashboard-component.service.d.ts.map +1 -1
  11. package/dist/dashboard/dashboard-component/dashboard-component.service.js +110 -3
  12. package/dist/dashboard/dashboard-component/dashboard-component.service.js.map +1 -1
  13. package/dist/dashboard/dashboard-component/dto/create.dto.d.ts +1 -0
  14. package/dist/dashboard/dashboard-component/dto/create.dto.d.ts.map +1 -1
  15. package/dist/dashboard/dashboard-component/dto/create.dto.js +5 -0
  16. package/dist/dashboard/dashboard-component/dto/create.dto.js.map +1 -1
  17. package/dist/dashboard/dashboard-component/dto/update.dto.d.ts +1 -0
  18. package/dist/dashboard/dashboard-component/dto/update.dto.d.ts.map +1 -1
  19. package/dist/dashboard/dashboard-component/dto/update.dto.js +5 -0
  20. package/dist/dashboard/dashboard-component/dto/update.dto.js.map +1 -1
  21. package/dist/dashboard/dashboard-component-role/dashboard-component-role.controller.d.ts +1 -0
  22. package/dist/dashboard/dashboard-component-role/dashboard-component-role.controller.d.ts.map +1 -1
  23. package/dist/dashboard/dashboard-component-role/dashboard-component-role.service.d.ts +1 -0
  24. package/dist/dashboard/dashboard-component-role/dashboard-component-role.service.d.ts.map +1 -1
  25. package/dist/dashboard/dashboard-core/dashboard-core.controller.d.ts +7 -1
  26. package/dist/dashboard/dashboard-core/dashboard-core.controller.d.ts.map +1 -1
  27. package/dist/dashboard/dashboard-core/dashboard-core.service.d.ts +7 -1
  28. package/dist/dashboard/dashboard-core/dashboard-core.service.d.ts.map +1 -1
  29. package/dist/dashboard/dashboard-core/dashboard-core.service.js +88 -4
  30. package/dist/dashboard/dashboard-core/dashboard-core.service.js.map +1 -1
  31. package/dist/dashboard/dashboard-item/dashboard-item.controller.d.ts +1 -0
  32. package/dist/dashboard/dashboard-item/dashboard-item.controller.d.ts.map +1 -1
  33. package/dist/dashboard/dashboard-item/dashboard-item.service.d.ts +1 -0
  34. package/dist/dashboard/dashboard-item/dashboard-item.service.d.ts.map +1 -1
  35. package/hedhog/data/dashboard_item.yaml +1 -1
  36. package/hedhog/data/route.yaml +12 -0
  37. package/hedhog/frontend/app/dashboard/[slug]/dashboard-content.tsx.ejs +141 -24
  38. package/hedhog/frontend/app/dashboard/[slug]/types.ts.ejs +3 -0
  39. package/hedhog/frontend/app/dashboard/[slug]/widget-renderer.tsx.ejs +136 -23
  40. package/hedhog/frontend/app/dashboard/components/add-widget-selector-dialog.tsx.ejs +266 -85
  41. package/hedhog/frontend/app/dashboard/components/widgets/core..gitkeep.ejs +11 -0
  42. package/hedhog/frontend/app/dashboard/components/widgets/core.account-security.tsx.ejs +192 -0
  43. package/hedhog/frontend/app/dashboard/components/widgets/core.user-sessions.tsx.ejs +236 -0
  44. package/hedhog/frontend/app/dashboard/components/widgets/finance.alerts.tsx.ejs +108 -0
  45. package/hedhog/frontend/app/dashboard/components/widgets/finance.cash-balance-kpi.tsx.ejs +66 -0
  46. package/hedhog/frontend/app/dashboard/components/widgets/finance.cash-flow-chart.tsx.ejs +122 -0
  47. package/hedhog/frontend/app/dashboard/components/widgets/finance.default-kpi.tsx.ejs +63 -0
  48. package/hedhog/frontend/app/dashboard/components/widgets/finance.payable-30d-kpi.tsx.ejs +73 -0
  49. package/hedhog/frontend/app/dashboard/components/widgets/finance.receivable-30d-kpi.tsx.ejs +73 -0
  50. package/hedhog/frontend/app/dashboard/components/widgets/finance.upcoming-payable.tsx.ejs +123 -0
  51. package/hedhog/frontend/app/dashboard/components/widgets/finance.upcoming-receivable.tsx.ejs +118 -0
  52. package/hedhog/frontend/messages/en.json +3 -0
  53. package/hedhog/frontend/messages/pt.json +3 -0
  54. package/hedhog/frontend/public/dashboard-previews/.gitkeep +12 -0
  55. package/hedhog/frontend/public/dashboard-previews/account-security.png +0 -0
  56. package/hedhog/frontend/public/dashboard-previews/active-users-card.png +0 -0
  57. package/hedhog/frontend/public/dashboard-previews/activity-timeline.png +0 -0
  58. package/hedhog/frontend/public/dashboard-previews/cash-balance-kpi.png +0 -0
  59. package/hedhog/frontend/public/dashboard-previews/cash-flow-chart.png +0 -0
  60. package/hedhog/frontend/public/dashboard-previews/default-kpi.png +0 -0
  61. package/hedhog/frontend/public/dashboard-previews/email-notifications.png +0 -0
  62. package/hedhog/frontend/public/dashboard-previews/financial-alerts.png +0 -0
  63. package/hedhog/frontend/public/dashboard-previews/login-history-chart.png +0 -0
  64. package/hedhog/frontend/public/dashboard-previews/mail-sent-card.png +0 -0
  65. package/hedhog/frontend/public/dashboard-previews/mail-sent-chart.png +0 -0
  66. package/hedhog/frontend/public/dashboard-previews/menus-card.png +0 -0
  67. package/hedhog/frontend/public/dashboard-previews/payable-30d-kpi.png +0 -0
  68. package/hedhog/frontend/public/dashboard-previews/permissions-card.png +0 -0
  69. package/hedhog/frontend/public/dashboard-previews/permissions-chart.png +0 -0
  70. package/hedhog/frontend/public/dashboard-previews/profile-card.png +0 -0
  71. package/hedhog/frontend/public/dashboard-previews/receivable-30d-kpi.png +0 -0
  72. package/hedhog/frontend/public/dashboard-previews/routes-card.png +0 -0
  73. package/hedhog/frontend/public/dashboard-previews/session-activity-chart.png +0 -0
  74. package/hedhog/frontend/public/dashboard-previews/sessions-today-card.png +0 -0
  75. package/hedhog/frontend/public/dashboard-previews/stat-access-level.png +0 -0
  76. package/hedhog/frontend/public/dashboard-previews/stat-actions-today.png +0 -0
  77. package/hedhog/frontend/public/dashboard-previews/stat-consecutive-days.png +0 -0
  78. package/hedhog/frontend/public/dashboard-previews/stat-online-time.png +0 -0
  79. package/hedhog/frontend/public/dashboard-previews/upcoming-payable.png +0 -0
  80. package/hedhog/frontend/public/dashboard-previews/upcoming-receivable.png +0 -0
  81. package/hedhog/frontend/public/dashboard-previews/user-growth-chart.png +0 -0
  82. package/hedhog/frontend/public/dashboard-previews/user-roles.png +0 -0
  83. package/hedhog/frontend/{app/dashboard/components/widgets → widgets}/account-security.tsx.ejs +33 -29
  84. package/hedhog/frontend/widgets/active-users-card.tsx.ejs +58 -0
  85. package/hedhog/frontend/widgets/activity-timeline.tsx.ejs +223 -0
  86. package/hedhog/frontend/widgets/email-notifications.tsx.ejs +226 -0
  87. package/hedhog/frontend/widgets/locale-config.tsx.ejs +168 -0
  88. package/hedhog/frontend/widgets/login-history-chart.tsx.ejs +115 -0
  89. package/hedhog/frontend/widgets/mail-config.tsx.ejs +199 -0
  90. package/hedhog/frontend/widgets/mail-sent-card.tsx.ejs +58 -0
  91. package/hedhog/frontend/widgets/mail-sent-chart.tsx.ejs +149 -0
  92. package/hedhog/frontend/widgets/menus-card.tsx.ejs +58 -0
  93. package/hedhog/frontend/widgets/oauth-config.tsx.ejs +175 -0
  94. package/hedhog/frontend/widgets/permissions-card.tsx.ejs +61 -0
  95. package/hedhog/frontend/widgets/permissions-chart.tsx.ejs +156 -0
  96. package/hedhog/frontend/widgets/profile-card.tsx.ejs +186 -0
  97. package/hedhog/frontend/widgets/routes-card.tsx.ejs +58 -0
  98. package/hedhog/frontend/widgets/session-activity-chart.tsx.ejs +183 -0
  99. package/hedhog/frontend/widgets/sessions-today-card.tsx.ejs +62 -0
  100. package/hedhog/frontend/widgets/stat-access-level.tsx.ejs +57 -0
  101. package/hedhog/frontend/widgets/stat-actions-today.tsx.ejs +57 -0
  102. package/hedhog/frontend/widgets/stat-consecutive-days.tsx.ejs +57 -0
  103. package/hedhog/frontend/widgets/stat-online-time.tsx.ejs +57 -0
  104. package/hedhog/frontend/widgets/storage-config.tsx.ejs +196 -0
  105. package/hedhog/frontend/widgets/theme-config.tsx.ejs +213 -0
  106. package/hedhog/frontend/widgets/user-growth-chart.tsx.ejs +210 -0
  107. package/hedhog/frontend/widgets/user-roles.tsx.ejs +132 -0
  108. package/hedhog/frontend/{app/dashboard/components/widgets → widgets}/user-sessions.tsx.ejs +1 -1
  109. package/hedhog/table/dashboard_component.yaml +7 -0
  110. package/package.json +4 -4
  111. package/src/dashboard/dashboard-component/dashboard-component.controller.ts +36 -12
  112. package/src/dashboard/dashboard-component/dashboard-component.service.ts +150 -3
  113. package/src/dashboard/dashboard-component/dto/create.dto.ts +4 -0
  114. package/src/dashboard/dashboard-component/dto/update.dto.ts +4 -0
  115. package/src/dashboard/dashboard-core/dashboard-core.service.ts +108 -5
  116. /package/hedhog/frontend/app/dashboard/components/widgets/{active-users-card.tsx.ejs → core.active-users-card.tsx.ejs} +0 -0
  117. /package/hedhog/frontend/app/dashboard/components/widgets/{activity-timeline.tsx.ejs → core.activity-timeline.tsx.ejs} +0 -0
  118. /package/hedhog/frontend/app/dashboard/components/widgets/{email-notifications.tsx.ejs → core.email-notifications.tsx.ejs} +0 -0
  119. /package/hedhog/frontend/app/dashboard/components/widgets/{locale-config.tsx.ejs → core.locale-config.tsx.ejs} +0 -0
  120. /package/hedhog/frontend/app/dashboard/components/widgets/{login-history-chart.tsx.ejs → core.login-history-chart.tsx.ejs} +0 -0
  121. /package/hedhog/frontend/app/dashboard/components/widgets/{mail-config.tsx.ejs → core.mail-config.tsx.ejs} +0 -0
  122. /package/hedhog/frontend/app/dashboard/components/widgets/{mail-sent-card.tsx.ejs → core.mail-sent-card.tsx.ejs} +0 -0
  123. /package/hedhog/frontend/app/dashboard/components/widgets/{mail-sent-chart.tsx.ejs → core.mail-sent-chart.tsx.ejs} +0 -0
  124. /package/hedhog/frontend/app/dashboard/components/widgets/{menus-card.tsx.ejs → core.menus-card.tsx.ejs} +0 -0
  125. /package/hedhog/frontend/app/dashboard/components/widgets/{oauth-config.tsx.ejs → core.oauth-config.tsx.ejs} +0 -0
  126. /package/hedhog/frontend/app/dashboard/components/widgets/{permissions-card.tsx.ejs → core.permissions-card.tsx.ejs} +0 -0
  127. /package/hedhog/frontend/app/dashboard/components/widgets/{permissions-chart.tsx.ejs → core.permissions-chart.tsx.ejs} +0 -0
  128. /package/hedhog/frontend/app/dashboard/components/widgets/{profile-card.tsx.ejs → core.profile-card.tsx.ejs} +0 -0
  129. /package/hedhog/frontend/app/dashboard/components/widgets/{routes-card.tsx.ejs → core.routes-card.tsx.ejs} +0 -0
  130. /package/hedhog/frontend/app/dashboard/components/widgets/{session-activity-chart.tsx.ejs → core.session-activity-chart.tsx.ejs} +0 -0
  131. /package/hedhog/frontend/app/dashboard/components/widgets/{sessions-today-card.tsx.ejs → core.sessions-today-card.tsx.ejs} +0 -0
  132. /package/hedhog/frontend/app/dashboard/components/widgets/{stat-access-level.tsx.ejs → core.stat-access-level.tsx.ejs} +0 -0
  133. /package/hedhog/frontend/app/dashboard/components/widgets/{stat-actions-today.tsx.ejs → core.stat-actions-today.tsx.ejs} +0 -0
  134. /package/hedhog/frontend/app/dashboard/components/widgets/{stat-consecutive-days.tsx.ejs → core.stat-consecutive-days.tsx.ejs} +0 -0
  135. /package/hedhog/frontend/app/dashboard/components/widgets/{stat-online-time.tsx.ejs → core.stat-online-time.tsx.ejs} +0 -0
  136. /package/hedhog/frontend/app/dashboard/components/widgets/{storage-config.tsx.ejs → core.storage-config.tsx.ejs} +0 -0
  137. /package/hedhog/frontend/app/dashboard/components/widgets/{theme-config.tsx.ejs → core.theme-config.tsx.ejs} +0 -0
  138. /package/hedhog/frontend/app/dashboard/components/widgets/{user-growth-chart.tsx.ejs → core.user-growth-chart.tsx.ejs} +0 -0
  139. /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).
@@ -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-3">
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>{t('description')}</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-4 flex flex-col items-center gap-2.5 rounded-xl bg-muted/50 p-3 sm:mb-5 sm:gap-3 sm:p-5">
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-4xl font-bold tracking-tight sm:text-5xl ${scoreColor}`}
85
+ className={`text-2xl font-bold tracking-tight sm:text-3xl ${scoreColor}`}
79
86
  >
80
87
  {score}
81
88
  </span>
82
- <span className="text-base text-muted-foreground sm:text-lg">
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-2.5 w-full max-w-xs"
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-[11px] text-muted-foreground sm:text-xs">
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.5">
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
- <div
111
+ <button
112
+ type="button"
105
113
  key={item.id}
106
- className="group flex flex-wrap items-start gap-2.5 rounded-lg p-2.5 transition-colors hover:bg-muted/50 sm:items-center sm:gap-3 sm:p-3"
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-8 w-8 shrink-0 items-center justify-center rounded-lg sm:h-9 sm:w-9 ${
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 sm:h-4 sm:w-4 ${
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-[13px] font-medium text-foreground sm:text-sm">
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-[11px] text-muted-foreground sm:text-xs">
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
- {!item.enabled && (
140
- <Button
141
- variant="ghost"
142
- size="sm"
143
- onClick={() => router.push('/core/account/2fa')}
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
+ }