@open-mercato/core 0.7.1-develop.7123.1.25365e718d → 0.7.1-develop.7131.1.20abf9e885
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/audit_logs/api/audit-logs/actions/redo/route.js +5 -0
- package/dist/modules/audit_logs/api/audit-logs/actions/redo/route.js.map +2 -2
- package/dist/modules/auth/api/profile/route.js +5 -0
- package/dist/modules/auth/api/profile/route.js.map +2 -2
- package/dist/modules/customers/api/activities/route.js +13 -0
- package/dist/modules/customers/api/activities/route.js.map +2 -2
- package/dist/modules/customers/api/dictionaries/[kind]/[id]/route.js +9 -0
- package/dist/modules/customers/api/dictionaries/[kind]/[id]/route.js.map +2 -2
- package/dist/modules/customers/api/dictionaries/[kind]/route.js +5 -0
- package/dist/modules/customers/api/dictionaries/[kind]/route.js.map +2 -2
- package/dist/modules/customers/api/dictionaries/kind-settings/route.js +5 -0
- package/dist/modules/customers/api/dictionaries/kind-settings/route.js.map +2 -2
- package/dist/modules/customers/api/interactions/cancel/route.js +5 -0
- package/dist/modules/customers/api/interactions/cancel/route.js.map +2 -2
- package/dist/modules/customers/api/interactions/complete/route.js +5 -0
- package/dist/modules/customers/api/interactions/complete/route.js.map +2 -2
- package/dist/modules/customers/api/labels/assign/route.js +5 -0
- package/dist/modules/customers/api/labels/assign/route.js.map +2 -2
- package/dist/modules/customers/api/labels/route.js +5 -0
- package/dist/modules/customers/api/labels/route.js.map +2 -2
- package/dist/modules/customers/api/labels/unassign/route.js +5 -0
- package/dist/modules/customers/api/labels/unassign/route.js.map +2 -2
- package/dist/modules/customers/api/people/[id]/companies/[linkId]/route.js +9 -0
- package/dist/modules/customers/api/people/[id]/companies/[linkId]/route.js.map +2 -2
- package/dist/modules/customers/api/people/[id]/companies/route.js +5 -0
- package/dist/modules/customers/api/people/[id]/companies/route.js.map +2 -2
- package/dist/modules/customers/api/pipeline-stages/reorder/route.js +5 -0
- package/dist/modules/customers/api/pipeline-stages/reorder/route.js.map +2 -2
- package/dist/modules/customers/api/pipeline-stages/route.js +13 -0
- package/dist/modules/customers/api/pipeline-stages/route.js.map +2 -2
- package/dist/modules/customers/api/pipelines/route.js +13 -0
- package/dist/modules/customers/api/pipelines/route.js.map +2 -2
- package/dist/modules/customers/api/settings/address-format/route.js +5 -0
- package/dist/modules/customers/api/settings/address-format/route.js.map +2 -2
- package/dist/modules/customers/api/settings/dictionary-sort-modes/route.js +5 -0
- package/dist/modules/customers/api/settings/dictionary-sort-modes/route.js.map +2 -2
- package/dist/modules/customers/api/settings/stuck-threshold/route.js +5 -0
- package/dist/modules/customers/api/settings/stuck-threshold/route.js.map +2 -2
- package/dist/modules/customers/api/tags/assign/route.js +5 -0
- package/dist/modules/customers/api/tags/assign/route.js.map +2 -2
- package/dist/modules/customers/api/tags/unassign/route.js +5 -0
- package/dist/modules/customers/api/tags/unassign/route.js.map +2 -2
- package/dist/modules/customers/api/todos/route.js +13 -0
- package/dist/modules/customers/api/todos/route.js.map +2 -2
- package/dist/modules/customers/backend/customers/deals/[id]/hooks/useDealAssociations.js +5 -117
- package/dist/modules/customers/backend/customers/deals/[id]/hooks/useDealAssociations.js.map +2 -2
- package/dist/modules/customers/backend/customers/deals/[id]/page.js +1 -3
- package/dist/modules/customers/backend/customers/deals/[id]/page.js.map +2 -2
- package/dist/modules/customers/commands/deals.js +33 -9
- package/dist/modules/customers/commands/deals.js.map +3 -3
- package/dist/modules/customers/components/AddressEditor.js +71 -1
- package/dist/modules/customers/components/AddressEditor.js.map +2 -2
- package/dist/modules/customers/utils/addressFormat.js +18 -2
- package/dist/modules/customers/utils/addressFormat.js.map +2 -2
- package/dist/modules/dictionaries/api/[dictionaryId]/entries/[entryId]/route.js +9 -0
- package/dist/modules/dictionaries/api/[dictionaryId]/entries/[entryId]/route.js.map +2 -2
- package/dist/modules/dictionaries/api/[dictionaryId]/entries/reorder/route.js +5 -0
- package/dist/modules/dictionaries/api/[dictionaryId]/entries/reorder/route.js.map +2 -2
- package/dist/modules/dictionaries/api/[dictionaryId]/entries/route.js +5 -0
- package/dist/modules/dictionaries/api/[dictionaryId]/entries/route.js.map +2 -2
- package/dist/modules/dictionaries/api/[dictionaryId]/entries/set-default/route.js +5 -0
- package/dist/modules/dictionaries/api/[dictionaryId]/entries/set-default/route.js.map +2 -2
- package/dist/modules/directory/api/organization-branding/route.js +5 -0
- package/dist/modules/directory/api/organization-branding/route.js.map +2 -2
- package/dist/modules/eudr/api/plots/import/route.js.map +2 -2
- package/dist/modules/eudr/api/product-mappings/suggestions/apply/route.js.map +2 -2
- package/dist/modules/feature_toggles/api/overrides/route.js +5 -0
- package/dist/modules/feature_toggles/api/overrides/route.js.map +2 -2
- package/dist/modules/messages/api/[id]/actions/[actionId]/route.js +5 -0
- package/dist/modules/messages/api/[id]/actions/[actionId]/route.js.map +2 -2
- package/dist/modules/messages/api/[id]/conversation/archive/route.js +5 -0
- package/dist/modules/messages/api/[id]/conversation/archive/route.js.map +2 -2
- package/dist/modules/messages/api/[id]/conversation/read/route.js +5 -0
- package/dist/modules/messages/api/[id]/conversation/read/route.js.map +2 -2
- package/dist/modules/messages/api/[id]/conversation/route.js +5 -0
- package/dist/modules/messages/api/[id]/conversation/route.js.map +2 -2
- package/dist/modules/messages/api/[id]/forward/route.js +5 -0
- package/dist/modules/messages/api/[id]/forward/route.js.map +2 -2
- package/dist/modules/messages/api/[id]/reply/route.js +5 -0
- package/dist/modules/messages/api/[id]/reply/route.js.map +2 -2
- package/dist/modules/messages/api/[id]/route.js +9 -0
- package/dist/modules/messages/api/[id]/route.js.map +2 -2
- package/dist/modules/messages/api/token/[token]/route.js +5 -0
- package/dist/modules/messages/api/token/[token]/route.js.map +2 -2
- package/dist/modules/resources/api/resources/tags/assign/route.js +5 -0
- package/dist/modules/resources/api/resources/tags/assign/route.js.map +2 -2
- package/dist/modules/resources/api/resources/tags/unassign/route.js +5 -0
- package/dist/modules/resources/api/resources/tags/unassign/route.js.map +2 -2
- package/dist/modules/sales/api/quotes/accept/route.js +5 -0
- package/dist/modules/sales/api/quotes/accept/route.js.map +2 -2
- package/dist/modules/sales/api/quotes/convert/route.js +5 -0
- package/dist/modules/sales/api/quotes/convert/route.js.map +2 -2
- package/dist/modules/sales/api/settings/document-numbers/route.js +5 -0
- package/dist/modules/sales/api/settings/document-numbers/route.js.map +2 -2
- package/dist/modules/sales/api/settings/order-editing/route.js +5 -0
- package/dist/modules/sales/api/settings/order-editing/route.js.map +2 -2
- package/dist/modules/sales/components/documents/AddressesSection.js +17 -2
- package/dist/modules/sales/components/documents/AddressesSection.js.map +2 -2
- package/dist/modules/sales/components/documents/SalesDocumentForm.js +7 -24
- package/dist/modules/sales/components/documents/SalesDocumentForm.js.map +2 -2
- package/dist/modules/sales/components/documents/normalizeAddressDraft.js +29 -0
- package/dist/modules/sales/components/documents/normalizeAddressDraft.js.map +7 -0
- package/dist/modules/staff/api/leave-requests/accept/route.js +5 -0
- package/dist/modules/staff/api/leave-requests/accept/route.js.map +2 -2
- package/dist/modules/staff/api/leave-requests/reject/route.js +5 -0
- package/dist/modules/staff/api/leave-requests/reject/route.js.map +2 -2
- package/dist/modules/staff/api/team-members/self/route.js +5 -0
- package/dist/modules/staff/api/team-members/self/route.js.map +2 -2
- package/dist/modules/staff/api/team-members/tags/assign/route.js +5 -0
- package/dist/modules/staff/api/team-members/tags/assign/route.js.map +2 -2
- package/dist/modules/staff/api/team-members/tags/unassign/route.js +5 -0
- package/dist/modules/staff/api/team-members/tags/unassign/route.js.map +2 -2
- package/dist/modules/staff/api/timesheets/time-entries/start-timer/route.js +5 -0
- package/dist/modules/staff/api/timesheets/time-entries/start-timer/route.js.map +2 -2
- package/dist/modules/translations/api/[entityType]/[entityId]/route.js +9 -0
- package/dist/modules/translations/api/[entityType]/[entityId]/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/ai/assess/route.js +5 -0
- package/dist/modules/warranty_claims/api/ai/assess/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/assign/route.js +5 -0
- package/dist/modules/warranty_claims/api/assign/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/credit-memo/route.js +5 -0
- package/dist/modules/warranty_claims/api/credit-memo/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/events/route.js +5 -0
- package/dist/modules/warranty_claims/api/events/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/portal/claims/[id]/submit/route.js +5 -0
- package/dist/modules/warranty_claims/api/portal/claims/[id]/submit/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/portal/claims/[id]/withdraw/route.js +5 -0
- package/dist/modules/warranty_claims/api/portal/claims/[id]/withdraw/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/portal/claims/route.js +5 -0
- package/dist/modules/warranty_claims/api/portal/claims/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/portal/events/route.js +5 -0
- package/dist/modules/warranty_claims/api/portal/events/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/receiving/route.js +5 -0
- package/dist/modules/warranty_claims/api/receiving/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/replacement-order/route.js +5 -0
- package/dist/modules/warranty_claims/api/replacement-order/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/return-label/route.js +5 -0
- package/dist/modules/warranty_claims/api/return-label/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/sales-return/route.js +5 -0
- package/dist/modules/warranty_claims/api/sales-return/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/settings-general/route.js +5 -0
- package/dist/modules/warranty_claims/api/settings-general/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/submit/route.js +5 -0
- package/dist/modules/warranty_claims/api/submit/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/transition/route.js +5 -0
- package/dist/modules/warranty_claims/api/transition/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/vendor-recovery/route.js +5 -0
- package/dist/modules/warranty_claims/api/vendor-recovery/route.js.map +2 -2
- package/dist/modules/wms/api/sales-orders/[salesOrderId]/warehouse-assignment/route.js +5 -0
- package/dist/modules/wms/api/sales-orders/[salesOrderId]/warehouse-assignment/route.js.map +2 -2
- package/package.json +7 -7
- package/src/modules/audit_logs/api/audit-logs/actions/redo/route.ts +5 -0
- package/src/modules/auth/api/profile/route.ts +5 -0
- package/src/modules/customers/api/activities/route.ts +13 -0
- package/src/modules/customers/api/dictionaries/[kind]/[id]/route.ts +9 -0
- package/src/modules/customers/api/dictionaries/[kind]/route.ts +5 -0
- package/src/modules/customers/api/dictionaries/kind-settings/route.ts +5 -0
- package/src/modules/customers/api/interactions/cancel/route.ts +5 -0
- package/src/modules/customers/api/interactions/complete/route.ts +5 -0
- package/src/modules/customers/api/labels/assign/route.ts +5 -0
- package/src/modules/customers/api/labels/route.ts +5 -0
- package/src/modules/customers/api/labels/unassign/route.ts +5 -0
- package/src/modules/customers/api/people/[id]/companies/[linkId]/route.ts +9 -0
- package/src/modules/customers/api/people/[id]/companies/route.ts +5 -0
- package/src/modules/customers/api/pipeline-stages/reorder/route.ts +5 -0
- package/src/modules/customers/api/pipeline-stages/route.ts +13 -0
- package/src/modules/customers/api/pipelines/route.ts +13 -0
- package/src/modules/customers/api/settings/address-format/route.ts +5 -0
- package/src/modules/customers/api/settings/dictionary-sort-modes/route.ts +5 -0
- package/src/modules/customers/api/settings/stuck-threshold/route.ts +5 -0
- package/src/modules/customers/api/tags/assign/route.ts +5 -0
- package/src/modules/customers/api/tags/unassign/route.ts +5 -0
- package/src/modules/customers/api/todos/route.ts +13 -0
- package/src/modules/customers/backend/customers/deals/[id]/hooks/types.ts +0 -23
- package/src/modules/customers/backend/customers/deals/[id]/hooks/useDealAssociations.ts +11 -172
- package/src/modules/customers/backend/customers/deals/[id]/page.tsx +1 -1
- package/src/modules/customers/commands/deals.ts +84 -9
- package/src/modules/customers/components/AddressEditor.tsx +116 -1
- package/src/modules/customers/i18n/de.json +6 -0
- package/src/modules/customers/i18n/en.json +6 -0
- package/src/modules/customers/i18n/es.json +6 -0
- package/src/modules/customers/i18n/ko.json +6 -0
- package/src/modules/customers/i18n/pl.json +6 -0
- package/src/modules/customers/utils/addressFormat.tsx +59 -1
- package/src/modules/dictionaries/api/[dictionaryId]/entries/[entryId]/route.ts +9 -0
- package/src/modules/dictionaries/api/[dictionaryId]/entries/reorder/route.ts +5 -0
- package/src/modules/dictionaries/api/[dictionaryId]/entries/route.ts +5 -0
- package/src/modules/dictionaries/api/[dictionaryId]/entries/set-default/route.ts +5 -0
- package/src/modules/directory/api/organization-branding/route.ts +5 -0
- package/src/modules/eudr/api/plots/import/route.ts +7 -0
- package/src/modules/eudr/api/product-mappings/suggestions/apply/route.ts +7 -0
- package/src/modules/feature_toggles/api/overrides/route.ts +5 -0
- package/src/modules/messages/api/[id]/actions/[actionId]/route.ts +5 -0
- package/src/modules/messages/api/[id]/conversation/archive/route.ts +5 -0
- package/src/modules/messages/api/[id]/conversation/read/route.ts +5 -0
- package/src/modules/messages/api/[id]/conversation/route.ts +5 -0
- package/src/modules/messages/api/[id]/forward/route.ts +5 -0
- package/src/modules/messages/api/[id]/reply/route.ts +5 -0
- package/src/modules/messages/api/[id]/route.ts +9 -0
- package/src/modules/messages/api/token/[token]/route.ts +5 -0
- package/src/modules/resources/api/resources/tags/assign/route.ts +5 -0
- package/src/modules/resources/api/resources/tags/unassign/route.ts +5 -0
- package/src/modules/sales/api/quotes/accept/route.ts +5 -0
- package/src/modules/sales/api/quotes/convert/route.ts +5 -0
- package/src/modules/sales/api/settings/document-numbers/route.ts +5 -0
- package/src/modules/sales/api/settings/order-editing/route.ts +5 -0
- package/src/modules/sales/components/documents/AddressesSection.tsx +48 -5
- package/src/modules/sales/components/documents/SalesDocumentForm.tsx +5 -23
- package/src/modules/sales/components/documents/normalizeAddressDraft.ts +29 -0
- package/src/modules/staff/api/leave-requests/accept/route.ts +5 -0
- package/src/modules/staff/api/leave-requests/reject/route.ts +5 -0
- package/src/modules/staff/api/team-members/self/route.ts +5 -0
- package/src/modules/staff/api/team-members/tags/assign/route.ts +5 -0
- package/src/modules/staff/api/team-members/tags/unassign/route.ts +5 -0
- package/src/modules/staff/api/timesheets/time-entries/start-timer/route.ts +5 -0
- package/src/modules/translations/api/[entityType]/[entityId]/route.ts +9 -0
- package/src/modules/warranty_claims/api/ai/assess/route.ts +5 -0
- package/src/modules/warranty_claims/api/assign/route.ts +5 -0
- package/src/modules/warranty_claims/api/credit-memo/route.ts +5 -0
- package/src/modules/warranty_claims/api/events/route.ts +5 -0
- package/src/modules/warranty_claims/api/portal/claims/[id]/submit/route.ts +5 -0
- package/src/modules/warranty_claims/api/portal/claims/[id]/withdraw/route.ts +5 -0
- package/src/modules/warranty_claims/api/portal/claims/route.ts +5 -0
- package/src/modules/warranty_claims/api/portal/events/route.ts +5 -0
- package/src/modules/warranty_claims/api/receiving/route.ts +5 -0
- package/src/modules/warranty_claims/api/replacement-order/route.ts +5 -0
- package/src/modules/warranty_claims/api/return-label/route.ts +5 -0
- package/src/modules/warranty_claims/api/sales-return/route.ts +5 -0
- package/src/modules/warranty_claims/api/settings-general/route.ts +5 -0
- package/src/modules/warranty_claims/api/submit/route.ts +5 -0
- package/src/modules/warranty_claims/api/transition/route.ts +5 -0
- package/src/modules/warranty_claims/api/vendor-recovery/route.ts +5 -0
- package/src/modules/wms/api/sales-orders/[salesOrderId]/warehouse-assignment/route.ts +5 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../src/modules/customers/api/tags/assign/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 { serializeOperationMetadata } from '@open-mercato/shared/lib/commands/operationMetadata'\nimport type { CommandRuntimeContext, CommandBus } from '@open-mercato/shared/lib/commands'\nimport { tagAssignmentSchema, type TagAssignmentInput } from '../../../data/validators'\nimport { CrudHttpError, isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { withScopedPayload } from '../../utils'\nimport {\n runCrudMutationGuardAfterSuccess,\n validateCrudMutationGuard,\n} from '@open-mercato/shared/lib/crud/mutation-guard'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\n\nconst logger = createLogger('customers')\n\nconst TAG_ASSIGNMENT_RESOURCE_KIND = 'customers.tagAssignment'\n\nexport const metadata = {\n POST: { requireAuth: true, requireFeatures: ['customers.activities.manage'] },\n}\n\nasync function buildContext(\n req: Request\n): Promise<{ ctx: CommandRuntimeContext; auth: Awaited<ReturnType<typeof getAuthFromRequest>>; translate: (key: string, fallback?: string) => string }> {\n const container = await createRequestContainer()\n const auth = await getAuthFromRequest(req)\n const { translate } = await resolveTranslations()\n if (!auth) throw new CrudHttpError(401, { error: translate('customers.errors.unauthorized', 'Unauthorized') })\n const scope = await resolveOrganizationScopeForRequest({ container, auth, request: req })\n const ctx: CommandRuntimeContext = {\n container,\n auth,\n organizationScope: scope,\n selectedOrganizationId: scope?.selectedId ?? auth.orgId ?? null,\n organizationIds: scope?.filterIds ?? (auth.orgId ? [auth.orgId] : null),\n request: req,\n }\n return { ctx, auth, translate }\n}\n\nexport async function POST(req: Request) {\n try {\n const { ctx, auth, translate } = await buildContext(req)\n const tenantId = auth!.tenantId\n const organizationId = ctx.selectedOrganizationId\n if (!tenantId || !organizationId) {\n return NextResponse.json({ error: translate('customers.errors.context_required', 'Organization and tenant context required') }, { status: 400 })\n }\n const body = await req.json().catch(() => ({}))\n const scoped = withScopedPayload(body, ctx, translate)\n const input = tagAssignmentSchema.parse(scoped)\n\n const guardResult = await validateCrudMutationGuard(ctx.container, {\n tenantId,\n organizationId,\n userId: auth!.sub,\n resourceKind: TAG_ASSIGNMENT_RESOURCE_KIND,\n resourceId: input.entityId,\n operation: 'custom',\n requestMethod: req.method,\n requestHeaders: req.headers,\n mutationPayload: input,\n })\n if (guardResult && !guardResult.ok) {\n return NextResponse.json(guardResult.body, { status: guardResult.status })\n }\n\n const commandBus = (ctx.container.resolve('commandBus') as CommandBus)\n const { result, logEntry } = await commandBus.execute<TagAssignmentInput, { assignmentId: string }>(\n 'customers.tags.assign',\n { input, ctx },\n )\n if (guardResult?.ok && guardResult.shouldRunAfterSuccess) {\n await runCrudMutationGuardAfterSuccess(ctx.container, {\n tenantId,\n organizationId,\n userId: auth!.sub,\n resourceKind: TAG_ASSIGNMENT_RESOURCE_KIND,\n resourceId: input.entityId,\n operation: 'custom',\n requestMethod: req.method,\n requestHeaders: req.headers,\n metadata: guardResult.metadata ?? null,\n })\n }\n const response = NextResponse.json({ id: result?.assignmentId ?? null }, { status: 201 })\n if (logEntry?.undoToken && logEntry?.id && logEntry?.commandId) {\n response.headers.set(\n 'x-om-operation',\n serializeOperationMetadata({\n id: logEntry.id,\n undoToken: logEntry.undoToken,\n commandId: logEntry.commandId,\n actionLabel: logEntry.actionLabel ?? null,\n resourceKind: logEntry.resourceKind ?? 'customers.tagAssignment',\n resourceId: logEntry.resourceId ?? result?.assignmentId ?? null,\n executedAt: logEntry.createdAt instanceof Date ? logEntry.createdAt.toISOString() : undefined,\n })\n )\n }\n return response\n } catch (err) {\n if (isCrudHttpError(err)) {\n return NextResponse.json(err.body, { status: err.status })\n }\n const { translate } = await resolveTranslations()\n logger.error('customers.tags.assign failed', { err })\n return NextResponse.json({ error: translate('customers.errors.assign_failed', 'Failed to assign tag') }, { status: 400 })\n }\n}\n\nconst tagAssignmentResponseSchema = z.object({\n id: z.string().uuid().nullable(),\n})\n\nconst tagAssignmentErrorSchema = z.object({\n error: z.string(),\n})\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Customers',\n summary: 'Assign customer tag',\n methods: {\n POST: {\n summary: 'Assign tag to customer entity',\n description: 'Links a tag to a customer entity within the validated tenant / organization scope.',\n requestBody: {\n contentType: 'application/json',\n schema: tagAssignmentSchema,\n },\n responses: [\n { status: 201, description: 'Tag assigned to customer', schema: tagAssignmentResponseSchema },\n ],\n errors: [\n { status: 400, description: 'Validation or assignment failed', schema: tagAssignmentErrorSchema },\n { status: 401, description: 'Unauthorized', schema: tagAssignmentErrorSchema },\n { status: 403, description: 'Insufficient tenant/organization access', schema: tagAssignmentErrorSchema },\n ],\n },\n },\n}\n"],
|
|
5
|
-
"mappings": "AAAA,SAAS,oBAAoB;AAC7B,SAAS,SAAS;AAClB,SAAS,8BAA8B;AACvC,SAAS,0BAA0B;AACnC,SAAS,0CAA0C;AACnD,SAAS,kCAAkC;AAE3C,SAAS,2BAAoD;AAC7D,SAAS,eAAe,uBAAuB;AAC/C,SAAS,2BAA2B;AACpC,SAAS,yBAAyB;AAClC;AAAA,EACE;AAAA,EACA;AAAA,OACK;AAEP,SAAS,oBAAoB;
|
|
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 { serializeOperationMetadata } from '@open-mercato/shared/lib/commands/operationMetadata'\nimport type { CommandRuntimeContext, CommandBus } from '@open-mercato/shared/lib/commands'\nimport { tagAssignmentSchema, type TagAssignmentInput } from '../../../data/validators'\nimport { CrudHttpError, isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { withScopedPayload } from '../../utils'\nimport {\n runCrudMutationGuardAfterSuccess,\n validateCrudMutationGuard,\n} from '@open-mercato/shared/lib/crud/mutation-guard'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\nimport { getCommandInterceptorHttpRejection } from '@open-mercato/shared/lib/commands/errors'\n\nconst logger = createLogger('customers')\n\nconst TAG_ASSIGNMENT_RESOURCE_KIND = 'customers.tagAssignment'\n\nexport const metadata = {\n POST: { requireAuth: true, requireFeatures: ['customers.activities.manage'] },\n}\n\nasync function buildContext(\n req: Request\n): Promise<{ ctx: CommandRuntimeContext; auth: Awaited<ReturnType<typeof getAuthFromRequest>>; translate: (key: string, fallback?: string) => string }> {\n const container = await createRequestContainer()\n const auth = await getAuthFromRequest(req)\n const { translate } = await resolveTranslations()\n if (!auth) throw new CrudHttpError(401, { error: translate('customers.errors.unauthorized', 'Unauthorized') })\n const scope = await resolveOrganizationScopeForRequest({ container, auth, request: req })\n const ctx: CommandRuntimeContext = {\n container,\n auth,\n organizationScope: scope,\n selectedOrganizationId: scope?.selectedId ?? auth.orgId ?? null,\n organizationIds: scope?.filterIds ?? (auth.orgId ? [auth.orgId] : null),\n request: req,\n }\n return { ctx, auth, translate }\n}\n\nexport async function POST(req: Request) {\n try {\n const { ctx, auth, translate } = await buildContext(req)\n const tenantId = auth!.tenantId\n const organizationId = ctx.selectedOrganizationId\n if (!tenantId || !organizationId) {\n return NextResponse.json({ error: translate('customers.errors.context_required', 'Organization and tenant context required') }, { status: 400 })\n }\n const body = await req.json().catch(() => ({}))\n const scoped = withScopedPayload(body, ctx, translate)\n const input = tagAssignmentSchema.parse(scoped)\n\n const guardResult = await validateCrudMutationGuard(ctx.container, {\n tenantId,\n organizationId,\n userId: auth!.sub,\n resourceKind: TAG_ASSIGNMENT_RESOURCE_KIND,\n resourceId: input.entityId,\n operation: 'custom',\n requestMethod: req.method,\n requestHeaders: req.headers,\n mutationPayload: input,\n })\n if (guardResult && !guardResult.ok) {\n return NextResponse.json(guardResult.body, { status: guardResult.status })\n }\n\n const commandBus = (ctx.container.resolve('commandBus') as CommandBus)\n const { result, logEntry } = await commandBus.execute<TagAssignmentInput, { assignmentId: string }>(\n 'customers.tags.assign',\n { input, ctx },\n )\n if (guardResult?.ok && guardResult.shouldRunAfterSuccess) {\n await runCrudMutationGuardAfterSuccess(ctx.container, {\n tenantId,\n organizationId,\n userId: auth!.sub,\n resourceKind: TAG_ASSIGNMENT_RESOURCE_KIND,\n resourceId: input.entityId,\n operation: 'custom',\n requestMethod: req.method,\n requestHeaders: req.headers,\n metadata: guardResult.metadata ?? null,\n })\n }\n const response = NextResponse.json({ id: result?.assignmentId ?? null }, { status: 201 })\n if (logEntry?.undoToken && logEntry?.id && logEntry?.commandId) {\n response.headers.set(\n 'x-om-operation',\n serializeOperationMetadata({\n id: logEntry.id,\n undoToken: logEntry.undoToken,\n commandId: logEntry.commandId,\n actionLabel: logEntry.actionLabel ?? null,\n resourceKind: logEntry.resourceKind ?? 'customers.tagAssignment',\n resourceId: logEntry.resourceId ?? result?.assignmentId ?? null,\n executedAt: logEntry.createdAt instanceof Date ? logEntry.createdAt.toISOString() : undefined,\n })\n )\n }\n return response\n } catch (err) {\n if (isCrudHttpError(err)) {\n return NextResponse.json(err.body, { status: err.status })\n }\n const interceptorRejection = getCommandInterceptorHttpRejection(err)\n if (interceptorRejection) {\n return NextResponse.json(interceptorRejection.body, { status: interceptorRejection.status })\n }\n const { translate } = await resolveTranslations()\n logger.error('customers.tags.assign failed', { err })\n return NextResponse.json({ error: translate('customers.errors.assign_failed', 'Failed to assign tag') }, { status: 400 })\n }\n}\n\nconst tagAssignmentResponseSchema = z.object({\n id: z.string().uuid().nullable(),\n})\n\nconst tagAssignmentErrorSchema = z.object({\n error: z.string(),\n})\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Customers',\n summary: 'Assign customer tag',\n methods: {\n POST: {\n summary: 'Assign tag to customer entity',\n description: 'Links a tag to a customer entity within the validated tenant / organization scope.',\n requestBody: {\n contentType: 'application/json',\n schema: tagAssignmentSchema,\n },\n responses: [\n { status: 201, description: 'Tag assigned to customer', schema: tagAssignmentResponseSchema },\n ],\n errors: [\n { status: 400, description: 'Validation or assignment failed', schema: tagAssignmentErrorSchema },\n { status: 401, description: 'Unauthorized', schema: tagAssignmentErrorSchema },\n { status: 403, description: 'Insufficient tenant/organization access', schema: tagAssignmentErrorSchema },\n ],\n },\n },\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
validateCrudMutationGuard
|
|
14
14
|
} from "@open-mercato/shared/lib/crud/mutation-guard";
|
|
15
15
|
import { createLogger } from "@open-mercato/shared/lib/logger";
|
|
16
|
+
import { getCommandInterceptorHttpRejection } from "@open-mercato/shared/lib/commands/errors";
|
|
16
17
|
const logger = createLogger("customers");
|
|
17
18
|
const TAG_ASSIGNMENT_RESOURCE_KIND = "customers.tagAssignment";
|
|
18
19
|
const metadata = {
|
|
@@ -97,6 +98,10 @@ async function POST(req) {
|
|
|
97
98
|
if (isCrudHttpError(err)) {
|
|
98
99
|
return NextResponse.json(err.body, { status: err.status });
|
|
99
100
|
}
|
|
101
|
+
const interceptorRejection = getCommandInterceptorHttpRejection(err);
|
|
102
|
+
if (interceptorRejection) {
|
|
103
|
+
return NextResponse.json(interceptorRejection.body, { status: interceptorRejection.status });
|
|
104
|
+
}
|
|
100
105
|
const { translate } = await resolveTranslations();
|
|
101
106
|
logger.error("customers.tags.unassign failed", { err });
|
|
102
107
|
return NextResponse.json({ error: translate("customers.errors.unassign_failed", "Failed to unassign tag") }, { status: 400 });
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../src/modules/customers/api/tags/unassign/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 { serializeOperationMetadata } from '@open-mercato/shared/lib/commands/operationMetadata'\nimport type { CommandRuntimeContext, CommandBus } from '@open-mercato/shared/lib/commands'\nimport { tagAssignmentSchema, type TagAssignmentInput } from '../../../data/validators'\nimport { CrudHttpError, isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { withScopedPayload } from '../../utils'\nimport {\n runCrudMutationGuardAfterSuccess,\n validateCrudMutationGuard,\n} from '@open-mercato/shared/lib/crud/mutation-guard'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\n\nconst logger = createLogger('customers')\n\nconst TAG_ASSIGNMENT_RESOURCE_KIND = 'customers.tagAssignment'\n\nexport const metadata = {\n POST: { requireAuth: true, requireFeatures: ['customers.activities.manage'] },\n}\n\nasync function buildContext(\n req: Request\n): Promise<{ ctx: CommandRuntimeContext; auth: Awaited<ReturnType<typeof getAuthFromRequest>>; translate: (key: string, fallback?: string) => string }> {\n const container = await createRequestContainer()\n const auth = await getAuthFromRequest(req)\n const { translate } = await resolveTranslations()\n if (!auth) throw new CrudHttpError(401, { error: translate('customers.errors.unauthorized', 'Unauthorized') })\n const scope = await resolveOrganizationScopeForRequest({ container, auth, request: req })\n const ctx: CommandRuntimeContext = {\n container,\n auth,\n organizationScope: scope,\n selectedOrganizationId: scope?.selectedId ?? auth.orgId ?? null,\n organizationIds: scope?.filterIds ?? (auth.orgId ? [auth.orgId] : null),\n request: req,\n }\n return { ctx, auth, translate }\n}\n\nexport async function POST(req: Request) {\n try {\n const { ctx, auth, translate } = await buildContext(req)\n const tenantId = auth!.tenantId\n const organizationId = ctx.selectedOrganizationId\n if (!tenantId || !organizationId) {\n return NextResponse.json({ error: translate('customers.errors.context_required', 'Organization and tenant context required') }, { status: 400 })\n }\n const body = await req.json().catch(() => ({}))\n const scoped = withScopedPayload(body, ctx, translate)\n const input = tagAssignmentSchema.parse(scoped)\n\n const guardResult = await validateCrudMutationGuard(ctx.container, {\n tenantId,\n organizationId,\n userId: auth!.sub,\n resourceKind: TAG_ASSIGNMENT_RESOURCE_KIND,\n resourceId: input.entityId,\n operation: 'custom',\n requestMethod: req.method,\n requestHeaders: req.headers,\n mutationPayload: input,\n })\n if (guardResult && !guardResult.ok) {\n return NextResponse.json(guardResult.body, { status: guardResult.status })\n }\n\n const commandBus = (ctx.container.resolve('commandBus') as CommandBus)\n const { result, logEntry } = await commandBus.execute<TagAssignmentInput, { assignmentId: string | null }>(\n 'customers.tags.unassign',\n { input, ctx },\n )\n if (guardResult?.ok && guardResult.shouldRunAfterSuccess) {\n await runCrudMutationGuardAfterSuccess(ctx.container, {\n tenantId,\n organizationId,\n userId: auth!.sub,\n resourceKind: TAG_ASSIGNMENT_RESOURCE_KIND,\n resourceId: input.entityId,\n operation: 'custom',\n requestMethod: req.method,\n requestHeaders: req.headers,\n metadata: guardResult.metadata ?? null,\n })\n }\n const response = NextResponse.json({ id: result?.assignmentId ?? null })\n if (logEntry?.undoToken && logEntry?.id && logEntry?.commandId) {\n response.headers.set(\n 'x-om-operation',\n serializeOperationMetadata({\n id: logEntry.id,\n undoToken: logEntry.undoToken,\n commandId: logEntry.commandId,\n actionLabel: logEntry.actionLabel ?? null,\n resourceKind: logEntry.resourceKind ?? 'customers.tagAssignment',\n resourceId: logEntry.resourceId ?? result?.assignmentId ?? null,\n executedAt: logEntry.createdAt instanceof Date ? logEntry.createdAt.toISOString() : undefined,\n })\n )\n }\n return response\n } catch (err) {\n if (isCrudHttpError(err)) {\n return NextResponse.json(err.body, { status: err.status })\n }\n const { translate } = await resolveTranslations()\n logger.error('customers.tags.unassign failed', { err })\n return NextResponse.json({ error: translate('customers.errors.unassign_failed', 'Failed to unassign tag') }, { status: 400 })\n }\n}\n\nconst tagUnassignResponseSchema = z.object({\n id: z.string().uuid().nullable(),\n})\n\nconst tagUnassignErrorSchema = z.object({\n error: z.string(),\n})\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Customers',\n summary: 'Unassign customer tag',\n methods: {\n POST: {\n summary: 'Remove tag from customer entity',\n description: 'Detaches a tag from a customer entity within the validated tenant / organization scope.',\n requestBody: {\n contentType: 'application/json',\n schema: tagAssignmentSchema,\n },\n responses: [\n { status: 200, description: 'Tag unassigned from customer', schema: tagUnassignResponseSchema },\n ],\n errors: [\n { status: 400, description: 'Validation or unassignment failed', schema: tagUnassignErrorSchema },\n { status: 401, description: 'Unauthorized', schema: tagUnassignErrorSchema },\n { status: 403, description: 'Insufficient tenant/organization access', schema: tagUnassignErrorSchema },\n ],\n },\n },\n}\n"],
|
|
5
|
-
"mappings": "AAAA,SAAS,oBAAoB;AAC7B,SAAS,SAAS;AAClB,SAAS,8BAA8B;AACvC,SAAS,0BAA0B;AACnC,SAAS,0CAA0C;AACnD,SAAS,kCAAkC;AAE3C,SAAS,2BAAoD;AAC7D,SAAS,eAAe,uBAAuB;AAC/C,SAAS,2BAA2B;AACpC,SAAS,yBAAyB;AAClC;AAAA,EACE;AAAA,EACA;AAAA,OACK;AAEP,SAAS,oBAAoB;
|
|
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 { serializeOperationMetadata } from '@open-mercato/shared/lib/commands/operationMetadata'\nimport type { CommandRuntimeContext, CommandBus } from '@open-mercato/shared/lib/commands'\nimport { tagAssignmentSchema, type TagAssignmentInput } from '../../../data/validators'\nimport { CrudHttpError, isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { withScopedPayload } from '../../utils'\nimport {\n runCrudMutationGuardAfterSuccess,\n validateCrudMutationGuard,\n} from '@open-mercato/shared/lib/crud/mutation-guard'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\nimport { getCommandInterceptorHttpRejection } from '@open-mercato/shared/lib/commands/errors'\n\nconst logger = createLogger('customers')\n\nconst TAG_ASSIGNMENT_RESOURCE_KIND = 'customers.tagAssignment'\n\nexport const metadata = {\n POST: { requireAuth: true, requireFeatures: ['customers.activities.manage'] },\n}\n\nasync function buildContext(\n req: Request\n): Promise<{ ctx: CommandRuntimeContext; auth: Awaited<ReturnType<typeof getAuthFromRequest>>; translate: (key: string, fallback?: string) => string }> {\n const container = await createRequestContainer()\n const auth = await getAuthFromRequest(req)\n const { translate } = await resolveTranslations()\n if (!auth) throw new CrudHttpError(401, { error: translate('customers.errors.unauthorized', 'Unauthorized') })\n const scope = await resolveOrganizationScopeForRequest({ container, auth, request: req })\n const ctx: CommandRuntimeContext = {\n container,\n auth,\n organizationScope: scope,\n selectedOrganizationId: scope?.selectedId ?? auth.orgId ?? null,\n organizationIds: scope?.filterIds ?? (auth.orgId ? [auth.orgId] : null),\n request: req,\n }\n return { ctx, auth, translate }\n}\n\nexport async function POST(req: Request) {\n try {\n const { ctx, auth, translate } = await buildContext(req)\n const tenantId = auth!.tenantId\n const organizationId = ctx.selectedOrganizationId\n if (!tenantId || !organizationId) {\n return NextResponse.json({ error: translate('customers.errors.context_required', 'Organization and tenant context required') }, { status: 400 })\n }\n const body = await req.json().catch(() => ({}))\n const scoped = withScopedPayload(body, ctx, translate)\n const input = tagAssignmentSchema.parse(scoped)\n\n const guardResult = await validateCrudMutationGuard(ctx.container, {\n tenantId,\n organizationId,\n userId: auth!.sub,\n resourceKind: TAG_ASSIGNMENT_RESOURCE_KIND,\n resourceId: input.entityId,\n operation: 'custom',\n requestMethod: req.method,\n requestHeaders: req.headers,\n mutationPayload: input,\n })\n if (guardResult && !guardResult.ok) {\n return NextResponse.json(guardResult.body, { status: guardResult.status })\n }\n\n const commandBus = (ctx.container.resolve('commandBus') as CommandBus)\n const { result, logEntry } = await commandBus.execute<TagAssignmentInput, { assignmentId: string | null }>(\n 'customers.tags.unassign',\n { input, ctx },\n )\n if (guardResult?.ok && guardResult.shouldRunAfterSuccess) {\n await runCrudMutationGuardAfterSuccess(ctx.container, {\n tenantId,\n organizationId,\n userId: auth!.sub,\n resourceKind: TAG_ASSIGNMENT_RESOURCE_KIND,\n resourceId: input.entityId,\n operation: 'custom',\n requestMethod: req.method,\n requestHeaders: req.headers,\n metadata: guardResult.metadata ?? null,\n })\n }\n const response = NextResponse.json({ id: result?.assignmentId ?? null })\n if (logEntry?.undoToken && logEntry?.id && logEntry?.commandId) {\n response.headers.set(\n 'x-om-operation',\n serializeOperationMetadata({\n id: logEntry.id,\n undoToken: logEntry.undoToken,\n commandId: logEntry.commandId,\n actionLabel: logEntry.actionLabel ?? null,\n resourceKind: logEntry.resourceKind ?? 'customers.tagAssignment',\n resourceId: logEntry.resourceId ?? result?.assignmentId ?? null,\n executedAt: logEntry.createdAt instanceof Date ? logEntry.createdAt.toISOString() : undefined,\n })\n )\n }\n return response\n } catch (err) {\n if (isCrudHttpError(err)) {\n return NextResponse.json(err.body, { status: err.status })\n }\n const interceptorRejection = getCommandInterceptorHttpRejection(err)\n if (interceptorRejection) {\n return NextResponse.json(interceptorRejection.body, { status: interceptorRejection.status })\n }\n const { translate } = await resolveTranslations()\n logger.error('customers.tags.unassign failed', { err })\n return NextResponse.json({ error: translate('customers.errors.unassign_failed', 'Failed to unassign tag') }, { status: 400 })\n }\n}\n\nconst tagUnassignResponseSchema = z.object({\n id: z.string().uuid().nullable(),\n})\n\nconst tagUnassignErrorSchema = z.object({\n error: z.string(),\n})\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Customers',\n summary: 'Unassign customer tag',\n methods: {\n POST: {\n summary: 'Remove tag from customer entity',\n description: 'Detaches a tag from a customer entity within the validated tenant / organization scope.',\n requestBody: {\n contentType: 'application/json',\n schema: tagAssignmentSchema,\n },\n responses: [\n { status: 200, description: 'Tag unassigned from customer', schema: tagUnassignResponseSchema },\n ],\n errors: [\n { status: 400, description: 'Validation or unassignment failed', schema: tagUnassignErrorSchema },\n { status: 401, description: 'Unauthorized', schema: tagUnassignErrorSchema },\n { status: 403, description: 'Insufficient tenant/organization access', schema: tagUnassignErrorSchema },\n ],\n },\n },\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -29,6 +29,7 @@ import {
|
|
|
29
29
|
resolveLegacyTodoDetails
|
|
30
30
|
} from "../../lib/todoCompatibility.js";
|
|
31
31
|
import { createLogger } from "@open-mercato/shared/lib/logger";
|
|
32
|
+
import { getCommandInterceptorHttpRejection } from "@open-mercato/shared/lib/commands/errors";
|
|
32
33
|
const logger = createLogger("customers");
|
|
33
34
|
const querySchema = z.object({
|
|
34
35
|
page: z.coerce.number().min(1).default(1),
|
|
@@ -349,6 +350,10 @@ async function POST(request) {
|
|
|
349
350
|
if (isCrudHttpError(err)) {
|
|
350
351
|
return withAdapterHeaders(NextResponse.json(err.body, { status: err.status }));
|
|
351
352
|
}
|
|
353
|
+
const interceptorRejection = getCommandInterceptorHttpRejection(err);
|
|
354
|
+
if (interceptorRejection) {
|
|
355
|
+
return withAdapterHeaders(NextResponse.json(interceptorRejection.body, { status: interceptorRejection.status }));
|
|
356
|
+
}
|
|
352
357
|
if (err instanceof z.ZodError) {
|
|
353
358
|
return withAdapterHeaders(
|
|
354
359
|
NextResponse.json({ error: "Validation failed", details: err.issues }, { status: 400 })
|
|
@@ -436,6 +441,10 @@ async function PUT(request) {
|
|
|
436
441
|
if (isCrudHttpError(err)) {
|
|
437
442
|
return withAdapterHeaders(NextResponse.json(err.body, { status: err.status }));
|
|
438
443
|
}
|
|
444
|
+
const interceptorRejection = getCommandInterceptorHttpRejection(err);
|
|
445
|
+
if (interceptorRejection) {
|
|
446
|
+
return withAdapterHeaders(NextResponse.json(interceptorRejection.body, { status: interceptorRejection.status }));
|
|
447
|
+
}
|
|
439
448
|
if (err instanceof z.ZodError) {
|
|
440
449
|
return withAdapterHeaders(
|
|
441
450
|
NextResponse.json({ error: "Validation failed", details: err.issues }, { status: 400 })
|
|
@@ -511,6 +520,10 @@ async function DELETE(request) {
|
|
|
511
520
|
if (isCrudHttpError(err)) {
|
|
512
521
|
return withAdapterHeaders(NextResponse.json(err.body, { status: err.status }));
|
|
513
522
|
}
|
|
523
|
+
const interceptorRejection = getCommandInterceptorHttpRejection(err);
|
|
524
|
+
if (interceptorRejection) {
|
|
525
|
+
return withAdapterHeaders(NextResponse.json(interceptorRejection.body, { status: interceptorRejection.status }));
|
|
526
|
+
}
|
|
514
527
|
if (err instanceof z.ZodError) {
|
|
515
528
|
return withAdapterHeaders(
|
|
516
529
|
NextResponse.json({ error: "Validation failed", details: err.issues }, { status: 400 })
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../src/modules/customers/api/todos/route.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * @deprecated Use /api/customers/interactions instead. This route is maintained\n * as a compatibility bridge per SPEC-046b. Will be removed in a future release.\n */\nimport { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport type { CommandBus } from '@open-mercato/shared/lib/commands'\nimport { isCrudHttpError, notFound } from '@open-mercato/shared/lib/crud/errors'\nimport { parseBooleanToken } from '@open-mercato/shared/lib/boolean'\nimport { isUnrestrictedOrganizationScope } from '@open-mercato/shared/lib/auth/organizationAccess'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport {\n runCrudMutationGuardAfterSuccess,\n validateCrudMutationGuard,\n} from '@open-mercato/shared/lib/crud/mutation-guard'\nimport { readJsonSafe } from '@open-mercato/shared/lib/http/readJsonSafe'\nimport type { QueryEngine } from '@open-mercato/shared/lib/query/types'\nimport { createCustomersCrudOpenApi, createPagedListResponseSchema, defaultOkResponseSchema } from '../openapi'\nimport { CustomerInteraction, CustomerTodoLink } from '../../data/entities'\nimport { todoLinkWithTodoCreateSchema } from '../../data/validators'\nimport { withOperationMetadata } from '../../lib/operationMetadata'\nimport { resolveCustomerInteractionFeatureFlags } from '../../lib/interactionFeatureFlags'\nimport { resolveCustomersRequestContext } from '../../lib/interactionRequestContext'\nimport {\n EXAMPLE_TODO_SOURCE,\n CUSTOMER_INTERACTION_TODO_ADAPTER_SOURCE,\n} from '../../lib/interactionCompatibility'\nimport {\n filterTodoRows,\n listCanonicalTodoRows,\n listLegacyTodoRows,\n normalizeTodoSearch,\n paginateTodoRows,\n sortTodoRows,\n resolveLegacyTodoDetails,\n} from '../../lib/todoCompatibility'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\n\nconst logger = createLogger('customers')\n\nconst querySchema = z.object({\n page: z.coerce.number().min(1).default(1),\n pageSize: z.coerce.number().min(1).max(100).default(50),\n search: z.string().optional(),\n all: z.string().optional(),\n entityId: z.string().uuid().optional(),\n})\n\nconst todoCreateBodySchema = todoLinkWithTodoCreateSchema.omit({\n tenantId: true,\n organizationId: true,\n}).passthrough()\n\nconst todoUpdateBodySchema = z\n .object({\n id: z.string().uuid(),\n linkId: z.string().uuid().optional(),\n title: z.string().trim().min(1).max(200).optional(),\n isDone: z.boolean().optional(),\n is_done: z.boolean().optional(),\n customFields: z.record(z.string(), z.unknown()).optional(),\n customValues: z.record(z.string(), z.unknown()).optional(),\n })\n .passthrough()\n\nconst todoDeleteBodySchema = z.object({\n id: z.string().uuid(),\n todoId: z.string().uuid().optional(),\n})\n\nexport const metadata = {\n GET: { requireAuth: true, requireFeatures: ['customers.view'] },\n POST: { requireAuth: true, requireFeatures: ['customers.interactions.manage'] },\n PUT: { requireAuth: true, requireFeatures: ['customers.interactions.manage'] },\n DELETE: { requireAuth: true, requireFeatures: ['customers.interactions.manage'] },\n}\n\nconst DEPRECATION_HEADERS = {\n Deprecation: 'true',\n Sunset: 'Tue, 30 Jun 2026 00:00:00 GMT',\n Link: '</api/customers/interactions>; rel=\"successor-version\"',\n}\n\n// Caps the per-source fetch window used by the deprecated merged (legacy +\n// canonical bridge) read path. Keeps memory bounded on tenants with large\n// todo history; deep-pagination beyond this window is not supported here \u2014\n// use /api/customers/interactions instead.\nconst MERGED_TODO_FETCH_CAP = 2000\n\nfunction resolveGuardUserId(auth: {\n sub?: string | null\n userId?: string | null\n keyId?: string | null\n}): string {\n if (typeof auth.sub === 'string' && auth.sub.trim().length > 0) return auth.sub\n if (typeof auth.userId === 'string' && auth.userId.trim().length > 0) return auth.userId\n if (typeof auth.keyId === 'string' && auth.keyId.trim().length > 0) return auth.keyId\n return 'system'\n}\n\nfunction withAdapterHeaders(response: Response): Response {\n const headers = new Headers(response.headers)\n Object.entries(DEPRECATION_HEADERS).forEach(([key, value]) => headers.set(key, value))\n return new Response(response.body, {\n status: response.status,\n statusText: response.statusText,\n headers,\n })\n}\n\nasync function legacyAdaptersDisabledResponse(): Promise<Response> {\n const { translate } = await resolveTranslations()\n return withAdapterHeaders(NextResponse.json(\n {\n error: translate(\n 'customers.interactions.legacyAdapters.disabled',\n 'This legacy adapter has been disabled. Use /api/customers/interactions instead.',\n ),\n },\n { status: 410 },\n ))\n}\n\nfunction normalizeTodoStatusInput(body: z.infer<typeof todoUpdateBodySchema>): boolean | undefined {\n if (typeof body.isDone === 'boolean') return body.isDone\n if (typeof body.is_done === 'boolean') return body.is_done\n return undefined\n}\n\nfunction collectTodoCustomValues(\n body: Record<string, unknown>,\n): Record<string, unknown> | undefined {\n const direct =\n body.customValues && typeof body.customValues === 'object'\n ? { ...(body.customValues as Record<string, unknown>) }\n : {}\n if (body.todoCustom && typeof body.todoCustom === 'object') {\n Object.assign(direct, body.todoCustom as Record<string, unknown>)\n }\n if (body.customFields && typeof body.customFields === 'object') {\n Object.assign(direct, body.customFields as Record<string, unknown>)\n }\n return Object.keys(direct).length > 0 ? direct : undefined\n}\n\nasync function findLegacyTodoLink(\n em: EntityManager,\n target: { linkId?: string; todoId?: string },\n tenantId: string,\n): Promise<CustomerTodoLink | null> {\n if (target.linkId) {\n const byLinkId = await em.findOne(CustomerTodoLink, { id: target.linkId, tenantId }, { populate: ['entity'] })\n if (byLinkId) return byLinkId\n }\n if (target.todoId) {\n return await em.findOne(CustomerTodoLink, { todoId: target.todoId, tenantId }, { populate: ['entity'] })\n }\n return null\n}\n\nasync function ensureCanonicalTodoBridge(\n em: EntityManager,\n queryEngine: QueryEngine,\n commandBus: CommandBus,\n commandContext: Parameters<CommandBus['execute']>[1]['ctx'],\n link: CustomerTodoLink,\n): Promise<string> {\n const existing = await em.findOne(CustomerInteraction, { id: link.todoId, tenantId: link.tenantId })\n if (existing) return existing.id\n\n const detailMap = await resolveLegacyTodoDetails(\n queryEngine,\n [link],\n link.tenantId,\n [link.organizationId],\n )\n const source =\n typeof link.todoSource === 'string' && link.todoSource.trim().length > 0\n ? link.todoSource\n : EXAMPLE_TODO_SOURCE\n const detail = detailMap.get(`${source}:${link.todoId}`) ?? null\n const entityId = typeof link.entity === 'string' ? link.entity : link.entity.id\n\n await commandBus.execute('customers.interactions.create', {\n input: {\n id: link.todoId,\n tenantId: link.tenantId,\n organizationId: link.organizationId,\n entityId,\n interactionType: 'task',\n title: detail?.title ?? null,\n body: detail?.description ?? null,\n status: detail?.isDone ? 'done' : 'planned',\n scheduledAt: detail?.dueAt ?? null,\n priority: detail?.priority ?? null,\n source: CUSTOMER_INTERACTION_TODO_ADAPTER_SOURCE,\n ...(detail?.customValues ? { customValues: detail.customValues } : {}),\n },\n ctx: commandContext,\n })\n\n return link.todoId\n}\n\nasync function resolveCanonicalTodoTargetId(\n em: EntityManager,\n queryEngine: QueryEngine,\n commandBus: CommandBus,\n commandContext: Parameters<CommandBus['execute']>[1]['ctx'],\n target: { todoId?: string; linkId?: string },\n tenantId: string,\n): Promise<string> {\n if (target.todoId) {\n const interaction = await em.findOne(CustomerInteraction, { id: target.todoId, tenantId })\n if (interaction) return interaction.id\n }\n\n const legacyLink = await findLegacyTodoLink(em, target, tenantId)\n if (!legacyLink) {\n if (!target.todoId) throw notFound('Todo not found')\n return target.todoId\n }\n\n return ensureCanonicalTodoBridge(\n em,\n queryEngine,\n commandBus,\n commandContext,\n legacyLink,\n )\n}\n\nexport async function GET(request: Request): Promise<Response> {\n try {\n const { auth, em, organizationIds, container, selectedOrganizationId, scope } =\n await resolveCustomersRequestContext(request)\n const query = querySchema.parse(Object.fromEntries(new URL(request.url).searchParams))\n const flags = await resolveCustomerInteractionFeatureFlags(container, auth.tenantId)\n if (!flags.legacyAdapters) {\n return await legacyAdaptersDisabledResponse()\n }\n const exportAll = parseBooleanToken(query.all) === true\n const isUnrestricted = isUnrestrictedOrganizationScope({\n isSuperAdmin: auth.isSuperAdmin === true,\n allowedOrganizationIds: scope?.allowedIds,\n })\n if (!isUnrestricted && (!organizationIds || organizationIds.length === 0)) {\n logger.warn('customers.todos.list collapsed organization scope', {\n tenantId: auth.tenantId,\n organizationIds,\n selectedId: scope?.selectedId ?? null,\n allowedIdsCount: scope?.allowedIds?.length ?? null,\n })\n return withAdapterHeaders(\n NextResponse.json({\n items: [],\n total: 0,\n page: exportAll ? 1 : query.page,\n pageSize: exportAll ? 0 : query.pageSize,\n totalPages: 1,\n }),\n )\n }\n const queryEngine = container.resolve('queryEngine') as QueryEngine\n const search = normalizeTodoSearch(query.search)\n\n if (flags.unified) {\n const canonical = await listCanonicalTodoRows(\n em,\n container,\n auth,\n selectedOrganizationId,\n organizationIds,\n {\n entityId: query.entityId,\n pagination: exportAll ? null : { page: query.page, pageSize: query.pageSize },\n searchText: search,\n isUnrestricted,\n },\n )\n const total = canonical.total\n return withAdapterHeaders(\n NextResponse.json({\n items: canonical.items,\n total,\n page: exportAll ? 1 : query.page,\n pageSize: exportAll ? canonical.items.length : query.pageSize,\n totalPages: exportAll ? 1 : Math.max(1, Math.ceil(total / query.pageSize)),\n }),\n )\n }\n\n const legacyWindow = exportAll\n ? null\n : Math.min(MERGED_TODO_FETCH_CAP, Math.max(query.pageSize, query.page * query.pageSize + query.pageSize))\n const [legacyRows, canonicalRows] = await Promise.all([\n listLegacyTodoRows(em, queryEngine, auth.tenantId, organizationIds, query.entityId, {\n limit: legacyWindow,\n isUnrestricted,\n }),\n listCanonicalTodoRows(\n em,\n container,\n auth,\n selectedOrganizationId,\n organizationIds,\n {\n entityId: query.entityId,\n includeDeleted: true,\n source: CUSTOMER_INTERACTION_TODO_ADAPTER_SOURCE,\n limit: legacyWindow,\n isUnrestricted,\n },\n ),\n ])\n const mergedRows = [\n ...legacyRows.filter((row) => !canonicalRows.bridgeIds.has(row.todoId)),\n ...canonicalRows.items,\n ]\n const filteredRows = filterTodoRows(sortTodoRows(mergedRows), search)\n const paged = paginateTodoRows(filteredRows, query.page, query.pageSize, exportAll)\n\n return withAdapterHeaders(\n NextResponse.json({\n items: paged.items,\n total: paged.total,\n page: paged.page,\n pageSize: paged.pageSize,\n totalPages: paged.totalPages,\n }),\n )\n } catch (err) {\n if (isCrudHttpError(err)) {\n return withAdapterHeaders(NextResponse.json(err.body, { status: err.status }))\n }\n if (err instanceof z.ZodError) {\n return withAdapterHeaders(\n NextResponse.json({ error: 'Validation failed', details: err.issues }, { status: 400 }),\n )\n }\n logger.error('customers.todos.get failed', { err })\n return withAdapterHeaders(\n NextResponse.json({ error: 'Internal server error' }, { status: 500 }),\n )\n }\n}\n\nexport async function POST(request: Request): Promise<Response> {\n try {\n const { commandContext, container, auth, selectedOrganizationId } = await resolveCustomersRequestContext(request)\n const flags = await resolveCustomerInteractionFeatureFlags(container, auth.tenantId)\n if (!flags.legacyAdapters) {\n return await legacyAdaptersDisabledResponse()\n }\n const commandBus = container.resolve('commandBus') as CommandBus\n const body = todoCreateBodySchema.parse(await readJsonSafe<Record<string, unknown>>(request, {}))\n const guardUserId = resolveGuardUserId(auth)\n const guardResult = await validateCrudMutationGuard(container, {\n tenantId: auth.tenantId,\n organizationId: selectedOrganizationId,\n userId: guardUserId,\n resourceKind: 'customers.todoLink',\n resourceId: body.entityId,\n operation: 'create',\n requestMethod: request.method,\n requestHeaders: request.headers,\n mutationPayload: body,\n })\n if (guardResult && !guardResult.ok) {\n return withAdapterHeaders(NextResponse.json(guardResult.body, { status: guardResult.status }))\n }\n const customValues = collectTodoCustomValues(body as Record<string, unknown>)\n\n const { result, logEntry } = await commandBus.execute('customers.interactions.create', {\n input: {\n tenantId: auth.tenantId,\n organizationId: selectedOrganizationId ?? auth.orgId,\n entityId: body.entityId,\n interactionType: 'task',\n title: body.title,\n status: body.is_done === true || body.isDone === true ? 'done' : 'planned',\n source: CUSTOMER_INTERACTION_TODO_ADAPTER_SOURCE,\n ...(customValues ? {\n customValues,\n priority: typeof customValues.priority === 'number' ? customValues.priority : null,\n body: typeof customValues.description === 'string' ? customValues.description : null,\n scheduledAt:\n typeof customValues.due_at === 'string'\n ? customValues.due_at\n : typeof customValues.dueAt === 'string'\n ? customValues.dueAt\n : null,\n } : {}),\n },\n ctx: commandContext,\n })\n if (guardResult?.ok && guardResult.shouldRunAfterSuccess) {\n await runCrudMutationGuardAfterSuccess(container, {\n tenantId: auth.tenantId,\n organizationId: selectedOrganizationId,\n userId: guardUserId,\n resourceKind: 'customers.todoLink',\n resourceId: body.entityId,\n operation: 'create',\n requestMethod: request.method,\n requestHeaders: request.headers,\n metadata: guardResult.metadata ?? null,\n })\n }\n\n const interactionId =\n result &&\n typeof result === 'object' &&\n 'interactionId' in result &&\n typeof result.interactionId === 'string'\n ? result.interactionId\n : result &&\n typeof result === 'object' &&\n 'id' in result &&\n typeof result.id === 'string'\n ? result.id\n : null\n return withAdapterHeaders(\n withOperationMetadata(\n NextResponse.json(\n {\n linkId: interactionId,\n todoId: interactionId,\n },\n { status: 201 },\n ),\n logEntry,\n { resourceKind: 'customers.todoLink', resourceId: interactionId },\n ),\n )\n } catch (err) {\n if (isCrudHttpError(err)) {\n return withAdapterHeaders(NextResponse.json(err.body, { status: err.status }))\n }\n if (err instanceof z.ZodError) {\n return withAdapterHeaders(\n NextResponse.json({ error: 'Validation failed', details: err.issues }, { status: 400 }),\n )\n }\n logger.error('customers.todos.post failed', { err })\n return withAdapterHeaders(\n NextResponse.json({ error: 'Internal server error' }, { status: 500 }),\n )\n }\n}\n\nexport async function PUT(request: Request): Promise<Response> {\n try {\n const { commandContext, container, em, auth, selectedOrganizationId } = await resolveCustomersRequestContext(request)\n const flags = await resolveCustomerInteractionFeatureFlags(container, auth.tenantId)\n if (!flags.legacyAdapters) {\n return await legacyAdaptersDisabledResponse()\n }\n const commandBus = container.resolve('commandBus') as CommandBus\n const queryEngine = container.resolve('queryEngine') as QueryEngine\n const body = todoUpdateBodySchema.parse(await readJsonSafe<Record<string, unknown>>(request, {}))\n const guardUserId = resolveGuardUserId(auth)\n const guardResult = await validateCrudMutationGuard(container, {\n tenantId: auth.tenantId,\n organizationId: selectedOrganizationId,\n userId: guardUserId,\n resourceKind: 'customers.todoLink',\n resourceId: body.linkId ?? body.id,\n operation: 'update',\n requestMethod: request.method,\n requestHeaders: request.headers,\n mutationPayload: body,\n })\n if (guardResult && !guardResult.ok) {\n return withAdapterHeaders(NextResponse.json(guardResult.body, { status: guardResult.status }))\n }\n const interactionId = flags.unified\n ? body.id\n : await resolveCanonicalTodoTargetId(\n em,\n queryEngine,\n commandBus,\n commandContext,\n {\n todoId: body.id,\n linkId: body.linkId,\n },\n auth.tenantId,\n )\n const customValues = collectTodoCustomValues(body as Record<string, unknown>)\n const nextDone = normalizeTodoStatusInput(body)\n\n const { logEntry } = await commandBus.execute('customers.interactions.update', {\n input: {\n id: interactionId,\n ...(body.title !== undefined ? { title: body.title } : {}),\n ...(nextDone !== undefined ? { status: nextDone ? 'done' : 'planned' } : {}),\n ...(customValues ? {\n customValues,\n priority: typeof customValues.priority === 'number' ? customValues.priority : null,\n body: typeof customValues.description === 'string' ? customValues.description : null,\n scheduledAt:\n typeof customValues.due_at === 'string'\n ? customValues.due_at\n : typeof customValues.dueAt === 'string'\n ? customValues.dueAt\n : null,\n } : {}),\n },\n ctx: commandContext,\n })\n if (guardResult?.ok && guardResult.shouldRunAfterSuccess) {\n await runCrudMutationGuardAfterSuccess(container, {\n tenantId: auth.tenantId,\n organizationId: selectedOrganizationId,\n userId: guardUserId,\n resourceKind: 'customers.todoLink',\n resourceId: body.linkId ?? body.id,\n operation: 'update',\n requestMethod: request.method,\n requestHeaders: request.headers,\n metadata: guardResult.metadata ?? null,\n })\n }\n\n return withAdapterHeaders(\n withOperationMetadata(\n NextResponse.json({ ok: true }),\n logEntry,\n { resourceKind: 'customers.todoLink', resourceId: body.linkId ?? body.id },\n ),\n )\n } catch (err) {\n if (isCrudHttpError(err)) {\n return withAdapterHeaders(NextResponse.json(err.body, { status: err.status }))\n }\n if (err instanceof z.ZodError) {\n return withAdapterHeaders(\n NextResponse.json({ error: 'Validation failed', details: err.issues }, { status: 400 }),\n )\n }\n logger.error('customers.todos.put failed', { err })\n return withAdapterHeaders(\n NextResponse.json({ error: 'Internal server error' }, { status: 500 }),\n )\n }\n}\n\nexport async function DELETE(request: Request): Promise<Response> {\n try {\n const { commandContext, container, em, auth, selectedOrganizationId } = await resolveCustomersRequestContext(request)\n const flags = await resolveCustomerInteractionFeatureFlags(container, auth.tenantId)\n if (!flags.legacyAdapters) {\n return await legacyAdaptersDisabledResponse()\n }\n const commandBus = container.resolve('commandBus') as CommandBus\n const queryEngine = container.resolve('queryEngine') as QueryEngine\n const body = todoDeleteBodySchema.parse(await readJsonSafe<Record<string, unknown>>(request, {}))\n const guardUserId = resolveGuardUserId(auth)\n const guardResult = await validateCrudMutationGuard(container, {\n tenantId: auth.tenantId,\n organizationId: selectedOrganizationId,\n userId: guardUserId,\n resourceKind: 'customers.todoLink',\n resourceId: body.id,\n operation: 'delete',\n requestMethod: request.method,\n requestHeaders: request.headers,\n mutationPayload: body,\n })\n if (guardResult && !guardResult.ok) {\n return withAdapterHeaders(NextResponse.json(guardResult.body, { status: guardResult.status }))\n }\n const interactionId = flags.unified\n ? body.todoId ?? body.id\n : await resolveCanonicalTodoTargetId(\n em,\n queryEngine,\n commandBus,\n commandContext,\n {\n linkId: body.id,\n todoId: body.todoId ?? body.id,\n },\n auth.tenantId,\n )\n\n const { logEntry } = await commandBus.execute('customers.interactions.delete', {\n input: { id: interactionId },\n ctx: commandContext,\n })\n if (guardResult?.ok && guardResult.shouldRunAfterSuccess) {\n await runCrudMutationGuardAfterSuccess(container, {\n tenantId: auth.tenantId,\n organizationId: selectedOrganizationId,\n userId: guardUserId,\n resourceKind: 'customers.todoLink',\n resourceId: body.id,\n operation: 'delete',\n requestMethod: request.method,\n requestHeaders: request.headers,\n metadata: guardResult.metadata ?? null,\n })\n }\n\n return withAdapterHeaders(\n withOperationMetadata(\n NextResponse.json({ ok: true }),\n logEntry,\n { resourceKind: 'customers.todoLink', resourceId: body.id },\n ),\n )\n } catch (err) {\n if (isCrudHttpError(err)) {\n return withAdapterHeaders(NextResponse.json(err.body, { status: err.status }))\n }\n if (err instanceof z.ZodError) {\n return withAdapterHeaders(\n NextResponse.json({ error: 'Validation failed', details: err.issues }, { status: 400 }),\n )\n }\n logger.error('customers.todos.delete failed', { err })\n return withAdapterHeaders(\n NextResponse.json({ error: 'Internal server error' }, { status: 500 }),\n )\n }\n}\n\nconst todoItemSchema = z.object({\n id: z.string(),\n todoId: z.string(),\n todoSource: z.string(),\n todoTitle: z.string().nullable(),\n todoIsDone: z.boolean().nullable(),\n todoPriority: z.number().nullable().optional(),\n todoSeverity: z.string().nullable().optional(),\n todoDescription: z.string().nullable().optional(),\n todoDueAt: z.string().nullable().optional(),\n todoCustomValues: z.record(z.string(), z.unknown()).nullable().optional(),\n todoOrganizationId: z.string().nullable(),\n todoUpdatedAt: z.string().nullable().optional(),\n organizationId: z.string(),\n tenantId: z.string(),\n createdAt: z.string(),\n externalHref: z.string().nullable().optional(),\n _integrations: z.record(z.string(), z.unknown()).optional(),\n customer: z.object({\n id: z.string().nullable(),\n displayName: z.string().nullable(),\n kind: z.string().nullable(),\n }),\n})\n\nconst todoCreateResponseSchema = z.object({\n linkId: z.string().uuid().nullable(),\n todoId: z.string().uuid().nullable(),\n})\n\nexport const openApi: OpenApiRouteDoc = createCustomersCrudOpenApi({\n resourceName: 'CustomerTodo',\n querySchema,\n listResponseSchema: createPagedListResponseSchema(todoItemSchema),\n create: {\n schema: todoCreateBodySchema,\n responseSchema: todoCreateResponseSchema,\n description: 'DEPRECATED (sunset 2026-06-30): Creates a customer task. Use POST /api/customers/interactions instead.',\n },\n update: {\n schema: todoUpdateBodySchema,\n responseSchema: defaultOkResponseSchema,\n description: 'DEPRECATED (sunset 2026-06-30): Updates a customer task. Use PUT /api/customers/interactions instead.',\n },\n del: {\n schema: todoDeleteBodySchema,\n responseSchema: defaultOkResponseSchema,\n description: 'DEPRECATED (sunset 2026-06-30): Deletes a customer task. Use DELETE /api/customers/interactions instead.',\n },\n})\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["/**\n * @deprecated Use /api/customers/interactions instead. This route is maintained\n * as a compatibility bridge per SPEC-046b. Will be removed in a future release.\n */\nimport { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport type { CommandBus } from '@open-mercato/shared/lib/commands'\nimport { isCrudHttpError, notFound } from '@open-mercato/shared/lib/crud/errors'\nimport { parseBooleanToken } from '@open-mercato/shared/lib/boolean'\nimport { isUnrestrictedOrganizationScope } from '@open-mercato/shared/lib/auth/organizationAccess'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport {\n runCrudMutationGuardAfterSuccess,\n validateCrudMutationGuard,\n} from '@open-mercato/shared/lib/crud/mutation-guard'\nimport { readJsonSafe } from '@open-mercato/shared/lib/http/readJsonSafe'\nimport type { QueryEngine } from '@open-mercato/shared/lib/query/types'\nimport { createCustomersCrudOpenApi, createPagedListResponseSchema, defaultOkResponseSchema } from '../openapi'\nimport { CustomerInteraction, CustomerTodoLink } from '../../data/entities'\nimport { todoLinkWithTodoCreateSchema } from '../../data/validators'\nimport { withOperationMetadata } from '../../lib/operationMetadata'\nimport { resolveCustomerInteractionFeatureFlags } from '../../lib/interactionFeatureFlags'\nimport { resolveCustomersRequestContext } from '../../lib/interactionRequestContext'\nimport {\n EXAMPLE_TODO_SOURCE,\n CUSTOMER_INTERACTION_TODO_ADAPTER_SOURCE,\n} from '../../lib/interactionCompatibility'\nimport {\n filterTodoRows,\n listCanonicalTodoRows,\n listLegacyTodoRows,\n normalizeTodoSearch,\n paginateTodoRows,\n sortTodoRows,\n resolveLegacyTodoDetails,\n} from '../../lib/todoCompatibility'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\nimport { getCommandInterceptorHttpRejection } from '@open-mercato/shared/lib/commands/errors'\n\nconst logger = createLogger('customers')\n\nconst querySchema = z.object({\n page: z.coerce.number().min(1).default(1),\n pageSize: z.coerce.number().min(1).max(100).default(50),\n search: z.string().optional(),\n all: z.string().optional(),\n entityId: z.string().uuid().optional(),\n})\n\nconst todoCreateBodySchema = todoLinkWithTodoCreateSchema.omit({\n tenantId: true,\n organizationId: true,\n}).passthrough()\n\nconst todoUpdateBodySchema = z\n .object({\n id: z.string().uuid(),\n linkId: z.string().uuid().optional(),\n title: z.string().trim().min(1).max(200).optional(),\n isDone: z.boolean().optional(),\n is_done: z.boolean().optional(),\n customFields: z.record(z.string(), z.unknown()).optional(),\n customValues: z.record(z.string(), z.unknown()).optional(),\n })\n .passthrough()\n\nconst todoDeleteBodySchema = z.object({\n id: z.string().uuid(),\n todoId: z.string().uuid().optional(),\n})\n\nexport const metadata = {\n GET: { requireAuth: true, requireFeatures: ['customers.view'] },\n POST: { requireAuth: true, requireFeatures: ['customers.interactions.manage'] },\n PUT: { requireAuth: true, requireFeatures: ['customers.interactions.manage'] },\n DELETE: { requireAuth: true, requireFeatures: ['customers.interactions.manage'] },\n}\n\nconst DEPRECATION_HEADERS = {\n Deprecation: 'true',\n Sunset: 'Tue, 30 Jun 2026 00:00:00 GMT',\n Link: '</api/customers/interactions>; rel=\"successor-version\"',\n}\n\n// Caps the per-source fetch window used by the deprecated merged (legacy +\n// canonical bridge) read path. Keeps memory bounded on tenants with large\n// todo history; deep-pagination beyond this window is not supported here \u2014\n// use /api/customers/interactions instead.\nconst MERGED_TODO_FETCH_CAP = 2000\n\nfunction resolveGuardUserId(auth: {\n sub?: string | null\n userId?: string | null\n keyId?: string | null\n}): string {\n if (typeof auth.sub === 'string' && auth.sub.trim().length > 0) return auth.sub\n if (typeof auth.userId === 'string' && auth.userId.trim().length > 0) return auth.userId\n if (typeof auth.keyId === 'string' && auth.keyId.trim().length > 0) return auth.keyId\n return 'system'\n}\n\nfunction withAdapterHeaders(response: Response): Response {\n const headers = new Headers(response.headers)\n Object.entries(DEPRECATION_HEADERS).forEach(([key, value]) => headers.set(key, value))\n return new Response(response.body, {\n status: response.status,\n statusText: response.statusText,\n headers,\n })\n}\n\nasync function legacyAdaptersDisabledResponse(): Promise<Response> {\n const { translate } = await resolveTranslations()\n return withAdapterHeaders(NextResponse.json(\n {\n error: translate(\n 'customers.interactions.legacyAdapters.disabled',\n 'This legacy adapter has been disabled. Use /api/customers/interactions instead.',\n ),\n },\n { status: 410 },\n ))\n}\n\nfunction normalizeTodoStatusInput(body: z.infer<typeof todoUpdateBodySchema>): boolean | undefined {\n if (typeof body.isDone === 'boolean') return body.isDone\n if (typeof body.is_done === 'boolean') return body.is_done\n return undefined\n}\n\nfunction collectTodoCustomValues(\n body: Record<string, unknown>,\n): Record<string, unknown> | undefined {\n const direct =\n body.customValues && typeof body.customValues === 'object'\n ? { ...(body.customValues as Record<string, unknown>) }\n : {}\n if (body.todoCustom && typeof body.todoCustom === 'object') {\n Object.assign(direct, body.todoCustom as Record<string, unknown>)\n }\n if (body.customFields && typeof body.customFields === 'object') {\n Object.assign(direct, body.customFields as Record<string, unknown>)\n }\n return Object.keys(direct).length > 0 ? direct : undefined\n}\n\nasync function findLegacyTodoLink(\n em: EntityManager,\n target: { linkId?: string; todoId?: string },\n tenantId: string,\n): Promise<CustomerTodoLink | null> {\n if (target.linkId) {\n const byLinkId = await em.findOne(CustomerTodoLink, { id: target.linkId, tenantId }, { populate: ['entity'] })\n if (byLinkId) return byLinkId\n }\n if (target.todoId) {\n return await em.findOne(CustomerTodoLink, { todoId: target.todoId, tenantId }, { populate: ['entity'] })\n }\n return null\n}\n\nasync function ensureCanonicalTodoBridge(\n em: EntityManager,\n queryEngine: QueryEngine,\n commandBus: CommandBus,\n commandContext: Parameters<CommandBus['execute']>[1]['ctx'],\n link: CustomerTodoLink,\n): Promise<string> {\n const existing = await em.findOne(CustomerInteraction, { id: link.todoId, tenantId: link.tenantId })\n if (existing) return existing.id\n\n const detailMap = await resolveLegacyTodoDetails(\n queryEngine,\n [link],\n link.tenantId,\n [link.organizationId],\n )\n const source =\n typeof link.todoSource === 'string' && link.todoSource.trim().length > 0\n ? link.todoSource\n : EXAMPLE_TODO_SOURCE\n const detail = detailMap.get(`${source}:${link.todoId}`) ?? null\n const entityId = typeof link.entity === 'string' ? link.entity : link.entity.id\n\n await commandBus.execute('customers.interactions.create', {\n input: {\n id: link.todoId,\n tenantId: link.tenantId,\n organizationId: link.organizationId,\n entityId,\n interactionType: 'task',\n title: detail?.title ?? null,\n body: detail?.description ?? null,\n status: detail?.isDone ? 'done' : 'planned',\n scheduledAt: detail?.dueAt ?? null,\n priority: detail?.priority ?? null,\n source: CUSTOMER_INTERACTION_TODO_ADAPTER_SOURCE,\n ...(detail?.customValues ? { customValues: detail.customValues } : {}),\n },\n ctx: commandContext,\n })\n\n return link.todoId\n}\n\nasync function resolveCanonicalTodoTargetId(\n em: EntityManager,\n queryEngine: QueryEngine,\n commandBus: CommandBus,\n commandContext: Parameters<CommandBus['execute']>[1]['ctx'],\n target: { todoId?: string; linkId?: string },\n tenantId: string,\n): Promise<string> {\n if (target.todoId) {\n const interaction = await em.findOne(CustomerInteraction, { id: target.todoId, tenantId })\n if (interaction) return interaction.id\n }\n\n const legacyLink = await findLegacyTodoLink(em, target, tenantId)\n if (!legacyLink) {\n if (!target.todoId) throw notFound('Todo not found')\n return target.todoId\n }\n\n return ensureCanonicalTodoBridge(\n em,\n queryEngine,\n commandBus,\n commandContext,\n legacyLink,\n )\n}\n\nexport async function GET(request: Request): Promise<Response> {\n try {\n const { auth, em, organizationIds, container, selectedOrganizationId, scope } =\n await resolveCustomersRequestContext(request)\n const query = querySchema.parse(Object.fromEntries(new URL(request.url).searchParams))\n const flags = await resolveCustomerInteractionFeatureFlags(container, auth.tenantId)\n if (!flags.legacyAdapters) {\n return await legacyAdaptersDisabledResponse()\n }\n const exportAll = parseBooleanToken(query.all) === true\n const isUnrestricted = isUnrestrictedOrganizationScope({\n isSuperAdmin: auth.isSuperAdmin === true,\n allowedOrganizationIds: scope?.allowedIds,\n })\n if (!isUnrestricted && (!organizationIds || organizationIds.length === 0)) {\n logger.warn('customers.todos.list collapsed organization scope', {\n tenantId: auth.tenantId,\n organizationIds,\n selectedId: scope?.selectedId ?? null,\n allowedIdsCount: scope?.allowedIds?.length ?? null,\n })\n return withAdapterHeaders(\n NextResponse.json({\n items: [],\n total: 0,\n page: exportAll ? 1 : query.page,\n pageSize: exportAll ? 0 : query.pageSize,\n totalPages: 1,\n }),\n )\n }\n const queryEngine = container.resolve('queryEngine') as QueryEngine\n const search = normalizeTodoSearch(query.search)\n\n if (flags.unified) {\n const canonical = await listCanonicalTodoRows(\n em,\n container,\n auth,\n selectedOrganizationId,\n organizationIds,\n {\n entityId: query.entityId,\n pagination: exportAll ? null : { page: query.page, pageSize: query.pageSize },\n searchText: search,\n isUnrestricted,\n },\n )\n const total = canonical.total\n return withAdapterHeaders(\n NextResponse.json({\n items: canonical.items,\n total,\n page: exportAll ? 1 : query.page,\n pageSize: exportAll ? canonical.items.length : query.pageSize,\n totalPages: exportAll ? 1 : Math.max(1, Math.ceil(total / query.pageSize)),\n }),\n )\n }\n\n const legacyWindow = exportAll\n ? null\n : Math.min(MERGED_TODO_FETCH_CAP, Math.max(query.pageSize, query.page * query.pageSize + query.pageSize))\n const [legacyRows, canonicalRows] = await Promise.all([\n listLegacyTodoRows(em, queryEngine, auth.tenantId, organizationIds, query.entityId, {\n limit: legacyWindow,\n isUnrestricted,\n }),\n listCanonicalTodoRows(\n em,\n container,\n auth,\n selectedOrganizationId,\n organizationIds,\n {\n entityId: query.entityId,\n includeDeleted: true,\n source: CUSTOMER_INTERACTION_TODO_ADAPTER_SOURCE,\n limit: legacyWindow,\n isUnrestricted,\n },\n ),\n ])\n const mergedRows = [\n ...legacyRows.filter((row) => !canonicalRows.bridgeIds.has(row.todoId)),\n ...canonicalRows.items,\n ]\n const filteredRows = filterTodoRows(sortTodoRows(mergedRows), search)\n const paged = paginateTodoRows(filteredRows, query.page, query.pageSize, exportAll)\n\n return withAdapterHeaders(\n NextResponse.json({\n items: paged.items,\n total: paged.total,\n page: paged.page,\n pageSize: paged.pageSize,\n totalPages: paged.totalPages,\n }),\n )\n } catch (err) {\n if (isCrudHttpError(err)) {\n return withAdapterHeaders(NextResponse.json(err.body, { status: err.status }))\n }\n if (err instanceof z.ZodError) {\n return withAdapterHeaders(\n NextResponse.json({ error: 'Validation failed', details: err.issues }, { status: 400 }),\n )\n }\n logger.error('customers.todos.get failed', { err })\n return withAdapterHeaders(\n NextResponse.json({ error: 'Internal server error' }, { status: 500 }),\n )\n }\n}\n\nexport async function POST(request: Request): Promise<Response> {\n try {\n const { commandContext, container, auth, selectedOrganizationId } = await resolveCustomersRequestContext(request)\n const flags = await resolveCustomerInteractionFeatureFlags(container, auth.tenantId)\n if (!flags.legacyAdapters) {\n return await legacyAdaptersDisabledResponse()\n }\n const commandBus = container.resolve('commandBus') as CommandBus\n const body = todoCreateBodySchema.parse(await readJsonSafe<Record<string, unknown>>(request, {}))\n const guardUserId = resolveGuardUserId(auth)\n const guardResult = await validateCrudMutationGuard(container, {\n tenantId: auth.tenantId,\n organizationId: selectedOrganizationId,\n userId: guardUserId,\n resourceKind: 'customers.todoLink',\n resourceId: body.entityId,\n operation: 'create',\n requestMethod: request.method,\n requestHeaders: request.headers,\n mutationPayload: body,\n })\n if (guardResult && !guardResult.ok) {\n return withAdapterHeaders(NextResponse.json(guardResult.body, { status: guardResult.status }))\n }\n const customValues = collectTodoCustomValues(body as Record<string, unknown>)\n\n const { result, logEntry } = await commandBus.execute('customers.interactions.create', {\n input: {\n tenantId: auth.tenantId,\n organizationId: selectedOrganizationId ?? auth.orgId,\n entityId: body.entityId,\n interactionType: 'task',\n title: body.title,\n status: body.is_done === true || body.isDone === true ? 'done' : 'planned',\n source: CUSTOMER_INTERACTION_TODO_ADAPTER_SOURCE,\n ...(customValues ? {\n customValues,\n priority: typeof customValues.priority === 'number' ? customValues.priority : null,\n body: typeof customValues.description === 'string' ? customValues.description : null,\n scheduledAt:\n typeof customValues.due_at === 'string'\n ? customValues.due_at\n : typeof customValues.dueAt === 'string'\n ? customValues.dueAt\n : null,\n } : {}),\n },\n ctx: commandContext,\n })\n if (guardResult?.ok && guardResult.shouldRunAfterSuccess) {\n await runCrudMutationGuardAfterSuccess(container, {\n tenantId: auth.tenantId,\n organizationId: selectedOrganizationId,\n userId: guardUserId,\n resourceKind: 'customers.todoLink',\n resourceId: body.entityId,\n operation: 'create',\n requestMethod: request.method,\n requestHeaders: request.headers,\n metadata: guardResult.metadata ?? null,\n })\n }\n\n const interactionId =\n result &&\n typeof result === 'object' &&\n 'interactionId' in result &&\n typeof result.interactionId === 'string'\n ? result.interactionId\n : result &&\n typeof result === 'object' &&\n 'id' in result &&\n typeof result.id === 'string'\n ? result.id\n : null\n return withAdapterHeaders(\n withOperationMetadata(\n NextResponse.json(\n {\n linkId: interactionId,\n todoId: interactionId,\n },\n { status: 201 },\n ),\n logEntry,\n { resourceKind: 'customers.todoLink', resourceId: interactionId },\n ),\n )\n } catch (err) {\n if (isCrudHttpError(err)) {\n return withAdapterHeaders(NextResponse.json(err.body, { status: err.status }))\n }\n const interceptorRejection = getCommandInterceptorHttpRejection(err)\n if (interceptorRejection) {\n return withAdapterHeaders(NextResponse.json(interceptorRejection.body, { status: interceptorRejection.status }))\n }\n if (err instanceof z.ZodError) {\n return withAdapterHeaders(\n NextResponse.json({ error: 'Validation failed', details: err.issues }, { status: 400 }),\n )\n }\n logger.error('customers.todos.post failed', { err })\n return withAdapterHeaders(\n NextResponse.json({ error: 'Internal server error' }, { status: 500 }),\n )\n }\n}\n\nexport async function PUT(request: Request): Promise<Response> {\n try {\n const { commandContext, container, em, auth, selectedOrganizationId } = await resolveCustomersRequestContext(request)\n const flags = await resolveCustomerInteractionFeatureFlags(container, auth.tenantId)\n if (!flags.legacyAdapters) {\n return await legacyAdaptersDisabledResponse()\n }\n const commandBus = container.resolve('commandBus') as CommandBus\n const queryEngine = container.resolve('queryEngine') as QueryEngine\n const body = todoUpdateBodySchema.parse(await readJsonSafe<Record<string, unknown>>(request, {}))\n const guardUserId = resolveGuardUserId(auth)\n const guardResult = await validateCrudMutationGuard(container, {\n tenantId: auth.tenantId,\n organizationId: selectedOrganizationId,\n userId: guardUserId,\n resourceKind: 'customers.todoLink',\n resourceId: body.linkId ?? body.id,\n operation: 'update',\n requestMethod: request.method,\n requestHeaders: request.headers,\n mutationPayload: body,\n })\n if (guardResult && !guardResult.ok) {\n return withAdapterHeaders(NextResponse.json(guardResult.body, { status: guardResult.status }))\n }\n const interactionId = flags.unified\n ? body.id\n : await resolveCanonicalTodoTargetId(\n em,\n queryEngine,\n commandBus,\n commandContext,\n {\n todoId: body.id,\n linkId: body.linkId,\n },\n auth.tenantId,\n )\n const customValues = collectTodoCustomValues(body as Record<string, unknown>)\n const nextDone = normalizeTodoStatusInput(body)\n\n const { logEntry } = await commandBus.execute('customers.interactions.update', {\n input: {\n id: interactionId,\n ...(body.title !== undefined ? { title: body.title } : {}),\n ...(nextDone !== undefined ? { status: nextDone ? 'done' : 'planned' } : {}),\n ...(customValues ? {\n customValues,\n priority: typeof customValues.priority === 'number' ? customValues.priority : null,\n body: typeof customValues.description === 'string' ? customValues.description : null,\n scheduledAt:\n typeof customValues.due_at === 'string'\n ? customValues.due_at\n : typeof customValues.dueAt === 'string'\n ? customValues.dueAt\n : null,\n } : {}),\n },\n ctx: commandContext,\n })\n if (guardResult?.ok && guardResult.shouldRunAfterSuccess) {\n await runCrudMutationGuardAfterSuccess(container, {\n tenantId: auth.tenantId,\n organizationId: selectedOrganizationId,\n userId: guardUserId,\n resourceKind: 'customers.todoLink',\n resourceId: body.linkId ?? body.id,\n operation: 'update',\n requestMethod: request.method,\n requestHeaders: request.headers,\n metadata: guardResult.metadata ?? null,\n })\n }\n\n return withAdapterHeaders(\n withOperationMetadata(\n NextResponse.json({ ok: true }),\n logEntry,\n { resourceKind: 'customers.todoLink', resourceId: body.linkId ?? body.id },\n ),\n )\n } catch (err) {\n if (isCrudHttpError(err)) {\n return withAdapterHeaders(NextResponse.json(err.body, { status: err.status }))\n }\n const interceptorRejection = getCommandInterceptorHttpRejection(err)\n if (interceptorRejection) {\n return withAdapterHeaders(NextResponse.json(interceptorRejection.body, { status: interceptorRejection.status }))\n }\n if (err instanceof z.ZodError) {\n return withAdapterHeaders(\n NextResponse.json({ error: 'Validation failed', details: err.issues }, { status: 400 }),\n )\n }\n logger.error('customers.todos.put failed', { err })\n return withAdapterHeaders(\n NextResponse.json({ error: 'Internal server error' }, { status: 500 }),\n )\n }\n}\n\nexport async function DELETE(request: Request): Promise<Response> {\n try {\n const { commandContext, container, em, auth, selectedOrganizationId } = await resolveCustomersRequestContext(request)\n const flags = await resolveCustomerInteractionFeatureFlags(container, auth.tenantId)\n if (!flags.legacyAdapters) {\n return await legacyAdaptersDisabledResponse()\n }\n const commandBus = container.resolve('commandBus') as CommandBus\n const queryEngine = container.resolve('queryEngine') as QueryEngine\n const body = todoDeleteBodySchema.parse(await readJsonSafe<Record<string, unknown>>(request, {}))\n const guardUserId = resolveGuardUserId(auth)\n const guardResult = await validateCrudMutationGuard(container, {\n tenantId: auth.tenantId,\n organizationId: selectedOrganizationId,\n userId: guardUserId,\n resourceKind: 'customers.todoLink',\n resourceId: body.id,\n operation: 'delete',\n requestMethod: request.method,\n requestHeaders: request.headers,\n mutationPayload: body,\n })\n if (guardResult && !guardResult.ok) {\n return withAdapterHeaders(NextResponse.json(guardResult.body, { status: guardResult.status }))\n }\n const interactionId = flags.unified\n ? body.todoId ?? body.id\n : await resolveCanonicalTodoTargetId(\n em,\n queryEngine,\n commandBus,\n commandContext,\n {\n linkId: body.id,\n todoId: body.todoId ?? body.id,\n },\n auth.tenantId,\n )\n\n const { logEntry } = await commandBus.execute('customers.interactions.delete', {\n input: { id: interactionId },\n ctx: commandContext,\n })\n if (guardResult?.ok && guardResult.shouldRunAfterSuccess) {\n await runCrudMutationGuardAfterSuccess(container, {\n tenantId: auth.tenantId,\n organizationId: selectedOrganizationId,\n userId: guardUserId,\n resourceKind: 'customers.todoLink',\n resourceId: body.id,\n operation: 'delete',\n requestMethod: request.method,\n requestHeaders: request.headers,\n metadata: guardResult.metadata ?? null,\n })\n }\n\n return withAdapterHeaders(\n withOperationMetadata(\n NextResponse.json({ ok: true }),\n logEntry,\n { resourceKind: 'customers.todoLink', resourceId: body.id },\n ),\n )\n } catch (err) {\n if (isCrudHttpError(err)) {\n return withAdapterHeaders(NextResponse.json(err.body, { status: err.status }))\n }\n const interceptorRejection = getCommandInterceptorHttpRejection(err)\n if (interceptorRejection) {\n return withAdapterHeaders(NextResponse.json(interceptorRejection.body, { status: interceptorRejection.status }))\n }\n if (err instanceof z.ZodError) {\n return withAdapterHeaders(\n NextResponse.json({ error: 'Validation failed', details: err.issues }, { status: 400 }),\n )\n }\n logger.error('customers.todos.delete failed', { err })\n return withAdapterHeaders(\n NextResponse.json({ error: 'Internal server error' }, { status: 500 }),\n )\n }\n}\n\nconst todoItemSchema = z.object({\n id: z.string(),\n todoId: z.string(),\n todoSource: z.string(),\n todoTitle: z.string().nullable(),\n todoIsDone: z.boolean().nullable(),\n todoPriority: z.number().nullable().optional(),\n todoSeverity: z.string().nullable().optional(),\n todoDescription: z.string().nullable().optional(),\n todoDueAt: z.string().nullable().optional(),\n todoCustomValues: z.record(z.string(), z.unknown()).nullable().optional(),\n todoOrganizationId: z.string().nullable(),\n todoUpdatedAt: z.string().nullable().optional(),\n organizationId: z.string(),\n tenantId: z.string(),\n createdAt: z.string(),\n externalHref: z.string().nullable().optional(),\n _integrations: z.record(z.string(), z.unknown()).optional(),\n customer: z.object({\n id: z.string().nullable(),\n displayName: z.string().nullable(),\n kind: z.string().nullable(),\n }),\n})\n\nconst todoCreateResponseSchema = z.object({\n linkId: z.string().uuid().nullable(),\n todoId: z.string().uuid().nullable(),\n})\n\nexport const openApi: OpenApiRouteDoc = createCustomersCrudOpenApi({\n resourceName: 'CustomerTodo',\n querySchema,\n listResponseSchema: createPagedListResponseSchema(todoItemSchema),\n create: {\n schema: todoCreateBodySchema,\n responseSchema: todoCreateResponseSchema,\n description: 'DEPRECATED (sunset 2026-06-30): Creates a customer task. Use POST /api/customers/interactions instead.',\n },\n update: {\n schema: todoUpdateBodySchema,\n responseSchema: defaultOkResponseSchema,\n description: 'DEPRECATED (sunset 2026-06-30): Updates a customer task. Use PUT /api/customers/interactions instead.',\n },\n del: {\n schema: todoDeleteBodySchema,\n responseSchema: defaultOkResponseSchema,\n description: 'DEPRECATED (sunset 2026-06-30): Deletes a customer task. Use DELETE /api/customers/interactions instead.',\n },\n})\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -5,31 +5,6 @@ import { updateCrud } from "@open-mercato/ui/backend/utils/crud";
|
|
|
5
5
|
import { readApiResultOrThrow, withScopedApiRequestHeaders } from "@open-mercato/ui/backend/utils/apiCall";
|
|
6
6
|
import { buildOptimisticLockHeader } from "@open-mercato/ui/backend/utils/optimisticLock";
|
|
7
7
|
import { useT } from "@open-mercato/shared/lib/i18n/context";
|
|
8
|
-
function normalizePersonAssociationRecord(record, fallbackId) {
|
|
9
|
-
const displayName = typeof record.displayName === "string" && record.displayName.trim().length ? record.displayName.trim() : typeof record.display_name === "string" && record.display_name.trim().length ? record.display_name.trim() : null;
|
|
10
|
-
const email = typeof record.primaryEmail === "string" && record.primaryEmail.trim().length ? record.primaryEmail.trim() : typeof record.primary_email === "string" && record.primary_email.trim().length ? record.primary_email.trim() : null;
|
|
11
|
-
const phone = typeof record.primaryPhone === "string" && record.primaryPhone.trim().length ? record.primaryPhone.trim() : typeof record.primary_phone === "string" && record.primary_phone.trim().length ? record.primary_phone.trim() : null;
|
|
12
|
-
const profile = record.personProfile ?? record.person_profile ?? null;
|
|
13
|
-
const jobTitle = profile && typeof profile.jobTitle === "string" && profile.jobTitle.trim().length ? profile.jobTitle.trim() : null;
|
|
14
|
-
return {
|
|
15
|
-
id: typeof record.id === "string" ? record.id : fallbackId,
|
|
16
|
-
label: displayName ?? email ?? phone ?? fallbackId,
|
|
17
|
-
subtitle: jobTitle ?? email ?? phone ?? null,
|
|
18
|
-
kind: "person"
|
|
19
|
-
};
|
|
20
|
-
}
|
|
21
|
-
function normalizeCompanyAssociationRecord(record, fallbackId) {
|
|
22
|
-
const displayName = typeof record.displayName === "string" && record.displayName.trim().length ? record.displayName.trim() : typeof record.display_name === "string" && record.display_name.trim().length ? record.display_name.trim() : null;
|
|
23
|
-
const profile = record.companyProfile ?? record.company_profile ?? null;
|
|
24
|
-
const domain = typeof record.domain === "string" && record.domain.trim().length ? record.domain.trim() : profile && typeof profile.domain === "string" && profile.domain.trim().length ? profile.domain.trim() : null;
|
|
25
|
-
const website = typeof record.websiteUrl === "string" && record.websiteUrl.trim().length ? record.websiteUrl.trim() : typeof record.website_url === "string" && record.website_url.trim().length ? record.website_url.trim() : profile && typeof profile.websiteUrl === "string" && profile.websiteUrl.trim().length ? profile.websiteUrl.trim() : null;
|
|
26
|
-
return {
|
|
27
|
-
id: typeof record.id === "string" ? record.id : fallbackId,
|
|
28
|
-
label: displayName ?? domain ?? website ?? fallbackId,
|
|
29
|
-
subtitle: domain ?? website ?? null,
|
|
30
|
-
kind: "company"
|
|
31
|
-
};
|
|
32
|
-
}
|
|
33
8
|
function sameIdList(left, right) {
|
|
34
9
|
if (left.length !== right.length) return false;
|
|
35
10
|
return left.every((value, index) => value === right[index]);
|
|
@@ -50,74 +25,6 @@ function useDealAssociations({
|
|
|
50
25
|
setPeopleEditorIds(data?.linkedPersonIds ?? []);
|
|
51
26
|
setCompaniesEditorIds(data?.linkedCompanyIds ?? []);
|
|
52
27
|
}, [data?.linkedCompanyIds, data?.linkedPersonIds]);
|
|
53
|
-
const loadPeopleAssociations = React.useCallback(async (ids) => {
|
|
54
|
-
const uniqueIds = Array.from(new Set(ids.map((value) => value.trim()).filter(Boolean)));
|
|
55
|
-
if (!uniqueIds.length) return [];
|
|
56
|
-
try {
|
|
57
|
-
const params = new URLSearchParams({
|
|
58
|
-
ids: uniqueIds.join(","),
|
|
59
|
-
pageSize: String(Math.max(uniqueIds.length, 1))
|
|
60
|
-
});
|
|
61
|
-
const payload = await readApiResultOrThrow(
|
|
62
|
-
`/api/customers/people?${params.toString()}`
|
|
63
|
-
);
|
|
64
|
-
const items = Array.isArray(payload.items) ? payload.items : [];
|
|
65
|
-
const byId = /* @__PURE__ */ new Map();
|
|
66
|
-
items.forEach((record) => {
|
|
67
|
-
if (record && typeof record.id === "string") byId.set(record.id, record);
|
|
68
|
-
});
|
|
69
|
-
return uniqueIds.map((personId) => {
|
|
70
|
-
const record = byId.get(personId);
|
|
71
|
-
return record ? normalizePersonAssociationRecord(record, personId) : {
|
|
72
|
-
id: personId,
|
|
73
|
-
label: personId,
|
|
74
|
-
subtitle: null,
|
|
75
|
-
kind: "person"
|
|
76
|
-
};
|
|
77
|
-
});
|
|
78
|
-
} catch {
|
|
79
|
-
return uniqueIds.map((personId) => ({
|
|
80
|
-
id: personId,
|
|
81
|
-
label: personId,
|
|
82
|
-
subtitle: null,
|
|
83
|
-
kind: "person"
|
|
84
|
-
}));
|
|
85
|
-
}
|
|
86
|
-
}, []);
|
|
87
|
-
const loadCompanyAssociations = React.useCallback(async (ids) => {
|
|
88
|
-
const uniqueIds = Array.from(new Set(ids.map((value) => value.trim()).filter(Boolean)));
|
|
89
|
-
if (!uniqueIds.length) return [];
|
|
90
|
-
try {
|
|
91
|
-
const params = new URLSearchParams({
|
|
92
|
-
ids: uniqueIds.join(","),
|
|
93
|
-
pageSize: String(Math.max(uniqueIds.length, 1))
|
|
94
|
-
});
|
|
95
|
-
const payload = await readApiResultOrThrow(
|
|
96
|
-
`/api/customers/companies?${params.toString()}`
|
|
97
|
-
);
|
|
98
|
-
const items = Array.isArray(payload.items) ? payload.items : [];
|
|
99
|
-
const byId = /* @__PURE__ */ new Map();
|
|
100
|
-
items.forEach((record) => {
|
|
101
|
-
if (record && typeof record.id === "string") byId.set(record.id, record);
|
|
102
|
-
});
|
|
103
|
-
return uniqueIds.map((companyId) => {
|
|
104
|
-
const record = byId.get(companyId);
|
|
105
|
-
return record ? normalizeCompanyAssociationRecord(record, companyId) : {
|
|
106
|
-
id: companyId,
|
|
107
|
-
label: companyId,
|
|
108
|
-
subtitle: null,
|
|
109
|
-
kind: "company"
|
|
110
|
-
};
|
|
111
|
-
});
|
|
112
|
-
} catch {
|
|
113
|
-
return uniqueIds.map((companyId) => ({
|
|
114
|
-
id: companyId,
|
|
115
|
-
label: companyId,
|
|
116
|
-
subtitle: null,
|
|
117
|
-
kind: "company"
|
|
118
|
-
}));
|
|
119
|
-
}
|
|
120
|
-
}, []);
|
|
121
28
|
const loadLinkedPeoplePage = React.useCallback(
|
|
122
29
|
async (page, query) => {
|
|
123
30
|
if (!currentDealId) {
|
|
@@ -178,15 +85,7 @@ function useDealAssociations({
|
|
|
178
85
|
),
|
|
179
86
|
{ id: currentDealId, personIds: nextIds, operation: "updateDealPeople" }
|
|
180
87
|
);
|
|
181
|
-
|
|
182
|
-
setData(
|
|
183
|
-
(prev) => prev ? {
|
|
184
|
-
...prev,
|
|
185
|
-
people: nextPeople,
|
|
186
|
-
linkedPersonIds: nextIds,
|
|
187
|
-
counts: { ...prev.counts, people: nextIds.length }
|
|
188
|
-
} : prev
|
|
189
|
-
);
|
|
88
|
+
await onRefresh();
|
|
190
89
|
} catch (error) {
|
|
191
90
|
setPeopleEditorIds(previousIds);
|
|
192
91
|
setData(
|
|
@@ -197,14 +96,14 @@ function useDealAssociations({
|
|
|
197
96
|
counts: { ...prev.counts, people: previousIds.length }
|
|
198
97
|
} : prev
|
|
199
98
|
);
|
|
200
|
-
if (!surfaceRecordConflict(error, t, { onRefresh
|
|
99
|
+
if (!surfaceRecordConflict(error, t, { onRefresh })) {
|
|
201
100
|
flash(t("customers.deals.detail.peopleUpdateError", "Failed to update linked people."), "error");
|
|
202
101
|
}
|
|
203
102
|
} finally {
|
|
204
103
|
setPeopleSaving(false);
|
|
205
104
|
}
|
|
206
105
|
},
|
|
207
|
-
[currentDealId, data?.deal.updatedAt, data?.people,
|
|
106
|
+
[currentDealId, data?.deal.updatedAt, data?.people, onRefresh, peopleEditorIds, runMutationWithContext, setData, t]
|
|
208
107
|
);
|
|
209
108
|
const handleCompaniesAssociationsChange = React.useCallback(
|
|
210
109
|
async (nextIds) => {
|
|
@@ -222,15 +121,7 @@ function useDealAssociations({
|
|
|
222
121
|
),
|
|
223
122
|
{ id: currentDealId, companyIds: nextIds, operation: "updateDealCompanies" }
|
|
224
123
|
);
|
|
225
|
-
|
|
226
|
-
setData(
|
|
227
|
-
(prev) => prev ? {
|
|
228
|
-
...prev,
|
|
229
|
-
companies: nextCompanies,
|
|
230
|
-
linkedCompanyIds: nextIds,
|
|
231
|
-
counts: { ...prev.counts, companies: nextIds.length }
|
|
232
|
-
} : prev
|
|
233
|
-
);
|
|
124
|
+
await onRefresh();
|
|
234
125
|
} catch (error) {
|
|
235
126
|
setCompaniesEditorIds(previousIds);
|
|
236
127
|
setData(
|
|
@@ -241,7 +132,7 @@ function useDealAssociations({
|
|
|
241
132
|
counts: { ...prev.counts, companies: previousIds.length }
|
|
242
133
|
} : prev
|
|
243
134
|
);
|
|
244
|
-
if (!surfaceRecordConflict(error, t, { onRefresh
|
|
135
|
+
if (!surfaceRecordConflict(error, t, { onRefresh })) {
|
|
245
136
|
flash(t("customers.deals.detail.companiesUpdateError", "Failed to update linked companies."), "error");
|
|
246
137
|
}
|
|
247
138
|
} finally {
|
|
@@ -253,7 +144,6 @@ function useDealAssociations({
|
|
|
253
144
|
currentDealId,
|
|
254
145
|
data?.companies,
|
|
255
146
|
data?.deal.updatedAt,
|
|
256
|
-
loadCompanyAssociations,
|
|
257
147
|
onRefresh,
|
|
258
148
|
runMutationWithContext,
|
|
259
149
|
setData,
|
|
@@ -272,8 +162,6 @@ function useDealAssociations({
|
|
|
272
162
|
};
|
|
273
163
|
}
|
|
274
164
|
export {
|
|
275
|
-
normalizeCompanyAssociationRecord,
|
|
276
|
-
normalizePersonAssociationRecord,
|
|
277
165
|
useDealAssociations
|
|
278
166
|
};
|
|
279
167
|
//# sourceMappingURL=useDealAssociations.js.map
|