@hed-hog/contact 0.0.312 → 0.0.314
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/dist/person/dto/create.dto.d.ts +1 -0
- package/dist/person/dto/create.dto.d.ts.map +1 -1
- package/dist/person/dto/create.dto.js +5 -0
- package/dist/person/dto/create.dto.js.map +1 -1
- package/dist/person/dto/update.dto.d.ts +1 -0
- package/dist/person/dto/update.dto.d.ts.map +1 -1
- package/dist/person/dto/update.dto.js +7 -2
- package/dist/person/dto/update.dto.js.map +1 -1
- package/dist/person/person.controller.d.ts +8 -1
- package/dist/person/person.controller.d.ts.map +1 -1
- package/dist/person/person.controller.js +14 -3
- package/dist/person/person.controller.js.map +1 -1
- package/dist/person/person.service.d.ts +9 -1
- package/dist/person/person.service.d.ts.map +1 -1
- package/dist/person/person.service.js +79 -2
- package/dist/person/person.service.js.map +1 -1
- package/hedhog/data/route.yaml +8 -0
- package/hedhog/frontend/app/person/_components/person-form-sheet.tsx.ejs +266 -1
- package/hedhog/frontend/app/person/_components/person-types.ts.ejs +5 -0
- package/hedhog/frontend/messages/en.json +14 -0
- package/hedhog/frontend/messages/pt.json +14 -0
- package/hedhog/table/person_user.yaml +18 -0
- package/package.json +5 -5
- package/src/person/dto/create.dto.ts +11 -7
- package/src/person/dto/update.dto.ts +15 -11
- package/src/person/person.controller.ts +27 -22
- package/src/person/person.service.ts +82 -3
package/hedhog/data/route.yaml
CHANGED
|
@@ -61,6 +61,14 @@
|
|
|
61
61
|
slug: admin
|
|
62
62
|
- where:
|
|
63
63
|
slug: admin-contact
|
|
64
|
+
- url: /person/linked-user-options
|
|
65
|
+
method: GET
|
|
66
|
+
relations:
|
|
67
|
+
role:
|
|
68
|
+
- where:
|
|
69
|
+
slug: admin
|
|
70
|
+
- where:
|
|
71
|
+
slug: admin-contact
|
|
64
72
|
- url: /person/followups
|
|
65
73
|
method: GET
|
|
66
74
|
relations: *a3
|
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
CollapsibleContent,
|
|
19
19
|
CollapsibleTrigger,
|
|
20
20
|
} from '@/components/ui/collapsible';
|
|
21
|
+
import { EntityPicker } from '@/components/ui/entity-picker';
|
|
21
22
|
import { Input } from '@/components/ui/input';
|
|
22
23
|
import { Label } from '@/components/ui/label';
|
|
23
24
|
import {
|
|
@@ -59,16 +60,20 @@ import {
|
|
|
59
60
|
Calendar as CalendarIcon,
|
|
60
61
|
ChevronDown,
|
|
61
62
|
ChevronUp,
|
|
63
|
+
Eye,
|
|
64
|
+
EyeOff,
|
|
62
65
|
FileText,
|
|
63
66
|
Loader2,
|
|
64
67
|
Mail,
|
|
65
68
|
MapPin,
|
|
66
69
|
Plus,
|
|
70
|
+
RefreshCw,
|
|
67
71
|
Save,
|
|
68
72
|
Star,
|
|
69
73
|
Trash2,
|
|
70
74
|
Upload,
|
|
71
75
|
User,
|
|
76
|
+
UserRound,
|
|
72
77
|
} from 'lucide-react';
|
|
73
78
|
import { useTranslations } from 'next-intl';
|
|
74
79
|
import {
|
|
@@ -176,6 +181,7 @@ type PersonSubmitPayload = {
|
|
|
176
181
|
job_title: string | null;
|
|
177
182
|
employer_company_id: number | null;
|
|
178
183
|
owner_user_id: number | null;
|
|
184
|
+
user_id: number | null;
|
|
179
185
|
source: PersonSource | null;
|
|
180
186
|
lifecycle_stage: PersonLifecycleStage | null;
|
|
181
187
|
next_action_at: string | null;
|
|
@@ -230,6 +236,8 @@ type PersonDraftPayload = {
|
|
|
230
236
|
documents: EditablePersonDocument[];
|
|
231
237
|
avatarId: number | null;
|
|
232
238
|
avatarPreviewUrl: string;
|
|
239
|
+
linkedUserId: number | null;
|
|
240
|
+
linkedUserLabel: string;
|
|
233
241
|
};
|
|
234
242
|
|
|
235
243
|
const PERSON_FORM_DRAFT_STORAGE_KEY = 'contact-person-form-draft';
|
|
@@ -800,6 +808,18 @@ export function PersonFormSheet({
|
|
|
800
808
|
const [contacts, setContacts] = useState<EditablePersonContact[]>([]);
|
|
801
809
|
const [addresses, setAddresses] = useState<EditablePersonAddress[]>([]);
|
|
802
810
|
const [documents, setDocuments] = useState<EditablePersonDocument[]>([]);
|
|
811
|
+
const [linkedUserId, setLinkedUserId] = useState<number | null>(null);
|
|
812
|
+
const [linkedUserLabel, setLinkedUserLabel] = useState<string>('');
|
|
813
|
+
const [userCreateShowPassword, setUserCreateShowPassword] = useState(false);
|
|
814
|
+
const [usersOpen, setUsersOpen] = useState(true);
|
|
815
|
+
|
|
816
|
+
const generatePassword = () => {
|
|
817
|
+
const chars =
|
|
818
|
+
'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*';
|
|
819
|
+
const array = new Uint8Array(16);
|
|
820
|
+
crypto.getRandomValues(array);
|
|
821
|
+
return Array.from(array, (byte) => chars[byte % chars.length]).join('');
|
|
822
|
+
};
|
|
803
823
|
const [contactsOpen, setContactsOpen] = useState(true);
|
|
804
824
|
const [addressesOpen, setAddressesOpen] = useState(true);
|
|
805
825
|
const [documentsOpen, setDocumentsOpen] = useState(true);
|
|
@@ -862,11 +882,12 @@ export function PersonFormSheet({
|
|
|
862
882
|
watchedFormValues.source ||
|
|
863
883
|
(watchedFormValues.lifecycle_stage ?? 'new') !== 'new' ||
|
|
864
884
|
avatarId != null ||
|
|
885
|
+
linkedUserId != null ||
|
|
865
886
|
contacts.length > 0 ||
|
|
866
887
|
addresses.length > 0 ||
|
|
867
888
|
documents.length > 0
|
|
868
889
|
),
|
|
869
|
-
[watchedFormValues, avatarId, contacts, addresses, documents]
|
|
890
|
+
[watchedFormValues, avatarId, linkedUserId, contacts, addresses, documents]
|
|
870
891
|
);
|
|
871
892
|
|
|
872
893
|
const draftValue = useMemo<PersonDraftPayload>(
|
|
@@ -898,6 +919,8 @@ export function PersonFormSheet({
|
|
|
898
919
|
documents,
|
|
899
920
|
avatarId,
|
|
900
921
|
avatarPreviewUrl,
|
|
922
|
+
linkedUserId,
|
|
923
|
+
linkedUserLabel,
|
|
901
924
|
}),
|
|
902
925
|
[
|
|
903
926
|
watchedFormValues,
|
|
@@ -906,6 +929,8 @@ export function PersonFormSheet({
|
|
|
906
929
|
documents,
|
|
907
930
|
avatarId,
|
|
908
931
|
avatarPreviewUrl,
|
|
932
|
+
linkedUserId,
|
|
933
|
+
linkedUserLabel,
|
|
909
934
|
person?.id,
|
|
910
935
|
]
|
|
911
936
|
);
|
|
@@ -1074,6 +1099,15 @@ export function PersonFormSheet({
|
|
|
1074
1099
|
setContactsOpen(true);
|
|
1075
1100
|
setAddressesOpen(true);
|
|
1076
1101
|
setDocumentsOpen(true);
|
|
1102
|
+
setUsersOpen(true);
|
|
1103
|
+
setLinkedUserId(
|
|
1104
|
+
restoredDraft?.linkedUserId ?? person?.person_user?.[0]?.user_id ?? null
|
|
1105
|
+
);
|
|
1106
|
+
setLinkedUserLabel(
|
|
1107
|
+
restoredDraft?.linkedUserLabel ??
|
|
1108
|
+
person?.person_user?.[0]?.user?.name ??
|
|
1109
|
+
''
|
|
1110
|
+
);
|
|
1077
1111
|
setLoadingCEP({});
|
|
1078
1112
|
setDuplicateDialogOpen(false);
|
|
1079
1113
|
setPendingDuplicateSubmission(null);
|
|
@@ -1638,6 +1672,7 @@ export function PersonFormSheet({
|
|
|
1638
1672
|
? (values.employer_company_id ?? null)
|
|
1639
1673
|
: null,
|
|
1640
1674
|
owner_user_id: values.owner_user_id ?? null,
|
|
1675
|
+
user_id: linkedUserId,
|
|
1641
1676
|
source: values.source || null,
|
|
1642
1677
|
lifecycle_stage: values.lifecycle_stage || 'new',
|
|
1643
1678
|
next_action_at: values.next_action_at
|
|
@@ -1847,6 +1882,7 @@ export function PersonFormSheet({
|
|
|
1847
1882
|
(item) => item.document_type_id === document.document_type_id
|
|
1848
1883
|
) ?? undefined,
|
|
1849
1884
|
})),
|
|
1885
|
+
person_user: payload.user_id != null ? [{ user_id: payload.user_id }] : [],
|
|
1850
1886
|
});
|
|
1851
1887
|
|
|
1852
1888
|
const finalizeSuccess = async (personId: number, fallbackPerson?: Person) => {
|
|
@@ -2432,6 +2468,235 @@ export function PersonFormSheet({
|
|
|
2432
2468
|
|
|
2433
2469
|
<Separator />
|
|
2434
2470
|
|
|
2471
|
+
<Collapsible open={usersOpen} onOpenChange={setUsersOpen}>
|
|
2472
|
+
<CollapsibleTrigger asChild>
|
|
2473
|
+
<div className="group flex cursor-pointer items-center justify-between">
|
|
2474
|
+
<div className="flex items-center gap-2">
|
|
2475
|
+
<UserRound className="h-4 w-4 text-violet-500" />
|
|
2476
|
+
<h3 className="text-sm font-semibold">
|
|
2477
|
+
{t('linkedUser')}
|
|
2478
|
+
</h3>
|
|
2479
|
+
{linkedUserId != null ? (
|
|
2480
|
+
<Badge
|
|
2481
|
+
variant="secondary"
|
|
2482
|
+
className="bg-violet-500/10 text-violet-600"
|
|
2483
|
+
>
|
|
2484
|
+
1
|
|
2485
|
+
</Badge>
|
|
2486
|
+
) : null}
|
|
2487
|
+
</div>
|
|
2488
|
+
<div className="flex items-center gap-1">
|
|
2489
|
+
{usersOpen ? (
|
|
2490
|
+
<ChevronUp className="h-4 w-4" />
|
|
2491
|
+
) : (
|
|
2492
|
+
<ChevronDown className="h-4 w-4" />
|
|
2493
|
+
)}
|
|
2494
|
+
</div>
|
|
2495
|
+
</div>
|
|
2496
|
+
</CollapsibleTrigger>
|
|
2497
|
+
|
|
2498
|
+
<CollapsibleContent className="mt-2">
|
|
2499
|
+
<EntityPicker
|
|
2500
|
+
value={linkedUserId}
|
|
2501
|
+
onChange={(val, opt) => {
|
|
2502
|
+
setLinkedUserId(val as number | null);
|
|
2503
|
+
setLinkedUserLabel(
|
|
2504
|
+
opt
|
|
2505
|
+
? ((opt as { name?: string; label?: string }).name ??
|
|
2506
|
+
(opt as { name?: string; label?: string })
|
|
2507
|
+
.label ??
|
|
2508
|
+
'')
|
|
2509
|
+
: ''
|
|
2510
|
+
);
|
|
2511
|
+
}}
|
|
2512
|
+
placeholder={t('selectLinkedUser')}
|
|
2513
|
+
entityLabel={t('user')}
|
|
2514
|
+
clearable
|
|
2515
|
+
valueType="number"
|
|
2516
|
+
initialSelectedLabel={linkedUserLabel}
|
|
2517
|
+
loadOptions={async ({ search }) => {
|
|
2518
|
+
const params = new URLSearchParams();
|
|
2519
|
+
if (search) params.set('search', search);
|
|
2520
|
+
const response = await request<
|
|
2521
|
+
Array<{ id: number; name: string; email?: string }>
|
|
2522
|
+
>({
|
|
2523
|
+
url: `/person/linked-user-options?${params.toString()}`,
|
|
2524
|
+
method: 'GET',
|
|
2525
|
+
});
|
|
2526
|
+
const items = Array.isArray(response.data)
|
|
2527
|
+
? response.data
|
|
2528
|
+
: [];
|
|
2529
|
+
return { items, hasMore: false };
|
|
2530
|
+
}}
|
|
2531
|
+
getOptionValue={(opt) => (opt as { id: number }).id}
|
|
2532
|
+
getOptionLabel={(opt) =>
|
|
2533
|
+
(opt as { name: string; email?: string }).name
|
|
2534
|
+
}
|
|
2535
|
+
getOptionDescription={(opt) =>
|
|
2536
|
+
(opt as { email?: string }).email
|
|
2537
|
+
}
|
|
2538
|
+
showCreateButton
|
|
2539
|
+
createTitle={t('createUserTitle')}
|
|
2540
|
+
mapSearchToCreateValues={(search) => ({
|
|
2541
|
+
name: search,
|
|
2542
|
+
password: generatePassword(),
|
|
2543
|
+
})}
|
|
2544
|
+
createFields={[
|
|
2545
|
+
{
|
|
2546
|
+
name: 'name',
|
|
2547
|
+
label: t('userName'),
|
|
2548
|
+
placeholder: t('userNamePlaceholder'),
|
|
2549
|
+
required: true,
|
|
2550
|
+
},
|
|
2551
|
+
{
|
|
2552
|
+
name: 'email',
|
|
2553
|
+
label: t('userEmail'),
|
|
2554
|
+
placeholder: t('userEmailPlaceholder'),
|
|
2555
|
+
type: 'email',
|
|
2556
|
+
required: true,
|
|
2557
|
+
},
|
|
2558
|
+
{
|
|
2559
|
+
name: 'password',
|
|
2560
|
+
label: t('userPassword'),
|
|
2561
|
+
placeholder: t('userPasswordPlaceholder'),
|
|
2562
|
+
type: 'password',
|
|
2563
|
+
required: true,
|
|
2564
|
+
},
|
|
2565
|
+
]}
|
|
2566
|
+
renderCreateContent={(ctx) => (
|
|
2567
|
+
<div className="mt-6 space-y-4">
|
|
2568
|
+
<div className="space-y-2">
|
|
2569
|
+
<Label>
|
|
2570
|
+
{t('userName')}{' '}
|
|
2571
|
+
<span className="text-destructive">*</span>
|
|
2572
|
+
</Label>
|
|
2573
|
+
<Input
|
|
2574
|
+
value={ctx.values.name ?? ''}
|
|
2575
|
+
onChange={(e) =>
|
|
2576
|
+
ctx.setValue('name', e.target.value)
|
|
2577
|
+
}
|
|
2578
|
+
placeholder={t('userNamePlaceholder')}
|
|
2579
|
+
/>
|
|
2580
|
+
</div>
|
|
2581
|
+
<div className="space-y-2">
|
|
2582
|
+
<Label>
|
|
2583
|
+
{t('userEmail')}{' '}
|
|
2584
|
+
<span className="text-destructive">*</span>
|
|
2585
|
+
</Label>
|
|
2586
|
+
<Input
|
|
2587
|
+
type="email"
|
|
2588
|
+
value={ctx.values.email ?? ''}
|
|
2589
|
+
onChange={(e) =>
|
|
2590
|
+
ctx.setValue('email', e.target.value)
|
|
2591
|
+
}
|
|
2592
|
+
placeholder={t('userEmailPlaceholder')}
|
|
2593
|
+
/>
|
|
2594
|
+
</div>
|
|
2595
|
+
<div className="space-y-2">
|
|
2596
|
+
<Label>
|
|
2597
|
+
{t('userPassword')}{' '}
|
|
2598
|
+
<span className="text-destructive">*</span>
|
|
2599
|
+
</Label>
|
|
2600
|
+
<div className="flex gap-2">
|
|
2601
|
+
<div className="relative flex-1">
|
|
2602
|
+
<Input
|
|
2603
|
+
type={
|
|
2604
|
+
userCreateShowPassword ? 'text' : 'password'
|
|
2605
|
+
}
|
|
2606
|
+
value={ctx.values.password ?? ''}
|
|
2607
|
+
onChange={(e) =>
|
|
2608
|
+
ctx.setValue('password', e.target.value)
|
|
2609
|
+
}
|
|
2610
|
+
placeholder={t('userPasswordPlaceholder')}
|
|
2611
|
+
className="pr-10"
|
|
2612
|
+
/>
|
|
2613
|
+
<Button
|
|
2614
|
+
type="button"
|
|
2615
|
+
variant="ghost"
|
|
2616
|
+
size="icon"
|
|
2617
|
+
className="absolute right-0 top-0 h-full px-3 hover:bg-transparent"
|
|
2618
|
+
onClick={() =>
|
|
2619
|
+
setUserCreateShowPassword((v) => !v)
|
|
2620
|
+
}
|
|
2621
|
+
tabIndex={-1}
|
|
2622
|
+
>
|
|
2623
|
+
{userCreateShowPassword ? (
|
|
2624
|
+
<EyeOff className="h-4 w-4" />
|
|
2625
|
+
) : (
|
|
2626
|
+
<Eye className="h-4 w-4" />
|
|
2627
|
+
)}
|
|
2628
|
+
</Button>
|
|
2629
|
+
</div>
|
|
2630
|
+
<Button
|
|
2631
|
+
type="button"
|
|
2632
|
+
variant="outline"
|
|
2633
|
+
size="icon"
|
|
2634
|
+
className="h-10 w-10 shrink-0"
|
|
2635
|
+
title={t('generatePassword')}
|
|
2636
|
+
onClick={() =>
|
|
2637
|
+
ctx.setValue('password', generatePassword())
|
|
2638
|
+
}
|
|
2639
|
+
>
|
|
2640
|
+
<RefreshCw className="h-4 w-4" />
|
|
2641
|
+
</Button>
|
|
2642
|
+
</div>
|
|
2643
|
+
</div>
|
|
2644
|
+
<div className="flex justify-end gap-2">
|
|
2645
|
+
<Button
|
|
2646
|
+
type="button"
|
|
2647
|
+
variant="outline"
|
|
2648
|
+
onClick={ctx.closeCreate}
|
|
2649
|
+
>
|
|
2650
|
+
{t('cancel')}
|
|
2651
|
+
</Button>
|
|
2652
|
+
<Button
|
|
2653
|
+
type="button"
|
|
2654
|
+
disabled={
|
|
2655
|
+
ctx.isCreating ||
|
|
2656
|
+
!ctx.values.name?.trim() ||
|
|
2657
|
+
!ctx.values.email?.trim() ||
|
|
2658
|
+
!ctx.values.password?.trim()
|
|
2659
|
+
}
|
|
2660
|
+
onClick={() => void ctx.submitCreate()}
|
|
2661
|
+
>
|
|
2662
|
+
{ctx.isCreating ? (
|
|
2663
|
+
<Loader2 className="h-4 w-4 animate-spin" />
|
|
2664
|
+
) : null}
|
|
2665
|
+
{t('save')}
|
|
2666
|
+
</Button>
|
|
2667
|
+
</div>
|
|
2668
|
+
</div>
|
|
2669
|
+
)}
|
|
2670
|
+
searchPlaceholder={t('searchUserPlaceholder')}
|
|
2671
|
+
emptyStateDescription={t('noUsersFound')}
|
|
2672
|
+
createActionLabel={t('createUser')}
|
|
2673
|
+
onCreate={async (values) => {
|
|
2674
|
+
const v = values as Record<string, string>;
|
|
2675
|
+
const response = await request<{
|
|
2676
|
+
user: { id: number; name: string };
|
|
2677
|
+
}>({
|
|
2678
|
+
url: '/user',
|
|
2679
|
+
method: 'POST',
|
|
2680
|
+
data: {
|
|
2681
|
+
name: v.name,
|
|
2682
|
+
email: v.email,
|
|
2683
|
+
password: v.password,
|
|
2684
|
+
},
|
|
2685
|
+
});
|
|
2686
|
+
const user = response.data?.user;
|
|
2687
|
+
if (!user?.id) return null;
|
|
2688
|
+
return {
|
|
2689
|
+
id: user.id,
|
|
2690
|
+
name: user.name,
|
|
2691
|
+
email: v.email,
|
|
2692
|
+
};
|
|
2693
|
+
}}
|
|
2694
|
+
/>
|
|
2695
|
+
</CollapsibleContent>
|
|
2696
|
+
</Collapsible>
|
|
2697
|
+
|
|
2698
|
+
<Separator />
|
|
2699
|
+
|
|
2435
2700
|
<Collapsible open={contactsOpen} onOpenChange={setContactsOpen}>
|
|
2436
2701
|
<CollapsibleTrigger asChild>
|
|
2437
2702
|
<div className="group flex cursor-pointer items-center justify-between">
|
|
@@ -91,6 +91,7 @@ export type PersonInteractionType =
|
|
|
91
91
|
export type UserOption = {
|
|
92
92
|
id: number;
|
|
93
93
|
name: string;
|
|
94
|
+
email?: string;
|
|
94
95
|
photo_id?: number | null;
|
|
95
96
|
};
|
|
96
97
|
|
|
@@ -140,6 +141,10 @@ export type Person = {
|
|
|
140
141
|
contact?: PersonContact[];
|
|
141
142
|
address?: PersonAddress[];
|
|
142
143
|
document?: PersonDocument[];
|
|
144
|
+
person_user?: Array<{
|
|
145
|
+
user_id: number;
|
|
146
|
+
user?: { id: number; name: string; email?: string };
|
|
147
|
+
}>;
|
|
143
148
|
};
|
|
144
149
|
|
|
145
150
|
export type PersonInteraction = {
|
|
@@ -47,6 +47,20 @@
|
|
|
47
47
|
"nameRequired": "Name must be at least 2 characters",
|
|
48
48
|
"tabGeneral": "General",
|
|
49
49
|
"tabContacts": "Contacts",
|
|
50
|
+
"linkedUser": "Linked User",
|
|
51
|
+
"selectLinkedUser": "Select a user",
|
|
52
|
+
"user": "User",
|
|
53
|
+
"createUserTitle": "Create User",
|
|
54
|
+
"createUser": "Create User",
|
|
55
|
+
"userName": "Name",
|
|
56
|
+
"userNamePlaceholder": "Full name",
|
|
57
|
+
"userEmail": "Email",
|
|
58
|
+
"userEmailPlaceholder": "email@example.com",
|
|
59
|
+
"userPassword": "Password",
|
|
60
|
+
"userPasswordPlaceholder": "Strong password",
|
|
61
|
+
"generatePassword": "Generate password",
|
|
62
|
+
"searchUserPlaceholder": "Search user...",
|
|
63
|
+
"noUsersFound": "No users found",
|
|
50
64
|
"tabAddresses": "Addresses",
|
|
51
65
|
"tabDocuments": "Documents",
|
|
52
66
|
"contactType": "Contact Type",
|
|
@@ -46,6 +46,20 @@
|
|
|
46
46
|
"nameRequired": "O nome deve ter pelo menos 2 caracteres",
|
|
47
47
|
"tabGeneral": "Geral",
|
|
48
48
|
"tabContacts": "Contatos",
|
|
49
|
+
"linkedUser": "Usuário Vinculado",
|
|
50
|
+
"selectLinkedUser": "Selecionar um usuário",
|
|
51
|
+
"user": "Usuário",
|
|
52
|
+
"createUserTitle": "Criar Usuário",
|
|
53
|
+
"createUser": "Criar Usuário",
|
|
54
|
+
"userName": "Nome",
|
|
55
|
+
"userNamePlaceholder": "Nome completo",
|
|
56
|
+
"userEmail": "Email",
|
|
57
|
+
"userEmailPlaceholder": "email@exemplo.com",
|
|
58
|
+
"userPassword": "Senha",
|
|
59
|
+
"userPasswordPlaceholder": "Senha forte",
|
|
60
|
+
"generatePassword": "Gerar senha",
|
|
61
|
+
"searchUserPlaceholder": "Buscar usuário...",
|
|
62
|
+
"noUsersFound": "Nenhum usuário encontrado",
|
|
49
63
|
"tabAddresses": "Endereços",
|
|
50
64
|
"tabDocuments": "Documentos",
|
|
51
65
|
"contactType": "Tipo de Contato",
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
columns:
|
|
2
|
+
- type: pk
|
|
3
|
+
- name: person_id
|
|
4
|
+
type: fk
|
|
5
|
+
references:
|
|
6
|
+
table: person
|
|
7
|
+
column: id
|
|
8
|
+
onDelete: CASCADE
|
|
9
|
+
onUpdate: CASCADE
|
|
10
|
+
- name: user_id
|
|
11
|
+
type: fk
|
|
12
|
+
references:
|
|
13
|
+
table: user
|
|
14
|
+
column: id
|
|
15
|
+
onDelete: CASCADE
|
|
16
|
+
onUpdate: CASCADE
|
|
17
|
+
- type: created_at
|
|
18
|
+
- type: updated_at
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hed-hog/contact",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.314",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"types": "dist/index.d.ts",
|
|
6
6
|
"dependencies": {
|
|
@@ -9,13 +9,13 @@
|
|
|
9
9
|
"@nestjs/core": "^11",
|
|
10
10
|
"@nestjs/jwt": "^11",
|
|
11
11
|
"@nestjs/mapped-types": "*",
|
|
12
|
+
"@hed-hog/core": "0.0.314",
|
|
13
|
+
"@hed-hog/address": "0.0.314",
|
|
12
14
|
"@hed-hog/api-mail": "0.0.9",
|
|
13
15
|
"@hed-hog/api": "0.0.6",
|
|
14
|
-
"@hed-hog/core": "0.0.312",
|
|
15
|
-
"@hed-hog/api-locale": "0.0.14",
|
|
16
|
-
"@hed-hog/api-pagination": "0.0.7",
|
|
17
16
|
"@hed-hog/api-prisma": "0.0.6",
|
|
18
|
-
"@hed-hog/
|
|
17
|
+
"@hed-hog/api-locale": "0.0.14",
|
|
18
|
+
"@hed-hog/api-pagination": "0.0.7"
|
|
19
19
|
},
|
|
20
20
|
"exports": {
|
|
21
21
|
".": {
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { getLocaleText } from '@hed-hog/api-locale';
|
|
2
2
|
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
3
|
+
IsArray,
|
|
4
|
+
IsDateString,
|
|
5
|
+
IsEnum,
|
|
6
|
+
IsInt,
|
|
7
|
+
IsNumber,
|
|
8
|
+
IsOptional,
|
|
9
|
+
IsString,
|
|
10
10
|
} from 'class-validator';
|
|
11
11
|
|
|
12
12
|
export enum PersonType {
|
|
@@ -94,6 +94,10 @@ export class CreateDTO {
|
|
|
94
94
|
@IsInt({ message: (args) => getLocaleText('validation.idMustBeInteger', args.value) })
|
|
95
95
|
owner_user_id?: number | null;
|
|
96
96
|
|
|
97
|
+
@IsOptional()
|
|
98
|
+
@IsInt({ message: (args) => getLocaleText('validation.idMustBeInteger', args.value) })
|
|
99
|
+
user_id?: number | null;
|
|
100
|
+
|
|
97
101
|
@IsOptional()
|
|
98
102
|
@IsEnum(PersonSource, { message: (args) => getLocaleText('validation.typeMustBeEnum', args.value) })
|
|
99
103
|
source?: PersonSource | null;
|
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
import { AddressTypeEnum } from '../../address-type.enum';
|
|
2
|
-
import { PersonGender, PersonLifecycleStage, PersonSource } from './create.dto';
|
|
3
1
|
import { getLocaleText } from '@hed-hog/api-locale';
|
|
4
2
|
import { Type } from 'class-transformer';
|
|
5
3
|
import {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
4
|
+
IsArray,
|
|
5
|
+
IsBoolean,
|
|
6
|
+
IsDateString,
|
|
7
|
+
IsEnum,
|
|
8
|
+
IsInt,
|
|
9
|
+
IsNumber,
|
|
10
|
+
IsOptional,
|
|
11
|
+
IsString,
|
|
12
|
+
ValidateNested,
|
|
15
13
|
} from 'class-validator';
|
|
14
|
+
import { AddressTypeEnum } from '../../address-type.enum';
|
|
15
|
+
import { PersonGender, PersonLifecycleStage, PersonSource } from './create.dto';
|
|
16
16
|
|
|
17
17
|
export class UpdateAllContactDTO {
|
|
18
18
|
@IsOptional()
|
|
@@ -126,6 +126,10 @@ export class UpdateAllPersonDTO {
|
|
|
126
126
|
@IsInt({ message: (args) => getLocaleText('validation.idMustBeInteger', args.value) })
|
|
127
127
|
owner_user_id?: number | null;
|
|
128
128
|
|
|
129
|
+
@IsOptional()
|
|
130
|
+
@IsInt({ message: (args) => getLocaleText('validation.idMustBeInteger', args.value) })
|
|
131
|
+
user_id?: number | null;
|
|
132
|
+
|
|
129
133
|
@IsOptional()
|
|
130
134
|
@IsEnum(PersonSource, { message: (args) => getLocaleText('validation.typeMustBeEnum', args.value) })
|
|
131
135
|
source?: PersonSource | null;
|
|
@@ -2,28 +2,28 @@ import { DeleteDTO, Public, Role, User } from '@hed-hog/api';
|
|
|
2
2
|
import { Locale } from '@hed-hog/api-locale';
|
|
3
3
|
import { Pagination } from '@hed-hog/api-pagination';
|
|
4
4
|
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
5
|
+
BadRequestException,
|
|
6
|
+
Body,
|
|
7
|
+
Controller,
|
|
8
|
+
Delete,
|
|
9
|
+
Get,
|
|
10
|
+
Inject,
|
|
11
|
+
Param,
|
|
12
|
+
ParseIntPipe,
|
|
13
|
+
Patch,
|
|
14
|
+
Post,
|
|
15
|
+
Query,
|
|
16
|
+
Res,
|
|
17
|
+
UploadedFile,
|
|
18
|
+
UseInterceptors,
|
|
19
|
+
forwardRef
|
|
20
20
|
} from '@nestjs/common';
|
|
21
21
|
import { FileInterceptor } from '@nestjs/platform-express';
|
|
22
22
|
import { Response } from 'express';
|
|
23
23
|
import {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
24
|
+
AccountListQueryDTO,
|
|
25
|
+
CreateAccountDTO,
|
|
26
|
+
UpdateAccountDTO,
|
|
27
27
|
} from './dto/account.dto';
|
|
28
28
|
import { ActivityListQueryDTO } from './dto/activity.dto';
|
|
29
29
|
import { CreateFollowupDTO } from './dto/create-followup.dto';
|
|
@@ -32,8 +32,8 @@ import { CreateDTO } from './dto/create.dto';
|
|
|
32
32
|
import { DashboardQueryDTO } from './dto/dashboard-query.dto';
|
|
33
33
|
import { CheckPersonDuplicatesQueryDTO } from './dto/duplicates-query.dto';
|
|
34
34
|
import {
|
|
35
|
-
|
|
36
|
-
|
|
35
|
+
FollowupListQueryDTO,
|
|
36
|
+
FollowupStatsQueryDTO,
|
|
37
37
|
} from './dto/followup-query.dto';
|
|
38
38
|
import { MergePersonDTO } from './dto/merge.dto';
|
|
39
39
|
import { ReportsQueryDTO } from './dto/reports-query.dto';
|
|
@@ -89,8 +89,13 @@ export class PersonController {
|
|
|
89
89
|
}
|
|
90
90
|
|
|
91
91
|
@Get('owner-options')
|
|
92
|
-
async getOwnerOptions(@User() user) {
|
|
93
|
-
return this.personService.getOwnerOptions(Number(user?.id || 0));
|
|
92
|
+
async getOwnerOptions(@User() user, @Query('search') search?: string) {
|
|
93
|
+
return this.personService.getOwnerOptions(Number(user?.id || 0), search);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
@Get('linked-user-options')
|
|
97
|
+
async getLinkedUserOptions(@Query('search') search?: string) {
|
|
98
|
+
return this.personService.getLinkedUserOptions(search);
|
|
94
99
|
}
|
|
95
100
|
|
|
96
101
|
@Get('duplicates')
|