@open-mercato/core 0.6.6-develop.6184.1.b7e55f8d61 → 0.6.6-develop.6198.1.0822f97cc6
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/auth/frontend/login.js +5 -5
- package/dist/modules/auth/frontend/login.js.map +2 -2
- package/dist/modules/catalog/backend/catalog/products/MerchandisingAssistantSheet.js +3 -2
- package/dist/modules/catalog/backend/catalog/products/MerchandisingAssistantSheet.js.map +2 -2
- package/dist/modules/catalog/widgets/injection/product-seo/widget.client.js +26 -14
- package/dist/modules/catalog/widgets/injection/product-seo/widget.client.js.map +2 -2
- package/dist/modules/customers/api/deals/[id]/route.js +169 -149
- package/dist/modules/customers/api/deals/[id]/route.js.map +3 -3
- package/dist/modules/customers/components/detail/InlineEditors.js +9 -4
- package/dist/modules/customers/components/detail/InlineEditors.js.map +2 -2
- package/dist/modules/integrations/backend/integrations/[id]/page.js +13 -9
- package/dist/modules/integrations/backend/integrations/[id]/page.js.map +2 -2
- package/dist/modules/integrations/backend/integrations/detail-page-refresh.js +17 -0
- package/dist/modules/integrations/backend/integrations/detail-page-refresh.js.map +7 -0
- package/dist/modules/sales/components/documents/SalesDocumentForm.js +299 -297
- package/dist/modules/sales/components/documents/SalesDocumentForm.js.map +2 -2
- package/dist/modules/workflows/components/EdgeEditDialog.js +120 -141
- package/dist/modules/workflows/components/EdgeEditDialog.js.map +2 -2
- package/dist/modules/workflows/components/NodeEditDialog.js +114 -118
- package/dist/modules/workflows/components/NodeEditDialog.js.map +2 -2
- package/dist/modules/workflows/frontend/checkout-demo/page.js +155 -260
- package/dist/modules/workflows/frontend/checkout-demo/page.js.map +2 -2
- package/package.json +7 -7
- package/src/modules/auth/frontend/login.tsx +5 -5
- package/src/modules/catalog/backend/catalog/products/MerchandisingAssistantSheet.tsx +4 -3
- package/src/modules/catalog/widgets/injection/product-seo/widget.client.tsx +27 -17
- package/src/modules/customers/api/deals/[id]/route.ts +208 -174
- package/src/modules/customers/components/detail/InlineEditors.tsx +6 -10
- package/src/modules/integrations/backend/integrations/[id]/page.tsx +13 -9
- package/src/modules/integrations/backend/integrations/detail-page-refresh.ts +36 -0
- package/src/modules/sales/components/documents/SalesDocumentForm.tsx +351 -327
- package/src/modules/workflows/components/EdgeEditDialog.tsx +132 -157
- package/src/modules/workflows/components/NodeEditDialog.tsx +116 -121
- package/src/modules/workflows/frontend/checkout-demo/page.tsx +172 -239
- package/src/modules/workflows/i18n/de.json +2 -0
- package/src/modules/workflows/i18n/en.json +2 -0
- package/src/modules/workflows/i18n/es.json +2 -0
- package/src/modules/workflows/i18n/pl.json +2 -0
|
@@ -279,92 +279,96 @@ async function GET(request, context) {
|
|
|
279
279
|
tenantId: deal.tenantId ?? auth.tenantId ?? null,
|
|
280
280
|
organizationId: deal.organizationId ?? auth.orgId ?? null
|
|
281
281
|
};
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
)
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
282
|
+
const resolveAssociations = async () => {
|
|
283
|
+
if (liteView) {
|
|
284
|
+
const [personLinkRows, companyLinkRows] = await Promise.all([
|
|
285
|
+
findWithDecryption(
|
|
286
|
+
em,
|
|
287
|
+
CustomerDealPersonLink,
|
|
288
|
+
{ deal: deal.id },
|
|
289
|
+
{ orderBy: { createdAt: "ASC" } },
|
|
290
|
+
decryptionScope
|
|
291
|
+
),
|
|
292
|
+
findWithDecryption(
|
|
293
|
+
em,
|
|
294
|
+
CustomerDealCompanyLink,
|
|
295
|
+
{ deal: deal.id },
|
|
296
|
+
{ orderBy: { createdAt: "ASC" } },
|
|
297
|
+
decryptionScope
|
|
298
|
+
)
|
|
299
|
+
]);
|
|
300
|
+
const linkedPersonIds2 = Array.from(
|
|
301
|
+
new Set(
|
|
302
|
+
personLinkRows.map((link) => {
|
|
303
|
+
const personRef = link.person;
|
|
304
|
+
if (!personRef) return null;
|
|
305
|
+
if (typeof personRef === "string") return personRef;
|
|
306
|
+
const personIdValue = personRef.id;
|
|
307
|
+
return typeof personIdValue === "string" ? personIdValue : null;
|
|
308
|
+
}).filter((value) => typeof value === "string" && value.trim().length > 0)
|
|
309
|
+
)
|
|
310
|
+
);
|
|
311
|
+
const linkedCompanyIds2 = Array.from(
|
|
312
|
+
new Set(
|
|
313
|
+
companyLinkRows.map((link) => {
|
|
314
|
+
const companyRef = link.company;
|
|
315
|
+
if (!companyRef) return null;
|
|
316
|
+
if (typeof companyRef === "string") return companyRef;
|
|
317
|
+
const companyIdValue = companyRef.id;
|
|
318
|
+
return typeof companyIdValue === "string" ? companyIdValue : null;
|
|
319
|
+
}).filter((value) => typeof value === "string" && value.trim().length > 0)
|
|
320
|
+
)
|
|
321
|
+
);
|
|
322
|
+
const [previewPeople, previewCompanies] = await Promise.all([
|
|
323
|
+
linkedPersonIds2.length ? findWithDecryption(
|
|
324
|
+
em,
|
|
325
|
+
CustomerEntity,
|
|
326
|
+
{ id: { $in: linkedPersonIds2.slice(0, 3) } },
|
|
327
|
+
{ populate: ["personProfile"] },
|
|
328
|
+
decryptionScope
|
|
329
|
+
) : [],
|
|
330
|
+
linkedCompanyIds2.length ? findWithDecryption(
|
|
331
|
+
em,
|
|
332
|
+
CustomerEntity,
|
|
333
|
+
{ id: { $in: linkedCompanyIds2.slice(0, 3) } },
|
|
334
|
+
{ populate: ["companyProfile"] },
|
|
335
|
+
decryptionScope
|
|
336
|
+
) : []
|
|
337
|
+
]);
|
|
338
|
+
const previewPeopleMap = new Map(previewPeople.map((entity) => [entity.id, entity]));
|
|
339
|
+
const previewCompaniesMap = new Map(previewCompanies.map((entity) => [entity.id, entity]));
|
|
340
|
+
const people3 = linkedPersonIds2.slice(0, 3).reduce((acc, personId) => {
|
|
341
|
+
const entity = previewPeopleMap.get(personId) ?? null;
|
|
342
|
+
if (!entity || entity.deletedAt) return acc;
|
|
343
|
+
const { label, subtitle } = normalizePersonAssociation(entity);
|
|
344
|
+
acc.push({ id: entity.id, label, subtitle, kind: "person" });
|
|
345
|
+
return acc;
|
|
346
|
+
}, []);
|
|
347
|
+
const companies3 = linkedCompanyIds2.slice(0, 3).reduce((acc, companyId) => {
|
|
348
|
+
const entity = previewCompaniesMap.get(companyId) ?? null;
|
|
349
|
+
if (!entity || entity.deletedAt) return acc;
|
|
350
|
+
const { label, subtitle } = normalizeCompanyAssociation(entity);
|
|
351
|
+
acc.push({ id: entity.id, label, subtitle, kind: "company" });
|
|
352
|
+
return acc;
|
|
353
|
+
}, []);
|
|
354
|
+
return { people: people3, companies: companies3, linkedPersonIds: linkedPersonIds2, linkedCompanyIds: linkedCompanyIds2 };
|
|
355
|
+
}
|
|
356
|
+
const [personLinks, companyLinks] = await Promise.all([
|
|
357
|
+
findWithDecryption(
|
|
358
|
+
em,
|
|
359
|
+
CustomerDealPersonLink,
|
|
360
|
+
{ deal: deal.id },
|
|
361
|
+
{ populate: ["person", "person.personProfile"] },
|
|
362
|
+
decryptionScope
|
|
363
|
+
),
|
|
364
|
+
findWithDecryption(
|
|
365
|
+
em,
|
|
366
|
+
CustomerDealCompanyLink,
|
|
367
|
+
{ deal: deal.id },
|
|
368
|
+
{ populate: ["company", "company.companyProfile"] },
|
|
369
|
+
decryptionScope
|
|
321
370
|
)
|
|
322
|
-
);
|
|
323
|
-
const previewPeople = linkedPersonIds.length ? await findWithDecryption(
|
|
324
|
-
em,
|
|
325
|
-
CustomerEntity,
|
|
326
|
-
{ id: { $in: linkedPersonIds.slice(0, 3) } },
|
|
327
|
-
{ populate: ["personProfile"] },
|
|
328
|
-
decryptionScope
|
|
329
|
-
) : [];
|
|
330
|
-
const previewCompanies = linkedCompanyIds.length ? await findWithDecryption(
|
|
331
|
-
em,
|
|
332
|
-
CustomerEntity,
|
|
333
|
-
{ id: { $in: linkedCompanyIds.slice(0, 3) } },
|
|
334
|
-
{ populate: ["companyProfile"] },
|
|
335
|
-
decryptionScope
|
|
336
|
-
) : [];
|
|
337
|
-
const previewPeopleMap = new Map(previewPeople.map((entity) => [entity.id, entity]));
|
|
338
|
-
const previewCompaniesMap = new Map(previewCompanies.map((entity) => [entity.id, entity]));
|
|
339
|
-
people = linkedPersonIds.slice(0, 3).reduce((acc, personId) => {
|
|
340
|
-
const entity = previewPeopleMap.get(personId) ?? null;
|
|
341
|
-
if (!entity || entity.deletedAt) return acc;
|
|
342
|
-
const { label, subtitle } = normalizePersonAssociation(entity);
|
|
343
|
-
acc.push({ id: entity.id, label, subtitle, kind: "person" });
|
|
344
|
-
return acc;
|
|
345
|
-
}, []);
|
|
346
|
-
companies = linkedCompanyIds.slice(0, 3).reduce((acc, companyId) => {
|
|
347
|
-
const entity = previewCompaniesMap.get(companyId) ?? null;
|
|
348
|
-
if (!entity || entity.deletedAt) return acc;
|
|
349
|
-
const { label, subtitle } = normalizeCompanyAssociation(entity);
|
|
350
|
-
acc.push({ id: entity.id, label, subtitle, kind: "company" });
|
|
351
|
-
return acc;
|
|
352
|
-
}, []);
|
|
353
|
-
} else {
|
|
354
|
-
const personLinks = await findWithDecryption(
|
|
355
|
-
em,
|
|
356
|
-
CustomerDealPersonLink,
|
|
357
|
-
{ deal: deal.id },
|
|
358
|
-
{ populate: ["person", "person.personProfile"] },
|
|
359
|
-
decryptionScope
|
|
360
|
-
);
|
|
361
|
-
const companyLinks = await findWithDecryption(
|
|
362
|
-
em,
|
|
363
|
-
CustomerDealCompanyLink,
|
|
364
|
-
{ deal: deal.id },
|
|
365
|
-
{ populate: ["company", "company.companyProfile"] },
|
|
366
|
-
decryptionScope
|
|
367
|
-
);
|
|
371
|
+
]);
|
|
368
372
|
const fallbackTenantId = deal.tenantId ?? auth.tenantId ?? null;
|
|
369
373
|
const fallbackOrgId = deal.organizationId ?? auth.orgId ?? null;
|
|
370
374
|
await decryptEntitiesWithFallbackScope(personLinks, {
|
|
@@ -377,93 +381,55 @@ async function GET(request, context) {
|
|
|
377
381
|
tenantId: fallbackTenantId,
|
|
378
382
|
organizationId: fallbackOrgId
|
|
379
383
|
});
|
|
380
|
-
|
|
384
|
+
const people2 = personLinks.reduce((acc, link) => {
|
|
381
385
|
const entity = link.person;
|
|
382
386
|
if (!entity || entity.deletedAt) return acc;
|
|
383
387
|
const { label, subtitle } = normalizePersonAssociation(entity);
|
|
384
388
|
acc.push({ id: entity.id, label, subtitle, kind: "person" });
|
|
385
389
|
return acc;
|
|
386
390
|
}, []);
|
|
387
|
-
|
|
391
|
+
const companies2 = companyLinks.reduce((acc, link) => {
|
|
388
392
|
const entity = link.company;
|
|
389
393
|
if (!entity || entity.deletedAt) return acc;
|
|
390
394
|
const { label, subtitle } = normalizeCompanyAssociation(entity);
|
|
391
395
|
acc.push({ id: entity.id, label, subtitle, kind: "company" });
|
|
392
396
|
return acc;
|
|
393
397
|
}, []);
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
entityId: E.customers.customer_deal,
|
|
400
|
-
recordIds: [deal.id],
|
|
401
|
-
tenantIdByRecord: { [deal.id]: deal.tenantId ?? null },
|
|
402
|
-
organizationIdByRecord: { [deal.id]: deal.organizationId ?? null },
|
|
403
|
-
tenantFallbacks: [deal.tenantId ?? auth.tenantId ?? null].filter((value) => !!value)
|
|
404
|
-
});
|
|
405
|
-
const customFields = normalizeCustomFieldResponse(customFieldValues[deal.id]) ?? {};
|
|
406
|
-
const viewerUserId = auth.isApiKey ? null : auth.sub ?? null;
|
|
407
|
-
let viewerName = null;
|
|
408
|
-
let viewerEmail = auth.email ?? null;
|
|
409
|
-
if (viewerUserId) {
|
|
410
|
-
const viewerScope = {
|
|
411
|
-
tenantId: auth.tenantId ?? null,
|
|
412
|
-
organizationId: auth.orgId ?? null
|
|
398
|
+
return {
|
|
399
|
+
people: people2,
|
|
400
|
+
companies: companies2,
|
|
401
|
+
linkedPersonIds: people2.map((entry) => entry.id),
|
|
402
|
+
linkedCompanyIds: companies2.map((entry) => entry.id)
|
|
413
403
|
};
|
|
404
|
+
};
|
|
405
|
+
const viewerUserId = auth.isApiKey ? null : auth.sub ?? null;
|
|
406
|
+
const resolveViewer = async () => {
|
|
407
|
+
if (!viewerUserId) {
|
|
408
|
+
return { name: null, email: auth.email ?? null };
|
|
409
|
+
}
|
|
414
410
|
const viewer = await findOneWithDecryption(
|
|
415
411
|
em,
|
|
416
412
|
User,
|
|
417
413
|
{ id: viewerUserId, tenantId: auth.tenantId ?? null },
|
|
418
414
|
{},
|
|
419
|
-
|
|
415
|
+
{ tenantId: auth.tenantId ?? null, organizationId: auth.orgId ?? null }
|
|
420
416
|
);
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
417
|
+
return {
|
|
418
|
+
name: viewer?.name ?? null,
|
|
419
|
+
email: viewer?.email ?? auth.email ?? null
|
|
420
|
+
};
|
|
421
|
+
};
|
|
422
|
+
const resolveOwner = async () => deal.ownerUserId ? await findOneWithDecryption(
|
|
425
423
|
em,
|
|
426
424
|
User,
|
|
427
425
|
{ id: deal.ownerUserId, tenantId: deal.tenantId ?? auth.tenantId ?? null },
|
|
428
426
|
{},
|
|
429
427
|
decryptionScope
|
|
430
428
|
) : null;
|
|
431
|
-
const
|
|
432
|
-
|
|
433
|
-
name: owner.name ?? owner.email ?? owner.id,
|
|
434
|
-
email: owner.email ?? ""
|
|
435
|
-
} : null;
|
|
436
|
-
const effectiveStage = includeStages ? await resolveEffectivePipelineStage(em, deal, decryptionScope) : null;
|
|
437
|
-
const effectivePipelineId = deal.pipelineId ?? effectiveStage?.pipelineId ?? null;
|
|
438
|
-
const effectivePipelineStageId = deal.pipelineStageId ?? effectiveStage?.id ?? null;
|
|
439
|
-
const effectivePipelineStageLabel = deal.pipelineStage ?? effectiveStage?.label ?? null;
|
|
440
|
-
const pipelineStages = includeStages && effectivePipelineId ? await findWithDecryption(
|
|
441
|
-
em,
|
|
442
|
-
CustomerPipelineStage,
|
|
443
|
-
{
|
|
444
|
-
pipelineId: effectivePipelineId,
|
|
445
|
-
organizationId: deal.organizationId,
|
|
446
|
-
tenantId: deal.tenantId
|
|
447
|
-
},
|
|
448
|
-
{ orderBy: { order: "ASC" } },
|
|
449
|
-
decryptionScope
|
|
450
|
-
) : [];
|
|
451
|
-
const pipeline = effectivePipelineId ? await findOneWithDecryption(
|
|
452
|
-
em,
|
|
453
|
-
CustomerPipeline,
|
|
454
|
-
{
|
|
455
|
-
id: effectivePipelineId,
|
|
456
|
-
organizationId: deal.organizationId,
|
|
457
|
-
tenantId: deal.tenantId
|
|
458
|
-
},
|
|
459
|
-
{},
|
|
460
|
-
decryptionScope
|
|
461
|
-
) : null;
|
|
462
|
-
const pipelineStageAppearanceMap = pipelineStages.length ? await loadPipelineStageAppearanceMap(em, pipelineStages, deal.organizationId, deal.tenantId) : /* @__PURE__ */ new Map();
|
|
463
|
-
let stageTransitions = [];
|
|
464
|
-
if (includeStages) {
|
|
429
|
+
const resolveStageTransitions = async () => {
|
|
430
|
+
if (!includeStages) return [];
|
|
465
431
|
try {
|
|
466
|
-
|
|
432
|
+
return await findWithDecryption(
|
|
467
433
|
em,
|
|
468
434
|
CustomerDealStageTransition,
|
|
469
435
|
{ deal: deal.id, deletedAt: null },
|
|
@@ -475,16 +441,70 @@ async function GET(request, context) {
|
|
|
475
441
|
throw error;
|
|
476
442
|
}
|
|
477
443
|
warnMissingDealStageTransitionTable("customers.api.deals.detail.GET");
|
|
478
|
-
|
|
444
|
+
return [];
|
|
479
445
|
}
|
|
480
|
-
}
|
|
446
|
+
};
|
|
447
|
+
const [associations, customFieldValues, viewerInfo, owner, effectiveStage, stageTransitions] = await Promise.all([
|
|
448
|
+
resolveAssociations(),
|
|
449
|
+
loadCustomFieldValues({
|
|
450
|
+
em,
|
|
451
|
+
entityId: E.customers.customer_deal,
|
|
452
|
+
recordIds: [deal.id],
|
|
453
|
+
tenantIdByRecord: { [deal.id]: deal.tenantId ?? null },
|
|
454
|
+
organizationIdByRecord: { [deal.id]: deal.organizationId ?? null },
|
|
455
|
+
tenantFallbacks: [deal.tenantId ?? auth.tenantId ?? null].filter((value) => !!value)
|
|
456
|
+
}),
|
|
457
|
+
resolveViewer(),
|
|
458
|
+
resolveOwner(),
|
|
459
|
+
includeStages ? resolveEffectivePipelineStage(em, deal, decryptionScope) : Promise.resolve(null),
|
|
460
|
+
resolveStageTransitions()
|
|
461
|
+
]);
|
|
462
|
+
const { people, companies, linkedPersonIds, linkedCompanyIds } = associations;
|
|
463
|
+
const customFields = normalizeCustomFieldResponse(customFieldValues[deal.id]) ?? {};
|
|
464
|
+
const viewerName = viewerInfo.name;
|
|
465
|
+
const viewerEmail = viewerInfo.email;
|
|
466
|
+
const ownerPayload = owner ? {
|
|
467
|
+
id: owner.id,
|
|
468
|
+
name: owner.name ?? owner.email ?? owner.id,
|
|
469
|
+
email: owner.email ?? ""
|
|
470
|
+
} : null;
|
|
471
|
+
const effectivePipelineId = deal.pipelineId ?? effectiveStage?.pipelineId ?? null;
|
|
472
|
+
const effectivePipelineStageId = deal.pipelineStageId ?? effectiveStage?.id ?? null;
|
|
473
|
+
const effectivePipelineStageLabel = deal.pipelineStage ?? effectiveStage?.label ?? null;
|
|
474
|
+
const [pipelineStages, pipeline] = await Promise.all([
|
|
475
|
+
includeStages && effectivePipelineId ? findWithDecryption(
|
|
476
|
+
em,
|
|
477
|
+
CustomerPipelineStage,
|
|
478
|
+
{
|
|
479
|
+
pipelineId: effectivePipelineId,
|
|
480
|
+
organizationId: deal.organizationId,
|
|
481
|
+
tenantId: deal.tenantId
|
|
482
|
+
},
|
|
483
|
+
{ orderBy: { order: "ASC" } },
|
|
484
|
+
decryptionScope
|
|
485
|
+
) : [],
|
|
486
|
+
effectivePipelineId ? findOneWithDecryption(
|
|
487
|
+
em,
|
|
488
|
+
CustomerPipeline,
|
|
489
|
+
{
|
|
490
|
+
id: effectivePipelineId,
|
|
491
|
+
organizationId: deal.organizationId,
|
|
492
|
+
tenantId: deal.tenantId
|
|
493
|
+
},
|
|
494
|
+
{},
|
|
495
|
+
decryptionScope
|
|
496
|
+
) : null
|
|
497
|
+
]);
|
|
481
498
|
const persistedStageTransitions = stageTransitions.map((transition) => ({
|
|
482
499
|
stageId: transition.stageId,
|
|
483
500
|
stageLabel: transition.stageLabel,
|
|
484
501
|
stageOrder: transition.stageOrder,
|
|
485
502
|
transitionedAt: transition.transitionedAt.toISOString()
|
|
486
503
|
}));
|
|
487
|
-
const recoveredStageTransitions =
|
|
504
|
+
const [pipelineStageAppearanceMap, recoveredStageTransitions] = await Promise.all([
|
|
505
|
+
pipelineStages.length ? loadPipelineStageAppearanceMap(em, pipelineStages, deal.organizationId, deal.tenantId) : /* @__PURE__ */ new Map(),
|
|
506
|
+
includeStages && persistedStageTransitions.length === 0 ? loadAuditStageTransitionsFallback({ container, deal, pipelineStages }) : []
|
|
507
|
+
]);
|
|
488
508
|
const effectiveCurrentStage = (() => {
|
|
489
509
|
if (!effectivePipelineStageId) return null;
|
|
490
510
|
const matchingStage = pipelineStages.find((stage) => stage.id === effectivePipelineStageId);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../src/modules/customers/api/deals/%5Bid%5D/route.ts"],
|
|
4
|
-
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\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 type { EntityManager } from '@mikro-orm/postgresql'\nimport {\n CustomerDeal,\n CustomerDealPersonLink,\n CustomerDealCompanyLink,\n CustomerDealStageTransition,\n CustomerDictionaryEntry,\n CustomerEntity,\n CustomerPipeline,\n CustomerPipelineStage,\n} from '../../../data/entities'\nimport { User } from '@open-mercato/core/modules/auth/data/entities'\nimport type { ActionLogService } from '@open-mercato/core/modules/audit_logs/services/actionLogService'\nimport { loadCustomFieldValues } from '@open-mercato/shared/lib/crud/custom-fields'\nimport { normalizeCustomFieldResponse } from '@open-mercato/shared/lib/custom-fields/normalize'\nimport { E } from '#generated/entities.ids.generated'\nimport type { RbacService } from '@open-mercato/core/modules/auth/services/rbacService'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { findWithDecryption, findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { isOrganizationReadAccessAllowed } from '@open-mercato/core/modules/directory/utils/organizationScopeGuard'\nimport { decryptEntitiesWithFallbackScope } from '@open-mercato/shared/lib/encryption/subscriber'\nimport { isMissingDealStageTransitionTable, warnMissingDealStageTransitionTable } from '../../../lib/dealStageTransitionTable'\n\nexport const metadata = {\n GET: { requireAuth: true, requireFeatures: ['customers.deals.view'] },\n}\n\nconst paramsSchema = z.object({\n id: z.string().uuid(),\n})\n\nfunction notFound(message: string) {\n return NextResponse.json({ error: message }, { status: 404 })\n}\n\nfunction forbidden(message: string) {\n return NextResponse.json({ error: message }, { status: 403 })\n}\n\ntype DealAssociation = {\n id: string\n label: string\n subtitle: string | null\n kind: 'person' | 'company'\n}\n\nfunction normalizePersonAssociation(entity: CustomerEntity): { label: string; subtitle: string | null } {\n const displayName = typeof entity.displayName === 'string' ? entity.displayName.trim() : ''\n const email =\n typeof entity.primaryEmail === 'string' && entity.primaryEmail.trim().length\n ? entity.primaryEmail.trim()\n : null\n const phone =\n typeof entity.primaryPhone === 'string' && entity.primaryPhone.trim().length\n ? entity.primaryPhone.trim()\n : null\n const jobTitle =\n entity.personProfile &&\n typeof (entity.personProfile as { jobTitle?: string | null })?.jobTitle === 'string' &&\n (entity.personProfile as { jobTitle?: string | null }).jobTitle?.trim().length\n ? ((entity.personProfile as { jobTitle?: string | null }).jobTitle as string).trim()\n : null\n const subtitle = jobTitle ?? email ?? phone ?? null\n const label = displayName.length ? displayName : email ?? phone ?? entity.id\n return { label, subtitle }\n}\n\nfunction normalizeCompanyAssociation(entity: CustomerEntity): { label: string; subtitle: string | null } {\n const displayName = typeof entity.displayName === 'string' ? entity.displayName.trim() : ''\n const domain =\n entity.companyProfile &&\n typeof (entity.companyProfile as { domain?: string | null })?.domain === 'string' &&\n (entity.companyProfile as { domain?: string | null }).domain?.trim().length\n ? ((entity.companyProfile as { domain?: string | null }).domain as string).trim()\n : null\n const website =\n entity.companyProfile &&\n typeof (entity.companyProfile as { websiteUrl?: string | null })?.websiteUrl === 'string' &&\n (entity.companyProfile as { websiteUrl?: string | null }).websiteUrl?.trim().length\n ? ((entity.companyProfile as { websiteUrl?: string | null }).websiteUrl as string).trim()\n : null\n const subtitle = domain ?? website ?? null\n const label = displayName.length ? displayName : domain ?? website ?? entity.id\n return { label, subtitle }\n}\n\nfunction readIncludeFlags(request: Request): Set<string> {\n const flags = new Set<string>()\n const url = new URL(request.url)\n for (const rawValue of url.searchParams.getAll('include')) {\n rawValue\n .split(',')\n .map((value) => value.trim().toLowerCase())\n .filter(Boolean)\n .forEach((value) => flags.add(value))\n }\n return flags\n}\n\nfunction readViewMode(request: Request): 'full' | 'lite' {\n const raw = new URL(request.url).searchParams.get('view')\n return raw === 'lite' || raw === 'detail-lite' ? 'lite' : 'full'\n}\n\nfunction normalizeStageLabel(value: string | null | undefined): string {\n return typeof value === 'string' ? value.trim().toLowerCase() : ''\n}\n\ntype StageTransitionPayload = {\n stageId: string\n stageLabel: string\n stageOrder: number\n transitionedAt: string\n}\n\ntype DealSnapshotStageInfo = {\n pipelineId: string | null\n stageId: string | null\n stageLabel: string | null\n}\n\nfunction asObject(value: unknown): Record<string, unknown> | null {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n ? value as Record<string, unknown>\n : null\n}\n\nfunction readRecordString(record: Record<string, unknown> | null, ...keys: string[]): string | null {\n if (!record) return null\n for (const key of keys) {\n const value = record[key]\n if (typeof value === 'string' && value.trim().length > 0) {\n return value.trim()\n }\n }\n return null\n}\n\nfunction readSnapshotDealRecord(snapshot: unknown): Record<string, unknown> | null {\n const root = asObject(snapshot)\n if (!root) return null\n return asObject(root.deal) ?? root\n}\n\nfunction readSnapshotStageInfo(snapshot: unknown): DealSnapshotStageInfo {\n const dealRecord = readSnapshotDealRecord(snapshot)\n return {\n pipelineId: readRecordString(dealRecord, 'pipelineId', 'pipeline_id'),\n stageId: readRecordString(dealRecord, 'pipelineStageId', 'pipeline_stage_id'),\n stageLabel: readRecordString(dealRecord, 'pipelineStage', 'pipeline_stage'),\n }\n}\n\nasync function loadAuditStageTransitionsFallback({\n container,\n deal,\n pipelineStages,\n}: {\n container: Awaited<ReturnType<typeof createRequestContainer>>\n deal: CustomerDeal\n pipelineStages: CustomerPipelineStage[]\n}): Promise<StageTransitionPayload[]> {\n if (!deal.tenantId || !deal.organizationId || !pipelineStages.length) return []\n\n let actionLogs: ActionLogService | null = null\n try {\n actionLogs = container.resolve('actionLogService') as ActionLogService\n } catch {\n return []\n }\n if (!actionLogs || typeof actionLogs.list !== 'function') return []\n const stageOrderById = new Map(pipelineStages.map((stage) => [stage.id, stage.order]))\n const stageLabelById = new Map(pipelineStages.map((stage) => [stage.id, stage.label]))\n const transitionsByStageId = new Map<string, StageTransitionPayload>()\n const logsResult = await actionLogs.list({\n tenantId: deal.tenantId,\n organizationId: deal.organizationId,\n resourceKind: 'customers.deal',\n resourceId: deal.id,\n limit: 200,\n offset: 0,\n sortField: 'createdAt',\n sortDir: 'asc',\n }).catch(() => null)\n const logs = logsResult?.items ?? []\n\n let previousStageId: string | null = null\n for (const log of logs) {\n if (log.executionState === 'failed' || log.executionState === 'undone') continue\n\n const before = readSnapshotStageInfo(log.snapshotBefore)\n const after = readSnapshotStageInfo(log.snapshotAfter)\n const nextStageId = after.stageId\n if (!nextStageId) continue\n\n const stageOrder = stageOrderById.get(nextStageId)\n if (typeof stageOrder !== 'number') {\n previousStageId = nextStageId\n continue\n }\n\n const effectivePreviousStageId: string | null = before.stageId ?? previousStageId\n if (effectivePreviousStageId === nextStageId && transitionsByStageId.has(nextStageId)) {\n previousStageId = nextStageId\n continue\n }\n\n transitionsByStageId.set(nextStageId, {\n stageId: nextStageId,\n stageLabel: after.stageLabel ?? stageLabelById.get(nextStageId) ?? nextStageId,\n stageOrder,\n transitionedAt: log.createdAt.toISOString(),\n })\n previousStageId = nextStageId\n }\n\n return Array.from(transitionsByStageId.values()).sort((left, right) => left.stageOrder - right.stageOrder)\n}\n\nfunction mergeStageTransitions({\n persisted,\n recovered,\n currentStage,\n fallbackTimestamp,\n}: {\n persisted: StageTransitionPayload[]\n recovered: StageTransitionPayload[]\n currentStage: { id: string; label: string; order: number } | null\n fallbackTimestamp: string\n}): StageTransitionPayload[] {\n const merged = new Map<string, StageTransitionPayload>()\n for (const transition of persisted) {\n merged.set(transition.stageId, transition)\n }\n for (const transition of recovered) {\n if (!merged.has(transition.stageId)) {\n merged.set(transition.stageId, transition)\n }\n }\n if (currentStage && !merged.has(currentStage.id)) {\n merged.set(currentStage.id, {\n stageId: currentStage.id,\n stageLabel: currentStage.label,\n stageOrder: currentStage.order,\n transitionedAt: fallbackTimestamp,\n })\n }\n return Array.from(merged.values()).sort((left, right) => left.stageOrder - right.stageOrder)\n}\n\nasync function loadPipelineStageAppearanceMap(\n em: EntityManager,\n stages: CustomerPipelineStage[],\n organizationId: string,\n tenantId: string,\n): Promise<Map<string, CustomerDictionaryEntry>> {\n const normalizedValues = stages\n .map((stage) => stage.label.trim().toLowerCase())\n .filter((value) => value.length > 0)\n if (!normalizedValues.length) return new Map<string, CustomerDictionaryEntry>()\n const entries = await findWithDecryption(\n em,\n CustomerDictionaryEntry,\n {\n organizationId,\n tenantId,\n kind: 'pipeline_stage',\n normalizedValue: { $in: normalizedValues },\n },\n undefined,\n { tenantId, organizationId },\n )\n const map = new Map<string, CustomerDictionaryEntry>()\n entries.forEach((entry) => map.set(entry.normalizedValue, entry))\n return map\n}\n\nasync function resolveEffectivePipelineStage(\n em: EntityManager,\n deal: CustomerDeal,\n decryptionScope: { tenantId: string | null; organizationId: string | null },\n): Promise<CustomerPipelineStage | null> {\n if (deal.pipelineStageId) {\n const exactStage = await findOneWithDecryption(\n em,\n CustomerPipelineStage,\n {\n id: deal.pipelineStageId,\n organizationId: deal.organizationId,\n tenantId: deal.tenantId,\n },\n {},\n decryptionScope,\n )\n if (exactStage) return exactStage\n }\n\n const normalizedStageLabel = normalizeStageLabel(deal.pipelineStage)\n if (!normalizedStageLabel) return null\n\n const scopedStages = await findWithDecryption(\n em,\n CustomerPipelineStage,\n {\n organizationId: deal.organizationId,\n tenantId: deal.tenantId,\n ...(deal.pipelineId ? { pipelineId: deal.pipelineId } : {}),\n },\n { orderBy: { order: 'ASC' } },\n decryptionScope,\n )\n\n const matchingStages = scopedStages.filter((stage) => normalizeStageLabel(stage.label) === normalizedStageLabel)\n if (matchingStages.length === 1) return matchingStages[0] ?? null\n if (matchingStages.length > 1) {\n const distinctPipelineIds = new Set(matchingStages.map((stage) => stage.pipelineId))\n if (distinctPipelineIds.size === 1) return matchingStages[0] ?? null\n }\n return null\n}\n\nexport async function GET(request: Request, context: { params?: Record<string, unknown> }) {\n const parsedParams = paramsSchema.safeParse(context.params)\n if (!parsedParams.success) {\n return notFound('Deal not found')\n }\n\n const includeFlags = readIncludeFlags(request)\n const viewMode = readViewMode(request)\n const liteView = viewMode === 'lite'\n const includeStages = includeFlags.has('stages')\n const container = await createRequestContainer()\n const auth = await getAuthFromRequest(request)\n if (!auth?.sub && !auth?.isApiKey) {\n return NextResponse.json({ error: 'Authentication required' }, { status: 401 })\n }\n\n let rbac: RbacService | null = null\n try {\n rbac = (container.resolve('rbacService') as RbacService)\n } catch {\n rbac = null\n }\n\n if (!rbac || !auth?.sub) {\n return forbidden('Access denied')\n }\n const hasFeature = await rbac.userHasAllFeatures(auth.sub, ['customers.deals.view'], {\n tenantId: auth.tenantId ?? null,\n organizationId: auth.orgId ?? null,\n })\n if (!hasFeature) {\n return forbidden('Access denied')\n }\n\n const scope = await resolveOrganizationScopeForRequest({ container, auth, request })\n const em = (container.resolve('em') as EntityManager)\n\n const deal = await findOneWithDecryption(\n em,\n CustomerDeal,\n { id: parsedParams.data.id, deletedAt: null },\n {\n populate: ['people.person', 'people.person.personProfile', 'companies.company', 'companies.company.companyProfile'],\n },\n { tenantId: auth.tenantId ?? null, organizationId: auth.orgId ?? null },\n )\n if (!deal) {\n return notFound('Deal not found')\n }\n\n if (auth.tenantId && deal.tenantId && auth.tenantId !== deal.tenantId) {\n return notFound('Deal not found')\n }\n\n if (!isOrganizationReadAccessAllowed({ scope, auth, organizationId: deal.organizationId })) {\n return forbidden('Access denied')\n }\n\n const decryptionScope = {\n tenantId: deal.tenantId ?? auth.tenantId ?? null,\n organizationId: deal.organizationId ?? auth.orgId ?? null,\n }\n let linkedPersonIds: string[] = []\n let linkedCompanyIds: string[] = []\n let people: DealAssociation[] = []\n let companies: DealAssociation[] = []\n\n if (liteView) {\n const personLinkRows = await findWithDecryption(\n em,\n CustomerDealPersonLink,\n { deal: deal.id },\n { orderBy: { createdAt: 'ASC' } },\n decryptionScope,\n )\n const companyLinkRows = await findWithDecryption(\n em,\n CustomerDealCompanyLink,\n { deal: deal.id },\n { orderBy: { createdAt: 'ASC' } },\n decryptionScope,\n )\n\n linkedPersonIds = Array.from(\n new Set(\n personLinkRows\n .map((link) => {\n const personRef = link.person\n if (!personRef) return null\n if (typeof personRef === 'string') return personRef\n const personIdValue = personRef.id\n return typeof personIdValue === 'string' ? personIdValue : null\n })\n .filter((value): value is string => typeof value === 'string' && value.trim().length > 0),\n ),\n )\n linkedCompanyIds = Array.from(\n new Set(\n companyLinkRows\n .map((link) => {\n const companyRef = link.company\n if (!companyRef) return null\n if (typeof companyRef === 'string') return companyRef\n const companyIdValue = companyRef.id\n return typeof companyIdValue === 'string' ? companyIdValue : null\n })\n .filter((value): value is string => typeof value === 'string' && value.trim().length > 0),\n ),\n )\n\n const previewPeople = linkedPersonIds.length\n ? await findWithDecryption(\n em,\n CustomerEntity,\n { id: { $in: linkedPersonIds.slice(0, 3) } },\n { populate: ['personProfile'] },\n decryptionScope,\n )\n : []\n const previewCompanies = linkedCompanyIds.length\n ? await findWithDecryption(\n em,\n CustomerEntity,\n { id: { $in: linkedCompanyIds.slice(0, 3) } },\n { populate: ['companyProfile'] },\n decryptionScope,\n )\n : []\n const previewPeopleMap = new Map(previewPeople.map((entity) => [entity.id, entity]))\n const previewCompaniesMap = new Map(previewCompanies.map((entity) => [entity.id, entity]))\n people = linkedPersonIds.slice(0, 3).reduce<DealAssociation[]>((acc, personId) => {\n const entity = previewPeopleMap.get(personId) ?? null\n if (!entity || entity.deletedAt) return acc\n const { label, subtitle } = normalizePersonAssociation(entity)\n acc.push({ id: entity.id, label, subtitle, kind: 'person' })\n return acc\n }, [])\n companies = linkedCompanyIds.slice(0, 3).reduce<DealAssociation[]>((acc, companyId) => {\n const entity = previewCompaniesMap.get(companyId) ?? null\n if (!entity || entity.deletedAt) return acc\n const { label, subtitle } = normalizeCompanyAssociation(entity)\n acc.push({ id: entity.id, label, subtitle, kind: 'company' })\n return acc\n }, [])\n } else {\n const personLinks = await findWithDecryption(\n em,\n CustomerDealPersonLink,\n { deal: deal.id },\n { populate: ['person', 'person.personProfile'] },\n decryptionScope,\n )\n const companyLinks = await findWithDecryption(\n em,\n CustomerDealCompanyLink,\n { deal: deal.id },\n { populate: ['company', 'company.companyProfile'] },\n decryptionScope,\n )\n const fallbackTenantId = deal.tenantId ?? auth.tenantId ?? null\n const fallbackOrgId = deal.organizationId ?? auth.orgId ?? null\n await decryptEntitiesWithFallbackScope(personLinks, {\n em,\n tenantId: fallbackTenantId,\n organizationId: fallbackOrgId,\n })\n await decryptEntitiesWithFallbackScope(companyLinks, {\n em,\n tenantId: fallbackTenantId,\n organizationId: fallbackOrgId,\n })\n\n people = personLinks.reduce<DealAssociation[]>((acc, link) => {\n const entity = link.person as CustomerEntity | null\n if (!entity || entity.deletedAt) return acc\n const { label, subtitle } = normalizePersonAssociation(entity)\n acc.push({ id: entity.id, label, subtitle, kind: 'person' })\n return acc\n }, [])\n\n companies = companyLinks.reduce<DealAssociation[]>((acc, link) => {\n const entity = link.company as CustomerEntity | null\n if (!entity || entity.deletedAt) return acc\n const { label, subtitle } = normalizeCompanyAssociation(entity)\n acc.push({ id: entity.id, label, subtitle, kind: 'company' })\n return acc\n }, [])\n linkedPersonIds = people.map((entry) => entry.id)\n linkedCompanyIds = companies.map((entry) => entry.id)\n }\n\n const customFieldValues = await loadCustomFieldValues({\n em,\n entityId: E.customers.customer_deal,\n recordIds: [deal.id],\n tenantIdByRecord: { [deal.id]: deal.tenantId ?? null },\n organizationIdByRecord: { [deal.id]: deal.organizationId ?? null },\n tenantFallbacks: [deal.tenantId ?? auth.tenantId ?? null].filter((value): value is string => !!value),\n })\n const customFields = normalizeCustomFieldResponse(customFieldValues[deal.id]) ?? {}\n\n const viewerUserId = auth.isApiKey ? null : auth.sub ?? null\n let viewerName: string | null = null\n let viewerEmail: string | null = auth.email ?? null\n if (viewerUserId) {\n const viewerScope = {\n tenantId: auth.tenantId ?? null,\n organizationId: auth.orgId ?? null,\n }\n const viewer = await findOneWithDecryption(\n em,\n User,\n { id: viewerUserId, tenantId: auth.tenantId ?? null },\n {},\n viewerScope,\n )\n viewerName = viewer?.name ?? null\n viewerEmail = viewer?.email ?? viewerEmail ?? null\n }\n\n const owner = deal.ownerUserId\n ? await findOneWithDecryption(\n em,\n User,\n { id: deal.ownerUserId, tenantId: deal.tenantId ?? auth.tenantId ?? null },\n {},\n decryptionScope,\n )\n : null\n const ownerPayload = owner\n ? {\n id: owner.id,\n name: owner.name ?? owner.email ?? owner.id,\n email: owner.email ?? '',\n }\n : null\n\n const effectiveStage = includeStages\n ? await resolveEffectivePipelineStage(em, deal, decryptionScope)\n : null\n const effectivePipelineId = deal.pipelineId ?? effectiveStage?.pipelineId ?? null\n const effectivePipelineStageId = deal.pipelineStageId ?? effectiveStage?.id ?? null\n const effectivePipelineStageLabel = deal.pipelineStage ?? effectiveStage?.label ?? null\n\n const pipelineStages = includeStages && effectivePipelineId\n ? await findWithDecryption(\n em,\n CustomerPipelineStage,\n {\n pipelineId: effectivePipelineId,\n organizationId: deal.organizationId,\n tenantId: deal.tenantId,\n },\n { orderBy: { order: 'ASC' } },\n decryptionScope,\n )\n : []\n const pipeline = effectivePipelineId\n ? await findOneWithDecryption(\n em,\n CustomerPipeline,\n {\n id: effectivePipelineId,\n organizationId: deal.organizationId,\n tenantId: deal.tenantId,\n },\n {},\n decryptionScope,\n )\n : null\n const pipelineStageAppearanceMap = pipelineStages.length\n ? await loadPipelineStageAppearanceMap(em, pipelineStages, deal.organizationId, deal.tenantId)\n : new Map<string, CustomerDictionaryEntry>()\n let stageTransitions: CustomerDealStageTransition[] = []\n if (includeStages) {\n try {\n stageTransitions = await findWithDecryption(\n em,\n CustomerDealStageTransition,\n { deal: deal.id, deletedAt: null },\n { orderBy: { stageOrder: 'ASC', transitionedAt: 'ASC' } },\n decryptionScope,\n )\n } catch (error) {\n if (!isMissingDealStageTransitionTable(error)) {\n throw error\n }\n warnMissingDealStageTransitionTable('customers.api.deals.detail.GET')\n stageTransitions = []\n }\n }\n const persistedStageTransitions = stageTransitions.map((transition) => ({\n stageId: transition.stageId,\n stageLabel: transition.stageLabel,\n stageOrder: transition.stageOrder,\n transitionedAt: transition.transitionedAt.toISOString(),\n }))\n const recoveredStageTransitions = includeStages && persistedStageTransitions.length === 0\n ? await loadAuditStageTransitionsFallback({ container, deal, pipelineStages })\n : []\n const effectiveCurrentStage = (() => {\n if (!effectivePipelineStageId) return null\n const matchingStage = pipelineStages.find((stage) => stage.id === effectivePipelineStageId)\n if (matchingStage) {\n return {\n id: matchingStage.id,\n label: matchingStage.label,\n order: matchingStage.order,\n }\n }\n if (!effectivePipelineStageLabel) return null\n return {\n id: effectivePipelineStageId,\n label: effectivePipelineStageLabel,\n order: 0,\n }\n })()\n const stageTransitionPayload = mergeStageTransitions({\n persisted: persistedStageTransitions,\n recovered: recoveredStageTransitions,\n currentStage: effectiveCurrentStage,\n fallbackTimestamp: deal.createdAt.toISOString(),\n })\n\n return NextResponse.json({\n deal: {\n id: deal.id,\n title: deal.title,\n description: deal.description ?? null,\n status: deal.status ?? null,\n pipelineStage: effectivePipelineStageLabel,\n pipelineId: effectivePipelineId,\n pipelineStageId: effectivePipelineStageId,\n valueAmount: deal.valueAmount ?? null,\n valueCurrency: deal.valueCurrency ?? null,\n probability: deal.probability ?? null,\n expectedCloseAt: deal.expectedCloseAt ? deal.expectedCloseAt.toISOString() : null,\n ownerUserId: deal.ownerUserId ?? null,\n source: deal.source ?? null,\n closureOutcome: deal.closureOutcome ?? null,\n lossReasonId: deal.lossReasonId ?? null,\n lossNotes: deal.lossNotes ?? null,\n organizationId: deal.organizationId ?? null,\n tenantId: deal.tenantId ?? null,\n createdAt: deal.createdAt.toISOString(),\n updatedAt: deal.updatedAt.toISOString(),\n },\n people,\n companies,\n linkedPersonIds,\n linkedCompanyIds,\n counts: {\n people: linkedPersonIds.length,\n companies: linkedCompanyIds.length,\n },\n customFields,\n viewer: {\n userId: viewerUserId,\n name: viewerName,\n email: viewerEmail,\n },\n pipelineStages: pipelineStages.map((stage) => {\n const appearance = pipelineStageAppearanceMap.get(stage.label.trim().toLowerCase())\n return {\n id: stage.id,\n label: stage.label,\n order: stage.order,\n color: appearance?.color ?? null,\n icon: appearance?.icon ?? null,\n }\n }),\n pipelineName: pipeline?.name ?? null,\n stageTransitions: stageTransitionPayload,\n owner: ownerPayload,\n })\n}\n\nconst dealDetailQuerySchema = z.object({\n include: z.string().optional(),\n})\n\nconst pipelineStageInfoSchema = z.object({\n id: z.string().uuid(),\n label: z.string(),\n order: z.number().int(),\n color: z.string().nullable(),\n icon: z.string().nullable(),\n})\n\nconst stageTransitionInfoSchema = z.object({\n stageId: z.string().uuid(),\n stageLabel: z.string(),\n stageOrder: z.number().int(),\n transitionedAt: z.string(),\n})\n\nconst dealDetailResponseSchema = z.object({\n deal: z.object({\n id: z.string().uuid(),\n title: z.string().nullable().optional(),\n description: z.string().nullable().optional(),\n status: z.string().nullable().optional(),\n pipelineStage: z.string().nullable().optional(),\n pipelineId: z.string().uuid().nullable().optional(),\n pipelineStageId: z.string().uuid().nullable().optional(),\n valueAmount: z.string().nullable().optional(),\n valueCurrency: z.string().nullable().optional(),\n probability: z.number().nullable().optional(),\n expectedCloseAt: z.string().nullable().optional(),\n ownerUserId: z.string().uuid().nullable().optional(),\n source: z.string().nullable().optional(),\n closureOutcome: z.enum(['won', 'lost']).nullable().optional(),\n lossReasonId: z.string().uuid().nullable().optional(),\n lossNotes: z.string().nullable().optional(),\n organizationId: z.string().uuid().nullable().optional(),\n tenantId: z.string().uuid().nullable().optional(),\n createdAt: z.string(),\n updatedAt: z.string(),\n }),\n people: z.array(\n z.object({\n id: z.string().uuid(),\n label: z.string(),\n subtitle: z.string().nullable().optional(),\n kind: z.literal('person'),\n }),\n ),\n companies: z.array(\n z.object({\n id: z.string().uuid(),\n label: z.string(),\n subtitle: z.string().nullable().optional(),\n kind: z.literal('company'),\n }),\n ),\n customFields: z.record(z.string(), z.unknown()),\n viewer: z.object({\n userId: z.string().uuid().nullable(),\n name: z.string().nullable(),\n email: z.string().nullable(),\n }),\n pipelineStages: z.array(pipelineStageInfoSchema),\n stageTransitions: z.array(stageTransitionInfoSchema),\n owner: z.object({\n id: z.string().uuid(),\n name: z.string(),\n email: z.string(),\n }).nullable(),\n})\n\nconst dealDetailErrorSchema = z.object({\n error: z.string(),\n})\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Customers',\n summary: 'Fetch deal detail',\n methods: {\n GET: {\n summary: 'Fetch deal with associations and pipeline context',\n description: 'Returns a deal with linked people, companies, closure fields, optional pipeline history, custom fields, and viewer context.',\n query: dealDetailQuerySchema,\n responses: [\n { status: 200, description: 'Deal detail payload', schema: dealDetailResponseSchema },\n ],\n errors: [\n { status: 401, description: 'Unauthorized', schema: dealDetailErrorSchema },\n { status: 403, description: 'Forbidden for tenant/organization scope', schema: dealDetailErrorSchema },\n { status: 404, description: 'Deal not found', schema: dealDetailErrorSchema },\n ],\n },\n },\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
6
|
-
"names": []
|
|
4
|
+
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\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 type { EntityManager } from '@mikro-orm/postgresql'\nimport {\n CustomerDeal,\n CustomerDealPersonLink,\n CustomerDealCompanyLink,\n CustomerDealStageTransition,\n CustomerDictionaryEntry,\n CustomerEntity,\n CustomerPipeline,\n CustomerPipelineStage,\n} from '../../../data/entities'\nimport { User } from '@open-mercato/core/modules/auth/data/entities'\nimport type { ActionLogService } from '@open-mercato/core/modules/audit_logs/services/actionLogService'\nimport { loadCustomFieldValues } from '@open-mercato/shared/lib/crud/custom-fields'\nimport { normalizeCustomFieldResponse } from '@open-mercato/shared/lib/custom-fields/normalize'\nimport { E } from '#generated/entities.ids.generated'\nimport type { RbacService } from '@open-mercato/core/modules/auth/services/rbacService'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { findWithDecryption, findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { isOrganizationReadAccessAllowed } from '@open-mercato/core/modules/directory/utils/organizationScopeGuard'\nimport { decryptEntitiesWithFallbackScope } from '@open-mercato/shared/lib/encryption/subscriber'\nimport { isMissingDealStageTransitionTable, warnMissingDealStageTransitionTable } from '../../../lib/dealStageTransitionTable'\n\nexport const metadata = {\n GET: { requireAuth: true, requireFeatures: ['customers.deals.view'] },\n}\n\nconst paramsSchema = z.object({\n id: z.string().uuid(),\n})\n\nfunction notFound(message: string) {\n return NextResponse.json({ error: message }, { status: 404 })\n}\n\nfunction forbidden(message: string) {\n return NextResponse.json({ error: message }, { status: 403 })\n}\n\ntype DealAssociation = {\n id: string\n label: string\n subtitle: string | null\n kind: 'person' | 'company'\n}\n\nfunction normalizePersonAssociation(entity: CustomerEntity): { label: string; subtitle: string | null } {\n const displayName = typeof entity.displayName === 'string' ? entity.displayName.trim() : ''\n const email =\n typeof entity.primaryEmail === 'string' && entity.primaryEmail.trim().length\n ? entity.primaryEmail.trim()\n : null\n const phone =\n typeof entity.primaryPhone === 'string' && entity.primaryPhone.trim().length\n ? entity.primaryPhone.trim()\n : null\n const jobTitle =\n entity.personProfile &&\n typeof (entity.personProfile as { jobTitle?: string | null })?.jobTitle === 'string' &&\n (entity.personProfile as { jobTitle?: string | null }).jobTitle?.trim().length\n ? ((entity.personProfile as { jobTitle?: string | null }).jobTitle as string).trim()\n : null\n const subtitle = jobTitle ?? email ?? phone ?? null\n const label = displayName.length ? displayName : email ?? phone ?? entity.id\n return { label, subtitle }\n}\n\nfunction normalizeCompanyAssociation(entity: CustomerEntity): { label: string; subtitle: string | null } {\n const displayName = typeof entity.displayName === 'string' ? entity.displayName.trim() : ''\n const domain =\n entity.companyProfile &&\n typeof (entity.companyProfile as { domain?: string | null })?.domain === 'string' &&\n (entity.companyProfile as { domain?: string | null }).domain?.trim().length\n ? ((entity.companyProfile as { domain?: string | null }).domain as string).trim()\n : null\n const website =\n entity.companyProfile &&\n typeof (entity.companyProfile as { websiteUrl?: string | null })?.websiteUrl === 'string' &&\n (entity.companyProfile as { websiteUrl?: string | null }).websiteUrl?.trim().length\n ? ((entity.companyProfile as { websiteUrl?: string | null }).websiteUrl as string).trim()\n : null\n const subtitle = domain ?? website ?? null\n const label = displayName.length ? displayName : domain ?? website ?? entity.id\n return { label, subtitle }\n}\n\nfunction readIncludeFlags(request: Request): Set<string> {\n const flags = new Set<string>()\n const url = new URL(request.url)\n for (const rawValue of url.searchParams.getAll('include')) {\n rawValue\n .split(',')\n .map((value) => value.trim().toLowerCase())\n .filter(Boolean)\n .forEach((value) => flags.add(value))\n }\n return flags\n}\n\nfunction readViewMode(request: Request): 'full' | 'lite' {\n const raw = new URL(request.url).searchParams.get('view')\n return raw === 'lite' || raw === 'detail-lite' ? 'lite' : 'full'\n}\n\nfunction normalizeStageLabel(value: string | null | undefined): string {\n return typeof value === 'string' ? value.trim().toLowerCase() : ''\n}\n\ntype StageTransitionPayload = {\n stageId: string\n stageLabel: string\n stageOrder: number\n transitionedAt: string\n}\n\ntype DealSnapshotStageInfo = {\n pipelineId: string | null\n stageId: string | null\n stageLabel: string | null\n}\n\nfunction asObject(value: unknown): Record<string, unknown> | null {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n ? value as Record<string, unknown>\n : null\n}\n\nfunction readRecordString(record: Record<string, unknown> | null, ...keys: string[]): string | null {\n if (!record) return null\n for (const key of keys) {\n const value = record[key]\n if (typeof value === 'string' && value.trim().length > 0) {\n return value.trim()\n }\n }\n return null\n}\n\nfunction readSnapshotDealRecord(snapshot: unknown): Record<string, unknown> | null {\n const root = asObject(snapshot)\n if (!root) return null\n return asObject(root.deal) ?? root\n}\n\nfunction readSnapshotStageInfo(snapshot: unknown): DealSnapshotStageInfo {\n const dealRecord = readSnapshotDealRecord(snapshot)\n return {\n pipelineId: readRecordString(dealRecord, 'pipelineId', 'pipeline_id'),\n stageId: readRecordString(dealRecord, 'pipelineStageId', 'pipeline_stage_id'),\n stageLabel: readRecordString(dealRecord, 'pipelineStage', 'pipeline_stage'),\n }\n}\n\nasync function loadAuditStageTransitionsFallback({\n container,\n deal,\n pipelineStages,\n}: {\n container: Awaited<ReturnType<typeof createRequestContainer>>\n deal: CustomerDeal\n pipelineStages: CustomerPipelineStage[]\n}): Promise<StageTransitionPayload[]> {\n if (!deal.tenantId || !deal.organizationId || !pipelineStages.length) return []\n\n let actionLogs: ActionLogService | null = null\n try {\n actionLogs = container.resolve('actionLogService') as ActionLogService\n } catch {\n return []\n }\n if (!actionLogs || typeof actionLogs.list !== 'function') return []\n const stageOrderById = new Map(pipelineStages.map((stage) => [stage.id, stage.order]))\n const stageLabelById = new Map(pipelineStages.map((stage) => [stage.id, stage.label]))\n const transitionsByStageId = new Map<string, StageTransitionPayload>()\n const logsResult = await actionLogs.list({\n tenantId: deal.tenantId,\n organizationId: deal.organizationId,\n resourceKind: 'customers.deal',\n resourceId: deal.id,\n limit: 200,\n offset: 0,\n sortField: 'createdAt',\n sortDir: 'asc',\n }).catch(() => null)\n const logs = logsResult?.items ?? []\n\n let previousStageId: string | null = null\n for (const log of logs) {\n if (log.executionState === 'failed' || log.executionState === 'undone') continue\n\n const before = readSnapshotStageInfo(log.snapshotBefore)\n const after = readSnapshotStageInfo(log.snapshotAfter)\n const nextStageId = after.stageId\n if (!nextStageId) continue\n\n const stageOrder = stageOrderById.get(nextStageId)\n if (typeof stageOrder !== 'number') {\n previousStageId = nextStageId\n continue\n }\n\n const effectivePreviousStageId: string | null = before.stageId ?? previousStageId\n if (effectivePreviousStageId === nextStageId && transitionsByStageId.has(nextStageId)) {\n previousStageId = nextStageId\n continue\n }\n\n transitionsByStageId.set(nextStageId, {\n stageId: nextStageId,\n stageLabel: after.stageLabel ?? stageLabelById.get(nextStageId) ?? nextStageId,\n stageOrder,\n transitionedAt: log.createdAt.toISOString(),\n })\n previousStageId = nextStageId\n }\n\n return Array.from(transitionsByStageId.values()).sort((left, right) => left.stageOrder - right.stageOrder)\n}\n\nfunction mergeStageTransitions({\n persisted,\n recovered,\n currentStage,\n fallbackTimestamp,\n}: {\n persisted: StageTransitionPayload[]\n recovered: StageTransitionPayload[]\n currentStage: { id: string; label: string; order: number } | null\n fallbackTimestamp: string\n}): StageTransitionPayload[] {\n const merged = new Map<string, StageTransitionPayload>()\n for (const transition of persisted) {\n merged.set(transition.stageId, transition)\n }\n for (const transition of recovered) {\n if (!merged.has(transition.stageId)) {\n merged.set(transition.stageId, transition)\n }\n }\n if (currentStage && !merged.has(currentStage.id)) {\n merged.set(currentStage.id, {\n stageId: currentStage.id,\n stageLabel: currentStage.label,\n stageOrder: currentStage.order,\n transitionedAt: fallbackTimestamp,\n })\n }\n return Array.from(merged.values()).sort((left, right) => left.stageOrder - right.stageOrder)\n}\n\nasync function loadPipelineStageAppearanceMap(\n em: EntityManager,\n stages: CustomerPipelineStage[],\n organizationId: string,\n tenantId: string,\n): Promise<Map<string, CustomerDictionaryEntry>> {\n const normalizedValues = stages\n .map((stage) => stage.label.trim().toLowerCase())\n .filter((value) => value.length > 0)\n if (!normalizedValues.length) return new Map<string, CustomerDictionaryEntry>()\n const entries = await findWithDecryption(\n em,\n CustomerDictionaryEntry,\n {\n organizationId,\n tenantId,\n kind: 'pipeline_stage',\n normalizedValue: { $in: normalizedValues },\n },\n undefined,\n { tenantId, organizationId },\n )\n const map = new Map<string, CustomerDictionaryEntry>()\n entries.forEach((entry) => map.set(entry.normalizedValue, entry))\n return map\n}\n\nasync function resolveEffectivePipelineStage(\n em: EntityManager,\n deal: CustomerDeal,\n decryptionScope: { tenantId: string | null; organizationId: string | null },\n): Promise<CustomerPipelineStage | null> {\n if (deal.pipelineStageId) {\n const exactStage = await findOneWithDecryption(\n em,\n CustomerPipelineStage,\n {\n id: deal.pipelineStageId,\n organizationId: deal.organizationId,\n tenantId: deal.tenantId,\n },\n {},\n decryptionScope,\n )\n if (exactStage) return exactStage\n }\n\n const normalizedStageLabel = normalizeStageLabel(deal.pipelineStage)\n if (!normalizedStageLabel) return null\n\n const scopedStages = await findWithDecryption(\n em,\n CustomerPipelineStage,\n {\n organizationId: deal.organizationId,\n tenantId: deal.tenantId,\n ...(deal.pipelineId ? { pipelineId: deal.pipelineId } : {}),\n },\n { orderBy: { order: 'ASC' } },\n decryptionScope,\n )\n\n const matchingStages = scopedStages.filter((stage) => normalizeStageLabel(stage.label) === normalizedStageLabel)\n if (matchingStages.length === 1) return matchingStages[0] ?? null\n if (matchingStages.length > 1) {\n const distinctPipelineIds = new Set(matchingStages.map((stage) => stage.pipelineId))\n if (distinctPipelineIds.size === 1) return matchingStages[0] ?? null\n }\n return null\n}\n\nexport async function GET(request: Request, context: { params?: Record<string, unknown> }) {\n const parsedParams = paramsSchema.safeParse(context.params)\n if (!parsedParams.success) {\n return notFound('Deal not found')\n }\n\n const includeFlags = readIncludeFlags(request)\n const viewMode = readViewMode(request)\n const liteView = viewMode === 'lite'\n const includeStages = includeFlags.has('stages')\n const container = await createRequestContainer()\n const auth = await getAuthFromRequest(request)\n if (!auth?.sub && !auth?.isApiKey) {\n return NextResponse.json({ error: 'Authentication required' }, { status: 401 })\n }\n\n let rbac: RbacService | null = null\n try {\n rbac = (container.resolve('rbacService') as RbacService)\n } catch {\n rbac = null\n }\n\n if (!rbac || !auth?.sub) {\n return forbidden('Access denied')\n }\n const hasFeature = await rbac.userHasAllFeatures(auth.sub, ['customers.deals.view'], {\n tenantId: auth.tenantId ?? null,\n organizationId: auth.orgId ?? null,\n })\n if (!hasFeature) {\n return forbidden('Access denied')\n }\n\n const scope = await resolveOrganizationScopeForRequest({ container, auth, request })\n const em = (container.resolve('em') as EntityManager)\n\n const deal = await findOneWithDecryption(\n em,\n CustomerDeal,\n { id: parsedParams.data.id, deletedAt: null },\n {\n populate: ['people.person', 'people.person.personProfile', 'companies.company', 'companies.company.companyProfile'],\n },\n { tenantId: auth.tenantId ?? null, organizationId: auth.orgId ?? null },\n )\n if (!deal) {\n return notFound('Deal not found')\n }\n\n if (auth.tenantId && deal.tenantId && auth.tenantId !== deal.tenantId) {\n return notFound('Deal not found')\n }\n\n if (!isOrganizationReadAccessAllowed({ scope, auth, organizationId: deal.organizationId })) {\n return forbidden('Access denied')\n }\n\n const decryptionScope = {\n tenantId: deal.tenantId ?? auth.tenantId ?? null,\n organizationId: deal.organizationId ?? auth.orgId ?? null,\n }\n // Issue #3175: the reads below only depend on the already-resolved deal +\n // organization scope + decryption scope, so they are grouped into Promise.all\n // batches instead of being awaited one after another. True dependencies stay\n // ordered: preview/decrypt run after their link rows, pipeline reads after the\n // effective stage is known, and the appearance map / audit fallback after the\n // pipeline stages load. This mirrors the parallel-read pattern already used\n // across the customers list/interactions/activities routes.\n const resolveAssociations = async (): Promise<{\n people: DealAssociation[]\n companies: DealAssociation[]\n linkedPersonIds: string[]\n linkedCompanyIds: string[]\n }> => {\n if (liteView) {\n const [personLinkRows, companyLinkRows] = await Promise.all([\n findWithDecryption(\n em,\n CustomerDealPersonLink,\n { deal: deal.id },\n { orderBy: { createdAt: 'ASC' } },\n decryptionScope,\n ),\n findWithDecryption(\n em,\n CustomerDealCompanyLink,\n { deal: deal.id },\n { orderBy: { createdAt: 'ASC' } },\n decryptionScope,\n ),\n ])\n\n const linkedPersonIds = Array.from(\n new Set(\n personLinkRows\n .map((link) => {\n const personRef = link.person\n if (!personRef) return null\n if (typeof personRef === 'string') return personRef\n const personIdValue = personRef.id\n return typeof personIdValue === 'string' ? personIdValue : null\n })\n .filter((value): value is string => typeof value === 'string' && value.trim().length > 0),\n ),\n )\n const linkedCompanyIds = Array.from(\n new Set(\n companyLinkRows\n .map((link) => {\n const companyRef = link.company\n if (!companyRef) return null\n if (typeof companyRef === 'string') return companyRef\n const companyIdValue = companyRef.id\n return typeof companyIdValue === 'string' ? companyIdValue : null\n })\n .filter((value): value is string => typeof value === 'string' && value.trim().length > 0),\n ),\n )\n\n const [previewPeople, previewCompanies] = await Promise.all([\n linkedPersonIds.length\n ? findWithDecryption(\n em,\n CustomerEntity,\n { id: { $in: linkedPersonIds.slice(0, 3) } },\n { populate: ['personProfile'] },\n decryptionScope,\n )\n : [],\n linkedCompanyIds.length\n ? findWithDecryption(\n em,\n CustomerEntity,\n { id: { $in: linkedCompanyIds.slice(0, 3) } },\n { populate: ['companyProfile'] },\n decryptionScope,\n )\n : [],\n ])\n const previewPeopleMap = new Map(previewPeople.map((entity) => [entity.id, entity]))\n const previewCompaniesMap = new Map(previewCompanies.map((entity) => [entity.id, entity]))\n const people = linkedPersonIds.slice(0, 3).reduce<DealAssociation[]>((acc, personId) => {\n const entity = previewPeopleMap.get(personId) ?? null\n if (!entity || entity.deletedAt) return acc\n const { label, subtitle } = normalizePersonAssociation(entity)\n acc.push({ id: entity.id, label, subtitle, kind: 'person' })\n return acc\n }, [])\n const companies = linkedCompanyIds.slice(0, 3).reduce<DealAssociation[]>((acc, companyId) => {\n const entity = previewCompaniesMap.get(companyId) ?? null\n if (!entity || entity.deletedAt) return acc\n const { label, subtitle } = normalizeCompanyAssociation(entity)\n acc.push({ id: entity.id, label, subtitle, kind: 'company' })\n return acc\n }, [])\n return { people, companies, linkedPersonIds, linkedCompanyIds }\n }\n\n const [personLinks, companyLinks] = await Promise.all([\n findWithDecryption(\n em,\n CustomerDealPersonLink,\n { deal: deal.id },\n { populate: ['person', 'person.personProfile'] },\n decryptionScope,\n ),\n findWithDecryption(\n em,\n CustomerDealCompanyLink,\n { deal: deal.id },\n { populate: ['company', 'company.companyProfile'] },\n decryptionScope,\n ),\n ])\n const fallbackTenantId = deal.tenantId ?? auth.tenantId ?? null\n const fallbackOrgId = deal.organizationId ?? auth.orgId ?? null\n await decryptEntitiesWithFallbackScope(personLinks, {\n em,\n tenantId: fallbackTenantId,\n organizationId: fallbackOrgId,\n })\n await decryptEntitiesWithFallbackScope(companyLinks, {\n em,\n tenantId: fallbackTenantId,\n organizationId: fallbackOrgId,\n })\n\n const people = personLinks.reduce<DealAssociation[]>((acc, link) => {\n const entity = link.person as CustomerEntity | null\n if (!entity || entity.deletedAt) return acc\n const { label, subtitle } = normalizePersonAssociation(entity)\n acc.push({ id: entity.id, label, subtitle, kind: 'person' })\n return acc\n }, [])\n\n const companies = companyLinks.reduce<DealAssociation[]>((acc, link) => {\n const entity = link.company as CustomerEntity | null\n if (!entity || entity.deletedAt) return acc\n const { label, subtitle } = normalizeCompanyAssociation(entity)\n acc.push({ id: entity.id, label, subtitle, kind: 'company' })\n return acc\n }, [])\n return {\n people,\n companies,\n linkedPersonIds: people.map((entry) => entry.id),\n linkedCompanyIds: companies.map((entry) => entry.id),\n }\n }\n\n const viewerUserId = auth.isApiKey ? null : auth.sub ?? null\n const resolveViewer = async (): Promise<{ name: string | null; email: string | null }> => {\n if (!viewerUserId) {\n return { name: null, email: auth.email ?? null }\n }\n const viewer = await findOneWithDecryption(\n em,\n User,\n { id: viewerUserId, tenantId: auth.tenantId ?? null },\n {},\n { tenantId: auth.tenantId ?? null, organizationId: auth.orgId ?? null },\n )\n return {\n name: viewer?.name ?? null,\n email: viewer?.email ?? auth.email ?? null,\n }\n }\n\n const resolveOwner = async () =>\n deal.ownerUserId\n ? await findOneWithDecryption(\n em,\n User,\n { id: deal.ownerUserId, tenantId: deal.tenantId ?? auth.tenantId ?? null },\n {},\n decryptionScope,\n )\n : null\n\n const resolveStageTransitions = async (): Promise<CustomerDealStageTransition[]> => {\n if (!includeStages) return []\n try {\n return await findWithDecryption(\n em,\n CustomerDealStageTransition,\n { deal: deal.id, deletedAt: null },\n { orderBy: { stageOrder: 'ASC', transitionedAt: 'ASC' } },\n decryptionScope,\n )\n } catch (error) {\n if (!isMissingDealStageTransitionTable(error)) {\n throw error\n }\n warnMissingDealStageTransitionTable('customers.api.deals.detail.GET')\n return []\n }\n }\n\n const [associations, customFieldValues, viewerInfo, owner, effectiveStage, stageTransitions] = await Promise.all([\n resolveAssociations(),\n loadCustomFieldValues({\n em,\n entityId: E.customers.customer_deal,\n recordIds: [deal.id],\n tenantIdByRecord: { [deal.id]: deal.tenantId ?? null },\n organizationIdByRecord: { [deal.id]: deal.organizationId ?? null },\n tenantFallbacks: [deal.tenantId ?? auth.tenantId ?? null].filter((value): value is string => !!value),\n }),\n resolveViewer(),\n resolveOwner(),\n includeStages ? resolveEffectivePipelineStage(em, deal, decryptionScope) : Promise.resolve(null),\n resolveStageTransitions(),\n ])\n\n const { people, companies, linkedPersonIds, linkedCompanyIds } = associations\n const customFields = normalizeCustomFieldResponse(customFieldValues[deal.id]) ?? {}\n const viewerName = viewerInfo.name\n const viewerEmail = viewerInfo.email\n const ownerPayload = owner\n ? {\n id: owner.id,\n name: owner.name ?? owner.email ?? owner.id,\n email: owner.email ?? '',\n }\n : null\n\n const effectivePipelineId = deal.pipelineId ?? effectiveStage?.pipelineId ?? null\n const effectivePipelineStageId = deal.pipelineStageId ?? effectiveStage?.id ?? null\n const effectivePipelineStageLabel = deal.pipelineStage ?? effectiveStage?.label ?? null\n\n const [pipelineStages, pipeline] = await Promise.all([\n includeStages && effectivePipelineId\n ? findWithDecryption(\n em,\n CustomerPipelineStage,\n {\n pipelineId: effectivePipelineId,\n organizationId: deal.organizationId,\n tenantId: deal.tenantId,\n },\n { orderBy: { order: 'ASC' } },\n decryptionScope,\n )\n : [],\n effectivePipelineId\n ? findOneWithDecryption(\n em,\n CustomerPipeline,\n {\n id: effectivePipelineId,\n organizationId: deal.organizationId,\n tenantId: deal.tenantId,\n },\n {},\n decryptionScope,\n )\n : null,\n ])\n\n const persistedStageTransitions = stageTransitions.map((transition) => ({\n stageId: transition.stageId,\n stageLabel: transition.stageLabel,\n stageOrder: transition.stageOrder,\n transitionedAt: transition.transitionedAt.toISOString(),\n }))\n const [pipelineStageAppearanceMap, recoveredStageTransitions] = await Promise.all([\n pipelineStages.length\n ? loadPipelineStageAppearanceMap(em, pipelineStages, deal.organizationId, deal.tenantId)\n : new Map<string, CustomerDictionaryEntry>(),\n includeStages && persistedStageTransitions.length === 0\n ? loadAuditStageTransitionsFallback({ container, deal, pipelineStages })\n : [],\n ])\n const effectiveCurrentStage = (() => {\n if (!effectivePipelineStageId) return null\n const matchingStage = pipelineStages.find((stage) => stage.id === effectivePipelineStageId)\n if (matchingStage) {\n return {\n id: matchingStage.id,\n label: matchingStage.label,\n order: matchingStage.order,\n }\n }\n if (!effectivePipelineStageLabel) return null\n return {\n id: effectivePipelineStageId,\n label: effectivePipelineStageLabel,\n order: 0,\n }\n })()\n const stageTransitionPayload = mergeStageTransitions({\n persisted: persistedStageTransitions,\n recovered: recoveredStageTransitions,\n currentStage: effectiveCurrentStage,\n fallbackTimestamp: deal.createdAt.toISOString(),\n })\n\n return NextResponse.json({\n deal: {\n id: deal.id,\n title: deal.title,\n description: deal.description ?? null,\n status: deal.status ?? null,\n pipelineStage: effectivePipelineStageLabel,\n pipelineId: effectivePipelineId,\n pipelineStageId: effectivePipelineStageId,\n valueAmount: deal.valueAmount ?? null,\n valueCurrency: deal.valueCurrency ?? null,\n probability: deal.probability ?? null,\n expectedCloseAt: deal.expectedCloseAt ? deal.expectedCloseAt.toISOString() : null,\n ownerUserId: deal.ownerUserId ?? null,\n source: deal.source ?? null,\n closureOutcome: deal.closureOutcome ?? null,\n lossReasonId: deal.lossReasonId ?? null,\n lossNotes: deal.lossNotes ?? null,\n organizationId: deal.organizationId ?? null,\n tenantId: deal.tenantId ?? null,\n createdAt: deal.createdAt.toISOString(),\n updatedAt: deal.updatedAt.toISOString(),\n },\n people,\n companies,\n linkedPersonIds,\n linkedCompanyIds,\n counts: {\n people: linkedPersonIds.length,\n companies: linkedCompanyIds.length,\n },\n customFields,\n viewer: {\n userId: viewerUserId,\n name: viewerName,\n email: viewerEmail,\n },\n pipelineStages: pipelineStages.map((stage) => {\n const appearance = pipelineStageAppearanceMap.get(stage.label.trim().toLowerCase())\n return {\n id: stage.id,\n label: stage.label,\n order: stage.order,\n color: appearance?.color ?? null,\n icon: appearance?.icon ?? null,\n }\n }),\n pipelineName: pipeline?.name ?? null,\n stageTransitions: stageTransitionPayload,\n owner: ownerPayload,\n })\n}\n\nconst dealDetailQuerySchema = z.object({\n include: z.string().optional(),\n})\n\nconst pipelineStageInfoSchema = z.object({\n id: z.string().uuid(),\n label: z.string(),\n order: z.number().int(),\n color: z.string().nullable(),\n icon: z.string().nullable(),\n})\n\nconst stageTransitionInfoSchema = z.object({\n stageId: z.string().uuid(),\n stageLabel: z.string(),\n stageOrder: z.number().int(),\n transitionedAt: z.string(),\n})\n\nconst dealDetailResponseSchema = z.object({\n deal: z.object({\n id: z.string().uuid(),\n title: z.string().nullable().optional(),\n description: z.string().nullable().optional(),\n status: z.string().nullable().optional(),\n pipelineStage: z.string().nullable().optional(),\n pipelineId: z.string().uuid().nullable().optional(),\n pipelineStageId: z.string().uuid().nullable().optional(),\n valueAmount: z.string().nullable().optional(),\n valueCurrency: z.string().nullable().optional(),\n probability: z.number().nullable().optional(),\n expectedCloseAt: z.string().nullable().optional(),\n ownerUserId: z.string().uuid().nullable().optional(),\n source: z.string().nullable().optional(),\n closureOutcome: z.enum(['won', 'lost']).nullable().optional(),\n lossReasonId: z.string().uuid().nullable().optional(),\n lossNotes: z.string().nullable().optional(),\n organizationId: z.string().uuid().nullable().optional(),\n tenantId: z.string().uuid().nullable().optional(),\n createdAt: z.string(),\n updatedAt: z.string(),\n }),\n people: z.array(\n z.object({\n id: z.string().uuid(),\n label: z.string(),\n subtitle: z.string().nullable().optional(),\n kind: z.literal('person'),\n }),\n ),\n companies: z.array(\n z.object({\n id: z.string().uuid(),\n label: z.string(),\n subtitle: z.string().nullable().optional(),\n kind: z.literal('company'),\n }),\n ),\n customFields: z.record(z.string(), z.unknown()),\n viewer: z.object({\n userId: z.string().uuid().nullable(),\n name: z.string().nullable(),\n email: z.string().nullable(),\n }),\n pipelineStages: z.array(pipelineStageInfoSchema),\n stageTransitions: z.array(stageTransitionInfoSchema),\n owner: z.object({\n id: z.string().uuid(),\n name: z.string(),\n email: z.string(),\n }).nullable(),\n})\n\nconst dealDetailErrorSchema = z.object({\n error: z.string(),\n})\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Customers',\n summary: 'Fetch deal detail',\n methods: {\n GET: {\n summary: 'Fetch deal with associations and pipeline context',\n description: 'Returns a deal with linked people, companies, closure fields, optional pipeline history, custom fields, and viewer context.',\n query: dealDetailQuerySchema,\n responses: [\n { status: 200, description: 'Deal detail payload', schema: dealDetailResponseSchema },\n ],\n errors: [\n { status: 401, description: 'Unauthorized', schema: dealDetailErrorSchema },\n { status: 403, description: 'Forbidden for tenant/organization scope', schema: dealDetailErrorSchema },\n { status: 404, description: 'Deal not found', schema: dealDetailErrorSchema },\n ],\n },\n },\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["linkedPersonIds", "linkedCompanyIds", "people", "companies"]
|
|
7
7
|
}
|
|
@@ -3,14 +3,13 @@ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
import Link from "next/link";
|
|
5
5
|
import { AtSign, Briefcase, Loader2, Pencil, X } from "lucide-react";
|
|
6
|
-
import ReactMarkdown from "react-markdown";
|
|
7
6
|
import { Button } from "@open-mercato/ui/primitives/button";
|
|
7
|
+
import { MarkdownContent } from "@open-mercato/ui/backend/markdown";
|
|
8
8
|
import { flash } from "@open-mercato/ui/backend/FlashMessages";
|
|
9
9
|
import { useT } from "@open-mercato/shared/lib/i18n/context";
|
|
10
10
|
import { cn } from "@open-mercato/shared/lib/utils";
|
|
11
11
|
import { useQueryClient } from "@tanstack/react-query";
|
|
12
12
|
import { useOrganizationScopeVersion } from "@open-mercato/shared/lib/frontend/useOrganizationScope";
|
|
13
|
-
import remarkGfm from "remark-gfm";
|
|
14
13
|
import { useEmailDuplicateCheck } from "../../backend/hooks/useEmailDuplicateCheck.js";
|
|
15
14
|
import { lookupPhoneDuplicate } from "../../utils/phoneDuplicates.js";
|
|
16
15
|
import {
|
|
@@ -155,7 +154,6 @@ function InlineTextEditor(props) {
|
|
|
155
154
|
}
|
|
156
155
|
const InlineMultilineEditor = UiInlineMultilineEditor;
|
|
157
156
|
const InlineSelectEditor = UiInlineSelectEditor;
|
|
158
|
-
const MARKDOWN_PREVIEW_PLUGINS = [remarkGfm];
|
|
159
157
|
function createSocialRenderDisplay(IconComponent) {
|
|
160
158
|
return ({ value, emptyLabel }) => {
|
|
161
159
|
const raw = typeof value === "string" ? value.trim() : "";
|
|
@@ -186,7 +184,14 @@ const renderMultilineMarkdownDisplay = ({ value, emptyLabel }) => {
|
|
|
186
184
|
if (!trimmed.length) {
|
|
187
185
|
return /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: emptyLabel });
|
|
188
186
|
}
|
|
189
|
-
return /* @__PURE__ */ jsx(
|
|
187
|
+
return /* @__PURE__ */ jsx(
|
|
188
|
+
MarkdownContent,
|
|
189
|
+
{
|
|
190
|
+
format: "markdown",
|
|
191
|
+
body: raw,
|
|
192
|
+
className: "text-sm text-foreground [&>*]:mb-2 [&>*:last-child]:mb-0 [&_ul]:ml-4 [&_ul]:list-disc [&_ol]:ml-4 [&_ol]:list-decimal [&_code]:rounded [&_code]:bg-muted [&_code]:px-1 [&_code]:py-0.5 [&_pre]:rounded-md [&_pre]:bg-muted [&_pre]:p-3 [&_pre]:text-xs"
|
|
193
|
+
}
|
|
194
|
+
);
|
|
190
195
|
};
|
|
191
196
|
function InlineDictionaryEditor({
|
|
192
197
|
label,
|