@hed-hog/finance 0.0.3 → 0.0.224
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/hedhog/data/menu.yaml +18 -23
- package/hedhog/frontend/app/_lib/formatters.ts.ejs +20 -0
- package/hedhog/frontend/app/_lib/use-finance-data.ts.ejs +87 -0
- package/hedhog/frontend/app/accounts-payable/approvals/page.tsx.ejs +265 -0
- package/hedhog/frontend/app/accounts-payable/installments/[id]/page.tsx.ejs +388 -0
- package/hedhog/frontend/app/accounts-payable/installments/page.tsx.ejs +364 -0
- package/hedhog/frontend/app/accounts-receivable/collections-default/page.tsx.ejs +388 -0
- package/hedhog/frontend/app/accounts-receivable/installments/[id]/page.tsx.ejs +385 -0
- package/hedhog/frontend/app/accounts-receivable/installments/page.tsx.ejs +364 -0
- package/hedhog/frontend/app/cash-and-banks/bank-accounts/page.tsx.ejs +274 -0
- package/hedhog/frontend/app/cash-and-banks/bank-reconciliation/page.tsx.ejs +401 -0
- package/hedhog/frontend/app/cash-and-banks/statements/page.tsx.ejs +266 -0
- package/hedhog/frontend/app/cash-and-banks/transfers/page.tsx.ejs +244 -0
- package/hedhog/frontend/app/page.tsx.ejs +313 -15
- package/hedhog/frontend/app/planning/cash-flow-forecast/page.tsx.ejs +285 -0
- package/hedhog/frontend/app/planning/receivables-calendar/page.tsx.ejs +195 -0
- package/hedhog/frontend/app/planning/scenarios/page.tsx.ejs +321 -0
- package/hedhog/query/constraints.sql +169 -0
- package/package.json +4 -4
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { Button } from '@/components/ui/button';
|
|
4
|
+
import {
|
|
5
|
+
Card,
|
|
6
|
+
CardContent,
|
|
7
|
+
CardDescription,
|
|
8
|
+
CardHeader,
|
|
9
|
+
CardTitle,
|
|
10
|
+
} from '@/components/ui/card';
|
|
11
|
+
import { Input } from '@/components/ui/input';
|
|
12
|
+
import { Label } from '@/components/ui/label';
|
|
13
|
+
import { Money } from '@/components/ui/money';
|
|
14
|
+
import { PageHeader } from '@/components/ui/page-header';
|
|
15
|
+
import {
|
|
16
|
+
Select,
|
|
17
|
+
SelectContent,
|
|
18
|
+
SelectItem,
|
|
19
|
+
SelectTrigger,
|
|
20
|
+
SelectValue,
|
|
21
|
+
} from '@/components/ui/select';
|
|
22
|
+
import {
|
|
23
|
+
Sheet,
|
|
24
|
+
SheetContent,
|
|
25
|
+
SheetDescription,
|
|
26
|
+
SheetHeader,
|
|
27
|
+
SheetTitle,
|
|
28
|
+
SheetTrigger,
|
|
29
|
+
} from '@/components/ui/sheet';
|
|
30
|
+
import {
|
|
31
|
+
Table,
|
|
32
|
+
TableBody,
|
|
33
|
+
TableCell,
|
|
34
|
+
TableHead,
|
|
35
|
+
TableHeader,
|
|
36
|
+
TableRow,
|
|
37
|
+
} from '@/components/ui/table';
|
|
38
|
+
import { Textarea } from '@/components/ui/textarea';
|
|
39
|
+
import { ArrowRight, Plus } from 'lucide-react';
|
|
40
|
+
import { formatarData } from '../../_lib/formatters';
|
|
41
|
+
import { useFinanceData } from '../../_lib/use-finance-data';
|
|
42
|
+
|
|
43
|
+
function NovaTransferenciaSheet({
|
|
44
|
+
contasBancarias,
|
|
45
|
+
}: {
|
|
46
|
+
contasBancarias: any[];
|
|
47
|
+
}) {
|
|
48
|
+
return (
|
|
49
|
+
<Sheet>
|
|
50
|
+
<SheetTrigger asChild>
|
|
51
|
+
<Button>
|
|
52
|
+
<Plus className="mr-2 h-4 w-4" />
|
|
53
|
+
Nova Transferência
|
|
54
|
+
</Button>
|
|
55
|
+
</SheetTrigger>
|
|
56
|
+
<SheetContent className="w-full sm:max-w-lg">
|
|
57
|
+
<SheetHeader>
|
|
58
|
+
<SheetTitle>Nova Transferência</SheetTitle>
|
|
59
|
+
<SheetDescription>
|
|
60
|
+
Registre uma transferência entre contas.
|
|
61
|
+
</SheetDescription>
|
|
62
|
+
</SheetHeader>
|
|
63
|
+
<form className="mt-6 space-y-4">
|
|
64
|
+
<div className="grid gap-4">
|
|
65
|
+
<div className="space-y-2">
|
|
66
|
+
<Label htmlFor="contaOrigem">Conta de Origem</Label>
|
|
67
|
+
<Select>
|
|
68
|
+
<SelectTrigger>
|
|
69
|
+
<SelectValue placeholder="Selecione..." />
|
|
70
|
+
</SelectTrigger>
|
|
71
|
+
<SelectContent>
|
|
72
|
+
{contasBancarias.map((conta) => (
|
|
73
|
+
<SelectItem key={conta.id} value={conta.id}>
|
|
74
|
+
{conta.banco} - {conta.descricao}
|
|
75
|
+
</SelectItem>
|
|
76
|
+
))}
|
|
77
|
+
</SelectContent>
|
|
78
|
+
</Select>
|
|
79
|
+
</div>
|
|
80
|
+
<div className="space-y-2">
|
|
81
|
+
<Label htmlFor="contaDestino">Conta de Destino</Label>
|
|
82
|
+
<Select>
|
|
83
|
+
<SelectTrigger>
|
|
84
|
+
<SelectValue placeholder="Selecione..." />
|
|
85
|
+
</SelectTrigger>
|
|
86
|
+
<SelectContent>
|
|
87
|
+
{contasBancarias.map((conta) => (
|
|
88
|
+
<SelectItem key={conta.id} value={conta.id}>
|
|
89
|
+
{conta.banco} - {conta.descricao}
|
|
90
|
+
</SelectItem>
|
|
91
|
+
))}
|
|
92
|
+
</SelectContent>
|
|
93
|
+
</Select>
|
|
94
|
+
</div>
|
|
95
|
+
<div className="grid grid-cols-2 gap-4">
|
|
96
|
+
<div className="space-y-2">
|
|
97
|
+
<Label htmlFor="data">Data</Label>
|
|
98
|
+
<Input id="data" type="date" />
|
|
99
|
+
</div>
|
|
100
|
+
<div className="space-y-2">
|
|
101
|
+
<Label htmlFor="valor">Valor</Label>
|
|
102
|
+
<Input
|
|
103
|
+
id="valor"
|
|
104
|
+
type="number"
|
|
105
|
+
placeholder="0,00"
|
|
106
|
+
step="0.01"
|
|
107
|
+
/>
|
|
108
|
+
</div>
|
|
109
|
+
</div>
|
|
110
|
+
<div className="space-y-2">
|
|
111
|
+
<Label htmlFor="descricao">Descrição</Label>
|
|
112
|
+
<Textarea
|
|
113
|
+
id="descricao"
|
|
114
|
+
placeholder="Motivo da transferência..."
|
|
115
|
+
/>
|
|
116
|
+
</div>
|
|
117
|
+
</div>
|
|
118
|
+
<div className="flex justify-end gap-2 pt-4">
|
|
119
|
+
<Button type="button" variant="outline">
|
|
120
|
+
Cancelar
|
|
121
|
+
</Button>
|
|
122
|
+
<Button type="submit">Registrar</Button>
|
|
123
|
+
</div>
|
|
124
|
+
</form>
|
|
125
|
+
</SheetContent>
|
|
126
|
+
</Sheet>
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export default function TransferenciasPage() {
|
|
131
|
+
const { data } = useFinanceData();
|
|
132
|
+
const { transferencias, contasBancarias } = data;
|
|
133
|
+
|
|
134
|
+
const totalTransferido = transferencias.reduce((acc, t) => acc + t.valor, 0);
|
|
135
|
+
|
|
136
|
+
return (
|
|
137
|
+
<div className="space-y-6">
|
|
138
|
+
<PageHeader
|
|
139
|
+
title="Transferências"
|
|
140
|
+
description="Gerencie transferências entre contas"
|
|
141
|
+
breadcrumbs={[
|
|
142
|
+
{ label: 'Caixa e Bancos', href: '/finance/cash-and-banks/bank-accounts' },
|
|
143
|
+
{ label: 'Transferências' },
|
|
144
|
+
]}
|
|
145
|
+
actions={<NovaTransferenciaSheet contasBancarias={contasBancarias} />}
|
|
146
|
+
/>
|
|
147
|
+
|
|
148
|
+
<div className="grid gap-4 md:grid-cols-2">
|
|
149
|
+
<Card>
|
|
150
|
+
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
|
|
151
|
+
<CardTitle className="text-sm font-medium">
|
|
152
|
+
Total Transferido
|
|
153
|
+
</CardTitle>
|
|
154
|
+
</CardHeader>
|
|
155
|
+
<CardContent>
|
|
156
|
+
<div className="text-2xl font-bold">
|
|
157
|
+
<Money value={totalTransferido} />
|
|
158
|
+
</div>
|
|
159
|
+
<p className="text-xs text-muted-foreground">
|
|
160
|
+
{transferencias.length} transferências
|
|
161
|
+
</p>
|
|
162
|
+
</CardContent>
|
|
163
|
+
</Card>
|
|
164
|
+
<Card>
|
|
165
|
+
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
|
|
166
|
+
<CardTitle className="text-sm font-medium">Contas Ativas</CardTitle>
|
|
167
|
+
</CardHeader>
|
|
168
|
+
<CardContent>
|
|
169
|
+
<div className="text-2xl font-bold">
|
|
170
|
+
{contasBancarias.filter((c) => c.ativo).length}
|
|
171
|
+
</div>
|
|
172
|
+
<p className="text-xs text-muted-foreground">disponíveis</p>
|
|
173
|
+
</CardContent>
|
|
174
|
+
</Card>
|
|
175
|
+
</div>
|
|
176
|
+
|
|
177
|
+
<Card>
|
|
178
|
+
<CardHeader>
|
|
179
|
+
<CardTitle>Transferências Recentes</CardTitle>
|
|
180
|
+
<CardDescription>
|
|
181
|
+
Histórico de transferências entre contas
|
|
182
|
+
</CardDescription>
|
|
183
|
+
</CardHeader>
|
|
184
|
+
<CardContent>
|
|
185
|
+
<Table>
|
|
186
|
+
<TableHeader>
|
|
187
|
+
<TableRow>
|
|
188
|
+
<TableHead>Data</TableHead>
|
|
189
|
+
<TableHead>Origem</TableHead>
|
|
190
|
+
<TableHead className="text-center">→</TableHead>
|
|
191
|
+
<TableHead>Destino</TableHead>
|
|
192
|
+
<TableHead className="text-right">Valor</TableHead>
|
|
193
|
+
<TableHead>Descrição</TableHead>
|
|
194
|
+
</TableRow>
|
|
195
|
+
</TableHeader>
|
|
196
|
+
<TableBody>
|
|
197
|
+
{transferencias.map((transferencia) => {
|
|
198
|
+
const contaOrigem = contasBancarias.find(
|
|
199
|
+
(conta) => conta.id === transferencia.contaOrigemId
|
|
200
|
+
);
|
|
201
|
+
const contaDestino = contasBancarias.find(
|
|
202
|
+
(conta) => conta.id === transferencia.contaDestinoId
|
|
203
|
+
);
|
|
204
|
+
|
|
205
|
+
return (
|
|
206
|
+
<TableRow key={transferencia.id}>
|
|
207
|
+
<TableCell>{formatarData(transferencia.data)}</TableCell>
|
|
208
|
+
<TableCell>
|
|
209
|
+
<div>
|
|
210
|
+
<p className="font-medium">{contaOrigem?.banco}</p>
|
|
211
|
+
<p className="text-xs text-muted-foreground">
|
|
212
|
+
{contaOrigem?.descricao}
|
|
213
|
+
</p>
|
|
214
|
+
</div>
|
|
215
|
+
</TableCell>
|
|
216
|
+
<TableCell className="text-center">
|
|
217
|
+
<ArrowRight className="h-4 w-4 text-muted-foreground mx-auto" />
|
|
218
|
+
</TableCell>
|
|
219
|
+
<TableCell>
|
|
220
|
+
<div>
|
|
221
|
+
<p className="font-medium">{contaDestino?.banco}</p>
|
|
222
|
+
<p className="text-xs text-muted-foreground">
|
|
223
|
+
{contaDestino?.descricao}
|
|
224
|
+
</p>
|
|
225
|
+
</div>
|
|
226
|
+
</TableCell>
|
|
227
|
+
<TableCell className="text-right font-semibold">
|
|
228
|
+
<Money value={transferencia.valor} />
|
|
229
|
+
</TableCell>
|
|
230
|
+
<TableCell className="text-muted-foreground">
|
|
231
|
+
{transferencia.descricao}
|
|
232
|
+
</TableCell>
|
|
233
|
+
</TableRow>
|
|
234
|
+
);
|
|
235
|
+
})}
|
|
236
|
+
</TableBody>
|
|
237
|
+
</Table>
|
|
238
|
+
</CardContent>
|
|
239
|
+
</Card>
|
|
240
|
+
</div>
|
|
241
|
+
);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
|
|
@@ -1,17 +1,315 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { Badge } from '@/components/ui/badge';
|
|
4
|
+
import {
|
|
5
|
+
Card,
|
|
6
|
+
CardContent,
|
|
7
|
+
CardDescription,
|
|
8
|
+
CardHeader,
|
|
9
|
+
CardTitle,
|
|
10
|
+
} from '@/components/ui/card';
|
|
11
|
+
import { KpiCard } from '@/components/ui/kpi-card';
|
|
12
|
+
import { Money } from '@/components/ui/money';
|
|
13
|
+
import { PageHeader } from '@/components/ui/page-header';
|
|
14
|
+
import { StatusBadge } from '@/components/ui/status-badge';
|
|
15
|
+
import {
|
|
16
|
+
AlertTriangle,
|
|
17
|
+
ArrowDownRight,
|
|
18
|
+
ArrowUpRight,
|
|
19
|
+
TrendingDown,
|
|
20
|
+
TrendingUp,
|
|
21
|
+
Wallet,
|
|
22
|
+
} from 'lucide-react';
|
|
23
|
+
import {
|
|
24
|
+
CartesianGrid,
|
|
25
|
+
Legend,
|
|
26
|
+
Line,
|
|
27
|
+
LineChart,
|
|
28
|
+
ResponsiveContainer,
|
|
29
|
+
Tooltip,
|
|
30
|
+
XAxis,
|
|
31
|
+
YAxis,
|
|
32
|
+
} from 'recharts';
|
|
33
|
+
import { formatarData, formatarMoeda } from './_lib/formatters';
|
|
34
|
+
import { useFinanceData } from './_lib/use-finance-data';
|
|
35
|
+
|
|
36
|
+
function DashboardChart({ fluxoCaixaPrevisto }: { fluxoCaixaPrevisto: any[] }) {
|
|
37
|
+
const chartData = fluxoCaixaPrevisto.map((item) => ({
|
|
38
|
+
...item,
|
|
39
|
+
data: formatarData(item.data),
|
|
40
|
+
}));
|
|
41
|
+
|
|
42
|
+
return (
|
|
43
|
+
<ResponsiveContainer width="100%" height={300}>
|
|
44
|
+
<LineChart data={chartData}>
|
|
45
|
+
<CartesianGrid strokeDasharray="3 3" className="stroke-muted" />
|
|
46
|
+
<XAxis
|
|
47
|
+
dataKey="data"
|
|
48
|
+
tick={{ fontSize: 12 }}
|
|
49
|
+
tickLine={false}
|
|
50
|
+
axisLine={false}
|
|
51
|
+
/>
|
|
52
|
+
<YAxis
|
|
53
|
+
tick={{ fontSize: 12 }}
|
|
54
|
+
tickLine={false}
|
|
55
|
+
axisLine={false}
|
|
56
|
+
tickFormatter={(value) => `${(value / 1000).toFixed(0)}k`}
|
|
57
|
+
/>
|
|
58
|
+
<Tooltip
|
|
59
|
+
formatter={(value: number) => formatarMoeda(value)}
|
|
60
|
+
contentStyle={{
|
|
61
|
+
backgroundColor: 'hsl(var(--background))',
|
|
62
|
+
border: '1px solid hsl(var(--border))',
|
|
63
|
+
borderRadius: '8px',
|
|
64
|
+
}}
|
|
65
|
+
/>
|
|
66
|
+
<Legend />
|
|
67
|
+
<Line
|
|
68
|
+
type="monotone"
|
|
69
|
+
dataKey="saldoPrevisto"
|
|
70
|
+
name="Previsto"
|
|
71
|
+
stroke="hsl(var(--primary))"
|
|
72
|
+
strokeWidth={2}
|
|
73
|
+
dot={false}
|
|
74
|
+
/>
|
|
75
|
+
<Line
|
|
76
|
+
type="monotone"
|
|
77
|
+
dataKey="saldoRealizado"
|
|
78
|
+
name="Realizado"
|
|
79
|
+
stroke="hsl(var(--chart-2))"
|
|
80
|
+
strokeWidth={2}
|
|
81
|
+
dot={false}
|
|
82
|
+
connectNulls
|
|
83
|
+
/>
|
|
84
|
+
</LineChart>
|
|
85
|
+
</ResponsiveContainer>
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function ProximosVencimentos({
|
|
90
|
+
titulosPagar,
|
|
91
|
+
titulosReceber,
|
|
92
|
+
getPessoaById,
|
|
93
|
+
}: {
|
|
94
|
+
titulosPagar: any[];
|
|
95
|
+
titulosReceber: any[];
|
|
96
|
+
getPessoaById: (id?: string) => any | undefined;
|
|
97
|
+
}) {
|
|
98
|
+
const vencimentosPagar = titulosPagar
|
|
99
|
+
.flatMap((t: any) =>
|
|
100
|
+
t.parcelas
|
|
101
|
+
.filter((p: any) => p.status === 'aberto' || p.status === 'vencido')
|
|
102
|
+
.map((p: any) => ({
|
|
103
|
+
tipo: 'pagar' as const,
|
|
104
|
+
documento: t.documento,
|
|
105
|
+
pessoa: getPessoaById(t.fornecedorId)?.nome || '',
|
|
106
|
+
vencimento: p.vencimento,
|
|
107
|
+
valor: p.valor,
|
|
108
|
+
status: p.status,
|
|
109
|
+
}))
|
|
110
|
+
)
|
|
111
|
+
.slice(0, 3);
|
|
112
|
+
|
|
113
|
+
const vencimentosReceber = titulosReceber
|
|
114
|
+
.flatMap((t: any) =>
|
|
115
|
+
t.parcelas
|
|
116
|
+
.filter((p: any) => p.status === 'aberto' || p.status === 'vencido')
|
|
117
|
+
.map((p: any) => ({
|
|
118
|
+
tipo: 'receber' as const,
|
|
119
|
+
documento: t.documento,
|
|
120
|
+
pessoa: getPessoaById(t.clienteId)?.nome || '',
|
|
121
|
+
vencimento: p.vencimento,
|
|
122
|
+
valor: p.valor,
|
|
123
|
+
status: p.status,
|
|
124
|
+
}))
|
|
125
|
+
)
|
|
126
|
+
.slice(0, 3);
|
|
127
|
+
|
|
128
|
+
return (
|
|
129
|
+
<div className="grid gap-6 lg:grid-cols-2">
|
|
130
|
+
<Card>
|
|
131
|
+
<CardHeader>
|
|
132
|
+
<CardTitle className="flex items-center gap-2 text-base">
|
|
133
|
+
<ArrowDownRight className="h-4 w-4 text-red-500" />A Pagar
|
|
134
|
+
</CardTitle>
|
|
135
|
+
<CardDescription>Próximos vencimentos</CardDescription>
|
|
136
|
+
</CardHeader>
|
|
137
|
+
<CardContent>
|
|
138
|
+
<div className="space-y-4">
|
|
139
|
+
{vencimentosPagar.map((item, i) => (
|
|
140
|
+
<div key={i} className="flex items-center justify-between">
|
|
141
|
+
<div className="space-y-1">
|
|
142
|
+
<p className="text-sm font-medium">{item.documento}</p>
|
|
143
|
+
<p className="text-xs text-muted-foreground">{item.pessoa}</p>
|
|
144
|
+
</div>
|
|
145
|
+
<div className="text-right">
|
|
146
|
+
<p className="text-sm font-medium">
|
|
147
|
+
<Money value={item.valor} />
|
|
148
|
+
</p>
|
|
149
|
+
<p className="text-xs text-muted-foreground">
|
|
150
|
+
{formatarData(item.vencimento)}
|
|
151
|
+
</p>
|
|
152
|
+
</div>
|
|
153
|
+
<StatusBadge status={item.status} />
|
|
154
|
+
</div>
|
|
155
|
+
))}
|
|
156
|
+
</div>
|
|
157
|
+
</CardContent>
|
|
158
|
+
</Card>
|
|
159
|
+
|
|
160
|
+
<Card>
|
|
161
|
+
<CardHeader>
|
|
162
|
+
<CardTitle className="flex items-center gap-2 text-base">
|
|
163
|
+
<ArrowUpRight className="h-4 w-4 text-green-500" />A Receber
|
|
164
|
+
</CardTitle>
|
|
165
|
+
<CardDescription>Próximos vencimentos</CardDescription>
|
|
166
|
+
</CardHeader>
|
|
167
|
+
<CardContent>
|
|
168
|
+
<div className="space-y-4">
|
|
169
|
+
{vencimentosReceber.map((item, i) => (
|
|
170
|
+
<div key={i} className="flex items-center justify-between">
|
|
171
|
+
<div className="space-y-1">
|
|
172
|
+
<p className="text-sm font-medium">{item.documento}</p>
|
|
173
|
+
<p className="text-xs text-muted-foreground">{item.pessoa}</p>
|
|
174
|
+
</div>
|
|
175
|
+
<div className="text-right">
|
|
176
|
+
<p className="text-sm font-medium">
|
|
177
|
+
<Money value={item.valor} />
|
|
178
|
+
</p>
|
|
179
|
+
<p className="text-xs text-muted-foreground">
|
|
180
|
+
{formatarData(item.vencimento)}
|
|
181
|
+
</p>
|
|
182
|
+
</div>
|
|
183
|
+
<StatusBadge status={item.status} />
|
|
184
|
+
</div>
|
|
185
|
+
))}
|
|
186
|
+
</div>
|
|
187
|
+
</CardContent>
|
|
188
|
+
</Card>
|
|
189
|
+
</div>
|
|
190
|
+
);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function Alertas({
|
|
194
|
+
titulosPagar,
|
|
195
|
+
extratos,
|
|
196
|
+
}: {
|
|
197
|
+
titulosPagar: any[];
|
|
198
|
+
extratos: any[];
|
|
199
|
+
}) {
|
|
200
|
+
const vencidos = titulosPagar.filter((t) =>
|
|
201
|
+
t.parcelas.some((p: any) => p.status === 'vencido')
|
|
202
|
+
).length;
|
|
203
|
+
const pendenteConciliacao = extratos.filter(
|
|
204
|
+
(e) => e.statusConciliacao === 'pendente'
|
|
205
|
+
).length;
|
|
206
|
+
|
|
207
|
+
return (
|
|
208
|
+
<Card>
|
|
209
|
+
<CardHeader>
|
|
210
|
+
<CardTitle className="flex items-center gap-2 text-base">
|
|
211
|
+
<AlertTriangle className="h-4 w-4 text-yellow-500" />
|
|
212
|
+
Alertas
|
|
213
|
+
</CardTitle>
|
|
214
|
+
</CardHeader>
|
|
215
|
+
<CardContent>
|
|
216
|
+
<div className="space-y-3">
|
|
217
|
+
{vencidos > 0 && (
|
|
218
|
+
<div className="flex items-center justify-between rounded-lg bg-red-50 p-3">
|
|
219
|
+
<span className="text-sm">Títulos vencidos</span>
|
|
220
|
+
<Badge variant="destructive">{vencidos}</Badge>
|
|
221
|
+
</div>
|
|
222
|
+
)}
|
|
223
|
+
{pendenteConciliacao > 0 && (
|
|
224
|
+
<div className="flex items-center justify-between rounded-lg bg-yellow-50 p-3">
|
|
225
|
+
<span className="text-sm">Conciliação pendente</span>
|
|
226
|
+
<Badge
|
|
227
|
+
variant="outline"
|
|
228
|
+
className="border-yellow-500 text-yellow-700"
|
|
229
|
+
>
|
|
230
|
+
{pendenteConciliacao}
|
|
231
|
+
</Badge>
|
|
232
|
+
</div>
|
|
233
|
+
)}
|
|
234
|
+
<div className="flex items-center justify-between rounded-lg bg-blue-50 p-3">
|
|
235
|
+
<span className="text-sm">Período aberto</span>
|
|
236
|
+
<Badge variant="outline" className="border-blue-500 text-blue-700">
|
|
237
|
+
Janeiro/2024
|
|
238
|
+
</Badge>
|
|
239
|
+
</div>
|
|
240
|
+
</div>
|
|
241
|
+
</CardContent>
|
|
242
|
+
</Card>
|
|
243
|
+
);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
export default function DashboardPage() {
|
|
247
|
+
const { data } = useFinanceData();
|
|
248
|
+
const {
|
|
249
|
+
kpis,
|
|
250
|
+
fluxoCaixaPrevisto,
|
|
251
|
+
titulosPagar,
|
|
252
|
+
titulosReceber,
|
|
253
|
+
extratos,
|
|
254
|
+
pessoas,
|
|
255
|
+
} = data;
|
|
256
|
+
|
|
257
|
+
const getPessoaById = (id?: string) => pessoas.find((p) => p.id === id);
|
|
258
|
+
|
|
259
|
+
return (
|
|
260
|
+
<div className="space-y-6">
|
|
261
|
+
<PageHeader
|
|
262
|
+
title="Visão Geral"
|
|
263
|
+
description="Acompanhe os principais indicadores financeiros"
|
|
264
|
+
/>
|
|
265
|
+
|
|
266
|
+
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-4">
|
|
267
|
+
<KpiCard
|
|
268
|
+
title="Saldo em Caixa"
|
|
269
|
+
value={formatarMoeda(kpis.saldoCaixa)}
|
|
270
|
+
icon={Wallet}
|
|
271
|
+
description="Todas as contas"
|
|
272
|
+
/>
|
|
273
|
+
<KpiCard
|
|
274
|
+
title="A Pagar (30 dias)"
|
|
275
|
+
value={formatarMoeda(kpis.aPagar30dias)}
|
|
276
|
+
icon={TrendingDown}
|
|
277
|
+
description={`7 dias: ${formatarMoeda(kpis.aPagar7dias)}`}
|
|
278
|
+
/>
|
|
279
|
+
<KpiCard
|
|
280
|
+
title="A Receber (30 dias)"
|
|
281
|
+
value={formatarMoeda(kpis.aReceber30dias)}
|
|
282
|
+
icon={TrendingUp}
|
|
283
|
+
description={`7 dias: ${formatarMoeda(kpis.aReceber7dias)}`}
|
|
284
|
+
/>
|
|
285
|
+
<KpiCard
|
|
286
|
+
title="Inadimplência"
|
|
287
|
+
value={formatarMoeda(kpis.inadimplencia)}
|
|
288
|
+
icon={AlertTriangle}
|
|
289
|
+
description="Títulos vencidos"
|
|
290
|
+
/>
|
|
291
|
+
</div>
|
|
292
|
+
|
|
293
|
+
<div className="grid gap-6 lg:grid-cols-3">
|
|
294
|
+
<Card className="lg:col-span-2">
|
|
295
|
+
<CardHeader>
|
|
296
|
+
<CardTitle>Fluxo de Caixa</CardTitle>
|
|
297
|
+
<CardDescription>Previsto vs Realizado</CardDescription>
|
|
298
|
+
</CardHeader>
|
|
299
|
+
<CardContent>
|
|
300
|
+
<DashboardChart fluxoCaixaPrevisto={fluxoCaixaPrevisto} />
|
|
301
|
+
</CardContent>
|
|
302
|
+
</Card>
|
|
303
|
+
<Alertas titulosPagar={titulosPagar} extratos={extratos} />
|
|
304
|
+
</div>
|
|
305
|
+
|
|
306
|
+
<ProximosVencimentos
|
|
307
|
+
titulosPagar={titulosPagar}
|
|
308
|
+
titulosReceber={titulosReceber}
|
|
309
|
+
getPessoaById={getPessoaById}
|
|
310
|
+
/>
|
|
311
|
+
</div>
|
|
312
|
+
);
|
|
313
|
+
}
|
|
3
314
|
|
|
4
|
-
export const Page = () => {
|
|
5
|
-
const t = useTranslations('finance.Home');
|
|
6
315
|
|
|
7
|
-
return (
|
|
8
|
-
<div className="flex flex-col h-screen px-4">
|
|
9
|
-
<PageHeader
|
|
10
|
-
breadcrumbs={[{ label: 'Home', href: '/' }, { label: t('title') }]}
|
|
11
|
-
title={t('title')}
|
|
12
|
-
description={t('description')}
|
|
13
|
-
/>
|
|
14
|
-
Finance
|
|
15
|
-
</div>
|
|
16
|
-
);
|
|
17
|
-
};
|