@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
@@ -0,0 +1,302 @@
1
+ "use strict";
2
+ /// <reference types="jest" />
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ const finance_contract_activated_subscriber_1 = require("./finance.contract-activated.subscriber");
5
+ describe('FinanceContractActivatedSubscriber', () => {
6
+ let integrationApi;
7
+ let financeService;
8
+ let prisma;
9
+ let subscriber;
10
+ beforeEach(() => {
11
+ integrationApi = {
12
+ subscribeMany: jest.fn(),
13
+ findLinksBySource: jest.fn(),
14
+ createLink: jest.fn().mockResolvedValue(undefined),
15
+ publishEvent: jest.fn().mockResolvedValue(undefined),
16
+ };
17
+ financeService = {
18
+ createAccountsReceivableTitle: jest.fn().mockResolvedValue({
19
+ id: '91',
20
+ parcelas: [
21
+ {
22
+ id: '501',
23
+ numero: 1,
24
+ vencimento: '2025-02-10T00:00:00.000Z',
25
+ valor: 1000,
26
+ },
27
+ {
28
+ id: '502',
29
+ numero: 2,
30
+ vencimento: '2025-03-10T00:00:00.000Z',
31
+ valor: 1000,
32
+ },
33
+ {
34
+ id: '503',
35
+ numero: 3,
36
+ vencimento: '2025-04-10T00:00:00.000Z',
37
+ valor: 1000,
38
+ },
39
+ ],
40
+ }),
41
+ createAccountsPayableTitle: jest.fn().mockResolvedValue({
42
+ id: '190',
43
+ parcelas: [
44
+ {
45
+ id: '801',
46
+ numero: 1,
47
+ vencimento: '2025-02-15T00:00:00.000Z',
48
+ valor: 450,
49
+ },
50
+ ],
51
+ }),
52
+ };
53
+ prisma = {
54
+ financial_title: {
55
+ findFirst: jest.fn().mockResolvedValue(null),
56
+ },
57
+ outbox_event: {
58
+ findMany: jest.fn().mockResolvedValue([]),
59
+ },
60
+ };
61
+ subscriber = new finance_contract_activated_subscriber_1.FinanceContractActivatedSubscriber(integrationApi, financeService, prisma);
62
+ });
63
+ afterEach(() => {
64
+ jest.clearAllMocks();
65
+ });
66
+ it('registers handlers for contract signing and activation', () => {
67
+ subscriber.onModuleInit();
68
+ const subscriptions = integrationApi.subscribeMany.mock.calls[0][0];
69
+ expect(subscriptions).toHaveLength(2);
70
+ expect(subscriptions.map((entry) => entry.eventName)).toEqual(expect.arrayContaining([
71
+ 'operations.contract.signed',
72
+ 'operations.contract.activated',
73
+ ]));
74
+ });
75
+ it('skips duplicate finance generation when a financial title link already exists', async () => {
76
+ subscriber.onModuleInit();
77
+ const subscriptions = integrationApi.subscribeMany.mock.calls[0][0];
78
+ const activated = subscriptions.find((entry) => entry.eventName === 'operations.contract.activated');
79
+ integrationApi.findLinksBySource.mockResolvedValue([
80
+ {
81
+ targetModule: 'finance',
82
+ targetEntityType: 'financial_title',
83
+ targetEntityId: '91',
84
+ },
85
+ ]);
86
+ await activated.handler({
87
+ eventName: 'operations.contract.activated',
88
+ sourceModule: 'operations',
89
+ aggregateType: 'contract',
90
+ aggregateId: '77',
91
+ payload: {
92
+ contractId: 77,
93
+ },
94
+ });
95
+ expect(financeService.createAccountsReceivableTitle).not.toHaveBeenCalled();
96
+ expect(financeService.createAccountsPayableTitle).not.toHaveBeenCalled();
97
+ expect(integrationApi.createLink).not.toHaveBeenCalled();
98
+ expect(integrationApi.publishEvent).not.toHaveBeenCalled();
99
+ });
100
+ it('recovers an existing title when the finance record already exists but the integration link is missing', async () => {
101
+ subscriber.onModuleInit();
102
+ const subscriptions = integrationApi.subscribeMany.mock.calls[0][0];
103
+ const activated = subscriptions.find((entry) => entry.eventName === 'operations.contract.activated');
104
+ integrationApi.findLinksBySource.mockResolvedValue([]);
105
+ prisma.financial_title.findFirst.mockResolvedValue({
106
+ id: 91,
107
+ financial_installment: [
108
+ {
109
+ id: 501,
110
+ installment_number: 1,
111
+ due_date: new Date('2025-02-10T00:00:00.000Z'),
112
+ amount_cents: 100000,
113
+ },
114
+ ],
115
+ });
116
+ await activated.handler({
117
+ eventName: 'operations.contract.activated',
118
+ sourceModule: 'operations',
119
+ aggregateType: 'contract',
120
+ aggregateId: '77',
121
+ payload: {
122
+ contractId: 77,
123
+ locale: 'en',
124
+ correlationId: 'contract:77',
125
+ activatedByUserId: 9,
126
+ contract: {
127
+ code: 'CTR-077',
128
+ name: 'Recovered Contract',
129
+ contractCategory: 'client',
130
+ contractType: 'service_agreement',
131
+ effectiveDate: '2025-02-01',
132
+ financialTerms: [
133
+ {
134
+ label: 'Recovered installment',
135
+ amount: 1000,
136
+ recurrence: 'one_time',
137
+ },
138
+ ],
139
+ },
140
+ receivable: {
141
+ personId: 5,
142
+ documentNumber: 'CTR-077',
143
+ description: 'Recovered Contract',
144
+ },
145
+ },
146
+ });
147
+ expect(financeService.createAccountsReceivableTitle).not.toHaveBeenCalled();
148
+ expect(integrationApi.createLink).toHaveBeenCalledWith(expect.objectContaining({
149
+ targetEntityType: 'financial_title',
150
+ targetEntityId: '91',
151
+ }));
152
+ });
153
+ it('creates receivable titles, installment links and a finance success event', async () => {
154
+ subscriber.onModuleInit();
155
+ const subscriptions = integrationApi.subscribeMany.mock.calls[0][0];
156
+ const signed = subscriptions.find((entry) => entry.eventName === 'operations.contract.signed');
157
+ integrationApi.findLinksBySource.mockResolvedValue([]);
158
+ await signed.handler({
159
+ eventName: 'operations.contract.signed',
160
+ sourceModule: 'operations',
161
+ aggregateType: 'contract',
162
+ aggregateId: '77',
163
+ payload: {
164
+ contractId: 77,
165
+ proposalId: 12,
166
+ locale: 'en',
167
+ correlationId: 'contract:77',
168
+ signedByUserId: 9,
169
+ contract: {
170
+ code: 'CTR-077',
171
+ name: 'Support Contract',
172
+ contractCategory: 'client',
173
+ contractType: 'service_agreement',
174
+ startDate: '2025-02-01',
175
+ endDate: '2025-04-30',
176
+ signedAt: '2025-02-01',
177
+ effectiveDate: '2025-02-01',
178
+ financialTerms: [
179
+ {
180
+ label: 'Monthly retainer',
181
+ amount: 1000,
182
+ recurrence: 'monthly',
183
+ dueDay: 10,
184
+ },
185
+ ],
186
+ },
187
+ receivable: {
188
+ personId: 5,
189
+ documentNumber: 'CTR-077',
190
+ description: 'Support Contract',
191
+ },
192
+ },
193
+ });
194
+ expect(financeService.createAccountsReceivableTitle).toHaveBeenCalledWith(expect.objectContaining({
195
+ person_id: 5,
196
+ document_number: 'CTR-077',
197
+ total_amount: 3000,
198
+ installments: [
199
+ expect.objectContaining({
200
+ installment_number: 1,
201
+ due_date: '2025-02-10',
202
+ amount: 1000,
203
+ }),
204
+ expect.objectContaining({
205
+ installment_number: 2,
206
+ due_date: '2025-03-10',
207
+ amount: 1000,
208
+ }),
209
+ expect.objectContaining({
210
+ installment_number: 3,
211
+ due_date: '2025-04-10',
212
+ amount: 1000,
213
+ }),
214
+ ],
215
+ }), 'en', 9);
216
+ expect(financeService.createAccountsPayableTitle).not.toHaveBeenCalled();
217
+ expect(integrationApi.createLink).toHaveBeenCalledTimes(4);
218
+ expect(integrationApi.createLink).toHaveBeenNthCalledWith(1, expect.objectContaining({
219
+ sourceModule: 'operations',
220
+ sourceEntityType: 'contract',
221
+ sourceEntityId: '77',
222
+ targetModule: 'finance',
223
+ targetEntityType: 'financial_title',
224
+ targetEntityId: '91',
225
+ metadata: expect.objectContaining({
226
+ eventName: 'operations.contract.signed',
227
+ titleType: 'receivable',
228
+ source_module: 'operations',
229
+ source_entity: 'contract',
230
+ source_id: '77',
231
+ installmentCount: 3,
232
+ }),
233
+ }));
234
+ expect(integrationApi.createLink).toHaveBeenNthCalledWith(2, expect.objectContaining({
235
+ targetEntityType: 'financial_installment',
236
+ targetEntityId: '501',
237
+ }));
238
+ expect(integrationApi.createLink).toHaveBeenNthCalledWith(3, expect.objectContaining({
239
+ targetEntityType: 'financial_installment',
240
+ targetEntityId: '502',
241
+ }));
242
+ expect(integrationApi.createLink).toHaveBeenNthCalledWith(4, expect.objectContaining({
243
+ targetEntityType: 'financial_installment',
244
+ targetEntityId: '503',
245
+ }));
246
+ expect(integrationApi.publishEvent).toHaveBeenCalledWith(expect.objectContaining({
247
+ eventName: 'finance.receivable.created_from_contract',
248
+ sourceModule: 'finance',
249
+ aggregateType: 'financial_title',
250
+ aggregateId: '91',
251
+ payload: expect.objectContaining({
252
+ proposalId: 12,
253
+ source_module: 'operations',
254
+ source_entity: 'contract',
255
+ source_id: '77',
256
+ triggerEvent: 'operations.contract.signed',
257
+ financialTitle: expect.objectContaining({
258
+ id: '91',
259
+ titleType: 'receivable',
260
+ installmentCount: 3,
261
+ }),
262
+ }),
263
+ }));
264
+ });
265
+ it('routes supplier contracts to accounts payable', async () => {
266
+ subscriber.onModuleInit();
267
+ const subscriptions = integrationApi.subscribeMany.mock.calls[0][0];
268
+ const activated = subscriptions.find((entry) => entry.eventName === 'operations.contract.activated');
269
+ integrationApi.findLinksBySource.mockResolvedValue([]);
270
+ await activated.handler({
271
+ eventName: 'operations.contract.activated',
272
+ sourceModule: 'operations',
273
+ aggregateType: 'contract',
274
+ aggregateId: '88',
275
+ payload: {
276
+ contractId: 88,
277
+ locale: 'en',
278
+ activatedByUserId: 11,
279
+ contract: {
280
+ code: 'CTR-088',
281
+ contractCategory: 'supplier',
282
+ contractType: 'service_agreement',
283
+ effectiveDate: '2025-02-15',
284
+ },
285
+ receivable: {
286
+ personId: 14,
287
+ totalAmount: 450,
288
+ documentNumber: 'CTR-088',
289
+ description: 'Supplier onboarding',
290
+ },
291
+ },
292
+ });
293
+ expect(financeService.createAccountsPayableTitle).toHaveBeenCalledWith(expect.objectContaining({
294
+ person_id: 14,
295
+ total_amount: 450,
296
+ }), 'en', 11);
297
+ expect(integrationApi.publishEvent).toHaveBeenCalledWith(expect.objectContaining({
298
+ eventName: 'finance.payable.created_from_contract',
299
+ }));
300
+ });
301
+ });
302
+ //# sourceMappingURL=finance.contract-activated.subscriber.spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"finance.contract-activated.subscriber.spec.js","sourceRoot":"","sources":["../src/finance.contract-activated.subscriber.spec.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"finance.module.d.ts","sourceRoot":"","sources":["../src/finance.module.ts"],"names":[],"mappings":"AAoBA,qBA0Ba,aAAa;CAAG"}
1
+ {"version":3,"file":"finance.module.d.ts","sourceRoot":"","sources":["../src/finance.module.ts"],"names":[],"mappings":"AAqBA,qBA8Ba,aAAa;CAAG"}
@@ -24,6 +24,7 @@ const finance_period_close_controller_1 = require("./finance-period-close.contro
24
24
  const finance_reports_controller_1 = require("./finance-reports.controller");
25
25
  const finance_statements_controller_1 = require("./finance-statements.controller");
26
26
  const finance_transfers_controller_1 = require("./finance-transfers.controller");
27
+ const finance_contract_activated_subscriber_1 = require("./finance.contract-activated.subscriber");
27
28
  const finance_integration_example_subscriber_1 = require("./finance.integration-example.subscriber");
28
29
  const finance_service_1 = require("./finance.service");
29
30
  let FinanceModule = class FinanceModule {
@@ -53,7 +54,11 @@ exports.FinanceModule = FinanceModule = __decorate([
53
54
  finance_statements_controller_1.FinanceStatementsController,
54
55
  finance_transfers_controller_1.FinanceTransfersController,
55
56
  ],
56
- providers: [finance_service_1.FinanceService, finance_integration_example_subscriber_1.FinanceIntegrationExampleSubscriber],
57
+ providers: [
58
+ finance_service_1.FinanceService,
59
+ finance_integration_example_subscriber_1.FinanceIntegrationExampleSubscriber,
60
+ finance_contract_activated_subscriber_1.FinanceContractActivatedSubscriber,
61
+ ],
57
62
  exports: [finance_service_1.FinanceService],
58
63
  })
59
64
  ], FinanceModule);
@@ -1 +1 @@
1
- {"version":3,"file":"finance.module.js","sourceRoot":"","sources":["../src/finance.module.ts"],"names":[],"mappings":";;;;;;;;;AAAA,oDAAmD;AACnD,4DAA2D;AAC3D,oDAAmD;AACnD,wCAAwE;AACxE,2CAAoD;AACpD,2CAA8C;AAC9C,mFAA6E;AAC7E,yFAAmF;AACnF,mFAA8E;AAC9E,qFAAgF;AAChF,uFAAiF;AACjF,uEAAkE;AAClE,uFAAkF;AAClF,uFAAiF;AACjF,6EAAwE;AACxE,mFAA8E;AAC9E,iFAA4E;AAC5E,qGAA+F;AAC/F,uDAAmD;AA4B5C,IAAM,aAAa,GAAnB,MAAM,aAAa;CAAG,CAAA;AAAhB,sCAAa;wBAAb,aAAa;IA1BzB,IAAA,eAAM,EAAC;QACN,OAAO,EAAE;YACP,qBAAY,CAAC,OAAO,EAAE;YACtB,IAAA,mBAAU,EAAC,GAAG,EAAE,CAAC,iCAAgB,CAAC;YAClC,IAAA,mBAAU,EAAC,GAAG,EAAE,CAAC,yBAAY,CAAC;YAC9B,IAAA,mBAAU,EAAC,GAAG,EAAE,CAAC,yBAAY,CAAC;YAC9B,IAAA,mBAAU,EAAC,GAAG,EAAE,CAAC,eAAQ,CAAC;YAC1B,IAAA,mBAAU,EAAC,GAAG,EAAE,CAAC,iBAAU,CAAC;YAC5B,IAAA,mBAAU,EAAC,GAAG,EAAE,CAAC,wBAAiB,CAAC;SACpC;QACD,WAAW,EAAE;YACX,0DAA0B;YAC1B,+CAAqB;YACrB,+DAA6B;YAC7B,qDAAwB;YACxB,gEAA6B;YAC7B,2DAA2B;YAC3B,6DAA4B;YAC5B,8DAA4B;YAC5B,8DAA4B;YAC5B,2DAA2B;YAC3B,yDAA0B;SAC3B;QACD,SAAS,EAAE,CAAC,gCAAc,EAAE,4EAAmC,CAAC;QAChE,OAAO,EAAE,CAAC,gCAAc,CAAC;KAC1B,CAAC;GACW,aAAa,CAAG"}
1
+ {"version":3,"file":"finance.module.js","sourceRoot":"","sources":["../src/finance.module.ts"],"names":[],"mappings":";;;;;;;;;AAAA,oDAAmD;AACnD,4DAA2D;AAC3D,oDAAmD;AACnD,wCAAwE;AACxE,2CAAoD;AACpD,2CAA8C;AAC9C,mFAA6E;AAC7E,yFAAmF;AACnF,mFAA8E;AAC9E,qFAAgF;AAChF,uFAAiF;AACjF,uEAAkE;AAClE,uFAAkF;AAClF,uFAAiF;AACjF,6EAAwE;AACxE,mFAA8E;AAC9E,iFAA4E;AAC5E,mGAA6F;AAC7F,qGAA+F;AAC/F,uDAAmD;AAgC5C,IAAM,aAAa,GAAnB,MAAM,aAAa;CAAG,CAAA;AAAhB,sCAAa;wBAAb,aAAa;IA9BzB,IAAA,eAAM,EAAC;QACN,OAAO,EAAE;YACP,qBAAY,CAAC,OAAO,EAAE;YACtB,IAAA,mBAAU,EAAC,GAAG,EAAE,CAAC,iCAAgB,CAAC;YAClC,IAAA,mBAAU,EAAC,GAAG,EAAE,CAAC,yBAAY,CAAC;YAC9B,IAAA,mBAAU,EAAC,GAAG,EAAE,CAAC,yBAAY,CAAC;YAC9B,IAAA,mBAAU,EAAC,GAAG,EAAE,CAAC,eAAQ,CAAC;YAC1B,IAAA,mBAAU,EAAC,GAAG,EAAE,CAAC,iBAAU,CAAC;YAC5B,IAAA,mBAAU,EAAC,GAAG,EAAE,CAAC,wBAAiB,CAAC;SACpC;QACD,WAAW,EAAE;YACX,0DAA0B;YAC1B,+CAAqB;YACrB,+DAA6B;YAC7B,qDAAwB;YACxB,gEAA6B;YAC7B,2DAA2B;YAC3B,6DAA4B;YAC5B,8DAA4B;YAC5B,8DAA4B;YAC5B,2DAA2B;YAC3B,yDAA0B;SAC3B;QACD,SAAS,EAAE;YACT,gCAAc;YACd,4EAAmC;YACnC,0EAAkC;SACnC;QACD,OAAO,EAAE,CAAC,gCAAc,CAAC;KAC1B,CAAC;GACW,aAAa,CAAG"}
@@ -11,23 +11,6 @@
11
11
  - where:
12
12
  slug: admin-finance
13
13
 
14
- - menu_id:
15
- where:
16
- slug: /finance
17
- icon: chart-no-axes-combined
18
- url: /finance
19
- name:
20
- en: Overview
21
- pt: Visão geral
22
- slug: /finance/overview
23
- order: 101
24
- relations:
25
- role:
26
- - where:
27
- slug: admin
28
- - where:
29
- slug: admin-finance
30
-
31
14
  - menu_id:
32
15
  where:
33
16
  slug: /finance
@@ -0,0 +1,108 @@
1
+ 'use client';
2
+
3
+ import {
4
+ Card,
5
+ CardContent,
6
+ CardDescription,
7
+ CardHeader,
8
+ CardTitle,
9
+ } from '@/components/ui/card';
10
+ import { cn } from '@/lib/utils';
11
+ import type { ReactNode } from 'react';
12
+
13
+ type FinancePageSectionProps = {
14
+ title?: ReactNode;
15
+ description?: ReactNode;
16
+ actions?: ReactNode;
17
+ children: ReactNode;
18
+ className?: string;
19
+ contentClassName?: string;
20
+ };
21
+
22
+ export function FinancePageSection({
23
+ title,
24
+ description,
25
+ actions,
26
+ children,
27
+ className,
28
+ contentClassName,
29
+ }: FinancePageSectionProps) {
30
+ const hasHeader = title || description || actions;
31
+
32
+ return (
33
+ <Card
34
+ className={cn(
35
+ 'overflow-hidden border-border/60 py-0 shadow-sm',
36
+ className
37
+ )}
38
+ >
39
+ {hasHeader ? (
40
+ <CardHeader className="flex flex-col gap-3 border-b border-border/50 px-5 py-4 sm:flex-row sm:items-start sm:justify-between">
41
+ <div className="space-y-1">
42
+ {title ? (
43
+ <CardTitle className="text-base">{title}</CardTitle>
44
+ ) : null}
45
+ {description ? (
46
+ <CardDescription>{description}</CardDescription>
47
+ ) : null}
48
+ </div>
49
+ {actions ? <div className="shrink-0">{actions}</div> : null}
50
+ </CardHeader>
51
+ ) : null}
52
+ <CardContent className={cn('p-0', contentClassName)}>
53
+ {children}
54
+ </CardContent>
55
+ </Card>
56
+ );
57
+ }
58
+
59
+ type FinanceSheetBodyProps = {
60
+ children: ReactNode;
61
+ className?: string;
62
+ };
63
+
64
+ export function FinanceSheetBody({
65
+ children,
66
+ className,
67
+ }: FinanceSheetBodyProps) {
68
+ return (
69
+ <div
70
+ className={cn(
71
+ 'flex-1 space-y-5 overflow-y-auto px-4 py-4 sm:px-6 sm:py-5',
72
+ className
73
+ )}
74
+ >
75
+ {children}
76
+ </div>
77
+ );
78
+ }
79
+
80
+ type FinanceSheetSectionProps = {
81
+ title: ReactNode;
82
+ description?: ReactNode;
83
+ children: ReactNode;
84
+ className?: string;
85
+ contentClassName?: string;
86
+ };
87
+
88
+ export function FinanceSheetSection({
89
+ title,
90
+ description,
91
+ children,
92
+ className,
93
+ contentClassName,
94
+ }: FinanceSheetSectionProps) {
95
+ return (
96
+ <section className={cn('', className)}>
97
+ <div className="mb-4 space-y-1">
98
+ <h3 className="text-sm font-semibold text-foreground">{title}</h3>
99
+ {description ? (
100
+ <p className="text-sm leading-relaxed text-muted-foreground">
101
+ {description}
102
+ </p>
103
+ ) : null}
104
+ </div>
105
+ <div className={cn('space-y-4', contentClassName)}>{children}</div>
106
+ </section>
107
+ );
108
+ }
@@ -1,14 +1,11 @@
1
1
  'use client';
2
2
 
3
+ 'use client';
4
+
5
+ import { FinancePageSection } from '@/app/(app)/(libraries)/finance/_components/finance-layout';
3
6
  import { Badge } from '@/components/ui/badge';
4
7
  import { Button } from '@/components/ui/button';
5
- import {
6
- Card,
7
- CardContent,
8
- CardDescription,
9
- CardHeader,
10
- CardTitle,
11
- } from '@/components/ui/card';
8
+ import { EmptyState, Page, PageHeader } from '@/components/entity-list';
12
9
  import {
13
10
  Dialog,
14
11
  DialogContent,
@@ -18,10 +15,9 @@ import {
18
15
  DialogTitle,
19
16
  DialogTrigger,
20
17
  } from '@/components/ui/dialog';
18
+ import { KpiCardsGrid } from '@/components/ui/kpi-cards-grid';
21
19
  import { Label } from '@/components/ui/label';
22
20
  import { Money } from '@/components/ui/money';
23
-
24
- import { Page, PageHeader } from '@/components/entity-list';
25
21
  import {
26
22
  Table,
27
23
  TableBody,
@@ -212,6 +208,34 @@ export default function AprovacoesPage() {
212
208
  };
213
209
 
214
210
  const totalPendente = aprovacoes.reduce((acc, a) => acc + a.valor, 0);
211
+ const urgentCount = aprovacoes.filter(
212
+ (a) => a.urgencia === 'alta' || a.urgencia === 'critica'
213
+ ).length;
214
+ const summaryCards = [
215
+ {
216
+ key: 'pending',
217
+ title: t('cards.pending'),
218
+ value: aprovacoes.length,
219
+ icon: Clock,
220
+ layout: 'compact' as const,
221
+ },
222
+ {
223
+ key: 'value',
224
+ title: t('cards.totalValue'),
225
+ value: <Money value={totalPendente} />,
226
+ icon: AlertTriangle,
227
+ layout: 'compact' as const,
228
+ },
229
+ {
230
+ key: 'urgent',
231
+ title: t('cards.urgent'),
232
+ value: urgentCount,
233
+ icon: AlertTriangle,
234
+ layout: 'compact' as const,
235
+ iconContainerClassName: 'bg-orange-500/10 text-orange-700',
236
+ accentClassName: 'from-orange-500/20 via-amber-500/10 to-transparent',
237
+ },
238
+ ];
215
239
 
216
240
  return (
217
241
  <Page>
@@ -225,57 +249,11 @@ export default function AprovacoesPage() {
225
249
  ]}
226
250
  />
227
251
 
228
- <div className="grid gap-4 md:grid-cols-3">
229
- <Card>
230
- <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
231
- <CardTitle className="text-sm font-medium">
232
- {t('cards.pending')}
233
- </CardTitle>
234
- <Clock className="h-4 w-4 text-muted-foreground" />
235
- </CardHeader>
236
- <CardContent>
237
- <div className="text-2xl font-bold">{aprovacoes.length}</div>
238
- </CardContent>
239
- </Card>
240
- <Card>
241
- <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
242
- <CardTitle className="text-sm font-medium">
243
- {t('cards.totalValue')}
244
- </CardTitle>
245
- <AlertTriangle className="h-4 w-4 text-muted-foreground" />
246
- </CardHeader>
247
- <CardContent>
248
- <div className="text-2xl font-bold">
249
- <Money value={totalPendente} />
250
- </div>
251
- </CardContent>
252
- </Card>
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.urgent')}
257
- </CardTitle>
258
- <AlertTriangle className="h-4 w-4 text-orange-500" />
259
- </CardHeader>
260
- <CardContent>
261
- <div className="text-2xl font-bold">
262
- {
263
- aprovacoes.filter(
264
- (a) => a.urgencia === 'alta' || a.urgencia === 'critica'
265
- ).length
266
- }
267
- </div>
268
- </CardContent>
269
- </Card>
270
- </div>
252
+ <KpiCardsGrid items={summaryCards} columns={3} />
271
253
 
272
- <Card>
273
- <CardHeader>
274
- <CardTitle>{t('table.title')}</CardTitle>
275
- <CardDescription>{t('table.description')}</CardDescription>
276
- </CardHeader>
277
- <CardContent>
278
- {aprovacoes.length > 0 ? (
254
+ <FinancePageSection className="border-none shadow-none p-0">
255
+ {aprovacoes.length > 0 ? (
256
+ <div className="overflow-x-auto">
279
257
  <Table>
280
258
  <TableHeader>
281
259
  <TableRow>
@@ -307,7 +285,7 @@ export default function AprovacoesPage() {
307
285
  ] || urgenciaConfig.media;
308
286
 
309
287
  return (
310
- <TableRow key={aprovacao.id}>
288
+ <TableRow key={aprovacao.id} className="hover:bg-muted/30">
311
289
  <TableCell className="font-medium">
312
290
  {titulo?.documento}
313
291
  </TableCell>
@@ -359,15 +337,19 @@ export default function AprovacoesPage() {
359
337
  })}
360
338
  </TableBody>
361
339
  </Table>
362
- ) : (
363
- <div className="flex flex-col items-center justify-center py-12 text-center">
364
- <CheckCircle className="h-12 w-12 text-green-500" />
365
- <h3 className="mt-4 text-lg font-semibold">{t('empty.title')}</h3>
366
- <p className="text-muted-foreground">{t('empty.description')}</p>
367
- </div>
368
- )}
369
- </CardContent>
370
- </Card>
340
+ </div>
341
+ ) : (
342
+ <div className="px-4 py-8 sm:px-6">
343
+ <EmptyState
344
+ icon={<CheckCircle className="h-12 w-12" />}
345
+ title={t('empty.title')}
346
+ description={t('empty.description')}
347
+ actionLabel={t('empty.refresh')}
348
+ onAction={() => void refetch()}
349
+ />
350
+ </div>
351
+ )}
352
+ </FinancePageSection>
371
353
  </Page>
372
354
  );
373
355
  }