@open-mercato/core 0.6.1-develop.3115.bc23703ef8 → 0.6.1-develop.3144.b39fb4dfa6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/modules/catalog/api/dictionaries/[key]/route.js +2 -2
- package/dist/modules/catalog/api/dictionaries/[key]/route.js.map +2 -2
- package/dist/modules/customers/api/activities/route.js +5 -5
- package/dist/modules/customers/api/activities/route.js.map +2 -2
- package/dist/modules/customers/api/assignable-staff/route.js +2 -2
- package/dist/modules/customers/api/assignable-staff/route.js.map +2 -2
- package/dist/modules/customers/api/companies/[id]/people/route.js +2 -2
- package/dist/modules/customers/api/companies/[id]/people/route.js.map +2 -2
- package/dist/modules/customers/api/dashboard/widgets/customer-todos/route.js +2 -2
- package/dist/modules/customers/api/dashboard/widgets/customer-todos/route.js.map +2 -2
- package/dist/modules/customers/api/dashboard/widgets/new-customers/route.js +2 -2
- package/dist/modules/customers/api/dashboard/widgets/new-customers/route.js.map +2 -2
- package/dist/modules/customers/api/dashboard/widgets/new-deals/route.js +2 -2
- package/dist/modules/customers/api/dashboard/widgets/new-deals/route.js.map +2 -2
- package/dist/modules/customers/api/dashboard/widgets/next-interactions/route.js +2 -2
- package/dist/modules/customers/api/dashboard/widgets/next-interactions/route.js.map +2 -2
- package/dist/modules/customers/api/deals/[id]/companies/route.js +2 -2
- package/dist/modules/customers/api/deals/[id]/companies/route.js.map +2 -2
- package/dist/modules/customers/api/deals/[id]/people/route.js +2 -2
- package/dist/modules/customers/api/deals/[id]/people/route.js.map +2 -2
- package/dist/modules/customers/api/dictionaries/[kind]/[id]/route.js +6 -6
- package/dist/modules/customers/api/dictionaries/[kind]/[id]/route.js.map +2 -2
- package/dist/modules/customers/api/dictionaries/[kind]/route.js +3 -3
- package/dist/modules/customers/api/dictionaries/[kind]/route.js.map +2 -2
- package/dist/modules/customers/api/dictionaries/currency/route.js +2 -2
- package/dist/modules/customers/api/dictionaries/currency/route.js.map +2 -2
- package/dist/modules/customers/api/dictionaries/kind-settings/route.js +3 -3
- package/dist/modules/customers/api/dictionaries/kind-settings/route.js.map +2 -2
- package/dist/modules/customers/api/entity-roles-factory.js +5 -5
- package/dist/modules/customers/api/entity-roles-factory.js.map +2 -2
- package/dist/modules/customers/api/interactions/cancel/route.js +2 -2
- package/dist/modules/customers/api/interactions/cancel/route.js.map +2 -2
- package/dist/modules/customers/api/interactions/complete/route.js +2 -2
- package/dist/modules/customers/api/interactions/complete/route.js.map +2 -2
- package/dist/modules/customers/api/interactions/conflicts/route.js +2 -2
- package/dist/modules/customers/api/interactions/conflicts/route.js.map +2 -2
- package/dist/modules/customers/api/interactions/counts/route.js +2 -2
- package/dist/modules/customers/api/interactions/counts/route.js.map +2 -2
- package/dist/modules/customers/api/interactions/route.js +2 -2
- package/dist/modules/customers/api/interactions/route.js.map +2 -2
- package/dist/modules/customers/api/interactions/tasks/route.js +2 -2
- package/dist/modules/customers/api/interactions/tasks/route.js.map +2 -2
- package/dist/modules/customers/api/labels/assign/route.js +2 -2
- package/dist/modules/customers/api/labels/assign/route.js.map +2 -2
- package/dist/modules/customers/api/labels/route.js +3 -3
- package/dist/modules/customers/api/labels/route.js.map +2 -2
- package/dist/modules/customers/api/labels/unassign/route.js +2 -2
- package/dist/modules/customers/api/labels/unassign/route.js.map +2 -2
- package/dist/modules/customers/api/people/[id]/companies/[linkId]/route.js +3 -3
- package/dist/modules/customers/api/people/[id]/companies/[linkId]/route.js.map +2 -2
- package/dist/modules/customers/api/people/[id]/companies/enriched/route.js +2 -2
- package/dist/modules/customers/api/people/[id]/companies/enriched/route.js.map +2 -2
- package/dist/modules/customers/api/people/[id]/companies/route.js +3 -3
- package/dist/modules/customers/api/people/[id]/companies/route.js.map +2 -2
- package/dist/modules/customers/api/pipeline-stages/reorder/route.js +2 -2
- package/dist/modules/customers/api/pipeline-stages/reorder/route.js.map +2 -2
- package/dist/modules/customers/api/pipeline-stages/route.js +5 -5
- package/dist/modules/customers/api/pipeline-stages/route.js.map +2 -2
- package/dist/modules/customers/api/pipelines/route.js +5 -5
- package/dist/modules/customers/api/pipelines/route.js.map +2 -2
- package/dist/modules/customers/api/settings/address-format/route.js +3 -3
- package/dist/modules/customers/api/settings/address-format/route.js.map +2 -2
- package/dist/modules/customers/api/tags/assign/route.js +2 -2
- package/dist/modules/customers/api/tags/assign/route.js.map +2 -2
- package/dist/modules/customers/api/tags/unassign/route.js +2 -2
- package/dist/modules/customers/api/tags/unassign/route.js.map +2 -2
- package/dist/modules/customers/api/todos/route.js +5 -5
- package/dist/modules/customers/api/todos/route.js.map +2 -2
- package/dist/modules/dictionaries/api/[dictionaryId]/entries/[entryId]/route.js +3 -3
- package/dist/modules/dictionaries/api/[dictionaryId]/entries/[entryId]/route.js.map +2 -2
- package/dist/modules/dictionaries/api/[dictionaryId]/entries/reorder/route.js +2 -2
- package/dist/modules/dictionaries/api/[dictionaryId]/entries/reorder/route.js.map +2 -2
- package/dist/modules/dictionaries/api/[dictionaryId]/entries/route.js +3 -3
- package/dist/modules/dictionaries/api/[dictionaryId]/entries/route.js.map +2 -2
- package/dist/modules/dictionaries/api/[dictionaryId]/entries/set-default/route.js +2 -2
- package/dist/modules/dictionaries/api/[dictionaryId]/entries/set-default/route.js.map +2 -2
- package/dist/modules/dictionaries/api/[dictionaryId]/route.js +4 -4
- package/dist/modules/dictionaries/api/[dictionaryId]/route.js.map +2 -2
- package/dist/modules/dictionaries/api/route.js +3 -3
- package/dist/modules/dictionaries/api/route.js.map +2 -2
- package/dist/modules/feature_toggles/api/overrides/route.js +2 -2
- package/dist/modules/feature_toggles/api/overrides/route.js.map +2 -2
- package/dist/modules/planner/api/availability-date-specific.js +2 -2
- package/dist/modules/planner/api/availability-date-specific.js.map +2 -2
- package/dist/modules/planner/api/availability-weekly.js +2 -2
- package/dist/modules/planner/api/availability-weekly.js.map +2 -2
- package/dist/modules/resources/api/resources/tags/assign/route.js +2 -2
- package/dist/modules/resources/api/resources/tags/assign/route.js.map +2 -2
- package/dist/modules/resources/api/resources/tags/unassign/route.js +2 -2
- package/dist/modules/resources/api/resources/tags/unassign/route.js.map +2 -2
- package/dist/modules/sales/api/document-history/route.js +2 -2
- package/dist/modules/sales/api/document-history/route.js.map +2 -2
- package/dist/modules/sales/api/document-numbers/route.js +2 -2
- package/dist/modules/sales/api/document-numbers/route.js.map +2 -2
- package/dist/modules/sales/api/quotes/accept/route.js +2 -2
- package/dist/modules/sales/api/quotes/accept/route.js.map +2 -2
- package/dist/modules/sales/api/quotes/convert/route.js +2 -2
- package/dist/modules/sales/api/quotes/convert/route.js.map +2 -2
- package/dist/modules/sales/api/quotes/public/[token]/route.js +2 -2
- package/dist/modules/sales/api/quotes/public/[token]/route.js.map +2 -2
- package/dist/modules/sales/api/quotes/send/route.js +2 -2
- package/dist/modules/sales/api/quotes/send/route.js.map +2 -2
- package/dist/modules/sales/api/returns/[id]/route.js +2 -2
- package/dist/modules/sales/api/returns/[id]/route.js.map +2 -2
- package/dist/modules/sales/api/settings/document-numbers/route.js +3 -3
- package/dist/modules/sales/api/settings/document-numbers/route.js.map +2 -2
- package/dist/modules/sales/api/settings/order-editing/route.js +3 -3
- package/dist/modules/sales/api/settings/order-editing/route.js.map +2 -2
- package/dist/modules/sales/widgets/dashboard/makeDashboardWidgetRoute.js +2 -2
- package/dist/modules/sales/widgets/dashboard/makeDashboardWidgetRoute.js.map +2 -2
- package/dist/modules/staff/api/leave-requests/accept/route.js +2 -2
- package/dist/modules/staff/api/leave-requests/accept/route.js.map +2 -2
- package/dist/modules/staff/api/leave-requests/reject/route.js +2 -2
- package/dist/modules/staff/api/leave-requests/reject/route.js.map +2 -2
- package/dist/modules/staff/api/team-members/self/route.js +3 -3
- package/dist/modules/staff/api/team-members/self/route.js.map +2 -2
- package/dist/modules/staff/api/team-members/tags/assign/route.js +2 -2
- 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 +2 -2
- package/dist/modules/staff/api/team-members/tags/unassign/route.js.map +2 -2
- package/dist/modules/translations/api/[entityType]/[entityId]/route.js +4 -4
- package/dist/modules/translations/api/[entityType]/[entityId]/route.js.map +2 -2
- package/dist/modules/translations/api/get/locales.js +2 -2
- package/dist/modules/translations/api/get/locales.js.map +2 -2
- package/dist/modules/translations/api/put/locales.js +2 -2
- package/dist/modules/translations/api/put/locales.js.map +2 -2
- package/package.json +5 -5
- package/src/modules/catalog/api/dictionaries/[key]/route.ts +2 -2
- package/src/modules/customers/api/activities/route.ts +5 -5
- package/src/modules/customers/api/assignable-staff/route.ts +2 -2
- package/src/modules/customers/api/companies/[id]/people/route.ts +2 -2
- package/src/modules/customers/api/dashboard/widgets/customer-todos/route.ts +2 -2
- package/src/modules/customers/api/dashboard/widgets/new-customers/route.ts +2 -2
- package/src/modules/customers/api/dashboard/widgets/new-deals/route.ts +2 -2
- package/src/modules/customers/api/dashboard/widgets/next-interactions/route.ts +2 -2
- package/src/modules/customers/api/deals/[id]/companies/route.ts +2 -2
- package/src/modules/customers/api/deals/[id]/people/route.ts +2 -2
- package/src/modules/customers/api/dictionaries/[kind]/[id]/route.ts +6 -6
- package/src/modules/customers/api/dictionaries/[kind]/route.ts +3 -3
- package/src/modules/customers/api/dictionaries/currency/route.ts +2 -2
- package/src/modules/customers/api/dictionaries/kind-settings/route.ts +3 -3
- package/src/modules/customers/api/entity-roles-factory.ts +5 -5
- package/src/modules/customers/api/interactions/cancel/route.ts +2 -2
- package/src/modules/customers/api/interactions/complete/route.ts +2 -2
- package/src/modules/customers/api/interactions/conflicts/route.ts +2 -2
- package/src/modules/customers/api/interactions/counts/route.ts +2 -2
- package/src/modules/customers/api/interactions/route.ts +2 -2
- package/src/modules/customers/api/interactions/tasks/route.ts +2 -2
- package/src/modules/customers/api/labels/assign/route.ts +2 -2
- package/src/modules/customers/api/labels/route.ts +3 -3
- package/src/modules/customers/api/labels/unassign/route.ts +2 -2
- package/src/modules/customers/api/people/[id]/companies/[linkId]/route.ts +3 -3
- package/src/modules/customers/api/people/[id]/companies/enriched/route.ts +2 -2
- package/src/modules/customers/api/people/[id]/companies/route.ts +3 -3
- package/src/modules/customers/api/pipeline-stages/reorder/route.ts +2 -2
- package/src/modules/customers/api/pipeline-stages/route.ts +5 -5
- package/src/modules/customers/api/pipelines/route.ts +5 -5
- package/src/modules/customers/api/settings/address-format/route.ts +3 -3
- package/src/modules/customers/api/tags/assign/route.ts +2 -2
- package/src/modules/customers/api/tags/unassign/route.ts +2 -2
- package/src/modules/customers/api/todos/route.ts +5 -5
- package/src/modules/dictionaries/api/[dictionaryId]/entries/[entryId]/route.ts +3 -3
- package/src/modules/dictionaries/api/[dictionaryId]/entries/reorder/route.ts +2 -2
- package/src/modules/dictionaries/api/[dictionaryId]/entries/route.ts +3 -3
- package/src/modules/dictionaries/api/[dictionaryId]/entries/set-default/route.ts +2 -2
- package/src/modules/dictionaries/api/[dictionaryId]/route.ts +4 -4
- package/src/modules/dictionaries/api/route.ts +3 -3
- package/src/modules/feature_toggles/api/overrides/route.ts +2 -2
- package/src/modules/planner/api/availability-date-specific.ts +2 -2
- package/src/modules/planner/api/availability-weekly.ts +2 -2
- package/src/modules/resources/api/resources/tags/assign/route.ts +2 -2
- package/src/modules/resources/api/resources/tags/unassign/route.ts +2 -2
- package/src/modules/sales/api/document-history/route.ts +2 -2
- package/src/modules/sales/api/document-numbers/route.ts +2 -2
- package/src/modules/sales/api/quotes/accept/route.ts +2 -2
- package/src/modules/sales/api/quotes/convert/route.ts +2 -2
- package/src/modules/sales/api/quotes/public/[token]/route.ts +2 -2
- package/src/modules/sales/api/quotes/send/route.ts +2 -2
- package/src/modules/sales/api/returns/[id]/route.ts +2 -2
- package/src/modules/sales/api/settings/document-numbers/route.ts +3 -3
- package/src/modules/sales/api/settings/order-editing/route.ts +3 -3
- package/src/modules/sales/widgets/dashboard/makeDashboardWidgetRoute.ts +2 -2
- package/src/modules/staff/api/leave-requests/accept/route.ts +2 -2
- package/src/modules/staff/api/leave-requests/reject/route.ts +2 -2
- package/src/modules/staff/api/team-members/self/route.ts +3 -3
- package/src/modules/staff/api/team-members/tags/assign/route.ts +2 -2
- package/src/modules/staff/api/team-members/tags/unassign/route.ts +2 -2
- package/src/modules/translations/api/[entityType]/[entityId]/route.ts +4 -4
- package/src/modules/translations/api/get/locales.ts +2 -2
- package/src/modules/translations/api/put/locales.ts +2 -2
|
@@ -5,7 +5,7 @@ import { getAuthFromRequest } from "@open-mercato/shared/lib/auth/server";
|
|
|
5
5
|
import { resolveOrganizationScopeForRequest } from "@open-mercato/core/modules/directory/utils/organizationScope";
|
|
6
6
|
import { pipelineStageReorderSchema } from "../../../data/validators.js";
|
|
7
7
|
import { withScopedPayload } from "../../utils.js";
|
|
8
|
-
import { CrudHttpError } from "@open-mercato/shared/lib/crud/errors";
|
|
8
|
+
import { CrudHttpError, isCrudHttpError } from "@open-mercato/shared/lib/crud/errors";
|
|
9
9
|
import { resolveTranslations } from "@open-mercato/shared/lib/i18n/server";
|
|
10
10
|
const metadata = {
|
|
11
11
|
POST: { requireAuth: true, requireFeatures: ["customers.pipelines.manage"] }
|
|
@@ -34,7 +34,7 @@ async function POST(req) {
|
|
|
34
34
|
);
|
|
35
35
|
return NextResponse.json({ ok: true });
|
|
36
36
|
} catch (err) {
|
|
37
|
-
if (err
|
|
37
|
+
if (isCrudHttpError(err)) {
|
|
38
38
|
return NextResponse.json(err.body, { status: err.status });
|
|
39
39
|
}
|
|
40
40
|
console.error("customers.pipeline-stages.reorder failed", err);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../src/modules/customers/api/pipeline-stages/reorder/route.ts"],
|
|
4
|
-
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { resolveOrganizationScopeForRequest } from '@open-mercato/core/modules/directory/utils/organizationScope'\nimport type { CommandRuntimeContext, CommandBus } from '@open-mercato/shared/lib/commands'\nimport { pipelineStageReorderSchema, type PipelineStageReorderInput } from '../../../data/validators'\nimport { withScopedPayload } from '../../utils'\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\n\nexport const metadata = {\n POST: { requireAuth: true, requireFeatures: ['customers.pipelines.manage'] },\n}\n\nexport async function POST(req: Request) {\n try {\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\n const body = await req.json().catch(() => ({}))\n const scoped = withScopedPayload(body, ctx, translate)\n\n const commandBus = (ctx.container.resolve('commandBus') as CommandBus)\n await commandBus.execute<PipelineStageReorderInput, void>(\n 'customers.pipeline-stages.reorder',\n { input: pipelineStageReorderSchema.parse(scoped), ctx },\n )\n return NextResponse.json({ ok: true })\n } catch (err) {\n if (err
|
|
5
|
-
"mappings": "AAAA,SAAS,oBAAoB;AAC7B,SAAS,SAAS;AAClB,SAAS,8BAA8B;AACvC,SAAS,0BAA0B;AACnC,SAAS,0CAA0C;AAEnD,SAAS,kCAAkE;AAC3E,SAAS,yBAAyB;AAClC,SAAS,
|
|
4
|
+
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { resolveOrganizationScopeForRequest } from '@open-mercato/core/modules/directory/utils/organizationScope'\nimport type { CommandRuntimeContext, CommandBus } from '@open-mercato/shared/lib/commands'\nimport { pipelineStageReorderSchema, type PipelineStageReorderInput } from '../../../data/validators'\nimport { withScopedPayload } from '../../utils'\nimport { CrudHttpError, isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\n\nexport const metadata = {\n POST: { requireAuth: true, requireFeatures: ['customers.pipelines.manage'] },\n}\n\nexport async function POST(req: Request) {\n try {\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\n const body = await req.json().catch(() => ({}))\n const scoped = withScopedPayload(body, ctx, translate)\n\n const commandBus = (ctx.container.resolve('commandBus') as CommandBus)\n await commandBus.execute<PipelineStageReorderInput, void>(\n 'customers.pipeline-stages.reorder',\n { input: pipelineStageReorderSchema.parse(scoped), ctx },\n )\n return NextResponse.json({ ok: true })\n } catch (err) {\n if (isCrudHttpError(err)) {\n return NextResponse.json(err.body, { status: err.status })\n }\n console.error('customers.pipeline-stages.reorder failed', err)\n return NextResponse.json({ error: 'Failed to reorder pipeline stages' }, { status: 400 })\n }\n}\n\nconst reorderOkResponseSchema = z.object({ ok: z.boolean() })\nconst reorderErrorSchema = z.object({ error: z.string() })\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Customers',\n summary: 'Reorder pipeline stages',\n methods: {\n POST: {\n summary: 'Reorder pipeline stages',\n description: 'Updates the order of pipeline stages in bulk.',\n requestBody: { contentType: 'application/json', schema: pipelineStageReorderSchema },\n responses: [\n { status: 200, description: 'Stages reordered', schema: reorderOkResponseSchema },\n ],\n errors: [\n { status: 400, description: 'Validation failed', schema: reorderErrorSchema },\n { status: 401, description: 'Unauthorized', schema: reorderErrorSchema },\n ],\n },\n },\n}\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,oBAAoB;AAC7B,SAAS,SAAS;AAClB,SAAS,8BAA8B;AACvC,SAAS,0BAA0B;AACnC,SAAS,0CAA0C;AAEnD,SAAS,kCAAkE;AAC3E,SAAS,yBAAyB;AAClC,SAAS,eAAe,uBAAuB;AAC/C,SAAS,2BAA2B;AAG7B,MAAM,WAAW;AAAA,EACtB,MAAM,EAAE,aAAa,MAAM,iBAAiB,CAAC,4BAA4B,EAAE;AAC7E;AAEA,eAAsB,KAAK,KAAc;AACvC,MAAI;AACF,UAAM,YAAY,MAAM,uBAAuB;AAC/C,UAAM,OAAO,MAAM,mBAAmB,GAAG;AACzC,UAAM,EAAE,UAAU,IAAI,MAAM,oBAAoB;AAChD,QAAI,CAAC,KAAM,OAAM,IAAI,cAAc,KAAK,EAAE,OAAO,UAAU,iCAAiC,cAAc,EAAE,CAAC;AAC7G,UAAM,QAAQ,MAAM,mCAAmC,EAAE,WAAW,MAAM,SAAS,IAAI,CAAC;AACxF,UAAM,MAA6B;AAAA,MACjC;AAAA,MACA;AAAA,MACA,mBAAmB;AAAA,MACnB,wBAAwB,OAAO,cAAc,KAAK,SAAS;AAAA,MAC3D,iBAAiB,OAAO,cAAc,KAAK,QAAQ,CAAC,KAAK,KAAK,IAAI;AAAA,MAClE,SAAS;AAAA,IACX;AAEA,UAAM,OAAO,MAAM,IAAI,KAAK,EAAE,MAAM,OAAO,CAAC,EAAE;AAC9C,UAAM,SAAS,kBAAkB,MAAM,KAAK,SAAS;AAErD,UAAM,aAAc,IAAI,UAAU,QAAQ,YAAY;AACtD,UAAM,WAAW;AAAA,MACf;AAAA,MACA,EAAE,OAAO,2BAA2B,MAAM,MAAM,GAAG,IAAI;AAAA,IACzD;AACA,WAAO,aAAa,KAAK,EAAE,IAAI,KAAK,CAAC;AAAA,EACvC,SAAS,KAAK;AACZ,QAAI,gBAAgB,GAAG,GAAG;AACxB,aAAO,aAAa,KAAK,IAAI,MAAM,EAAE,QAAQ,IAAI,OAAO,CAAC;AAAA,IAC3D;AACA,YAAQ,MAAM,4CAA4C,GAAG;AAC7D,WAAO,aAAa,KAAK,EAAE,OAAO,oCAAoC,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EAC1F;AACF;AAEA,MAAM,0BAA0B,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;AAC5D,MAAM,qBAAqB,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC;AAElD,MAAM,UAA2B;AAAA,EACtC,KAAK;AAAA,EACL,SAAS;AAAA,EACT,SAAS;AAAA,IACP,MAAM;AAAA,MACJ,SAAS;AAAA,MACT,aAAa;AAAA,MACb,aAAa,EAAE,aAAa,oBAAoB,QAAQ,2BAA2B;AAAA,MACnF,WAAW;AAAA,QACT,EAAE,QAAQ,KAAK,aAAa,oBAAoB,QAAQ,wBAAwB;AAAA,MAClF;AAAA,MACA,QAAQ;AAAA,QACN,EAAE,QAAQ,KAAK,aAAa,qBAAqB,QAAQ,mBAAmB;AAAA,QAC5E,EAAE,QAAQ,KAAK,aAAa,gBAAgB,QAAQ,mBAAmB;AAAA,MACzE;AAAA,IACF;AAAA,EACF;AACF;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
} from "../../data/validators.js";
|
|
12
12
|
import { withScopedPayload } from "../utils.js";
|
|
13
13
|
import { ensureDictionaryEntry } from "../../commands/shared.js";
|
|
14
|
-
import { CrudHttpError } from "@open-mercato/shared/lib/crud/errors";
|
|
14
|
+
import { CrudHttpError, isCrudHttpError } from "@open-mercato/shared/lib/crud/errors";
|
|
15
15
|
import { resolveTranslations } from "@open-mercato/shared/lib/i18n/server";
|
|
16
16
|
import { serializeOperationMetadata } from "@open-mercato/shared/lib/commands/operationMetadata";
|
|
17
17
|
const metadata = {
|
|
@@ -88,7 +88,7 @@ async function GET(req) {
|
|
|
88
88
|
});
|
|
89
89
|
return NextResponse.json({ items, total: items.length });
|
|
90
90
|
} catch (err) {
|
|
91
|
-
if (err
|
|
91
|
+
if (isCrudHttpError(err)) {
|
|
92
92
|
return NextResponse.json(err.body, { status: err.status });
|
|
93
93
|
}
|
|
94
94
|
console.error("customers.pipeline-stages GET failed", err);
|
|
@@ -123,7 +123,7 @@ async function POST(req) {
|
|
|
123
123
|
}
|
|
124
124
|
return response;
|
|
125
125
|
} catch (err) {
|
|
126
|
-
if (err
|
|
126
|
+
if (isCrudHttpError(err)) {
|
|
127
127
|
return NextResponse.json(err.body, { status: err.status });
|
|
128
128
|
}
|
|
129
129
|
console.error("customers.pipeline-stages POST failed", err);
|
|
@@ -158,7 +158,7 @@ async function PUT(req) {
|
|
|
158
158
|
}
|
|
159
159
|
return response;
|
|
160
160
|
} catch (err) {
|
|
161
|
-
if (err
|
|
161
|
+
if (isCrudHttpError(err)) {
|
|
162
162
|
return NextResponse.json(err.body, { status: err.status });
|
|
163
163
|
}
|
|
164
164
|
console.error("customers.pipeline-stages PUT failed", err);
|
|
@@ -178,7 +178,7 @@ async function DELETE(req) {
|
|
|
178
178
|
);
|
|
179
179
|
return NextResponse.json({ ok: true });
|
|
180
180
|
} catch (err) {
|
|
181
|
-
if (err
|
|
181
|
+
if (isCrudHttpError(err)) {
|
|
182
182
|
return NextResponse.json(err.body, { status: err.status });
|
|
183
183
|
}
|
|
184
184
|
console.error("customers.pipeline-stages DELETE failed", err);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../src/modules/customers/api/pipeline-stages/route.ts"],
|
|
4
|
-
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { resolveOrganizationScopeForRequest } from '@open-mercato/core/modules/directory/utils/organizationScope'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport type { CommandRuntimeContext, CommandBus } from '@open-mercato/shared/lib/commands'\nimport { CustomerPipelineStage, CustomerDictionaryEntry } from '../../data/entities'\nimport {\n pipelineStageCreateSchema,\n pipelineStageUpdateSchema,\n pipelineStageDeleteSchema,\n type PipelineStageCreateInput,\n type PipelineStageUpdateInput,\n type PipelineStageDeleteInput,\n} from '../../data/validators'\nimport { withScopedPayload } from '../utils'\nimport { ensureDictionaryEntry } from '../../commands/shared'\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { serializeOperationMetadata } from '@open-mercato/shared/lib/commands/operationMetadata'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\n\nexport const metadata = {\n GET: { requireAuth: true, requireFeatures: ['customers.pipelines.view'] },\n POST: { requireAuth: true, requireFeatures: ['customers.pipelines.manage'] },\n PUT: { requireAuth: true, requireFeatures: ['customers.pipelines.manage'] },\n DELETE: { requireAuth: true, requireFeatures: ['customers.pipelines.manage'] },\n}\n\nasync function buildContext(\n req: Request\n): Promise<{ ctx: CommandRuntimeContext; organizationId: string | null; tenantId: string | null }> {\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 const organizationId = scope?.selectedId ?? auth.orgId ?? null\n const tenantId = auth.tenantId ?? null\n return { ctx, organizationId, tenantId }\n}\n\nexport async function GET(req: Request) {\n try {\n const { ctx, organizationId, tenantId } = await buildContext(req)\n if (!organizationId || !tenantId) {\n return NextResponse.json({ error: 'Organization and tenant context required' }, { status: 400 })\n }\n const url = new URL(req.url)\n const pipelineId = url.searchParams.get('pipelineId')\n\n const em = (ctx.container.resolve('em') as EntityManager)\n const where: Record<string, unknown> = { organizationId, tenantId }\n if (pipelineId) where.pipelineId = pipelineId\n\n const stages = await em.find(CustomerPipelineStage, where, { orderBy: { order: 'ASC' } })\n\n const stageLabels = stages.map((s) => s.label.trim().toLowerCase())\n const dictEntries = stageLabels.length\n ? await em.find(CustomerDictionaryEntry, {\n organizationId,\n tenantId,\n kind: 'pipeline_stage',\n normalizedValue: { $in: stageLabels },\n })\n : []\n const dictByNormalized = new Map<string, CustomerDictionaryEntry>()\n dictEntries.forEach((entry) => dictByNormalized.set(entry.normalizedValue, entry))\n\n const missingStages = stages.filter((s) => !dictByNormalized.has(s.label.trim().toLowerCase()))\n if (missingStages.length) {\n for (const stage of missingStages) {\n const created = await ensureDictionaryEntry(em, {\n tenantId,\n organizationId,\n kind: 'pipeline_stage',\n value: stage.label,\n })\n if (created) dictByNormalized.set(created.normalizedValue, created)\n }\n }\n\n const items = stages.map((stage) => {\n const dictEntry = dictByNormalized.get(stage.label.trim().toLowerCase())\n return {\n id: stage.id,\n pipelineId: stage.pipelineId,\n label: stage.label,\n order: stage.order,\n color: dictEntry?.color ?? null,\n icon: dictEntry?.icon ?? null,\n organizationId: stage.organizationId,\n tenantId: stage.tenantId,\n createdAt: stage.createdAt,\n updatedAt: stage.updatedAt,\n }\n })\n return NextResponse.json({ items, total: items.length })\n } catch (err) {\n if (err instanceof CrudHttpError) {\n return NextResponse.json(err.body, { status: err.status })\n }\n console.error('customers.pipeline-stages GET failed', err)\n return NextResponse.json({ error: 'Failed to load pipeline stages' }, { status: 500 })\n }\n}\n\nexport async function POST(req: Request) {\n try {\n const { ctx } = await buildContext(req)\n const body = await req.json().catch(() => ({}))\n const { translate } = await resolveTranslations()\n const scoped = withScopedPayload(body, ctx, translate)\n\n const commandBus = (ctx.container.resolve('commandBus') as CommandBus)\n const { result, logEntry } = await commandBus.execute<PipelineStageCreateInput, { stageId: string }>(\n 'customers.pipeline-stages.create',\n { input: pipelineStageCreateSchema.parse(scoped), ctx },\n )\n const response = NextResponse.json({ id: result?.stageId ?? 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.pipelineStage',\n resourceId: logEntry.resourceId ?? result?.stageId ?? null,\n executedAt: logEntry.createdAt instanceof Date ? logEntry.createdAt.toISOString() : undefined,\n })\n )\n }\n return response\n } catch (err) {\n if (err instanceof CrudHttpError) {\n return NextResponse.json(err.body, { status: err.status })\n }\n console.error('customers.pipeline-stages POST failed', err)\n return NextResponse.json({ error: 'Failed to create pipeline stage' }, { status: 400 })\n }\n}\n\nexport async function PUT(req: Request) {\n try {\n const { ctx } = await buildContext(req)\n const body = await req.json().catch(() => ({}))\n const { translate } = await resolveTranslations()\n const scoped = withScopedPayload(body, ctx, translate)\n\n const commandBus = (ctx.container.resolve('commandBus') as CommandBus)\n const { logEntry } = await commandBus.execute<PipelineStageUpdateInput, void>(\n 'customers.pipeline-stages.update',\n { input: pipelineStageUpdateSchema.parse(scoped), ctx },\n )\n const response = NextResponse.json({ ok: true })\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.pipelineStage',\n resourceId: logEntry.resourceId ?? null,\n executedAt: logEntry.createdAt instanceof Date ? logEntry.createdAt.toISOString() : undefined,\n })\n )\n }\n return response\n } catch (err) {\n if (err instanceof CrudHttpError) {\n return NextResponse.json(err.body, { status: err.status })\n }\n console.error('customers.pipeline-stages PUT failed', err)\n return NextResponse.json({ error: 'Failed to update pipeline stage' }, { status: 400 })\n }\n}\n\nexport async function DELETE(req: Request) {\n try {\n const { ctx } = await buildContext(req)\n const body = await req.json().catch(() => ({}))\n const { translate } = await resolveTranslations()\n const scoped = withScopedPayload(body, ctx, translate)\n\n const commandBus = (ctx.container.resolve('commandBus') as CommandBus)\n await commandBus.execute<PipelineStageDeleteInput, void>(\n 'customers.pipeline-stages.delete',\n { input: pipelineStageDeleteSchema.parse(scoped), ctx },\n )\n return NextResponse.json({ ok: true })\n } catch (err) {\n if (err instanceof CrudHttpError) {\n return NextResponse.json(err.body, { status: err.status })\n }\n console.error('customers.pipeline-stages DELETE failed', err)\n return NextResponse.json({ error: 'Failed to delete pipeline stage' }, { status: 400 })\n }\n}\n\nconst stageItemSchema = z.object({\n id: z.string().uuid(),\n pipelineId: z.string().uuid(),\n label: z.string(),\n order: z.number(),\n color: z.string().nullable(),\n icon: z.string().nullable(),\n organizationId: z.string().uuid(),\n tenantId: z.string().uuid(),\n createdAt: z.date(),\n updatedAt: z.date(),\n})\n\nconst stageListResponseSchema = z.object({\n items: z.array(stageItemSchema),\n total: z.number(),\n})\n\nconst stageCreateResponseSchema = z.object({\n id: z.string().uuid().nullable(),\n})\n\nconst stageOkResponseSchema = z.object({\n ok: z.boolean(),\n})\n\nconst stageErrorSchema = z.object({\n error: z.string(),\n})\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Customers',\n summary: 'Manage pipeline stages',\n methods: {\n GET: {\n summary: 'List pipeline stages',\n description: 'Returns pipeline stages for the authenticated organization, optionally filtered by pipelineId.',\n query: z.object({ pipelineId: z.string().uuid().optional() }),\n responses: [\n { status: 200, description: 'Stage list', schema: stageListResponseSchema },\n ],\n errors: [\n { status: 401, description: 'Unauthorized', schema: stageErrorSchema },\n { status: 400, description: 'Invalid request', schema: stageErrorSchema },\n ],\n },\n POST: {\n summary: 'Create pipeline stage',\n description: 'Creates a new pipeline stage.',\n requestBody: { contentType: 'application/json', schema: pipelineStageCreateSchema },\n responses: [\n { status: 201, description: 'Stage created', schema: stageCreateResponseSchema },\n ],\n errors: [\n { status: 400, description: 'Validation failed', schema: stageErrorSchema },\n { status: 401, description: 'Unauthorized', schema: stageErrorSchema },\n ],\n },\n PUT: {\n summary: 'Update pipeline stage',\n description: 'Updates an existing pipeline stage.',\n requestBody: { contentType: 'application/json', schema: pipelineStageUpdateSchema },\n responses: [\n { status: 200, description: 'Stage updated', schema: stageOkResponseSchema },\n ],\n errors: [\n { status: 400, description: 'Validation failed', schema: stageErrorSchema },\n { status: 404, description: 'Stage not found', schema: stageErrorSchema },\n ],\n },\n DELETE: {\n summary: 'Delete pipeline stage',\n description: 'Deletes a pipeline stage. Returns 409 if active deals use this stage.',\n requestBody: { contentType: 'application/json', schema: pipelineStageDeleteSchema },\n responses: [\n { status: 200, description: 'Stage deleted', schema: stageOkResponseSchema },\n ],\n errors: [\n { status: 409, description: 'Stage has active deals', schema: stageErrorSchema },\n { status: 404, description: 'Stage not found', schema: stageErrorSchema },\n ],\n },\n },\n}\n"],
|
|
5
|
-
"mappings": "AAAA,SAAS,oBAAoB;AAC7B,SAAS,SAAS;AAClB,SAAS,8BAA8B;AACvC,SAAS,0BAA0B;AACnC,SAAS,0CAA0C;AAGnD,SAAS,uBAAuB,+BAA+B;AAC/D;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OAIK;AACP,SAAS,yBAAyB;AAClC,SAAS,6BAA6B;AACtC,SAAS,
|
|
4
|
+
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { resolveOrganizationScopeForRequest } from '@open-mercato/core/modules/directory/utils/organizationScope'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport type { CommandRuntimeContext, CommandBus } from '@open-mercato/shared/lib/commands'\nimport { CustomerPipelineStage, CustomerDictionaryEntry } from '../../data/entities'\nimport {\n pipelineStageCreateSchema,\n pipelineStageUpdateSchema,\n pipelineStageDeleteSchema,\n type PipelineStageCreateInput,\n type PipelineStageUpdateInput,\n type PipelineStageDeleteInput,\n} from '../../data/validators'\nimport { withScopedPayload } from '../utils'\nimport { ensureDictionaryEntry } from '../../commands/shared'\nimport { CrudHttpError, isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { serializeOperationMetadata } from '@open-mercato/shared/lib/commands/operationMetadata'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\n\nexport const metadata = {\n GET: { requireAuth: true, requireFeatures: ['customers.pipelines.view'] },\n POST: { requireAuth: true, requireFeatures: ['customers.pipelines.manage'] },\n PUT: { requireAuth: true, requireFeatures: ['customers.pipelines.manage'] },\n DELETE: { requireAuth: true, requireFeatures: ['customers.pipelines.manage'] },\n}\n\nasync function buildContext(\n req: Request\n): Promise<{ ctx: CommandRuntimeContext; organizationId: string | null; tenantId: string | null }> {\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 const organizationId = scope?.selectedId ?? auth.orgId ?? null\n const tenantId = auth.tenantId ?? null\n return { ctx, organizationId, tenantId }\n}\n\nexport async function GET(req: Request) {\n try {\n const { ctx, organizationId, tenantId } = await buildContext(req)\n if (!organizationId || !tenantId) {\n return NextResponse.json({ error: 'Organization and tenant context required' }, { status: 400 })\n }\n const url = new URL(req.url)\n const pipelineId = url.searchParams.get('pipelineId')\n\n const em = (ctx.container.resolve('em') as EntityManager)\n const where: Record<string, unknown> = { organizationId, tenantId }\n if (pipelineId) where.pipelineId = pipelineId\n\n const stages = await em.find(CustomerPipelineStage, where, { orderBy: { order: 'ASC' } })\n\n const stageLabels = stages.map((s) => s.label.trim().toLowerCase())\n const dictEntries = stageLabels.length\n ? await em.find(CustomerDictionaryEntry, {\n organizationId,\n tenantId,\n kind: 'pipeline_stage',\n normalizedValue: { $in: stageLabels },\n })\n : []\n const dictByNormalized = new Map<string, CustomerDictionaryEntry>()\n dictEntries.forEach((entry) => dictByNormalized.set(entry.normalizedValue, entry))\n\n const missingStages = stages.filter((s) => !dictByNormalized.has(s.label.trim().toLowerCase()))\n if (missingStages.length) {\n for (const stage of missingStages) {\n const created = await ensureDictionaryEntry(em, {\n tenantId,\n organizationId,\n kind: 'pipeline_stage',\n value: stage.label,\n })\n if (created) dictByNormalized.set(created.normalizedValue, created)\n }\n }\n\n const items = stages.map((stage) => {\n const dictEntry = dictByNormalized.get(stage.label.trim().toLowerCase())\n return {\n id: stage.id,\n pipelineId: stage.pipelineId,\n label: stage.label,\n order: stage.order,\n color: dictEntry?.color ?? null,\n icon: dictEntry?.icon ?? null,\n organizationId: stage.organizationId,\n tenantId: stage.tenantId,\n createdAt: stage.createdAt,\n updatedAt: stage.updatedAt,\n }\n })\n return NextResponse.json({ items, total: items.length })\n } catch (err) {\n if (isCrudHttpError(err)) {\n return NextResponse.json(err.body, { status: err.status })\n }\n console.error('customers.pipeline-stages GET failed', err)\n return NextResponse.json({ error: 'Failed to load pipeline stages' }, { status: 500 })\n }\n}\n\nexport async function POST(req: Request) {\n try {\n const { ctx } = await buildContext(req)\n const body = await req.json().catch(() => ({}))\n const { translate } = await resolveTranslations()\n const scoped = withScopedPayload(body, ctx, translate)\n\n const commandBus = (ctx.container.resolve('commandBus') as CommandBus)\n const { result, logEntry } = await commandBus.execute<PipelineStageCreateInput, { stageId: string }>(\n 'customers.pipeline-stages.create',\n { input: pipelineStageCreateSchema.parse(scoped), ctx },\n )\n const response = NextResponse.json({ id: result?.stageId ?? 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.pipelineStage',\n resourceId: logEntry.resourceId ?? result?.stageId ?? 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 console.error('customers.pipeline-stages POST failed', err)\n return NextResponse.json({ error: 'Failed to create pipeline stage' }, { status: 400 })\n }\n}\n\nexport async function PUT(req: Request) {\n try {\n const { ctx } = await buildContext(req)\n const body = await req.json().catch(() => ({}))\n const { translate } = await resolveTranslations()\n const scoped = withScopedPayload(body, ctx, translate)\n\n const commandBus = (ctx.container.resolve('commandBus') as CommandBus)\n const { logEntry } = await commandBus.execute<PipelineStageUpdateInput, void>(\n 'customers.pipeline-stages.update',\n { input: pipelineStageUpdateSchema.parse(scoped), ctx },\n )\n const response = NextResponse.json({ ok: true })\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.pipelineStage',\n resourceId: logEntry.resourceId ?? 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 console.error('customers.pipeline-stages PUT failed', err)\n return NextResponse.json({ error: 'Failed to update pipeline stage' }, { status: 400 })\n }\n}\n\nexport async function DELETE(req: Request) {\n try {\n const { ctx } = await buildContext(req)\n const body = await req.json().catch(() => ({}))\n const { translate } = await resolveTranslations()\n const scoped = withScopedPayload(body, ctx, translate)\n\n const commandBus = (ctx.container.resolve('commandBus') as CommandBus)\n await commandBus.execute<PipelineStageDeleteInput, void>(\n 'customers.pipeline-stages.delete',\n { input: pipelineStageDeleteSchema.parse(scoped), ctx },\n )\n return NextResponse.json({ ok: true })\n } catch (err) {\n if (isCrudHttpError(err)) {\n return NextResponse.json(err.body, { status: err.status })\n }\n console.error('customers.pipeline-stages DELETE failed', err)\n return NextResponse.json({ error: 'Failed to delete pipeline stage' }, { status: 400 })\n }\n}\n\nconst stageItemSchema = z.object({\n id: z.string().uuid(),\n pipelineId: z.string().uuid(),\n label: z.string(),\n order: z.number(),\n color: z.string().nullable(),\n icon: z.string().nullable(),\n organizationId: z.string().uuid(),\n tenantId: z.string().uuid(),\n createdAt: z.date(),\n updatedAt: z.date(),\n})\n\nconst stageListResponseSchema = z.object({\n items: z.array(stageItemSchema),\n total: z.number(),\n})\n\nconst stageCreateResponseSchema = z.object({\n id: z.string().uuid().nullable(),\n})\n\nconst stageOkResponseSchema = z.object({\n ok: z.boolean(),\n})\n\nconst stageErrorSchema = z.object({\n error: z.string(),\n})\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Customers',\n summary: 'Manage pipeline stages',\n methods: {\n GET: {\n summary: 'List pipeline stages',\n description: 'Returns pipeline stages for the authenticated organization, optionally filtered by pipelineId.',\n query: z.object({ pipelineId: z.string().uuid().optional() }),\n responses: [\n { status: 200, description: 'Stage list', schema: stageListResponseSchema },\n ],\n errors: [\n { status: 401, description: 'Unauthorized', schema: stageErrorSchema },\n { status: 400, description: 'Invalid request', schema: stageErrorSchema },\n ],\n },\n POST: {\n summary: 'Create pipeline stage',\n description: 'Creates a new pipeline stage.',\n requestBody: { contentType: 'application/json', schema: pipelineStageCreateSchema },\n responses: [\n { status: 201, description: 'Stage created', schema: stageCreateResponseSchema },\n ],\n errors: [\n { status: 400, description: 'Validation failed', schema: stageErrorSchema },\n { status: 401, description: 'Unauthorized', schema: stageErrorSchema },\n ],\n },\n PUT: {\n summary: 'Update pipeline stage',\n description: 'Updates an existing pipeline stage.',\n requestBody: { contentType: 'application/json', schema: pipelineStageUpdateSchema },\n responses: [\n { status: 200, description: 'Stage updated', schema: stageOkResponseSchema },\n ],\n errors: [\n { status: 400, description: 'Validation failed', schema: stageErrorSchema },\n { status: 404, description: 'Stage not found', schema: stageErrorSchema },\n ],\n },\n DELETE: {\n summary: 'Delete pipeline stage',\n description: 'Deletes a pipeline stage. Returns 409 if active deals use this stage.',\n requestBody: { contentType: 'application/json', schema: pipelineStageDeleteSchema },\n responses: [\n { status: 200, description: 'Stage deleted', schema: stageOkResponseSchema },\n ],\n errors: [\n { status: 409, description: 'Stage has active deals', schema: stageErrorSchema },\n { status: 404, description: 'Stage not found', schema: stageErrorSchema },\n ],\n },\n },\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
pipelineDeleteSchema
|
|
11
11
|
} from "../../data/validators.js";
|
|
12
12
|
import { withScopedPayload } from "../utils.js";
|
|
13
|
-
import { CrudHttpError } from "@open-mercato/shared/lib/crud/errors";
|
|
13
|
+
import { CrudHttpError, isCrudHttpError } from "@open-mercato/shared/lib/crud/errors";
|
|
14
14
|
import { resolveTranslations } from "@open-mercato/shared/lib/i18n/server";
|
|
15
15
|
import { serializeOperationMetadata } from "@open-mercato/shared/lib/commands/operationMetadata";
|
|
16
16
|
const metadata = {
|
|
@@ -61,7 +61,7 @@ async function GET(req) {
|
|
|
61
61
|
}));
|
|
62
62
|
return NextResponse.json({ items, total: items.length });
|
|
63
63
|
} catch (err) {
|
|
64
|
-
if (err
|
|
64
|
+
if (isCrudHttpError(err)) {
|
|
65
65
|
return NextResponse.json(err.body, { status: err.status });
|
|
66
66
|
}
|
|
67
67
|
console.error("customers.pipelines GET failed", err);
|
|
@@ -96,7 +96,7 @@ async function POST(req) {
|
|
|
96
96
|
}
|
|
97
97
|
return response;
|
|
98
98
|
} catch (err) {
|
|
99
|
-
if (err
|
|
99
|
+
if (isCrudHttpError(err)) {
|
|
100
100
|
return NextResponse.json(err.body, { status: err.status });
|
|
101
101
|
}
|
|
102
102
|
console.error("customers.pipelines POST failed", err);
|
|
@@ -131,7 +131,7 @@ async function PUT(req) {
|
|
|
131
131
|
}
|
|
132
132
|
return response;
|
|
133
133
|
} catch (err) {
|
|
134
|
-
if (err
|
|
134
|
+
if (isCrudHttpError(err)) {
|
|
135
135
|
return NextResponse.json(err.body, { status: err.status });
|
|
136
136
|
}
|
|
137
137
|
console.error("customers.pipelines PUT failed", err);
|
|
@@ -151,7 +151,7 @@ async function DELETE(req) {
|
|
|
151
151
|
);
|
|
152
152
|
return NextResponse.json({ ok: true });
|
|
153
153
|
} catch (err) {
|
|
154
|
-
if (err
|
|
154
|
+
if (isCrudHttpError(err)) {
|
|
155
155
|
return NextResponse.json(err.body, { status: err.status });
|
|
156
156
|
}
|
|
157
157
|
console.error("customers.pipelines DELETE failed", err);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../src/modules/customers/api/pipelines/route.ts"],
|
|
4
|
-
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { resolveOrganizationScopeForRequest } from '@open-mercato/core/modules/directory/utils/organizationScope'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport type { CommandRuntimeContext, CommandBus } from '@open-mercato/shared/lib/commands'\nimport { CustomerPipeline } from '../../data/entities'\nimport {\n pipelineCreateSchema,\n pipelineUpdateSchema,\n pipelineDeleteSchema,\n type PipelineCreateInput,\n type PipelineUpdateInput,\n type PipelineDeleteInput,\n} from '../../data/validators'\nimport { withScopedPayload } from '../utils'\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { serializeOperationMetadata } from '@open-mercato/shared/lib/commands/operationMetadata'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\n\nexport const metadata = {\n GET: { requireAuth: true, requireFeatures: ['customers.pipelines.view'] },\n POST: { requireAuth: true, requireFeatures: ['customers.pipelines.manage'] },\n PUT: { requireAuth: true, requireFeatures: ['customers.pipelines.manage'] },\n DELETE: { requireAuth: true, requireFeatures: ['customers.pipelines.manage'] },\n}\n\nasync function buildContext(\n req: Request\n): Promise<{ ctx: CommandRuntimeContext; organizationId: string | null; tenantId: string | null }> {\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 const organizationId = scope?.selectedId ?? auth.orgId ?? null\n const tenantId = auth.tenantId ?? null\n return { ctx, organizationId, tenantId }\n}\n\nexport async function GET(req: Request) {\n try {\n const { ctx, organizationId, tenantId } = await buildContext(req)\n if (!organizationId || !tenantId) {\n return NextResponse.json({ error: 'Organization and tenant context required' }, { status: 400 })\n }\n const url = new URL(req.url)\n const isDefaultParam = url.searchParams.get('isDefault')\n\n const em = (ctx.container.resolve('em') as EntityManager)\n const where: Record<string, unknown> = { organizationId, tenantId }\n if (isDefaultParam === 'true') where.isDefault = true\n if (isDefaultParam === 'false') where.isDefault = false\n\n const pipelines = await em.find(CustomerPipeline, where, { orderBy: { createdAt: 'ASC' } })\n const items = pipelines.map((pipeline) => ({\n id: pipeline.id,\n name: pipeline.name,\n isDefault: pipeline.isDefault,\n organizationId: pipeline.organizationId,\n tenantId: pipeline.tenantId,\n createdAt: pipeline.createdAt,\n updatedAt: pipeline.updatedAt,\n }))\n return NextResponse.json({ items, total: items.length })\n } catch (err) {\n if (err instanceof CrudHttpError) {\n return NextResponse.json(err.body, { status: err.status })\n }\n console.error('customers.pipelines GET failed', err)\n return NextResponse.json({ error: 'Failed to load pipelines' }, { status: 500 })\n }\n}\n\nexport async function POST(req: Request) {\n try {\n const { ctx } = await buildContext(req)\n const body = await req.json().catch(() => ({}))\n const { translate } = await resolveTranslations()\n const scoped = withScopedPayload(body, ctx, translate)\n\n const commandBus = (ctx.container.resolve('commandBus') as CommandBus)\n const { result, logEntry } = await commandBus.execute<PipelineCreateInput, { pipelineId: string }>(\n 'customers.pipelines.create',\n { input: pipelineCreateSchema.parse(scoped), ctx },\n )\n const response = NextResponse.json({ id: result?.pipelineId ?? 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.pipeline',\n resourceId: logEntry.resourceId ?? result?.pipelineId ?? null,\n executedAt: logEntry.createdAt instanceof Date ? logEntry.createdAt.toISOString() : undefined,\n })\n )\n }\n return response\n } catch (err) {\n if (err instanceof CrudHttpError) {\n return NextResponse.json(err.body, { status: err.status })\n }\n console.error('customers.pipelines POST failed', err)\n return NextResponse.json({ error: 'Failed to create pipeline' }, { status: 400 })\n }\n}\n\nexport async function PUT(req: Request) {\n try {\n const { ctx } = await buildContext(req)\n const body = await req.json().catch(() => ({}))\n const { translate } = await resolveTranslations()\n const scoped = withScopedPayload(body, ctx, translate)\n\n const commandBus = (ctx.container.resolve('commandBus') as CommandBus)\n const { logEntry } = await commandBus.execute<PipelineUpdateInput, void>(\n 'customers.pipelines.update',\n { input: pipelineUpdateSchema.parse(scoped), ctx },\n )\n const response = NextResponse.json({ ok: true })\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.pipeline',\n resourceId: logEntry.resourceId ?? null,\n executedAt: logEntry.createdAt instanceof Date ? logEntry.createdAt.toISOString() : undefined,\n })\n )\n }\n return response\n } catch (err) {\n if (err instanceof CrudHttpError) {\n return NextResponse.json(err.body, { status: err.status })\n }\n console.error('customers.pipelines PUT failed', err)\n return NextResponse.json({ error: 'Failed to update pipeline' }, { status: 400 })\n }\n}\n\nexport async function DELETE(req: Request) {\n try {\n const { ctx } = await buildContext(req)\n const body = await req.json().catch(() => ({}))\n const { translate } = await resolveTranslations()\n const scoped = withScopedPayload(body, ctx, translate)\n\n const commandBus = (ctx.container.resolve('commandBus') as CommandBus)\n await commandBus.execute<PipelineDeleteInput, void>(\n 'customers.pipelines.delete',\n { input: pipelineDeleteSchema.parse(scoped), ctx },\n )\n return NextResponse.json({ ok: true })\n } catch (err) {\n if (err instanceof CrudHttpError) {\n return NextResponse.json(err.body, { status: err.status })\n }\n console.error('customers.pipelines DELETE failed', err)\n return NextResponse.json({ error: 'Failed to delete pipeline' }, { status: 400 })\n }\n}\n\nconst pipelineItemSchema = z.object({\n id: z.string().uuid(),\n name: z.string(),\n isDefault: z.boolean(),\n organizationId: z.string().uuid(),\n tenantId: z.string().uuid(),\n createdAt: z.date(),\n updatedAt: z.date(),\n})\n\nconst pipelineListResponseSchema = z.object({\n items: z.array(pipelineItemSchema),\n total: z.number(),\n})\n\nconst pipelineCreateResponseSchema = z.object({\n id: z.string().uuid().nullable(),\n})\n\nconst pipelineOkResponseSchema = z.object({\n ok: z.boolean(),\n})\n\nconst pipelineErrorSchema = z.object({\n error: z.string(),\n})\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Customers',\n summary: 'Manage customer pipelines',\n methods: {\n GET: {\n summary: 'List pipelines',\n description: 'Returns a list of pipelines scoped to the authenticated organization.',\n query: z.object({ isDefault: z.string().optional() }),\n responses: [\n { status: 200, description: 'Pipeline list', schema: pipelineListResponseSchema },\n ],\n errors: [\n { status: 401, description: 'Unauthorized', schema: pipelineErrorSchema },\n { status: 400, description: 'Invalid request', schema: pipelineErrorSchema },\n ],\n },\n POST: {\n summary: 'Create pipeline',\n description: 'Creates a new pipeline within the authenticated organization.',\n requestBody: { contentType: 'application/json', schema: pipelineCreateSchema },\n responses: [\n { status: 201, description: 'Pipeline created', schema: pipelineCreateResponseSchema },\n ],\n errors: [\n { status: 400, description: 'Validation failed', schema: pipelineErrorSchema },\n { status: 401, description: 'Unauthorized', schema: pipelineErrorSchema },\n ],\n },\n PUT: {\n summary: 'Update pipeline',\n description: 'Updates an existing pipeline.',\n requestBody: { contentType: 'application/json', schema: pipelineUpdateSchema },\n responses: [\n { status: 200, description: 'Pipeline updated', schema: pipelineOkResponseSchema },\n ],\n errors: [\n { status: 400, description: 'Validation failed', schema: pipelineErrorSchema },\n { status: 404, description: 'Pipeline not found', schema: pipelineErrorSchema },\n ],\n },\n DELETE: {\n summary: 'Delete pipeline',\n description: 'Deletes a pipeline. Returns 409 if active deals exist.',\n requestBody: { contentType: 'application/json', schema: pipelineDeleteSchema },\n responses: [\n { status: 200, description: 'Pipeline deleted', schema: pipelineOkResponseSchema },\n ],\n errors: [\n { status: 409, description: 'Pipeline has active deals', schema: pipelineErrorSchema },\n { status: 404, description: 'Pipeline not found', schema: pipelineErrorSchema },\n ],\n },\n },\n}\n"],
|
|
5
|
-
"mappings": "AAAA,SAAS,oBAAoB;AAC7B,SAAS,SAAS;AAClB,SAAS,8BAA8B;AACvC,SAAS,0BAA0B;AACnC,SAAS,0CAA0C;AAGnD,SAAS,wBAAwB;AACjC;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OAIK;AACP,SAAS,yBAAyB;AAClC,SAAS,
|
|
4
|
+
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { resolveOrganizationScopeForRequest } from '@open-mercato/core/modules/directory/utils/organizationScope'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport type { CommandRuntimeContext, CommandBus } from '@open-mercato/shared/lib/commands'\nimport { CustomerPipeline } from '../../data/entities'\nimport {\n pipelineCreateSchema,\n pipelineUpdateSchema,\n pipelineDeleteSchema,\n type PipelineCreateInput,\n type PipelineUpdateInput,\n type PipelineDeleteInput,\n} from '../../data/validators'\nimport { withScopedPayload } from '../utils'\nimport { CrudHttpError, isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { serializeOperationMetadata } from '@open-mercato/shared/lib/commands/operationMetadata'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\n\nexport const metadata = {\n GET: { requireAuth: true, requireFeatures: ['customers.pipelines.view'] },\n POST: { requireAuth: true, requireFeatures: ['customers.pipelines.manage'] },\n PUT: { requireAuth: true, requireFeatures: ['customers.pipelines.manage'] },\n DELETE: { requireAuth: true, requireFeatures: ['customers.pipelines.manage'] },\n}\n\nasync function buildContext(\n req: Request\n): Promise<{ ctx: CommandRuntimeContext; organizationId: string | null; tenantId: string | null }> {\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 const organizationId = scope?.selectedId ?? auth.orgId ?? null\n const tenantId = auth.tenantId ?? null\n return { ctx, organizationId, tenantId }\n}\n\nexport async function GET(req: Request) {\n try {\n const { ctx, organizationId, tenantId } = await buildContext(req)\n if (!organizationId || !tenantId) {\n return NextResponse.json({ error: 'Organization and tenant context required' }, { status: 400 })\n }\n const url = new URL(req.url)\n const isDefaultParam = url.searchParams.get('isDefault')\n\n const em = (ctx.container.resolve('em') as EntityManager)\n const where: Record<string, unknown> = { organizationId, tenantId }\n if (isDefaultParam === 'true') where.isDefault = true\n if (isDefaultParam === 'false') where.isDefault = false\n\n const pipelines = await em.find(CustomerPipeline, where, { orderBy: { createdAt: 'ASC' } })\n const items = pipelines.map((pipeline) => ({\n id: pipeline.id,\n name: pipeline.name,\n isDefault: pipeline.isDefault,\n organizationId: pipeline.organizationId,\n tenantId: pipeline.tenantId,\n createdAt: pipeline.createdAt,\n updatedAt: pipeline.updatedAt,\n }))\n return NextResponse.json({ items, total: items.length })\n } catch (err) {\n if (isCrudHttpError(err)) {\n return NextResponse.json(err.body, { status: err.status })\n }\n console.error('customers.pipelines GET failed', err)\n return NextResponse.json({ error: 'Failed to load pipelines' }, { status: 500 })\n }\n}\n\nexport async function POST(req: Request) {\n try {\n const { ctx } = await buildContext(req)\n const body = await req.json().catch(() => ({}))\n const { translate } = await resolveTranslations()\n const scoped = withScopedPayload(body, ctx, translate)\n\n const commandBus = (ctx.container.resolve('commandBus') as CommandBus)\n const { result, logEntry } = await commandBus.execute<PipelineCreateInput, { pipelineId: string }>(\n 'customers.pipelines.create',\n { input: pipelineCreateSchema.parse(scoped), ctx },\n )\n const response = NextResponse.json({ id: result?.pipelineId ?? 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.pipeline',\n resourceId: logEntry.resourceId ?? result?.pipelineId ?? 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 console.error('customers.pipelines POST failed', err)\n return NextResponse.json({ error: 'Failed to create pipeline' }, { status: 400 })\n }\n}\n\nexport async function PUT(req: Request) {\n try {\n const { ctx } = await buildContext(req)\n const body = await req.json().catch(() => ({}))\n const { translate } = await resolveTranslations()\n const scoped = withScopedPayload(body, ctx, translate)\n\n const commandBus = (ctx.container.resolve('commandBus') as CommandBus)\n const { logEntry } = await commandBus.execute<PipelineUpdateInput, void>(\n 'customers.pipelines.update',\n { input: pipelineUpdateSchema.parse(scoped), ctx },\n )\n const response = NextResponse.json({ ok: true })\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.pipeline',\n resourceId: logEntry.resourceId ?? 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 console.error('customers.pipelines PUT failed', err)\n return NextResponse.json({ error: 'Failed to update pipeline' }, { status: 400 })\n }\n}\n\nexport async function DELETE(req: Request) {\n try {\n const { ctx } = await buildContext(req)\n const body = await req.json().catch(() => ({}))\n const { translate } = await resolveTranslations()\n const scoped = withScopedPayload(body, ctx, translate)\n\n const commandBus = (ctx.container.resolve('commandBus') as CommandBus)\n await commandBus.execute<PipelineDeleteInput, void>(\n 'customers.pipelines.delete',\n { input: pipelineDeleteSchema.parse(scoped), ctx },\n )\n return NextResponse.json({ ok: true })\n } catch (err) {\n if (isCrudHttpError(err)) {\n return NextResponse.json(err.body, { status: err.status })\n }\n console.error('customers.pipelines DELETE failed', err)\n return NextResponse.json({ error: 'Failed to delete pipeline' }, { status: 400 })\n }\n}\n\nconst pipelineItemSchema = z.object({\n id: z.string().uuid(),\n name: z.string(),\n isDefault: z.boolean(),\n organizationId: z.string().uuid(),\n tenantId: z.string().uuid(),\n createdAt: z.date(),\n updatedAt: z.date(),\n})\n\nconst pipelineListResponseSchema = z.object({\n items: z.array(pipelineItemSchema),\n total: z.number(),\n})\n\nconst pipelineCreateResponseSchema = z.object({\n id: z.string().uuid().nullable(),\n})\n\nconst pipelineOkResponseSchema = z.object({\n ok: z.boolean(),\n})\n\nconst pipelineErrorSchema = z.object({\n error: z.string(),\n})\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Customers',\n summary: 'Manage customer pipelines',\n methods: {\n GET: {\n summary: 'List pipelines',\n description: 'Returns a list of pipelines scoped to the authenticated organization.',\n query: z.object({ isDefault: z.string().optional() }),\n responses: [\n { status: 200, description: 'Pipeline list', schema: pipelineListResponseSchema },\n ],\n errors: [\n { status: 401, description: 'Unauthorized', schema: pipelineErrorSchema },\n { status: 400, description: 'Invalid request', schema: pipelineErrorSchema },\n ],\n },\n POST: {\n summary: 'Create pipeline',\n description: 'Creates a new pipeline within the authenticated organization.',\n requestBody: { contentType: 'application/json', schema: pipelineCreateSchema },\n responses: [\n { status: 201, description: 'Pipeline created', schema: pipelineCreateResponseSchema },\n ],\n errors: [\n { status: 400, description: 'Validation failed', schema: pipelineErrorSchema },\n { status: 401, description: 'Unauthorized', schema: pipelineErrorSchema },\n ],\n },\n PUT: {\n summary: 'Update pipeline',\n description: 'Updates an existing pipeline.',\n requestBody: { contentType: 'application/json', schema: pipelineUpdateSchema },\n responses: [\n { status: 200, description: 'Pipeline updated', schema: pipelineOkResponseSchema },\n ],\n errors: [\n { status: 400, description: 'Validation failed', schema: pipelineErrorSchema },\n { status: 404, description: 'Pipeline not found', schema: pipelineErrorSchema },\n ],\n },\n DELETE: {\n summary: 'Delete pipeline',\n description: 'Deletes a pipeline. Returns 409 if active deals exist.',\n requestBody: { contentType: 'application/json', schema: pipelineDeleteSchema },\n responses: [\n { status: 200, description: 'Pipeline deleted', schema: pipelineOkResponseSchema },\n ],\n errors: [\n { status: 409, description: 'Pipeline has active deals', schema: pipelineErrorSchema },\n { status: 404, description: 'Pipeline not found', schema: pipelineErrorSchema },\n ],\n },\n },\n}\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,oBAAoB;AAC7B,SAAS,SAAS;AAClB,SAAS,8BAA8B;AACvC,SAAS,0BAA0B;AACnC,SAAS,0CAA0C;AAGnD,SAAS,wBAAwB;AACjC;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OAIK;AACP,SAAS,yBAAyB;AAClC,SAAS,eAAe,uBAAuB;AAC/C,SAAS,2BAA2B;AACpC,SAAS,kCAAkC;AAGpC,MAAM,WAAW;AAAA,EACtB,KAAK,EAAE,aAAa,MAAM,iBAAiB,CAAC,0BAA0B,EAAE;AAAA,EACxE,MAAM,EAAE,aAAa,MAAM,iBAAiB,CAAC,4BAA4B,EAAE;AAAA,EAC3E,KAAK,EAAE,aAAa,MAAM,iBAAiB,CAAC,4BAA4B,EAAE;AAAA,EAC1E,QAAQ,EAAE,aAAa,MAAM,iBAAiB,CAAC,4BAA4B,EAAE;AAC/E;AAEA,eAAe,aACb,KACiG;AACjG,QAAM,YAAY,MAAM,uBAAuB;AAC/C,QAAM,OAAO,MAAM,mBAAmB,GAAG;AACzC,QAAM,EAAE,UAAU,IAAI,MAAM,oBAAoB;AAChD,MAAI,CAAC,KAAM,OAAM,IAAI,cAAc,KAAK,EAAE,OAAO,UAAU,iCAAiC,cAAc,EAAE,CAAC;AAC7G,QAAM,QAAQ,MAAM,mCAAmC,EAAE,WAAW,MAAM,SAAS,IAAI,CAAC;AACxF,QAAM,MAA6B;AAAA,IACjC;AAAA,IACA;AAAA,IACA,mBAAmB;AAAA,IACnB,wBAAwB,OAAO,cAAc,KAAK,SAAS;AAAA,IAC3D,iBAAiB,OAAO,cAAc,KAAK,QAAQ,CAAC,KAAK,KAAK,IAAI;AAAA,IAClE,SAAS;AAAA,EACX;AACA,QAAM,iBAAiB,OAAO,cAAc,KAAK,SAAS;AAC1D,QAAM,WAAW,KAAK,YAAY;AAClC,SAAO,EAAE,KAAK,gBAAgB,SAAS;AACzC;AAEA,eAAsB,IAAI,KAAc;AACtC,MAAI;AACF,UAAM,EAAE,KAAK,gBAAgB,SAAS,IAAI,MAAM,aAAa,GAAG;AAChE,QAAI,CAAC,kBAAkB,CAAC,UAAU;AAChC,aAAO,aAAa,KAAK,EAAE,OAAO,2CAA2C,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,IACjG;AACA,UAAM,MAAM,IAAI,IAAI,IAAI,GAAG;AAC3B,UAAM,iBAAiB,IAAI,aAAa,IAAI,WAAW;AAEvD,UAAM,KAAM,IAAI,UAAU,QAAQ,IAAI;AACtC,UAAM,QAAiC,EAAE,gBAAgB,SAAS;AAClE,QAAI,mBAAmB,OAAQ,OAAM,YAAY;AACjD,QAAI,mBAAmB,QAAS,OAAM,YAAY;AAElD,UAAM,YAAY,MAAM,GAAG,KAAK,kBAAkB,OAAO,EAAE,SAAS,EAAE,WAAW,MAAM,EAAE,CAAC;AAC1F,UAAM,QAAQ,UAAU,IAAI,CAAC,cAAc;AAAA,MACzC,IAAI,SAAS;AAAA,MACb,MAAM,SAAS;AAAA,MACf,WAAW,SAAS;AAAA,MACpB,gBAAgB,SAAS;AAAA,MACzB,UAAU,SAAS;AAAA,MACnB,WAAW,SAAS;AAAA,MACpB,WAAW,SAAS;AAAA,IACtB,EAAE;AACF,WAAO,aAAa,KAAK,EAAE,OAAO,OAAO,MAAM,OAAO,CAAC;AAAA,EACzD,SAAS,KAAK;AACZ,QAAI,gBAAgB,GAAG,GAAG;AACxB,aAAO,aAAa,KAAK,IAAI,MAAM,EAAE,QAAQ,IAAI,OAAO,CAAC;AAAA,IAC3D;AACA,YAAQ,MAAM,kCAAkC,GAAG;AACnD,WAAO,aAAa,KAAK,EAAE,OAAO,2BAA2B,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EACjF;AACF;AAEA,eAAsB,KAAK,KAAc;AACvC,MAAI;AACF,UAAM,EAAE,IAAI,IAAI,MAAM,aAAa,GAAG;AACtC,UAAM,OAAO,MAAM,IAAI,KAAK,EAAE,MAAM,OAAO,CAAC,EAAE;AAC9C,UAAM,EAAE,UAAU,IAAI,MAAM,oBAAoB;AAChD,UAAM,SAAS,kBAAkB,MAAM,KAAK,SAAS;AAErD,UAAM,aAAc,IAAI,UAAU,QAAQ,YAAY;AACtD,UAAM,EAAE,QAAQ,SAAS,IAAI,MAAM,WAAW;AAAA,MAC5C;AAAA,MACA,EAAE,OAAO,qBAAqB,MAAM,MAAM,GAAG,IAAI;AAAA,IACnD;AACA,UAAM,WAAW,aAAa,KAAK,EAAE,IAAI,QAAQ,cAAc,KAAK,GAAG,EAAE,QAAQ,IAAI,CAAC;AACtF,QAAI,UAAU,aAAa,UAAU,MAAM,UAAU,WAAW;AAC9D,eAAS,QAAQ;AAAA,QACf;AAAA,QACA,2BAA2B;AAAA,UACzB,IAAI,SAAS;AAAA,UACb,WAAW,SAAS;AAAA,UACpB,WAAW,SAAS;AAAA,UACpB,aAAa,SAAS,eAAe;AAAA,UACrC,cAAc,SAAS,gBAAgB;AAAA,UACvC,YAAY,SAAS,cAAc,QAAQ,cAAc;AAAA,UACzD,YAAY,SAAS,qBAAqB,OAAO,SAAS,UAAU,YAAY,IAAI;AAAA,QACtF,CAAC;AAAA,MACH;AAAA,IACF;AACA,WAAO;AAAA,EACT,SAAS,KAAK;AACZ,QAAI,gBAAgB,GAAG,GAAG;AACxB,aAAO,aAAa,KAAK,IAAI,MAAM,EAAE,QAAQ,IAAI,OAAO,CAAC;AAAA,IAC3D;AACA,YAAQ,MAAM,mCAAmC,GAAG;AACpD,WAAO,aAAa,KAAK,EAAE,OAAO,4BAA4B,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EAClF;AACF;AAEA,eAAsB,IAAI,KAAc;AACtC,MAAI;AACF,UAAM,EAAE,IAAI,IAAI,MAAM,aAAa,GAAG;AACtC,UAAM,OAAO,MAAM,IAAI,KAAK,EAAE,MAAM,OAAO,CAAC,EAAE;AAC9C,UAAM,EAAE,UAAU,IAAI,MAAM,oBAAoB;AAChD,UAAM,SAAS,kBAAkB,MAAM,KAAK,SAAS;AAErD,UAAM,aAAc,IAAI,UAAU,QAAQ,YAAY;AACtD,UAAM,EAAE,SAAS,IAAI,MAAM,WAAW;AAAA,MACpC;AAAA,MACA,EAAE,OAAO,qBAAqB,MAAM,MAAM,GAAG,IAAI;AAAA,IACnD;AACA,UAAM,WAAW,aAAa,KAAK,EAAE,IAAI,KAAK,CAAC;AAC/C,QAAI,UAAU,aAAa,UAAU,MAAM,UAAU,WAAW;AAC9D,eAAS,QAAQ;AAAA,QACf;AAAA,QACA,2BAA2B;AAAA,UACzB,IAAI,SAAS;AAAA,UACb,WAAW,SAAS;AAAA,UACpB,WAAW,SAAS;AAAA,UACpB,aAAa,SAAS,eAAe;AAAA,UACrC,cAAc,SAAS,gBAAgB;AAAA,UACvC,YAAY,SAAS,cAAc;AAAA,UACnC,YAAY,SAAS,qBAAqB,OAAO,SAAS,UAAU,YAAY,IAAI;AAAA,QACtF,CAAC;AAAA,MACH;AAAA,IACF;AACA,WAAO;AAAA,EACT,SAAS,KAAK;AACZ,QAAI,gBAAgB,GAAG,GAAG;AACxB,aAAO,aAAa,KAAK,IAAI,MAAM,EAAE,QAAQ,IAAI,OAAO,CAAC;AAAA,IAC3D;AACA,YAAQ,MAAM,kCAAkC,GAAG;AACnD,WAAO,aAAa,KAAK,EAAE,OAAO,4BAA4B,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EAClF;AACF;AAEA,eAAsB,OAAO,KAAc;AACzC,MAAI;AACF,UAAM,EAAE,IAAI,IAAI,MAAM,aAAa,GAAG;AACtC,UAAM,OAAO,MAAM,IAAI,KAAK,EAAE,MAAM,OAAO,CAAC,EAAE;AAC9C,UAAM,EAAE,UAAU,IAAI,MAAM,oBAAoB;AAChD,UAAM,SAAS,kBAAkB,MAAM,KAAK,SAAS;AAErD,UAAM,aAAc,IAAI,UAAU,QAAQ,YAAY;AACtD,UAAM,WAAW;AAAA,MACf;AAAA,MACA,EAAE,OAAO,qBAAqB,MAAM,MAAM,GAAG,IAAI;AAAA,IACnD;AACA,WAAO,aAAa,KAAK,EAAE,IAAI,KAAK,CAAC;AAAA,EACvC,SAAS,KAAK;AACZ,QAAI,gBAAgB,GAAG,GAAG;AACxB,aAAO,aAAa,KAAK,IAAI,MAAM,EAAE,QAAQ,IAAI,OAAO,CAAC;AAAA,IAC3D;AACA,YAAQ,MAAM,qCAAqC,GAAG;AACtD,WAAO,aAAa,KAAK,EAAE,OAAO,4BAA4B,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EAClF;AACF;AAEA,MAAM,qBAAqB,EAAE,OAAO;AAAA,EAClC,IAAI,EAAE,OAAO,EAAE,KAAK;AAAA,EACpB,MAAM,EAAE,OAAO;AAAA,EACf,WAAW,EAAE,QAAQ;AAAA,EACrB,gBAAgB,EAAE,OAAO,EAAE,KAAK;AAAA,EAChC,UAAU,EAAE,OAAO,EAAE,KAAK;AAAA,EAC1B,WAAW,EAAE,KAAK;AAAA,EAClB,WAAW,EAAE,KAAK;AACpB,CAAC;AAED,MAAM,6BAA6B,EAAE,OAAO;AAAA,EAC1C,OAAO,EAAE,MAAM,kBAAkB;AAAA,EACjC,OAAO,EAAE,OAAO;AAClB,CAAC;AAED,MAAM,+BAA+B,EAAE,OAAO;AAAA,EAC5C,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS;AACjC,CAAC;AAED,MAAM,2BAA2B,EAAE,OAAO;AAAA,EACxC,IAAI,EAAE,QAAQ;AAChB,CAAC;AAED,MAAM,sBAAsB,EAAE,OAAO;AAAA,EACnC,OAAO,EAAE,OAAO;AAClB,CAAC;AAEM,MAAM,UAA2B;AAAA,EACtC,KAAK;AAAA,EACL,SAAS;AAAA,EACT,SAAS;AAAA,IACP,KAAK;AAAA,MACH,SAAS;AAAA,MACT,aAAa;AAAA,MACb,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC;AAAA,MACpD,WAAW;AAAA,QACT,EAAE,QAAQ,KAAK,aAAa,iBAAiB,QAAQ,2BAA2B;AAAA,MAClF;AAAA,MACA,QAAQ;AAAA,QACN,EAAE,QAAQ,KAAK,aAAa,gBAAgB,QAAQ,oBAAoB;AAAA,QACxE,EAAE,QAAQ,KAAK,aAAa,mBAAmB,QAAQ,oBAAoB;AAAA,MAC7E;AAAA,IACF;AAAA,IACA,MAAM;AAAA,MACJ,SAAS;AAAA,MACT,aAAa;AAAA,MACb,aAAa,EAAE,aAAa,oBAAoB,QAAQ,qBAAqB;AAAA,MAC7E,WAAW;AAAA,QACT,EAAE,QAAQ,KAAK,aAAa,oBAAoB,QAAQ,6BAA6B;AAAA,MACvF;AAAA,MACA,QAAQ;AAAA,QACN,EAAE,QAAQ,KAAK,aAAa,qBAAqB,QAAQ,oBAAoB;AAAA,QAC7E,EAAE,QAAQ,KAAK,aAAa,gBAAgB,QAAQ,oBAAoB;AAAA,MAC1E;AAAA,IACF;AAAA,IACA,KAAK;AAAA,MACH,SAAS;AAAA,MACT,aAAa;AAAA,MACb,aAAa,EAAE,aAAa,oBAAoB,QAAQ,qBAAqB;AAAA,MAC7E,WAAW;AAAA,QACT,EAAE,QAAQ,KAAK,aAAa,oBAAoB,QAAQ,yBAAyB;AAAA,MACnF;AAAA,MACA,QAAQ;AAAA,QACN,EAAE,QAAQ,KAAK,aAAa,qBAAqB,QAAQ,oBAAoB;AAAA,QAC7E,EAAE,QAAQ,KAAK,aAAa,sBAAsB,QAAQ,oBAAoB;AAAA,MAChF;AAAA,IACF;AAAA,IACA,QAAQ;AAAA,MACN,SAAS;AAAA,MACT,aAAa;AAAA,MACb,aAAa,EAAE,aAAa,oBAAoB,QAAQ,qBAAqB;AAAA,MAC7E,WAAW;AAAA,QACT,EAAE,QAAQ,KAAK,aAAa,oBAAoB,QAAQ,yBAAyB;AAAA,MACnF;AAAA,MACA,QAAQ;AAAA,QACN,EAAE,QAAQ,KAAK,aAAa,6BAA6B,QAAQ,oBAAoB;AAAA,QACrF,EAAE,QAAQ,KAAK,aAAa,sBAAsB,QAAQ,oBAAoB;AAAA,MAChF;AAAA,IACF;AAAA,EACF;AACF;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -4,7 +4,7 @@ import { createRequestContainer } from "@open-mercato/shared/lib/di/container";
|
|
|
4
4
|
import { getAuthFromRequest } from "@open-mercato/shared/lib/auth/server";
|
|
5
5
|
import { resolveOrganizationScopeForRequest } from "@open-mercato/core/modules/directory/utils/organizationScope";
|
|
6
6
|
import { resolveTranslations } from "@open-mercato/shared/lib/i18n/server";
|
|
7
|
-
import { CrudHttpError } from "@open-mercato/shared/lib/crud/errors";
|
|
7
|
+
import { CrudHttpError, isCrudHttpError } from "@open-mercato/shared/lib/crud/errors";
|
|
8
8
|
import { customerSettingsUpsertSchema } from "../../../data/validators.js";
|
|
9
9
|
import { loadCustomerSettings } from "../../../commands/settings.js";
|
|
10
10
|
import { withScopedPayload } from "../../utils.js";
|
|
@@ -49,7 +49,7 @@ async function GET(req) {
|
|
|
49
49
|
addressFormat: record?.addressFormat ?? "line_first"
|
|
50
50
|
});
|
|
51
51
|
} catch (err) {
|
|
52
|
-
if (err
|
|
52
|
+
if (isCrudHttpError(err)) {
|
|
53
53
|
return NextResponse.json(err.body, { status: err.status });
|
|
54
54
|
}
|
|
55
55
|
const { translate } = await resolveTranslations();
|
|
@@ -72,7 +72,7 @@ async function PUT(req) {
|
|
|
72
72
|
addressFormat: result?.addressFormat ?? input.addressFormat
|
|
73
73
|
});
|
|
74
74
|
} catch (err) {
|
|
75
|
-
if (err
|
|
75
|
+
if (isCrudHttpError(err)) {
|
|
76
76
|
return NextResponse.json(err.body, { status: err.status });
|
|
77
77
|
}
|
|
78
78
|
const { translate } = await resolveTranslations();
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../src/modules/customers/api/settings/address-format/route.ts"],
|
|
4
|
-
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { resolveOrganizationScopeForRequest } from '@open-mercato/core/modules/directory/utils/organizationScope'\nimport type { CommandBus, CommandRuntimeContext } from '@open-mercato/shared/lib/commands'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { customerSettingsUpsertSchema, type CustomerSettingsUpsertInput } from '../../../data/validators'\nimport { loadCustomerSettings } from '../../../commands/settings'\nimport type { CustomerAddressFormat } from '../../../data/entities'\nimport { withScopedPayload } from '../../utils'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\n\nexport const metadata = {\n GET: { requireAuth: true, requireFeatures: ['customers.settings.manage'] },\n PUT: { requireAuth: true, requireFeatures: ['customers.settings.manage'] },\n}\n\ntype SettingsRouteContext = {\n ctx: CommandRuntimeContext\n tenantId: string\n organizationId: string\n translate: (key: string, fallback?: string) => string\n em: EntityManager\n}\n\nasync function resolveSettingsContext(req: Request): Promise<SettingsRouteContext> {\n const container = await createRequestContainer()\n const auth = await getAuthFromRequest(req)\n const { translate } = await resolveTranslations()\n if (!auth || !auth.tenantId) {\n throw new CrudHttpError(401, { error: translate('customers.errors.unauthorized', 'Unauthorized') })\n }\n\n const scope = await resolveOrganizationScopeForRequest({ container, auth, request: req })\n const organizationId = scope?.selectedId ?? auth.orgId ?? null\n if (!organizationId) {\n throw new CrudHttpError(400, { error: translate('customers.errors.organization_required', 'Organization context is required') })\n }\n\n const ctx: CommandRuntimeContext = {\n container,\n auth,\n organizationScope: scope,\n selectedOrganizationId: organizationId,\n organizationIds: scope?.filterIds ?? (auth.orgId ? [auth.orgId] : null),\n request: req,\n }\n\n const em = (container.resolve('em') as EntityManager)\n return {\n ctx,\n tenantId: auth.tenantId,\n organizationId,\n translate,\n em,\n }\n}\n\nexport async function GET(req: Request) {\n try {\n const { em, tenantId, organizationId } = await resolveSettingsContext(req)\n const record = await loadCustomerSettings(em, { tenantId, organizationId })\n return NextResponse.json({\n addressFormat: record?.addressFormat ?? 'line_first',\n })\n } catch (err) {\n if (err
|
|
5
|
-
"mappings": "AAAA,SAAS,oBAAoB;AAC7B,SAAS,SAAS;AAClB,SAAS,8BAA8B;AACvC,SAAS,0BAA0B;AACnC,SAAS,0CAA0C;AAGnD,SAAS,2BAA2B;AACpC,SAAS,
|
|
4
|
+
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { resolveOrganizationScopeForRequest } from '@open-mercato/core/modules/directory/utils/organizationScope'\nimport type { CommandBus, CommandRuntimeContext } from '@open-mercato/shared/lib/commands'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { CrudHttpError, isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { customerSettingsUpsertSchema, type CustomerSettingsUpsertInput } from '../../../data/validators'\nimport { loadCustomerSettings } from '../../../commands/settings'\nimport type { CustomerAddressFormat } from '../../../data/entities'\nimport { withScopedPayload } from '../../utils'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\n\nexport const metadata = {\n GET: { requireAuth: true, requireFeatures: ['customers.settings.manage'] },\n PUT: { requireAuth: true, requireFeatures: ['customers.settings.manage'] },\n}\n\ntype SettingsRouteContext = {\n ctx: CommandRuntimeContext\n tenantId: string\n organizationId: string\n translate: (key: string, fallback?: string) => string\n em: EntityManager\n}\n\nasync function resolveSettingsContext(req: Request): Promise<SettingsRouteContext> {\n const container = await createRequestContainer()\n const auth = await getAuthFromRequest(req)\n const { translate } = await resolveTranslations()\n if (!auth || !auth.tenantId) {\n throw new CrudHttpError(401, { error: translate('customers.errors.unauthorized', 'Unauthorized') })\n }\n\n const scope = await resolveOrganizationScopeForRequest({ container, auth, request: req })\n const organizationId = scope?.selectedId ?? auth.orgId ?? null\n if (!organizationId) {\n throw new CrudHttpError(400, { error: translate('customers.errors.organization_required', 'Organization context is required') })\n }\n\n const ctx: CommandRuntimeContext = {\n container,\n auth,\n organizationScope: scope,\n selectedOrganizationId: organizationId,\n organizationIds: scope?.filterIds ?? (auth.orgId ? [auth.orgId] : null),\n request: req,\n }\n\n const em = (container.resolve('em') as EntityManager)\n return {\n ctx,\n tenantId: auth.tenantId,\n organizationId,\n translate,\n em,\n }\n}\n\nexport async function GET(req: Request) {\n try {\n const { em, tenantId, organizationId } = await resolveSettingsContext(req)\n const record = await loadCustomerSettings(em, { tenantId, organizationId })\n return NextResponse.json({\n addressFormat: record?.addressFormat ?? 'line_first',\n })\n } catch (err) {\n if (isCrudHttpError(err)) {\n return NextResponse.json(err.body, { status: err.status })\n }\n const { translate } = await resolveTranslations()\n console.error('customers.settings.address-format.get failed', err)\n return NextResponse.json({ error: translate('customers.errors.lookup_failed', 'Failed to load settings') }, { status: 400 })\n }\n}\n\nexport async function PUT(req: Request) {\n try {\n const { ctx, translate } = await resolveSettingsContext(req)\n const payload = await req.json().catch(() => ({}))\n const scoped = withScopedPayload(payload, ctx, translate)\n const input = customerSettingsUpsertSchema.parse(scoped)\n\n const commandBus = (ctx.container.resolve('commandBus') as CommandBus)\n const { result } = await commandBus.execute<CustomerSettingsUpsertInput, { settingsId: string; addressFormat: CustomerAddressFormat }>(\n 'customers.settings.save',\n { input, ctx },\n )\n\n return NextResponse.json({\n addressFormat: result?.addressFormat ?? input.addressFormat,\n })\n } catch (err) {\n if (isCrudHttpError(err)) {\n return NextResponse.json(err.body, { status: err.status })\n }\n const { translate } = await resolveTranslations()\n console.error('customers.settings.address-format.put failed', err)\n return NextResponse.json({ error: translate('customers.errors.save_failed', 'Failed to save settings') }, { status: 400 })\n }\n}\n\nconst addressFormatResponseSchema = z.object({\n addressFormat: z.string(),\n})\n\nconst addressFormatErrorSchema = z.object({\n error: z.string(),\n})\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Customers',\n summary: 'Customer address format settings',\n methods: {\n GET: {\n summary: 'Retrieve address format',\n description: 'Returns the current address formatting preference for the selected organization.',\n responses: [\n { status: 200, description: 'Current address format', schema: addressFormatResponseSchema },\n { status: 401, description: 'Unauthorized', schema: addressFormatErrorSchema },\n { status: 400, description: 'Organization context missing', schema: addressFormatErrorSchema },\n ],\n },\n PUT: {\n summary: 'Update address format',\n description: 'Updates the address format preference for the selected organization.',\n requestBody: {\n contentType: 'application/json',\n schema: customerSettingsUpsertSchema,\n },\n responses: [\n { status: 200, description: 'Updated address format', schema: addressFormatResponseSchema },\n { status: 401, description: 'Unauthorized', schema: addressFormatErrorSchema },\n { status: 400, description: 'Invalid payload or organization context', schema: addressFormatErrorSchema },\n ],\n },\n },\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -5,7 +5,7 @@ import { getAuthFromRequest } from "@open-mercato/shared/lib/auth/server";
|
|
|
5
5
|
import { resolveOrganizationScopeForRequest } from "@open-mercato/core/modules/directory/utils/organizationScope";
|
|
6
6
|
import { serializeOperationMetadata } from "@open-mercato/shared/lib/commands/operationMetadata";
|
|
7
7
|
import { tagAssignmentSchema } from "../../../data/validators.js";
|
|
8
|
-
import { CrudHttpError } from "@open-mercato/shared/lib/crud/errors";
|
|
8
|
+
import { CrudHttpError, isCrudHttpError } from "@open-mercato/shared/lib/crud/errors";
|
|
9
9
|
import { resolveTranslations } from "@open-mercato/shared/lib/i18n/server";
|
|
10
10
|
import { withScopedPayload } from "../../utils.js";
|
|
11
11
|
const metadata = {
|
|
@@ -55,7 +55,7 @@ async function POST(req) {
|
|
|
55
55
|
}
|
|
56
56
|
return response;
|
|
57
57
|
} catch (err) {
|
|
58
|
-
if (err
|
|
58
|
+
if (isCrudHttpError(err)) {
|
|
59
59
|
return NextResponse.json(err.body, { status: err.status });
|
|
60
60
|
}
|
|
61
61
|
const { translate } = await resolveTranslations();
|
|
@@ -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 } from '@open-mercato/shared/lib/crud/errors'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { withScopedPayload } from '../../utils'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\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 body = await req.json().catch(() => ({}))\n const scoped = withScopedPayload(body, ctx, translate)\n const input = tagAssignmentSchema.parse(scoped)\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 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 (err
|
|
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,
|
|
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 type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\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 body = await req.json().catch(() => ({}))\n const scoped = withScopedPayload(body, ctx, translate)\n const input = tagAssignmentSchema.parse(scoped)\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 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 console.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;AAG3B,MAAM,WAAW;AAAA,EACtB,MAAM,EAAE,aAAa,MAAM,iBAAiB,CAAC,6BAA6B,EAAE;AAC9E;AAEA,eAAe,aACb,KACsJ;AACtJ,QAAM,YAAY,MAAM,uBAAuB;AAC/C,QAAM,OAAO,MAAM,mBAAmB,GAAG;AACzC,QAAM,EAAE,UAAU,IAAI,MAAM,oBAAoB;AAChD,MAAI,CAAC,KAAM,OAAM,IAAI,cAAc,KAAK,EAAE,OAAO,UAAU,iCAAiC,cAAc,EAAE,CAAC;AAC7G,QAAM,QAAQ,MAAM,mCAAmC,EAAE,WAAW,MAAM,SAAS,IAAI,CAAC;AACxF,QAAM,MAA6B;AAAA,IACjC;AAAA,IACA;AAAA,IACA,mBAAmB;AAAA,IACnB,wBAAwB,OAAO,cAAc,KAAK,SAAS;AAAA,IAC3D,iBAAiB,OAAO,cAAc,KAAK,QAAQ,CAAC,KAAK,KAAK,IAAI;AAAA,IAClE,SAAS;AAAA,EACX;AACA,SAAO,EAAE,KAAK,MAAM,UAAU;AAChC;AAEA,eAAsB,KAAK,KAAc;AACvC,MAAI;AACF,UAAM,EAAE,KAAK,MAAM,UAAU,IAAI,MAAM,aAAa,GAAG;AACvD,UAAM,OAAO,MAAM,IAAI,KAAK,EAAE,MAAM,OAAO,CAAC,EAAE;AAC9C,UAAM,SAAS,kBAAkB,MAAM,KAAK,SAAS;AACrD,UAAM,QAAQ,oBAAoB,MAAM,MAAM;AAE9C,UAAM,aAAc,IAAI,UAAU,QAAQ,YAAY;AACtD,UAAM,EAAE,QAAQ,SAAS,IAAI,MAAM,WAAW;AAAA,MAC9C;AAAA,MACA,EAAE,OAAO,IAAI;AAAA,IACf;AACE,UAAM,WAAW,aAAa,KAAK,EAAE,IAAI,QAAQ,gBAAgB,KAAK,GAAG,EAAE,QAAQ,IAAI,CAAC;AACxF,QAAI,UAAU,aAAa,UAAU,MAAM,UAAU,WAAW;AAC9D,eAAS,QAAQ;AAAA,QACf;AAAA,QACA,2BAA2B;AAAA,UACzB,IAAI,SAAS;AAAA,UACb,WAAW,SAAS;AAAA,UACpB,WAAW,SAAS;AAAA,UACpB,aAAa,SAAS,eAAe;AAAA,UACrC,cAAc,SAAS,gBAAgB;AAAA,UACvC,YAAY,SAAS,cAAc,QAAQ,gBAAgB;AAAA,UAC3D,YAAY,SAAS,qBAAqB,OAAO,SAAS,UAAU,YAAY,IAAI;AAAA,QACtF,CAAC;AAAA,MACH;AAAA,IACF;AACA,WAAO;AAAA,EACT,SAAS,KAAK;AACZ,QAAI,gBAAgB,GAAG,GAAG;AACxB,aAAO,aAAa,KAAK,IAAI,MAAM,EAAE,QAAQ,IAAI,OAAO,CAAC;AAAA,IAC3D;AACA,UAAM,EAAE,UAAU,IAAI,MAAM,oBAAoB;AAChD,YAAQ,MAAM,gCAAgC,GAAG;AACjD,WAAO,aAAa,KAAK,EAAE,OAAO,UAAU,kCAAkC,sBAAsB,EAAE,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EAC1H;AACF;AAEA,MAAM,8BAA8B,EAAE,OAAO;AAAA,EAC3C,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS;AACjC,CAAC;AAED,MAAM,2BAA2B,EAAE,OAAO;AAAA,EACxC,OAAO,EAAE,OAAO;AAClB,CAAC;AAEM,MAAM,UAA2B;AAAA,EACtC,KAAK;AAAA,EACL,SAAS;AAAA,EACT,SAAS;AAAA,IACP,MAAM;AAAA,MACJ,SAAS;AAAA,MACT,aAAa;AAAA,MACb,aAAa;AAAA,QACX,aAAa;AAAA,QACb,QAAQ;AAAA,MACV;AAAA,MACA,WAAW;AAAA,QACT,EAAE,QAAQ,KAAK,aAAa,4BAA4B,QAAQ,4BAA4B;AAAA,MAC9F;AAAA,MACA,QAAQ;AAAA,QACN,EAAE,QAAQ,KAAK,aAAa,mCAAmC,QAAQ,yBAAyB;AAAA,QAChG,EAAE,QAAQ,KAAK,aAAa,gBAAgB,QAAQ,yBAAyB;AAAA,QAC7E,EAAE,QAAQ,KAAK,aAAa,2CAA2C,QAAQ,yBAAyB;AAAA,MAC1G;AAAA,IACF;AAAA,EACF;AACF;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -5,7 +5,7 @@ import { getAuthFromRequest } from "@open-mercato/shared/lib/auth/server";
|
|
|
5
5
|
import { resolveOrganizationScopeForRequest } from "@open-mercato/core/modules/directory/utils/organizationScope";
|
|
6
6
|
import { serializeOperationMetadata } from "@open-mercato/shared/lib/commands/operationMetadata";
|
|
7
7
|
import { tagAssignmentSchema } from "../../../data/validators.js";
|
|
8
|
-
import { CrudHttpError } from "@open-mercato/shared/lib/crud/errors";
|
|
8
|
+
import { CrudHttpError, isCrudHttpError } from "@open-mercato/shared/lib/crud/errors";
|
|
9
9
|
import { resolveTranslations } from "@open-mercato/shared/lib/i18n/server";
|
|
10
10
|
import { withScopedPayload } from "../../utils.js";
|
|
11
11
|
const metadata = {
|
|
@@ -55,7 +55,7 @@ async function POST(req) {
|
|
|
55
55
|
}
|
|
56
56
|
return response;
|
|
57
57
|
} catch (err) {
|
|
58
|
-
if (err
|
|
58
|
+
if (isCrudHttpError(err)) {
|
|
59
59
|
return NextResponse.json(err.body, { status: err.status });
|
|
60
60
|
}
|
|
61
61
|
const { translate } = await resolveTranslations();
|
|
@@ -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 } from '@open-mercato/shared/lib/crud/errors'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { withScopedPayload } from '../../utils'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\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 body = await req.json().catch(() => ({}))\n const scoped = withScopedPayload(body, ctx, translate)\n const input = tagAssignmentSchema.parse(scoped)\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 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 (err
|
|
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,
|
|
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 type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\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 body = await req.json().catch(() => ({}))\n const scoped = withScopedPayload(body, ctx, translate)\n const input = tagAssignmentSchema.parse(scoped)\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 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 console.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
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { NextResponse } from "next/server";
|
|
2
2
|
import { z } from "zod";
|
|
3
|
-
import { CrudHttpError } from "@open-mercato/shared/lib/crud/errors";
|
|
3
|
+
import { CrudHttpError, isCrudHttpError } from "@open-mercato/shared/lib/crud/errors";
|
|
4
4
|
import { parseBooleanToken } from "@open-mercato/shared/lib/boolean";
|
|
5
5
|
import { resolveTranslations } from "@open-mercato/shared/lib/i18n/server";
|
|
6
6
|
import {
|
|
@@ -233,7 +233,7 @@ async function GET(request) {
|
|
|
233
233
|
})
|
|
234
234
|
);
|
|
235
235
|
} catch (err) {
|
|
236
|
-
if (err
|
|
236
|
+
if (isCrudHttpError(err)) {
|
|
237
237
|
return withAdapterHeaders(NextResponse.json(err.body, { status: err.status }));
|
|
238
238
|
}
|
|
239
239
|
if (err instanceof z.ZodError) {
|
|
@@ -314,7 +314,7 @@ async function POST(request) {
|
|
|
314
314
|
)
|
|
315
315
|
);
|
|
316
316
|
} catch (err) {
|
|
317
|
-
if (err
|
|
317
|
+
if (isCrudHttpError(err)) {
|
|
318
318
|
return withAdapterHeaders(NextResponse.json(err.body, { status: err.status }));
|
|
319
319
|
}
|
|
320
320
|
if (err instanceof z.ZodError) {
|
|
@@ -395,7 +395,7 @@ async function PUT(request) {
|
|
|
395
395
|
}
|
|
396
396
|
return withAdapterHeaders(NextResponse.json({ ok: true }));
|
|
397
397
|
} catch (err) {
|
|
398
|
-
if (err
|
|
398
|
+
if (isCrudHttpError(err)) {
|
|
399
399
|
return withAdapterHeaders(NextResponse.json(err.body, { status: err.status }));
|
|
400
400
|
}
|
|
401
401
|
if (err instanceof z.ZodError) {
|
|
@@ -464,7 +464,7 @@ async function DELETE(request) {
|
|
|
464
464
|
}
|
|
465
465
|
return withAdapterHeaders(NextResponse.json({ ok: true }));
|
|
466
466
|
} catch (err) {
|
|
467
|
-
if (err
|
|
467
|
+
if (isCrudHttpError(err)) {
|
|
468
468
|
return withAdapterHeaders(NextResponse.json(err.body, { status: err.status }));
|
|
469
469
|
}
|
|
470
470
|
if (err instanceof z.ZodError) {
|