@hed-hog/finance 0.0.279 → 0.0.286

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 +238 -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 +239 -130
  27. package/hedhog/frontend/app/reports/top-operational-expenses/page.tsx.ejs +242 -135
  28. package/hedhog/frontend/messages/en.json +33 -2
  29. package/hedhog/frontend/messages/pt.json +33 -2
  30. package/package.json +7 -7
  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
@@ -1,6 +1,7 @@
1
1
  'use client';
2
2
 
3
3
  import { Page, PageHeader } from '@/components/entity-list';
4
+ import { Button } from '@/components/ui/button';
4
5
  import {
5
6
  Card,
6
7
  CardContent,
@@ -9,7 +10,6 @@ import {
9
10
  CardTitle,
10
11
  } from '@/components/ui/card';
11
12
  import { Input } from '@/components/ui/input';
12
- import { Label } from '@/components/ui/label';
13
13
  import { Money } from '@/components/ui/money';
14
14
  import {
15
15
  Select,
@@ -28,11 +28,14 @@ import {
28
28
  } from '@/components/ui/table';
29
29
  import { Building2, CircleDollarSign, TriangleAlert } from 'lucide-react';
30
30
  import { useTranslations } from 'next-intl';
31
- import { useMemo, useState } from 'react';
31
+ import { type FormEvent, useState } from 'react';
32
+ import type { TooltipProps } from 'recharts';
32
33
  import {
33
34
  Bar,
34
35
  BarChart,
36
+ CartesianGrid,
35
37
  Cell,
38
+ Legend,
36
39
  Pie,
37
40
  PieChart,
38
41
  ResponsiveContainer,
@@ -41,47 +44,84 @@ import {
41
44
  YAxis,
42
45
  } from 'recharts';
43
46
  import {
44
- aggregateOperationalExpenses,
45
47
  getDefaultDateRange,
46
- } from '../_lib/report-aggregations';
47
- import { type GroupBy } from '../_lib/report-mocks';
48
+ type GroupBy,
49
+ useTopOperationalExpensesReport,
50
+ } from '../_lib/use-finance-reports';
48
51
 
49
- const pieColors = [
50
- 'hsl(var(--chart-1))',
51
- 'hsl(var(--chart-2))',
52
- 'hsl(var(--chart-3))',
53
- 'hsl(var(--chart-4))',
54
- 'hsl(var(--chart-5))',
55
- '#64748B',
56
- '#0EA5E9',
57
- '#22C55E',
52
+ const rankingColors = [
53
+ '#2563EB',
54
+ '#DC2626',
55
+ '#059669',
56
+ '#D97706',
57
+ '#7C3AED',
58
+ '#0F766E',
59
+ '#BE123C',
60
+ '#1D4ED8',
58
61
  ];
59
62
 
63
+ const currencyFormatter = new Intl.NumberFormat('pt-BR', {
64
+ style: 'currency',
65
+ currency: 'BRL',
66
+ });
67
+
60
68
  export default function TopOperationalExpensesReportPage() {
61
69
  const t = useTranslations('finance.TopOperationalExpensesReportPage');
62
70
  const defaults = getDefaultDateRange();
71
+ const [filters, setFilters] = useState({
72
+ from: defaults.from,
73
+ to: defaults.to,
74
+ groupBy: 'year' as GroupBy,
75
+ search: '',
76
+ });
77
+ const [appliedFilters, setAppliedFilters] = useState(filters);
78
+ const { data: viewData, isFetching } = useTopOperationalExpensesReport({
79
+ from: appliedFilters.from,
80
+ to: appliedFilters.to,
81
+ groupBy: appliedFilters.groupBy,
82
+ search: appliedFilters.search,
83
+ topN: 20,
84
+ });
63
85
 
64
- const [from, setFrom] = useState(defaults.from);
65
- const [to, setTo] = useState(defaults.to);
66
- const [groupBy, setGroupBy] = useState<GroupBy>('year');
86
+ const handleSubmitFilters = (event: FormEvent<HTMLFormElement>) => {
87
+ event.preventDefault();
88
+ setAppliedFilters(filters);
89
+ };
67
90
 
68
- const data = useMemo(
69
- () =>
70
- aggregateOperationalExpenses({
71
- from,
72
- to,
73
- groupBy,
74
- topN: 20,
75
- }),
76
- [from, to, groupBy]
77
- );
91
+ const renderSolidTooltip = ({
92
+ active,
93
+ payload,
94
+ label,
95
+ }: TooltipProps<string | number, string>) => {
96
+ if (!active || !payload || payload.length === 0) {
97
+ return null;
98
+ }
78
99
 
79
- const highest = data.topExpenses[0];
80
- const average =
81
- data.topExpenses.length > 0
82
- ? data.topExpenses.reduce((acc, item) => acc + item.value, 0) /
83
- data.topExpenses.length
84
- : 0;
100
+ return (
101
+ <div className="min-w-52 rounded-md border bg-popover px-3 py-2 shadow-xl">
102
+ <p className="mb-1 text-sm font-semibold text-popover-foreground">
103
+ {String(label ?? payload[0]?.name ?? '')}
104
+ </p>
105
+ {payload.map((row, index) => (
106
+ <div
107
+ key={`${String(row.dataKey ?? row.name ?? 'item')}-${index}`}
108
+ className="flex items-center justify-between gap-3 text-sm"
109
+ >
110
+ <div className="flex items-center gap-2 text-popover-foreground">
111
+ <span
112
+ className="inline-block h-2.5 w-2.5 rounded-full"
113
+ style={{ backgroundColor: row.color || 'hsl(var(--chart-1))' }}
114
+ />
115
+ <span>{row.name ?? row.dataKey ?? 'Valor'}</span>
116
+ </div>
117
+ <span className="font-semibold text-popover-foreground">
118
+ {currencyFormatter.format(Number(row.value || 0))}
119
+ </span>
120
+ </div>
121
+ ))}
122
+ </div>
123
+ );
124
+ };
85
125
 
86
126
  return (
87
127
  <Page>
@@ -95,166 +135,231 @@ export default function TopOperationalExpensesReportPage() {
95
135
  ]}
96
136
  />
97
137
 
98
- <Card>
99
- <CardHeader>
100
- <CardTitle>{t('filters.title')}</CardTitle>
101
- <CardDescription>{t('filters.description')}</CardDescription>
102
- </CardHeader>
103
- <CardContent className="grid gap-4 md:grid-cols-3">
104
- <div className="space-y-2">
105
- <Label htmlFor="from">{t('filters.from')}</Label>
106
- <Input
107
- id="from"
108
- type="date"
109
- value={from}
110
- onChange={(event) => setFrom(event.target.value)}
111
- max={to}
112
- />
113
- </div>
114
- <div className="space-y-2">
115
- <Label htmlFor="to">{t('filters.to')}</Label>
116
- <Input
117
- id="to"
118
- type="date"
119
- value={to}
120
- onChange={(event) => setTo(event.target.value)}
121
- min={from}
122
- />
123
- </div>
124
- <div className="space-y-2">
125
- <Label>{t('filters.groupBy')}</Label>
126
- <Select
127
- value={groupBy}
128
- onValueChange={(value) => setGroupBy(value as GroupBy)}
129
- >
130
- <SelectTrigger>
131
- <SelectValue />
132
- </SelectTrigger>
133
- <SelectContent>
134
- <SelectItem value="day">{t('groupBy.day')}</SelectItem>
135
- <SelectItem value="week">{t('groupBy.week')}</SelectItem>
136
- <SelectItem value="month">{t('groupBy.month')}</SelectItem>
137
- <SelectItem value="year">{t('groupBy.year')}</SelectItem>
138
- </SelectContent>
139
- </Select>
140
- </div>
141
- </CardContent>
142
- </Card>
138
+ <form
139
+ onSubmit={handleSubmitFilters}
140
+ className="flex w-full flex-col gap-3 lg:flex-row"
141
+ >
142
+ <Input
143
+ type="search"
144
+ value={filters.search}
145
+ onChange={(event) =>
146
+ setFilters((current) => ({
147
+ ...current,
148
+ search: event.target.value,
149
+ }))
150
+ }
151
+ placeholder={t('filters.searchPlaceholder')}
152
+ className="w-full lg:flex-[2.4]"
153
+ aria-label={t('filters.searchAria')}
154
+ />
155
+ <Input
156
+ id="from"
157
+ type="date"
158
+ value={filters.from}
159
+ onChange={(event) =>
160
+ setFilters((current) => ({
161
+ ...current,
162
+ from: event.target.value,
163
+ }))
164
+ }
165
+ max={filters.to}
166
+ className="w-full lg:w-[170px]"
167
+ aria-label={t('filters.fromAria')}
168
+ placeholder={t('filters.fromPlaceholder')}
169
+ />
170
+ <Input
171
+ id="to"
172
+ type="date"
173
+ value={filters.to}
174
+ onChange={(event) =>
175
+ setFilters((current) => ({
176
+ ...current,
177
+ to: event.target.value,
178
+ }))
179
+ }
180
+ min={filters.from}
181
+ className="w-full lg:w-[170px]"
182
+ aria-label={t('filters.toAria')}
183
+ placeholder={t('filters.toPlaceholder')}
184
+ />
185
+ <Select
186
+ value={filters.groupBy}
187
+ onValueChange={(value) =>
188
+ setFilters((current) => ({
189
+ ...current,
190
+ groupBy: value as GroupBy,
191
+ }))
192
+ }
193
+ >
194
+ <SelectTrigger
195
+ className="w-full lg:w-[150px]"
196
+ aria-label={t('filters.groupByAria')}
197
+ >
198
+ <SelectValue placeholder={t('filters.groupByPlaceholder')} />
199
+ </SelectTrigger>
200
+ <SelectContent>
201
+ <SelectItem value="day">{t('groupBy.day')}</SelectItem>
202
+ <SelectItem value="week">{t('groupBy.week')}</SelectItem>
203
+ <SelectItem value="month">{t('groupBy.month')}</SelectItem>
204
+ <SelectItem value="year">{t('groupBy.year')}</SelectItem>
205
+ </SelectContent>
206
+ </Select>
207
+ <Button
208
+ type="submit"
209
+ className="w-full lg:w-auto"
210
+ disabled={isFetching}
211
+ >
212
+ {t('filters.submit')}
213
+ </Button>
214
+ </form>
143
215
 
144
- <div className="grid gap-4 md:grid-cols-3">
216
+ <div className="grid gap-3 md:grid-cols-3">
145
217
  <Card>
146
- <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
147
- <CardTitle className="text-sm font-medium">
218
+ <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-1 pt-3 px-4">
219
+ <CardTitle className="text-xs font-medium tracking-wide text-muted-foreground uppercase">
148
220
  {t('cards.total')}
149
221
  </CardTitle>
150
- <CircleDollarSign className="h-4 w-4 text-muted-foreground" />
222
+ <div className="rounded-full bg-orange-500/10 p-1.5">
223
+ <CircleDollarSign className="h-4 w-4 text-orange-600" />
224
+ </div>
151
225
  </CardHeader>
152
- <CardContent>
153
- <div className="text-2xl font-bold">
154
- <Money value={data.total} />
226
+ <CardContent className="pb-3 pt-0 px-4">
227
+ <div className="text-xl font-bold">
228
+ <Money value={viewData.total} />
155
229
  </div>
156
230
  </CardContent>
157
231
  </Card>
158
232
 
159
233
  <Card>
160
- <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
161
- <CardTitle className="text-sm font-medium">
234
+ <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-1 pt-3 px-4">
235
+ <CardTitle className="text-xs font-medium tracking-wide text-muted-foreground uppercase">
162
236
  {t('cards.highestExpense')}
163
237
  </CardTitle>
164
- <TriangleAlert className="h-4 w-4 text-amber-500" />
238
+ <div className="rounded-full bg-amber-500/10 p-1.5">
239
+ <TriangleAlert className="h-4 w-4 text-amber-500" />
240
+ </div>
165
241
  </CardHeader>
166
- <CardContent>
167
- <div className="text-lg font-semibold">
168
- {highest?.category || '-'}
242
+ <CardContent className="pb-3 pt-0 px-4">
243
+ <div className="text-sm font-medium">
244
+ {viewData.highest?.category || '-'}
169
245
  </div>
170
- <div className="text-muted-foreground text-sm">
171
- {highest ? <Money value={highest.value} /> : '-'}
246
+ <div className="text-xs text-muted-foreground">
247
+ {viewData.highest ? (
248
+ <Money value={viewData.highest.value} />
249
+ ) : (
250
+ '-'
251
+ )}
172
252
  </div>
173
253
  </CardContent>
174
254
  </Card>
175
255
 
176
256
  <Card>
177
- <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
178
- <CardTitle className="text-sm font-medium">
257
+ <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-1 pt-3 px-4">
258
+ <CardTitle className="text-xs font-medium tracking-wide text-muted-foreground uppercase">
179
259
  {t('cards.average')}
180
260
  </CardTitle>
181
- <Building2 className="h-4 w-4 text-muted-foreground" />
261
+ <div className="rounded-full bg-blue-500/10 p-1.5">
262
+ <Building2 className="h-4 w-4 text-blue-600" />
263
+ </div>
182
264
  </CardHeader>
183
- <CardContent>
184
- <div className="text-2xl font-bold">
185
- <Money value={average} />
265
+ <CardContent className="pb-3 pt-0 px-4">
266
+ <div className="text-xl font-bold">
267
+ <Money value={viewData.average} />
186
268
  </div>
187
269
  </CardContent>
188
270
  </Card>
189
271
  </div>
190
272
 
191
- <div className="grid gap-4 xl:grid-cols-5">
273
+ <div className="grid gap-3 xl:grid-cols-5">
192
274
  <Card className="xl:col-span-3">
193
- <CardHeader>
275
+ <CardHeader className="pb-3">
194
276
  <CardTitle>{t('bars.title')}</CardTitle>
195
277
  <CardDescription>{t('bars.description')}</CardDescription>
196
278
  </CardHeader>
197
- <CardContent>
198
- <ResponsiveContainer width="100%" height={560}>
279
+ <CardContent className="pb-4 pt-0">
280
+ <ResponsiveContainer width="100%" height={360}>
199
281
  <BarChart
200
- data={[...data.topExpenses].reverse()}
282
+ data={viewData.topExpenses}
201
283
  layout="vertical"
202
- margin={{ left: 80, right: 16 }}
284
+ margin={{ left: 60, right: 16 }}
203
285
  >
286
+ <CartesianGrid strokeDasharray="3 3" className="stroke-muted" />
204
287
  <XAxis
205
288
  type="number"
206
289
  tickFormatter={(value) => `${(value / 1000).toFixed(0)}k`}
207
290
  />
208
- <YAxis dataKey="category" type="category" width={210} />
291
+ <YAxis dataKey="label" type="category" width={190} />
209
292
  <Tooltip
210
- formatter={(value: number) =>
211
- new Intl.NumberFormat('pt-BR', {
212
- style: 'currency',
213
- currency: 'BRL',
214
- }).format(value)
215
- }
216
- />
217
- <Bar
218
- dataKey="value"
219
- radius={[0, 6, 6, 0]}
220
- fill="hsl(var(--chart-5))"
293
+ cursor={{ fill: 'hsl(var(--muted) / 0.35)' }}
294
+ content={renderSolidTooltip}
221
295
  />
296
+ <Bar dataKey="value" radius={[0, 6, 6, 0]}>
297
+ {viewData.topExpenses.map((entry, index) => (
298
+ <Cell
299
+ key={entry.label}
300
+ fill={rankingColors[index % rankingColors.length]}
301
+ stroke="hsl(var(--background))"
302
+ strokeWidth={1}
303
+ />
304
+ ))}
305
+ </Bar>
222
306
  </BarChart>
223
307
  </ResponsiveContainer>
308
+ <div className="mt-4 grid grid-cols-2 gap-2 text-sm sm:grid-cols-3">
309
+ {viewData.topExpenses.slice(0, 6).map((item, index) => (
310
+ <div key={item.label} className="flex items-center gap-2">
311
+ <span
312
+ className="inline-block h-2.5 w-2.5 rounded-full"
313
+ style={{
314
+ backgroundColor:
315
+ rankingColors[index % rankingColors.length],
316
+ }}
317
+ />
318
+ <span className="truncate text-foreground/90">
319
+ #{index + 1} {item.category}
320
+ </span>
321
+ </div>
322
+ ))}
323
+ </div>
224
324
  </CardContent>
225
325
  </Card>
226
326
 
227
327
  <Card className="xl:col-span-2">
228
- <CardHeader>
328
+ <CardHeader className="pb-3">
229
329
  <CardTitle>{t('pie.title')}</CardTitle>
230
330
  <CardDescription>{t('pie.description')}</CardDescription>
231
331
  </CardHeader>
232
- <CardContent>
233
- <ResponsiveContainer width="100%" height={380}>
332
+ <CardContent className="pb-4 pt-0">
333
+ <ResponsiveContainer width="100%" height={340}>
234
334
  <PieChart>
235
335
  <Pie
236
- data={data.pieData}
336
+ data={viewData.pieData}
237
337
  dataKey="value"
238
338
  nameKey="name"
239
- innerRadius={65}
240
- outerRadius={120}
339
+ innerRadius={55}
340
+ outerRadius={100}
241
341
  paddingAngle={2}
342
+ cy="42%"
242
343
  >
243
- {data.pieData.map((entry, index) => (
344
+ {viewData.pieData.map((entry, index) => (
244
345
  <Cell
245
346
  key={entry.name}
246
- fill={pieColors[index % pieColors.length]}
347
+ fill={rankingColors[index % rankingColors.length]}
348
+ stroke="hsl(var(--background))"
349
+ strokeWidth={1}
247
350
  />
248
351
  ))}
249
352
  </Pie>
250
- <Tooltip
251
- formatter={(value: number) =>
252
- new Intl.NumberFormat('pt-BR', {
253
- style: 'currency',
254
- currency: 'BRL',
255
- }).format(value)
256
- }
353
+ <Legend
354
+ iconType="circle"
355
+ verticalAlign="bottom"
356
+ wrapperStyle={{
357
+ fontSize: 12,
358
+ lineHeight: '20px',
359
+ paddingTop: 12,
360
+ }}
257
361
  />
362
+ <Tooltip cursor={false} content={renderSolidTooltip} />
258
363
  </PieChart>
259
364
  </ResponsiveContainer>
260
365
  </CardContent>
@@ -281,7 +386,7 @@ export default function TopOperationalExpensesReportPage() {
281
386
  </TableRow>
282
387
  </TableHeader>
283
388
  <TableBody>
284
- {data.topExpenses.length === 0 ? (
389
+ {viewData.topExpenses.length === 0 ? (
285
390
  <TableRow>
286
391
  <TableCell
287
392
  colSpan={4}
@@ -291,9 +396,11 @@ export default function TopOperationalExpensesReportPage() {
291
396
  </TableCell>
292
397
  </TableRow>
293
398
  ) : (
294
- data.topExpenses.map((item) => {
399
+ viewData.topExpenses.map((item) => {
295
400
  const participation =
296
- data.total > 0 ? (item.value / data.total) * 100 : 0;
401
+ viewData.total > 0
402
+ ? (item.value / viewData.total) * 100
403
+ : 0;
297
404
 
298
405
  return (
299
406
  <TableRow key={item.category}>
@@ -277,6 +277,10 @@
277
277
  "openAttachment": "Open attachment"
278
278
  }
279
279
  },
280
+ "empty": {
281
+ "title": "No titles found",
282
+ "description": "Create the first title to start managing accounts payable."
283
+ },
280
284
  "dialogs": {
281
285
  "cancel": {
282
286
  "title": "Confirm cancellation",
@@ -570,6 +574,11 @@
570
574
  "total": "Total",
571
575
  "actions": "Actions"
572
576
  }
577
+ },
578
+ "empty": {
579
+ "title": "No default data found",
580
+ "description": "There are no late clients for the current filters.",
581
+ "action": "Refresh data"
573
582
  }
574
583
  },
575
584
  "ReceivableInstallmentsPage": {
@@ -995,6 +1004,10 @@
995
1004
  "reconciliation": "Reconciliation"
996
1005
  }
997
1006
  },
1007
+ "empty": {
1008
+ "title": "No transactions found",
1009
+ "description": "Import a statement to view account transactions."
1010
+ },
998
1011
  "types": {
999
1012
  "inflow": "Inflow",
1000
1013
  "outflow": "Outflow"
@@ -1651,7 +1664,16 @@
1651
1664
  "description": "Select period and grouping level",
1652
1665
  "from": "From",
1653
1666
  "to": "To",
1654
- "groupBy": "Group by"
1667
+ "groupBy": "Group by",
1668
+ "fromPlaceholder": "From date",
1669
+ "toPlaceholder": "To date",
1670
+ "groupByPlaceholder": "Group",
1671
+ "searchPlaceholder": "Search customer, supplier, category...",
1672
+ "fromAria": "Start date filter",
1673
+ "toAria": "End date filter",
1674
+ "groupByAria": "Grouping filter",
1675
+ "searchAria": "Search filter by customer",
1676
+ "submit": "Apply"
1655
1677
  },
1656
1678
  "groupBy": {
1657
1679
  "day": "Day",
@@ -1698,7 +1720,16 @@
1698
1720
  "description": "Select period and grouping level",
1699
1721
  "from": "From",
1700
1722
  "to": "To",
1701
- "groupBy": "Group by"
1723
+ "groupBy": "Group by",
1724
+ "fromPlaceholder": "From date",
1725
+ "toPlaceholder": "To date",
1726
+ "groupByPlaceholder": "Group",
1727
+ "searchPlaceholder": "Search customer, supplier, category...",
1728
+ "fromAria": "Start date filter",
1729
+ "toAria": "End date filter",
1730
+ "groupByAria": "Grouping filter",
1731
+ "searchAria": "Search filter by category or cost center",
1732
+ "submit": "Apply"
1702
1733
  },
1703
1734
  "groupBy": {
1704
1735
  "day": "Day",
@@ -277,6 +277,10 @@
277
277
  "openAttachment": "Abrir anexo"
278
278
  }
279
279
  },
280
+ "empty": {
281
+ "title": "Nenhum título encontrado",
282
+ "description": "Cadastre o primeiro título para começar a gerenciar contas a pagar."
283
+ },
280
284
  "dialogs": {
281
285
  "cancel": {
282
286
  "title": "Confirmar cancelamento",
@@ -570,6 +574,11 @@
570
574
  "total": "Total",
571
575
  "actions": "Ações"
572
576
  }
577
+ },
578
+ "empty": {
579
+ "title": "Nenhum dado de inadimplência",
580
+ "description": "Não há clientes inadimplentes para os filtros atuais.",
581
+ "action": "Atualizar dados"
573
582
  }
574
583
  },
575
584
  "ReceivableInstallmentsPage": {
@@ -995,6 +1004,10 @@
995
1004
  "reconciliation": "Conciliação"
996
1005
  }
997
1006
  },
1007
+ "empty": {
1008
+ "title": "Nenhuma movimentação encontrada",
1009
+ "description": "Importe um extrato para visualizar as movimentações desta conta."
1010
+ },
998
1011
  "types": {
999
1012
  "inflow": "Entrada",
1000
1013
  "outflow": "Saída"
@@ -1651,7 +1664,16 @@
1651
1664
  "description": "Selecione o período e o nível de agrupamento",
1652
1665
  "from": "Data de",
1653
1666
  "to": "Data até",
1654
- "groupBy": "Agrupar por"
1667
+ "groupBy": "Agrupar por",
1668
+ "fromPlaceholder": "Data de",
1669
+ "toPlaceholder": "Data até",
1670
+ "groupByPlaceholder": "Agrupar",
1671
+ "searchPlaceholder": "Buscar cliente, fornecedor, categoria...",
1672
+ "fromAria": "Filtro de data inicial",
1673
+ "toAria": "Filtro de data final",
1674
+ "groupByAria": "Filtro de agrupamento",
1675
+ "searchAria": "Filtro de busca por cliente",
1676
+ "submit": "Aplicar"
1655
1677
  },
1656
1678
  "groupBy": {
1657
1679
  "day": "Dia",
@@ -1698,7 +1720,16 @@
1698
1720
  "description": "Selecione o período e o nível de agrupamento",
1699
1721
  "from": "Data de",
1700
1722
  "to": "Data até",
1701
- "groupBy": "Agrupar por"
1723
+ "groupBy": "Agrupar por",
1724
+ "fromPlaceholder": "Data de",
1725
+ "toPlaceholder": "Data até",
1726
+ "groupByPlaceholder": "Agrupar",
1727
+ "searchPlaceholder": "Buscar cliente, fornecedor, categoria...",
1728
+ "fromAria": "Filtro de data inicial",
1729
+ "toAria": "Filtro de data final",
1730
+ "groupByAria": "Filtro de agrupamento",
1731
+ "searchAria": "Filtro de busca por categoria ou centro de custo",
1732
+ "submit": "Aplicar"
1702
1733
  },
1703
1734
  "groupBy": {
1704
1735
  "day": "Dia",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hed-hog/finance",
3
- "version": "0.0.279",
3
+ "version": "0.0.286",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "dependencies": {
@@ -9,14 +9,14 @@
9
9
  "@nestjs/core": "^11",
10
10
  "@nestjs/jwt": "^11",
11
11
  "@nestjs/mapped-types": "*",
12
- "@hed-hog/tag": "0.0.279",
13
- "@hed-hog/contact": "0.0.279",
14
- "@hed-hog/api-prisma": "0.0.5",
15
12
  "@hed-hog/api": "0.0.4",
13
+ "@hed-hog/contact": "0.0.286",
14
+ "@hed-hog/tag": "0.0.286",
15
+ "@hed-hog/api-pagination": "0.0.6",
16
16
  "@hed-hog/api-locale": "0.0.13",
17
- "@hed-hog/core": "0.0.279",
18
- "@hed-hog/api-types": "0.0.1",
19
- "@hed-hog/api-pagination": "0.0.6"
17
+ "@hed-hog/api-prisma": "0.0.5",
18
+ "@hed-hog/core": "0.0.286",
19
+ "@hed-hog/api-types": "0.0.1"
20
20
  },
21
21
  "exports": {
22
22
  ".": {