@open-mercato/core 0.6.5-develop.5382.1.f542de69af → 0.6.6-develop.5412.1.e2a52b14f0
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/helpers/integration/crmFixtures.js +4 -0
- package/dist/helpers/integration/crmFixtures.js.map +2 -2
- package/dist/modules/attachments/api/route.js +2 -0
- package/dist/modules/attachments/api/route.js.map +2 -2
- package/dist/modules/attachments/lib/access.js +18 -0
- package/dist/modules/attachments/lib/access.js.map +2 -2
- package/dist/modules/auth/services/rbacService.js +3 -2
- package/dist/modules/auth/services/rbacService.js.map +2 -2
- package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/Diagnostics.js +0 -3
- package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/Diagnostics.js.map +2 -2
- package/dist/modules/customers/api/deals/route.js +43 -2
- package/dist/modules/customers/api/deals/route.js.map +2 -2
- package/dist/modules/customers/api/deals/summary/route.js +402 -0
- package/dist/modules/customers/api/deals/summary/route.js.map +7 -0
- package/dist/modules/customers/backend/customers/deals/[id]/hooks/useDealActivities.js +16 -5
- package/dist/modules/customers/backend/customers/deals/[id]/hooks/useDealActivities.js.map +2 -2
- package/dist/modules/customers/backend/customers/deals/[id]/hooks/useDealData.js +22 -5
- package/dist/modules/customers/backend/customers/deals/[id]/hooks/useDealData.js.map +2 -2
- package/dist/modules/customers/backend/customers/deals/[id]/page.js +12 -2
- package/dist/modules/customers/backend/customers/deals/[id]/page.js.map +2 -2
- package/dist/modules/customers/backend/customers/deals/page.js +221 -56
- package/dist/modules/customers/backend/customers/deals/page.js.map +3 -3
- package/dist/modules/customers/backend/customers/deals/pipeline/page.js +1 -1
- package/dist/modules/customers/backend/customers/deals/pipeline/page.js.map +2 -2
- package/dist/modules/customers/cli.js +15 -9
- package/dist/modules/customers/cli.js.map +2 -2
- package/dist/modules/customers/components/DealsKpiStrip.js +282 -0
- package/dist/modules/customers/components/DealsKpiStrip.js.map +7 -0
- package/dist/modules/customers/components/detail/ConfirmDealLostDialog.js +0 -1
- package/dist/modules/customers/components/detail/ConfirmDealLostDialog.js.map +2 -2
- package/dist/modules/customers/components/detail/DealForm.js +100 -17
- package/dist/modules/customers/components/detail/DealForm.js.map +2 -2
- package/dist/modules/customers/components/detail/ScheduleActivityDialog.js +1 -2
- package/dist/modules/customers/components/detail/ScheduleActivityDialog.js.map +2 -2
- package/dist/modules/customers/components/kpi/PipelineStageBar.js +63 -0
- package/dist/modules/customers/components/kpi/PipelineStageBar.js.map +7 -0
- package/dist/modules/customers/lib/dealsMetrics.js +82 -0
- package/dist/modules/customers/lib/dealsMetrics.js.map +7 -0
- package/dist/modules/directory/subscribers/invalidateOrgScopeCache.js +2 -1
- package/dist/modules/directory/subscribers/invalidateOrgScopeCache.js.map +2 -2
- package/dist/modules/directory/utils/organizationScope.js +59 -27
- package/dist/modules/directory/utils/organizationScope.js.map +2 -2
- package/dist/modules/entities/api/entities.js +7 -0
- package/dist/modules/entities/api/entities.js.map +2 -2
- package/dist/modules/entities/api/records.js +26 -15
- package/dist/modules/entities/api/records.js.map +2 -2
- package/dist/modules/entities/backend/entities/user/[entityId]/records/[recordId]/page.js +14 -0
- package/dist/modules/entities/backend/entities/user/[entityId]/records/[recordId]/page.js.map +2 -2
- package/dist/modules/entities/backend/entities/user/[entityId]/records/create/page.js +14 -0
- package/dist/modules/entities/backend/entities/user/[entityId]/records/create/page.js.map +2 -2
- package/dist/modules/entities/backend/entities/user/[entityId]/records/page.js +12 -0
- package/dist/modules/entities/backend/entities/user/[entityId]/records/page.js.map +2 -2
- package/dist/modules/entities/components/useRecordsEntityGuard.js +30 -0
- package/dist/modules/entities/components/useRecordsEntityGuard.js.map +7 -0
- package/dist/modules/query_index/lib/engine.js +4 -2
- package/dist/modules/query_index/lib/engine.js.map +2 -2
- package/dist/modules/staff/api/team-members.js +9 -2
- package/dist/modules/staff/api/team-members.js.map +2 -2
- package/dist/modules/staff/api/timesheets/time-entries/[id]/timer-start/route.js +24 -1
- package/dist/modules/staff/api/timesheets/time-entries/[id]/timer-start/route.js.map +2 -2
- package/dist/modules/staff/backend/staff/team-members/[id]/page.js +11 -6
- package/dist/modules/staff/backend/staff/team-members/[id]/page.js.map +2 -2
- package/dist/modules/staff/commands/team-members.js +1 -1
- package/dist/modules/staff/commands/team-members.js.map +2 -2
- package/dist/modules/staff/components/TeamMemberForm.js +1 -1
- package/dist/modules/staff/components/TeamMemberForm.js.map +2 -2
- package/dist/modules/staff/lib/scheduleSwitch.js +23 -0
- package/dist/modules/staff/lib/scheduleSwitch.js.map +7 -0
- package/dist/modules/workflows/backend/definitions/create/page.js +1 -2
- package/dist/modules/workflows/backend/definitions/create/page.js.map +2 -2
- package/dist/modules/workflows/backend/definitions/visual-editor/page.js +1 -2
- package/dist/modules/workflows/backend/definitions/visual-editor/page.js.map +2 -2
- package/dist/modules/workflows/components/DefinitionTriggersEditor.js +1 -2
- package/dist/modules/workflows/components/DefinitionTriggersEditor.js.map +2 -2
- package/dist/modules/workflows/components/NodeEditDialog.js +4 -13
- package/dist/modules/workflows/components/NodeEditDialog.js.map +2 -2
- package/dist/modules/workflows/components/NodeEditDialogCrudForm.js +4 -13
- package/dist/modules/workflows/components/NodeEditDialogCrudForm.js.map +2 -2
- package/dist/modules/workflows/components/WorkflowGraphImpl.js +1 -4
- package/dist/modules/workflows/components/WorkflowGraphImpl.js.map +2 -2
- package/dist/modules/workflows/components/fields/FormFieldArrayEditor.js +2 -5
- package/dist/modules/workflows/components/fields/FormFieldArrayEditor.js.map +2 -2
- package/package.json +8 -8
- package/src/helpers/integration/crmFixtures.ts +21 -1
- package/src/modules/attachments/AGENTS.md +79 -0
- package/src/modules/attachments/api/route.ts +2 -0
- package/src/modules/attachments/lib/access.ts +36 -0
- package/src/modules/auth/services/rbacService.ts +11 -2
- package/src/modules/customer_accounts/backend/customer_accounts/settings/domain/components/Diagnostics.tsx +0 -3
- package/src/modules/customers/api/deals/route.ts +51 -2
- package/src/modules/customers/api/deals/summary/route.ts +496 -0
- package/src/modules/customers/backend/customers/deals/[id]/hooks/useDealActivities.ts +28 -6
- package/src/modules/customers/backend/customers/deals/[id]/hooks/useDealData.ts +33 -6
- package/src/modules/customers/backend/customers/deals/[id]/page.tsx +17 -2
- package/src/modules/customers/backend/customers/deals/page.tsx +254 -66
- package/src/modules/customers/backend/customers/deals/pipeline/page.tsx +1 -2
- package/src/modules/customers/cli.ts +15 -15
- package/src/modules/customers/components/DealsKpiStrip.tsx +389 -0
- package/src/modules/customers/components/detail/ConfirmDealLostDialog.tsx +0 -1
- package/src/modules/customers/components/detail/DealForm.tsx +121 -19
- package/src/modules/customers/components/detail/ScheduleActivityDialog.tsx +1 -2
- package/src/modules/customers/components/kpi/PipelineStageBar.tsx +77 -0
- package/src/modules/customers/i18n/de.json +43 -0
- package/src/modules/customers/i18n/en.json +43 -0
- package/src/modules/customers/i18n/es.json +43 -0
- package/src/modules/customers/i18n/pl.json +43 -0
- package/src/modules/customers/lib/dealsMetrics.ts +159 -0
- package/src/modules/directory/subscribers/invalidateOrgScopeCache.ts +3 -1
- package/src/modules/directory/utils/organizationScope.ts +85 -30
- package/src/modules/entities/api/entities.ts +11 -0
- package/src/modules/entities/api/records.ts +46 -25
- package/src/modules/entities/backend/entities/user/[entityId]/records/[recordId]/page.tsx +15 -0
- package/src/modules/entities/backend/entities/user/[entityId]/records/create/page.tsx +15 -0
- package/src/modules/entities/backend/entities/user/[entityId]/records/page.tsx +23 -0
- package/src/modules/entities/components/useRecordsEntityGuard.ts +41 -0
- package/src/modules/entities/i18n/de.json +1 -0
- package/src/modules/entities/i18n/en.json +1 -0
- package/src/modules/entities/i18n/es.json +1 -0
- package/src/modules/entities/i18n/pl.json +1 -0
- package/src/modules/query_index/lib/engine.ts +11 -5
- package/src/modules/staff/api/team-members.ts +9 -2
- package/src/modules/staff/api/timesheets/time-entries/[id]/timer-start/route.ts +31 -1
- package/src/modules/staff/backend/staff/team-members/[id]/page.tsx +18 -8
- package/src/modules/staff/commands/team-members.ts +5 -2
- package/src/modules/staff/components/TeamMemberForm.tsx +4 -1
- package/src/modules/staff/i18n/de.json +1 -0
- package/src/modules/staff/i18n/en.json +1 -0
- package/src/modules/staff/i18n/es.json +1 -0
- package/src/modules/staff/i18n/pl.json +1 -0
- package/src/modules/staff/lib/scheduleSwitch.ts +46 -0
- package/src/modules/workflows/backend/definitions/create/page.tsx +1 -2
- package/src/modules/workflows/backend/definitions/visual-editor/page.tsx +1 -2
- package/src/modules/workflows/components/DefinitionTriggersEditor.tsx +1 -2
- package/src/modules/workflows/components/NodeEditDialog.tsx +1 -4
- package/src/modules/workflows/components/NodeEditDialogCrudForm.tsx +4 -7
- package/src/modules/workflows/components/WorkflowGraphImpl.tsx +1 -2
- package/src/modules/workflows/components/fields/FormFieldArrayEditor.tsx +2 -3
|
@@ -24,6 +24,7 @@ import { consumeAdvancedFilterState, mergeAdvancedFilterTree } from "@open-merca
|
|
|
24
24
|
import { fetchStuckDealIds } from "../../lib/stuckDeals.js";
|
|
25
25
|
const rawBodySchema = z.object({}).passthrough();
|
|
26
26
|
const stringOrStringArray = z.union([z.string(), z.array(z.string())]);
|
|
27
|
+
const OPEN_DEAL_STATUSES = ["open", "in_progress"];
|
|
27
28
|
const booleanQueryParam = z.preprocess((value) => {
|
|
28
29
|
const parsed = parseBooleanFromUnknown(value);
|
|
29
30
|
return parsed === null ? value : parsed;
|
|
@@ -42,6 +43,7 @@ const dealListQuerySchema = z.object({
|
|
|
42
43
|
expectedCloseAtTo: z.string().optional(),
|
|
43
44
|
isStuck: booleanQueryParam,
|
|
44
45
|
isOverdue: booleanQueryParam,
|
|
46
|
+
needsAttention: booleanQueryParam,
|
|
45
47
|
valueCurrency: stringOrStringArray.optional(),
|
|
46
48
|
sortField: z.string().optional(),
|
|
47
49
|
sortDir: z.enum(["asc", "desc"]).optional(),
|
|
@@ -119,6 +121,36 @@ async function fetchDealIdsMatchingAssociations(em, organizationId, tenantId, pe
|
|
|
119
121
|
);
|
|
120
122
|
return rows.map((row) => row.id);
|
|
121
123
|
}
|
|
124
|
+
async function fetchNeedAttentionDealIds(em, organizationId, tenantId) {
|
|
125
|
+
const connection = em.getConnection();
|
|
126
|
+
const overdueRows = await connection.execute(
|
|
127
|
+
`SELECT id FROM customer_deals
|
|
128
|
+
WHERE organization_id = ?
|
|
129
|
+
AND tenant_id = ?
|
|
130
|
+
AND deleted_at IS NULL
|
|
131
|
+
AND status = 'open'
|
|
132
|
+
AND expected_close_at IS NOT NULL
|
|
133
|
+
AND expected_close_at < CURRENT_DATE`,
|
|
134
|
+
[organizationId, tenantId]
|
|
135
|
+
);
|
|
136
|
+
const attentionIds = new Set(overdueRows.map((row) => row.id));
|
|
137
|
+
const stuckIds = await fetchStuckDealIds(em, organizationId, tenantId);
|
|
138
|
+
if (stuckIds.length > 0) {
|
|
139
|
+
const idPlaceholders = stuckIds.map(() => "?").join(",");
|
|
140
|
+
const statusPlaceholders = OPEN_DEAL_STATUSES.map(() => "?").join(",");
|
|
141
|
+
const openStuckRows = await connection.execute(
|
|
142
|
+
`SELECT id FROM customer_deals
|
|
143
|
+
WHERE organization_id = ?
|
|
144
|
+
AND tenant_id = ?
|
|
145
|
+
AND deleted_at IS NULL
|
|
146
|
+
AND status IN (${statusPlaceholders})
|
|
147
|
+
AND id IN (${idPlaceholders})`,
|
|
148
|
+
[organizationId, tenantId, ...OPEN_DEAL_STATUSES, ...stuckIds]
|
|
149
|
+
);
|
|
150
|
+
for (const row of openStuckRows) attentionIds.add(row.id);
|
|
151
|
+
}
|
|
152
|
+
return Array.from(attentionIds);
|
|
153
|
+
}
|
|
122
154
|
function normalizeCurrencyList(value) {
|
|
123
155
|
const set = /* @__PURE__ */ new Set();
|
|
124
156
|
const visit = (entry) => {
|
|
@@ -233,7 +265,7 @@ async function buildDealListFilters(query, ctx) {
|
|
|
233
265
|
if (expectedCloseTo) range.$lte = expectedCloseTo;
|
|
234
266
|
filters.expected_close_at = range;
|
|
235
267
|
}
|
|
236
|
-
if (query.isOverdue) {
|
|
268
|
+
if (query.isOverdue && !query.needsAttention) {
|
|
237
269
|
const today = /* @__PURE__ */ new Date();
|
|
238
270
|
today.setHours(0, 0, 0, 0);
|
|
239
271
|
if (statusList.length === 0) {
|
|
@@ -243,7 +275,7 @@ async function buildDealListFilters(query, ctx) {
|
|
|
243
275
|
existingRange.$lt = today;
|
|
244
276
|
filters.expected_close_at = existingRange;
|
|
245
277
|
}
|
|
246
|
-
if (query.isStuck && ctx) {
|
|
278
|
+
if (query.isStuck && !query.needsAttention && ctx) {
|
|
247
279
|
const tenantId = ctx.auth?.tenantId;
|
|
248
280
|
const organizationId = ctx.auth?.orgId;
|
|
249
281
|
if (typeof tenantId === "string" && typeof organizationId === "string") {
|
|
@@ -252,6 +284,15 @@ async function buildDealListFilters(query, ctx) {
|
|
|
252
284
|
intersectIds(stuckIds);
|
|
253
285
|
}
|
|
254
286
|
}
|
|
287
|
+
if (query.needsAttention && ctx) {
|
|
288
|
+
const tenantId = ctx.auth?.tenantId;
|
|
289
|
+
const organizationId = ctx.auth?.orgId;
|
|
290
|
+
if (typeof tenantId === "string" && typeof organizationId === "string") {
|
|
291
|
+
const em = ctx.container.resolve("em");
|
|
292
|
+
const attentionIds = await fetchNeedAttentionDealIds(em, organizationId, tenantId);
|
|
293
|
+
intersectIds(attentionIds);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
255
296
|
const url = ctx?.request ? new URL(ctx.request.url) : null;
|
|
256
297
|
const personCandidates = [query.personId, query.personEntityId];
|
|
257
298
|
const companyCandidates = [query.companyId, query.companyEntityId];
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../src/modules/customers/api/deals/route.ts"],
|
|
4
|
-
"sourcesContent": ["import { z } from 'zod'\nimport { makeCrudRoute } from '@open-mercato/shared/lib/crud/factory'\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { CustomerDeal, CustomerDealPersonLink, CustomerDealCompanyLink } from '../../data/entities'\nimport { dealCreateSchema, dealUpdateSchema } from '../../data/validators'\nimport { E } from '#generated/entities.ids.generated'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { parseBooleanFromUnknown } from '@open-mercato/shared/lib/boolean'\nimport {\n applyEntityIdRestriction,\n findMatchingEntityIdsWithQueryEngine,\n findMatchingEntityIdsBySearchTokensAcrossSources,\n parseScopedCommandInput,\n} from '../utils'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport {\n createCustomersCrudOpenApi,\n createPagedListResponseSchema,\n defaultOkResponseSchema,\n} from '../openapi'\nimport { findWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { isTenantDataEncryptionEnabled } from '@open-mercato/shared/lib/encryption/toggles'\nimport { escapeLikePattern } from '@open-mercato/shared/lib/db/escapeLikePattern'\nimport { consumeAdvancedFilterState, mergeAdvancedFilterTree } from '@open-mercato/shared/lib/crud/advanced-filter-integration'\nimport { fetchStuckDealIds } from '../../lib/stuckDeals'\n\nconst rawBodySchema = z.object({}).passthrough()\n\nconst stringOrStringArray = z.union([z.string(), z.array(z.string())])\nconst booleanQueryParam = z.preprocess((value) => {\n const parsed = parseBooleanFromUnknown(value)\n return parsed === null ? value : parsed\n}, z.boolean()).optional()\n\nexport const dealListQuerySchema = z\n .object({\n page: z.coerce.number().min(1).default(1),\n pageSize: z.coerce.number().min(1).max(100).default(50),\n id: z.string().uuid().optional(),\n search: z.string().optional(),\n status: stringOrStringArray.optional(),\n pipelineStage: z.string().optional(),\n pipelineId: stringOrStringArray.optional(),\n pipelineStageId: z.union([z.string().uuid(), z.literal('__unassigned')]).optional(),\n ownerUserId: stringOrStringArray.optional(),\n expectedCloseAtFrom: z.string().optional(),\n expectedCloseAtTo: z.string().optional(),\n isStuck: booleanQueryParam,\n isOverdue: booleanQueryParam,\n valueCurrency: stringOrStringArray.optional(),\n sortField: z.string().optional(),\n sortDir: z.enum(['asc', 'desc']).optional(),\n /**\n * @deprecated Use `personId` instead. The `personEntityId` alias is kept for backward\n * compatibility with older clients and will be removed in a future minor release.\n */\n personEntityId: z.string().uuid().optional().describe('Deprecated; use personId'),\n /**\n * @deprecated Use `companyId` instead. The `companyEntityId` alias is kept for backward\n * compatibility with older clients and will be removed in a future minor release.\n */\n companyEntityId: z.string().uuid().optional().describe('Deprecated; use companyId'),\n personId: stringOrStringArray.optional(),\n companyId: stringOrStringArray.optional(),\n })\n .passthrough()\n\nconst routeMetadata = {\n GET: { requireAuth: true, requireFeatures: ['customers.deals.view'] },\n POST: { requireAuth: true, requireFeatures: ['customers.deals.manage'] },\n PUT: { requireAuth: true, requireFeatures: ['customers.deals.manage'] },\n DELETE: { requireAuth: true, requireFeatures: ['customers.deals.manage'] },\n}\n\nexport const metadata = routeMetadata\n\nexport type DealListQuery = z.infer<typeof dealListQuerySchema>\n\nfunction parseUuid(value: unknown): string | null {\n if (typeof value !== 'string') return null\n const trimmed = value.trim()\n if (!trimmed.length) return null\n const result = z.string().uuid().safeParse(trimmed)\n return result.success ? trimmed : null\n}\n\nfunction normalizeStringList(value: unknown): string[] {\n const set = new Set<string>()\n const visit = (entry: unknown) => {\n if (entry == null) return\n if (Array.isArray(entry)) {\n entry.forEach(visit)\n return\n }\n if (typeof entry !== 'string') return\n entry\n .split(',')\n .map((token) => token.trim())\n .filter((token) => token.length > 0)\n .forEach((token) => set.add(token))\n }\n visit(value)\n return Array.from(set)\n}\n\nfunction parseDateInput(value: unknown): Date | null {\n if (!(typeof value === 'string') || value.trim().length === 0) return null\n const date = new Date(value)\n return Number.isNaN(date.getTime()) ? null : date\n}\n\n/**\n * Pre-pagination ID narrowing for person/company filters.\n *\n * Mirrors the SQL the aggregate route uses (`EXISTS ... IN (...)`) so the list endpoint\n * and the lane-header aggregate return a consistent set of deals for the same filters.\n * Semantics: OR within a category (any selected person/company matches), AND across\n * categories (deal must match at least one person AND at least one company when both\n * filter lists are provided).\n *\n * Returns the matched deal IDs (UUIDs). An empty array means \"no deals match\" \u2014 callers\n * must intersect with `restrictedIds` (which will collapse the list to zero).\n */\nasync function fetchDealIdsMatchingAssociations(\n em: EntityManager,\n organizationId: string,\n tenantId: string,\n personIds: string[],\n companyIds: string[],\n): Promise<string[]> {\n if (!personIds.length && !companyIds.length) return []\n const where: string[] = [\n 'organization_id = ?',\n 'tenant_id = ?',\n 'deleted_at IS NULL',\n ]\n const values: Array<string | number> = [organizationId, tenantId]\n if (personIds.length) {\n const placeholders = personIds.map(() => '?').join(',')\n where.push(\n `EXISTS (SELECT 1 FROM customer_deal_people dp WHERE dp.deal_id = customer_deals.id AND dp.person_entity_id IN (${placeholders}))`,\n )\n values.push(...personIds)\n }\n if (companyIds.length) {\n const placeholders = companyIds.map(() => '?').join(',')\n where.push(\n `EXISTS (SELECT 1 FROM customer_deal_companies dc WHERE dc.deal_id = customer_deals.id AND dc.company_entity_id IN (${placeholders}))`,\n )\n values.push(...companyIds)\n }\n const rows = await em.getConnection().execute<Array<{ id: string }>>(\n `SELECT id FROM customer_deals WHERE ${where.join(' AND ')}`,\n values,\n )\n return rows.map((row) => row.id)\n}\n\nfunction normalizeCurrencyList(value: unknown): string[] {\n const set = new Set<string>()\n const visit = (entry: unknown) => {\n if (entry == null) return\n if (Array.isArray(entry)) {\n entry.forEach(visit)\n return\n }\n if (typeof entry !== 'string') return\n entry\n .split(',')\n .map((token) => token.trim().toUpperCase())\n .filter((token) => /^[A-Z]{3}$/.test(token))\n .forEach((token) => set.add(token))\n }\n visit(value)\n return Array.from(set)\n}\n\nfunction normalizeUuidList(values: Array<unknown>): string[] {\n const set = new Set<string>()\n values.forEach((candidate) => {\n if (Array.isArray(candidate)) {\n candidate.forEach((entry) => {\n const parsed = parseUuid(entry)\n if (parsed) set.add(parsed)\n })\n return\n }\n if (typeof candidate === 'string' && candidate.includes(',')) {\n candidate\n .split(',')\n .map((entry) => entry.trim())\n .forEach((entry) => {\n const parsed = parseUuid(entry)\n if (parsed) set.add(parsed)\n })\n return\n }\n const parsed = parseUuid(candidate)\n if (parsed) set.add(parsed)\n })\n return Array.from(set)\n}\n\nexport async function buildDealListFilters(query: DealListQuery, ctx?: import('@open-mercato/shared/lib/crud/factory').CrudCtx) {\n const advancedFilterTree = consumeAdvancedFilterState(query)\n const filters: Record<string, unknown> = {}\n let restrictedIds: string[] | null = null\n\n const intersectIds = (ids: string[]) => {\n if (restrictedIds === null) {\n restrictedIds = ids\n return\n }\n const lookup = new Set(ids)\n restrictedIds = restrictedIds.filter((id) => lookup.has(id))\n }\n\n if (query.id) filters.id = { $eq: query.id }\n\n if (query.search) {\n const matchingIds = ctx\n ? await findMatchingEntityIdsBySearchTokensAcrossSources({\n ctx,\n query: query.search,\n sources: [\n {\n entityType: E.customers.customer_deal,\n fields: [\n 'title',\n 'description',\n 'status',\n 'pipeline_stage',\n 'source',\n 'value_amount',\n 'value_currency',\n 'cf:competitive_risk',\n 'cf:implementation_complexity',\n ],\n },\n ],\n })\n : null\n if (matchingIds !== null && matchingIds.length > 0) {\n intersectIds(matchingIds)\n } else if (isTenantDataEncryptionEnabled()) {\n // `customers:customer_deal.title` and `.description` are encrypted at rest\n // (see `encryption.ts`). The `$ilike` fallback below would silently match\n // nothing on the ciphertext and produce empty pages without disclosing why \u2014\n // collapse the result to \"no matches\" instead so the kanban + list views\n // agree, and lane aggregates stay consistent with the list endpoint.\n intersectIds([])\n } else {\n const searchPattern = `%${escapeLikePattern(query.search)}%`\n filters.$or = [\n { title: { $ilike: searchPattern } },\n { description: { $ilike: searchPattern } },\n ]\n }\n }\n\n const statusList = query.status ? normalizeStringList(query.status) : []\n if (statusList.length > 0) {\n filters.status = statusList.length === 1 ? { $eq: statusList[0] } : { $in: statusList }\n }\n\n if (query.pipelineStage) {\n filters.pipeline_stage = { $eq: query.pipelineStage }\n }\n\n const pipelineIds = query.pipelineId ? normalizeUuidList([query.pipelineId]) : []\n if (pipelineIds.length > 0) {\n filters.pipeline_id = pipelineIds.length === 1 ? { $eq: pipelineIds[0] } : { $in: pipelineIds }\n }\n\n if (query.pipelineStageId === '__unassigned') {\n filters.pipeline_stage_id = { $eq: null }\n } else if (query.pipelineStageId) {\n filters.pipeline_stage_id = { $eq: query.pipelineStageId }\n }\n\n const ownerUserIds = query.ownerUserId ? normalizeUuidList([query.ownerUserId]) : []\n if (ownerUserIds.length > 0) {\n filters.owner_user_id =\n ownerUserIds.length === 1 ? { $eq: ownerUserIds[0] } : { $in: ownerUserIds }\n }\n\n // Currency filter (additive; sourced by the kanban Currency popover). Codes are\n // normalised to upper-case so `usd` / `USD` / mixed-case query params all hit the same\n // stored value (the deals API persists `value_currency` upper-case already).\n const currencyCodes = query.valueCurrency ? normalizeCurrencyList(query.valueCurrency) : []\n if (currencyCodes.length > 0) {\n filters.value_currency =\n currencyCodes.length === 1 ? { $eq: currencyCodes[0] } : { $in: currencyCodes }\n }\n\n const expectedCloseFrom = parseDateInput(query.expectedCloseAtFrom)\n const expectedCloseTo = parseDateInput(query.expectedCloseAtTo)\n if (expectedCloseFrom || expectedCloseTo) {\n const range: Record<string, Date> = {}\n if (expectedCloseFrom) range.$gte = expectedCloseFrom\n if (expectedCloseTo) range.$lte = expectedCloseTo\n filters.expected_close_at = range\n }\n\n if (query.isOverdue) {\n const today = new Date()\n today.setHours(0, 0, 0, 0)\n if (statusList.length === 0) {\n filters.status = { $eq: 'open' }\n }\n const existingRange =\n filters.expected_close_at && typeof filters.expected_close_at === 'object'\n ? (filters.expected_close_at as Record<string, Date>)\n : {}\n existingRange.$lt = today\n filters.expected_close_at = existingRange\n }\n\n if (query.isStuck && ctx) {\n const tenantId = ctx.auth?.tenantId\n // CrudCtx.auth carries `orgId` (not `organizationId`). The previous code referenced\n // `organizationId` which is always `undefined`, so the typeof check below silently\n // skipped the entire isStuck branch \u2014 `?isStuck=true` was a no-op on this endpoint.\n const organizationId = ctx.auth?.orgId\n if (typeof tenantId === 'string' && typeof organizationId === 'string') {\n const em = ctx.container.resolve<EntityManager>('em')\n const stuckIds = await fetchStuckDealIds(em, organizationId, tenantId)\n intersectIds(stuckIds)\n }\n }\n\n // Pre-pagination association filter. Must run on the FULL dataset (before pagination),\n // otherwise matching deals on later pages disappear and `total` would be wrong. Read the\n // raw URL too so legacy `?personEntityId=` / `?companyEntityId=` keep working alongside the\n // canonical `?personId=` / `?companyId=`.\n const url = ctx?.request ? new URL(ctx.request.url) : null\n const personCandidates: unknown[] = [query.personId, query.personEntityId]\n const companyCandidates: unknown[] = [query.companyId, query.companyEntityId]\n if (url) {\n personCandidates.push(url.searchParams.getAll('personId'))\n personCandidates.push(url.searchParams.getAll('personEntityId'))\n companyCandidates.push(url.searchParams.getAll('companyId'))\n companyCandidates.push(url.searchParams.getAll('companyEntityId'))\n }\n const selectedPersonIds = normalizeUuidList(personCandidates)\n const selectedCompanyIds = normalizeUuidList(companyCandidates)\n if ((selectedPersonIds.length > 0 || selectedCompanyIds.length > 0) && ctx) {\n const tenantId = ctx.auth?.tenantId\n // `ctx.auth` exposes `orgId` (see AuthContext in @open-mercato/shared/lib/auth/server).\n // Read it under the correct key \u2014 the previous code's `organizationId` would always be\n // `undefined`, silently disabling association filtering on the deals list endpoint.\n const organizationId = ctx.auth?.orgId\n if (typeof tenantId === 'string' && typeof organizationId === 'string') {\n const em = ctx.container.resolve<EntityManager>('em')\n const matchedIds = await fetchDealIdsMatchingAssociations(\n em,\n organizationId,\n tenantId,\n selectedPersonIds,\n selectedCompanyIds,\n )\n // intersectIds with empty array \u2192 no rows; collapses the page to zero, total stays correct.\n intersectIds(matchedIds)\n }\n }\n\n if (ctx && advancedFilterTree) {\n const advancedFilters = mergeAdvancedFilterTree({ ...filters }, advancedFilterTree)\n const matchedIds = await findMatchingEntityIdsWithQueryEngine({\n ctx,\n entityId: E.customers.customer_deal,\n filters: advancedFilters,\n })\n if (matchedIds !== null) {\n intersectIds(matchedIds)\n }\n }\n\n if (restrictedIds !== null) {\n applyEntityIdRestriction(filters, restrictedIds)\n }\n\n return filters\n}\n\nconst crud = makeCrudRoute<unknown, unknown, DealListQuery>({\n metadata: routeMetadata,\n orm: {\n entity: CustomerDeal,\n idField: 'id',\n orgField: 'organizationId',\n tenantField: 'tenantId',\n softDeleteField: 'deletedAt',\n },\n indexer: {\n entityType: E.customers.customer_deal,\n },\n enrichers: { entityId: 'customers.deal' },\n list: {\n schema: dealListQuerySchema,\n entityId: E.customers.customer_deal,\n fields: [\n 'id',\n 'title',\n 'description',\n 'status',\n 'pipeline_stage',\n 'pipeline_id',\n 'pipeline_stage_id',\n 'value_amount',\n 'value_currency',\n 'probability',\n 'expected_close_at',\n 'owner_user_id',\n 'source',\n 'closure_outcome',\n 'loss_reason_id',\n 'loss_notes',\n 'organization_id',\n 'tenant_id',\n 'created_at',\n 'updated_at',\n ],\n decorateCustomFields: {\n entityIds: E.customers.customer_deal,\n },\n sortFieldMap: {\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 buildFilters: buildDealListFilters,\n },\n actions: {\n create: {\n commandId: 'customers.deals.create',\n schema: rawBodySchema,\n mapInput: async ({ raw, ctx }) => {\n const { translate } = await resolveTranslations()\n return parseScopedCommandInput(dealCreateSchema, raw ?? {}, ctx, translate)\n },\n response: ({ result }) => ({ id: result?.dealId ?? result?.id ?? null }),\n status: 201,\n },\n update: {\n commandId: 'customers.deals.update',\n schema: rawBodySchema,\n mapInput: async ({ raw, ctx }) => {\n const { translate } = await resolveTranslations()\n return parseScopedCommandInput(dealUpdateSchema, raw ?? {}, ctx, translate)\n },\n response: () => ({ ok: true }),\n },\n delete: {\n commandId: 'customers.deals.delete',\n schema: rawBodySchema,\n mapInput: async ({ parsed, ctx }) => {\n const { translate } = await resolveTranslations()\n const id =\n parsed?.body?.id ??\n parsed?.id ??\n parsed?.query?.id ??\n (ctx.request ? new URL(ctx.request.url).searchParams.get('id') : null)\n if (!id) throw new CrudHttpError(400, { error: translate('customers.errors.deal_required', 'Deal id is required') })\n return { id }\n },\n response: () => ({ ok: true }),\n },\n },\n hooks: {\n // afterList only DECORATES results with `people`/`companies` arrays \u2014 it must not filter,\n // because filtering after pagination would drop deals on later pages and rewrite `total`\n // to a misleading value. Association filtering happens pre-pagination in `buildFilters`\n // via `fetchDealIdsMatchingAssociations`.\n afterList: async (payload, ctx) => {\n const items = Array.isArray(payload.items) ? payload.items : []\n if (!items.length) return\n const scopeSource = (items[0] ?? {}) as Record<string, unknown>\n const tenantIdRaw = scopeSource.tenantId ?? scopeSource.tenant_id\n const fallbackTenantId = (typeof tenantIdRaw === 'string' && tenantIdRaw.trim().length ? tenantIdRaw : null) ?? ctx.auth?.tenantId ?? null\n const orgIdRaw = scopeSource.organizationId ?? scopeSource.organization_id\n const fallbackOrganizationId = (typeof orgIdRaw === 'string' && orgIdRaw.trim().length ? orgIdRaw : null) ?? ctx.auth?.orgId ?? null\n const ids = items\n .map((item: unknown) => {\n if (!item || typeof item !== 'object') return null\n const candidate = (item as Record<string, unknown>).id\n return typeof candidate === 'string' && candidate.trim().length ? candidate : null\n })\n .filter((value: string | null): value is string => typeof value === 'string' && value.length > 0)\n if (!ids.length) {\n payload.items = []\n payload.total = 0\n return\n }\n try {\n const em = (ctx.container.resolve('em') as EntityManager)\n const [allPersonLinks, allCompanyLinks] = await Promise.all([\n findWithDecryption(\n em,\n CustomerDealPersonLink,\n { deal: { $in: ids } },\n { populate: ['person'] },\n { tenantId: fallbackTenantId, organizationId: fallbackOrganizationId },\n ),\n findWithDecryption(\n em,\n CustomerDealCompanyLink,\n { deal: { $in: ids } },\n { populate: ['company'] },\n { tenantId: fallbackTenantId, organizationId: fallbackOrganizationId },\n ),\n ])\n\n const personAssignments = new Map<string, { id: string; label: string }[]>()\n allPersonLinks.forEach((link) => {\n const deal = link.deal\n const dealRecord = deal && typeof deal === 'object' ? deal as unknown as Record<string, unknown> : null\n const dealId = typeof deal === 'string' ? deal\n : dealRecord && typeof dealRecord.id === 'string' ? dealRecord.id\n : null\n if (!dealId) return\n const personRef = link.person\n const personRecord = personRef && typeof personRef === 'object' ? personRef as unknown as Record<string, unknown> : null\n const personId = typeof personRef === 'string' ? personRef\n : personRecord && typeof personRecord.id === 'string' ? personRecord.id\n : null\n if (!personId) return\n const label = personRecord && typeof personRecord.displayName === 'string'\n ? personRecord.displayName\n : ''\n const bucket = personAssignments.get(dealId) ?? []\n if (!bucket.some((entry) => entry.id === personId)) {\n bucket.push({ id: personId, label })\n personAssignments.set(dealId, bucket)\n }\n })\n\n const companyAssignments = new Map<string, { id: string; label: string }[]>()\n allCompanyLinks.forEach((link) => {\n const deal = link.deal\n const dealRecord = deal && typeof deal === 'object' ? deal as unknown as Record<string, unknown> : null\n const dealId = typeof deal === 'string' ? deal\n : dealRecord && typeof dealRecord.id === 'string' ? dealRecord.id\n : null\n if (!dealId) return\n const companyRef = link.company\n const companyRecord = companyRef && typeof companyRef === 'object' ? companyRef as unknown as Record<string, unknown> : null\n const companyId = typeof companyRef === 'string' ? companyRef\n : companyRecord && typeof companyRecord.id === 'string' ? companyRecord.id\n : null\n if (!companyId) return\n const label = companyRecord && typeof companyRecord.displayName === 'string'\n ? companyRecord.displayName\n : ''\n const bucket = companyAssignments.get(dealId) ?? []\n if (!bucket.some((entry) => entry.id === companyId)) {\n bucket.push({ id: companyId, label })\n companyAssignments.set(dealId, bucket)\n }\n })\n\n const enhancedItems = items\n .map((item: unknown) => {\n if (!item || typeof item !== 'object') return null\n const data = item as Record<string, unknown>\n const candidate = typeof data.id === 'string' ? data.id : null\n if (!candidate || !candidate.trim().length) return null\n const people = personAssignments.get(candidate) ?? []\n const companies = companyAssignments.get(candidate) ?? []\n const tenantIdRaw =\n typeof data.tenantId === 'string'\n ? data.tenantId\n : typeof data.tenant_id === 'string'\n ? data.tenant_id\n : null\n const organizationIdRaw =\n typeof data.organizationId === 'string'\n ? data.organizationId\n : typeof data.organization_id === 'string'\n ? data.organization_id\n : null\n const tenantId = tenantIdRaw && tenantIdRaw.trim().length ? tenantIdRaw.trim() : null\n const organizationId = organizationIdRaw && organizationIdRaw.trim().length ? organizationIdRaw.trim() : null\n return {\n ...data,\n personIds: people.map((entry) => entry.id),\n people,\n companyIds: companies.map((entry) => entry.id),\n companies,\n tenantId,\n organizationId,\n }\n })\n .filter(\n (item: Record<string, unknown> | null): item is Record<string, unknown> => item !== null,\n )\n\n payload.items = enhancedItems\n } catch (err) {\n // We swallow rather than fail the request because the kanban is still useful without\n // people/companies labels (cards just lose their company pill). Tag every item with\n // `_associations: { ok: false }` so a future surface can render a degraded-state hint\n // instead of silently showing cards without company badges.\n console.warn('[customers.deals] failed to decorate items with person/company links', err)\n payload.items = items.map((item: unknown) => {\n if (!item || typeof item !== 'object') return item\n return {\n ...(item as Record<string, unknown>),\n _associations: {\n ok: false,\n reason: err instanceof Error ? err.message : 'unknown',\n },\n }\n })\n }\n },\n },\n})\n\nconst { POST, PUT, DELETE } = crud\n\nexport { POST, PUT, DELETE }\nexport const GET = crud.GET\n\nconst dealAssociationSchema = z.object({\n id: z.string().uuid(),\n label: z.string().nullable(),\n})\n\nconst dealListItemSchema = z\n .object({\n id: z.string().uuid(),\n title: z.string().nullable(),\n description: z.string().nullable().optional(),\n status: z.string().nullable().optional(),\n pipeline_stage: z.string().nullable().optional(),\n pipeline_id: z.string().uuid().nullable().optional(),\n pipeline_stage_id: z.string().uuid().nullable().optional(),\n value_amount: z.number().nullable().optional(),\n value_currency: z.string().nullable().optional(),\n probability: z.number().nullable().optional(),\n expected_close_at: z.string().nullable().optional(),\n owner_user_id: z.string().uuid().nullable().optional(),\n source: z.string().nullable().optional(),\n organization_id: z.string().uuid().nullable().optional(),\n tenant_id: z.string().uuid().nullable().optional(),\n created_at: z.string().nullable().optional(),\n updated_at: z.string().nullable().optional(),\n personIds: z.array(z.string().uuid()).optional(),\n people: z.array(dealAssociationSchema).optional(),\n companyIds: z.array(z.string().uuid()).optional(),\n companies: z.array(dealAssociationSchema).optional(),\n organizationId: z.string().uuid().nullable().optional(),\n tenantId: z.string().uuid().nullable().optional(),\n })\n .passthrough()\n\nconst dealCreateResponseSchema = z.object({\n id: z.string().uuid().nullable(),\n})\n\nexport const openApi = createCustomersCrudOpenApi({\n resourceName: 'Deal',\n querySchema: dealListQuerySchema,\n listResponseSchema: createPagedListResponseSchema(dealListItemSchema),\n create: {\n schema: dealCreateSchema,\n responseSchema: dealCreateResponseSchema,\n description: 'Creates a sales deal, optionally associating people and companies.',\n },\n update: {\n schema: dealUpdateSchema,\n responseSchema: defaultOkResponseSchema,\n description: 'Updates pipeline position, metadata, or associations for an existing deal.',\n },\n del: {\n schema: z.object({ id: z.string().uuid() }),\n responseSchema: defaultOkResponseSchema,\n description: 'Deletes a deal by `id`. The identifier may be provided in the body or query parameters.',\n },\n})\n"],
|
|
5
|
-
"mappings": "AAAA,SAAS,SAAS;AAClB,SAAS,qBAAqB;AAC9B,SAAS,qBAAqB;AAC9B,SAAS,cAAc,wBAAwB,+BAA+B;AAC9E,SAAS,kBAAkB,wBAAwB;AACnD,SAAS,SAAS;AAClB,SAAS,2BAA2B;AACpC,SAAS,+BAA+B;AACxC;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAEP;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,0BAA0B;AACnC,SAAS,qCAAqC;AAC9C,SAAS,yBAAyB;AAClC,SAAS,4BAA4B,+BAA+B;AACpE,SAAS,yBAAyB;AAElC,MAAM,gBAAgB,EAAE,OAAO,CAAC,CAAC,EAAE,YAAY;AAE/C,MAAM,sBAAsB,EAAE,MAAM,CAAC,EAAE,OAAO,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;AACrE,MAAM,oBAAoB,EAAE,WAAW,CAAC,UAAU;AAChD,QAAM,SAAS,wBAAwB,KAAK;AAC5C,SAAO,WAAW,OAAO,QAAQ;AACnC,GAAG,EAAE,QAAQ,CAAC,EAAE,SAAS;AAElB,MAAM,sBAAsB,EAChC,OAAO;AAAA,EACN,MAAM,EAAE,OAAO,OAAO,EAAE,IAAI,CAAC,EAAE,QAAQ,CAAC;AAAA,EACxC,UAAU,EAAE,OAAO,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,GAAG,EAAE,QAAQ,EAAE;AAAA,EACtD,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS;AAAA,EAC/B,QAAQ,EAAE,OAAO,EAAE,SAAS;AAAA,EAC5B,QAAQ,oBAAoB,SAAS;AAAA,EACrC,eAAe,EAAE,OAAO,EAAE,SAAS;AAAA,EACnC,YAAY,oBAAoB,SAAS;AAAA,EACzC,iBAAiB,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,KAAK,GAAG,EAAE,QAAQ,cAAc,CAAC,CAAC,EAAE,SAAS;AAAA,EAClF,aAAa,oBAAoB,SAAS;AAAA,EAC1C,qBAAqB,EAAE,OAAO,EAAE,SAAS;AAAA,EACzC,mBAAmB,EAAE,OAAO,EAAE,SAAS;AAAA,EACvC,SAAS;AAAA,EACT,WAAW;AAAA,EACX,eAAe,oBAAoB,SAAS;AAAA,EAC5C,WAAW,EAAE,OAAO,EAAE,SAAS;AAAA,EAC/B,SAAS,EAAE,KAAK,CAAC,OAAO,MAAM,CAAC,EAAE,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA,EAK1C,gBAAgB,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,SAAS,0BAA0B;AAAA;AAAA;AAAA;AAAA;AAAA,EAKhF,iBAAiB,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,SAAS,2BAA2B;AAAA,EAClF,UAAU,oBAAoB,SAAS;AAAA,EACvC,WAAW,oBAAoB,SAAS;AAC1C,CAAC,EACA,YAAY;AAEf,MAAM,gBAAgB;AAAA,EACpB,KAAK,EAAE,aAAa,MAAM,iBAAiB,CAAC,sBAAsB,EAAE;AAAA,EACpE,MAAM,EAAE,aAAa,MAAM,iBAAiB,CAAC,wBAAwB,EAAE;AAAA,EACvE,KAAK,EAAE,aAAa,MAAM,iBAAiB,CAAC,wBAAwB,EAAE;AAAA,EACtE,QAAQ,EAAE,aAAa,MAAM,iBAAiB,CAAC,wBAAwB,EAAE;AAC3E;AAEO,MAAM,WAAW;AAIxB,SAAS,UAAU,OAA+B;AAChD,MAAI,OAAO,UAAU,SAAU,QAAO;AACtC,QAAM,UAAU,MAAM,KAAK;AAC3B,MAAI,CAAC,QAAQ,OAAQ,QAAO;AAC5B,QAAM,SAAS,EAAE,OAAO,EAAE,KAAK,EAAE,UAAU,OAAO;AAClD,SAAO,OAAO,UAAU,UAAU;AACpC;AAEA,SAAS,oBAAoB,OAA0B;AACrD,QAAM,MAAM,oBAAI,IAAY;AAC5B,QAAM,QAAQ,CAAC,UAAmB;AAChC,QAAI,SAAS,KAAM;AACnB,QAAI,MAAM,QAAQ,KAAK,GAAG;AACxB,YAAM,QAAQ,KAAK;AACnB;AAAA,IACF;AACA,QAAI,OAAO,UAAU,SAAU;AAC/B,UACG,MAAM,GAAG,EACT,IAAI,CAAC,UAAU,MAAM,KAAK,CAAC,EAC3B,OAAO,CAAC,UAAU,MAAM,SAAS,CAAC,EAClC,QAAQ,CAAC,UAAU,IAAI,IAAI,KAAK,CAAC;AAAA,EACtC;AACA,QAAM,KAAK;AACX,SAAO,MAAM,KAAK,GAAG;AACvB;AAEA,SAAS,eAAe,OAA6B;AACnD,MAAI,EAAE,OAAO,UAAU,aAAa,MAAM,KAAK,EAAE,WAAW,EAAG,QAAO;AACtE,QAAM,OAAO,IAAI,KAAK,KAAK;AAC3B,SAAO,OAAO,MAAM,KAAK,QAAQ,CAAC,IAAI,OAAO;AAC/C;AAcA,eAAe,iCACb,IACA,gBACA,UACA,WACA,YACmB;AACnB,MAAI,CAAC,UAAU,UAAU,CAAC,WAAW,OAAQ,QAAO,CAAC;AACrD,QAAM,QAAkB;AAAA,IACtB;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACA,QAAM,SAAiC,CAAC,gBAAgB,QAAQ;AAChE,MAAI,UAAU,QAAQ;AACpB,UAAM,eAAe,UAAU,IAAI,MAAM,GAAG,EAAE,KAAK,GAAG;AACtD,UAAM;AAAA,MACJ,kHAAkH,YAAY;AAAA,IAChI;AACA,WAAO,KAAK,GAAG,SAAS;AAAA,EAC1B;AACA,MAAI,WAAW,QAAQ;AACrB,UAAM,eAAe,WAAW,IAAI,MAAM,GAAG,EAAE,KAAK,GAAG;AACvD,UAAM;AAAA,MACJ,sHAAsH,YAAY;AAAA,IACpI;AACA,WAAO,KAAK,GAAG,UAAU;AAAA,EAC3B;AACA,QAAM,OAAO,MAAM,GAAG,cAAc,EAAE;AAAA,IACpC,uCAAuC,MAAM,KAAK,OAAO,CAAC;AAAA,IAC1D;AAAA,EACF;AACA,SAAO,KAAK,IAAI,CAAC,QAAQ,IAAI,EAAE;AACjC;AAEA,SAAS,sBAAsB,OAA0B;AACvD,QAAM,MAAM,oBAAI,IAAY;AAC5B,QAAM,QAAQ,CAAC,UAAmB;AAChC,QAAI,SAAS,KAAM;AACnB,QAAI,MAAM,QAAQ,KAAK,GAAG;AACxB,YAAM,QAAQ,KAAK;AACnB;AAAA,IACF;AACA,QAAI,OAAO,UAAU,SAAU;AAC/B,UACG,MAAM,GAAG,EACT,IAAI,CAAC,UAAU,MAAM,KAAK,EAAE,YAAY,CAAC,EACzC,OAAO,CAAC,UAAU,aAAa,KAAK,KAAK,CAAC,EAC1C,QAAQ,CAAC,UAAU,IAAI,IAAI,KAAK,CAAC;AAAA,EACtC;AACA,QAAM,KAAK;AACX,SAAO,MAAM,KAAK,GAAG;AACvB;AAEA,SAAS,kBAAkB,QAAkC;AAC3D,QAAM,MAAM,oBAAI,IAAY;AAC5B,SAAO,QAAQ,CAAC,cAAc;AAC5B,QAAI,MAAM,QAAQ,SAAS,GAAG;AAC5B,gBAAU,QAAQ,CAAC,UAAU;AAC3B,cAAMA,UAAS,UAAU,KAAK;AAC9B,YAAIA,QAAQ,KAAI,IAAIA,OAAM;AAAA,MAC5B,CAAC;AACD;AAAA,IACF;AACA,QAAI,OAAO,cAAc,YAAY,UAAU,SAAS,GAAG,GAAG;AAC5D,gBACG,MAAM,GAAG,EACT,IAAI,CAAC,UAAU,MAAM,KAAK,CAAC,EAC3B,QAAQ,CAAC,UAAU;AAClB,cAAMA,UAAS,UAAU,KAAK;AAC9B,YAAIA,QAAQ,KAAI,IAAIA,OAAM;AAAA,MAC5B,CAAC;AACH;AAAA,IACF;AACA,UAAM,SAAS,UAAU,SAAS;AAClC,QAAI,OAAQ,KAAI,IAAI,MAAM;AAAA,EAC5B,CAAC;AACD,SAAO,MAAM,KAAK,GAAG;AACvB;AAEA,eAAsB,qBAAqB,OAAsB,KAA+D;AAC9H,QAAM,qBAAqB,2BAA2B,KAAK;AAC3D,QAAM,UAAmC,CAAC;AAC1C,MAAI,gBAAiC;AAErC,QAAM,eAAe,CAAC,QAAkB;AACtC,QAAI,kBAAkB,MAAM;AAC1B,sBAAgB;AAChB;AAAA,IACF;AACA,UAAM,SAAS,IAAI,IAAI,GAAG;AAC1B,oBAAgB,cAAc,OAAO,CAAC,OAAO,OAAO,IAAI,EAAE,CAAC;AAAA,EAC7D;AAEA,MAAI,MAAM,GAAI,SAAQ,KAAK,EAAE,KAAK,MAAM,GAAG;AAE3C,MAAI,MAAM,QAAQ;AAChB,UAAM,cAAc,MAChB,MAAM,iDAAiD;AAAA,MACrD;AAAA,MACA,OAAO,MAAM;AAAA,MACb,SAAS;AAAA,QACP;AAAA,UACE,YAAY,EAAE,UAAU;AAAA,UACxB,QAAQ;AAAA,YACN;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,IACF,CAAC,IACD;AACJ,QAAI,gBAAgB,QAAQ,YAAY,SAAS,GAAG;AAClD,mBAAa,WAAW;AAAA,IAC1B,WAAW,8BAA8B,GAAG;AAM1C,mBAAa,CAAC,CAAC;AAAA,IACjB,OAAO;AACL,YAAM,gBAAgB,IAAI,kBAAkB,MAAM,MAAM,CAAC;AACzD,cAAQ,MAAM;AAAA,QACZ,EAAE,OAAO,EAAE,QAAQ,cAAc,EAAE;AAAA,QACnC,EAAE,aAAa,EAAE,QAAQ,cAAc,EAAE;AAAA,MAC3C;AAAA,IACF;AAAA,EACF;AAEA,QAAM,aAAa,MAAM,SAAS,oBAAoB,MAAM,MAAM,IAAI,CAAC;AACvE,MAAI,WAAW,SAAS,GAAG;AACzB,YAAQ,SAAS,WAAW,WAAW,IAAI,EAAE,KAAK,WAAW,CAAC,EAAE,IAAI,EAAE,KAAK,WAAW;AAAA,EACxF;AAEA,MAAI,MAAM,eAAe;AACvB,YAAQ,iBAAiB,EAAE,KAAK,MAAM,cAAc;AAAA,EACtD;AAEA,QAAM,cAAc,MAAM,aAAa,kBAAkB,CAAC,MAAM,UAAU,CAAC,IAAI,CAAC;AAChF,MAAI,YAAY,SAAS,GAAG;AAC1B,YAAQ,cAAc,YAAY,WAAW,IAAI,EAAE,KAAK,YAAY,CAAC,EAAE,IAAI,EAAE,KAAK,YAAY;AAAA,EAChG;AAEA,MAAI,MAAM,oBAAoB,gBAAgB;AAC5C,YAAQ,oBAAoB,EAAE,KAAK,KAAK;AAAA,EAC1C,WAAW,MAAM,iBAAiB;AAChC,YAAQ,oBAAoB,EAAE,KAAK,MAAM,gBAAgB;AAAA,EAC3D;AAEA,QAAM,eAAe,MAAM,cAAc,kBAAkB,CAAC,MAAM,WAAW,CAAC,IAAI,CAAC;AACnF,MAAI,aAAa,SAAS,GAAG;AAC3B,YAAQ,gBACN,aAAa,WAAW,IAAI,EAAE,KAAK,aAAa,CAAC,EAAE,IAAI,EAAE,KAAK,aAAa;AAAA,EAC/E;AAKA,QAAM,gBAAgB,MAAM,gBAAgB,sBAAsB,MAAM,aAAa,IAAI,CAAC;AAC1F,MAAI,cAAc,SAAS,GAAG;AAC5B,YAAQ,iBACN,cAAc,WAAW,IAAI,EAAE,KAAK,cAAc,CAAC,EAAE,IAAI,EAAE,KAAK,cAAc;AAAA,EAClF;AAEA,QAAM,oBAAoB,eAAe,MAAM,mBAAmB;AAClE,QAAM,kBAAkB,eAAe,MAAM,iBAAiB;AAC9D,MAAI,qBAAqB,iBAAiB;AACxC,UAAM,QAA8B,CAAC;AACrC,QAAI,kBAAmB,OAAM,OAAO;AACpC,QAAI,gBAAiB,OAAM,OAAO;AAClC,YAAQ,oBAAoB;AAAA,EAC9B;AAEA,MAAI,MAAM,WAAW;AACnB,UAAM,QAAQ,oBAAI,KAAK;AACvB,UAAM,SAAS,GAAG,GAAG,GAAG,CAAC;AACzB,QAAI,WAAW,WAAW,GAAG;AAC3B,cAAQ,SAAS,EAAE,KAAK,OAAO;AAAA,IACjC;AACA,UAAM,gBACJ,QAAQ,qBAAqB,OAAO,QAAQ,sBAAsB,WAC7D,QAAQ,oBACT,CAAC;AACP,kBAAc,MAAM;AACpB,YAAQ,oBAAoB;AAAA,EAC9B;AAEA,MAAI,MAAM,WAAW,KAAK;AACxB,UAAM,WAAW,IAAI,MAAM;AAI3B,UAAM,iBAAiB,IAAI,MAAM;AACjC,QAAI,OAAO,aAAa,YAAY,OAAO,mBAAmB,UAAU;AACtE,YAAM,KAAK,IAAI,UAAU,QAAuB,IAAI;AACpD,YAAM,WAAW,MAAM,kBAAkB,IAAI,gBAAgB,QAAQ;AACrE,mBAAa,QAAQ;AAAA,IACvB;AAAA,EACF;AAMA,QAAM,MAAM,KAAK,UAAU,IAAI,IAAI,IAAI,QAAQ,GAAG,IAAI;AACtD,QAAM,mBAA8B,CAAC,MAAM,UAAU,MAAM,cAAc;AACzE,QAAM,oBAA+B,CAAC,MAAM,WAAW,MAAM,eAAe;AAC5E,MAAI,KAAK;AACP,qBAAiB,KAAK,IAAI,aAAa,OAAO,UAAU,CAAC;AACzD,qBAAiB,KAAK,IAAI,aAAa,OAAO,gBAAgB,CAAC;AAC/D,sBAAkB,KAAK,IAAI,aAAa,OAAO,WAAW,CAAC;AAC3D,sBAAkB,KAAK,IAAI,aAAa,OAAO,iBAAiB,CAAC;AAAA,EACnE;AACA,QAAM,oBAAoB,kBAAkB,gBAAgB;AAC5D,QAAM,qBAAqB,kBAAkB,iBAAiB;AAC9D,OAAK,kBAAkB,SAAS,KAAK,mBAAmB,SAAS,MAAM,KAAK;AAC1E,UAAM,WAAW,IAAI,MAAM;AAI3B,UAAM,iBAAiB,IAAI,MAAM;AACjC,QAAI,OAAO,aAAa,YAAY,OAAO,mBAAmB,UAAU;AACtE,YAAM,KAAK,IAAI,UAAU,QAAuB,IAAI;AACpD,YAAM,aAAa,MAAM;AAAA,QACvB;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAEA,mBAAa,UAAU;AAAA,IACzB;AAAA,EACF;AAEA,MAAI,OAAO,oBAAoB;AAC7B,UAAM,kBAAkB,wBAAwB,EAAE,GAAG,QAAQ,GAAG,kBAAkB;AAClF,UAAM,aAAa,MAAM,qCAAqC;AAAA,MAC5D;AAAA,MACA,UAAU,EAAE,UAAU;AAAA,MACtB,SAAS;AAAA,IACX,CAAC;AACD,QAAI,eAAe,MAAM;AACvB,mBAAa,UAAU;AAAA,IACzB;AAAA,EACF;AAEA,MAAI,kBAAkB,MAAM;AAC1B,6BAAyB,SAAS,aAAa;AAAA,EACjD;AAEA,SAAO;AACT;AAEA,MAAM,OAAO,cAA+C;AAAA,EAC1D,UAAU;AAAA,EACV,KAAK;AAAA,IACH,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,UAAU;AAAA,IACV,aAAa;AAAA,IACb,iBAAiB;AAAA,EACnB;AAAA,EACA,SAAS;AAAA,IACP,YAAY,EAAE,UAAU;AAAA,EAC1B;AAAA,EACA,WAAW,EAAE,UAAU,iBAAiB;AAAA,EACxC,MAAM;AAAA,IACJ,QAAQ;AAAA,IACR,UAAU,EAAE,UAAU;AAAA,IACtB,QAAQ;AAAA,MACN;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,IACA,sBAAsB;AAAA,MACpB,WAAW,EAAE,UAAU;AAAA,IACzB;AAAA,IACA,cAAc;AAAA,MACZ,WAAW;AAAA,MACX,WAAW;AAAA,MACX,OAAO;AAAA,MACP,OAAO;AAAA,MACP,aAAa;AAAA,MACb,iBAAiB;AAAA,IACnB;AAAA,IACA,cAAc;AAAA,EAChB;AAAA,EACA,SAAS;AAAA,IACP,QAAQ;AAAA,MACN,WAAW;AAAA,MACX,QAAQ;AAAA,MACR,UAAU,OAAO,EAAE,KAAK,IAAI,MAAM;AAChC,cAAM,EAAE,UAAU,IAAI,MAAM,oBAAoB;AAChD,eAAO,wBAAwB,kBAAkB,OAAO,CAAC,GAAG,KAAK,SAAS;AAAA,MAC5E;AAAA,MACA,UAAU,CAAC,EAAE,OAAO,OAAO,EAAE,IAAI,QAAQ,UAAU,QAAQ,MAAM,KAAK;AAAA,MACtE,QAAQ;AAAA,IACV;AAAA,IACA,QAAQ;AAAA,MACN,WAAW;AAAA,MACX,QAAQ;AAAA,MACR,UAAU,OAAO,EAAE,KAAK,IAAI,MAAM;AAChC,cAAM,EAAE,UAAU,IAAI,MAAM,oBAAoB;AAChD,eAAO,wBAAwB,kBAAkB,OAAO,CAAC,GAAG,KAAK,SAAS;AAAA,MAC5E;AAAA,MACA,UAAU,OAAO,EAAE,IAAI,KAAK;AAAA,IAC9B;AAAA,IACA,QAAQ;AAAA,MACN,WAAW;AAAA,MACX,QAAQ;AAAA,MACR,UAAU,OAAO,EAAE,QAAQ,IAAI,MAAM;AACnC,cAAM,EAAE,UAAU,IAAI,MAAM,oBAAoB;AAChD,cAAM,KACJ,QAAQ,MAAM,MACd,QAAQ,MACR,QAAQ,OAAO,OACd,IAAI,UAAU,IAAI,IAAI,IAAI,QAAQ,GAAG,EAAE,aAAa,IAAI,IAAI,IAAI;AACnE,YAAI,CAAC,GAAI,OAAM,IAAI,cAAc,KAAK,EAAE,OAAO,UAAU,kCAAkC,qBAAqB,EAAE,CAAC;AACnH,eAAO,EAAE,GAAG;AAAA,MACd;AAAA,MACA,UAAU,OAAO,EAAE,IAAI,KAAK;AAAA,IAC9B;AAAA,EACF;AAAA,EACA,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA,IAKL,WAAW,OAAO,SAAS,QAAQ;AACjC,YAAM,QAAQ,MAAM,QAAQ,QAAQ,KAAK,IAAI,QAAQ,QAAQ,CAAC;AAC9D,UAAI,CAAC,MAAM,OAAQ;AACnB,YAAM,cAAe,MAAM,CAAC,KAAK,CAAC;AAClC,YAAM,cAAc,YAAY,YAAY,YAAY;AACxD,YAAM,oBAAoB,OAAO,gBAAgB,YAAY,YAAY,KAAK,EAAE,SAAS,cAAc,SAAS,IAAI,MAAM,YAAY;AACtI,YAAM,WAAW,YAAY,kBAAkB,YAAY;AAC3D,YAAM,0BAA0B,OAAO,aAAa,YAAY,SAAS,KAAK,EAAE,SAAS,WAAW,SAAS,IAAI,MAAM,SAAS;AAChI,YAAM,MAAM,MACT,IAAI,CAAC,SAAkB;AACtB,YAAI,CAAC,QAAQ,OAAO,SAAS,SAAU,QAAO;AAC9C,cAAM,YAAa,KAAiC;AACpD,eAAO,OAAO,cAAc,YAAY,UAAU,KAAK,EAAE,SAAS,YAAY;AAAA,MAChF,CAAC,EACA,OAAO,CAAC,UAA0C,OAAO,UAAU,YAAY,MAAM,SAAS,CAAC;AAClG,UAAI,CAAC,IAAI,QAAQ;AACf,gBAAQ,QAAQ,CAAC;AACjB,gBAAQ,QAAQ;AAChB;AAAA,MACF;AACA,UAAI;AACF,cAAM,KAAM,IAAI,UAAU,QAAQ,IAAI;AACtC,cAAM,CAAC,gBAAgB,eAAe,IAAI,MAAM,QAAQ,IAAI;AAAA,UAC1D;AAAA,YACE;AAAA,YACA;AAAA,YACA,EAAE,MAAM,EAAE,KAAK,IAAI,EAAE;AAAA,YACrB,EAAE,UAAU,CAAC,QAAQ,EAAE;AAAA,YACvB,EAAE,UAAU,kBAAkB,gBAAgB,uBAAuB;AAAA,UACvE;AAAA,UACA;AAAA,YACE;AAAA,YACA;AAAA,YACA,EAAE,MAAM,EAAE,KAAK,IAAI,EAAE;AAAA,YACrB,EAAE,UAAU,CAAC,SAAS,EAAE;AAAA,YACxB,EAAE,UAAU,kBAAkB,gBAAgB,uBAAuB;AAAA,UACvE;AAAA,QACF,CAAC;AAED,cAAM,oBAAoB,oBAAI,IAA6C;AAC3E,uBAAe,QAAQ,CAAC,SAAS;AAC/B,gBAAM,OAAO,KAAK;AAClB,gBAAM,aAAa,QAAQ,OAAO,SAAS,WAAW,OAA6C;AACnG,gBAAM,SAAS,OAAO,SAAS,WAAW,OACtC,cAAc,OAAO,WAAW,OAAO,WAAW,WAAW,KAC7D;AACJ,cAAI,CAAC,OAAQ;AACb,gBAAM,YAAY,KAAK;AACvB,gBAAM,eAAe,aAAa,OAAO,cAAc,WAAW,YAAkD;AACpH,gBAAM,WAAW,OAAO,cAAc,WAAW,YAC7C,gBAAgB,OAAO,aAAa,OAAO,WAAW,aAAa,KACnE;AACJ,cAAI,CAAC,SAAU;AACf,gBAAM,QAAQ,gBAAgB,OAAO,aAAa,gBAAgB,WAC9D,aAAa,cACb;AACJ,gBAAM,SAAS,kBAAkB,IAAI,MAAM,KAAK,CAAC;AACjD,cAAI,CAAC,OAAO,KAAK,CAAC,UAAU,MAAM,OAAO,QAAQ,GAAG;AAClD,mBAAO,KAAK,EAAE,IAAI,UAAU,MAAM,CAAC;AACnC,8BAAkB,IAAI,QAAQ,MAAM;AAAA,UACtC;AAAA,QACF,CAAC;AAED,cAAM,qBAAqB,oBAAI,IAA6C;AAC5E,wBAAgB,QAAQ,CAAC,SAAS;AAChC,gBAAM,OAAO,KAAK;AAClB,gBAAM,aAAa,QAAQ,OAAO,SAAS,WAAW,OAA6C;AACnG,gBAAM,SAAS,OAAO,SAAS,WAAW,OACtC,cAAc,OAAO,WAAW,OAAO,WAAW,WAAW,KAC7D;AACJ,cAAI,CAAC,OAAQ;AACb,gBAAM,aAAa,KAAK;AACxB,gBAAM,gBAAgB,cAAc,OAAO,eAAe,WAAW,aAAmD;AACxH,gBAAM,YAAY,OAAO,eAAe,WAAW,aAC/C,iBAAiB,OAAO,cAAc,OAAO,WAAW,cAAc,KACtE;AACJ,cAAI,CAAC,UAAW;AAChB,gBAAM,QAAQ,iBAAiB,OAAO,cAAc,gBAAgB,WAChE,cAAc,cACd;AACJ,gBAAM,SAAS,mBAAmB,IAAI,MAAM,KAAK,CAAC;AAClD,cAAI,CAAC,OAAO,KAAK,CAAC,UAAU,MAAM,OAAO,SAAS,GAAG;AACnD,mBAAO,KAAK,EAAE,IAAI,WAAW,MAAM,CAAC;AACpC,+BAAmB,IAAI,QAAQ,MAAM;AAAA,UACvC;AAAA,QACF,CAAC;AAED,cAAM,gBAAgB,MACnB,IAAI,CAAC,SAAkB;AACtB,cAAI,CAAC,QAAQ,OAAO,SAAS,SAAU,QAAO;AAC9C,gBAAM,OAAO;AACb,gBAAM,YAAY,OAAO,KAAK,OAAO,WAAW,KAAK,KAAK;AAC1D,cAAI,CAAC,aAAa,CAAC,UAAU,KAAK,EAAE,OAAQ,QAAO;AACnD,gBAAM,SAAS,kBAAkB,IAAI,SAAS,KAAK,CAAC;AACpD,gBAAM,YAAY,mBAAmB,IAAI,SAAS,KAAK,CAAC;AACxD,gBAAMC,eACJ,OAAO,KAAK,aAAa,WACrB,KAAK,WACL,OAAO,KAAK,cAAc,WACxB,KAAK,YACL;AACR,gBAAM,oBACJ,OAAO,KAAK,mBAAmB,WAC3B,KAAK,iBACL,OAAO,KAAK,oBAAoB,WAC9B,KAAK,kBACL;AACR,gBAAM,WAAWA,gBAAeA,aAAY,KAAK,EAAE,SAASA,aAAY,KAAK,IAAI;AACjF,gBAAM,iBAAiB,qBAAqB,kBAAkB,KAAK,EAAE,SAAS,kBAAkB,KAAK,IAAI;AACzG,iBAAO;AAAA,YACL,GAAG;AAAA,YACH,WAAW,OAAO,IAAI,CAAC,UAAU,MAAM,EAAE;AAAA,YACzC;AAAA,YACA,YAAY,UAAU,IAAI,CAAC,UAAU,MAAM,EAAE;AAAA,YAC7C;AAAA,YACA;AAAA,YACA;AAAA,UACF;AAAA,QACF,CAAC,EACA;AAAA,UACC,CAAC,SAA0E,SAAS;AAAA,QACtF;AAEF,gBAAQ,QAAQ;AAAA,MAClB,SAAS,KAAK;AAKZ,gBAAQ,KAAK,wEAAwE,GAAG;AACxF,gBAAQ,QAAQ,MAAM,IAAI,CAAC,SAAkB;AAC3C,cAAI,CAAC,QAAQ,OAAO,SAAS,SAAU,QAAO;AAC9C,iBAAO;AAAA,YACL,GAAI;AAAA,YACJ,eAAe;AAAA,cACb,IAAI;AAAA,cACJ,QAAQ,eAAe,QAAQ,IAAI,UAAU;AAAA,YAC/C;AAAA,UACF;AAAA,QACF,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AACF,CAAC;AAED,MAAM,EAAE,MAAM,KAAK,OAAO,IAAI;AAGvB,MAAM,MAAM,KAAK;AAExB,MAAM,wBAAwB,EAAE,OAAO;AAAA,EACrC,IAAI,EAAE,OAAO,EAAE,KAAK;AAAA,EACpB,OAAO,EAAE,OAAO,EAAE,SAAS;AAC7B,CAAC;AAED,MAAM,qBAAqB,EACxB,OAAO;AAAA,EACN,IAAI,EAAE,OAAO,EAAE,KAAK;AAAA,EACpB,OAAO,EAAE,OAAO,EAAE,SAAS;AAAA,EAC3B,aAAa,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,EAC5C,QAAQ,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,EACvC,gBAAgB,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,EAC/C,aAAa,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,SAAS;AAAA,EACnD,mBAAmB,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,SAAS;AAAA,EACzD,cAAc,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,EAC7C,gBAAgB,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,EAC/C,aAAa,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,EAC5C,mBAAmB,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,EAClD,eAAe,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,SAAS;AAAA,EACrD,QAAQ,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,EACvC,iBAAiB,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,SAAS;AAAA,EACvD,WAAW,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,SAAS;AAAA,EACjD,YAAY,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,EAC3C,YAAY,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,EAC3C,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,CAAC,EAAE,SAAS;AAAA,EAC/C,QAAQ,EAAE,MAAM,qBAAqB,EAAE,SAAS;AAAA,EAChD,YAAY,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,CAAC,EAAE,SAAS;AAAA,EAChD,WAAW,EAAE,MAAM,qBAAqB,EAAE,SAAS;AAAA,EACnD,gBAAgB,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,SAAS;AAAA,EACtD,UAAU,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,SAAS;AAClD,CAAC,EACA,YAAY;AAEf,MAAM,2BAA2B,EAAE,OAAO;AAAA,EACxC,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS;AACjC,CAAC;AAEM,MAAM,UAAU,2BAA2B;AAAA,EAChD,cAAc;AAAA,EACd,aAAa;AAAA,EACb,oBAAoB,8BAA8B,kBAAkB;AAAA,EACpE,QAAQ;AAAA,IACN,QAAQ;AAAA,IACR,gBAAgB;AAAA,IAChB,aAAa;AAAA,EACf;AAAA,EACA,QAAQ;AAAA,IACN,QAAQ;AAAA,IACR,gBAAgB;AAAA,IAChB,aAAa;AAAA,EACf;AAAA,EACA,KAAK;AAAA,IACH,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;AAAA,IAC1C,gBAAgB;AAAA,IAChB,aAAa;AAAA,EACf;AACF,CAAC;",
|
|
4
|
+
"sourcesContent": ["import { z } from 'zod'\nimport { makeCrudRoute } from '@open-mercato/shared/lib/crud/factory'\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { CustomerDeal, CustomerDealPersonLink, CustomerDealCompanyLink } from '../../data/entities'\nimport { dealCreateSchema, dealUpdateSchema } from '../../data/validators'\nimport { E } from '#generated/entities.ids.generated'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { parseBooleanFromUnknown } from '@open-mercato/shared/lib/boolean'\nimport {\n applyEntityIdRestriction,\n findMatchingEntityIdsWithQueryEngine,\n findMatchingEntityIdsBySearchTokensAcrossSources,\n parseScopedCommandInput,\n} from '../utils'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport {\n createCustomersCrudOpenApi,\n createPagedListResponseSchema,\n defaultOkResponseSchema,\n} from '../openapi'\nimport { findWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { isTenantDataEncryptionEnabled } from '@open-mercato/shared/lib/encryption/toggles'\nimport { escapeLikePattern } from '@open-mercato/shared/lib/db/escapeLikePattern'\nimport { consumeAdvancedFilterState, mergeAdvancedFilterTree } from '@open-mercato/shared/lib/crud/advanced-filter-integration'\nimport { fetchStuckDealIds } from '../../lib/stuckDeals'\n\nconst rawBodySchema = z.object({}).passthrough()\n\nconst stringOrStringArray = z.union([z.string(), z.array(z.string())])\nconst OPEN_DEAL_STATUSES = ['open', 'in_progress'] as const\nconst booleanQueryParam = z.preprocess((value) => {\n const parsed = parseBooleanFromUnknown(value)\n return parsed === null ? value : parsed\n}, z.boolean()).optional()\n\nexport const dealListQuerySchema = z\n .object({\n page: z.coerce.number().min(1).default(1),\n pageSize: z.coerce.number().min(1).max(100).default(50),\n id: z.string().uuid().optional(),\n search: z.string().optional(),\n status: stringOrStringArray.optional(),\n pipelineStage: z.string().optional(),\n pipelineId: stringOrStringArray.optional(),\n pipelineStageId: z.union([z.string().uuid(), z.literal('__unassigned')]).optional(),\n ownerUserId: stringOrStringArray.optional(),\n expectedCloseAtFrom: z.string().optional(),\n expectedCloseAtTo: z.string().optional(),\n isStuck: booleanQueryParam,\n isOverdue: booleanQueryParam,\n needsAttention: booleanQueryParam,\n valueCurrency: stringOrStringArray.optional(),\n sortField: z.string().optional(),\n sortDir: z.enum(['asc', 'desc']).optional(),\n /**\n * @deprecated Use `personId` instead. The `personEntityId` alias is kept for backward\n * compatibility with older clients and will be removed in a future minor release.\n */\n personEntityId: z.string().uuid().optional().describe('Deprecated; use personId'),\n /**\n * @deprecated Use `companyId` instead. The `companyEntityId` alias is kept for backward\n * compatibility with older clients and will be removed in a future minor release.\n */\n companyEntityId: z.string().uuid().optional().describe('Deprecated; use companyId'),\n personId: stringOrStringArray.optional(),\n companyId: stringOrStringArray.optional(),\n })\n .passthrough()\n\nconst routeMetadata = {\n GET: { requireAuth: true, requireFeatures: ['customers.deals.view'] },\n POST: { requireAuth: true, requireFeatures: ['customers.deals.manage'] },\n PUT: { requireAuth: true, requireFeatures: ['customers.deals.manage'] },\n DELETE: { requireAuth: true, requireFeatures: ['customers.deals.manage'] },\n}\n\nexport const metadata = routeMetadata\n\nexport type DealListQuery = z.infer<typeof dealListQuerySchema>\n\nfunction parseUuid(value: unknown): string | null {\n if (typeof value !== 'string') return null\n const trimmed = value.trim()\n if (!trimmed.length) return null\n const result = z.string().uuid().safeParse(trimmed)\n return result.success ? trimmed : null\n}\n\nfunction normalizeStringList(value: unknown): string[] {\n const set = new Set<string>()\n const visit = (entry: unknown) => {\n if (entry == null) return\n if (Array.isArray(entry)) {\n entry.forEach(visit)\n return\n }\n if (typeof entry !== 'string') return\n entry\n .split(',')\n .map((token) => token.trim())\n .filter((token) => token.length > 0)\n .forEach((token) => set.add(token))\n }\n visit(value)\n return Array.from(set)\n}\n\nfunction parseDateInput(value: unknown): Date | null {\n if (!(typeof value === 'string') || value.trim().length === 0) return null\n const date = new Date(value)\n return Number.isNaN(date.getTime()) ? null : date\n}\n\n/**\n * Pre-pagination ID narrowing for person/company filters.\n *\n * Mirrors the SQL the aggregate route uses (`EXISTS ... IN (...)`) so the list endpoint\n * and the lane-header aggregate return a consistent set of deals for the same filters.\n * Semantics: OR within a category (any selected person/company matches), AND across\n * categories (deal must match at least one person AND at least one company when both\n * filter lists are provided).\n *\n * Returns the matched deal IDs (UUIDs). An empty array means \"no deals match\" \u2014 callers\n * must intersect with `restrictedIds` (which will collapse the list to zero).\n */\nasync function fetchDealIdsMatchingAssociations(\n em: EntityManager,\n organizationId: string,\n tenantId: string,\n personIds: string[],\n companyIds: string[],\n): Promise<string[]> {\n if (!personIds.length && !companyIds.length) return []\n const where: string[] = [\n 'organization_id = ?',\n 'tenant_id = ?',\n 'deleted_at IS NULL',\n ]\n const values: Array<string | number> = [organizationId, tenantId]\n if (personIds.length) {\n const placeholders = personIds.map(() => '?').join(',')\n where.push(\n `EXISTS (SELECT 1 FROM customer_deal_people dp WHERE dp.deal_id = customer_deals.id AND dp.person_entity_id IN (${placeholders}))`,\n )\n values.push(...personIds)\n }\n if (companyIds.length) {\n const placeholders = companyIds.map(() => '?').join(',')\n where.push(\n `EXISTS (SELECT 1 FROM customer_deal_companies dc WHERE dc.deal_id = customer_deals.id AND dc.company_entity_id IN (${placeholders}))`,\n )\n values.push(...companyIds)\n }\n const rows = await em.getConnection().execute<Array<{ id: string }>>(\n `SELECT id FROM customer_deals WHERE ${where.join(' AND ')}`,\n values,\n )\n return rows.map((row) => row.id)\n}\n\nasync function fetchNeedAttentionDealIds(\n em: EntityManager,\n organizationId: string,\n tenantId: string,\n): Promise<string[]> {\n const connection = em.getConnection()\n const overdueRows = await connection.execute<Array<{ id: string }>>(\n `SELECT id FROM customer_deals\n WHERE organization_id = ?\n AND tenant_id = ?\n AND deleted_at IS NULL\n AND status = 'open'\n AND expected_close_at IS NOT NULL\n AND expected_close_at < CURRENT_DATE`,\n [organizationId, tenantId],\n )\n\n const attentionIds = new Set(overdueRows.map((row) => row.id))\n const stuckIds = await fetchStuckDealIds(em, organizationId, tenantId)\n if (stuckIds.length > 0) {\n const idPlaceholders = stuckIds.map(() => '?').join(',')\n const statusPlaceholders = OPEN_DEAL_STATUSES.map(() => '?').join(',')\n const openStuckRows = await connection.execute<Array<{ id: string }>>(\n `SELECT id FROM customer_deals\n WHERE organization_id = ?\n AND tenant_id = ?\n AND deleted_at IS NULL\n AND status IN (${statusPlaceholders})\n AND id IN (${idPlaceholders})`,\n [organizationId, tenantId, ...OPEN_DEAL_STATUSES, ...stuckIds],\n )\n for (const row of openStuckRows) attentionIds.add(row.id)\n }\n\n return Array.from(attentionIds)\n}\n\nfunction normalizeCurrencyList(value: unknown): string[] {\n const set = new Set<string>()\n const visit = (entry: unknown) => {\n if (entry == null) return\n if (Array.isArray(entry)) {\n entry.forEach(visit)\n return\n }\n if (typeof entry !== 'string') return\n entry\n .split(',')\n .map((token) => token.trim().toUpperCase())\n .filter((token) => /^[A-Z]{3}$/.test(token))\n .forEach((token) => set.add(token))\n }\n visit(value)\n return Array.from(set)\n}\n\nfunction normalizeUuidList(values: Array<unknown>): string[] {\n const set = new Set<string>()\n values.forEach((candidate) => {\n if (Array.isArray(candidate)) {\n candidate.forEach((entry) => {\n const parsed = parseUuid(entry)\n if (parsed) set.add(parsed)\n })\n return\n }\n if (typeof candidate === 'string' && candidate.includes(',')) {\n candidate\n .split(',')\n .map((entry) => entry.trim())\n .forEach((entry) => {\n const parsed = parseUuid(entry)\n if (parsed) set.add(parsed)\n })\n return\n }\n const parsed = parseUuid(candidate)\n if (parsed) set.add(parsed)\n })\n return Array.from(set)\n}\n\nexport async function buildDealListFilters(query: DealListQuery, ctx?: import('@open-mercato/shared/lib/crud/factory').CrudCtx) {\n const advancedFilterTree = consumeAdvancedFilterState(query)\n const filters: Record<string, unknown> = {}\n let restrictedIds: string[] | null = null\n\n const intersectIds = (ids: string[]) => {\n if (restrictedIds === null) {\n restrictedIds = ids\n return\n }\n const lookup = new Set(ids)\n restrictedIds = restrictedIds.filter((id) => lookup.has(id))\n }\n\n if (query.id) filters.id = { $eq: query.id }\n\n if (query.search) {\n const matchingIds = ctx\n ? await findMatchingEntityIdsBySearchTokensAcrossSources({\n ctx,\n query: query.search,\n sources: [\n {\n entityType: E.customers.customer_deal,\n fields: [\n 'title',\n 'description',\n 'status',\n 'pipeline_stage',\n 'source',\n 'value_amount',\n 'value_currency',\n 'cf:competitive_risk',\n 'cf:implementation_complexity',\n ],\n },\n ],\n })\n : null\n if (matchingIds !== null && matchingIds.length > 0) {\n intersectIds(matchingIds)\n } else if (isTenantDataEncryptionEnabled()) {\n // `customers:customer_deal.title` and `.description` are encrypted at rest\n // (see `encryption.ts`). The `$ilike` fallback below would silently match\n // nothing on the ciphertext and produce empty pages without disclosing why \u2014\n // collapse the result to \"no matches\" instead so the kanban + list views\n // agree, and lane aggregates stay consistent with the list endpoint.\n intersectIds([])\n } else {\n const searchPattern = `%${escapeLikePattern(query.search)}%`\n filters.$or = [\n { title: { $ilike: searchPattern } },\n { description: { $ilike: searchPattern } },\n ]\n }\n }\n\n const statusList = query.status ? normalizeStringList(query.status) : []\n if (statusList.length > 0) {\n filters.status = statusList.length === 1 ? { $eq: statusList[0] } : { $in: statusList }\n }\n\n if (query.pipelineStage) {\n filters.pipeline_stage = { $eq: query.pipelineStage }\n }\n\n const pipelineIds = query.pipelineId ? normalizeUuidList([query.pipelineId]) : []\n if (pipelineIds.length > 0) {\n filters.pipeline_id = pipelineIds.length === 1 ? { $eq: pipelineIds[0] } : { $in: pipelineIds }\n }\n\n if (query.pipelineStageId === '__unassigned') {\n filters.pipeline_stage_id = { $eq: null }\n } else if (query.pipelineStageId) {\n filters.pipeline_stage_id = { $eq: query.pipelineStageId }\n }\n\n const ownerUserIds = query.ownerUserId ? normalizeUuidList([query.ownerUserId]) : []\n if (ownerUserIds.length > 0) {\n filters.owner_user_id =\n ownerUserIds.length === 1 ? { $eq: ownerUserIds[0] } : { $in: ownerUserIds }\n }\n\n // Currency filter (additive; sourced by the kanban Currency popover). Codes are\n // normalised to upper-case so `usd` / `USD` / mixed-case query params all hit the same\n // stored value (the deals API persists `value_currency` upper-case already).\n const currencyCodes = query.valueCurrency ? normalizeCurrencyList(query.valueCurrency) : []\n if (currencyCodes.length > 0) {\n filters.value_currency =\n currencyCodes.length === 1 ? { $eq: currencyCodes[0] } : { $in: currencyCodes }\n }\n\n const expectedCloseFrom = parseDateInput(query.expectedCloseAtFrom)\n const expectedCloseTo = parseDateInput(query.expectedCloseAtTo)\n if (expectedCloseFrom || expectedCloseTo) {\n const range: Record<string, Date> = {}\n if (expectedCloseFrom) range.$gte = expectedCloseFrom\n if (expectedCloseTo) range.$lte = expectedCloseTo\n filters.expected_close_at = range\n }\n\n if (query.isOverdue && !query.needsAttention) {\n const today = new Date()\n today.setHours(0, 0, 0, 0)\n if (statusList.length === 0) {\n filters.status = { $eq: 'open' }\n }\n const existingRange =\n filters.expected_close_at && typeof filters.expected_close_at === 'object'\n ? (filters.expected_close_at as Record<string, Date>)\n : {}\n existingRange.$lt = today\n filters.expected_close_at = existingRange\n }\n\n if (query.isStuck && !query.needsAttention && ctx) {\n const tenantId = ctx.auth?.tenantId\n // CrudCtx.auth carries `orgId` (not `organizationId`). The previous code referenced\n // `organizationId` which is always `undefined`, so the typeof check below silently\n // skipped the entire isStuck branch \u2014 `?isStuck=true` was a no-op on this endpoint.\n const organizationId = ctx.auth?.orgId\n if (typeof tenantId === 'string' && typeof organizationId === 'string') {\n const em = ctx.container.resolve<EntityManager>('em')\n const stuckIds = await fetchStuckDealIds(em, organizationId, tenantId)\n intersectIds(stuckIds)\n }\n }\n\n if (query.needsAttention && ctx) {\n const tenantId = ctx.auth?.tenantId\n const organizationId = ctx.auth?.orgId\n if (typeof tenantId === 'string' && typeof organizationId === 'string') {\n const em = ctx.container.resolve<EntityManager>('em')\n const attentionIds = await fetchNeedAttentionDealIds(em, organizationId, tenantId)\n intersectIds(attentionIds)\n }\n }\n\n // Pre-pagination association filter. Must run on the FULL dataset (before pagination),\n // otherwise matching deals on later pages disappear and `total` would be wrong. Read the\n // raw URL too so legacy `?personEntityId=` / `?companyEntityId=` keep working alongside the\n // canonical `?personId=` / `?companyId=`.\n const url = ctx?.request ? new URL(ctx.request.url) : null\n const personCandidates: unknown[] = [query.personId, query.personEntityId]\n const companyCandidates: unknown[] = [query.companyId, query.companyEntityId]\n if (url) {\n personCandidates.push(url.searchParams.getAll('personId'))\n personCandidates.push(url.searchParams.getAll('personEntityId'))\n companyCandidates.push(url.searchParams.getAll('companyId'))\n companyCandidates.push(url.searchParams.getAll('companyEntityId'))\n }\n const selectedPersonIds = normalizeUuidList(personCandidates)\n const selectedCompanyIds = normalizeUuidList(companyCandidates)\n if ((selectedPersonIds.length > 0 || selectedCompanyIds.length > 0) && ctx) {\n const tenantId = ctx.auth?.tenantId\n // `ctx.auth` exposes `orgId` (see AuthContext in @open-mercato/shared/lib/auth/server).\n // Read it under the correct key \u2014 the previous code's `organizationId` would always be\n // `undefined`, silently disabling association filtering on the deals list endpoint.\n const organizationId = ctx.auth?.orgId\n if (typeof tenantId === 'string' && typeof organizationId === 'string') {\n const em = ctx.container.resolve<EntityManager>('em')\n const matchedIds = await fetchDealIdsMatchingAssociations(\n em,\n organizationId,\n tenantId,\n selectedPersonIds,\n selectedCompanyIds,\n )\n // intersectIds with empty array \u2192 no rows; collapses the page to zero, total stays correct.\n intersectIds(matchedIds)\n }\n }\n\n if (ctx && advancedFilterTree) {\n const advancedFilters = mergeAdvancedFilterTree({ ...filters }, advancedFilterTree)\n const matchedIds = await findMatchingEntityIdsWithQueryEngine({\n ctx,\n entityId: E.customers.customer_deal,\n filters: advancedFilters,\n })\n if (matchedIds !== null) {\n intersectIds(matchedIds)\n }\n }\n\n if (restrictedIds !== null) {\n applyEntityIdRestriction(filters, restrictedIds)\n }\n\n return filters\n}\n\nconst crud = makeCrudRoute<unknown, unknown, DealListQuery>({\n metadata: routeMetadata,\n orm: {\n entity: CustomerDeal,\n idField: 'id',\n orgField: 'organizationId',\n tenantField: 'tenantId',\n softDeleteField: 'deletedAt',\n },\n indexer: {\n entityType: E.customers.customer_deal,\n },\n enrichers: { entityId: 'customers.deal' },\n list: {\n schema: dealListQuerySchema,\n entityId: E.customers.customer_deal,\n fields: [\n 'id',\n 'title',\n 'description',\n 'status',\n 'pipeline_stage',\n 'pipeline_id',\n 'pipeline_stage_id',\n 'value_amount',\n 'value_currency',\n 'probability',\n 'expected_close_at',\n 'owner_user_id',\n 'source',\n 'closure_outcome',\n 'loss_reason_id',\n 'loss_notes',\n 'organization_id',\n 'tenant_id',\n 'created_at',\n 'updated_at',\n ],\n decorateCustomFields: {\n entityIds: E.customers.customer_deal,\n },\n sortFieldMap: {\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 buildFilters: buildDealListFilters,\n },\n actions: {\n create: {\n commandId: 'customers.deals.create',\n schema: rawBodySchema,\n mapInput: async ({ raw, ctx }) => {\n const { translate } = await resolveTranslations()\n return parseScopedCommandInput(dealCreateSchema, raw ?? {}, ctx, translate)\n },\n response: ({ result }) => ({ id: result?.dealId ?? result?.id ?? null }),\n status: 201,\n },\n update: {\n commandId: 'customers.deals.update',\n schema: rawBodySchema,\n mapInput: async ({ raw, ctx }) => {\n const { translate } = await resolveTranslations()\n return parseScopedCommandInput(dealUpdateSchema, raw ?? {}, ctx, translate)\n },\n response: () => ({ ok: true }),\n },\n delete: {\n commandId: 'customers.deals.delete',\n schema: rawBodySchema,\n mapInput: async ({ parsed, ctx }) => {\n const { translate } = await resolveTranslations()\n const id =\n parsed?.body?.id ??\n parsed?.id ??\n parsed?.query?.id ??\n (ctx.request ? new URL(ctx.request.url).searchParams.get('id') : null)\n if (!id) throw new CrudHttpError(400, { error: translate('customers.errors.deal_required', 'Deal id is required') })\n return { id }\n },\n response: () => ({ ok: true }),\n },\n },\n hooks: {\n // afterList only DECORATES results with `people`/`companies` arrays \u2014 it must not filter,\n // because filtering after pagination would drop deals on later pages and rewrite `total`\n // to a misleading value. Association filtering happens pre-pagination in `buildFilters`\n // via `fetchDealIdsMatchingAssociations`.\n afterList: async (payload, ctx) => {\n const items = Array.isArray(payload.items) ? payload.items : []\n if (!items.length) return\n const scopeSource = (items[0] ?? {}) as Record<string, unknown>\n const tenantIdRaw = scopeSource.tenantId ?? scopeSource.tenant_id\n const fallbackTenantId = (typeof tenantIdRaw === 'string' && tenantIdRaw.trim().length ? tenantIdRaw : null) ?? ctx.auth?.tenantId ?? null\n const orgIdRaw = scopeSource.organizationId ?? scopeSource.organization_id\n const fallbackOrganizationId = (typeof orgIdRaw === 'string' && orgIdRaw.trim().length ? orgIdRaw : null) ?? ctx.auth?.orgId ?? null\n const ids = items\n .map((item: unknown) => {\n if (!item || typeof item !== 'object') return null\n const candidate = (item as Record<string, unknown>).id\n return typeof candidate === 'string' && candidate.trim().length ? candidate : null\n })\n .filter((value: string | null): value is string => typeof value === 'string' && value.length > 0)\n if (!ids.length) {\n payload.items = []\n payload.total = 0\n return\n }\n try {\n const em = (ctx.container.resolve('em') as EntityManager)\n const [allPersonLinks, allCompanyLinks] = await Promise.all([\n findWithDecryption(\n em,\n CustomerDealPersonLink,\n { deal: { $in: ids } },\n { populate: ['person'] },\n { tenantId: fallbackTenantId, organizationId: fallbackOrganizationId },\n ),\n findWithDecryption(\n em,\n CustomerDealCompanyLink,\n { deal: { $in: ids } },\n { populate: ['company'] },\n { tenantId: fallbackTenantId, organizationId: fallbackOrganizationId },\n ),\n ])\n\n const personAssignments = new Map<string, { id: string; label: string }[]>()\n allPersonLinks.forEach((link) => {\n const deal = link.deal\n const dealRecord = deal && typeof deal === 'object' ? deal as unknown as Record<string, unknown> : null\n const dealId = typeof deal === 'string' ? deal\n : dealRecord && typeof dealRecord.id === 'string' ? dealRecord.id\n : null\n if (!dealId) return\n const personRef = link.person\n const personRecord = personRef && typeof personRef === 'object' ? personRef as unknown as Record<string, unknown> : null\n const personId = typeof personRef === 'string' ? personRef\n : personRecord && typeof personRecord.id === 'string' ? personRecord.id\n : null\n if (!personId) return\n const label = personRecord && typeof personRecord.displayName === 'string'\n ? personRecord.displayName\n : ''\n const bucket = personAssignments.get(dealId) ?? []\n if (!bucket.some((entry) => entry.id === personId)) {\n bucket.push({ id: personId, label })\n personAssignments.set(dealId, bucket)\n }\n })\n\n const companyAssignments = new Map<string, { id: string; label: string }[]>()\n allCompanyLinks.forEach((link) => {\n const deal = link.deal\n const dealRecord = deal && typeof deal === 'object' ? deal as unknown as Record<string, unknown> : null\n const dealId = typeof deal === 'string' ? deal\n : dealRecord && typeof dealRecord.id === 'string' ? dealRecord.id\n : null\n if (!dealId) return\n const companyRef = link.company\n const companyRecord = companyRef && typeof companyRef === 'object' ? companyRef as unknown as Record<string, unknown> : null\n const companyId = typeof companyRef === 'string' ? companyRef\n : companyRecord && typeof companyRecord.id === 'string' ? companyRecord.id\n : null\n if (!companyId) return\n const label = companyRecord && typeof companyRecord.displayName === 'string'\n ? companyRecord.displayName\n : ''\n const bucket = companyAssignments.get(dealId) ?? []\n if (!bucket.some((entry) => entry.id === companyId)) {\n bucket.push({ id: companyId, label })\n companyAssignments.set(dealId, bucket)\n }\n })\n\n const enhancedItems = items\n .map((item: unknown) => {\n if (!item || typeof item !== 'object') return null\n const data = item as Record<string, unknown>\n const candidate = typeof data.id === 'string' ? data.id : null\n if (!candidate || !candidate.trim().length) return null\n const people = personAssignments.get(candidate) ?? []\n const companies = companyAssignments.get(candidate) ?? []\n const tenantIdRaw =\n typeof data.tenantId === 'string'\n ? data.tenantId\n : typeof data.tenant_id === 'string'\n ? data.tenant_id\n : null\n const organizationIdRaw =\n typeof data.organizationId === 'string'\n ? data.organizationId\n : typeof data.organization_id === 'string'\n ? data.organization_id\n : null\n const tenantId = tenantIdRaw && tenantIdRaw.trim().length ? tenantIdRaw.trim() : null\n const organizationId = organizationIdRaw && organizationIdRaw.trim().length ? organizationIdRaw.trim() : null\n return {\n ...data,\n personIds: people.map((entry) => entry.id),\n people,\n companyIds: companies.map((entry) => entry.id),\n companies,\n tenantId,\n organizationId,\n }\n })\n .filter(\n (item: Record<string, unknown> | null): item is Record<string, unknown> => item !== null,\n )\n\n payload.items = enhancedItems\n } catch (err) {\n // We swallow rather than fail the request because the kanban is still useful without\n // people/companies labels (cards just lose their company pill). Tag every item with\n // `_associations: { ok: false }` so a future surface can render a degraded-state hint\n // instead of silently showing cards without company badges.\n console.warn('[customers.deals] failed to decorate items with person/company links', err)\n payload.items = items.map((item: unknown) => {\n if (!item || typeof item !== 'object') return item\n return {\n ...(item as Record<string, unknown>),\n _associations: {\n ok: false,\n reason: err instanceof Error ? err.message : 'unknown',\n },\n }\n })\n }\n },\n },\n})\n\nconst { POST, PUT, DELETE } = crud\n\nexport { POST, PUT, DELETE }\nexport const GET = crud.GET\n\nconst dealAssociationSchema = z.object({\n id: z.string().uuid(),\n label: z.string().nullable(),\n})\n\nconst dealListItemSchema = z\n .object({\n id: z.string().uuid(),\n title: z.string().nullable(),\n description: z.string().nullable().optional(),\n status: z.string().nullable().optional(),\n pipeline_stage: z.string().nullable().optional(),\n pipeline_id: z.string().uuid().nullable().optional(),\n pipeline_stage_id: z.string().uuid().nullable().optional(),\n value_amount: z.number().nullable().optional(),\n value_currency: z.string().nullable().optional(),\n probability: z.number().nullable().optional(),\n expected_close_at: z.string().nullable().optional(),\n owner_user_id: z.string().uuid().nullable().optional(),\n source: z.string().nullable().optional(),\n organization_id: z.string().uuid().nullable().optional(),\n tenant_id: z.string().uuid().nullable().optional(),\n created_at: z.string().nullable().optional(),\n updated_at: z.string().nullable().optional(),\n personIds: z.array(z.string().uuid()).optional(),\n people: z.array(dealAssociationSchema).optional(),\n companyIds: z.array(z.string().uuid()).optional(),\n companies: z.array(dealAssociationSchema).optional(),\n organizationId: z.string().uuid().nullable().optional(),\n tenantId: z.string().uuid().nullable().optional(),\n })\n .passthrough()\n\nconst dealCreateResponseSchema = z.object({\n id: z.string().uuid().nullable(),\n})\n\nexport const openApi = createCustomersCrudOpenApi({\n resourceName: 'Deal',\n querySchema: dealListQuerySchema,\n listResponseSchema: createPagedListResponseSchema(dealListItemSchema),\n create: {\n schema: dealCreateSchema,\n responseSchema: dealCreateResponseSchema,\n description: 'Creates a sales deal, optionally associating people and companies.',\n },\n update: {\n schema: dealUpdateSchema,\n responseSchema: defaultOkResponseSchema,\n description: 'Updates pipeline position, metadata, or associations for an existing deal.',\n },\n del: {\n schema: z.object({ id: z.string().uuid() }),\n responseSchema: defaultOkResponseSchema,\n description: 'Deletes a deal by `id`. The identifier may be provided in the body or query parameters.',\n },\n})\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["parsed", "tenantIdRaw"]
|
|
7
7
|
}
|