@hed-hog/finance 0.0.300 → 0.0.302

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 (41) 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/data/menu.yaml +0 -17
  13. package/hedhog/frontend/app/_components/finance-layout.tsx.ejs +108 -0
  14. package/hedhog/frontend/app/accounts-payable/approvals/page.tsx.ejs +51 -69
  15. package/hedhog/frontend/app/accounts-payable/installments/page.tsx.ejs +1312 -1138
  16. package/hedhog/frontend/app/accounts-receivable/collections-default/page.tsx.ejs +288 -268
  17. package/hedhog/frontend/app/accounts-receivable/installments/page.tsx.ejs +1175 -1016
  18. package/hedhog/frontend/app/administration/audit-logs/page.tsx.ejs +157 -173
  19. package/hedhog/frontend/app/administration/categories/page.tsx.ejs +44 -62
  20. package/hedhog/frontend/app/administration/cost-centers/page.tsx.ejs +62 -80
  21. package/hedhog/frontend/app/administration/period-close/page.tsx.ejs +151 -170
  22. package/hedhog/frontend/app/cash-and-banks/bank-accounts/page.tsx.ejs +369 -322
  23. package/hedhog/frontend/app/cash-and-banks/bank-reconciliation/page.tsx.ejs +204 -226
  24. package/hedhog/frontend/app/cash-and-banks/statements/page.tsx.ejs +122 -140
  25. package/hedhog/frontend/app/cash-and-banks/transfers/page.tsx.ejs +31 -49
  26. package/hedhog/frontend/app/page.tsx.ejs +3 -370
  27. package/hedhog/frontend/app/planning/cash-flow-forecast/page.tsx.ejs +150 -182
  28. package/hedhog/frontend/app/planning/receivables-calendar/page.tsx.ejs +52 -70
  29. package/hedhog/frontend/app/planning/scenarios/page.tsx.ejs +101 -95
  30. package/hedhog/frontend/app/reports/actual-vs-forecast/page.tsx.ejs +100 -125
  31. package/hedhog/frontend/app/reports/aging-default/page.tsx.ejs +77 -105
  32. package/hedhog/frontend/app/reports/cash-position/page.tsx.ejs +99 -134
  33. package/hedhog/frontend/app/reports/overview-results/page.tsx.ejs +147 -182
  34. package/hedhog/frontend/app/reports/top-customers/page.tsx.ejs +49 -61
  35. package/hedhog/frontend/app/reports/top-operational-expenses/page.tsx.ejs +49 -67
  36. package/hedhog/frontend/messages/en.json +176 -68
  37. package/hedhog/frontend/messages/pt.json +176 -68
  38. package/package.json +6 -5
  39. package/src/finance.contract-activated.subscriber.spec.ts +392 -0
  40. package/src/finance.contract-activated.subscriber.ts +780 -0
  41. package/src/finance.module.ts +6 -1
@@ -1,5 +1,6 @@
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';
@@ -84,109 +85,114 @@ export default function CenariosPage() {
84
85
  ]}
85
86
  />
86
87
 
87
- <div className="grid gap-4 md:grid-cols-3">
88
- {cenarios.map((cenario) => {
89
- const isAtivo = cenario.id === cenarioAtivo;
90
- const Icon =
91
- cenario.nome === 'Otimista'
92
- ? TrendingUp
93
- : cenario.nome === 'Pessimista'
94
- ? TrendingDown
95
- : Target;
88
+ <FinancePageSection
89
+ contentClassName="p-0"
90
+ className="border-none shadow-none"
91
+ >
92
+ <div className="grid gap-4 md:grid-cols-3">
93
+ {cenarios.map((cenario) => {
94
+ const isAtivo = cenario.id === cenarioAtivo;
95
+ const Icon =
96
+ cenario.nome === 'Otimista'
97
+ ? TrendingUp
98
+ : cenario.nome === 'Pessimista'
99
+ ? TrendingDown
100
+ : Target;
96
101
 
97
- return (
98
- <Card
99
- key={cenario.id}
100
- className={`cursor-pointer transition-all ${
101
- isAtivo
102
- ? 'border-primary ring-2 ring-primary/20'
103
- : 'hover:border-muted-foreground/50'
104
- }`}
105
- onClick={() => {
106
- setCenarioAtivo(cenario.id);
107
- setPremissas({
108
- atrasoMedio: cenario.atrasoMedio,
109
- taxaInadimplencia: cenario.taxaInadimplencia,
110
- crescimentoReceita: cenario.crescimentoReceita,
111
- });
112
- }}
113
- >
114
- <CardHeader>
115
- <div className="flex items-center justify-between">
116
- <div className="flex items-center gap-3">
117
- <div
118
- className={`flex h-10 w-10 items-center justify-center rounded-lg ${
119
- cenario.nome === 'Otimista'
120
- ? 'bg-green-100'
121
- : cenario.nome === 'Pessimista'
122
- ? 'bg-red-100'
123
- : 'bg-blue-100'
124
- }`}
125
- >
126
- <Icon
127
- className={`h-5 w-5 ${
102
+ return (
103
+ <Card
104
+ key={cenario.id}
105
+ className={`cursor-pointer transition-all ${
106
+ isAtivo
107
+ ? 'border-primary ring-2 ring-primary/20'
108
+ : 'hover:border-muted-foreground/50'
109
+ }`}
110
+ onClick={() => {
111
+ setCenarioAtivo(cenario.id);
112
+ setPremissas({
113
+ atrasoMedio: cenario.atrasoMedio,
114
+ taxaInadimplencia: cenario.taxaInadimplencia,
115
+ crescimentoReceita: cenario.crescimentoReceita,
116
+ });
117
+ }}
118
+ >
119
+ <CardHeader>
120
+ <div className="flex items-center justify-between">
121
+ <div className="flex items-center gap-3">
122
+ <div
123
+ className={`flex h-10 w-10 items-center justify-center rounded-lg ${
128
124
  cenario.nome === 'Otimista'
129
- ? 'text-green-600'
125
+ ? 'bg-green-100'
130
126
  : cenario.nome === 'Pessimista'
131
- ? 'text-red-600'
132
- : 'text-blue-600'
127
+ ? 'bg-red-100'
128
+ : 'bg-blue-100'
133
129
  }`}
134
- />
130
+ >
131
+ <Icon
132
+ className={`h-5 w-5 ${
133
+ cenario.nome === 'Otimista'
134
+ ? 'text-green-600'
135
+ : cenario.nome === 'Pessimista'
136
+ ? 'text-red-600'
137
+ : 'text-blue-600'
138
+ }`}
139
+ />
140
+ </div>
141
+ <div>
142
+ <CardTitle className="text-base">
143
+ {cenario.nome}
144
+ </CardTitle>
145
+ <CardDescription>{cenario.descricao}</CardDescription>
146
+ </div>
135
147
  </div>
148
+ {isAtivo && (
149
+ <Badge className="bg-primary text-primary-foreground">
150
+ <Check className="mr-1 h-3 w-3" />
151
+ {t('cards.active')}
152
+ </Badge>
153
+ )}
154
+ </div>
155
+ </CardHeader>
156
+ <CardContent>
157
+ <div className="grid grid-cols-3 gap-4 text-center">
136
158
  <div>
137
- <CardTitle className="text-base">
138
- {cenario.nome}
139
- </CardTitle>
140
- <CardDescription>{cenario.descricao}</CardDescription>
159
+ <p className="text-xs text-muted-foreground">
160
+ {t('cards.delay')}
161
+ </p>
162
+ <p className="font-semibold">
163
+ {t('cards.days', { value: cenario.atrasoMedio })}
164
+ </p>
165
+ </div>
166
+ <div>
167
+ <p className="text-xs text-muted-foreground">
168
+ {t('cards.defaultRate')}
169
+ </p>
170
+ <p className="font-semibold">
171
+ {cenario.taxaInadimplencia}%
172
+ </p>
173
+ </div>
174
+ <div>
175
+ <p className="text-xs text-muted-foreground">
176
+ {t('cards.growth')}
177
+ </p>
178
+ <p
179
+ className={`font-semibold ${
180
+ cenario.crescimentoReceita >= 0
181
+ ? 'text-green-600'
182
+ : 'text-red-600'
183
+ }`}
184
+ >
185
+ {cenario.crescimentoReceita > 0 && '+'}
186
+ {cenario.crescimentoReceita}%
187
+ </p>
141
188
  </div>
142
189
  </div>
143
- {isAtivo && (
144
- <Badge className="bg-primary text-primary-foreground">
145
- <Check className="mr-1 h-3 w-3" />
146
- {t('cards.active')}
147
- </Badge>
148
- )}
149
- </div>
150
- </CardHeader>
151
- <CardContent>
152
- <div className="grid grid-cols-3 gap-4 text-center">
153
- <div>
154
- <p className="text-xs text-muted-foreground">
155
- {t('cards.delay')}
156
- </p>
157
- <p className="font-semibold">
158
- {t('cards.days', { value: cenario.atrasoMedio })}
159
- </p>
160
- </div>
161
- <div>
162
- <p className="text-xs text-muted-foreground">
163
- {t('cards.defaultRate')}
164
- </p>
165
- <p className="font-semibold">
166
- {cenario.taxaInadimplencia}%
167
- </p>
168
- </div>
169
- <div>
170
- <p className="text-xs text-muted-foreground">
171
- {t('cards.growth')}
172
- </p>
173
- <p
174
- className={`font-semibold ${
175
- cenario.crescimentoReceita >= 0
176
- ? 'text-green-600'
177
- : 'text-red-600'
178
- }`}
179
- >
180
- {cenario.crescimentoReceita > 0 && '+'}
181
- {cenario.crescimentoReceita}%
182
- </p>
183
- </div>
184
- </div>
185
- </CardContent>
186
- </Card>
187
- );
188
- })}
189
- </div>
190
+ </CardContent>
191
+ </Card>
192
+ );
193
+ })}
194
+ </div>
195
+ </FinancePageSection>
190
196
 
191
197
  <div className="grid gap-6 lg:grid-cols-2">
192
198
  <Card>
@@ -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,
@@ -104,6 +99,47 @@ export default function ActualVsForecastPage() {
104
99
  acuracia,
105
100
  };
106
101
  }, [comparisonData]);
102
+ const summaryCards = [
103
+ {
104
+ key: 'forecast',
105
+ title: t('cards.totalForecast'),
106
+ value: <Money value={totals.previsto} />,
107
+ icon: Target,
108
+ layout: 'compact' as const,
109
+ },
110
+ {
111
+ key: 'actual',
112
+ title: t('cards.totalActual'),
113
+ value: <Money value={totals.realizado} />,
114
+ icon: TrendingUp,
115
+ layout: 'compact' as const,
116
+ iconContainerClassName: 'bg-blue-500/10 text-blue-700',
117
+ accentClassName: 'from-blue-500/20 via-sky-500/10 to-transparent',
118
+ },
119
+ {
120
+ key: 'deviation',
121
+ title: t('cards.totalDeviation'),
122
+ value: <Money value={totals.desvio} showSign />,
123
+ icon: totals.desvio >= 0 ? TrendingUp : TrendingDown,
124
+ layout: 'compact' as const,
125
+ valueClassName: totals.desvio >= 0 ? 'text-green-600' : 'text-red-600',
126
+ iconContainerClassName:
127
+ totals.desvio >= 0
128
+ ? 'bg-green-500/10 text-green-700'
129
+ : 'bg-red-500/10 text-red-700',
130
+ accentClassName:
131
+ totals.desvio >= 0
132
+ ? 'from-green-500/20 via-emerald-500/10 to-transparent'
133
+ : 'from-red-500/20 via-rose-500/10 to-transparent',
134
+ },
135
+ {
136
+ key: 'accuracy',
137
+ title: t('cards.accuracy'),
138
+ value: `${totals.acuracia.toFixed(1)}%`,
139
+ icon: Target,
140
+ layout: 'compact' as const,
141
+ },
142
+ ];
107
143
 
108
144
  return (
109
145
  <Page>
@@ -129,7 +165,7 @@ export default function ActualVsForecastPage() {
129
165
  onValueChange={setHorizonte}
130
166
  disabled={isFetching}
131
167
  >
132
- <SelectTrigger className="w-[180px]">
168
+ <SelectTrigger className="w-45">
133
169
  <SelectValue placeholder={t('filters.horizon')} />
134
170
  </SelectTrigger>
135
171
  <SelectContent>
@@ -145,7 +181,7 @@ export default function ActualVsForecastPage() {
145
181
  onValueChange={(value) => setCenario(value as Scenario)}
146
182
  disabled={isFetching}
147
183
  >
148
- <SelectTrigger className="w-[180px]">
184
+ <SelectTrigger className="w-45">
149
185
  <SelectValue placeholder={t('filters.scenario')} />
150
186
  </SelectTrigger>
151
187
  <SelectContent>
@@ -166,122 +202,61 @@ export default function ActualVsForecastPage() {
166
202
  ) : null}
167
203
  </div>
168
204
 
169
- <div className="grid gap-4 md:grid-cols-4">
170
- <Card>
171
- <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
172
- <CardTitle className="text-sm font-medium">
173
- {t('cards.totalForecast')}
174
- </CardTitle>
175
- <Target className="h-4 w-4 text-muted-foreground" />
176
- </CardHeader>
177
- <CardContent>
178
- <div className="text-2xl font-bold">
179
- <Money value={totals.previsto} />
180
- </div>
181
- </CardContent>
182
- </Card>
183
- <Card>
184
- <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
185
- <CardTitle className="text-sm font-medium">
186
- {t('cards.totalActual')}
187
- </CardTitle>
188
- <TrendingUp className="h-4 w-4 text-muted-foreground" />
189
- </CardHeader>
190
- <CardContent>
191
- <div className="text-2xl font-bold">
192
- <Money value={totals.realizado} />
193
- </div>
194
- </CardContent>
195
- </Card>
196
- <Card>
197
- <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
198
- <CardTitle className="text-sm font-medium">
199
- {t('cards.totalDeviation')}
200
- </CardTitle>
201
- {totals.desvio >= 0 ? (
202
- <TrendingUp className="h-4 w-4 text-green-600" />
203
- ) : (
204
- <TrendingDown className="h-4 w-4 text-red-600" />
205
- )}
206
- </CardHeader>
207
- <CardContent>
208
- <div
209
- className={`text-2xl font-bold ${totals.desvio >= 0 ? 'text-green-600' : 'text-red-600'}`}
210
- >
211
- <Money value={totals.desvio} showSign />
212
- </div>
213
- </CardContent>
214
- </Card>
215
- <Card>
216
- <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
217
- <CardTitle className="text-sm font-medium">
218
- {t('cards.accuracy')}
219
- </CardTitle>
220
- <Target className="h-4 w-4 text-muted-foreground" />
221
- </CardHeader>
222
- <CardContent>
223
- <div className="text-2xl font-bold">
224
- {totals.acuracia.toFixed(1)}%
225
- </div>
226
- </CardContent>
227
- </Card>
228
- </div>
205
+ <KpiCardsGrid items={summaryCards} columns={4} />
229
206
 
230
- <Card>
231
- <CardHeader>
232
- <CardTitle>{t('chart.title')}</CardTitle>
233
- <CardDescription>{t('chart.description')}</CardDescription>
234
- </CardHeader>
235
- <CardContent>
236
- <ResponsiveContainer width="100%" height={340}>
237
- <LineChart data={comparisonData}>
238
- <CartesianGrid strokeDasharray="3 3" className="stroke-muted" />
239
- <XAxis dataKey="dataLabel" tick={{ fontSize: 12 }} />
240
- <YAxis
241
- tick={{ fontSize: 12 }}
242
- tickFormatter={(value) => `${(value / 1000).toFixed(0)}k`}
243
- />
244
- <Tooltip
245
- formatter={(value: number) =>
246
- new Intl.NumberFormat('pt-BR', {
247
- style: 'currency',
248
- currency: 'BRL',
249
- }).format(value)
250
- }
251
- contentStyle={{
252
- backgroundColor: 'hsl(var(--background))',
253
- border: '1px solid hsl(var(--border))',
254
- borderRadius: '8px',
255
- }}
256
- />
257
- <Legend />
258
- <Line
259
- type="monotone"
260
- dataKey="previsto"
261
- name={t('chart.forecastLabel')}
262
- stroke="hsl(var(--primary))"
263
- strokeWidth={2}
264
- dot={false}
265
- />
266
- <Line
267
- type="monotone"
268
- dataKey="realizado"
269
- name={t('chart.actualLabel')}
270
- stroke="hsl(var(--chart-2))"
271
- strokeWidth={2}
272
- dot={false}
273
- />
274
- </LineChart>
275
- </ResponsiveContainer>
276
- </CardContent>
277
- </Card>
207
+ <FinancePageSection
208
+ title={t('chart.title')}
209
+ description={t('chart.description')}
210
+ contentClassName="p-4 sm:p-5"
211
+ >
212
+ <ResponsiveContainer width="100%" height={340}>
213
+ <LineChart data={comparisonData}>
214
+ <CartesianGrid strokeDasharray="3 3" className="stroke-muted" />
215
+ <XAxis dataKey="dataLabel" tick={{ fontSize: 12 }} />
216
+ <YAxis
217
+ tick={{ fontSize: 12 }}
218
+ tickFormatter={(value) => `${(value / 1000).toFixed(0)}k`}
219
+ />
220
+ <Tooltip
221
+ formatter={(value: number) =>
222
+ new Intl.NumberFormat('pt-BR', {
223
+ style: 'currency',
224
+ currency: 'BRL',
225
+ }).format(value)
226
+ }
227
+ contentStyle={{
228
+ backgroundColor: 'hsl(var(--background))',
229
+ border: '1px solid hsl(var(--border))',
230
+ borderRadius: '8px',
231
+ }}
232
+ />
233
+ <Legend />
234
+ <Line
235
+ type="monotone"
236
+ dataKey="previsto"
237
+ name={t('chart.forecastLabel')}
238
+ stroke="hsl(var(--primary))"
239
+ strokeWidth={2}
240
+ dot={false}
241
+ />
242
+ <Line
243
+ type="monotone"
244
+ dataKey="realizado"
245
+ name={t('chart.actualLabel')}
246
+ stroke="hsl(var(--chart-2))"
247
+ strokeWidth={2}
248
+ dot={false}
249
+ />
250
+ </LineChart>
251
+ </ResponsiveContainer>
252
+ </FinancePageSection>
278
253
 
279
- <Card>
280
- <CardHeader>
281
- <CardTitle>{t('table.title')}</CardTitle>
282
- <CardDescription>{t('table.description')}</CardDescription>
283
- </CardHeader>
284
- <CardContent>
254
+ <FinancePageSection
255
+ title={t('table.title')}
256
+ description={t('table.description')}
257
+ contentClassName="p-4 sm:p-5"
258
+ >
259
+ <div className="overflow-x-auto">
285
260
  <Table>
286
261
  <TableHeader>
287
262
  <TableRow>
@@ -354,8 +329,8 @@ export default function ActualVsForecastPage() {
354
329
  )}
355
330
  </TableBody>
356
331
  </Table>
357
- </CardContent>
358
- </Card>
332
+ </div>
333
+ </FinancePageSection>
359
334
  </Page>
360
335
  );
361
336
  }