@hed-hog/contact 0.0.304 → 0.0.305
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.
- package/README.md +225 -17
- package/dist/person/dto/account.dto.d.ts +5 -0
- package/dist/person/dto/account.dto.d.ts.map +1 -1
- package/dist/person/dto/account.dto.js +29 -0
- package/dist/person/dto/account.dto.js.map +1 -1
- package/dist/person/dto/import-preview.dto.d.ts +7 -0
- package/dist/person/dto/import-preview.dto.d.ts.map +1 -0
- package/dist/person/dto/import-preview.dto.js +7 -0
- package/dist/person/dto/import-preview.dto.js.map +1 -0
- package/dist/person/dto/import.dto.d.ts +15 -0
- package/dist/person/dto/import.dto.d.ts.map +1 -0
- package/dist/person/dto/import.dto.js +51 -0
- package/dist/person/dto/import.dto.js.map +1 -0
- package/dist/person/person.controller.d.ts +14 -0
- package/dist/person/person.controller.d.ts.map +1 -1
- package/dist/person/person.controller.js +53 -0
- package/dist/person/person.controller.js.map +1 -1
- package/dist/person/person.service.d.ts +19 -0
- package/dist/person/person.service.d.ts.map +1 -1
- package/dist/person/person.service.js +481 -67
- package/dist/person/person.service.js.map +1 -1
- package/dist/person-relation-type/person-relation-type.controller.d.ts +2 -2
- package/dist/person-relation-type/person-relation-type.service.d.ts +2 -2
- package/hedhog/data/route.yaml +6 -0
- package/hedhog/frontend/app/accounts/_components/account-form-sheet.tsx.ejs +2242 -484
- package/hedhog/frontend/app/accounts/_components/account-types.ts.ejs +51 -0
- package/hedhog/frontend/app/accounts/page.tsx.ejs +181 -16
- package/hedhog/frontend/app/contact-type/page.tsx.ejs +223 -29
- package/hedhog/frontend/app/document-type/page.tsx.ejs +248 -37
- package/hedhog/frontend/app/follow-ups/page.tsx.ejs +129 -19
- package/hedhog/frontend/app/person/_components/person-field-with-create.tsx.ejs +78 -212
- package/hedhog/frontend/app/person/_components/person-form-sheet.tsx.ejs +760 -178
- package/hedhog/frontend/app/person/_components/person-import-sheet.tsx.ejs +1120 -0
- package/hedhog/frontend/app/person/_components/person-interaction-dialog.tsx.ejs +171 -4
- package/hedhog/frontend/app/person/page.tsx.ejs +17 -0
- package/hedhog/frontend/app/pipeline/_components/lead-proposals-tab.tsx.ejs +160 -35
- package/hedhog/frontend/messages/en.json +104 -2
- package/hedhog/frontend/messages/pt.json +111 -9
- package/package.json +5 -5
- package/src/person/dto/account.dto.ts +31 -0
- package/src/person/dto/import-preview.dto.ts +6 -0
- package/src/person/dto/import.dto.ts +61 -0
- package/src/person/person.controller.ts +74 -12
- package/src/person/person.service.ts +615 -68
|
@@ -18,14 +18,6 @@ import {
|
|
|
18
18
|
AlertDialogTitle,
|
|
19
19
|
} from '@/components/ui/alert-dialog';
|
|
20
20
|
import { Button } from '@/components/ui/button';
|
|
21
|
-
import {
|
|
22
|
-
Sheet,
|
|
23
|
-
SheetContent,
|
|
24
|
-
SheetDescription,
|
|
25
|
-
SheetFooter,
|
|
26
|
-
SheetHeader,
|
|
27
|
-
SheetTitle,
|
|
28
|
-
} from '@/components/ui/sheet';
|
|
29
21
|
import {
|
|
30
22
|
DropdownMenu,
|
|
31
23
|
DropdownMenuContent,
|
|
@@ -43,6 +35,14 @@ import {
|
|
|
43
35
|
FormMessage,
|
|
44
36
|
} from '@/components/ui/form';
|
|
45
37
|
import { Input } from '@/components/ui/input';
|
|
38
|
+
import {
|
|
39
|
+
Sheet,
|
|
40
|
+
SheetContent,
|
|
41
|
+
SheetDescription,
|
|
42
|
+
SheetFooter,
|
|
43
|
+
SheetHeader,
|
|
44
|
+
SheetTitle,
|
|
45
|
+
} from '@/components/ui/sheet';
|
|
46
46
|
import {
|
|
47
47
|
Table,
|
|
48
48
|
TableBody,
|
|
@@ -51,13 +51,16 @@ import {
|
|
|
51
51
|
TableHeader,
|
|
52
52
|
TableRow,
|
|
53
53
|
} from '@/components/ui/table';
|
|
54
|
-
import {
|
|
54
|
+
import { useFormDraft } from '@/hooks/use-form-draft';
|
|
55
|
+
import { formatDate, formatDateTime } from '@/lib/format-date';
|
|
55
56
|
import { useApp, useQuery } from '@hed-hog/next-app-provider';
|
|
56
57
|
import { zodResolver } from '@hookform/resolvers/zod';
|
|
58
|
+
import { formatDistanceToNow } from 'date-fns';
|
|
59
|
+
import { enUS, ptBR } from 'date-fns/locale';
|
|
57
60
|
import { MoreHorizontal, Pencil, Plus, Trash2 } from 'lucide-react';
|
|
58
61
|
import { useTranslations } from 'next-intl';
|
|
59
|
-
import { useState } from 'react';
|
|
60
|
-
import { useForm } from 'react-hook-form';
|
|
62
|
+
import { useMemo, useState } from 'react';
|
|
63
|
+
import { useForm, useWatch } from 'react-hook-form';
|
|
61
64
|
import { toast } from 'sonner';
|
|
62
65
|
import { z } from 'zod';
|
|
63
66
|
|
|
@@ -76,6 +79,37 @@ type ContactType = {
|
|
|
76
79
|
created_at: string;
|
|
77
80
|
};
|
|
78
81
|
|
|
82
|
+
type ContactTypeDraftPayload = {
|
|
83
|
+
mode: 'create' | 'edit';
|
|
84
|
+
contactTypeId: number | null;
|
|
85
|
+
values: {
|
|
86
|
+
code: string;
|
|
87
|
+
name: string;
|
|
88
|
+
};
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
type ContactTypeResponse = {
|
|
92
|
+
code?: string;
|
|
93
|
+
name?: string;
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
function getErrorMessage(error: unknown, fallbackMessage: string) {
|
|
97
|
+
if (
|
|
98
|
+
error &&
|
|
99
|
+
typeof error === 'object' &&
|
|
100
|
+
'message' in error &&
|
|
101
|
+
typeof error.message === 'string'
|
|
102
|
+
) {
|
|
103
|
+
return error.message;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return fallbackMessage;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const CONTACT_TYPE_CREATE_DRAFT_STORAGE_KEY =
|
|
110
|
+
'contact-contact-type-create-draft';
|
|
111
|
+
const CONTACT_TYPE_EDIT_DRAFT_STORAGE_KEY = 'contact-contact-type-edit-draft';
|
|
112
|
+
|
|
79
113
|
export default function ContactTypePage() {
|
|
80
114
|
const t = useTranslations('contact.ContactType');
|
|
81
115
|
|
|
@@ -129,6 +163,124 @@ export default function ContactTypePage() {
|
|
|
129
163
|
resolver: zodResolver(contactTypeSchema),
|
|
130
164
|
});
|
|
131
165
|
|
|
166
|
+
const watchedCreateValues = useWatch({
|
|
167
|
+
control: form.control,
|
|
168
|
+
});
|
|
169
|
+
const watchedEditValues = useWatch({
|
|
170
|
+
control: editForm.control,
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
const {
|
|
174
|
+
clearDraft: clearCreateDraft,
|
|
175
|
+
loadDraft: loadCreateDraft,
|
|
176
|
+
hasDraft: hasCreateDraft,
|
|
177
|
+
savedAt: createDraftSavedAt,
|
|
178
|
+
} = useFormDraft<ContactTypeDraftPayload>({
|
|
179
|
+
storageKey: CONTACT_TYPE_CREATE_DRAFT_STORAGE_KEY,
|
|
180
|
+
value: {
|
|
181
|
+
mode: 'create',
|
|
182
|
+
contactTypeId: null,
|
|
183
|
+
values: {
|
|
184
|
+
code: watchedCreateValues.code ?? '',
|
|
185
|
+
name: watchedCreateValues.name ?? '',
|
|
186
|
+
},
|
|
187
|
+
},
|
|
188
|
+
hasData: Boolean(
|
|
189
|
+
(watchedCreateValues.code ?? '').trim() ||
|
|
190
|
+
(watchedCreateValues.name ?? '').trim()
|
|
191
|
+
),
|
|
192
|
+
enabled: isDialogOpen,
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
const {
|
|
196
|
+
clearDraft: clearEditDraft,
|
|
197
|
+
loadDraft: loadEditDraft,
|
|
198
|
+
hasDraft: hasEditDraft,
|
|
199
|
+
savedAt: editDraftSavedAt,
|
|
200
|
+
} = useFormDraft<ContactTypeDraftPayload>({
|
|
201
|
+
storageKey: CONTACT_TYPE_EDIT_DRAFT_STORAGE_KEY,
|
|
202
|
+
value: {
|
|
203
|
+
mode: 'edit',
|
|
204
|
+
contactTypeId:
|
|
205
|
+
editingContactType?.contact_type_id || editingContactType?.id || null,
|
|
206
|
+
values: {
|
|
207
|
+
code: watchedEditValues.code ?? '',
|
|
208
|
+
name: watchedEditValues.name ?? '',
|
|
209
|
+
},
|
|
210
|
+
},
|
|
211
|
+
hasData: Boolean(
|
|
212
|
+
(watchedEditValues.code ?? '').trim() ||
|
|
213
|
+
(watchedEditValues.name ?? '').trim()
|
|
214
|
+
),
|
|
215
|
+
enabled: isEditDialogOpen,
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
const createDraftStatusContent = useMemo(() => {
|
|
219
|
+
if (!hasCreateDraft || !createDraftSavedAt) {
|
|
220
|
+
return null;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
const savedDate = new Date(createDraftSavedAt);
|
|
224
|
+
if (Number.isNaN(savedDate.getTime())) {
|
|
225
|
+
return null;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
const locale = currentLocaleCode.startsWith('pt') ? ptBR : enUS;
|
|
229
|
+
const relativeLabel = formatDistanceToNow(savedDate, {
|
|
230
|
+
addSuffix: true,
|
|
231
|
+
locale,
|
|
232
|
+
});
|
|
233
|
+
const absoluteLabel = formatDateTime(
|
|
234
|
+
savedDate,
|
|
235
|
+
getSettingValue,
|
|
236
|
+
currentLocaleCode
|
|
237
|
+
);
|
|
238
|
+
|
|
239
|
+
return currentLocaleCode.startsWith('pt')
|
|
240
|
+
? `Rascunho salvo ${relativeLabel} • Último salvamento: ${absoluteLabel}`
|
|
241
|
+
: `Draft saved ${relativeLabel} • Last saved: ${absoluteLabel}`;
|
|
242
|
+
}, [createDraftSavedAt, currentLocaleCode, getSettingValue, hasCreateDraft]);
|
|
243
|
+
|
|
244
|
+
const editDraftStatusContent = useMemo(() => {
|
|
245
|
+
if (!hasEditDraft || !editDraftSavedAt) {
|
|
246
|
+
return null;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
const savedDate = new Date(editDraftSavedAt);
|
|
250
|
+
if (Number.isNaN(savedDate.getTime())) {
|
|
251
|
+
return null;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
const locale = currentLocaleCode.startsWith('pt') ? ptBR : enUS;
|
|
255
|
+
const relativeLabel = formatDistanceToNow(savedDate, {
|
|
256
|
+
addSuffix: true,
|
|
257
|
+
locale,
|
|
258
|
+
});
|
|
259
|
+
const absoluteLabel = formatDateTime(
|
|
260
|
+
savedDate,
|
|
261
|
+
getSettingValue,
|
|
262
|
+
currentLocaleCode
|
|
263
|
+
);
|
|
264
|
+
|
|
265
|
+
return currentLocaleCode.startsWith('pt')
|
|
266
|
+
? `Rascunho salvo ${relativeLabel} • Último salvamento: ${absoluteLabel}`
|
|
267
|
+
: `Draft saved ${relativeLabel} • Last saved: ${absoluteLabel}`;
|
|
268
|
+
}, [editDraftSavedAt, currentLocaleCode, getSettingValue, hasEditDraft]);
|
|
269
|
+
|
|
270
|
+
const openCreateDialog = () => {
|
|
271
|
+
const storedDraft = loadCreateDraft();
|
|
272
|
+
|
|
273
|
+
form.reset(
|
|
274
|
+
storedDraft?.payload.mode === 'create'
|
|
275
|
+
? storedDraft.payload.values
|
|
276
|
+
: {
|
|
277
|
+
code: '',
|
|
278
|
+
name: '',
|
|
279
|
+
}
|
|
280
|
+
);
|
|
281
|
+
setIsDialogOpen(true);
|
|
282
|
+
};
|
|
283
|
+
|
|
132
284
|
const onSubmit = async (values: z.infer<typeof contactTypeSchema>) => {
|
|
133
285
|
const payload = {
|
|
134
286
|
code: values.code,
|
|
@@ -146,12 +298,13 @@ export default function ContactTypePage() {
|
|
|
146
298
|
data: payload,
|
|
147
299
|
});
|
|
148
300
|
|
|
301
|
+
clearCreateDraft();
|
|
149
302
|
toast.success(t('successCreate'));
|
|
150
303
|
setIsDialogOpen(false);
|
|
151
304
|
form.reset();
|
|
152
305
|
refetch();
|
|
153
|
-
} catch (error:
|
|
154
|
-
toast.error(error
|
|
306
|
+
} catch (error: unknown) {
|
|
307
|
+
toast.error(getErrorMessage(error, t('errorCreate')));
|
|
155
308
|
}
|
|
156
309
|
};
|
|
157
310
|
|
|
@@ -174,13 +327,14 @@ export default function ContactTypePage() {
|
|
|
174
327
|
data: payload,
|
|
175
328
|
});
|
|
176
329
|
|
|
330
|
+
clearEditDraft();
|
|
177
331
|
toast.success(t('successUpdate'));
|
|
178
332
|
setIsEditDialogOpen(false);
|
|
179
333
|
setEditingContactType(null);
|
|
180
334
|
editForm.reset();
|
|
181
335
|
refetch();
|
|
182
|
-
} catch (error:
|
|
183
|
-
toast.error(error
|
|
336
|
+
} catch (error: unknown) {
|
|
337
|
+
toast.error(getErrorMessage(error, t('errorUpdate')));
|
|
184
338
|
}
|
|
185
339
|
};
|
|
186
340
|
|
|
@@ -196,8 +350,8 @@ export default function ContactTypePage() {
|
|
|
196
350
|
setDeleteDialogOpen(false);
|
|
197
351
|
setDeletingId(null);
|
|
198
352
|
refetch();
|
|
199
|
-
} catch (error:
|
|
200
|
-
toast.error(error
|
|
353
|
+
} catch (error: unknown) {
|
|
354
|
+
toast.error(getErrorMessage(error, t('errorDelete')));
|
|
201
355
|
}
|
|
202
356
|
};
|
|
203
357
|
|
|
@@ -205,19 +359,39 @@ export default function ContactTypePage() {
|
|
|
205
359
|
(async () => {
|
|
206
360
|
setEditingContactType(contactType);
|
|
207
361
|
try {
|
|
208
|
-
const { data } = await request<
|
|
362
|
+
const { data } = await request<ContactTypeResponse>({
|
|
209
363
|
url: `/person-contact-type/${contactType.contact_type_id || contactType.id}?locale=${currentLocaleCode}`,
|
|
210
364
|
method: 'GET',
|
|
211
365
|
});
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
366
|
+
const storedDraft = loadEditDraft();
|
|
367
|
+
const shouldRestoreDraft =
|
|
368
|
+
storedDraft?.payload.mode === 'edit' &&
|
|
369
|
+
storedDraft.payload.contactTypeId ===
|
|
370
|
+
(contactType.contact_type_id || contactType.id);
|
|
371
|
+
|
|
372
|
+
editForm.reset(
|
|
373
|
+
shouldRestoreDraft
|
|
374
|
+
? storedDraft.payload.values
|
|
375
|
+
: {
|
|
376
|
+
code: data.code || contactType.code,
|
|
377
|
+
name: data.name || contactType.name || '',
|
|
378
|
+
}
|
|
379
|
+
);
|
|
216
380
|
} catch {
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
381
|
+
const storedDraft = loadEditDraft();
|
|
382
|
+
const shouldRestoreDraft =
|
|
383
|
+
storedDraft?.payload.mode === 'edit' &&
|
|
384
|
+
storedDraft.payload.contactTypeId ===
|
|
385
|
+
(contactType.contact_type_id || contactType.id);
|
|
386
|
+
|
|
387
|
+
editForm.reset(
|
|
388
|
+
shouldRestoreDraft
|
|
389
|
+
? storedDraft.payload.values
|
|
390
|
+
: {
|
|
391
|
+
code: contactType.code,
|
|
392
|
+
name: contactType.name || '',
|
|
393
|
+
}
|
|
394
|
+
);
|
|
221
395
|
}
|
|
222
396
|
setIsEditDialogOpen(true);
|
|
223
397
|
})();
|
|
@@ -235,7 +409,7 @@ export default function ContactTypePage() {
|
|
|
235
409
|
actions={[
|
|
236
410
|
{
|
|
237
411
|
label: t('buttonNewType'),
|
|
238
|
-
onClick:
|
|
412
|
+
onClick: openCreateDialog,
|
|
239
413
|
variant: 'default',
|
|
240
414
|
icon: <Plus />,
|
|
241
415
|
},
|
|
@@ -332,7 +506,7 @@ export default function ContactTypePage() {
|
|
|
332
506
|
title={t('noResults')}
|
|
333
507
|
description={t('emptyStateDescription')}
|
|
334
508
|
actionLabel={t('emptyStateAction')}
|
|
335
|
-
onAction={
|
|
509
|
+
onAction={openCreateDialog}
|
|
336
510
|
/>
|
|
337
511
|
</div>
|
|
338
512
|
)}
|
|
@@ -346,7 +520,15 @@ export default function ContactTypePage() {
|
|
|
346
520
|
pageSizeOptions={[10, 20, 30, 40, 50]}
|
|
347
521
|
/>
|
|
348
522
|
|
|
349
|
-
<Sheet
|
|
523
|
+
<Sheet
|
|
524
|
+
open={isDialogOpen}
|
|
525
|
+
onOpenChange={(open) => {
|
|
526
|
+
setIsDialogOpen(open);
|
|
527
|
+
if (!open) {
|
|
528
|
+
form.reset();
|
|
529
|
+
}
|
|
530
|
+
}}
|
|
531
|
+
>
|
|
350
532
|
<SheetContent className="w-full sm:max-w-md">
|
|
351
533
|
<SheetHeader>
|
|
352
534
|
<SheetTitle>{t('dialogNewTitle')}</SheetTitle>
|
|
@@ -392,6 +574,12 @@ export default function ContactTypePage() {
|
|
|
392
574
|
)}
|
|
393
575
|
/>
|
|
394
576
|
|
|
577
|
+
{createDraftStatusContent ? (
|
|
578
|
+
<p className="text-xs text-muted-foreground">
|
|
579
|
+
{createDraftStatusContent}
|
|
580
|
+
</p>
|
|
581
|
+
) : null}
|
|
582
|
+
|
|
395
583
|
<SheetFooter className="px-0">
|
|
396
584
|
<Button type="submit">{t('buttonCreate')}</Button>
|
|
397
585
|
</SheetFooter>
|
|
@@ -455,6 +643,12 @@ export default function ContactTypePage() {
|
|
|
455
643
|
)}
|
|
456
644
|
/>
|
|
457
645
|
|
|
646
|
+
{editDraftStatusContent ? (
|
|
647
|
+
<p className="text-xs text-muted-foreground">
|
|
648
|
+
{editDraftStatusContent}
|
|
649
|
+
</p>
|
|
650
|
+
) : null}
|
|
651
|
+
|
|
458
652
|
<SheetFooter className="px-0">
|
|
459
653
|
<Button type="submit">{t('buttonUpdate')}</Button>
|
|
460
654
|
</SheetFooter>
|