@open-mercato/core 0.6.6-develop.6386.1.391a1afb9e → 0.6.6-develop.6388.1.b9b9f5e778
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/.turbo/turbo-build.log +1 -1
- package/dist/modules/customers/api/deals/map/route.js +433 -0
- package/dist/modules/customers/api/deals/map/route.js.map +7 -0
- package/dist/modules/customers/backend/customers/companies/create/page.js +2 -0
- package/dist/modules/customers/backend/customers/companies/create/page.js.map +2 -2
- package/dist/modules/customers/backend/customers/deals/map/components/DealsLocationPanel.js +159 -0
- package/dist/modules/customers/backend/customers/deals/map/components/DealsLocationPanel.js.map +7 -0
- package/dist/modules/customers/backend/customers/deals/map/components/DealsMapCanvas.js +26 -0
- package/dist/modules/customers/backend/customers/deals/map/components/DealsMapCanvas.js.map +7 -0
- package/dist/modules/customers/backend/customers/deals/map/components/DealsMapCanvasImpl.js +321 -0
- package/dist/modules/customers/backend/customers/deals/map/components/DealsMapCanvasImpl.js.map +7 -0
- package/dist/modules/customers/backend/customers/deals/map/components/DealsMapView.js +533 -0
- package/dist/modules/customers/backend/customers/deals/map/components/DealsMapView.js.map +7 -0
- package/dist/modules/customers/backend/customers/deals/map/page.js +55 -0
- package/dist/modules/customers/backend/customers/deals/map/page.js.map +7 -0
- package/dist/modules/customers/backend/customers/deals/map/page.meta.js +38 -0
- package/dist/modules/customers/backend/customers/deals/map/page.meta.js.map +7 -0
- package/dist/modules/customers/backend/customers/deals/pipeline/components/DealCard.js +38 -47
- package/dist/modules/customers/backend/customers/deals/pipeline/components/DealCard.js.map +2 -2
- package/dist/modules/customers/backend/customers/deals/pipeline/components/FilterBarRow.js +3 -3
- package/dist/modules/customers/backend/customers/deals/pipeline/components/FilterBarRow.js.map +2 -2
- package/dist/modules/customers/backend/customers/deals/pipeline/components/ViewTabsRow.js +22 -1
- package/dist/modules/customers/backend/customers/deals/pipeline/components/ViewTabsRow.js.map +2 -2
- package/dist/modules/customers/backend/customers/people/create/page.js +2 -0
- package/dist/modules/customers/backend/customers/people/create/page.js.map +2 -2
- package/dist/modules/customers/components/AddressEditor.js +34 -2
- package/dist/modules/customers/components/AddressEditor.js.map +2 -2
- package/dist/modules/customers/components/AddressTiles.js +43 -3
- package/dist/modules/customers/components/AddressTiles.js.map +2 -2
- package/dist/modules/customers/components/detail/AddressesSection.js +4 -1
- package/dist/modules/customers/components/detail/AddressesSection.js.map +2 -2
- package/dist/modules/customers/components/formConfig.js +10 -0
- package/dist/modules/customers/components/formConfig.js.map +2 -2
- package/dist/modules/customers/data/validators.js +3 -2
- package/dist/modules/customers/data/validators.js.map +2 -2
- package/dist/modules/customers/lib/dealsMapLocation.js +75 -0
- package/dist/modules/customers/lib/dealsMapLocation.js.map +7 -0
- package/package.json +11 -7
- package/src/modules/customers/api/deals/map/route.ts +549 -0
- package/src/modules/customers/backend/customers/companies/create/page.tsx +2 -0
- package/src/modules/customers/backend/customers/deals/map/components/DealsLocationPanel.tsx +213 -0
- package/src/modules/customers/backend/customers/deals/map/components/DealsMapCanvas.tsx +79 -0
- package/src/modules/customers/backend/customers/deals/map/components/DealsMapCanvasImpl.tsx +407 -0
- package/src/modules/customers/backend/customers/deals/map/components/DealsMapView.tsx +667 -0
- package/src/modules/customers/backend/customers/deals/map/page.meta.ts +36 -0
- package/src/modules/customers/backend/customers/deals/map/page.tsx +77 -0
- package/src/modules/customers/backend/customers/deals/pipeline/components/DealCard.tsx +57 -42
- package/src/modules/customers/backend/customers/deals/pipeline/components/FilterBarRow.tsx +3 -3
- package/src/modules/customers/backend/customers/deals/pipeline/components/ViewTabsRow.tsx +22 -1
- package/src/modules/customers/backend/customers/people/create/page.tsx +2 -0
- package/src/modules/customers/components/AddressEditor.tsx +35 -0
- package/src/modules/customers/components/AddressTiles.tsx +51 -1
- package/src/modules/customers/components/detail/AddressesSection.tsx +3 -0
- package/src/modules/customers/components/formConfig.tsx +10 -0
- package/src/modules/customers/data/validators.ts +13 -2
- package/src/modules/customers/i18n/de.json +24 -0
- package/src/modules/customers/i18n/en.json +24 -0
- package/src/modules/customers/i18n/es.json +24 -0
- package/src/modules/customers/i18n/pl.json +24 -0
- package/src/modules/customers/lib/dealsMapLocation.ts +125 -0
package/.turbo/turbo-build.log
CHANGED
|
@@ -0,0 +1,433 @@
|
|
|
1
|
+
import { NextResponse } from "next/server";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { getAuthFromRequest } from "@open-mercato/shared/lib/auth/server";
|
|
4
|
+
import { createRequestContainer } from "@open-mercato/shared/lib/di/container";
|
|
5
|
+
import { resolveOrganizationScopeForRequest } from "@open-mercato/core/modules/directory/utils/organizationScope";
|
|
6
|
+
import { SortDir } from "@open-mercato/shared/lib/query/types";
|
|
7
|
+
import { findWithDecryption } from "@open-mercato/shared/lib/encryption/find";
|
|
8
|
+
import {
|
|
9
|
+
CustomerAddress,
|
|
10
|
+
CustomerDealCompanyLink,
|
|
11
|
+
CustomerDealPersonLink
|
|
12
|
+
} from "../../../data/entities.js";
|
|
13
|
+
import { buildDealListFilters, dealListQuerySchema } from "../route.js";
|
|
14
|
+
import { applyEntityIdRestriction, findMatchingEntityIdsBySearchTokensAcrossSources } from "../../utils.js";
|
|
15
|
+
import { createPagedListResponseSchema } from "../../openapi.js";
|
|
16
|
+
import {
|
|
17
|
+
resolveDealLocations
|
|
18
|
+
} from "../../../lib/dealsMapLocation.js";
|
|
19
|
+
import { E } from "../../../../../generated/entities.ids.generated.js";
|
|
20
|
+
const metadata = {
|
|
21
|
+
GET: { requireAuth: true, requireFeatures: ["customers.deals.view", "customers.activities.view"] }
|
|
22
|
+
};
|
|
23
|
+
const mapSortFields = ["createdAt", "updatedAt", "title", "value", "probability", "expectedCloseAt"];
|
|
24
|
+
const sortFieldMap = {
|
|
25
|
+
createdAt: "created_at",
|
|
26
|
+
updatedAt: "updated_at",
|
|
27
|
+
title: "title",
|
|
28
|
+
value: "value_amount",
|
|
29
|
+
probability: "probability",
|
|
30
|
+
expectedCloseAt: "expected_close_at"
|
|
31
|
+
};
|
|
32
|
+
const querySchema = dealListQuerySchema.extend({
|
|
33
|
+
pageSize: z.coerce.number().min(1).max(100).default(100),
|
|
34
|
+
sortField: z.enum(mapSortFields).optional()
|
|
35
|
+
});
|
|
36
|
+
const dealMapAssociationSchema = z.object({
|
|
37
|
+
id: z.string().uuid(),
|
|
38
|
+
label: z.string().nullable()
|
|
39
|
+
});
|
|
40
|
+
const dealMapLocationSchema = z.object({
|
|
41
|
+
latitude: z.number(),
|
|
42
|
+
longitude: z.number(),
|
|
43
|
+
city: z.string().nullable(),
|
|
44
|
+
region: z.string().nullable(),
|
|
45
|
+
country: z.string().nullable(),
|
|
46
|
+
source: z.enum(["company", "person"]),
|
|
47
|
+
entityId: z.string().uuid(),
|
|
48
|
+
addressId: z.string().uuid()
|
|
49
|
+
});
|
|
50
|
+
const dealMapItemSchema = z.object({
|
|
51
|
+
id: z.string().uuid(),
|
|
52
|
+
title: z.string().nullable(),
|
|
53
|
+
status: z.string().nullable(),
|
|
54
|
+
pipelineId: z.string().uuid().nullable(),
|
|
55
|
+
pipelineStageId: z.string().uuid().nullable(),
|
|
56
|
+
pipelineStage: z.string().nullable(),
|
|
57
|
+
valueAmount: z.number().nullable(),
|
|
58
|
+
valueCurrency: z.string().nullable(),
|
|
59
|
+
probability: z.number().nullable(),
|
|
60
|
+
expectedCloseAt: z.string().nullable(),
|
|
61
|
+
ownerUserId: z.string().uuid().nullable(),
|
|
62
|
+
updatedAt: z.string().nullable(),
|
|
63
|
+
companies: z.array(dealMapAssociationSchema),
|
|
64
|
+
people: z.array(dealMapAssociationSchema),
|
|
65
|
+
location: dealMapLocationSchema.nullable()
|
|
66
|
+
});
|
|
67
|
+
const mapErrorSchema = z.object({
|
|
68
|
+
error: z.string()
|
|
69
|
+
});
|
|
70
|
+
const openApi = {
|
|
71
|
+
tag: "Customers",
|
|
72
|
+
summary: "Deals map listing with resolved locations",
|
|
73
|
+
methods: {
|
|
74
|
+
GET: {
|
|
75
|
+
summary: "Paginated deals that have a resolvable map location",
|
|
76
|
+
description: "Returns a page of deals that have a coordinate-bearing linked company/person address, each enriched with one resolved location (company primary first, then earliest created; person addresses as fallback). Deals with no coordinate-bearing address are excluded entirely, so every item carries a non-null location in normal operation; the schema keeps location nullable only for the rare case where the address is deleted between the located-deal resolution and the page fetch.",
|
|
77
|
+
query: querySchema,
|
|
78
|
+
responses: [
|
|
79
|
+
{ status: 200, description: "Paged located deals", schema: createPagedListResponseSchema(dealMapItemSchema) }
|
|
80
|
+
],
|
|
81
|
+
errors: [
|
|
82
|
+
{ status: 400, description: "Invalid query parameters", schema: mapErrorSchema },
|
|
83
|
+
{ status: 401, description: "Unauthorized", schema: mapErrorSchema },
|
|
84
|
+
{ status: 403, description: "Missing required features", schema: mapErrorSchema }
|
|
85
|
+
]
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
function readEntityRef(ref) {
|
|
90
|
+
if (typeof ref === "string") return { id: ref, record: null };
|
|
91
|
+
if (ref && typeof ref === "object") {
|
|
92
|
+
const record = ref;
|
|
93
|
+
return { id: typeof record.id === "string" ? record.id : null, record };
|
|
94
|
+
}
|
|
95
|
+
return { id: null, record: null };
|
|
96
|
+
}
|
|
97
|
+
function readLinkRow(link, linkedRef) {
|
|
98
|
+
const { id: dealId } = readEntityRef(link.deal);
|
|
99
|
+
if (!dealId) return null;
|
|
100
|
+
const { id: entityId, record } = readEntityRef(linkedRef);
|
|
101
|
+
if (!entityId) return null;
|
|
102
|
+
const label = record && typeof record.displayName === "string" ? record.displayName : "";
|
|
103
|
+
return { dealId, entityId, label, createdAt: link.createdAt ?? null };
|
|
104
|
+
}
|
|
105
|
+
function groupAssociations(rows) {
|
|
106
|
+
const byDeal = /* @__PURE__ */ new Map();
|
|
107
|
+
for (const row of rows) {
|
|
108
|
+
const bucket = byDeal.get(row.dealId) ?? [];
|
|
109
|
+
if (!bucket.some((entry) => entry.id === row.entityId)) {
|
|
110
|
+
bucket.push({ id: row.entityId, label: row.label });
|
|
111
|
+
byDeal.set(row.dealId, bucket);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return byDeal;
|
|
115
|
+
}
|
|
116
|
+
function toStringOrNull(value) {
|
|
117
|
+
return typeof value === "string" && value.length > 0 ? value : null;
|
|
118
|
+
}
|
|
119
|
+
function toFiniteNumberOrNull(value) {
|
|
120
|
+
if (typeof value === "number") return Number.isFinite(value) ? value : null;
|
|
121
|
+
if (typeof value === "string" && value.trim().length > 0) {
|
|
122
|
+
const parsed = Number(value);
|
|
123
|
+
return Number.isFinite(parsed) ? parsed : null;
|
|
124
|
+
}
|
|
125
|
+
return null;
|
|
126
|
+
}
|
|
127
|
+
function toIsoStringOrNull(value) {
|
|
128
|
+
if (value instanceof Date) return value.toISOString();
|
|
129
|
+
return toStringOrNull(value);
|
|
130
|
+
}
|
|
131
|
+
function readArrayParam(searchParams, key) {
|
|
132
|
+
const all = searchParams.getAll(key);
|
|
133
|
+
if (!all.length) return null;
|
|
134
|
+
const trimmed = all.flatMap((value) => value.split(",")).map((value) => value.trim()).filter(Boolean);
|
|
135
|
+
return trimmed.length ? trimmed : null;
|
|
136
|
+
}
|
|
137
|
+
function collectRefIds(rows, key) {
|
|
138
|
+
const ids = /* @__PURE__ */ new Set();
|
|
139
|
+
for (const row of rows) {
|
|
140
|
+
if (!row || typeof row !== "object") continue;
|
|
141
|
+
const { id } = readEntityRef(row[key]);
|
|
142
|
+
if (id) ids.add(id);
|
|
143
|
+
}
|
|
144
|
+
return Array.from(ids);
|
|
145
|
+
}
|
|
146
|
+
function readFilterIdAllowlist(filters) {
|
|
147
|
+
const idFilter = filters.id;
|
|
148
|
+
if (!idFilter || typeof idFilter !== "object" || Array.isArray(idFilter)) return null;
|
|
149
|
+
const record = idFilter;
|
|
150
|
+
if (typeof record.$eq === "string") return [record.$eq];
|
|
151
|
+
if (Array.isArray(record.$in)) return record.$in.filter((value) => typeof value === "string");
|
|
152
|
+
return null;
|
|
153
|
+
}
|
|
154
|
+
function emptyMapResponse(page, pageSize) {
|
|
155
|
+
return NextResponse.json({ items: [], total: 0, page, pageSize, totalPages: 0 });
|
|
156
|
+
}
|
|
157
|
+
async function GET(req) {
|
|
158
|
+
const auth = await getAuthFromRequest(req);
|
|
159
|
+
if (!auth?.tenantId) {
|
|
160
|
+
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
|
161
|
+
}
|
|
162
|
+
const url = new URL(req.url);
|
|
163
|
+
const params = url.searchParams;
|
|
164
|
+
const parsed = querySchema.safeParse({
|
|
165
|
+
page: params.get("page") ?? void 0,
|
|
166
|
+
pageSize: params.get("pageSize") ?? void 0,
|
|
167
|
+
search: params.get("search") ?? void 0,
|
|
168
|
+
status: readArrayParam(params, "status") ?? void 0,
|
|
169
|
+
pipelineId: readArrayParam(params, "pipelineId") ?? void 0,
|
|
170
|
+
pipelineStageId: params.get("pipelineStageId") ?? void 0,
|
|
171
|
+
ownerUserId: readArrayParam(params, "ownerUserId") ?? void 0,
|
|
172
|
+
personId: readArrayParam(params, "personId") ?? void 0,
|
|
173
|
+
companyId: readArrayParam(params, "companyId") ?? void 0,
|
|
174
|
+
valueCurrency: readArrayParam(params, "valueCurrency") ?? void 0,
|
|
175
|
+
expectedCloseAtFrom: params.get("expectedCloseAtFrom") ?? void 0,
|
|
176
|
+
expectedCloseAtTo: params.get("expectedCloseAtTo") ?? void 0,
|
|
177
|
+
isStuck: params.get("isStuck") ?? void 0,
|
|
178
|
+
isOverdue: params.get("isOverdue") ?? void 0,
|
|
179
|
+
sortField: params.get("sortField") ?? void 0,
|
|
180
|
+
sortDir: params.get("sortDir") ?? void 0
|
|
181
|
+
});
|
|
182
|
+
if (!parsed.success) {
|
|
183
|
+
return NextResponse.json({ error: "Invalid query parameters" }, { status: 400 });
|
|
184
|
+
}
|
|
185
|
+
const query = parsed.data;
|
|
186
|
+
const container = await createRequestContainer();
|
|
187
|
+
const em = container.resolve("em");
|
|
188
|
+
const queryEngine = container.resolve("queryEngine");
|
|
189
|
+
const scope = await resolveOrganizationScopeForRequest({ container, auth, request: req });
|
|
190
|
+
const effectiveTenantId = scope.tenantId ?? auth.tenantId;
|
|
191
|
+
const orgScopeIds = scope.filterIds === null ? null : Array.isArray(scope.filterIds) && scope.filterIds.length > 0 ? Array.from(new Set(scope.filterIds.filter((id) => typeof id === "string" && id.length > 0))) : auth.orgId ? [auth.orgId] : [];
|
|
192
|
+
if (!effectiveTenantId || Array.isArray(orgScopeIds) && orgScopeIds.length === 0) {
|
|
193
|
+
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
|
194
|
+
}
|
|
195
|
+
const orgScopeFilter = orgScopeIds ? { organizationId: { $in: orgScopeIds } } : {};
|
|
196
|
+
const fallbackOrgId = orgScopeIds && orgScopeIds.length > 0 ? orgScopeIds[0] : void 0;
|
|
197
|
+
const decryptionScope = { tenantId: effectiveTenantId, organizationId: fallbackOrgId };
|
|
198
|
+
const coordinateEntityRows = await findWithDecryption(
|
|
199
|
+
em,
|
|
200
|
+
CustomerAddress,
|
|
201
|
+
{
|
|
202
|
+
latitude: { $ne: null },
|
|
203
|
+
longitude: { $ne: null },
|
|
204
|
+
tenantId: effectiveTenantId,
|
|
205
|
+
...orgScopeFilter
|
|
206
|
+
},
|
|
207
|
+
{ fields: ["entity"] },
|
|
208
|
+
decryptionScope
|
|
209
|
+
);
|
|
210
|
+
const locatedEntityIds = collectRefIds(coordinateEntityRows, "entity");
|
|
211
|
+
if (locatedEntityIds.length === 0) {
|
|
212
|
+
return emptyMapResponse(query.page, query.pageSize);
|
|
213
|
+
}
|
|
214
|
+
const [locatedCompanyLinkRows, locatedPersonLinkRows] = await Promise.all([
|
|
215
|
+
findWithDecryption(
|
|
216
|
+
em,
|
|
217
|
+
CustomerDealCompanyLink,
|
|
218
|
+
{ company: { $in: locatedEntityIds } },
|
|
219
|
+
{ fields: ["deal"] },
|
|
220
|
+
decryptionScope
|
|
221
|
+
),
|
|
222
|
+
findWithDecryption(
|
|
223
|
+
em,
|
|
224
|
+
CustomerDealPersonLink,
|
|
225
|
+
{ person: { $in: locatedEntityIds } },
|
|
226
|
+
{ fields: ["deal"] },
|
|
227
|
+
decryptionScope
|
|
228
|
+
)
|
|
229
|
+
]);
|
|
230
|
+
const locatedDealIds = Array.from(
|
|
231
|
+
/* @__PURE__ */ new Set([
|
|
232
|
+
...collectRefIds(locatedCompanyLinkRows, "deal"),
|
|
233
|
+
...collectRefIds(locatedPersonLinkRows, "deal")
|
|
234
|
+
])
|
|
235
|
+
);
|
|
236
|
+
if (locatedDealIds.length === 0) {
|
|
237
|
+
return emptyMapResponse(query.page, query.pageSize);
|
|
238
|
+
}
|
|
239
|
+
const ctx = {
|
|
240
|
+
container,
|
|
241
|
+
auth,
|
|
242
|
+
organizationScope: scope,
|
|
243
|
+
selectedOrganizationId: fallbackOrgId ?? null,
|
|
244
|
+
organizationIds: orgScopeIds,
|
|
245
|
+
request: req
|
|
246
|
+
};
|
|
247
|
+
const filters = await buildDealListFilters(query, ctx);
|
|
248
|
+
const existingAllowlist = readFilterIdAllowlist(filters);
|
|
249
|
+
let searchAllowlist = existingAllowlist;
|
|
250
|
+
if (query.search && locatedEntityIds.length > 0) {
|
|
251
|
+
const nameMatchedEntityIds = await findMatchingEntityIdsBySearchTokensAcrossSources({
|
|
252
|
+
ctx,
|
|
253
|
+
query: query.search,
|
|
254
|
+
sources: [
|
|
255
|
+
{
|
|
256
|
+
entityType: E.customers.customer_entity,
|
|
257
|
+
fields: ["display_name", "primary_email", "primary_phone", "description"]
|
|
258
|
+
}
|
|
259
|
+
]
|
|
260
|
+
});
|
|
261
|
+
const locatedEntitySet = new Set(locatedEntityIds);
|
|
262
|
+
const locatedMatchedEntityIds = (nameMatchedEntityIds ?? []).filter((id) => locatedEntitySet.has(id));
|
|
263
|
+
if (locatedMatchedEntityIds.length > 0) {
|
|
264
|
+
const [companyNameLinkRows, personNameLinkRows] = await Promise.all([
|
|
265
|
+
findWithDecryption(
|
|
266
|
+
em,
|
|
267
|
+
CustomerDealCompanyLink,
|
|
268
|
+
{ company: { $in: locatedMatchedEntityIds } },
|
|
269
|
+
{ fields: ["deal"] },
|
|
270
|
+
decryptionScope
|
|
271
|
+
),
|
|
272
|
+
findWithDecryption(
|
|
273
|
+
em,
|
|
274
|
+
CustomerDealPersonLink,
|
|
275
|
+
{ person: { $in: locatedMatchedEntityIds } },
|
|
276
|
+
{ fields: ["deal"] },
|
|
277
|
+
decryptionScope
|
|
278
|
+
)
|
|
279
|
+
]);
|
|
280
|
+
let nameMatchedDealIds = [
|
|
281
|
+
...collectRefIds(companyNameLinkRows, "deal"),
|
|
282
|
+
...collectRefIds(personNameLinkRows, "deal")
|
|
283
|
+
];
|
|
284
|
+
if (nameMatchedDealIds.length > 0) {
|
|
285
|
+
const nonSearchAllowlist = readFilterIdAllowlist(
|
|
286
|
+
await buildDealListFilters({ ...query, search: void 0 }, ctx)
|
|
287
|
+
);
|
|
288
|
+
if (nonSearchAllowlist) {
|
|
289
|
+
const nonSearchLookup = new Set(nonSearchAllowlist);
|
|
290
|
+
nameMatchedDealIds = nameMatchedDealIds.filter((id) => nonSearchLookup.has(id));
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
if (nameMatchedDealIds.length > 0) {
|
|
294
|
+
searchAllowlist = Array.from(/* @__PURE__ */ new Set([...existingAllowlist ?? [], ...nameMatchedDealIds]));
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
const allowlistLookup = searchAllowlist ? new Set(searchAllowlist) : null;
|
|
299
|
+
const restrictedDealIds = allowlistLookup ? locatedDealIds.filter((id) => allowlistLookup.has(id)) : locatedDealIds;
|
|
300
|
+
delete filters.id;
|
|
301
|
+
applyEntityIdRestriction(filters, restrictedDealIds);
|
|
302
|
+
const sortColumn = query.sortField ? sortFieldMap[query.sortField] : "id";
|
|
303
|
+
const sortDir = query.sortDir === "desc" ? SortDir.Desc : SortDir.Asc;
|
|
304
|
+
const sort = sortColumn === "id" ? [{ field: "id", dir: sortDir }] : [{ field: sortColumn, dir: sortDir }, { field: "id", dir: SortDir.Asc }];
|
|
305
|
+
const res = await queryEngine.query(E.customers.customer_deal, {
|
|
306
|
+
fields: [
|
|
307
|
+
"id",
|
|
308
|
+
// `organization_id` is projected (never exposed in the response) so the query engine can decrypt
|
|
309
|
+
// each row's encrypted fields (e.g. `title`) with the row's OWN org. Without it, an unrestricted
|
|
310
|
+
// "All organizations" scope has no single fallback org to decrypt with and titles come back as
|
|
311
|
+
// ciphertext — the List route projects it for the same reason (#3481).
|
|
312
|
+
"organization_id",
|
|
313
|
+
"title",
|
|
314
|
+
"status",
|
|
315
|
+
"pipeline_id",
|
|
316
|
+
"pipeline_stage_id",
|
|
317
|
+
"pipeline_stage",
|
|
318
|
+
"value_amount",
|
|
319
|
+
"value_currency",
|
|
320
|
+
"probability",
|
|
321
|
+
"expected_close_at",
|
|
322
|
+
"owner_user_id",
|
|
323
|
+
"updated_at"
|
|
324
|
+
],
|
|
325
|
+
sort,
|
|
326
|
+
page: { page: query.page, pageSize: query.pageSize },
|
|
327
|
+
filters,
|
|
328
|
+
tenantId: effectiveTenantId,
|
|
329
|
+
organizationId: fallbackOrgId,
|
|
330
|
+
organizationIds: orgScopeIds ?? void 0
|
|
331
|
+
});
|
|
332
|
+
const rows = (Array.isArray(res.items) ? res.items : []).filter(
|
|
333
|
+
(row) => !!row && typeof row === "object"
|
|
334
|
+
);
|
|
335
|
+
const dealIds = rows.map((row) => typeof row.id === "string" && row.id.trim().length ? row.id : null).filter((value) => value !== null);
|
|
336
|
+
const [companyLinks, personLinks] = dealIds.length ? await Promise.all([
|
|
337
|
+
findWithDecryption(
|
|
338
|
+
em,
|
|
339
|
+
CustomerDealCompanyLink,
|
|
340
|
+
{ deal: { $in: dealIds } },
|
|
341
|
+
{ populate: ["company"] },
|
|
342
|
+
decryptionScope
|
|
343
|
+
),
|
|
344
|
+
findWithDecryption(
|
|
345
|
+
em,
|
|
346
|
+
CustomerDealPersonLink,
|
|
347
|
+
{ deal: { $in: dealIds } },
|
|
348
|
+
{ populate: ["person"] },
|
|
349
|
+
decryptionScope
|
|
350
|
+
)
|
|
351
|
+
]) : [[], []];
|
|
352
|
+
const companyRows = companyLinks.map((link) => readLinkRow(link, link.company)).filter((row) => row !== null);
|
|
353
|
+
const personRows = personLinks.map((link) => readLinkRow(link, link.person)).filter((row) => row !== null);
|
|
354
|
+
const companiesByDeal = groupAssociations(companyRows);
|
|
355
|
+
const peopleByDeal = groupAssociations(personRows);
|
|
356
|
+
const linkedEntityIds = Array.from(
|
|
357
|
+
new Set([...companyRows, ...personRows].map((row) => row.entityId))
|
|
358
|
+
);
|
|
359
|
+
const addresses = linkedEntityIds.length ? await findWithDecryption(
|
|
360
|
+
em,
|
|
361
|
+
CustomerAddress,
|
|
362
|
+
{
|
|
363
|
+
entity: { $in: linkedEntityIds },
|
|
364
|
+
latitude: { $ne: null },
|
|
365
|
+
longitude: { $ne: null },
|
|
366
|
+
tenantId: effectiveTenantId,
|
|
367
|
+
...orgScopeFilter
|
|
368
|
+
},
|
|
369
|
+
{},
|
|
370
|
+
decryptionScope
|
|
371
|
+
) : [];
|
|
372
|
+
const addressRows = [];
|
|
373
|
+
for (const address of addresses) {
|
|
374
|
+
const { id: entityId } = readEntityRef(address.entity);
|
|
375
|
+
if (!entityId) continue;
|
|
376
|
+
addressRows.push({
|
|
377
|
+
id: address.id,
|
|
378
|
+
entityId,
|
|
379
|
+
isPrimary: address.isPrimary === true,
|
|
380
|
+
latitude: address.latitude ?? null,
|
|
381
|
+
longitude: address.longitude ?? null,
|
|
382
|
+
city: address.city ?? null,
|
|
383
|
+
region: address.region ?? null,
|
|
384
|
+
country: address.country ?? null,
|
|
385
|
+
createdAt: address.createdAt ?? null
|
|
386
|
+
});
|
|
387
|
+
}
|
|
388
|
+
const toLink = (row) => ({
|
|
389
|
+
dealId: row.dealId,
|
|
390
|
+
entityId: row.entityId,
|
|
391
|
+
createdAt: row.createdAt
|
|
392
|
+
});
|
|
393
|
+
const locationByDeal = resolveDealLocations(
|
|
394
|
+
dealIds,
|
|
395
|
+
companyRows.map(toLink),
|
|
396
|
+
personRows.map(toLink),
|
|
397
|
+
addressRows
|
|
398
|
+
);
|
|
399
|
+
const items = rows.map((row) => {
|
|
400
|
+
const id = typeof row.id === "string" && row.id.trim().length ? row.id : null;
|
|
401
|
+
if (!id) return null;
|
|
402
|
+
return {
|
|
403
|
+
id,
|
|
404
|
+
title: toStringOrNull(row.title),
|
|
405
|
+
status: toStringOrNull(row.status),
|
|
406
|
+
pipelineId: toStringOrNull(row.pipeline_id),
|
|
407
|
+
pipelineStageId: toStringOrNull(row.pipeline_stage_id),
|
|
408
|
+
pipelineStage: toStringOrNull(row.pipeline_stage),
|
|
409
|
+
valueAmount: toFiniteNumberOrNull(row.value_amount),
|
|
410
|
+
valueCurrency: toStringOrNull(row.value_currency),
|
|
411
|
+
probability: toFiniteNumberOrNull(row.probability),
|
|
412
|
+
expectedCloseAt: toIsoStringOrNull(row.expected_close_at),
|
|
413
|
+
ownerUserId: toStringOrNull(row.owner_user_id),
|
|
414
|
+
updatedAt: toIsoStringOrNull(row.updated_at),
|
|
415
|
+
companies: companiesByDeal.get(id) ?? [],
|
|
416
|
+
people: peopleByDeal.get(id) ?? [],
|
|
417
|
+
location: locationByDeal.get(id) ?? null
|
|
418
|
+
};
|
|
419
|
+
}).filter((item) => item !== null);
|
|
420
|
+
return NextResponse.json({
|
|
421
|
+
items,
|
|
422
|
+
total: res.total,
|
|
423
|
+
page: query.page,
|
|
424
|
+
pageSize: query.pageSize,
|
|
425
|
+
totalPages: Math.ceil(res.total / (query.pageSize || 1))
|
|
426
|
+
});
|
|
427
|
+
}
|
|
428
|
+
export {
|
|
429
|
+
GET,
|
|
430
|
+
metadata,
|
|
431
|
+
openApi
|
|
432
|
+
};
|
|
433
|
+
//# sourceMappingURL=route.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../../../src/modules/customers/api/deals/map/route.ts"],
|
|
4
|
+
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { resolveOrganizationScopeForRequest } from '@open-mercato/core/modules/directory/utils/organizationScope'\nimport type { CrudCtx } from '@open-mercato/shared/lib/crud/factory'\nimport { SortDir, type QueryEngine } from '@open-mercato/shared/lib/query/types'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { findWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport {\n CustomerAddress,\n CustomerDealCompanyLink,\n CustomerDealPersonLink,\n} from '../../../data/entities'\nimport { buildDealListFilters, dealListQuerySchema } from '../route'\nimport { applyEntityIdRestriction, findMatchingEntityIdsBySearchTokensAcrossSources } from '../../utils'\nimport { createPagedListResponseSchema } from '../../openapi'\nimport {\n resolveDealLocations,\n type DealMapAddress,\n type DealMapLink,\n type DealMapLocation,\n} from '../../../lib/dealsMapLocation'\nimport { E } from '#generated/entities.ids.generated'\n\nexport const metadata = {\n GET: { requireAuth: true, requireFeatures: ['customers.deals.view', 'customers.activities.view'] },\n}\n\nconst mapSortFields = ['createdAt', 'updatedAt', 'title', 'value', 'probability', 'expectedCloseAt'] as const\n\nconst sortFieldMap: Record<(typeof mapSortFields)[number], string> = {\n createdAt: 'created_at',\n updatedAt: 'updated_at',\n title: 'title',\n value: 'value_amount',\n probability: 'probability',\n expectedCloseAt: 'expected_close_at',\n}\n\nconst querySchema = dealListQuerySchema.extend({\n pageSize: z.coerce.number().min(1).max(100).default(100),\n sortField: z.enum(mapSortFields).optional(),\n})\n\nconst dealMapAssociationSchema = z.object({\n id: z.string().uuid(),\n label: z.string().nullable(),\n})\n\nconst dealMapLocationSchema = z.object({\n latitude: z.number(),\n longitude: z.number(),\n city: z.string().nullable(),\n region: z.string().nullable(),\n country: z.string().nullable(),\n source: z.enum(['company', 'person']),\n entityId: z.string().uuid(),\n addressId: z.string().uuid(),\n})\n\nconst dealMapItemSchema = z.object({\n id: z.string().uuid(),\n title: z.string().nullable(),\n status: z.string().nullable(),\n pipelineId: z.string().uuid().nullable(),\n pipelineStageId: z.string().uuid().nullable(),\n pipelineStage: z.string().nullable(),\n valueAmount: z.number().nullable(),\n valueCurrency: z.string().nullable(),\n probability: z.number().nullable(),\n expectedCloseAt: z.string().nullable(),\n ownerUserId: z.string().uuid().nullable(),\n updatedAt: z.string().nullable(),\n companies: z.array(dealMapAssociationSchema),\n people: z.array(dealMapAssociationSchema),\n location: dealMapLocationSchema.nullable(),\n})\n\nconst mapErrorSchema = z.object({\n error: z.string(),\n})\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Customers',\n summary: 'Deals map listing with resolved locations',\n methods: {\n GET: {\n summary: 'Paginated deals that have a resolvable map location',\n description:\n 'Returns a page of deals that have a coordinate-bearing linked company/person address, each enriched with one resolved location (company primary first, then earliest created; person addresses as fallback). Deals with no coordinate-bearing address are excluded entirely, so every item carries a non-null location in normal operation; the schema keeps location nullable only for the rare case where the address is deleted between the located-deal resolution and the page fetch.',\n query: querySchema,\n responses: [\n { status: 200, description: 'Paged located deals', schema: createPagedListResponseSchema(dealMapItemSchema) },\n ],\n errors: [\n { status: 400, description: 'Invalid query parameters', schema: mapErrorSchema },\n { status: 401, description: 'Unauthorized', schema: mapErrorSchema },\n { status: 403, description: 'Missing required features', schema: mapErrorSchema },\n ],\n },\n },\n}\n\ntype DealMapAssociation = z.infer<typeof dealMapAssociationSchema>\n\ntype LinkRow = {\n dealId: string\n entityId: string\n label: string\n createdAt: Date | string | null\n}\n\nfunction readEntityRef(ref: unknown): { id: string | null; record: Record<string, unknown> | null } {\n if (typeof ref === 'string') return { id: ref, record: null }\n if (ref && typeof ref === 'object') {\n const record = ref as Record<string, unknown>\n return { id: typeof record.id === 'string' ? record.id : null, record }\n }\n return { id: null, record: null }\n}\n\nfunction readLinkRow(link: { deal: unknown; createdAt?: Date | string | null }, linkedRef: unknown): LinkRow | null {\n const { id: dealId } = readEntityRef(link.deal)\n if (!dealId) return null\n const { id: entityId, record } = readEntityRef(linkedRef)\n if (!entityId) return null\n const label = record && typeof record.displayName === 'string' ? record.displayName : ''\n return { dealId, entityId, label, createdAt: link.createdAt ?? null }\n}\n\nfunction groupAssociations(rows: LinkRow[]): Map<string, DealMapAssociation[]> {\n const byDeal = new Map<string, DealMapAssociation[]>()\n for (const row of rows) {\n const bucket = byDeal.get(row.dealId) ?? []\n if (!bucket.some((entry) => entry.id === row.entityId)) {\n bucket.push({ id: row.entityId, label: row.label })\n byDeal.set(row.dealId, bucket)\n }\n }\n return byDeal\n}\n\nfunction toStringOrNull(value: unknown): string | null {\n return typeof value === 'string' && value.length > 0 ? value : null\n}\n\nfunction toFiniteNumberOrNull(value: unknown): number | null {\n if (typeof value === 'number') return Number.isFinite(value) ? value : null\n if (typeof value === 'string' && value.trim().length > 0) {\n const parsed = Number(value)\n return Number.isFinite(parsed) ? parsed : null\n }\n return null\n}\n\nfunction toIsoStringOrNull(value: unknown): string | null {\n if (value instanceof Date) return value.toISOString()\n return toStringOrNull(value)\n}\n\nfunction readArrayParam(searchParams: URLSearchParams, key: string): string[] | null {\n const all = searchParams.getAll(key)\n if (!all.length) return null\n const trimmed = all.flatMap((value) => value.split(',')).map((value) => value.trim()).filter(Boolean)\n return trimmed.length ? trimmed : null\n}\n\nfunction collectRefIds(rows: unknown[], key: string): string[] {\n const ids = new Set<string>()\n for (const row of rows) {\n if (!row || typeof row !== 'object') continue\n const { id } = readEntityRef((row as Record<string, unknown>)[key])\n if (id) ids.add(id)\n }\n return Array.from(ids)\n}\n\n// The deal-list filter builder may already restrict `id` (search, association, stuck, or advanced\n// filters). `applyEntityIdRestriction` overwrites an existing `$in`, so intersect the located-deal\n// set with whatever allow-list is already present before re-applying it.\nfunction readFilterIdAllowlist(filters: Record<string, unknown>): string[] | null {\n const idFilter = filters.id\n if (!idFilter || typeof idFilter !== 'object' || Array.isArray(idFilter)) return null\n const record = idFilter as { $eq?: unknown; $in?: unknown }\n if (typeof record.$eq === 'string') return [record.$eq]\n if (Array.isArray(record.$in)) return record.$in.filter((value): value is string => typeof value === 'string')\n return null\n}\n\nfunction emptyMapResponse(page: number, pageSize: number) {\n return NextResponse.json({ items: [], total: 0, page, pageSize, totalPages: 0 })\n}\n\nexport async function GET(req: Request) {\n const auth = await getAuthFromRequest(req)\n if (!auth?.tenantId) {\n return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n }\n\n const url = new URL(req.url)\n const params = url.searchParams\n const parsed = querySchema.safeParse({\n page: params.get('page') ?? undefined,\n pageSize: params.get('pageSize') ?? undefined,\n search: params.get('search') ?? undefined,\n status: readArrayParam(params, 'status') ?? undefined,\n pipelineId: readArrayParam(params, 'pipelineId') ?? undefined,\n pipelineStageId: params.get('pipelineStageId') ?? undefined,\n ownerUserId: readArrayParam(params, 'ownerUserId') ?? undefined,\n personId: readArrayParam(params, 'personId') ?? undefined,\n companyId: readArrayParam(params, 'companyId') ?? undefined,\n valueCurrency: readArrayParam(params, 'valueCurrency') ?? undefined,\n expectedCloseAtFrom: params.get('expectedCloseAtFrom') ?? undefined,\n expectedCloseAtTo: params.get('expectedCloseAtTo') ?? undefined,\n isStuck: params.get('isStuck') ?? undefined,\n isOverdue: params.get('isOverdue') ?? undefined,\n sortField: params.get('sortField') ?? undefined,\n sortDir: params.get('sortDir') ?? undefined,\n })\n if (!parsed.success) {\n return NextResponse.json({ error: 'Invalid query parameters' }, { status: 400 })\n }\n const query = parsed.data\n\n const container = await createRequestContainer()\n const em = container.resolve<EntityManager>('em')\n const queryEngine = container.resolve<QueryEngine>('queryEngine')\n\n const scope = await resolveOrganizationScopeForRequest({ container, auth, request: req })\n const effectiveTenantId = scope.tenantId ?? auth.tenantId\n // `null` = unrestricted access (superadmin or an \"all organizations\" grant): aggregate tenant-wide,\n // exactly like the deals List route (`makeCrudRoute`) and the query_index status route. A populated\n // array bounds the query to the caller's visible orgs; an empty array means no org visibility \u2192 401.\n // The previous guard hard-required `auth.orgId`, which is empty under the header \"All organizations\"\n // scope, so the map 401'd (and hung on the loading spinner) while List/Kanban aggregated fine (#3481).\n const orgScopeIds: string[] | null =\n scope.filterIds === null\n ? null\n : Array.isArray(scope.filterIds) && scope.filterIds.length > 0\n ? Array.from(new Set(scope.filterIds.filter((id) => typeof id === 'string' && id.length > 0)))\n : auth.orgId\n ? [auth.orgId]\n : []\n if (!effectiveTenantId || (Array.isArray(orgScopeIds) && orgScopeIds.length === 0)) {\n return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n }\n\n // Bounded `$in` when the caller is org-restricted; omitted entirely under unrestricted access so\n // every query stays tenant-scoped only. `fallbackOrgId` is undefined when unrestricted.\n const orgScopeFilter = orgScopeIds ? { organizationId: { $in: orgScopeIds } } : {}\n const fallbackOrgId = orgScopeIds && orgScopeIds.length > 0 ? orgScopeIds[0] : undefined\n\n // `findWithDecryption` decrypts each row with the row's OWN tenant/organization (the encryption\n // subscriber's resolveScope(target) wins); this scope is only a fallback for rows that carry no\n // scope columns. CustomerAddress always carries organization_id, so every org's rows still decrypt\n // correctly under a multi-org / unrestricted scope even though `fallbackOrgId` may be undefined.\n const decryptionScope = { tenantId: effectiveTenantId, organizationId: fallbackOrgId }\n\n // Located-only, resolved in two stages so the per-request cost stays page-bounded:\n // 1) LIGHT id-only queries (FK columns only, no decryption) determine which deals have a\n // coordinate-bearing linked company/person, so pagination + `total` operate on located deals.\n // 2) The HEAVY decrypted/populated fetch (labels + address coordinates) runs only for the deals\n // that actually land on the requested page \u2014 never the whole located universe.\n const coordinateEntityRows = await findWithDecryption(\n em,\n CustomerAddress,\n {\n latitude: { $ne: null },\n longitude: { $ne: null },\n tenantId: effectiveTenantId,\n ...orgScopeFilter,\n },\n { fields: ['entity'] },\n decryptionScope,\n )\n const locatedEntityIds = collectRefIds(coordinateEntityRows, 'entity')\n if (locatedEntityIds.length === 0) {\n return emptyMapResponse(query.page, query.pageSize)\n }\n\n const [locatedCompanyLinkRows, locatedPersonLinkRows] = await Promise.all([\n findWithDecryption(\n em,\n CustomerDealCompanyLink,\n { company: { $in: locatedEntityIds } },\n { fields: ['deal'] },\n decryptionScope,\n ),\n findWithDecryption(\n em,\n CustomerDealPersonLink,\n { person: { $in: locatedEntityIds } },\n { fields: ['deal'] },\n decryptionScope,\n ),\n ])\n const locatedDealIds = Array.from(\n new Set([\n ...collectRefIds(locatedCompanyLinkRows, 'deal'),\n ...collectRefIds(locatedPersonLinkRows, 'deal'),\n ]),\n )\n if (locatedDealIds.length === 0) {\n return emptyMapResponse(query.page, query.pageSize)\n }\n\n const ctx: CrudCtx = {\n container,\n auth,\n organizationScope: scope,\n selectedOrganizationId: fallbackOrgId ?? null,\n organizationIds: orgScopeIds,\n request: req,\n }\n\n const filters = await buildDealListFilters(query, ctx)\n const existingAllowlist = readFilterIdAllowlist(filters)\n\n // The map card headline is the linked company/person name, which the deal-field token search\n // (title/description/status/\u2026) used by `buildDealListFilters` does NOT cover \u2014 so searching a\n // company/person name a user can plainly see on a card would otherwise return nothing. When a\n // search term is present, also resolve located deals whose linked company/person ENTITY name\n // matches, and union them with the deal-field matches. The sentinel \"no match\" id from the\n // deal-field search drops out naturally at the located intersection below, so no special-casing.\n let searchAllowlist = existingAllowlist\n if (query.search && locatedEntityIds.length > 0) {\n const nameMatchedEntityIds = await findMatchingEntityIdsBySearchTokensAcrossSources({\n ctx,\n query: query.search,\n sources: [\n {\n entityType: E.customers.customer_entity,\n fields: ['display_name', 'primary_email', 'primary_phone', 'description'],\n },\n ],\n })\n const locatedEntitySet = new Set(locatedEntityIds)\n const locatedMatchedEntityIds = (nameMatchedEntityIds ?? []).filter((id) => locatedEntitySet.has(id))\n if (locatedMatchedEntityIds.length > 0) {\n const [companyNameLinkRows, personNameLinkRows] = await Promise.all([\n findWithDecryption(\n em,\n CustomerDealCompanyLink,\n { company: { $in: locatedMatchedEntityIds } },\n { fields: ['deal'] },\n decryptionScope,\n ),\n findWithDecryption(\n em,\n CustomerDealPersonLink,\n { person: { $in: locatedMatchedEntityIds } },\n { fields: ['deal'] },\n decryptionScope,\n ),\n ])\n let nameMatchedDealIds = [\n ...collectRefIds(companyNameLinkRows, 'deal'),\n ...collectRefIds(personNameLinkRows, 'deal'),\n ]\n if (nameMatchedDealIds.length > 0) {\n // The deal-field search in `buildDealListFilters` is intersected with the People/Companies\n // association (and stuck/`?id`) filters \u2014 they all land together in `filters.id`. The name\n // matches above carry NO such constraint, so unioning them raw would let a co-active\n // People/Companies filter be bypassed (over-broad results). Recover exactly the non-search id\n // allowlist by rebuilding the deal filters WITHOUT the search term, and intersect the name\n // matches with it so the active filter is honored: located \u2229 nonSearch \u2229 (dealField \u222A name).\n // (The map's query carries no advanced `filter[...]` params, so the consume-mutation in\n // buildDealListFilters does not affect this second build.)\n const nonSearchAllowlist = readFilterIdAllowlist(\n await buildDealListFilters({ ...query, search: undefined }, ctx),\n )\n if (nonSearchAllowlist) {\n const nonSearchLookup = new Set(nonSearchAllowlist)\n nameMatchedDealIds = nameMatchedDealIds.filter((id) => nonSearchLookup.has(id))\n }\n }\n if (nameMatchedDealIds.length > 0) {\n searchAllowlist = Array.from(new Set([...(existingAllowlist ?? []), ...nameMatchedDealIds]))\n }\n }\n }\n\n const allowlistLookup = searchAllowlist ? new Set(searchAllowlist) : null\n const restrictedDealIds = allowlistLookup\n ? locatedDealIds.filter((id) => allowlistLookup.has(id))\n : locatedDealIds\n // `restrictedDealIds` is the final located \u2229 (deal-field \u222A company/person-name) set \u2014 it already\n // captures every id constraint `buildDealListFilters` expressed (read back via\n // `readFilterIdAllowlist`). Clear the stale `filters.id` first so `applyEntityIdRestriction` does\n // not re-intersect against the deal-field \"no match\" sentinel and drop the name-matched deals.\n delete (filters as Record<string, unknown>).id\n applyEntityIdRestriction(filters, restrictedDealIds)\n\n const sortColumn = query.sortField ? sortFieldMap[query.sortField] : 'id'\n const sortDir = query.sortDir === 'desc' ? SortDir.Desc : SortDir.Asc\n // Always append `id` as a stable tiebreaker \u2014 non-unique sort columns (title, value,\n // probability, stage, or colliding timestamps) otherwise reorder between OFFSET pages and the\n // client's multi-page accumulation would skip or duplicate deals at page boundaries.\n const sort = sortColumn === 'id'\n ? [{ field: 'id', dir: sortDir }]\n : [{ field: sortColumn, dir: sortDir }, { field: 'id', dir: SortDir.Asc }]\n\n const res = await queryEngine.query(E.customers.customer_deal, {\n fields: [\n 'id',\n // `organization_id` is projected (never exposed in the response) so the query engine can decrypt\n // each row's encrypted fields (e.g. `title`) with the row's OWN org. Without it, an unrestricted\n // \"All organizations\" scope has no single fallback org to decrypt with and titles come back as\n // ciphertext \u2014 the List route projects it for the same reason (#3481).\n 'organization_id',\n 'title',\n 'status',\n 'pipeline_id',\n 'pipeline_stage_id',\n 'pipeline_stage',\n 'value_amount',\n 'value_currency',\n 'probability',\n 'expected_close_at',\n 'owner_user_id',\n 'updated_at',\n ],\n sort,\n page: { page: query.page, pageSize: query.pageSize },\n filters,\n tenantId: effectiveTenantId,\n organizationId: fallbackOrgId,\n organizationIds: orgScopeIds ?? undefined,\n })\n\n const rows = (Array.isArray(res.items) ? res.items : []).filter(\n (row): row is Record<string, unknown> => !!row && typeof row === 'object',\n )\n const dealIds = rows\n .map((row) => (typeof row.id === 'string' && row.id.trim().length ? row.id : null))\n .filter((value): value is string => value !== null)\n\n // Heavy fetch, page-bounded: pull decrypted links (for labels) and coordinate-bearing addresses\n // only for the deals on this page.\n const [companyLinks, personLinks] = dealIds.length\n ? await Promise.all([\n findWithDecryption(\n em,\n CustomerDealCompanyLink,\n { deal: { $in: dealIds } },\n { populate: ['company'] },\n decryptionScope,\n ),\n findWithDecryption(\n em,\n CustomerDealPersonLink,\n { deal: { $in: dealIds } },\n { populate: ['person'] },\n decryptionScope,\n ),\n ])\n : [[], []]\n\n const companyRows = companyLinks\n .map((link) => readLinkRow(link, link.company))\n .filter((row): row is LinkRow => row !== null)\n const personRows = personLinks\n .map((link) => readLinkRow(link, link.person))\n .filter((row): row is LinkRow => row !== null)\n const companiesByDeal = groupAssociations(companyRows)\n const peopleByDeal = groupAssociations(personRows)\n\n const linkedEntityIds = Array.from(\n new Set([...companyRows, ...personRows].map((row) => row.entityId)),\n )\n const addresses = linkedEntityIds.length\n ? await findWithDecryption(\n em,\n CustomerAddress,\n {\n entity: { $in: linkedEntityIds },\n latitude: { $ne: null },\n longitude: { $ne: null },\n tenantId: effectiveTenantId,\n ...orgScopeFilter,\n },\n {},\n decryptionScope,\n )\n : []\n\n const addressRows: DealMapAddress[] = []\n for (const address of addresses) {\n const { id: entityId } = readEntityRef(address.entity)\n if (!entityId) continue\n addressRows.push({\n id: address.id,\n entityId,\n isPrimary: address.isPrimary === true,\n latitude: address.latitude ?? null,\n longitude: address.longitude ?? null,\n city: address.city ?? null,\n region: address.region ?? null,\n country: address.country ?? null,\n createdAt: address.createdAt ?? null,\n })\n }\n\n const toLink = (row: LinkRow): DealMapLink => ({\n dealId: row.dealId,\n entityId: row.entityId,\n createdAt: row.createdAt,\n })\n const locationByDeal: Map<string, DealMapLocation | null> = resolveDealLocations(\n dealIds,\n companyRows.map(toLink),\n personRows.map(toLink),\n addressRows,\n )\n\n const items = rows\n .map((row) => {\n const id = typeof row.id === 'string' && row.id.trim().length ? row.id : null\n if (!id) return null\n return {\n id,\n title: toStringOrNull(row.title),\n status: toStringOrNull(row.status),\n pipelineId: toStringOrNull(row.pipeline_id),\n pipelineStageId: toStringOrNull(row.pipeline_stage_id),\n pipelineStage: toStringOrNull(row.pipeline_stage),\n valueAmount: toFiniteNumberOrNull(row.value_amount),\n valueCurrency: toStringOrNull(row.value_currency),\n probability: toFiniteNumberOrNull(row.probability),\n expectedCloseAt: toIsoStringOrNull(row.expected_close_at),\n ownerUserId: toStringOrNull(row.owner_user_id),\n updatedAt: toIsoStringOrNull(row.updated_at),\n companies: companiesByDeal.get(id) ?? [],\n people: peopleByDeal.get(id) ?? [],\n location: locationByDeal.get(id) ?? null,\n }\n })\n .filter((item): item is NonNullable<typeof item> => item !== null)\n\n return NextResponse.json({\n items,\n total: res.total,\n page: query.page,\n pageSize: query.pageSize,\n totalPages: Math.ceil(res.total / (query.pageSize || 1)),\n })\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -93,6 +93,8 @@ function CreateCompanyPage() {
|
|
|
93
93
|
if (postalCode !== void 0) body.postalCode = postalCode;
|
|
94
94
|
const country = normalize(entry.country);
|
|
95
95
|
if (country !== void 0) body.country = country.toUpperCase();
|
|
96
|
+
if (typeof entry.latitude === "number") body.latitude = entry.latitude;
|
|
97
|
+
if (typeof entry.longitude === "number") body.longitude = entry.longitude;
|
|
96
98
|
try {
|
|
97
99
|
await createCrud("customers/addresses", body);
|
|
98
100
|
} catch (addressErr) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../../src/modules/customers/backend/customers/companies/create/page.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { useRouter, useSearchParams } from 'next/navigation'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { CrudForm } from '@open-mercato/ui/backend/CrudForm'\nimport { createCrud } from '@open-mercato/ui/backend/utils/crud'\nimport { createCrudFormError } from '@open-mercato/ui/backend/utils/serverErrors'\nimport { E } from '#generated/entities.ids.generated'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { useOrganizationScopeDetail } from '@open-mercato/shared/lib/frontend/useOrganizationScope'\nimport {\n buildCompanyPayload,\n createCompanyFormFields,\n createCompanyFormGroups,\n createCompanyFormSchema,\n type CompanyFormValues,\n} from '../../../../components/formConfig'\n\nexport default function CreateCompanyPage() {\n const t = useT()\n const router = useRouter()\n const searchParams = useSearchParams()\n const { organizationId } = useOrganizationScopeDetail()\n\n const formSchema = React.useMemo(() => createCompanyFormSchema(), [])\n const fields = React.useMemo(() => createCompanyFormFields(t), [t])\n const groups = React.useMemo(() => createCompanyFormGroups(t), [t])\n const returnTo = searchParams.get('returnTo')\n\n return (\n <Page>\n <PageBody>\n <CrudForm<CompanyFormValues>\n title={t('customers.companies.create.title')}\n backHref={returnTo ?? '/backend/customers/companies'}\n fields={fields}\n groups={groups}\n initialValues={{ addresses: [] as CompanyFormValues['addresses'] }}\n entityIds={[E.customers.customer_entity, E.customers.customer_company_profile]}\n submitLabel={t('customers.companies.form.submit')}\n cancelHref={returnTo ?? '/backend/customers/companies'}\n schema={formSchema}\n onSubmit={async (values) => {\n const addresses = Array.isArray(values.addresses) ? values.addresses : []\n let payload: Record<string, unknown>\n try {\n payload = buildCompanyPayload(values, organizationId)\n } catch (err) {\n if (err instanceof Error) {\n if (err.message === 'DISPLAY_NAME_REQUIRED') {\n const message = t('customers.companies.form.displayName.error')\n throw createCrudFormError(message, { displayName: message })\n }\n if (err.message === 'ANNUAL_REVENUE_INVALID') {\n const message = t('customers.companies.form.annualRevenue.error')\n throw createCrudFormError(message, { annualRevenue: message })\n }\n }\n throw err\n }\n\n const { result: created } = await createCrud<{ id?: string; entityId?: string }>(\n 'customers/companies',\n payload,\n )\n const newId =\n created && typeof created.id === 'string'\n ? created.id\n : (typeof created?.entityId === 'string' ? created.entityId : null)\n\n if (newId && addresses.length) {\n const normalize = (value?: string | null) => {\n if (typeof value !== 'string') return undefined\n const trimmed = value.trim()\n return trimmed.length ? trimmed : undefined\n }\n for (const entry of addresses) {\n const normalizedLine1 = normalize(entry.addressLine1)\n if (!normalizedLine1) continue\n const body: Record<string, unknown> = {\n entityId: newId,\n ...(organizationId ? { organizationId } : {}),\n addressLine1: normalizedLine1,\n isPrimary: entry.isPrimary ?? false,\n }\n const name = normalize(entry.name)\n if (name !== undefined) body.name = name\n const purpose = normalize(entry.purpose)\n if (purpose !== undefined) body.purpose = purpose\n const line2 = normalize(entry.addressLine2)\n if (line2 !== undefined) body.addressLine2 = line2\n const buildingNumber = normalize(entry.buildingNumber)\n if (buildingNumber !== undefined) body.buildingNumber = buildingNumber\n const flatNumber = normalize(entry.flatNumber)\n if (flatNumber !== undefined) body.flatNumber = flatNumber\n const city = normalize(entry.city)\n if (city !== undefined) body.city = city\n const region = normalize(entry.region)\n if (region !== undefined) body.region = region\n const postalCode = normalize(entry.postalCode)\n if (postalCode !== undefined) body.postalCode = postalCode\n const country = normalize(entry.country)\n if (country !== undefined) body.country = country.toUpperCase()\n try {\n await createCrud('customers/addresses', body)\n } catch (addressErr) {\n const message =\n addressErr instanceof Error && addressErr.message\n ? addressErr.message\n : t('customers.companies.detail.addresses.error')\n flash(message, 'error')\n }\n }\n }\n\n flash(t('customers.companies.form.success'), 'success')\n if (returnTo) router.push(returnTo)\n else if (newId) router.push(`/backend/customers/companies-v2/${newId}`)\n else router.push('/backend/customers/companies')\n }}\n />\n </PageBody>\n </Page>\n )\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { useRouter, useSearchParams } from 'next/navigation'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { CrudForm } from '@open-mercato/ui/backend/CrudForm'\nimport { createCrud } from '@open-mercato/ui/backend/utils/crud'\nimport { createCrudFormError } from '@open-mercato/ui/backend/utils/serverErrors'\nimport { E } from '#generated/entities.ids.generated'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { useOrganizationScopeDetail } from '@open-mercato/shared/lib/frontend/useOrganizationScope'\nimport {\n buildCompanyPayload,\n createCompanyFormFields,\n createCompanyFormGroups,\n createCompanyFormSchema,\n type CompanyFormValues,\n} from '../../../../components/formConfig'\n\nexport default function CreateCompanyPage() {\n const t = useT()\n const router = useRouter()\n const searchParams = useSearchParams()\n const { organizationId } = useOrganizationScopeDetail()\n\n const formSchema = React.useMemo(() => createCompanyFormSchema(), [])\n const fields = React.useMemo(() => createCompanyFormFields(t), [t])\n const groups = React.useMemo(() => createCompanyFormGroups(t), [t])\n const returnTo = searchParams.get('returnTo')\n\n return (\n <Page>\n <PageBody>\n <CrudForm<CompanyFormValues>\n title={t('customers.companies.create.title')}\n backHref={returnTo ?? '/backend/customers/companies'}\n fields={fields}\n groups={groups}\n initialValues={{ addresses: [] as CompanyFormValues['addresses'] }}\n entityIds={[E.customers.customer_entity, E.customers.customer_company_profile]}\n submitLabel={t('customers.companies.form.submit')}\n cancelHref={returnTo ?? '/backend/customers/companies'}\n schema={formSchema}\n onSubmit={async (values) => {\n const addresses = Array.isArray(values.addresses) ? values.addresses : []\n let payload: Record<string, unknown>\n try {\n payload = buildCompanyPayload(values, organizationId)\n } catch (err) {\n if (err instanceof Error) {\n if (err.message === 'DISPLAY_NAME_REQUIRED') {\n const message = t('customers.companies.form.displayName.error')\n throw createCrudFormError(message, { displayName: message })\n }\n if (err.message === 'ANNUAL_REVENUE_INVALID') {\n const message = t('customers.companies.form.annualRevenue.error')\n throw createCrudFormError(message, { annualRevenue: message })\n }\n }\n throw err\n }\n\n const { result: created } = await createCrud<{ id?: string; entityId?: string }>(\n 'customers/companies',\n payload,\n )\n const newId =\n created && typeof created.id === 'string'\n ? created.id\n : (typeof created?.entityId === 'string' ? created.entityId : null)\n\n if (newId && addresses.length) {\n const normalize = (value?: string | null) => {\n if (typeof value !== 'string') return undefined\n const trimmed = value.trim()\n return trimmed.length ? trimmed : undefined\n }\n for (const entry of addresses) {\n const normalizedLine1 = normalize(entry.addressLine1)\n if (!normalizedLine1) continue\n const body: Record<string, unknown> = {\n entityId: newId,\n ...(organizationId ? { organizationId } : {}),\n addressLine1: normalizedLine1,\n isPrimary: entry.isPrimary ?? false,\n }\n const name = normalize(entry.name)\n if (name !== undefined) body.name = name\n const purpose = normalize(entry.purpose)\n if (purpose !== undefined) body.purpose = purpose\n const line2 = normalize(entry.addressLine2)\n if (line2 !== undefined) body.addressLine2 = line2\n const buildingNumber = normalize(entry.buildingNumber)\n if (buildingNumber !== undefined) body.buildingNumber = buildingNumber\n const flatNumber = normalize(entry.flatNumber)\n if (flatNumber !== undefined) body.flatNumber = flatNumber\n const city = normalize(entry.city)\n if (city !== undefined) body.city = city\n const region = normalize(entry.region)\n if (region !== undefined) body.region = region\n const postalCode = normalize(entry.postalCode)\n if (postalCode !== undefined) body.postalCode = postalCode\n const country = normalize(entry.country)\n if (country !== undefined) body.country = country.toUpperCase()\n if (typeof entry.latitude === 'number') body.latitude = entry.latitude\n if (typeof entry.longitude === 'number') body.longitude = entry.longitude\n try {\n await createCrud('customers/addresses', body)\n } catch (addressErr) {\n const message =\n addressErr instanceof Error && addressErr.message\n ? addressErr.message\n : t('customers.companies.detail.addresses.error')\n flash(message, 'error')\n }\n }\n }\n\n flash(t('customers.companies.form.success'), 'success')\n if (returnTo) router.push(returnTo)\n else if (newId) router.push(`/backend/customers/companies-v2/${newId}`)\n else router.push('/backend/customers/companies')\n }}\n />\n </PageBody>\n </Page>\n )\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|