@hed-hog/finance 0.0.279 → 0.0.285

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 (35) hide show
  1. package/dist/dto/finance-report-query.dto.d.ts +16 -0
  2. package/dist/dto/finance-report-query.dto.d.ts.map +1 -0
  3. package/dist/dto/finance-report-query.dto.js +59 -0
  4. package/dist/dto/finance-report-query.dto.js.map +1 -0
  5. package/dist/finance-reports.controller.d.ts +71 -0
  6. package/dist/finance-reports.controller.d.ts.map +1 -0
  7. package/dist/finance-reports.controller.js +61 -0
  8. package/dist/finance-reports.controller.js.map +1 -0
  9. package/dist/finance.module.d.ts.map +1 -1
  10. package/dist/finance.module.js +2 -0
  11. package/dist/finance.module.js.map +1 -1
  12. package/dist/finance.service.d.ts +93 -0
  13. package/dist/finance.service.d.ts.map +1 -1
  14. package/dist/finance.service.js +456 -0
  15. package/dist/finance.service.js.map +1 -1
  16. package/dist/index.d.ts +1 -0
  17. package/dist/index.d.ts.map +1 -1
  18. package/dist/index.js +1 -0
  19. package/dist/index.js.map +1 -1
  20. package/hedhog/data/route.yaml +27 -0
  21. package/hedhog/frontend/app/accounts-payable/installments/page.tsx.ejs +158 -125
  22. package/hedhog/frontend/app/accounts-receivable/collections-default/page.tsx.ejs +102 -88
  23. package/hedhog/frontend/app/cash-and-banks/statements/page.tsx.ejs +113 -89
  24. package/hedhog/frontend/app/reports/_lib/use-finance-reports.ts.ejs +233 -0
  25. package/hedhog/frontend/app/reports/overview-results/page.tsx.ejs +96 -78
  26. package/hedhog/frontend/app/reports/top-customers/page.tsx.ejs +247 -130
  27. package/hedhog/frontend/app/reports/top-operational-expenses/page.tsx.ejs +250 -135
  28. package/hedhog/frontend/messages/en.json +33 -2
  29. package/hedhog/frontend/messages/pt.json +33 -2
  30. package/package.json +6 -6
  31. package/src/dto/finance-report-query.dto.ts +49 -0
  32. package/src/finance-reports.controller.ts +28 -0
  33. package/src/finance.module.ts +2 -0
  34. package/src/finance.service.ts +645 -10
  35. package/src/index.ts +1 -0
@@ -28,8 +28,9 @@ import {
28
28
  TableRow,
29
29
  } from '@/components/ui/table';
30
30
  import { Target, TrendingDown, TrendingUp } from 'lucide-react';
31
- import { useTranslations } from 'next-intl';
31
+ import { useLocale, useTranslations } from 'next-intl';
32
32
  import { useMemo, useState } from 'react';
33
+ import type { TooltipProps } from 'recharts';
33
34
  import {
34
35
  CartesianGrid,
35
36
  Legend,
@@ -41,51 +42,72 @@ import {
41
42
  YAxis,
42
43
  } from 'recharts';
43
44
  import {
44
- aggregateOverview,
45
+ formatReportBucketLabel,
45
46
  getDefaultDateRange,
46
- } from '../_lib/report-aggregations';
47
- import { type GroupBy } from '../_lib/report-mocks';
47
+ type GroupBy,
48
+ useOverviewResultsReport,
49
+ } from '../_lib/use-finance-reports';
50
+
51
+ const LINE_COLORS = {
52
+ faturamento: '#2563eb',
53
+ despesasEmprestimos: '#ea580c',
54
+ diferenca: '#16a34a',
55
+ };
56
+
57
+ const currencyFormatter = new Intl.NumberFormat('pt-BR', {
58
+ style: 'currency',
59
+ currency: 'BRL',
60
+ });
61
+
62
+ function renderSolidTooltip({
63
+ active,
64
+ payload,
65
+ label,
66
+ }: TooltipProps<number, string>) {
67
+ if (!active || !payload?.length) return null;
68
+ return (
69
+ <div className="rounded-lg border bg-popover px-3 py-2 shadow-xl text-popover-foreground text-sm">
70
+ <p className="mb-1 font-semibold text-popover-foreground">{label}</p>
71
+ {payload.map((entry) => (
72
+ <div key={entry.dataKey} className="flex items-center gap-2 py-0.5">
73
+ <span
74
+ className="inline-block h-2.5 w-2.5 rounded-sm shrink-0"
75
+ style={{ background: entry.color }}
76
+ />
77
+ <span className="text-muted-foreground">{entry.name}:</span>
78
+ <span className="font-semibold text-popover-foreground">
79
+ {typeof entry.value === 'number'
80
+ ? currencyFormatter.format(entry.value)
81
+ : entry.value}
82
+ </span>
83
+ </div>
84
+ ))}
85
+ </div>
86
+ );
87
+ }
48
88
 
49
89
  export default function OverviewResultsReportPage() {
50
90
  const t = useTranslations('finance.OverviewResultsReportPage');
91
+ const locale = useLocale();
51
92
  const defaults = getDefaultDateRange();
52
93
  const [from, setFrom] = useState(defaults.from);
53
94
  const [to, setTo] = useState(defaults.to);
54
95
  const [groupBy, setGroupBy] = useState<GroupBy>('year');
96
+ const { data } = useOverviewResultsReport({
97
+ from,
98
+ to,
99
+ groupBy,
100
+ });
55
101
 
56
102
  const rows = useMemo(
57
103
  () =>
58
- aggregateOverview({
59
- from,
60
- to,
61
- groupBy,
62
- }),
63
- [from, to, groupBy]
104
+ data.rows.map((row) => ({
105
+ ...row,
106
+ label: formatReportBucketLabel(row.period, groupBy, locale),
107
+ })),
108
+ [data.rows, groupBy, locale]
64
109
  );
65
-
66
- const totals = useMemo(() => {
67
- const faturamento = rows.reduce((acc, row) => acc + row.faturamento, 0);
68
- const despesasEmprestimos = rows.reduce(
69
- (acc, row) => acc + row.despesasEmprestimos,
70
- 0
71
- );
72
- const diferenca = faturamento - despesasEmprestimos;
73
- const aporteInvestidor = rows.reduce(
74
- (acc, row) => acc + row.aporteInvestidor,
75
- 0
76
- );
77
-
78
- const margem =
79
- faturamento > 0 ? (diferenca / Math.abs(faturamento)) * 100 : 0;
80
-
81
- return {
82
- faturamento,
83
- despesasEmprestimos,
84
- diferenca,
85
- aporteInvestidor,
86
- margem,
87
- };
88
- }, [rows]);
110
+ const totals = data.totals;
89
111
 
90
112
  return (
91
113
  <Page>
@@ -145,49 +167,57 @@ export default function OverviewResultsReportPage() {
145
167
  </CardContent>
146
168
  </Card>
147
169
 
148
- <div className="grid gap-4 md:grid-cols-4">
170
+ <div className="grid gap-3 md:grid-cols-4">
149
171
  <Card>
150
- <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
151
- <CardTitle className="text-sm font-medium">
172
+ <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-1 pt-3 px-4">
173
+ <CardTitle className="text-xs font-medium tracking-wide text-muted-foreground uppercase">
152
174
  {t('cards.revenue')}
153
175
  </CardTitle>
154
- <TrendingUp className="h-4 w-4 text-blue-600" />
176
+ <div className="rounded-full bg-blue-500/10 p-1.5">
177
+ <TrendingUp className="h-4 w-4 text-blue-600" />
178
+ </div>
155
179
  </CardHeader>
156
- <CardContent>
157
- <div className="text-2xl font-bold">
180
+ <CardContent className="pb-3 pt-0 px-4">
181
+ <div className="text-xl font-bold">
158
182
  <Money value={totals.faturamento} />
159
183
  </div>
160
184
  </CardContent>
161
185
  </Card>
162
186
 
163
187
  <Card>
164
- <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
165
- <CardTitle className="text-sm font-medium">
188
+ <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-1 pt-3 px-4">
189
+ <CardTitle className="text-xs font-medium tracking-wide text-muted-foreground uppercase">
166
190
  {t('cards.expensesAndLoans')}
167
191
  </CardTitle>
168
- <TrendingDown className="h-4 w-4 text-orange-600" />
192
+ <div className="rounded-full bg-orange-500/10 p-1.5">
193
+ <TrendingDown className="h-4 w-4 text-orange-600" />
194
+ </div>
169
195
  </CardHeader>
170
- <CardContent>
171
- <div className="text-2xl font-bold">
196
+ <CardContent className="pb-3 pt-0 px-4">
197
+ <div className="text-xl font-bold">
172
198
  <Money value={totals.despesasEmprestimos} />
173
199
  </div>
174
200
  </CardContent>
175
201
  </Card>
176
202
 
177
203
  <Card>
178
- <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
179
- <CardTitle className="text-sm font-medium">
204
+ <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-1 pt-3 px-4">
205
+ <CardTitle className="text-xs font-medium tracking-wide text-muted-foreground uppercase">
180
206
  {t('cards.resultDifference')}
181
207
  </CardTitle>
182
- {totals.diferenca >= 0 ? (
183
- <TrendingUp className="h-4 w-4 text-green-600" />
184
- ) : (
185
- <TrendingDown className="h-4 w-4 text-red-600" />
186
- )}
208
+ <div
209
+ className={`rounded-full p-1.5 ${totals.diferenca >= 0 ? 'bg-green-500/10' : 'bg-red-500/10'}`}
210
+ >
211
+ {totals.diferenca >= 0 ? (
212
+ <TrendingUp className="h-4 w-4 text-green-600" />
213
+ ) : (
214
+ <TrendingDown className="h-4 w-4 text-red-600" />
215
+ )}
216
+ </div>
187
217
  </CardHeader>
188
- <CardContent>
218
+ <CardContent className="pb-3 pt-0 px-4">
189
219
  <div
190
- className={`text-2xl font-bold ${totals.diferenca >= 0 ? 'text-green-600' : 'text-red-600'}`}
220
+ className={`text-xl font-bold ${totals.diferenca >= 0 ? 'text-green-600' : 'text-red-600'}`}
191
221
  >
192
222
  <Money value={totals.diferenca} showSign />
193
223
  </div>
@@ -195,18 +225,18 @@ export default function OverviewResultsReportPage() {
195
225
  </Card>
196
226
 
197
227
  <Card>
198
- <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
199
- <CardTitle className="text-sm font-medium">
228
+ <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-1 pt-3 px-4">
229
+ <CardTitle className="text-xs font-medium tracking-wide text-muted-foreground uppercase">
200
230
  {t('cards.margin')}
201
231
  </CardTitle>
202
- <Target className="h-4 w-4 text-muted-foreground" />
203
- </CardHeader>
204
- <CardContent>
205
- <div className="text-2xl font-bold">
206
- {totals.margem.toFixed(1)}%
232
+ <div className="rounded-full bg-violet-500/10 p-1.5">
233
+ <Target className="h-4 w-4 text-violet-600" />
207
234
  </div>
235
+ </CardHeader>
236
+ <CardContent className="pb-3 pt-0 px-4">
237
+ <div className="text-xl font-bold">{totals.margem.toFixed(1)}%</div>
208
238
  <Badge
209
- className="mt-2"
239
+ className="mt-1"
210
240
  variant={totals.margem >= 0 ? 'default' : 'destructive'}
211
241
  >
212
242
  {totals.margem >= 0 ? t('status.positive') : t('status.negative')}
@@ -229,25 +259,13 @@ export default function OverviewResultsReportPage() {
229
259
  tick={{ fontSize: 12 }}
230
260
  tickFormatter={(value) => `${(value / 1000).toFixed(0)}k`}
231
261
  />
232
- <Tooltip
233
- formatter={(value: number) =>
234
- new Intl.NumberFormat('pt-BR', {
235
- style: 'currency',
236
- currency: 'BRL',
237
- }).format(value)
238
- }
239
- contentStyle={{
240
- backgroundColor: 'hsl(var(--background))',
241
- border: '1px solid hsl(var(--border))',
242
- borderRadius: '8px',
243
- }}
244
- />
262
+ <Tooltip content={renderSolidTooltip} />
245
263
  <Legend />
246
264
  <Line
247
265
  type="monotone"
248
266
  dataKey="faturamento"
249
267
  name={t('chart.revenueLabel')}
250
- stroke="hsl(var(--chart-1))"
268
+ stroke={LINE_COLORS.faturamento}
251
269
  strokeWidth={3}
252
270
  dot={false}
253
271
  />
@@ -255,7 +273,7 @@ export default function OverviewResultsReportPage() {
255
273
  type="monotone"
256
274
  dataKey="despesasEmprestimos"
257
275
  name={t('chart.expensesLabel')}
258
- stroke="hsl(var(--chart-5))"
276
+ stroke={LINE_COLORS.despesasEmprestimos}
259
277
  strokeWidth={3}
260
278
  dot={false}
261
279
  />
@@ -263,7 +281,7 @@ export default function OverviewResultsReportPage() {
263
281
  type="monotone"
264
282
  dataKey="diferenca"
265
283
  name={t('chart.differenceLabel')}
266
- stroke="hsl(var(--chart-2))"
284
+ stroke={LINE_COLORS.diferenca}
267
285
  strokeWidth={2}
268
286
  strokeDasharray="6 4"
269
287
  dot={false}