@open-mercato/core 0.6.3-develop.3651.1.d3a548a5f1 → 0.6.3-develop.3660.1.53b73d5aa4

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.
Files changed (160) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/dist/generated/entities/customer_settings/index.js +2 -0
  3. package/dist/generated/entities/customer_settings/index.js.map +2 -2
  4. package/dist/generated/entity-fields-registry.js +1 -0
  5. package/dist/generated/entity-fields-registry.js.map +2 -2
  6. package/dist/modules/customers/api/deals/aggregate/route.js +332 -0
  7. package/dist/modules/customers/api/deals/aggregate/route.js.map +7 -0
  8. package/dist/modules/customers/api/deals/bulk-update-owner/route.js +188 -0
  9. package/dist/modules/customers/api/deals/bulk-update-owner/route.js.map +7 -0
  10. package/dist/modules/customers/api/deals/bulk-update-stage/route.js +188 -0
  11. package/dist/modules/customers/api/deals/bulk-update-stage/route.js.map +7 -0
  12. package/dist/modules/customers/api/deals/route.js +255 -122
  13. package/dist/modules/customers/api/deals/route.js.map +2 -2
  14. package/dist/modules/customers/api/settings/stuck-threshold/route.js +169 -0
  15. package/dist/modules/customers/api/settings/stuck-threshold/route.js.map +7 -0
  16. package/dist/modules/customers/backend/config/customers/deals/page.js +181 -0
  17. package/dist/modules/customers/backend/config/customers/deals/page.js.map +7 -0
  18. package/dist/modules/customers/backend/config/customers/deals/page.meta.js +38 -0
  19. package/dist/modules/customers/backend/config/customers/deals/page.meta.js.map +7 -0
  20. package/dist/modules/customers/backend/customers/deals/create/page.js +17 -6
  21. package/dist/modules/customers/backend/customers/deals/create/page.js.map +2 -2
  22. package/dist/modules/customers/backend/customers/deals/page.js +2 -0
  23. package/dist/modules/customers/backend/customers/deals/page.js.map +2 -2
  24. package/dist/modules/customers/backend/customers/deals/pipeline/components/ActivityComposerDialog.js +217 -0
  25. package/dist/modules/customers/backend/customers/deals/pipeline/components/ActivityComposerDialog.js.map +7 -0
  26. package/dist/modules/customers/backend/customers/deals/pipeline/components/AddStageDialog.js +335 -0
  27. package/dist/modules/customers/backend/customers/deals/pipeline/components/AddStageDialog.js.map +7 -0
  28. package/dist/modules/customers/backend/customers/deals/pipeline/components/AddStageLane.js +29 -0
  29. package/dist/modules/customers/backend/customers/deals/pipeline/components/AddStageLane.js.map +7 -0
  30. package/dist/modules/customers/backend/customers/deals/pipeline/components/BulkActionsBar.js +160 -0
  31. package/dist/modules/customers/backend/customers/deals/pipeline/components/BulkActionsBar.js.map +7 -0
  32. package/dist/modules/customers/backend/customers/deals/pipeline/components/ChangeOwnerDialog.js +157 -0
  33. package/dist/modules/customers/backend/customers/deals/pipeline/components/ChangeOwnerDialog.js.map +7 -0
  34. package/dist/modules/customers/backend/customers/deals/pipeline/components/ChangeStageDialog.js +114 -0
  35. package/dist/modules/customers/backend/customers/deals/pipeline/components/ChangeStageDialog.js.map +7 -0
  36. package/dist/modules/customers/backend/customers/deals/pipeline/components/ChipButton.js +36 -0
  37. package/dist/modules/customers/backend/customers/deals/pipeline/components/ChipButton.js.map +7 -0
  38. package/dist/modules/customers/backend/customers/deals/pipeline/components/CloseDateFilterPopover.js +199 -0
  39. package/dist/modules/customers/backend/customers/deals/pipeline/components/CloseDateFilterPopover.js.map +7 -0
  40. package/dist/modules/customers/backend/customers/deals/pipeline/components/CurrencyBreakdownTable.js +71 -0
  41. package/dist/modules/customers/backend/customers/deals/pipeline/components/CurrencyBreakdownTable.js.map +7 -0
  42. package/dist/modules/customers/backend/customers/deals/pipeline/components/CurrencyFilterPopover.js +233 -0
  43. package/dist/modules/customers/backend/customers/deals/pipeline/components/CurrencyFilterPopover.js.map +7 -0
  44. package/dist/modules/customers/backend/customers/deals/pipeline/components/CustomizeViewDialog.js +126 -0
  45. package/dist/modules/customers/backend/customers/deals/pipeline/components/CustomizeViewDialog.js.map +7 -0
  46. package/dist/modules/customers/backend/customers/deals/pipeline/components/DashedTileButton.js +31 -0
  47. package/dist/modules/customers/backend/customers/deals/pipeline/components/DashedTileButton.js.map +7 -0
  48. package/dist/modules/customers/backend/customers/deals/pipeline/components/DealCard.js +389 -0
  49. package/dist/modules/customers/backend/customers/deals/pipeline/components/DealCard.js.map +7 -0
  50. package/dist/modules/customers/backend/customers/deals/pipeline/components/DealCardMenu.js +181 -0
  51. package/dist/modules/customers/backend/customers/deals/pipeline/components/DealCardMenu.js.map +7 -0
  52. package/dist/modules/customers/backend/customers/deals/pipeline/components/EntityFilterPopover.js +199 -0
  53. package/dist/modules/customers/backend/customers/deals/pipeline/components/EntityFilterPopover.js.map +7 -0
  54. package/dist/modules/customers/backend/customers/deals/pipeline/components/FilterBarRow.js +44 -0
  55. package/dist/modules/customers/backend/customers/deals/pipeline/components/FilterBarRow.js.map +7 -0
  56. package/dist/modules/customers/backend/customers/deals/pipeline/components/FilterPopoverShell.js +70 -0
  57. package/dist/modules/customers/backend/customers/deals/pipeline/components/FilterPopoverShell.js.map +7 -0
  58. package/dist/modules/customers/backend/customers/deals/pipeline/components/Lane.js +278 -0
  59. package/dist/modules/customers/backend/customers/deals/pipeline/components/Lane.js.map +7 -0
  60. package/dist/modules/customers/backend/customers/deals/pipeline/components/LaneCurrencyBreakdown.js +135 -0
  61. package/dist/modules/customers/backend/customers/deals/pipeline/components/LaneCurrencyBreakdown.js.map +7 -0
  62. package/dist/modules/customers/backend/customers/deals/pipeline/components/LaneResizeHandle.js +83 -0
  63. package/dist/modules/customers/backend/customers/deals/pipeline/components/LaneResizeHandle.js.map +7 -0
  64. package/dist/modules/customers/backend/customers/deals/pipeline/components/PipelineFilterPopover.js +155 -0
  65. package/dist/modules/customers/backend/customers/deals/pipeline/components/PipelineFilterPopover.js.map +7 -0
  66. package/dist/modules/customers/backend/customers/deals/pipeline/components/QuickDealDialog.js +318 -0
  67. package/dist/modules/customers/backend/customers/deals/pipeline/components/QuickDealDialog.js.map +7 -0
  68. package/dist/modules/customers/backend/customers/deals/pipeline/components/SortByPopover.js +138 -0
  69. package/dist/modules/customers/backend/customers/deals/pipeline/components/SortByPopover.js.map +7 -0
  70. package/dist/modules/customers/backend/customers/deals/pipeline/components/StatusFilterPopover.js +126 -0
  71. package/dist/modules/customers/backend/customers/deals/pipeline/components/StatusFilterPopover.js.map +7 -0
  72. package/dist/modules/customers/backend/customers/deals/pipeline/components/ViewTabsRow.js +71 -0
  73. package/dist/modules/customers/backend/customers/deals/pipeline/components/ViewTabsRow.js.map +7 -0
  74. package/dist/modules/customers/backend/customers/deals/pipeline/components/constants.js +7 -0
  75. package/dist/modules/customers/backend/customers/deals/pipeline/components/constants.js.map +7 -0
  76. package/dist/modules/customers/backend/customers/deals/pipeline/page.js +2330 -415
  77. package/dist/modules/customers/backend/customers/deals/pipeline/page.js.map +3 -3
  78. package/dist/modules/customers/commands/deals.js +6 -0
  79. package/dist/modules/customers/commands/deals.js.map +2 -2
  80. package/dist/modules/customers/commands/pipeline-stages.js +48 -13
  81. package/dist/modules/customers/commands/pipeline-stages.js.map +3 -3
  82. package/dist/modules/customers/commands/settings.js +45 -5
  83. package/dist/modules/customers/commands/settings.js.map +2 -2
  84. package/dist/modules/customers/data/enrichers.js +130 -1
  85. package/dist/modules/customers/data/enrichers.js.map +2 -2
  86. package/dist/modules/customers/data/entities.js +4 -0
  87. package/dist/modules/customers/data/entities.js.map +2 -2
  88. package/dist/modules/customers/data/validators.js +29 -2
  89. package/dist/modules/customers/data/validators.js.map +2 -2
  90. package/dist/modules/customers/lib/bulkDeals.js +150 -0
  91. package/dist/modules/customers/lib/bulkDeals.js.map +7 -0
  92. package/dist/modules/customers/lib/kysely.js +12 -0
  93. package/dist/modules/customers/lib/kysely.js.map +7 -0
  94. package/dist/modules/customers/lib/stuckDeals.js +32 -0
  95. package/dist/modules/customers/lib/stuckDeals.js.map +7 -0
  96. package/dist/modules/customers/migrations/Migration20260513203311_customers.js +13 -0
  97. package/dist/modules/customers/migrations/Migration20260513203311_customers.js.map +7 -0
  98. package/dist/modules/customers/migrations/Migration20260519120000_pipeline_stage_color_tones.js +52 -0
  99. package/dist/modules/customers/migrations/Migration20260519120000_pipeline_stage_color_tones.js.map +7 -0
  100. package/dist/modules/customers/workers/deals-bulk-update-owner.js +41 -0
  101. package/dist/modules/customers/workers/deals-bulk-update-owner.js.map +7 -0
  102. package/dist/modules/customers/workers/deals-bulk-update-stage.js +41 -0
  103. package/dist/modules/customers/workers/deals-bulk-update-stage.js.map +7 -0
  104. package/generated/entities/customer_settings/index.ts +1 -0
  105. package/generated/entity-fields-registry.ts +1 -0
  106. package/package.json +7 -7
  107. package/src/modules/customers/api/deals/aggregate/route.ts +438 -0
  108. package/src/modules/customers/api/deals/bulk-update-owner/route.ts +216 -0
  109. package/src/modules/customers/api/deals/bulk-update-stage/route.ts +228 -0
  110. package/src/modules/customers/api/deals/route.ts +330 -135
  111. package/src/modules/customers/api/settings/stuck-threshold/route.ts +195 -0
  112. package/src/modules/customers/backend/config/customers/deals/page.meta.ts +36 -0
  113. package/src/modules/customers/backend/config/customers/deals/page.tsx +205 -0
  114. package/src/modules/customers/backend/customers/deals/create/page.tsx +25 -6
  115. package/src/modules/customers/backend/customers/deals/page.tsx +2 -0
  116. package/src/modules/customers/backend/customers/deals/pipeline/components/ActivityComposerDialog.tsx +277 -0
  117. package/src/modules/customers/backend/customers/deals/pipeline/components/AddStageDialog.tsx +425 -0
  118. package/src/modules/customers/backend/customers/deals/pipeline/components/AddStageLane.tsx +31 -0
  119. package/src/modules/customers/backend/customers/deals/pipeline/components/BulkActionsBar.tsx +171 -0
  120. package/src/modules/customers/backend/customers/deals/pipeline/components/ChangeOwnerDialog.tsx +196 -0
  121. package/src/modules/customers/backend/customers/deals/pipeline/components/ChangeStageDialog.tsx +138 -0
  122. package/src/modules/customers/backend/customers/deals/pipeline/components/ChipButton.tsx +47 -0
  123. package/src/modules/customers/backend/customers/deals/pipeline/components/CloseDateFilterPopover.tsx +232 -0
  124. package/src/modules/customers/backend/customers/deals/pipeline/components/CurrencyBreakdownTable.tsx +110 -0
  125. package/src/modules/customers/backend/customers/deals/pipeline/components/CurrencyFilterPopover.tsx +310 -0
  126. package/src/modules/customers/backend/customers/deals/pipeline/components/CustomizeViewDialog.tsx +169 -0
  127. package/src/modules/customers/backend/customers/deals/pipeline/components/DashedTileButton.tsx +54 -0
  128. package/src/modules/customers/backend/customers/deals/pipeline/components/DealCard.tsx +562 -0
  129. package/src/modules/customers/backend/customers/deals/pipeline/components/DealCardMenu.tsx +214 -0
  130. package/src/modules/customers/backend/customers/deals/pipeline/components/EntityFilterPopover.tsx +257 -0
  131. package/src/modules/customers/backend/customers/deals/pipeline/components/FilterBarRow.tsx +62 -0
  132. package/src/modules/customers/backend/customers/deals/pipeline/components/FilterPopoverShell.tsx +101 -0
  133. package/src/modules/customers/backend/customers/deals/pipeline/components/Lane.tsx +378 -0
  134. package/src/modules/customers/backend/customers/deals/pipeline/components/LaneCurrencyBreakdown.tsx +192 -0
  135. package/src/modules/customers/backend/customers/deals/pipeline/components/LaneResizeHandle.tsx +107 -0
  136. package/src/modules/customers/backend/customers/deals/pipeline/components/PipelineFilterPopover.tsx +196 -0
  137. package/src/modules/customers/backend/customers/deals/pipeline/components/QuickDealDialog.tsx +408 -0
  138. package/src/modules/customers/backend/customers/deals/pipeline/components/SortByPopover.tsx +164 -0
  139. package/src/modules/customers/backend/customers/deals/pipeline/components/StatusFilterPopover.tsx +171 -0
  140. package/src/modules/customers/backend/customers/deals/pipeline/components/ViewTabsRow.tsx +84 -0
  141. package/src/modules/customers/backend/customers/deals/pipeline/components/constants.ts +16 -0
  142. package/src/modules/customers/backend/customers/deals/pipeline/page.tsx +2841 -550
  143. package/src/modules/customers/commands/deals.ts +19 -0
  144. package/src/modules/customers/commands/pipeline-stages.ts +68 -13
  145. package/src/modules/customers/commands/settings.ts +64 -5
  146. package/src/modules/customers/data/enrichers.ts +209 -2
  147. package/src/modules/customers/data/entities.ts +4 -1
  148. package/src/modules/customers/data/validators.ts +36 -2
  149. package/src/modules/customers/i18n/de.json +244 -0
  150. package/src/modules/customers/i18n/en.json +244 -0
  151. package/src/modules/customers/i18n/es.json +244 -0
  152. package/src/modules/customers/i18n/pl.json +244 -0
  153. package/src/modules/customers/lib/bulkDeals.ts +231 -0
  154. package/src/modules/customers/lib/kysely.ts +60 -0
  155. package/src/modules/customers/lib/stuckDeals.ts +88 -0
  156. package/src/modules/customers/migrations/.snapshot-open-mercato.json +16 -0
  157. package/src/modules/customers/migrations/Migration20260513203311_customers.ts +13 -0
  158. package/src/modules/customers/migrations/Migration20260519120000_pipeline_stage_color_tones.ts +71 -0
  159. package/src/modules/customers/workers/deals-bulk-update-owner.ts +46 -0
  160. package/src/modules/customers/workers/deals-bulk-update-stage.ts +46 -0
@@ -0,0 +1,188 @@
1
+ import { NextResponse } from "next/server";
2
+ import { getAuthFromRequest } from "@open-mercato/shared/lib/auth/server";
3
+ import { createRequestContainer } from "@open-mercato/shared/lib/di/container";
4
+ import { readJsonSafe } from "@open-mercato/shared/lib/http/readJsonSafe";
5
+ import { resolveTranslations } from "@open-mercato/shared/lib/i18n/server";
6
+ import {
7
+ runCrudMutationGuardAfterSuccess,
8
+ validateCrudMutationGuard
9
+ } from "@open-mercato/shared/lib/crud/mutation-guard";
10
+ import {
11
+ CUSTOMERS_DEALS_BULK_UPDATE_STAGE_QUEUE,
12
+ getCustomersQueue
13
+ } from "../../../lib/bulkDeals.js";
14
+ import {
15
+ dealsBulkUpdateStageSchema as requestSchema,
16
+ dealsBulkUpdateResponseSchema as responseSchema
17
+ } from "../../../data/validators.js";
18
+ const metadata = {
19
+ POST: { requireAuth: true, requireFeatures: ["customers.deals.manage"] }
20
+ };
21
+ const openApi = {
22
+ tags: ["Customers"],
23
+ summary: "Bulk update deal pipeline stage",
24
+ description: "Queues a background job that moves the listed deals to the same pipeline stage. Returns a progress job id to poll for completion."
25
+ };
26
+ async function postImpl(req, translate) {
27
+ const auth = await getAuthFromRequest(req);
28
+ if (!auth?.tenantId || !auth.orgId) {
29
+ return NextResponse.json(
30
+ responseSchema.parse({
31
+ ok: false,
32
+ progressJobId: null,
33
+ message: translate("customers.errors.unauthorized", "Unauthorized")
34
+ }),
35
+ { status: 401 }
36
+ );
37
+ }
38
+ const parsed = requestSchema.safeParse(await readJsonSafe(req));
39
+ if (!parsed.success) {
40
+ return NextResponse.json(
41
+ responseSchema.parse({
42
+ ok: false,
43
+ progressJobId: null,
44
+ message: translate("customers.errors.invalid_payload", "Invalid payload")
45
+ }),
46
+ { status: 400 }
47
+ );
48
+ }
49
+ const ids = Array.from(new Set(parsed.data.ids));
50
+ const container = await createRequestContainer();
51
+ let guardResult = null;
52
+ try {
53
+ guardResult = await validateCrudMutationGuard(container, {
54
+ tenantId: auth.tenantId,
55
+ organizationId: auth.orgId,
56
+ userId: auth.sub,
57
+ resourceKind: "customers.deal",
58
+ resourceId: ids.join(","),
59
+ operation: "custom",
60
+ requestMethod: req.method,
61
+ requestHeaders: req.headers,
62
+ mutationPayload: { ids, pipelineStageId: parsed.data.pipelineStageId }
63
+ });
64
+ } catch (guardError) {
65
+ console.warn("[customers.deals.bulk-update-stage] mutation-guard skipped", guardError);
66
+ guardResult = null;
67
+ }
68
+ if (guardResult && !guardResult.ok) {
69
+ return NextResponse.json(guardResult.body, { status: guardResult.status });
70
+ }
71
+ let progressService;
72
+ try {
73
+ progressService = container.resolve("progressService");
74
+ } catch (resolveError) {
75
+ console.error("[customers.deals.bulk-update-stage] progressService resolve failed", resolveError);
76
+ throw new Error(
77
+ `progressService resolve failed: ${resolveError instanceof Error ? resolveError.message : String(resolveError)}`
78
+ );
79
+ }
80
+ let progressJob;
81
+ try {
82
+ progressJob = await progressService.createJob(
83
+ {
84
+ jobType: "customers.deals.bulk_update_stage",
85
+ name: translate(
86
+ "customers.deals.kanban.bulk.changeStage.progress.name",
87
+ "Move selected deals to a new stage"
88
+ ),
89
+ description: translate(
90
+ "customers.deals.kanban.bulk.changeStage.progress.description",
91
+ "{count} deals queued for stage update",
92
+ { count: ids.length }
93
+ ),
94
+ totalCount: ids.length,
95
+ cancellable: false,
96
+ meta: {
97
+ source: "customers.deals.bulk-update-stage",
98
+ pipelineStageId: parsed.data.pipelineStageId
99
+ }
100
+ },
101
+ {
102
+ tenantId: auth.tenantId,
103
+ organizationId: auth.orgId,
104
+ userId: auth.sub
105
+ }
106
+ );
107
+ } catch (createError) {
108
+ console.error("[customers.deals.bulk-update-stage] progressService.createJob failed", createError);
109
+ throw new Error(
110
+ `progressService.createJob failed: ${createError instanceof Error ? createError.message : String(createError)}`
111
+ );
112
+ }
113
+ const queue = getCustomersQueue(CUSTOMERS_DEALS_BULK_UPDATE_STAGE_QUEUE);
114
+ try {
115
+ await queue.enqueue({
116
+ progressJobId: progressJob.id,
117
+ ids,
118
+ pipelineStageId: parsed.data.pipelineStageId,
119
+ scope: {
120
+ organizationId: auth.orgId,
121
+ tenantId: auth.tenantId,
122
+ userId: auth.sub
123
+ }
124
+ });
125
+ } catch (error) {
126
+ await progressService.failJob(
127
+ progressJob.id,
128
+ {
129
+ errorMessage: error instanceof Error ? error.message : translate("customers.errors.bulk_enqueue_failed", "Failed to enqueue bulk job")
130
+ },
131
+ {
132
+ tenantId: auth.tenantId,
133
+ organizationId: auth.orgId,
134
+ userId: auth.sub
135
+ }
136
+ ).catch((failErr) => {
137
+ console.warn("[customers.deals.bulk-update-stage] failed to mark progress job as failed", failErr);
138
+ });
139
+ throw error;
140
+ }
141
+ if (guardResult?.ok && guardResult.shouldRunAfterSuccess) {
142
+ await runCrudMutationGuardAfterSuccess(container, {
143
+ tenantId: auth.tenantId,
144
+ organizationId: auth.orgId,
145
+ userId: auth.sub,
146
+ resourceKind: "customers.deal",
147
+ resourceId: ids.join(","),
148
+ operation: "custom",
149
+ requestMethod: req.method,
150
+ requestHeaders: req.headers,
151
+ metadata: guardResult.metadata ?? null
152
+ });
153
+ }
154
+ return NextResponse.json(
155
+ responseSchema.parse({
156
+ ok: true,
157
+ progressJobId: progressJob.id,
158
+ message: translate(
159
+ "customers.deals.kanban.bulk.changeStage.queued",
160
+ "Bulk stage update started ({count} deals).",
161
+ { count: ids.length }
162
+ )
163
+ }),
164
+ { status: 202 }
165
+ );
166
+ }
167
+ async function POST(req) {
168
+ const { translate } = await resolveTranslations();
169
+ try {
170
+ return await postImpl(req, translate);
171
+ } catch (error) {
172
+ console.error("[customers.deals.bulk-update-stage] route failed", error);
173
+ return NextResponse.json(
174
+ responseSchema.parse({
175
+ ok: false,
176
+ progressJobId: null,
177
+ message: error instanceof Error ? error.message : translate("customers.errors.bulk_enqueue_failed", "Failed to enqueue bulk job")
178
+ }),
179
+ { status: 500 }
180
+ );
181
+ }
182
+ }
183
+ export {
184
+ POST,
185
+ metadata,
186
+ openApi
187
+ };
188
+ //# sourceMappingURL=route.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../../src/modules/customers/api/deals/bulk-update-stage/route.ts"],
4
+ "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { readJsonSafe } from '@open-mercato/shared/lib/http/readJsonSafe'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport type { TranslateWithFallbackFn } from '@open-mercato/shared/lib/i18n/translate'\nimport {\n runCrudMutationGuardAfterSuccess,\n validateCrudMutationGuard,\n} from '@open-mercato/shared/lib/crud/mutation-guard'\nimport type { ProgressService } from '../../../../progress/lib/progressService'\nimport {\n CUSTOMERS_DEALS_BULK_UPDATE_STAGE_QUEUE,\n getCustomersQueue,\n} from '../../../lib/bulkDeals'\nimport {\n dealsBulkUpdateStageSchema as requestSchema,\n dealsBulkUpdateResponseSchema as responseSchema,\n} from '../../../data/validators'\n\nexport const metadata = {\n POST: { requireAuth: true, requireFeatures: ['customers.deals.manage'] },\n}\n\nexport const openApi = {\n tags: ['Customers'],\n summary: 'Bulk update deal pipeline stage',\n description:\n 'Queues a background job that moves the listed deals to the same pipeline stage. Returns a progress job id to poll for completion.',\n}\n\nasync function postImpl(req: Request, translate: TranslateWithFallbackFn): Promise<NextResponse> {\n const auth = await getAuthFromRequest(req)\n if (!auth?.tenantId || !auth.orgId) {\n return NextResponse.json(\n responseSchema.parse({\n ok: false,\n progressJobId: null,\n message: translate('customers.errors.unauthorized', 'Unauthorized'),\n }),\n { status: 401 },\n )\n }\n\n const parsed = requestSchema.safeParse(await readJsonSafe(req))\n if (!parsed.success) {\n return NextResponse.json(\n responseSchema.parse({\n ok: false,\n progressJobId: null,\n message: translate('customers.errors.invalid_payload', 'Invalid payload'),\n }),\n { status: 400 },\n )\n }\n\n const ids = Array.from(new Set(parsed.data.ids))\n const container = await createRequestContainer()\n\n // Mutation-guard contract for custom write routes \u2014 lets injection modules (record-lock\n // conflict handling, scoped headers, undo-history reconciliation) run `onBeforeSave` /\n // `onAfterSave` for non-`makeCrudRoute` writes. Per-record locks are enforced by the\n // worker (the worker calls `customers.deals.update` per id, which itself goes through\n // the single-record route + guard). The guard at the bulk-entry point is best-effort:\n // it gives lock-aware modules a chance to register a bulk activity log, but failures\n // (e.g. a guard that expects a single-record id, or transient lock-table errors) must\n // NOT take the whole bulk enqueue down. Swallow the error so the bulk job still gets\n // queued and per-record enforcement happens downstream.\n let guardResult: Awaited<ReturnType<typeof validateCrudMutationGuard>> = null\n try {\n guardResult = await validateCrudMutationGuard(container, {\n tenantId: auth.tenantId,\n organizationId: auth.orgId,\n userId: auth.sub,\n resourceKind: 'customers.deal',\n resourceId: ids.join(','),\n operation: 'custom',\n requestMethod: req.method,\n requestHeaders: req.headers,\n mutationPayload: { ids, pipelineStageId: parsed.data.pipelineStageId },\n })\n } catch (guardError) {\n console.warn('[customers.deals.bulk-update-stage] mutation-guard skipped', guardError)\n guardResult = null\n }\n if (guardResult && !guardResult.ok) {\n return NextResponse.json(guardResult.body, { status: guardResult.status })\n }\n\n let progressService: ProgressService\n try {\n progressService = container.resolve('progressService') as ProgressService\n } catch (resolveError) {\n console.error('[customers.deals.bulk-update-stage] progressService resolve failed', resolveError)\n throw new Error(\n `progressService resolve failed: ${resolveError instanceof Error ? resolveError.message : String(resolveError)}`,\n )\n }\n\n let progressJob: Awaited<ReturnType<ProgressService['createJob']>>\n try {\n progressJob = await progressService.createJob(\n {\n jobType: 'customers.deals.bulk_update_stage',\n name: translate(\n 'customers.deals.kanban.bulk.changeStage.progress.name',\n 'Move selected deals to a new stage',\n ),\n description: translate(\n 'customers.deals.kanban.bulk.changeStage.progress.description',\n '{count} deals queued for stage update',\n { count: ids.length },\n ),\n totalCount: ids.length,\n cancellable: false,\n meta: {\n source: 'customers.deals.bulk-update-stage',\n pipelineStageId: parsed.data.pipelineStageId,\n },\n },\n {\n tenantId: auth.tenantId,\n organizationId: auth.orgId,\n userId: auth.sub,\n },\n )\n } catch (createError) {\n console.error('[customers.deals.bulk-update-stage] progressService.createJob failed', createError)\n throw new Error(\n `progressService.createJob failed: ${createError instanceof Error ? createError.message : String(createError)}`,\n )\n }\n\n const queue = getCustomersQueue(CUSTOMERS_DEALS_BULK_UPDATE_STAGE_QUEUE)\n try {\n await queue.enqueue({\n progressJobId: progressJob.id,\n ids,\n pipelineStageId: parsed.data.pipelineStageId,\n scope: {\n organizationId: auth.orgId,\n tenantId: auth.tenantId,\n userId: auth.sub,\n },\n })\n } catch (error) {\n // Without this guard the progress row sits in `pending` forever \u2014 the worker\n // never picks the job up, and the top-bar polls a job that will never finish.\n // Mark it failed so the operator gets a deterministic UX (a failure toast +\n // ability to retry) instead of an invisible stuck job.\n await progressService\n .failJob(\n progressJob.id,\n {\n errorMessage:\n error instanceof Error\n ? error.message\n : translate('customers.errors.bulk_enqueue_failed', 'Failed to enqueue bulk job'),\n },\n {\n tenantId: auth.tenantId,\n organizationId: auth.orgId,\n userId: auth.sub,\n },\n )\n .catch((failErr) => {\n console.warn('[customers.deals.bulk-update-stage] failed to mark progress job as failed', failErr)\n })\n throw error\n }\n\n // After-success half of the mutation-guard contract \u2014 fires `onAfterSave` injection\n // hooks. We invoke it here because enqueue succeeded; the worker takes over from here.\n // If an injection-side after-handler needs the bulk job's actual completion (rather than\n // enqueue), it should subscribe to the progress event stream \u2014 beyond the scope of the\n // synchronous guard contract.\n if (guardResult?.ok && guardResult.shouldRunAfterSuccess) {\n await runCrudMutationGuardAfterSuccess(container, {\n tenantId: auth.tenantId,\n organizationId: auth.orgId,\n userId: auth.sub,\n resourceKind: 'customers.deal',\n resourceId: ids.join(','),\n operation: 'custom',\n requestMethod: req.method,\n requestHeaders: req.headers,\n metadata: guardResult.metadata ?? null,\n })\n }\n\n return NextResponse.json(\n responseSchema.parse({\n ok: true,\n progressJobId: progressJob.id,\n message: translate(\n 'customers.deals.kanban.bulk.changeStage.queued',\n 'Bulk stage update started ({count} deals).',\n { count: ids.length },\n ),\n }),\n { status: 202 },\n )\n}\n\n// Outer wrapper: every uncaught error from `postImpl` becomes a controlled 500 with\n// diagnostic JSON instead of Next.js's default HTML error page. Routes that throw\n// produced opaque 500s in CI integration tests (no body, no message), making the\n// root cause invisible. With this wrapper the operator sees the actual error string\n// in the response body and the server log, and the test framework can surface it.\nexport async function POST(req: Request) {\n const { translate } = await resolveTranslations()\n try {\n return await postImpl(req, translate)\n } catch (error) {\n console.error('[customers.deals.bulk-update-stage] route failed', error)\n return NextResponse.json(\n responseSchema.parse({\n ok: false,\n progressJobId: null,\n message:\n error instanceof Error\n ? error.message\n : translate('customers.errors.bulk_enqueue_failed', 'Failed to enqueue bulk job'),\n }),\n { status: 500 },\n )\n }\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }