@hed-hog/finance 0.0.328 → 0.0.329

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.
@@ -0,0 +1,3 @@
1
+ 'use client';
2
+
3
+ export { CategoryPickerField, type FinanceCategory } from './finance-picker';
@@ -0,0 +1,3 @@
1
+ 'use client';
2
+
3
+ export { CostCenterPickerField, type CostCenter } from './finance-picker';
@@ -6,14 +6,14 @@ import { useTranslations } from 'next-intl';
6
6
  import { useMemo, useState } from 'react';
7
7
  import { FieldValues, Path, UseFormReturn } from 'react-hook-form';
8
8
 
9
- type FinanceCategory = {
9
+ export type FinanceCategory = {
10
10
  id: number | string;
11
11
  codigo?: string;
12
12
  nome: string;
13
13
  natureza?: string;
14
14
  };
15
15
 
16
- type CostCenter = {
16
+ export type CostCenter = {
17
17
  id: number | string;
18
18
  codigo?: string;
19
19
  nome: string;
@@ -44,7 +44,7 @@ const extractCreatedId = (
44
44
  return String(id);
45
45
  };
46
46
 
47
- export function CategoryFieldWithCreate<TFieldValues extends FieldValues>({
47
+ export function CategoryPickerField<TFieldValues extends FieldValues>({
48
48
  form,
49
49
  name,
50
50
  label,
@@ -144,7 +144,7 @@ export function CategoryFieldWithCreate<TFieldValues extends FieldValues>({
144
144
  );
145
145
  }
146
146
 
147
- export function CostCenterFieldWithCreate<TFieldValues extends FieldValues>({
147
+ export function CostCenterPickerField<TFieldValues extends FieldValues>({
148
148
  form,
149
149
  name,
150
150
  label,
@@ -235,3 +235,8 @@ export function CostCenterFieldWithCreate<TFieldValues extends FieldValues>({
235
235
  />
236
236
  );
237
237
  }
238
+
239
+ export {
240
+ CategoryPickerField as CategoryFieldWithCreate,
241
+ CostCenterPickerField as CostCenterFieldWithCreate,
242
+ };
@@ -0,0 +1,242 @@
1
+ 'use client';
2
+
3
+ import { EntityPicker } from '@/components/ui/entity-picker';
4
+ import { useApp } from '@hed-hog/next-app-provider';
5
+ import { useTranslations } from 'next-intl';
6
+ import { useMemo, useState } from 'react';
7
+ import { FieldValues, Path, UseFormReturn } from 'react-hook-form';
8
+
9
+ export type FinanceCategory = {
10
+ id: number | string;
11
+ codigo?: string;
12
+ nome: string;
13
+ natureza?: string;
14
+ };
15
+
16
+ export type CostCenter = {
17
+ id: number | string;
18
+ codigo?: string;
19
+ nome: string;
20
+ };
21
+
22
+ type CreatedEntityResponse = {
23
+ id?: number | string | null;
24
+ data?: {
25
+ id?: number | string | null;
26
+ } | null;
27
+ category_id?: number | string | null;
28
+ cost_center_id?: number | string | null;
29
+ };
30
+
31
+ const extractCreatedId = (
32
+ payload: CreatedEntityResponse | null | undefined
33
+ ): string | null => {
34
+ const id =
35
+ payload?.id ??
36
+ payload?.data?.id ??
37
+ payload?.category_id ??
38
+ payload?.cost_center_id;
39
+
40
+ if (id === undefined || id === null || id === '') {
41
+ return null;
42
+ }
43
+
44
+ return String(id);
45
+ };
46
+
47
+ export function CategoryPickerField<TFieldValues extends FieldValues>({
48
+ form,
49
+ name,
50
+ label,
51
+ selectPlaceholder,
52
+ categories,
53
+ categoryKind,
54
+ onCreated,
55
+ }: {
56
+ form: UseFormReturn<TFieldValues>;
57
+ name: Path<TFieldValues>;
58
+ label: string;
59
+ selectPlaceholder: string;
60
+ categories: FinanceCategory[];
61
+ categoryKind: 'receita' | 'despesa';
62
+ onCreated?: () => Promise<unknown> | void;
63
+ }) {
64
+ const { request, showToastHandler } = useApp();
65
+ const t = useTranslations('finance.FinanceEntityFieldWithCreate');
66
+ const [localCategories, setLocalCategories] = useState<FinanceCategory[]>([]);
67
+
68
+ const filteredCategories = useMemo(() => {
69
+ const merged = [...(categories || []), ...localCategories];
70
+ const uniqueById = merged.filter(
71
+ (item, index, arr) =>
72
+ arr.findIndex(
73
+ (candidate) => String(candidate.id) === String(item.id)
74
+ ) === index
75
+ );
76
+
77
+ return uniqueById.filter((item) => item.natureza === categoryKind);
78
+ }, [categories, categoryKind, localCategories]);
79
+
80
+ return (
81
+ <EntityPicker<FinanceCategory, TFieldValues>
82
+ form={form}
83
+ name={name}
84
+ label={label}
85
+ placeholder={selectPlaceholder}
86
+ searchPlaceholder={selectPlaceholder}
87
+ emptySelectionLabel={selectPlaceholder}
88
+ clearable
89
+ allowEmptySelection
90
+ options={filteredCategories}
91
+ createActionLabel={t('actions.createCategoryAria')}
92
+ createTitle={t('categorySheet.title')}
93
+ createDescription={t('categorySheet.description')}
94
+ createFields={[
95
+ {
96
+ name: 'nome',
97
+ label: t('fields.name'),
98
+ placeholder: t('categorySheet.namePlaceholder'),
99
+ required: true,
100
+ },
101
+ ]}
102
+ getOptionValue={(category) => String(category.id)}
103
+ getOptionLabel={(category) =>
104
+ category.codigo
105
+ ? `${category.codigo} - ${category.nome}`
106
+ : category.nome
107
+ }
108
+ onCreate={async (values) => {
109
+ try {
110
+ const createdName = values.nome?.trim() ?? '';
111
+ const response = await request<CreatedEntityResponse>({
112
+ url: '/finance/categories',
113
+ method: 'POST',
114
+ data: {
115
+ name: createdName,
116
+ kind: categoryKind,
117
+ parent_id: null,
118
+ },
119
+ });
120
+
121
+ const createdId = extractCreatedId(response?.data);
122
+ const createdCategory = createdId
123
+ ? {
124
+ id: String(createdId),
125
+ nome: createdName,
126
+ natureza: categoryKind,
127
+ }
128
+ : null;
129
+
130
+ if (createdCategory) {
131
+ setLocalCategories((prev) => [...prev, createdCategory]);
132
+ }
133
+
134
+ await onCreated?.();
135
+ showToastHandler?.('success', t('messages.categoryCreateSuccess'));
136
+
137
+ return createdCategory;
138
+ } catch {
139
+ showToastHandler?.('error', t('messages.categoryCreateError'));
140
+ return null;
141
+ }
142
+ }}
143
+ />
144
+ );
145
+ }
146
+
147
+ export function CostCenterPickerField<TFieldValues extends FieldValues>({
148
+ form,
149
+ name,
150
+ label,
151
+ selectPlaceholder,
152
+ costCenters,
153
+ onCreated,
154
+ }: {
155
+ form: UseFormReturn<TFieldValues>;
156
+ name: Path<TFieldValues>;
157
+ label: string;
158
+ selectPlaceholder: string;
159
+ costCenters: CostCenter[];
160
+ onCreated?: () => Promise<unknown> | void;
161
+ }) {
162
+ const { request, showToastHandler } = useApp();
163
+ const t = useTranslations('finance.FinanceEntityFieldWithCreate');
164
+ const [localCostCenters, setLocalCostCenters] = useState<CostCenter[]>([]);
165
+
166
+ const mergedCostCenters = useMemo(() => {
167
+ const merged = [...(costCenters || []), ...localCostCenters];
168
+
169
+ return merged.filter(
170
+ (item, index, arr) =>
171
+ arr.findIndex(
172
+ (candidate) => String(candidate.id) === String(item.id)
173
+ ) === index
174
+ );
175
+ }, [costCenters, localCostCenters]);
176
+
177
+ return (
178
+ <EntityPicker<CostCenter, TFieldValues>
179
+ form={form}
180
+ name={name}
181
+ label={label}
182
+ placeholder={selectPlaceholder}
183
+ searchPlaceholder={selectPlaceholder}
184
+ emptySelectionLabel={selectPlaceholder}
185
+ clearable
186
+ allowEmptySelection
187
+ options={mergedCostCenters}
188
+ createActionLabel={t('actions.createCostCenterAria')}
189
+ createTitle={t('costCenterSheet.title')}
190
+ createDescription={t('costCenterSheet.description')}
191
+ createFields={[
192
+ {
193
+ name: 'nome',
194
+ label: t('fields.name'),
195
+ placeholder: t('costCenterSheet.namePlaceholder'),
196
+ required: true,
197
+ },
198
+ ]}
199
+ getOptionValue={(center) => String(center.id)}
200
+ getOptionLabel={(center) =>
201
+ center.codigo ? `${center.codigo} - ${center.nome}` : center.nome
202
+ }
203
+ onCreate={async (values) => {
204
+ try {
205
+ const createdName = values.nome?.trim() ?? '';
206
+ const response = await request<CreatedEntityResponse>({
207
+ url: '/finance/cost-centers',
208
+ method: 'POST',
209
+ data: {
210
+ name: createdName,
211
+ },
212
+ });
213
+
214
+ const createdId = extractCreatedId(response?.data);
215
+ const createdCostCenter = createdId
216
+ ? {
217
+ id: String(createdId),
218
+ nome: createdName,
219
+ }
220
+ : null;
221
+
222
+ if (createdCostCenter) {
223
+ setLocalCostCenters((prev) => [...prev, createdCostCenter]);
224
+ }
225
+
226
+ await onCreated?.();
227
+ showToastHandler?.('success', t('messages.costCenterCreateSuccess'));
228
+
229
+ return createdCostCenter;
230
+ } catch {
231
+ showToastHandler?.('error', t('messages.costCenterCreateError'));
232
+ return null;
233
+ }
234
+ }}
235
+ />
236
+ );
237
+ }
238
+
239
+ export {
240
+ CategoryPickerField as CategoryFieldWithCreate,
241
+ CostCenterPickerField as CostCenterFieldWithCreate,
242
+ };
@@ -1,10 +1,12 @@
1
1
  'use client';
2
2
 
3
- import { PersonFieldWithCreate } from '@/app/(app)/(libraries)/contact/person/_components/person-field-with-create';
3
+ import { PersonPickerField } from '@/app/(app)/(libraries)/contact/_components/person-picker';
4
4
  import {
5
- CategoryFieldWithCreate,
6
- CostCenterFieldWithCreate,
7
- } from '@/app/(app)/(libraries)/finance/_components/finance-entity-field-with-create';
5
+ CategoryPickerField,
6
+ } from '@/app/(app)/(libraries)/finance/_components/category-picker-field';
7
+ import {
8
+ CostCenterPickerField,
9
+ } from '@/app/(app)/(libraries)/finance/_components/cost-center-picker-field';
8
10
  import {
9
11
  FinancePageSection,
10
12
  FinanceSheetBody,
@@ -963,7 +965,7 @@ function NovoTituloSheet({
963
965
  />
964
966
  </div>
965
967
 
966
- <PersonFieldWithCreate
968
+ <PersonPickerField
967
969
  form={form}
968
970
  name="fornecedorId"
969
971
  label={t('fields.supplier')}
@@ -1216,7 +1218,7 @@ function NovoTituloSheet({
1216
1218
  description={t('sections.classification.description')}
1217
1219
  >
1218
1220
  <div className="grid gap-4 md:grid-cols-2 xl:grid-cols-3">
1219
- <CategoryFieldWithCreate
1221
+ <CategoryPickerField
1220
1222
  form={form}
1221
1223
  name="categoriaId"
1222
1224
  label={t('fields.category')}
@@ -1226,7 +1228,7 @@ function NovoTituloSheet({
1226
1228
  onCreated={onCategoriesUpdated}
1227
1229
  />
1228
1230
 
1229
- <CostCenterFieldWithCreate
1231
+ <CostCenterPickerField
1230
1232
  form={form}
1231
1233
  name="centroCustoId"
1232
1234
  label={t('fields.costCenter')}
@@ -2079,7 +2081,7 @@ function EditarTituloSheet({
2079
2081
  />
2080
2082
  </div>
2081
2083
 
2082
- <PersonFieldWithCreate
2084
+ <PersonPickerField
2083
2085
  form={form}
2084
2086
  name="fornecedorId"
2085
2087
  label={t('fields.supplier')}
@@ -2333,7 +2335,7 @@ function EditarTituloSheet({
2333
2335
  description={t('sections.classification.description')}
2334
2336
  >
2335
2337
  <div className="grid gap-4 md:grid-cols-2 xl:grid-cols-3">
2336
- <CategoryFieldWithCreate
2338
+ <CategoryPickerField
2337
2339
  form={form}
2338
2340
  name="categoriaId"
2339
2341
  label={t('fields.category')}
@@ -2343,7 +2345,7 @@ function EditarTituloSheet({
2343
2345
  onCreated={onCategoriesUpdated}
2344
2346
  />
2345
2347
 
2346
- <CostCenterFieldWithCreate
2348
+ <CostCenterPickerField
2347
2349
  form={form}
2348
2350
  name="centroCustoId"
2349
2351
  label={t('fields.costCenter')}
@@ -1,10 +1,12 @@
1
1
  'use client';
2
2
 
3
- import { PersonFieldWithCreate } from '@/app/(app)/(libraries)/contact/person/_components/person-field-with-create';
3
+ import { PersonPickerField } from '@/app/(app)/(libraries)/contact/_components/person-picker';
4
4
  import {
5
- CategoryFieldWithCreate,
6
- CostCenterFieldWithCreate,
7
- } from '@/app/(app)/(libraries)/finance/_components/finance-entity-field-with-create';
5
+ CategoryPickerField,
6
+ } from '@/app/(app)/(libraries)/finance/_components/category-picker-field';
7
+ import {
8
+ CostCenterPickerField,
9
+ } from '@/app/(app)/(libraries)/finance/_components/cost-center-picker-field';
8
10
  import {
9
11
  FinancePageSection,
10
12
  FinanceSheetBody,
@@ -943,7 +945,7 @@ function NovoTituloSheet({
943
945
  />
944
946
  </div>
945
947
 
946
- <PersonFieldWithCreate
948
+ <PersonPickerField
947
949
  form={form}
948
950
  name="clienteId"
949
951
  label={t('fields.client')}
@@ -1194,7 +1196,7 @@ function NovoTituloSheet({
1194
1196
  description={t('sections.classification.description')}
1195
1197
  >
1196
1198
  <div className="grid gap-4 md:grid-cols-2 xl:grid-cols-3">
1197
- <CategoryFieldWithCreate
1199
+ <CategoryPickerField
1198
1200
  form={form}
1199
1201
  name="categoriaId"
1200
1202
  label={t('fields.category')}
@@ -1204,7 +1206,7 @@ function NovoTituloSheet({
1204
1206
  onCreated={onOptionsUpdated}
1205
1207
  />
1206
1208
 
1207
- <CostCenterFieldWithCreate
1209
+ <CostCenterPickerField
1208
1210
  form={form}
1209
1211
  name="centroCustoId"
1210
1212
  label={t('fields.costCenter')}
@@ -2049,7 +2051,7 @@ function EditarTituloSheet({
2049
2051
  />
2050
2052
  </div>
2051
2053
 
2052
- <PersonFieldWithCreate
2054
+ <PersonPickerField
2053
2055
  form={form}
2054
2056
  name="clienteId"
2055
2057
  label={t('fields.client')}
@@ -2302,7 +2304,7 @@ function EditarTituloSheet({
2302
2304
  description={t('sections.classification.description')}
2303
2305
  >
2304
2306
  <div className="grid gap-4 md:grid-cols-2 xl:grid-cols-3">
2305
- <CategoryFieldWithCreate
2307
+ <CategoryPickerField
2306
2308
  form={form}
2307
2309
  name="categoriaId"
2308
2310
  label={t('fields.category')}
@@ -2312,7 +2314,7 @@ function EditarTituloSheet({
2312
2314
  onCreated={onOptionsUpdated}
2313
2315
  />
2314
2316
 
2315
- <CostCenterFieldWithCreate
2317
+ <CostCenterPickerField
2316
2318
  form={form}
2317
2319
  name="centroCustoId"
2318
2320
  label={t('fields.costCenter')}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hed-hog/finance",
3
- "version": "0.0.328",
3
+ "version": "0.0.329",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "dependencies": {
@@ -10,13 +10,13 @@
10
10
  "@nestjs/jwt": "^11",
11
11
  "@nestjs/mapped-types": "*",
12
12
  "@hed-hog/api": "0.0.8",
13
- "@hed-hog/api-prisma": "0.0.6",
14
13
  "@hed-hog/api-locale": "0.0.14",
15
- "@hed-hog/contact": "0.0.328",
16
- "@hed-hog/api-pagination": "0.0.7",
14
+ "@hed-hog/contact": "0.0.329",
15
+ "@hed-hog/tag": "0.0.329",
16
+ "@hed-hog/core": "0.0.329",
17
+ "@hed-hog/api-prisma": "0.0.6",
17
18
  "@hed-hog/api-types": "0.0.1",
18
- "@hed-hog/tag": "0.0.328",
19
- "@hed-hog/core": "0.0.328"
19
+ "@hed-hog/api-pagination": "0.0.7"
20
20
  },
21
21
  "exports": {
22
22
  ".": {