@hed-hog/finance 0.0.300 → 0.0.301

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 (39) hide show
  1. package/dist/finance.contract-activated.subscriber.d.ts +24 -0
  2. package/dist/finance.contract-activated.subscriber.d.ts.map +1 -0
  3. package/dist/finance.contract-activated.subscriber.js +519 -0
  4. package/dist/finance.contract-activated.subscriber.js.map +1 -0
  5. package/dist/finance.contract-activated.subscriber.spec.d.ts +2 -0
  6. package/dist/finance.contract-activated.subscriber.spec.d.ts.map +1 -0
  7. package/dist/finance.contract-activated.subscriber.spec.js +302 -0
  8. package/dist/finance.contract-activated.subscriber.spec.js.map +1 -0
  9. package/dist/finance.module.d.ts.map +1 -1
  10. package/dist/finance.module.js +6 -1
  11. package/dist/finance.module.js.map +1 -1
  12. package/hedhog/frontend/app/_components/finance-layout.tsx.ejs +108 -0
  13. package/hedhog/frontend/app/accounts-payable/approvals/page.tsx.ejs +91 -106
  14. package/hedhog/frontend/app/accounts-payable/installments/page.tsx.ejs +1306 -1145
  15. package/hedhog/frontend/app/accounts-receivable/collections-default/page.tsx.ejs +288 -268
  16. package/hedhog/frontend/app/accounts-receivable/installments/page.tsx.ejs +491 -351
  17. package/hedhog/frontend/app/administration/audit-logs/page.tsx.ejs +157 -173
  18. package/hedhog/frontend/app/administration/categories/page.tsx.ejs +44 -62
  19. package/hedhog/frontend/app/administration/cost-centers/page.tsx.ejs +62 -80
  20. package/hedhog/frontend/app/administration/period-close/page.tsx.ejs +151 -170
  21. package/hedhog/frontend/app/cash-and-banks/bank-accounts/page.tsx.ejs +332 -286
  22. package/hedhog/frontend/app/cash-and-banks/bank-reconciliation/page.tsx.ejs +204 -226
  23. package/hedhog/frontend/app/cash-and-banks/statements/page.tsx.ejs +122 -140
  24. package/hedhog/frontend/app/cash-and-banks/transfers/page.tsx.ejs +32 -49
  25. package/hedhog/frontend/app/planning/cash-flow-forecast/page.tsx.ejs +84 -108
  26. package/hedhog/frontend/app/planning/receivables-calendar/page.tsx.ejs +53 -70
  27. package/hedhog/frontend/app/planning/scenarios/page.tsx.ejs +98 -95
  28. package/hedhog/frontend/app/reports/actual-vs-forecast/page.tsx.ejs +100 -125
  29. package/hedhog/frontend/app/reports/aging-default/page.tsx.ejs +77 -105
  30. package/hedhog/frontend/app/reports/cash-position/page.tsx.ejs +99 -134
  31. package/hedhog/frontend/app/reports/overview-results/page.tsx.ejs +147 -182
  32. package/hedhog/frontend/app/reports/top-customers/page.tsx.ejs +49 -61
  33. package/hedhog/frontend/app/reports/top-operational-expenses/page.tsx.ejs +49 -67
  34. package/hedhog/frontend/messages/en.json +176 -68
  35. package/hedhog/frontend/messages/pt.json +176 -68
  36. package/package.json +7 -6
  37. package/src/finance.contract-activated.subscriber.spec.ts +392 -0
  38. package/src/finance.contract-activated.subscriber.ts +780 -0
  39. package/src/finance.module.ts +6 -1
@@ -1,14 +1,9 @@
1
1
  'use client';
2
2
 
3
+ import { FinancePageSection } from '@/app/(app)/(libraries)/finance/_components/finance-layout';
3
4
  import { Page, PageHeader } from '@/components/entity-list';
4
5
  import { Badge } from '@/components/ui/badge';
5
- import {
6
- Card,
7
- CardContent,
8
- CardDescription,
9
- CardHeader,
10
- CardTitle,
11
- } from '@/components/ui/card';
6
+ import { KpiCardsGrid } from '@/components/ui/kpi-cards-grid';
12
7
  import { Money } from '@/components/ui/money';
13
8
  import {
14
9
  Select,
@@ -115,6 +110,36 @@ export default function AgingDefaultReportPage() {
115
110
  value: totals.bucket90plus,
116
111
  },
117
112
  ];
113
+ const summaryCards = [
114
+ {
115
+ key: 'totalDefault',
116
+ title: t('cards.totalDefault'),
117
+ value: <Money value={totals.total} />,
118
+ icon: AlertTriangle,
119
+ layout: 'compact' as const,
120
+ iconContainerClassName: 'bg-red-500/10 text-red-700',
121
+ accentClassName: 'from-red-500/20 via-rose-500/10 to-transparent',
122
+ },
123
+ {
124
+ key: 'lateClients',
125
+ title: t('cards.lateClients'),
126
+ value: totals.clients,
127
+ layout: 'compact' as const,
128
+ },
129
+ {
130
+ key: 'over90Days',
131
+ title: t('cards.over90Days'),
132
+ value: <Money value={totals.bucket90plus} />,
133
+ layout: 'compact' as const,
134
+ valueClassName: 'text-destructive',
135
+ },
136
+ {
137
+ key: 'defaultRate',
138
+ title: t('cards.defaultRate'),
139
+ value: `${toPercent(Number(data.kpis?.inadimplencia || 0)).toFixed(1)}%`,
140
+ layout: 'compact' as const,
141
+ },
142
+ ];
118
143
 
119
144
  return (
120
145
  <Page>
@@ -134,7 +159,7 @@ export default function AgingDefaultReportPage() {
134
159
  onValueChange={setHorizonte}
135
160
  disabled={isFetching}
136
161
  >
137
- <SelectTrigger className="w-[180px]">
162
+ <SelectTrigger className="w-45">
138
163
  <SelectValue placeholder={t('filters.horizon')} />
139
164
  </SelectTrigger>
140
165
  <SelectContent>
@@ -150,7 +175,7 @@ export default function AgingDefaultReportPage() {
150
175
  onValueChange={(value) => setCenario(value as Scenario)}
151
176
  disabled={isFetching}
152
177
  >
153
- <SelectTrigger className="w-[180px]">
178
+ <SelectTrigger className="w-45">
154
179
  <SelectValue placeholder={t('filters.scenario')} />
155
180
  </SelectTrigger>
156
181
  <SelectContent>
@@ -171,102 +196,49 @@ export default function AgingDefaultReportPage() {
171
196
  ) : null}
172
197
  </div>
173
198
 
174
- <div className="grid gap-4 md:grid-cols-4">
175
- <Card>
176
- <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
177
- <CardTitle className="text-sm font-medium">
178
- {t('cards.totalDefault')}
179
- </CardTitle>
180
- <AlertTriangle className="h-4 w-4 text-destructive" />
181
- </CardHeader>
182
- <CardContent>
183
- <div className="text-2xl font-bold">
184
- <Money value={totals.total} />
185
- </div>
186
- </CardContent>
187
- </Card>
188
-
189
- <Card>
190
- <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
191
- <CardTitle className="text-sm font-medium">
192
- {t('cards.lateClients')}
193
- </CardTitle>
194
- </CardHeader>
195
- <CardContent>
196
- <div className="text-2xl font-bold">{totals.clients}</div>
197
- </CardContent>
198
- </Card>
199
-
200
- <Card>
201
- <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
202
- <CardTitle className="text-sm font-medium">
203
- {t('cards.over90Days')}
204
- </CardTitle>
205
- </CardHeader>
206
- <CardContent>
207
- <div className="text-2xl font-bold text-destructive">
208
- <Money value={totals.bucket90plus} />
209
- </div>
210
- </CardContent>
211
- </Card>
212
-
213
- <Card>
214
- <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
215
- <CardTitle className="text-sm font-medium">
216
- {t('cards.defaultRate')}
217
- </CardTitle>
218
- </CardHeader>
219
- <CardContent>
220
- <div className="text-2xl font-bold">
221
- {toPercent(Number(data.kpis?.inadimplencia || 0)).toFixed(1)}%
222
- </div>
223
- </CardContent>
224
- </Card>
225
- </div>
199
+ <KpiCardsGrid items={summaryCards} columns={4} />
226
200
 
227
- <Card>
228
- <CardHeader>
229
- <CardTitle>{t('chart.title')}</CardTitle>
230
- <CardDescription>{t('chart.description')}</CardDescription>
231
- </CardHeader>
232
- <CardContent>
233
- <ResponsiveContainer width="100%" height={280}>
234
- <BarChart data={chartData}>
235
- <CartesianGrid strokeDasharray="3 3" className="stroke-muted" />
236
- <XAxis dataKey="name" tick={{ fontSize: 12 }} />
237
- <YAxis
238
- tick={{ fontSize: 12 }}
239
- tickFormatter={(value) => `${(value / 1000).toFixed(0)}k`}
240
- />
241
- <Tooltip
242
- formatter={(value: number) =>
243
- new Intl.NumberFormat('pt-BR', {
244
- style: 'currency',
245
- currency: 'BRL',
246
- }).format(value)
247
- }
248
- contentStyle={{
249
- backgroundColor: 'hsl(var(--background))',
250
- border: '1px solid hsl(var(--border))',
251
- borderRadius: '8px',
252
- }}
253
- />
254
- <Bar
255
- dataKey="value"
256
- radius={[4, 4, 0, 0]}
257
- fill="hsl(var(--primary))"
258
- />
259
- </BarChart>
260
- </ResponsiveContainer>
261
- </CardContent>
262
- </Card>
201
+ <FinancePageSection
202
+ title={t('chart.title')}
203
+ description={t('chart.description')}
204
+ contentClassName="p-4 sm:p-5"
205
+ >
206
+ <ResponsiveContainer width="100%" height={280}>
207
+ <BarChart data={chartData}>
208
+ <CartesianGrid strokeDasharray="3 3" className="stroke-muted" />
209
+ <XAxis dataKey="name" tick={{ fontSize: 12 }} />
210
+ <YAxis
211
+ tick={{ fontSize: 12 }}
212
+ tickFormatter={(value) => `${(value / 1000).toFixed(0)}k`}
213
+ />
214
+ <Tooltip
215
+ formatter={(value: number) =>
216
+ new Intl.NumberFormat('pt-BR', {
217
+ style: 'currency',
218
+ currency: 'BRL',
219
+ }).format(value)
220
+ }
221
+ contentStyle={{
222
+ backgroundColor: 'hsl(var(--background))',
223
+ border: '1px solid hsl(var(--border))',
224
+ borderRadius: '8px',
225
+ }}
226
+ />
227
+ <Bar
228
+ dataKey="value"
229
+ radius={[4, 4, 0, 0]}
230
+ fill="hsl(var(--primary))"
231
+ />
232
+ </BarChart>
233
+ </ResponsiveContainer>
234
+ </FinancePageSection>
263
235
 
264
- <Card>
265
- <CardHeader>
266
- <CardTitle>{t('table.title')}</CardTitle>
267
- <CardDescription>{t('table.description')}</CardDescription>
268
- </CardHeader>
269
- <CardContent>
236
+ <FinancePageSection
237
+ title={t('table.title')}
238
+ description={t('table.description')}
239
+ contentClassName="p-4 sm:p-5"
240
+ >
241
+ <div className="overflow-x-auto">
270
242
  <Table>
271
243
  <TableHeader>
272
244
  <TableRow>
@@ -361,8 +333,8 @@ export default function AgingDefaultReportPage() {
361
333
  )}
362
334
  </TableBody>
363
335
  </Table>
364
- </CardContent>
365
- </Card>
336
+ </div>
337
+ </FinancePageSection>
366
338
  </Page>
367
339
  );
368
340
  }
@@ -1,15 +1,10 @@
1
1
  'use client';
2
2
 
3
+ import { FinancePageSection } from '@/app/(app)/(libraries)/finance/_components/finance-layout';
3
4
  import { Page, PageHeader } from '@/components/entity-list';
4
5
  import { Badge } from '@/components/ui/badge';
5
6
  import { Button } from '@/components/ui/button';
6
- import {
7
- Card,
8
- CardContent,
9
- CardDescription,
10
- CardHeader,
11
- CardTitle,
12
- } from '@/components/ui/card';
7
+ import { KpiCardsGrid } from '@/components/ui/kpi-cards-grid';
13
8
  import { Money } from '@/components/ui/money';
14
9
  import {
15
10
  Select,
@@ -167,6 +162,51 @@ export default function CashPositionReportPage() {
167
162
  .slice(0, 12),
168
163
  [filteredExtratos]
169
164
  );
165
+ const summaryCards = [
166
+ {
167
+ key: 'currentBalance',
168
+ title: t('cards.currentBalance'),
169
+ value: <Money value={totals.saldoAtual} />,
170
+ icon: Wallet,
171
+ layout: 'compact' as const,
172
+ },
173
+ {
174
+ key: 'reconciledBalance',
175
+ title: t('cards.reconciledBalance'),
176
+ value: <Money value={totals.saldoConciliado} />,
177
+ icon: Wallet,
178
+ layout: 'compact' as const,
179
+ },
180
+ {
181
+ key: 'inflows',
182
+ title: t('cards.inflows'),
183
+ value: <Money value={totals.entradas} />,
184
+ icon: TrendingUp,
185
+ layout: 'compact' as const,
186
+ valueClassName: 'text-green-600',
187
+ iconContainerClassName: 'bg-green-500/10 text-green-700',
188
+ accentClassName: 'from-green-500/20 via-emerald-500/10 to-transparent',
189
+ },
190
+ {
191
+ key: 'outflows',
192
+ title: t('cards.outflows'),
193
+ value: <Money value={totals.saidas} />,
194
+ icon: TrendingDown,
195
+ layout: 'compact' as const,
196
+ valueClassName: 'text-red-600',
197
+ iconContainerClassName: 'bg-red-500/10 text-red-700',
198
+ accentClassName: 'from-red-500/20 via-rose-500/10 to-transparent',
199
+ },
200
+ {
201
+ key: 'netMovement',
202
+ title: t('cards.netMovement'),
203
+ value: <Money value={totals.movimentacaoLiquida} showSign />,
204
+ icon: totals.movimentacaoLiquida >= 0 ? TrendingUp : TrendingDown,
205
+ layout: 'compact' as const,
206
+ valueClassName:
207
+ totals.movimentacaoLiquida >= 0 ? 'text-green-600' : 'text-red-600',
208
+ },
209
+ ];
170
210
 
171
211
  return (
172
212
  <Page>
@@ -192,7 +232,7 @@ export default function CashPositionReportPage() {
192
232
  onValueChange={setHorizonte}
193
233
  disabled={isFetching}
194
234
  >
195
- <SelectTrigger className="w-[180px]">
235
+ <SelectTrigger className="w-45">
196
236
  <SelectValue placeholder={t('filters.horizon')} />
197
237
  </SelectTrigger>
198
238
  <SelectContent>
@@ -209,7 +249,7 @@ export default function CashPositionReportPage() {
209
249
  onValueChange={(value) => setCenario(value as Scenario)}
210
250
  disabled={isFetching}
211
251
  >
212
- <SelectTrigger className="w-[180px]">
252
+ <SelectTrigger className="w-45">
213
253
  <SelectValue placeholder={t('filters.scenario')} />
214
254
  </SelectTrigger>
215
255
  <SelectContent>
@@ -249,130 +289,55 @@ export default function CashPositionReportPage() {
249
289
  ) : null}
250
290
  </div>
251
291
 
252
- <div className="grid gap-4 md:grid-cols-5">
253
- <Card>
254
- <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
255
- <CardTitle className="text-sm font-medium">
256
- {t('cards.currentBalance')}
257
- </CardTitle>
258
- <Wallet className="h-4 w-4 text-muted-foreground" />
259
- </CardHeader>
260
- <CardContent>
261
- <div className="text-2xl font-bold">
262
- <Money value={totals.saldoAtual} />
263
- </div>
264
- </CardContent>
265
- </Card>
266
-
267
- <Card>
268
- <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
269
- <CardTitle className="text-sm font-medium">
270
- {t('cards.reconciledBalance')}
271
- </CardTitle>
272
- <Wallet className="h-4 w-4 text-muted-foreground" />
273
- </CardHeader>
274
- <CardContent>
275
- <div className="text-2xl font-bold">
276
- <Money value={totals.saldoConciliado} />
277
- </div>
278
- </CardContent>
279
- </Card>
280
-
281
- <Card>
282
- <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
283
- <CardTitle className="text-sm font-medium">
284
- {t('cards.inflows')}
285
- </CardTitle>
286
- <TrendingUp className="h-4 w-4 text-green-600" />
287
- </CardHeader>
288
- <CardContent>
289
- <div className="text-2xl font-bold text-green-600">
290
- <Money value={totals.entradas} />
291
- </div>
292
- </CardContent>
293
- </Card>
294
-
295
- <Card>
296
- <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
297
- <CardTitle className="text-sm font-medium">
298
- {t('cards.outflows')}
299
- </CardTitle>
300
- <TrendingDown className="h-4 w-4 text-red-600" />
301
- </CardHeader>
302
- <CardContent>
303
- <div className="text-2xl font-bold text-red-600">
304
- <Money value={totals.saidas} />
305
- </div>
306
- </CardContent>
307
- </Card>
308
-
309
- <Card>
310
- <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
311
- <CardTitle className="text-sm font-medium">
312
- {t('cards.netMovement')}
313
- </CardTitle>
314
- {totals.movimentacaoLiquida >= 0 ? (
315
- <TrendingUp className="h-4 w-4 text-green-600" />
316
- ) : (
317
- <TrendingDown className="h-4 w-4 text-red-600" />
318
- )}
319
- </CardHeader>
320
- <CardContent>
321
- <div
322
- className={`text-2xl font-bold ${totals.movimentacaoLiquida >= 0 ? 'text-green-600' : 'text-red-600'}`}
323
- >
324
- <Money value={totals.movimentacaoLiquida} showSign />
325
- </div>
326
- </CardContent>
327
- </Card>
328
- </div>
292
+ <KpiCardsGrid
293
+ items={summaryCards}
294
+ className="grid gap-4 md:grid-cols-2 xl:grid-cols-5"
295
+ />
329
296
 
330
- <Card>
331
- <CardHeader>
332
- <CardTitle>{t('chart.title')}</CardTitle>
333
- <CardDescription>{t('chart.description')}</CardDescription>
334
- </CardHeader>
335
- <CardContent>
336
- <ResponsiveContainer width="100%" height={320}>
337
- <AreaChart data={chartData}>
338
- <CartesianGrid strokeDasharray="3 3" className="stroke-muted" />
339
- <XAxis dataKey="label" tick={{ fontSize: 12 }} />
340
- <YAxis
341
- tick={{ fontSize: 12 }}
342
- tickFormatter={(value) => `${(value / 1000).toFixed(0)}k`}
343
- />
344
- <Tooltip
345
- formatter={(value: number) =>
346
- new Intl.NumberFormat('pt-BR', {
347
- style: 'currency',
348
- currency: 'BRL',
349
- }).format(value)
350
- }
351
- contentStyle={{
352
- backgroundColor: 'hsl(var(--background))',
353
- border: '1px solid hsl(var(--border))',
354
- borderRadius: '8px',
355
- }}
356
- />
357
- <Area
358
- type="monotone"
359
- dataKey="saldoAcumulado"
360
- name={t('chart.balanceLabel')}
361
- stroke="hsl(var(--primary))"
362
- fill="hsl(var(--primary))"
363
- fillOpacity={0.2}
364
- />
365
- </AreaChart>
366
- </ResponsiveContainer>
367
- </CardContent>
368
- </Card>
369
-
370
- <Card>
371
- <CardHeader>
372
- <CardTitle>{t('table.title')}</CardTitle>
373
- <CardDescription>{t('table.description')}</CardDescription>
374
- </CardHeader>
375
- <CardContent>
297
+ <FinancePageSection
298
+ title={t('chart.title')}
299
+ description={t('chart.description')}
300
+ contentClassName="p-4 sm:p-5"
301
+ >
302
+ <ResponsiveContainer width="100%" height={320}>
303
+ <AreaChart data={chartData}>
304
+ <CartesianGrid strokeDasharray="3 3" className="stroke-muted" />
305
+ <XAxis dataKey="label" tick={{ fontSize: 12 }} />
306
+ <YAxis
307
+ tick={{ fontSize: 12 }}
308
+ tickFormatter={(value) => `${(value / 1000).toFixed(0)}k`}
309
+ />
310
+ <Tooltip
311
+ formatter={(value: number) =>
312
+ new Intl.NumberFormat('pt-BR', {
313
+ style: 'currency',
314
+ currency: 'BRL',
315
+ }).format(value)
316
+ }
317
+ contentStyle={{
318
+ backgroundColor: 'hsl(var(--background))',
319
+ border: '1px solid hsl(var(--border))',
320
+ borderRadius: '8px',
321
+ }}
322
+ />
323
+ <Area
324
+ type="monotone"
325
+ dataKey="saldoAcumulado"
326
+ name={t('chart.balanceLabel')}
327
+ stroke="hsl(var(--primary))"
328
+ fill="hsl(var(--primary))"
329
+ fillOpacity={0.2}
330
+ />
331
+ </AreaChart>
332
+ </ResponsiveContainer>
333
+ </FinancePageSection>
334
+
335
+ <FinancePageSection
336
+ title={t('table.title')}
337
+ description={t('table.description')}
338
+ contentClassName="p-4 sm:p-5"
339
+ >
340
+ <div className="overflow-x-auto">
376
341
  <Table>
377
342
  <TableHeader>
378
343
  <TableRow>
@@ -425,8 +390,8 @@ export default function CashPositionReportPage() {
425
390
  )}
426
391
  </TableBody>
427
392
  </Table>
428
- </CardContent>
429
- </Card>
393
+ </div>
394
+ </FinancePageSection>
430
395
  </Page>
431
396
  );
432
397
  }