@hed-hog/contact 0.0.333 → 0.0.338
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/person.controller.d.ts +6 -7
- package/dist/person/person.controller.d.ts.map +1 -1
- package/dist/person/person.controller.js +7 -3
- package/dist/person/person.controller.js.map +1 -1
- package/dist/person/person.service.d.ts +5 -2
- package/dist/person/person.service.d.ts.map +1 -1
- package/dist/person/person.service.js +57 -5
- package/dist/person/person.service.js.map +1 -1
- package/hedhog/data/role.yaml +9 -1
- package/hedhog/frontend/app/_components/person-picker.tsx.ejs +4 -4
- package/hedhog/frontend/app/accounts/page.tsx.ejs +6 -1
- package/hedhog/frontend/app/activities/page.tsx.ejs +6 -1
- package/hedhog/frontend/app/contact-type/page.tsx.ejs +6 -1
- package/hedhog/frontend/app/document-type/page.tsx.ejs +6 -1
- package/hedhog/frontend/app/follow-ups/page.tsx.ejs +6 -1
- package/hedhog/frontend/app/person/_components/person-form-sheet.tsx.ejs +14 -24
- package/hedhog/frontend/app/person/_components/person-import-sheet.tsx.ejs +102 -102
- package/hedhog/frontend/app/person/page.tsx.ejs +6 -1
- package/hedhog/frontend/app/proposals/_components/proposal-form-sheet.tsx.ejs +1306 -1306
- package/hedhog/frontend/app/proposals/_components/proposal-types.ts.ejs +172 -172
- package/hedhog/frontend/app/proposals/_components/proposals-management-page.tsx.ejs +74 -95
- package/package.json +6 -6
- package/src/person/person.controller.ts +29 -22
- package/src/person/person.service.ts +94 -25
|
@@ -4,40 +4,40 @@ import { PaginationDTO, PaginationService } from '@hed-hog/api-pagination';
|
|
|
4
4
|
import { Prisma, PrismaService } from '@hed-hog/api-prisma';
|
|
5
5
|
import { FileService, IntegrationDeveloperApiService, SettingService } from '@hed-hog/core';
|
|
6
6
|
import {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
7
|
+
BadRequestException,
|
|
8
|
+
Inject,
|
|
9
|
+
Injectable,
|
|
10
|
+
Logger,
|
|
11
|
+
NotFoundException,
|
|
12
|
+
forwardRef,
|
|
13
13
|
} from '@nestjs/common';
|
|
14
14
|
import {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
15
|
+
ACCOUNT_LIFECYCLE_STAGES,
|
|
16
|
+
type AccountLifecycleStage,
|
|
17
|
+
type CreateAccountDTO,
|
|
18
|
+
type UpdateAccountDTO,
|
|
19
19
|
} from './dto/account.dto';
|
|
20
20
|
import {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
21
|
+
type ActivityListQueryDTO,
|
|
22
|
+
type CrmActivityPriority,
|
|
23
|
+
type CrmActivitySourceKind,
|
|
24
|
+
type CrmActivityStatus,
|
|
25
|
+
type CrmActivityType,
|
|
26
26
|
} from './dto/activity.dto';
|
|
27
27
|
import { CreateFollowupDTO } from './dto/create-followup.dto';
|
|
28
28
|
import {
|
|
29
|
-
|
|
30
|
-
|
|
29
|
+
CreateInteractionDTO,
|
|
30
|
+
PersonInteractionTypeDTO,
|
|
31
31
|
} from './dto/create-interaction.dto';
|
|
32
32
|
import { CreateDTO } from './dto/create.dto';
|
|
33
33
|
import {
|
|
34
|
-
|
|
35
|
-
|
|
34
|
+
type CrmDashboardPeriod,
|
|
35
|
+
type DashboardQueryDTO,
|
|
36
36
|
} from './dto/dashboard-query.dto';
|
|
37
37
|
import { CheckPersonDuplicatesQueryDTO } from './dto/duplicates-query.dto';
|
|
38
38
|
import {
|
|
39
|
-
|
|
40
|
-
|
|
39
|
+
FollowupListQueryDTO,
|
|
40
|
+
FollowupStatsQueryDTO,
|
|
41
41
|
} from './dto/followup-query.dto';
|
|
42
42
|
import { CRM_IMPORT_FIELDS } from './dto/import.dto';
|
|
43
43
|
import { MergePersonDTO } from './dto/merge.dto';
|
|
@@ -52,6 +52,11 @@ const CONTACT_OWNER_ALLOWED_ROLE_SLUGS = [
|
|
|
52
52
|
'admin-contact',
|
|
53
53
|
CONTACT_OWNER_ROLE_SLUG,
|
|
54
54
|
];
|
|
55
|
+
const PERSON_USER_LINKER_ALLOWED_ROLE_SLUGS = [
|
|
56
|
+
'admin',
|
|
57
|
+
'admin-contact',
|
|
58
|
+
'person-user-linker',
|
|
59
|
+
];
|
|
55
60
|
type PersonActivityAction = 'created' | 'updated' | 'interaction_created';
|
|
56
61
|
const EMPLOYER_COMPANY_METADATA_KEY = 'employer_company_id';
|
|
57
62
|
const NOTES_METADATA_KEY = 'notes';
|
|
@@ -850,6 +855,32 @@ export class PersonService {
|
|
|
850
855
|
return Array.from(byId.values());
|
|
851
856
|
}
|
|
852
857
|
|
|
858
|
+
private async canLinkPersonUser(userId: number): Promise<boolean> {
|
|
859
|
+
if (!userId || userId <= 0) {
|
|
860
|
+
return false;
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
const userWithRole = await this.prismaService.user.findUnique({
|
|
864
|
+
where: { id: userId },
|
|
865
|
+
select: {
|
|
866
|
+
role_user: {
|
|
867
|
+
select: {
|
|
868
|
+
role: {
|
|
869
|
+
select: { slug: true },
|
|
870
|
+
},
|
|
871
|
+
},
|
|
872
|
+
},
|
|
873
|
+
},
|
|
874
|
+
});
|
|
875
|
+
|
|
876
|
+
if (!userWithRole?.role_user) {
|
|
877
|
+
return false;
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
const userRoles = userWithRole.role_user.map((r) => r.role.slug);
|
|
881
|
+
return userRoles.some((role) => PERSON_USER_LINKER_ALLOWED_ROLE_SLUGS.includes(role));
|
|
882
|
+
}
|
|
883
|
+
|
|
853
884
|
async getLinkedUserOptions(search?: string) {
|
|
854
885
|
const where: Prisma.userWhereInput = search
|
|
855
886
|
? { name: { contains: search, mode: 'insensitive' } }
|
|
@@ -860,7 +891,6 @@ export class PersonService {
|
|
|
860
891
|
select: {
|
|
861
892
|
id: true,
|
|
862
893
|
name: true,
|
|
863
|
-
photo_id: true,
|
|
864
894
|
user_identifier: {
|
|
865
895
|
where: { type: 'email' },
|
|
866
896
|
select: { value: true },
|
|
@@ -875,7 +905,6 @@ export class PersonService {
|
|
|
875
905
|
id: user.id,
|
|
876
906
|
name: user.name || `#${user.id}`,
|
|
877
907
|
email: user.user_identifier[0]?.value || '',
|
|
878
|
-
photo_id: user.photo_id ?? null,
|
|
879
908
|
}));
|
|
880
909
|
}
|
|
881
910
|
|
|
@@ -2171,7 +2200,7 @@ export class PersonService {
|
|
|
2171
2200
|
};
|
|
2172
2201
|
}
|
|
2173
2202
|
|
|
2174
|
-
async create(data: CreateDTO, locale: string) {
|
|
2203
|
+
async create(data: CreateDTO, locale: string, user?: { id?: number; name?: string | null }) {
|
|
2175
2204
|
const allowCompanyRegistration = await this.isCompanyRegistrationAllowed();
|
|
2176
2205
|
|
|
2177
2206
|
await this.ensureCompanyRegistrationAllowed({
|
|
@@ -2179,6 +2208,20 @@ export class PersonService {
|
|
|
2179
2208
|
locale,
|
|
2180
2209
|
});
|
|
2181
2210
|
|
|
2211
|
+
// Validate user linking permission
|
|
2212
|
+
if (data.user_id !== undefined && data.user_id !== null) {
|
|
2213
|
+
const canLink = await this.canLinkPersonUser(data.user_id);
|
|
2214
|
+
if (!canLink) {
|
|
2215
|
+
throw new BadRequestException(
|
|
2216
|
+
getLocaleText(
|
|
2217
|
+
'personUserLinkingNotAllowed',
|
|
2218
|
+
locale,
|
|
2219
|
+
'You do not have permission to link a person to a user.',
|
|
2220
|
+
),
|
|
2221
|
+
);
|
|
2222
|
+
}
|
|
2223
|
+
}
|
|
2224
|
+
|
|
2182
2225
|
const result = await this.prismaService.$transaction(async (tx) => {
|
|
2183
2226
|
const person = await tx.person.create({
|
|
2184
2227
|
data: {
|
|
@@ -2226,7 +2269,16 @@ export class PersonService {
|
|
|
2226
2269
|
user?: { id?: number; name?: string | null },
|
|
2227
2270
|
) {
|
|
2228
2271
|
const allowCompanyRegistration = await this.isCompanyRegistrationAllowed();
|
|
2229
|
-
const person = await this.prismaService.person.findUnique({
|
|
2272
|
+
const person = await this.prismaService.person.findUnique({
|
|
2273
|
+
where: { id },
|
|
2274
|
+
include: {
|
|
2275
|
+
person_user: {
|
|
2276
|
+
select: {
|
|
2277
|
+
user_id: true,
|
|
2278
|
+
},
|
|
2279
|
+
},
|
|
2280
|
+
},
|
|
2281
|
+
});
|
|
2230
2282
|
if (!person) {
|
|
2231
2283
|
throw new BadRequestException(
|
|
2232
2284
|
getLocaleText('personNotFound', locale, `Person with ID ${id} not found`),
|
|
@@ -2245,6 +2297,23 @@ export class PersonService {
|
|
|
2245
2297
|
locale,
|
|
2246
2298
|
});
|
|
2247
2299
|
|
|
2300
|
+
// Validate user linking permission
|
|
2301
|
+
if (data.user_id !== undefined && data.user_id !== person.person_user?.[0]?.user_id) {
|
|
2302
|
+
if (data.user_id !== null) {
|
|
2303
|
+
const canLink = await this.canLinkPersonUser(data.user_id);
|
|
2304
|
+
if (!canLink) {
|
|
2305
|
+
throw new BadRequestException(
|
|
2306
|
+
getLocaleText(
|
|
2307
|
+
'personUserLinkingNotAllowed',
|
|
2308
|
+
locale,
|
|
2309
|
+
'You do not have permission to link a person to a user.',
|
|
2310
|
+
),
|
|
2311
|
+
);
|
|
2312
|
+
}
|
|
2313
|
+
}
|
|
2314
|
+
// Permitir remover vinculação (null) sem restrição
|
|
2315
|
+
}
|
|
2316
|
+
|
|
2248
2317
|
const currentLifecycleStage = await this.getPersonLifecycleStage(id);
|
|
2249
2318
|
const nextLifecycleStage = this.normalizeTextOrNull(data.lifecycle_stage);
|
|
2250
2319
|
const stageTransitionParams =
|