@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,364 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { Badge } from '@/components/ui/badge';
|
|
4
|
+
import { Button } from '@/components/ui/button';
|
|
5
|
+
import {
|
|
6
|
+
DropdownMenu,
|
|
7
|
+
DropdownMenuContent,
|
|
8
|
+
DropdownMenuItem,
|
|
9
|
+
DropdownMenuSeparator,
|
|
10
|
+
DropdownMenuTrigger,
|
|
11
|
+
} from '@/components/ui/dropdown-menu';
|
|
12
|
+
import { FilterBar } from '@/components/ui/filter-bar';
|
|
13
|
+
import { Input } from '@/components/ui/input';
|
|
14
|
+
import { Label } from '@/components/ui/label';
|
|
15
|
+
import { Money } from '@/components/ui/money';
|
|
16
|
+
import { PageHeader } from '@/components/ui/page-header';
|
|
17
|
+
import {
|
|
18
|
+
Select,
|
|
19
|
+
SelectContent,
|
|
20
|
+
SelectItem,
|
|
21
|
+
SelectTrigger,
|
|
22
|
+
SelectValue,
|
|
23
|
+
} from '@/components/ui/select';
|
|
24
|
+
import {
|
|
25
|
+
Sheet,
|
|
26
|
+
SheetContent,
|
|
27
|
+
SheetDescription,
|
|
28
|
+
SheetHeader,
|
|
29
|
+
SheetTitle,
|
|
30
|
+
SheetTrigger,
|
|
31
|
+
} from '@/components/ui/sheet';
|
|
32
|
+
import { StatusBadge } from '@/components/ui/status-badge';
|
|
33
|
+
import {
|
|
34
|
+
Table,
|
|
35
|
+
TableBody,
|
|
36
|
+
TableCell,
|
|
37
|
+
TableHead,
|
|
38
|
+
TableHeader,
|
|
39
|
+
TableRow,
|
|
40
|
+
} from '@/components/ui/table';
|
|
41
|
+
import { Textarea } from '@/components/ui/textarea';
|
|
42
|
+
import {
|
|
43
|
+
Download,
|
|
44
|
+
Edit,
|
|
45
|
+
Eye,
|
|
46
|
+
MoreHorizontal,
|
|
47
|
+
Paperclip,
|
|
48
|
+
Plus,
|
|
49
|
+
Send,
|
|
50
|
+
} from 'lucide-react';
|
|
51
|
+
import Link from 'next/link';
|
|
52
|
+
import { useState } from 'react';
|
|
53
|
+
import { formatarData } from '../../_lib/formatters';
|
|
54
|
+
import { useFinanceData } from '../../_lib/use-finance-data';
|
|
55
|
+
|
|
56
|
+
function NovoTituloSheet({
|
|
57
|
+
pessoas,
|
|
58
|
+
categorias,
|
|
59
|
+
centrosCusto,
|
|
60
|
+
}: {
|
|
61
|
+
pessoas: any[];
|
|
62
|
+
categorias: any[];
|
|
63
|
+
centrosCusto: any[];
|
|
64
|
+
}) {
|
|
65
|
+
return (
|
|
66
|
+
<Sheet>
|
|
67
|
+
<SheetTrigger asChild>
|
|
68
|
+
<Button>
|
|
69
|
+
<Plus className="mr-2 h-4 w-4" />
|
|
70
|
+
Novo Título
|
|
71
|
+
</Button>
|
|
72
|
+
</SheetTrigger>
|
|
73
|
+
<SheetContent className="w-full sm:max-w-lg overflow-y-auto">
|
|
74
|
+
<SheetHeader>
|
|
75
|
+
<SheetTitle>Novo Título a Receber</SheetTitle>
|
|
76
|
+
<SheetDescription>
|
|
77
|
+
Preencha os dados para criar um novo título a receber.
|
|
78
|
+
</SheetDescription>
|
|
79
|
+
</SheetHeader>
|
|
80
|
+
<form className="mt-6 space-y-4">
|
|
81
|
+
<div className="grid gap-4">
|
|
82
|
+
<div className="space-y-2">
|
|
83
|
+
<Label htmlFor="documento">Documento</Label>
|
|
84
|
+
<Input id="documento" placeholder="FAT-00000" />
|
|
85
|
+
</div>
|
|
86
|
+
<div className="space-y-2">
|
|
87
|
+
<Label htmlFor="cliente">Cliente</Label>
|
|
88
|
+
<Select>
|
|
89
|
+
<SelectTrigger>
|
|
90
|
+
<SelectValue placeholder="Selecione..." />
|
|
91
|
+
</SelectTrigger>
|
|
92
|
+
<SelectContent>
|
|
93
|
+
{pessoas
|
|
94
|
+
.filter((p) => p.tipo === 'cliente' || p.tipo === 'ambos')
|
|
95
|
+
.map((p) => (
|
|
96
|
+
<SelectItem key={p.id} value={p.id}>
|
|
97
|
+
{p.nome}
|
|
98
|
+
</SelectItem>
|
|
99
|
+
))}
|
|
100
|
+
</SelectContent>
|
|
101
|
+
</Select>
|
|
102
|
+
</div>
|
|
103
|
+
<div className="grid grid-cols-2 gap-4">
|
|
104
|
+
<div className="space-y-2">
|
|
105
|
+
<Label htmlFor="competencia">Competência</Label>
|
|
106
|
+
<Input id="competencia" type="month" />
|
|
107
|
+
</div>
|
|
108
|
+
<div className="space-y-2">
|
|
109
|
+
<Label htmlFor="vencimento">Vencimento</Label>
|
|
110
|
+
<Input id="vencimento" type="date" />
|
|
111
|
+
</div>
|
|
112
|
+
</div>
|
|
113
|
+
<div className="space-y-2">
|
|
114
|
+
<Label htmlFor="valor">Valor Total</Label>
|
|
115
|
+
<Input id="valor" type="number" placeholder="0,00" step="0.01" />
|
|
116
|
+
</div>
|
|
117
|
+
<div className="space-y-2">
|
|
118
|
+
<Label htmlFor="categoria">Categoria</Label>
|
|
119
|
+
<Select>
|
|
120
|
+
<SelectTrigger>
|
|
121
|
+
<SelectValue placeholder="Selecione..." />
|
|
122
|
+
</SelectTrigger>
|
|
123
|
+
<SelectContent>
|
|
124
|
+
{categorias
|
|
125
|
+
.filter((c) => c.natureza === 'receita')
|
|
126
|
+
.map((c) => (
|
|
127
|
+
<SelectItem key={c.id} value={c.id}>
|
|
128
|
+
{c.codigo} - {c.nome}
|
|
129
|
+
</SelectItem>
|
|
130
|
+
))}
|
|
131
|
+
</SelectContent>
|
|
132
|
+
</Select>
|
|
133
|
+
</div>
|
|
134
|
+
<div className="space-y-2">
|
|
135
|
+
<Label htmlFor="centroCusto">Centro de Custo</Label>
|
|
136
|
+
<Select>
|
|
137
|
+
<SelectTrigger>
|
|
138
|
+
<SelectValue placeholder="Selecione..." />
|
|
139
|
+
</SelectTrigger>
|
|
140
|
+
<SelectContent>
|
|
141
|
+
{centrosCusto.map((c) => (
|
|
142
|
+
<SelectItem key={c.id} value={c.id}>
|
|
143
|
+
{c.codigo} - {c.nome}
|
|
144
|
+
</SelectItem>
|
|
145
|
+
))}
|
|
146
|
+
</SelectContent>
|
|
147
|
+
</Select>
|
|
148
|
+
</div>
|
|
149
|
+
<div className="space-y-2">
|
|
150
|
+
<Label htmlFor="canal">Canal de Recebimento</Label>
|
|
151
|
+
<Select>
|
|
152
|
+
<SelectTrigger>
|
|
153
|
+
<SelectValue placeholder="Selecione..." />
|
|
154
|
+
</SelectTrigger>
|
|
155
|
+
<SelectContent>
|
|
156
|
+
<SelectItem value="boleto">Boleto</SelectItem>
|
|
157
|
+
<SelectItem value="pix">PIX</SelectItem>
|
|
158
|
+
<SelectItem value="cartao">Cartão</SelectItem>
|
|
159
|
+
<SelectItem value="transferencia">Transferência</SelectItem>
|
|
160
|
+
</SelectContent>
|
|
161
|
+
</Select>
|
|
162
|
+
</div>
|
|
163
|
+
<div className="space-y-2">
|
|
164
|
+
<Label htmlFor="descricao">Descrição</Label>
|
|
165
|
+
<Textarea id="descricao" placeholder="Descrição do título..." />
|
|
166
|
+
</div>
|
|
167
|
+
</div>
|
|
168
|
+
<div className="flex justify-end gap-2 pt-4">
|
|
169
|
+
<Button type="button" variant="outline">
|
|
170
|
+
Cancelar
|
|
171
|
+
</Button>
|
|
172
|
+
<Button type="submit">Salvar</Button>
|
|
173
|
+
</div>
|
|
174
|
+
</form>
|
|
175
|
+
</SheetContent>
|
|
176
|
+
</Sheet>
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const canalBadge = {
|
|
181
|
+
boleto: { label: 'Boleto', className: 'bg-blue-100 text-blue-700' },
|
|
182
|
+
pix: { label: 'PIX', className: 'bg-green-100 text-green-700' },
|
|
183
|
+
cartao: { label: 'Cartão', className: 'bg-purple-100 text-purple-700' },
|
|
184
|
+
transferencia: {
|
|
185
|
+
label: 'Transferência',
|
|
186
|
+
className: 'bg-orange-100 text-orange-700',
|
|
187
|
+
},
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
export default function TitulosReceberPage() {
|
|
191
|
+
const { data } = useFinanceData();
|
|
192
|
+
const { titulosReceber, pessoas, categorias, centrosCusto } = data;
|
|
193
|
+
|
|
194
|
+
const getPessoaById = (id?: string) => pessoas.find((p) => p.id === id);
|
|
195
|
+
|
|
196
|
+
const [search, setSearch] = useState('');
|
|
197
|
+
const [statusFilter, setStatusFilter] = useState<string>('');
|
|
198
|
+
|
|
199
|
+
const filteredTitulos = titulosReceber.filter((titulo) => {
|
|
200
|
+
const matchesSearch =
|
|
201
|
+
titulo.documento.toLowerCase().includes(search.toLowerCase()) ||
|
|
202
|
+
getPessoaById(titulo.clienteId)
|
|
203
|
+
?.nome.toLowerCase()
|
|
204
|
+
.includes(search.toLowerCase());
|
|
205
|
+
|
|
206
|
+
const matchesStatus = !statusFilter || titulo.status === statusFilter;
|
|
207
|
+
|
|
208
|
+
return matchesSearch && matchesStatus;
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
return (
|
|
212
|
+
<div className="space-y-6">
|
|
213
|
+
<PageHeader
|
|
214
|
+
title="Títulos a Receber"
|
|
215
|
+
description="Gerencie suas contas a receber"
|
|
216
|
+
breadcrumbs={[
|
|
217
|
+
{ label: 'Contas a Receber', href: '/finance/accounts-receivable/installments' },
|
|
218
|
+
{ label: 'Títulos e Parcelas' },
|
|
219
|
+
]}
|
|
220
|
+
actions={
|
|
221
|
+
<NovoTituloSheet
|
|
222
|
+
pessoas={pessoas}
|
|
223
|
+
categorias={categorias}
|
|
224
|
+
centrosCusto={centrosCusto}
|
|
225
|
+
/>
|
|
226
|
+
}
|
|
227
|
+
/>
|
|
228
|
+
|
|
229
|
+
<FilterBar
|
|
230
|
+
searchPlaceholder="Buscar por documento ou cliente..."
|
|
231
|
+
searchValue={search}
|
|
232
|
+
onSearchChange={setSearch}
|
|
233
|
+
filters={[
|
|
234
|
+
{
|
|
235
|
+
id: 'status',
|
|
236
|
+
label: 'Status',
|
|
237
|
+
value: statusFilter,
|
|
238
|
+
onChange: setStatusFilter,
|
|
239
|
+
options: [
|
|
240
|
+
{ value: 'all', label: 'Todos' },
|
|
241
|
+
{ value: 'aberto', label: 'Aberto' },
|
|
242
|
+
{ value: 'parcial', label: 'Parcial' },
|
|
243
|
+
{ value: 'liquidado', label: 'Liquidado' },
|
|
244
|
+
{ value: 'vencido', label: 'Vencido' },
|
|
245
|
+
{ value: 'cancelado', label: 'Cancelado' },
|
|
246
|
+
],
|
|
247
|
+
},
|
|
248
|
+
]}
|
|
249
|
+
activeFilters={statusFilter && statusFilter !== 'all' ? 1 : 0}
|
|
250
|
+
onClearFilters={() => setStatusFilter('')}
|
|
251
|
+
/>
|
|
252
|
+
|
|
253
|
+
<div className="rounded-md border">
|
|
254
|
+
<Table>
|
|
255
|
+
<TableHeader>
|
|
256
|
+
<TableRow>
|
|
257
|
+
<TableHead>Documento</TableHead>
|
|
258
|
+
<TableHead>Cliente</TableHead>
|
|
259
|
+
<TableHead>Competência</TableHead>
|
|
260
|
+
<TableHead>Vencimento</TableHead>
|
|
261
|
+
<TableHead className="text-right">Valor</TableHead>
|
|
262
|
+
<TableHead>Canal</TableHead>
|
|
263
|
+
<TableHead>Status</TableHead>
|
|
264
|
+
<TableHead className="w-[50px]" />
|
|
265
|
+
</TableRow>
|
|
266
|
+
</TableHeader>
|
|
267
|
+
<TableBody>
|
|
268
|
+
{filteredTitulos.map((titulo) => {
|
|
269
|
+
const cliente = getPessoaById(titulo.clienteId);
|
|
270
|
+
const canal =
|
|
271
|
+
canalBadge[titulo.canal as keyof typeof canalBadge] ||
|
|
272
|
+
canalBadge.transferencia;
|
|
273
|
+
const proximaParcela = titulo.parcelas.find(
|
|
274
|
+
(p: any) => p.status === 'aberto' || p.status === 'vencido'
|
|
275
|
+
);
|
|
276
|
+
|
|
277
|
+
return (
|
|
278
|
+
<TableRow key={titulo.id}>
|
|
279
|
+
<TableCell className="font-medium">
|
|
280
|
+
<Link
|
|
281
|
+
href={`/finance/accounts-receivable/installments/${titulo.id}`}
|
|
282
|
+
className="hover:underline"
|
|
283
|
+
>
|
|
284
|
+
{titulo.documento}
|
|
285
|
+
</Link>
|
|
286
|
+
{titulo.anexos.length > 0 && (
|
|
287
|
+
<Paperclip className="ml-1 inline h-3 w-3 text-muted-foreground" />
|
|
288
|
+
)}
|
|
289
|
+
</TableCell>
|
|
290
|
+
<TableCell>{cliente?.nome}</TableCell>
|
|
291
|
+
<TableCell>{titulo.competencia}</TableCell>
|
|
292
|
+
<TableCell>
|
|
293
|
+
{proximaParcela
|
|
294
|
+
? formatarData(proximaParcela.vencimento)
|
|
295
|
+
: '-'}
|
|
296
|
+
</TableCell>
|
|
297
|
+
<TableCell className="text-right">
|
|
298
|
+
<Money value={titulo.valorTotal} />
|
|
299
|
+
</TableCell>
|
|
300
|
+
<TableCell>
|
|
301
|
+
<Badge className={canal.className} variant="outline">
|
|
302
|
+
{canal.label}
|
|
303
|
+
</Badge>
|
|
304
|
+
</TableCell>
|
|
305
|
+
<TableCell>
|
|
306
|
+
<StatusBadge status={titulo.status} />
|
|
307
|
+
</TableCell>
|
|
308
|
+
<TableCell>
|
|
309
|
+
<DropdownMenu>
|
|
310
|
+
<DropdownMenuTrigger asChild>
|
|
311
|
+
<Button variant="ghost" size="icon">
|
|
312
|
+
<MoreHorizontal className="h-4 w-4" />
|
|
313
|
+
<span className="sr-only">Ações</span>
|
|
314
|
+
</Button>
|
|
315
|
+
</DropdownMenuTrigger>
|
|
316
|
+
<DropdownMenuContent align="end">
|
|
317
|
+
<DropdownMenuItem asChild>
|
|
318
|
+
<Link href={`/finance/accounts-receivable/installments/${titulo.id}`}>
|
|
319
|
+
<Eye className="mr-2 h-4 w-4" />
|
|
320
|
+
Ver Detalhes
|
|
321
|
+
</Link>
|
|
322
|
+
</DropdownMenuItem>
|
|
323
|
+
<DropdownMenuItem>
|
|
324
|
+
<Edit className="mr-2 h-4 w-4" />
|
|
325
|
+
Editar
|
|
326
|
+
</DropdownMenuItem>
|
|
327
|
+
<DropdownMenuSeparator />
|
|
328
|
+
<DropdownMenuItem>
|
|
329
|
+
<Download className="mr-2 h-4 w-4" />
|
|
330
|
+
Registrar Recebimento
|
|
331
|
+
</DropdownMenuItem>
|
|
332
|
+
<DropdownMenuItem>
|
|
333
|
+
<Send className="mr-2 h-4 w-4" />
|
|
334
|
+
Enviar Cobrança
|
|
335
|
+
</DropdownMenuItem>
|
|
336
|
+
</DropdownMenuContent>
|
|
337
|
+
</DropdownMenu>
|
|
338
|
+
</TableCell>
|
|
339
|
+
</TableRow>
|
|
340
|
+
);
|
|
341
|
+
})}
|
|
342
|
+
</TableBody>
|
|
343
|
+
</Table>
|
|
344
|
+
</div>
|
|
345
|
+
|
|
346
|
+
<div className="flex items-center justify-between">
|
|
347
|
+
<p className="text-sm text-muted-foreground">
|
|
348
|
+
Mostrando {filteredTitulos.length} de {titulosReceber.length}{' '}
|
|
349
|
+
registros
|
|
350
|
+
</p>
|
|
351
|
+
<div className="flex items-center gap-2">
|
|
352
|
+
<Button variant="outline" size="sm" disabled>
|
|
353
|
+
Anterior
|
|
354
|
+
</Button>
|
|
355
|
+
<Button variant="outline" size="sm" disabled>
|
|
356
|
+
Próximo
|
|
357
|
+
</Button>
|
|
358
|
+
</div>
|
|
359
|
+
</div>
|
|
360
|
+
</div>
|
|
361
|
+
);
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
|
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { Badge } from '@/components/ui/badge';
|
|
4
|
+
import { Button } from '@/components/ui/button';
|
|
5
|
+
import {
|
|
6
|
+
Card,
|
|
7
|
+
CardContent,
|
|
8
|
+
CardDescription,
|
|
9
|
+
CardHeader,
|
|
10
|
+
CardTitle,
|
|
11
|
+
} from '@/components/ui/card';
|
|
12
|
+
import { Input } from '@/components/ui/input';
|
|
13
|
+
import { Label } from '@/components/ui/label';
|
|
14
|
+
import { Money } from '@/components/ui/money';
|
|
15
|
+
import { PageHeader } from '@/components/ui/page-header';
|
|
16
|
+
import {
|
|
17
|
+
Select,
|
|
18
|
+
SelectContent,
|
|
19
|
+
SelectItem,
|
|
20
|
+
SelectTrigger,
|
|
21
|
+
SelectValue,
|
|
22
|
+
} from '@/components/ui/select';
|
|
23
|
+
import {
|
|
24
|
+
Sheet,
|
|
25
|
+
SheetContent,
|
|
26
|
+
SheetDescription,
|
|
27
|
+
SheetHeader,
|
|
28
|
+
SheetTitle,
|
|
29
|
+
SheetTrigger,
|
|
30
|
+
} from '@/components/ui/sheet';
|
|
31
|
+
import {
|
|
32
|
+
Building2,
|
|
33
|
+
Eye,
|
|
34
|
+
Landmark,
|
|
35
|
+
PiggyBank,
|
|
36
|
+
Plus,
|
|
37
|
+
RefreshCw,
|
|
38
|
+
TrendingUp,
|
|
39
|
+
Upload,
|
|
40
|
+
Wallet,
|
|
41
|
+
} from 'lucide-react';
|
|
42
|
+
import Link from 'next/link';
|
|
43
|
+
import { useFinanceData } from '../../_lib/use-finance-data';
|
|
44
|
+
|
|
45
|
+
const tipoConfig = {
|
|
46
|
+
corrente: { label: 'Conta Corrente', icon: Building2 },
|
|
47
|
+
poupanca: { label: 'Poupança', icon: PiggyBank },
|
|
48
|
+
investimento: { label: 'Investimento', icon: TrendingUp },
|
|
49
|
+
caixa: { label: 'Caixa Físico', icon: Wallet },
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
function NovaContaSheet() {
|
|
53
|
+
return (
|
|
54
|
+
<Sheet>
|
|
55
|
+
<SheetTrigger asChild>
|
|
56
|
+
<Button>
|
|
57
|
+
<Plus className="mr-2 h-4 w-4" />
|
|
58
|
+
Nova Conta
|
|
59
|
+
</Button>
|
|
60
|
+
</SheetTrigger>
|
|
61
|
+
<SheetContent className="w-full sm:max-w-lg">
|
|
62
|
+
<SheetHeader>
|
|
63
|
+
<SheetTitle>Nova Conta Bancária</SheetTitle>
|
|
64
|
+
<SheetDescription>Cadastre uma nova conta bancária.</SheetDescription>
|
|
65
|
+
</SheetHeader>
|
|
66
|
+
<form className="mt-6 space-y-4">
|
|
67
|
+
<div className="grid gap-4">
|
|
68
|
+
<div className="space-y-2">
|
|
69
|
+
<Label htmlFor="banco">Banco</Label>
|
|
70
|
+
<Input id="banco" placeholder="Nome do banco" />
|
|
71
|
+
</div>
|
|
72
|
+
<div className="grid grid-cols-2 gap-4">
|
|
73
|
+
<div className="space-y-2">
|
|
74
|
+
<Label htmlFor="agencia">Agência</Label>
|
|
75
|
+
<Input id="agencia" placeholder="0000" />
|
|
76
|
+
</div>
|
|
77
|
+
<div className="space-y-2">
|
|
78
|
+
<Label htmlFor="conta">Conta</Label>
|
|
79
|
+
<Input id="conta" placeholder="00000-0" />
|
|
80
|
+
</div>
|
|
81
|
+
</div>
|
|
82
|
+
<div className="space-y-2">
|
|
83
|
+
<Label htmlFor="tipo">Tipo</Label>
|
|
84
|
+
<Select>
|
|
85
|
+
<SelectTrigger>
|
|
86
|
+
<SelectValue placeholder="Selecione..." />
|
|
87
|
+
</SelectTrigger>
|
|
88
|
+
<SelectContent>
|
|
89
|
+
<SelectItem value="corrente">Conta Corrente</SelectItem>
|
|
90
|
+
<SelectItem value="poupanca">Poupança</SelectItem>
|
|
91
|
+
<SelectItem value="investimento">Investimento</SelectItem>
|
|
92
|
+
<SelectItem value="caixa">Caixa Físico</SelectItem>
|
|
93
|
+
</SelectContent>
|
|
94
|
+
</Select>
|
|
95
|
+
</div>
|
|
96
|
+
<div className="space-y-2">
|
|
97
|
+
<Label htmlFor="descricao">Descrição</Label>
|
|
98
|
+
<Input id="descricao" placeholder="Ex: Conta Principal" />
|
|
99
|
+
</div>
|
|
100
|
+
<div className="space-y-2">
|
|
101
|
+
<Label htmlFor="saldoInicial">Saldo Inicial</Label>
|
|
102
|
+
<Input
|
|
103
|
+
id="saldoInicial"
|
|
104
|
+
type="number"
|
|
105
|
+
placeholder="0,00"
|
|
106
|
+
step="0.01"
|
|
107
|
+
/>
|
|
108
|
+
</div>
|
|
109
|
+
</div>
|
|
110
|
+
<div className="flex justify-end gap-2 pt-4">
|
|
111
|
+
<Button type="button" variant="outline">
|
|
112
|
+
Cancelar
|
|
113
|
+
</Button>
|
|
114
|
+
<Button type="submit">Salvar</Button>
|
|
115
|
+
</div>
|
|
116
|
+
</form>
|
|
117
|
+
</SheetContent>
|
|
118
|
+
</Sheet>
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export default function ContasBancariasPage() {
|
|
123
|
+
const { data } = useFinanceData();
|
|
124
|
+
const { contasBancarias } = data;
|
|
125
|
+
|
|
126
|
+
const saldoTotal = contasBancarias
|
|
127
|
+
.filter((c) => c.ativo)
|
|
128
|
+
.reduce((acc, c) => acc + c.saldoAtual, 0);
|
|
129
|
+
|
|
130
|
+
const saldoConciliadoTotal = contasBancarias
|
|
131
|
+
.filter((c) => c.ativo)
|
|
132
|
+
.reduce((acc, c) => acc + c.saldoConciliado, 0);
|
|
133
|
+
|
|
134
|
+
return (
|
|
135
|
+
<div className="space-y-6">
|
|
136
|
+
<PageHeader
|
|
137
|
+
title="Contas Bancárias"
|
|
138
|
+
description="Gerencie suas contas bancárias"
|
|
139
|
+
breadcrumbs={[
|
|
140
|
+
{ label: 'Caixa e Bancos', href: '/finance/cash-and-banks/bank-accounts' },
|
|
141
|
+
{ label: 'Contas Bancárias' },
|
|
142
|
+
]}
|
|
143
|
+
actions={<NovaContaSheet />}
|
|
144
|
+
/>
|
|
145
|
+
|
|
146
|
+
<div className="grid gap-4 md:grid-cols-2">
|
|
147
|
+
<Card>
|
|
148
|
+
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
|
|
149
|
+
<CardTitle className="text-sm font-medium">Saldo Total</CardTitle>
|
|
150
|
+
<Landmark className="h-4 w-4 text-muted-foreground" />
|
|
151
|
+
</CardHeader>
|
|
152
|
+
<CardContent>
|
|
153
|
+
<div className="text-2xl font-bold">
|
|
154
|
+
<Money value={saldoTotal} />
|
|
155
|
+
</div>
|
|
156
|
+
<p className="text-xs text-muted-foreground">
|
|
157
|
+
{contasBancarias.filter((c) => c.ativo).length} contas ativas
|
|
158
|
+
</p>
|
|
159
|
+
</CardContent>
|
|
160
|
+
</Card>
|
|
161
|
+
<Card>
|
|
162
|
+
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
|
|
163
|
+
<CardTitle className="text-sm font-medium">
|
|
164
|
+
Saldo Conciliado
|
|
165
|
+
</CardTitle>
|
|
166
|
+
<RefreshCw className="h-4 w-4 text-muted-foreground" />
|
|
167
|
+
</CardHeader>
|
|
168
|
+
<CardContent>
|
|
169
|
+
<div className="text-2xl font-bold">
|
|
170
|
+
<Money value={saldoConciliadoTotal} />
|
|
171
|
+
</div>
|
|
172
|
+
<p className="text-xs text-muted-foreground">
|
|
173
|
+
Diferença: <Money value={saldoTotal - saldoConciliadoTotal} />
|
|
174
|
+
</p>
|
|
175
|
+
</CardContent>
|
|
176
|
+
</Card>
|
|
177
|
+
</div>
|
|
178
|
+
|
|
179
|
+
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-3">
|
|
180
|
+
{contasBancarias.map((conta) => {
|
|
181
|
+
const tipo =
|
|
182
|
+
tipoConfig[conta.tipo as keyof typeof tipoConfig] ||
|
|
183
|
+
tipoConfig.corrente;
|
|
184
|
+
const TipoIcon = tipo.icon;
|
|
185
|
+
const diferenca = conta.saldoAtual - conta.saldoConciliado;
|
|
186
|
+
|
|
187
|
+
return (
|
|
188
|
+
<Card key={conta.id} className={!conta.ativo ? 'opacity-60' : ''}>
|
|
189
|
+
<CardHeader>
|
|
190
|
+
<div className="flex items-center justify-between">
|
|
191
|
+
<div className="flex items-center gap-2">
|
|
192
|
+
<div className="flex h-10 w-10 items-center justify-center rounded-lg bg-muted">
|
|
193
|
+
<TipoIcon className="h-5 w-5" />
|
|
194
|
+
</div>
|
|
195
|
+
<div>
|
|
196
|
+
<CardTitle className="text-base">{conta.banco}</CardTitle>
|
|
197
|
+
<CardDescription>
|
|
198
|
+
{conta.agencia !== '-'
|
|
199
|
+
? `Ag: ${conta.agencia} | Cc: ${conta.conta}`
|
|
200
|
+
: conta.descricao}
|
|
201
|
+
</CardDescription>
|
|
202
|
+
</div>
|
|
203
|
+
</div>
|
|
204
|
+
{!conta.ativo && (
|
|
205
|
+
<Badge variant="outline" className="text-muted-foreground">
|
|
206
|
+
Inativa
|
|
207
|
+
</Badge>
|
|
208
|
+
)}
|
|
209
|
+
</div>
|
|
210
|
+
</CardHeader>
|
|
211
|
+
<CardContent>
|
|
212
|
+
<div className="space-y-3">
|
|
213
|
+
<div>
|
|
214
|
+
<p className="text-sm text-muted-foreground">Saldo Atual</p>
|
|
215
|
+
<p className="text-2xl font-bold">
|
|
216
|
+
<Money value={conta.saldoAtual} />
|
|
217
|
+
</p>
|
|
218
|
+
</div>
|
|
219
|
+
<div className="flex items-center justify-between text-sm">
|
|
220
|
+
<span className="text-muted-foreground">
|
|
221
|
+
Saldo Conciliado
|
|
222
|
+
</span>
|
|
223
|
+
<Money value={conta.saldoConciliado} />
|
|
224
|
+
</div>
|
|
225
|
+
{diferenca !== 0 && (
|
|
226
|
+
<div className="flex items-center justify-between text-sm">
|
|
227
|
+
<span className="text-muted-foreground">Diferença</span>
|
|
228
|
+
<span
|
|
229
|
+
className={
|
|
230
|
+
diferenca > 0 ? 'text-green-600' : 'text-red-600'
|
|
231
|
+
}
|
|
232
|
+
>
|
|
233
|
+
<Money value={diferenca} showSign />
|
|
234
|
+
</span>
|
|
235
|
+
</div>
|
|
236
|
+
)}
|
|
237
|
+
<div className="flex gap-2 pt-2">
|
|
238
|
+
<Button
|
|
239
|
+
variant="outline"
|
|
240
|
+
size="sm"
|
|
241
|
+
className="flex-1 bg-transparent"
|
|
242
|
+
asChild
|
|
243
|
+
>
|
|
244
|
+
<Link href="/finance/cash-and-banks/statements">
|
|
245
|
+
<Eye className="mr-2 h-4 w-4" />
|
|
246
|
+
Extrato
|
|
247
|
+
</Link>
|
|
248
|
+
</Button>
|
|
249
|
+
<Button
|
|
250
|
+
variant="outline"
|
|
251
|
+
size="sm"
|
|
252
|
+
className="flex-1 bg-transparent"
|
|
253
|
+
asChild
|
|
254
|
+
>
|
|
255
|
+
<Link href="/finance/cash-and-banks/bank-reconciliation">
|
|
256
|
+
<RefreshCw className="mr-2 h-4 w-4" />
|
|
257
|
+
Conciliar
|
|
258
|
+
</Link>
|
|
259
|
+
</Button>
|
|
260
|
+
<Button variant="outline" size="sm">
|
|
261
|
+
<Upload className="h-4 w-4" />
|
|
262
|
+
</Button>
|
|
263
|
+
</div>
|
|
264
|
+
</div>
|
|
265
|
+
</CardContent>
|
|
266
|
+
</Card>
|
|
267
|
+
);
|
|
268
|
+
})}
|
|
269
|
+
</div>
|
|
270
|
+
</div>
|
|
271
|
+
);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
|