@open-mercato/core 0.6.8-develop.7013.1.48f7adca80 → 0.6.8-develop.7015.1.af90a2ddc7
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/modules/customers/api/companies/[id]/people/route.js +19 -63
- package/dist/modules/customers/api/companies/[id]/people/route.js.map +2 -2
- package/dist/modules/customers/api/companies/[id]/route.js +9 -85
- package/dist/modules/customers/api/companies/[id]/route.js.map +2 -2
- package/dist/modules/customers/api/people/[id]/companies/[linkId]/route.js +4 -3
- package/dist/modules/customers/api/people/[id]/companies/[linkId]/route.js.map +2 -2
- package/dist/modules/customers/commands/personCompanyLinks.js +131 -2
- package/dist/modules/customers/commands/personCompanyLinks.js.map +2 -2
- package/dist/modules/customers/components/detail/CompanyPeopleSection.js +3 -1
- package/dist/modules/customers/components/detail/CompanyPeopleSection.js.map +2 -2
- package/dist/modules/customers/components/detail/PersonCompaniesSection.js +3 -1
- package/dist/modules/customers/components/detail/PersonCompaniesSection.js.map +2 -2
- package/dist/modules/customers/data/validators.js +10 -2
- package/dist/modules/customers/data/validators.js.map +2 -2
- package/dist/modules/customers/events.js +5 -0
- package/dist/modules/customers/events.js.map +2 -2
- package/dist/modules/customers/lib/personCompanies.js +100 -2
- package/dist/modules/customers/lib/personCompanies.js.map +2 -2
- package/package.json +7 -7
- package/src/modules/customers/api/companies/[id]/people/route.ts +20 -72
- package/src/modules/customers/api/companies/[id]/route.ts +11 -97
- package/src/modules/customers/api/people/[id]/companies/[linkId]/route.ts +12 -4
- package/src/modules/customers/commands/personCompanyLinks.ts +224 -8
- package/src/modules/customers/components/detail/CompanyPeopleSection.tsx +8 -1
- package/src/modules/customers/components/detail/PersonCompaniesSection.tsx +8 -1
- package/src/modules/customers/data/validators.ts +18 -3
- package/src/modules/customers/events.ts +5 -0
- package/src/modules/customers/lib/personCompanies.ts +136 -1
|
@@ -5,17 +5,10 @@ import { createRequestContainer } from "@open-mercato/shared/lib/di/container";
|
|
|
5
5
|
import { getAuthFromRequest } from "@open-mercato/shared/lib/auth/server";
|
|
6
6
|
import { resolveOrganizationScopeForRequest } from "@open-mercato/core/modules/directory/utils/organizationScope";
|
|
7
7
|
import { resolveTranslations } from "@open-mercato/shared/lib/i18n/server";
|
|
8
|
-
import { findOneWithDecryption
|
|
8
|
+
import { findOneWithDecryption } from "@open-mercato/shared/lib/encryption/find";
|
|
9
9
|
import { isOrganizationReadAccessAllowed } from "@open-mercato/core/modules/directory/utils/organizationScopeGuard";
|
|
10
|
-
import {
|
|
11
|
-
|
|
12
|
-
CustomerPersonCompanyLink,
|
|
13
|
-
CustomerPersonProfile
|
|
14
|
-
} from "../../../../data/entities.js";
|
|
15
|
-
import {
|
|
16
|
-
filterActivePersonCompanyLinks,
|
|
17
|
-
withActiveCustomerPersonCompanyLinkFilter
|
|
18
|
-
} from "../../../../lib/personCompanyLinkTable.js";
|
|
10
|
+
import { CustomerEntity } from "../../../../data/entities.js";
|
|
11
|
+
import { loadCompanyPeopleUnion } from "../../../../lib/personCompanies.js";
|
|
19
12
|
import { createLogger } from "@open-mercato/shared/lib/logger";
|
|
20
13
|
const logger = createLogger("customers");
|
|
21
14
|
const paramsSchema = z.object({
|
|
@@ -91,59 +84,22 @@ async function GET(req, ctx) {
|
|
|
91
84
|
throw new CrudHttpError(403, { error: translate("customers.errors.access_denied", "Access denied") });
|
|
92
85
|
}
|
|
93
86
|
const entityScope = { tenantId: auth.tenantId, organizationId: company.organizationId };
|
|
94
|
-
const
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
)
|
|
111
|
-
);
|
|
112
|
-
const personIds = links.map((link) => link.person?.id).filter((personId) => typeof personId === "string" && personId.length > 0);
|
|
113
|
-
const profiles = personIds.length > 0 ? await findWithDecryption(
|
|
114
|
-
em,
|
|
115
|
-
CustomerPersonProfile,
|
|
116
|
-
{
|
|
117
|
-
entity: { $in: personIds },
|
|
118
|
-
tenantId: company.tenantId,
|
|
119
|
-
organizationId: company.organizationId
|
|
120
|
-
},
|
|
121
|
-
{},
|
|
122
|
-
entityScope
|
|
123
|
-
) : [];
|
|
124
|
-
const profileByPersonId = new Map(
|
|
125
|
-
profiles.map((profile) => [profile.entity.id, profile])
|
|
126
|
-
);
|
|
127
|
-
const items = links.map((link) => {
|
|
128
|
-
const person = link.person;
|
|
129
|
-
if (!person?.id) return null;
|
|
130
|
-
const profile = profileByPersonId.get(person.id) ?? null;
|
|
131
|
-
return {
|
|
132
|
-
id: person.id,
|
|
133
|
-
displayName: person.displayName ?? person.primaryEmail ?? person.id,
|
|
134
|
-
primaryEmail: person.primaryEmail ?? null,
|
|
135
|
-
primaryPhone: person.primaryPhone ?? null,
|
|
136
|
-
status: person.status ?? null,
|
|
137
|
-
lifecycleStage: person.lifecycleStage ?? null,
|
|
138
|
-
jobTitle: profile?.jobTitle ?? null,
|
|
139
|
-
department: profile?.department ?? null,
|
|
140
|
-
createdAt: person.createdAt.toISOString(),
|
|
141
|
-
organizationId: person.organizationId,
|
|
142
|
-
temperature: person.temperature ?? null,
|
|
143
|
-
source: person.source ?? null,
|
|
144
|
-
linkedAt: link.createdAt ? link.createdAt.toISOString() : null
|
|
145
|
-
};
|
|
146
|
-
}).filter((item) => item !== null);
|
|
87
|
+
const union = await loadCompanyPeopleUnion(em, company, entityScope);
|
|
88
|
+
const items = union.map(({ entity: person, profile, linkedAt }) => ({
|
|
89
|
+
id: person.id,
|
|
90
|
+
displayName: person.displayName ?? person.primaryEmail ?? person.id,
|
|
91
|
+
primaryEmail: person.primaryEmail ?? null,
|
|
92
|
+
primaryPhone: person.primaryPhone ?? null,
|
|
93
|
+
status: person.status ?? null,
|
|
94
|
+
lifecycleStage: person.lifecycleStage ?? null,
|
|
95
|
+
jobTitle: profile?.jobTitle ?? null,
|
|
96
|
+
department: profile?.department ?? null,
|
|
97
|
+
createdAt: person.createdAt.toISOString(),
|
|
98
|
+
organizationId: person.organizationId,
|
|
99
|
+
temperature: person.temperature ?? null,
|
|
100
|
+
source: person.source ?? null,
|
|
101
|
+
linkedAt
|
|
102
|
+
}));
|
|
147
103
|
const filtered = query.search?.trim().length ? items.filter((item) => matchesSearch(item, query.search ?? "")) : items;
|
|
148
104
|
const sorted = sortItems(filtered, query.sort);
|
|
149
105
|
const total = sorted.length;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../../src/modules/customers/api/companies/%5Bid%5D/people/route.ts"],
|
|
4
|
-
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { CrudHttpError, isCrudHttpError, notFound } from '@open-mercato/shared/lib/crud/errors'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { resolveOrganizationScopeForRequest } from '@open-mercato/core/modules/directory/utils/organizationScope'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { findOneWithDecryption
|
|
5
|
-
"mappings": "AAAA,SAAS,oBAAoB;AAC7B,SAAS,SAAS;AAElB,SAAS,eAAe,iBAAiB,gBAAgB;AACzD,SAAS,8BAA8B;AACvC,SAAS,0BAA0B;AACnC,SAAS,0CAA0C;AACnD,SAAS,2BAA2B;AAEpC,SAAS,
|
|
4
|
+
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { CrudHttpError, isCrudHttpError, notFound } from '@open-mercato/shared/lib/crud/errors'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { resolveOrganizationScopeForRequest } from '@open-mercato/core/modules/directory/utils/organizationScope'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { isOrganizationReadAccessAllowed } from '@open-mercato/core/modules/directory/utils/organizationScopeGuard'\nimport { CustomerEntity } from '../../../../data/entities'\nimport { loadCompanyPeopleUnion } from '../../../../lib/personCompanies'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\n\nconst logger = createLogger('customers')\n\nconst paramsSchema = z.object({\n id: z.string().uuid(),\n})\n\nconst querySchema = z.object({\n page: z.coerce.number().min(1).default(1),\n pageSize: z.coerce.number().min(1).max(100).default(20),\n search: z.string().optional(),\n sort: z.enum(['name-asc', 'name-desc', 'recent']).default('name-asc'),\n})\n\ntype CompanyPersonItem = {\n id: string\n displayName: string\n primaryEmail: string | null\n primaryPhone: string | null\n status: string | null\n lifecycleStage: string | null\n jobTitle: string | null\n department: string | null\n createdAt: string\n organizationId: string\n temperature: string | null\n source: string | null\n linkedAt: string | null\n}\n\nfunction matchesSearch(item: CompanyPersonItem, query: string): boolean {\n const normalized = query.trim().toLowerCase()\n if (!normalized.length) return true\n return [\n item.displayName,\n item.primaryEmail,\n item.primaryPhone,\n item.jobTitle,\n item.department,\n item.status,\n item.lifecycleStage,\n item.source,\n ]\n .filter((value): value is string => typeof value === 'string' && value.length > 0)\n .some((value) => value.toLowerCase().includes(normalized))\n}\n\nfunction sortItems(items: CompanyPersonItem[], sort: 'name-asc' | 'name-desc' | 'recent'): CompanyPersonItem[] {\n if (sort === 'recent') {\n return [...items].sort((left, right) => {\n const leftTimestamp = left.linkedAt ? new Date(left.linkedAt).getTime() : new Date(left.createdAt).getTime()\n const rightTimestamp = right.linkedAt ? new Date(right.linkedAt).getTime() : new Date(right.createdAt).getTime()\n if (leftTimestamp === rightTimestamp) return left.displayName.localeCompare(right.displayName)\n return rightTimestamp - leftTimestamp\n })\n }\n\n return [...items].sort((left, right) => {\n const compare = left.displayName.localeCompare(right.displayName, undefined, { sensitivity: 'base' })\n return sort === 'name-asc' ? compare : -compare\n })\n}\n\nexport const metadata = {\n GET: { requireAuth: true, requireFeatures: ['customers.companies.view'] },\n}\n\nexport async function GET(req: Request, ctx: { params?: { id?: string } }) {\n const { translate } = await resolveTranslations()\n try {\n const { id } = paramsSchema.parse({ id: ctx.params?.id })\n const auth = await getAuthFromRequest(req)\n if (!auth?.tenantId) throw new CrudHttpError(401, { error: 'Unauthorized' })\n\n const query = querySchema.parse({\n page: new URL(req.url).searchParams.get('page') ?? undefined,\n pageSize: new URL(req.url).searchParams.get('pageSize') ?? undefined,\n search: new URL(req.url).searchParams.get('search') ?? undefined,\n sort: new URL(req.url).searchParams.get('sort') ?? undefined,\n })\n\n const container = await createRequestContainer()\n const scope = await resolveOrganizationScopeForRequest({ container, auth, request: req })\n const em = (container.resolve('em') as EntityManager).fork()\n const decryptionScope = {\n tenantId: auth.tenantId,\n organizationId: scope?.selectedId ?? auth.orgId ?? null,\n }\n\n const company = await findOneWithDecryption(\n em,\n CustomerEntity,\n { id, kind: 'company', tenantId: auth.tenantId, deletedAt: null },\n {},\n decryptionScope,\n )\n if (!company) {\n throw notFound(translate('customers.errors.company_not_found', 'Company not found'))\n }\n\n if (!isOrganizationReadAccessAllowed({ scope, auth, organizationId: company.organizationId })) {\n throw new CrudHttpError(403, { error: translate('customers.errors.access_denied', 'Access denied') })\n }\n\n const entityScope = { tenantId: auth.tenantId, organizationId: company.organizationId }\n const union = await loadCompanyPeopleUnion(em, company, entityScope)\n\n const items = union.map(({ entity: person, profile, linkedAt }) => ({\n id: person.id,\n displayName: person.displayName ?? person.primaryEmail ?? person.id,\n primaryEmail: person.primaryEmail ?? null,\n primaryPhone: person.primaryPhone ?? null,\n status: person.status ?? null,\n lifecycleStage: person.lifecycleStage ?? null,\n jobTitle: profile?.jobTitle ?? null,\n department: profile?.department ?? null,\n createdAt: person.createdAt.toISOString(),\n organizationId: person.organizationId,\n temperature: person.temperature ?? null,\n source: person.source ?? null,\n linkedAt,\n } satisfies CompanyPersonItem))\n\n const filtered = query.search?.trim().length ? items.filter((item) => matchesSearch(item, query.search ?? '')) : items\n const sorted = sortItems(filtered, query.sort)\n const total = sorted.length\n const totalPages = Math.max(1, Math.ceil(total / query.pageSize))\n const page = Math.min(query.page, totalPages)\n const start = (page - 1) * query.pageSize\n\n return NextResponse.json({\n items: sorted.slice(start, start + query.pageSize),\n total,\n page,\n pageSize: query.pageSize,\n totalPages,\n })\n } catch (error) {\n if (isCrudHttpError(error)) {\n return NextResponse.json(error.body, { status: error.status })\n }\n logger.error('customers.companies.people.GET', { err: error })\n return NextResponse.json({ error: translate('customers.errors.company_people_load_failed', 'Failed to load linked people') }, { status: 500 })\n }\n}\n\nconst companyPeopleItemSchema = z.object({\n id: z.string().uuid(),\n displayName: z.string(),\n primaryEmail: z.string().nullable(),\n primaryPhone: z.string().nullable(),\n status: z.string().nullable(),\n lifecycleStage: z.string().nullable(),\n jobTitle: z.string().nullable(),\n department: z.string().nullable(),\n createdAt: z.string(),\n organizationId: z.string().uuid().nullable(),\n temperature: z.string().nullable(),\n source: z.string().nullable(),\n linkedAt: z.string().nullable(),\n})\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Customers',\n methods: {\n GET: {\n summary: 'List linked people for a company',\n query: querySchema,\n responses: [\n {\n status: 200,\n description: 'Paginated linked people',\n schema: z.object({\n items: z.array(companyPeopleItemSchema),\n total: z.number().int().nonnegative(),\n page: z.number().int().min(1),\n pageSize: z.number().int().min(1),\n totalPages: z.number().int().min(1),\n }),\n },\n ],\n },\n },\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -13,8 +13,6 @@ import {
|
|
|
13
13
|
CustomerLabelAssignment,
|
|
14
14
|
CustomerDealCompanyLink,
|
|
15
15
|
CustomerTodoLink,
|
|
16
|
-
CustomerPersonCompanyLink,
|
|
17
|
-
CustomerPersonProfile,
|
|
18
16
|
CustomerInteraction
|
|
19
17
|
} from "../../../data/entities.js";
|
|
20
18
|
import { User } from "@open-mercato/core/modules/auth/data/entities";
|
|
@@ -40,9 +38,9 @@ import { resolveCustomerDetailTenantScope } from "../../../lib/detailTenantScope
|
|
|
40
38
|
import { findOneWithDecryption, findWithDecryption } from "@open-mercato/shared/lib/encryption/find";
|
|
41
39
|
import { parseBooleanFromUnknown } from "@open-mercato/shared/lib/boolean";
|
|
42
40
|
import {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
} from "../../../lib/
|
|
41
|
+
countCompanyPeopleUnion,
|
|
42
|
+
loadCompanyPeopleUnion
|
|
43
|
+
} from "../../../lib/personCompanies.js";
|
|
46
44
|
import { normalizeCustomerDetailCustomFields } from "../../detailCustomFields.js";
|
|
47
45
|
import { isOrganizationReadAccessAllowed } from "@open-mercato/core/modules/directory/utils/organizationScopeGuard";
|
|
48
46
|
import { runWithCacheTenant } from "@open-mercato/cache";
|
|
@@ -631,68 +629,13 @@ async function GET(_req, ctx) {
|
|
|
631
629
|
)).map((link) => link.deal ?? null).filter(
|
|
632
630
|
(deal) => !!deal && typeof deal !== "string" && deal.tenantId === company.tenantId && deal.organizationId === company.organizationId
|
|
633
631
|
);
|
|
632
|
+
const peopleUnionScope = {
|
|
633
|
+
tenantId: company.tenantId ?? auth.tenantId ?? null,
|
|
634
|
+
organizationId: company.organizationId ?? scope?.selectedId ?? auth.orgId ?? null
|
|
635
|
+
};
|
|
634
636
|
let relatedPeople = [];
|
|
635
637
|
if (includePeople) {
|
|
636
|
-
|
|
637
|
-
tenantId: company.tenantId ?? auth.tenantId ?? null,
|
|
638
|
-
organizationId: company.organizationId ?? scope?.selectedId ?? auth.orgId ?? null
|
|
639
|
-
};
|
|
640
|
-
const relatedPeopleById = /* @__PURE__ */ new Map();
|
|
641
|
-
const companyLinkWhere = await withActiveCustomerPersonCompanyLinkFilter(
|
|
642
|
-
em,
|
|
643
|
-
{
|
|
644
|
-
company: company.id,
|
|
645
|
-
organizationId: company.organizationId,
|
|
646
|
-
tenantId: company.tenantId
|
|
647
|
-
},
|
|
648
|
-
"customers.companies.GET"
|
|
649
|
-
);
|
|
650
|
-
const companyLinks = filterActivePersonCompanyLinks(
|
|
651
|
-
await findWithDecryption(
|
|
652
|
-
em,
|
|
653
|
-
CustomerPersonCompanyLink,
|
|
654
|
-
companyLinkWhere,
|
|
655
|
-
{
|
|
656
|
-
populate: ["person", "person.personProfile"],
|
|
657
|
-
orderBy: { isPrimary: "desc", createdAt: "asc" }
|
|
658
|
-
},
|
|
659
|
-
peopleDecryptionScope
|
|
660
|
-
)
|
|
661
|
-
);
|
|
662
|
-
companyLinks.forEach((link) => {
|
|
663
|
-
const entity = typeof link.person === "string" ? null : link.person;
|
|
664
|
-
if (!entity || entity.kind !== "person" || entity.deletedAt) return;
|
|
665
|
-
const personProfile = entity.personProfile && typeof entity.personProfile !== "string" ? entity.personProfile : null;
|
|
666
|
-
relatedPeopleById.set(entity.id, {
|
|
667
|
-
entity,
|
|
668
|
-
profile: personProfile,
|
|
669
|
-
linkedAt: link.createdAt instanceof Date ? link.createdAt.toISOString() : null
|
|
670
|
-
});
|
|
671
|
-
});
|
|
672
|
-
const profiles = await findWithDecryption(
|
|
673
|
-
em,
|
|
674
|
-
CustomerPersonProfile,
|
|
675
|
-
{
|
|
676
|
-
company: company.id,
|
|
677
|
-
tenantId: company.tenantId,
|
|
678
|
-
organizationId: company.organizationId,
|
|
679
|
-
entity: { deletedAt: null }
|
|
680
|
-
},
|
|
681
|
-
{ populate: ["entity"] },
|
|
682
|
-
peopleDecryptionScope
|
|
683
|
-
);
|
|
684
|
-
profiles.forEach((entry) => {
|
|
685
|
-
const entity = entry.entity;
|
|
686
|
-
if (!entity || entity.kind !== "person" || entity.deletedAt) return;
|
|
687
|
-
if (!relatedPeopleById.has(entity.id)) {
|
|
688
|
-
relatedPeopleById.set(entity.id, {
|
|
689
|
-
entity,
|
|
690
|
-
profile: entry ?? null,
|
|
691
|
-
linkedAt: entry.createdAt instanceof Date ? entry.createdAt.toISOString() : null
|
|
692
|
-
});
|
|
693
|
-
}
|
|
694
|
-
});
|
|
695
|
-
relatedPeople = Array.from(relatedPeopleById.values());
|
|
638
|
+
relatedPeople = await loadCompanyPeopleUnion(em, company, peopleUnionScope);
|
|
696
639
|
}
|
|
697
640
|
const profileId = profile?.id ?? null;
|
|
698
641
|
const [entityCustomFieldValues, profileCustomFieldValues, routing] = await Promise.all([
|
|
@@ -725,26 +668,7 @@ async function GET(_req, ctx) {
|
|
|
725
668
|
profileId ? profileCustomFieldValues?.[profileId] ?? {} : {}
|
|
726
669
|
)
|
|
727
670
|
);
|
|
728
|
-
const peopleCountQuery = includePeople ? Promise.resolve(relatedPeople.length) : (
|
|
729
|
-
const peopleLinkWhere = await withActiveCustomerPersonCompanyLinkFilter(
|
|
730
|
-
em,
|
|
731
|
-
{
|
|
732
|
-
company: company.id,
|
|
733
|
-
organizationId: company.organizationId,
|
|
734
|
-
tenantId: company.tenantId
|
|
735
|
-
},
|
|
736
|
-
"customers.companies.GET"
|
|
737
|
-
);
|
|
738
|
-
return filterActivePersonCompanyLinks(
|
|
739
|
-
await findWithDecryption(
|
|
740
|
-
em,
|
|
741
|
-
CustomerPersonCompanyLink,
|
|
742
|
-
peopleLinkWhere,
|
|
743
|
-
{},
|
|
744
|
-
{ tenantId: company.tenantId, organizationId: company.organizationId }
|
|
745
|
-
)
|
|
746
|
-
).length;
|
|
747
|
-
})();
|
|
671
|
+
const peopleCountQuery = includePeople ? Promise.resolve(relatedPeople.length) : countCompanyPeopleUnion(em, company);
|
|
748
672
|
const [
|
|
749
673
|
activityCount,
|
|
750
674
|
interactionCount,
|