@open-mercato/core 0.4.5-develop-0f0e676c72 → 0.4.5-develop-e694581d9f

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 (113) hide show
  1. package/dist/generated/entities/customer_deal/index.js +4 -0
  2. package/dist/generated/entities/customer_deal/index.js.map +2 -2
  3. package/dist/generated/entities/customer_pipeline/index.js +17 -0
  4. package/dist/generated/entities/customer_pipeline/index.js.map +7 -0
  5. package/dist/generated/entities/customer_pipeline_stage/index.js +19 -0
  6. package/dist/generated/entities/customer_pipeline_stage/index.js.map +7 -0
  7. package/dist/generated/entities.ids.generated.js +2 -0
  8. package/dist/generated/entities.ids.generated.js.map +2 -2
  9. package/dist/generated/entity-fields-registry.js +4 -0
  10. package/dist/generated/entity-fields-registry.js.map +2 -2
  11. package/dist/modules/customers/acl.js +2 -0
  12. package/dist/modules/customers/acl.js.map +2 -2
  13. package/dist/modules/customers/api/deals/[id]/route.js +4 -0
  14. package/dist/modules/customers/api/deals/[id]/route.js.map +2 -2
  15. package/dist/modules/customers/api/deals/route.js +12 -0
  16. package/dist/modules/customers/api/deals/route.js.map +2 -2
  17. package/dist/modules/customers/api/dictionaries/[kind]/route.js +20 -1
  18. package/dist/modules/customers/api/dictionaries/[kind]/route.js.map +2 -2
  19. package/dist/modules/customers/api/pipeline-stages/reorder/route.js +69 -0
  20. package/dist/modules/customers/api/pipeline-stages/reorder/route.js.map +7 -0
  21. package/dist/modules/customers/api/pipeline-stages/route.js +275 -0
  22. package/dist/modules/customers/api/pipeline-stages/route.js.map +7 -0
  23. package/dist/modules/customers/api/pipelines/route.js +245 -0
  24. package/dist/modules/customers/api/pipelines/route.js.map +7 -0
  25. package/dist/modules/customers/backend/config/customers/page.js +2 -0
  26. package/dist/modules/customers/backend/config/customers/page.js.map +2 -2
  27. package/dist/modules/customers/backend/config/customers/pipeline-stages/page.js +439 -0
  28. package/dist/modules/customers/backend/config/customers/pipeline-stages/page.js.map +7 -0
  29. package/dist/modules/customers/backend/config/customers/pipeline-stages/page.meta.js +17 -0
  30. package/dist/modules/customers/backend/config/customers/pipeline-stages/page.meta.js.map +7 -0
  31. package/dist/modules/customers/backend/customers/deals/[id]/page.js +19 -1
  32. package/dist/modules/customers/backend/customers/deals/[id]/page.js.map +2 -2
  33. package/dist/modules/customers/backend/customers/deals/page.js +35 -1
  34. package/dist/modules/customers/backend/customers/deals/page.js.map +2 -2
  35. package/dist/modules/customers/backend/customers/deals/pipeline/page.js +102 -74
  36. package/dist/modules/customers/backend/customers/deals/pipeline/page.js.map +2 -2
  37. package/dist/modules/customers/cli.js +28 -2
  38. package/dist/modules/customers/cli.js.map +2 -2
  39. package/dist/modules/customers/commands/deals.js +34 -2
  40. package/dist/modules/customers/commands/deals.js.map +2 -2
  41. package/dist/modules/customers/commands/index.js +2 -0
  42. package/dist/modules/customers/commands/index.js.map +2 -2
  43. package/dist/modules/customers/commands/pipeline-stages.js +126 -0
  44. package/dist/modules/customers/commands/pipeline-stages.js.map +7 -0
  45. package/dist/modules/customers/commands/pipelines.js +87 -0
  46. package/dist/modules/customers/commands/pipelines.js.map +7 -0
  47. package/dist/modules/customers/components/DictionarySettings.js +0 -5
  48. package/dist/modules/customers/components/DictionarySettings.js.map +2 -2
  49. package/dist/modules/customers/components/PipelineSettings.js +474 -0
  50. package/dist/modules/customers/components/PipelineSettings.js.map +7 -0
  51. package/dist/modules/customers/components/detail/DealForm.js +84 -12
  52. package/dist/modules/customers/components/detail/DealForm.js.map +2 -2
  53. package/dist/modules/customers/data/entities.js +78 -0
  54. package/dist/modules/customers/data/entities.js.map +2 -2
  55. package/dist/modules/customers/data/validators.js +44 -0
  56. package/dist/modules/customers/data/validators.js.map +2 -2
  57. package/dist/modules/customers/migrations/Migration20260218191730.js +77 -0
  58. package/dist/modules/customers/migrations/Migration20260218191730.js.map +7 -0
  59. package/dist/modules/customers/setup.js +7 -3
  60. package/dist/modules/customers/setup.js.map +2 -2
  61. package/dist/modules/translations/api/[entityType]/[entityId]/route.js +46 -44
  62. package/dist/modules/translations/api/[entityType]/[entityId]/route.js.map +2 -2
  63. package/dist/modules/translations/api/context.js +10 -1
  64. package/dist/modules/translations/api/context.js.map +2 -2
  65. package/dist/modules/translations/commands/index.js +2 -0
  66. package/dist/modules/translations/commands/index.js.map +7 -0
  67. package/dist/modules/translations/commands/translations.js +160 -0
  68. package/dist/modules/translations/commands/translations.js.map +7 -0
  69. package/dist/modules/translations/index.js +1 -0
  70. package/dist/modules/translations/index.js.map +2 -2
  71. package/dist/modules/workflows/migrations/Migration20260222205305.js +14 -0
  72. package/dist/modules/workflows/migrations/Migration20260222205305.js.map +7 -0
  73. package/generated/entities/customer_deal/index.ts +2 -0
  74. package/generated/entities/customer_pipeline/index.ts +7 -0
  75. package/generated/entities/customer_pipeline_stage/index.ts +8 -0
  76. package/generated/entities.ids.generated.ts +2 -0
  77. package/generated/entity-fields-registry.ts +4 -0
  78. package/package.json +2 -2
  79. package/src/modules/customers/acl.ts +2 -0
  80. package/src/modules/customers/api/deals/[id]/route.ts +4 -0
  81. package/src/modules/customers/api/deals/route.ts +12 -0
  82. package/src/modules/customers/api/dictionaries/[kind]/route.ts +21 -1
  83. package/src/modules/customers/api/pipeline-stages/reorder/route.ts +71 -0
  84. package/src/modules/customers/api/pipeline-stages/route.ts +296 -0
  85. package/src/modules/customers/api/pipelines/route.ts +261 -0
  86. package/src/modules/customers/backend/config/customers/page.tsx +2 -0
  87. package/src/modules/customers/backend/config/customers/pipeline-stages/page.meta.ts +13 -0
  88. package/src/modules/customers/backend/config/customers/pipeline-stages/page.tsx +512 -0
  89. package/src/modules/customers/backend/customers/deals/[id]/page.tsx +21 -1
  90. package/src/modules/customers/backend/customers/deals/page.tsx +33 -1
  91. package/src/modules/customers/backend/customers/deals/pipeline/page.tsx +119 -79
  92. package/src/modules/customers/cli.ts +29 -1
  93. package/src/modules/customers/commands/deals.ts +44 -1
  94. package/src/modules/customers/commands/index.ts +2 -0
  95. package/src/modules/customers/commands/pipeline-stages.ts +156 -0
  96. package/src/modules/customers/commands/pipelines.ts +105 -0
  97. package/src/modules/customers/components/DictionarySettings.tsx +0 -5
  98. package/src/modules/customers/components/PipelineSettings.tsx +570 -0
  99. package/src/modules/customers/components/detail/DealForm.tsx +89 -11
  100. package/src/modules/customers/data/entities.ts +64 -0
  101. package/src/modules/customers/data/validators.ts +57 -0
  102. package/src/modules/customers/i18n/de.json +4 -0
  103. package/src/modules/customers/i18n/en.json +4 -0
  104. package/src/modules/customers/i18n/es.json +4 -0
  105. package/src/modules/customers/i18n/pl.json +5 -1
  106. package/src/modules/customers/migrations/Migration20260218191730.ts +84 -0
  107. package/src/modules/customers/setup.ts +5 -1
  108. package/src/modules/translations/api/[entityType]/[entityId]/route.ts +65 -60
  109. package/src/modules/translations/api/context.ts +12 -0
  110. package/src/modules/translations/commands/index.ts +1 -0
  111. package/src/modules/translations/commands/translations.ts +253 -0
  112. package/src/modules/translations/index.ts +1 -0
  113. package/src/modules/workflows/migrations/Migration20260222205305.ts +13 -0
@@ -1,9 +1,10 @@
1
- import { seedCustomerDictionaries, seedCurrencyDictionary, seedCustomerExamples } from "./cli.js";
1
+ import { seedCustomerDictionaries, seedCurrencyDictionary, seedCustomerExamples, seedDefaultPipeline } from "./cli.js";
2
2
  const setup = {
3
3
  seedDefaults: async (ctx) => {
4
4
  const scope = { tenantId: ctx.tenantId, organizationId: ctx.organizationId };
5
5
  await seedCustomerDictionaries(ctx.em, scope);
6
6
  await seedCurrencyDictionary(ctx.em, scope);
7
+ await seedDefaultPipeline(ctx.em, scope);
7
8
  },
8
9
  seedExamples: async (ctx) => {
9
10
  const scope = { tenantId: ctx.tenantId, organizationId: ctx.organizationId };
@@ -17,14 +18,17 @@ const setup = {
17
18
  "customers.companies.view",
18
19
  "customers.companies.manage",
19
20
  "customers.deals.view",
20
- "customers.deals.manage"
21
+ "customers.deals.manage",
22
+ "customers.pipelines.view",
23
+ "customers.pipelines.manage"
21
24
  ],
22
25
  employee: [
23
26
  "customers.*",
24
27
  "customers.people.view",
25
28
  "customers.people.manage",
26
29
  "customers.companies.view",
27
- "customers.companies.manage"
30
+ "customers.companies.manage",
31
+ "customers.pipelines.view"
28
32
  ]
29
33
  }
30
34
  };
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/modules/customers/setup.ts"],
4
- "sourcesContent": ["import type { ModuleSetupConfig } from '@open-mercato/shared/modules/setup'\nimport { seedCustomerDictionaries, seedCurrencyDictionary, seedCustomerExamples } from './cli'\n\nexport const setup: ModuleSetupConfig = {\n seedDefaults: async (ctx) => {\n const scope = { tenantId: ctx.tenantId, organizationId: ctx.organizationId }\n await seedCustomerDictionaries(ctx.em, scope)\n await seedCurrencyDictionary(ctx.em, scope)\n },\n\n seedExamples: async (ctx) => {\n const scope = { tenantId: ctx.tenantId, organizationId: ctx.organizationId }\n await seedCustomerExamples(ctx.em, ctx.container, scope)\n },\n\n defaultRoleFeatures: {\n admin: [\n 'customers.*',\n 'customers.people.view',\n 'customers.people.manage',\n 'customers.companies.view',\n 'customers.companies.manage',\n 'customers.deals.view',\n 'customers.deals.manage',\n ],\n employee: [\n 'customers.*',\n 'customers.people.view',\n 'customers.people.manage',\n 'customers.companies.view',\n 'customers.companies.manage',\n ],\n },\n}\n\nexport default setup\n"],
5
- "mappings": "AACA,SAAS,0BAA0B,wBAAwB,4BAA4B;AAEhF,MAAM,QAA2B;AAAA,EACtC,cAAc,OAAO,QAAQ;AAC3B,UAAM,QAAQ,EAAE,UAAU,IAAI,UAAU,gBAAgB,IAAI,eAAe;AAC3E,UAAM,yBAAyB,IAAI,IAAI,KAAK;AAC5C,UAAM,uBAAuB,IAAI,IAAI,KAAK;AAAA,EAC5C;AAAA,EAEA,cAAc,OAAO,QAAQ;AAC3B,UAAM,QAAQ,EAAE,UAAU,IAAI,UAAU,gBAAgB,IAAI,eAAe;AAC3E,UAAM,qBAAqB,IAAI,IAAI,IAAI,WAAW,KAAK;AAAA,EACzD;AAAA,EAEA,qBAAqB;AAAA,IACnB,OAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,IACA,UAAU;AAAA,MACR;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AACF;AAEA,IAAO,gBAAQ;",
4
+ "sourcesContent": ["import type { ModuleSetupConfig } from '@open-mercato/shared/modules/setup'\nimport { seedCustomerDictionaries, seedCurrencyDictionary, seedCustomerExamples, seedDefaultPipeline } from './cli'\n\nexport const setup: ModuleSetupConfig = {\n seedDefaults: async (ctx) => {\n const scope = { tenantId: ctx.tenantId, organizationId: ctx.organizationId }\n await seedCustomerDictionaries(ctx.em, scope)\n await seedCurrencyDictionary(ctx.em, scope)\n await seedDefaultPipeline(ctx.em, scope)\n },\n\n seedExamples: async (ctx) => {\n const scope = { tenantId: ctx.tenantId, organizationId: ctx.organizationId }\n await seedCustomerExamples(ctx.em, ctx.container, scope)\n },\n\n defaultRoleFeatures: {\n admin: [\n 'customers.*',\n 'customers.people.view',\n 'customers.people.manage',\n 'customers.companies.view',\n 'customers.companies.manage',\n 'customers.deals.view',\n 'customers.deals.manage',\n 'customers.pipelines.view',\n 'customers.pipelines.manage',\n ],\n employee: [\n 'customers.*',\n 'customers.people.view',\n 'customers.people.manage',\n 'customers.companies.view',\n 'customers.companies.manage',\n 'customers.pipelines.view',\n ],\n },\n}\n\nexport default setup\n"],
5
+ "mappings": "AACA,SAAS,0BAA0B,wBAAwB,sBAAsB,2BAA2B;AAErG,MAAM,QAA2B;AAAA,EACtC,cAAc,OAAO,QAAQ;AAC3B,UAAM,QAAQ,EAAE,UAAU,IAAI,UAAU,gBAAgB,IAAI,eAAe;AAC3E,UAAM,yBAAyB,IAAI,IAAI,KAAK;AAC5C,UAAM,uBAAuB,IAAI,IAAI,KAAK;AAC1C,UAAM,oBAAoB,IAAI,IAAI,KAAK;AAAA,EACzC;AAAA,EAEA,cAAc,OAAO,QAAQ;AAC3B,UAAM,QAAQ,EAAE,UAAU,IAAI,UAAU,gBAAgB,IAAI,eAAe;AAC3E,UAAM,qBAAqB,IAAI,IAAI,IAAI,WAAW,KAAK;AAAA,EACzD;AAAA,EAEA,qBAAqB;AAAA,IACnB,OAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,IACA,UAAU;AAAA,MACR;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AACF;AAEA,IAAO,gBAAQ;",
6
6
  "names": []
7
7
  }
@@ -3,6 +3,7 @@ import { z } from "zod";
3
3
  import { resolveTranslationsRouteContext } from "@open-mercato/core/modules/translations/api/context";
4
4
  import { translationBodySchema, entityTypeParamSchema, entityIdParamSchema } from "@open-mercato/core/modules/translations/data/validators";
5
5
  import { CrudHttpError } from "@open-mercato/shared/lib/crud/errors";
6
+ import { serializeOperationMetadata } from "@open-mercato/shared/lib/commands/operationMetadata";
6
7
  const paramsSchema = z.object({
7
8
  entityType: entityTypeParamSchema,
8
9
  entityId: entityIdParamSchema
@@ -53,49 +54,40 @@ async function PUT(req, ctx) {
53
54
  });
54
55
  const rawBody = await req.json().catch(() => ({}));
55
56
  const translations = translationBodySchema.parse(rawBody);
56
- const existing = await context.knex("entity_translations").where({
57
- entity_type: entityType,
58
- entity_id: entityId
59
- }).andWhereRaw("tenant_id is not distinct from ?", [context.tenantId]).andWhereRaw("organization_id is not distinct from ?", [context.organizationId]).first();
60
- const now = context.knex.fn.now();
61
- if (existing) {
62
- await context.knex("entity_translations").where({ id: existing.id }).update({
63
- translations,
64
- updated_at: now
65
- });
66
- } else {
67
- await context.knex("entity_translations").insert({
68
- entity_type: entityType,
69
- entity_id: entityId,
70
- organization_id: context.organizationId,
71
- tenant_id: context.tenantId,
72
- translations,
73
- created_at: now,
74
- updated_at: now
75
- });
76
- }
77
- try {
78
- const bus = context.container.resolve("eventBus");
79
- await bus.emitEvent("translations.translation.updated", {
57
+ const commandBus = context.container.resolve("commandBus");
58
+ const { result, logEntry } = await commandBus.execute("translations.translation.save", {
59
+ input: {
80
60
  entityType,
81
61
  entityId,
62
+ translations,
82
63
  organizationId: context.organizationId,
83
64
  tenantId: context.tenantId
84
- });
85
- } catch (err) {
86
- console.warn("[translations] Failed to emit translations.translation.updated:", err instanceof Error ? err.message : "unknown");
87
- }
88
- const row = await context.knex("entity_translations").where({
89
- entity_type: entityType,
90
- entity_id: entityId
91
- }).andWhereRaw("tenant_id is not distinct from ?", [context.tenantId]).andWhereRaw("organization_id is not distinct from ?", [context.organizationId]).first();
92
- return NextResponse.json({
65
+ },
66
+ ctx: context.commandCtx
67
+ });
68
+ const row = await context.knex("entity_translations").where({ id: result.rowId }).first();
69
+ const response = NextResponse.json({
93
70
  entityType: row.entity_type,
94
71
  entityId: row.entity_id,
95
72
  translations: row.translations,
96
73
  createdAt: row.created_at,
97
74
  updatedAt: row.updated_at
98
75
  });
76
+ if (logEntry?.undoToken && logEntry?.id && logEntry?.commandId) {
77
+ response.headers.set(
78
+ "x-om-operation",
79
+ serializeOperationMetadata({
80
+ id: logEntry.id,
81
+ undoToken: logEntry.undoToken,
82
+ commandId: logEntry.commandId,
83
+ actionLabel: logEntry.actionLabel ?? null,
84
+ resourceKind: logEntry.resourceKind ?? "translations.translation",
85
+ resourceId: logEntry.resourceId ?? result.rowId,
86
+ executedAt: logEntry.createdAt instanceof Date ? logEntry.createdAt.toISOString() : typeof logEntry.createdAt === "string" ? logEntry.createdAt : (/* @__PURE__ */ new Date()).toISOString()
87
+ })
88
+ );
89
+ }
90
+ return response;
99
91
  } catch (err) {
100
92
  if (err instanceof CrudHttpError) {
101
93
  return NextResponse.json(err.body, { status: err.status });
@@ -114,22 +106,32 @@ async function DELETE(req, ctx) {
114
106
  entityType: ctx.params?.entityType,
115
107
  entityId: ctx.params?.entityId
116
108
  });
117
- await context.knex("entity_translations").where({
118
- entity_type: entityType,
119
- entity_id: entityId
120
- }).andWhereRaw("tenant_id is not distinct from ?", [context.tenantId]).andWhereRaw("organization_id is not distinct from ?", [context.organizationId]).del();
121
- try {
122
- const bus = context.container.resolve("eventBus");
123
- await bus.emitEvent("translations.translation.deleted", {
109
+ const commandBus = context.container.resolve("commandBus");
110
+ const { logEntry } = await commandBus.execute("translations.translation.delete", {
111
+ input: {
124
112
  entityType,
125
113
  entityId,
126
114
  organizationId: context.organizationId,
127
115
  tenantId: context.tenantId
128
- });
129
- } catch (err) {
130
- console.warn("[translations] Failed to emit translations.translation.deleted:", err instanceof Error ? err.message : "unknown");
116
+ },
117
+ ctx: context.commandCtx
118
+ });
119
+ const response = new NextResponse(null, { status: 204 });
120
+ if (logEntry?.undoToken && logEntry?.id && logEntry?.commandId) {
121
+ response.headers.set(
122
+ "x-om-operation",
123
+ serializeOperationMetadata({
124
+ id: logEntry.id,
125
+ undoToken: logEntry.undoToken,
126
+ commandId: logEntry.commandId,
127
+ actionLabel: logEntry.actionLabel ?? null,
128
+ resourceKind: logEntry.resourceKind ?? "translations.translation",
129
+ resourceId: logEntry.resourceId ?? null,
130
+ executedAt: logEntry.createdAt instanceof Date ? logEntry.createdAt.toISOString() : typeof logEntry.createdAt === "string" ? logEntry.createdAt : (/* @__PURE__ */ new Date()).toISOString()
131
+ })
132
+ );
131
133
  }
132
- return new NextResponse(null, { status: 204 });
134
+ return response;
133
135
  } catch (err) {
134
136
  if (err instanceof CrudHttpError) {
135
137
  return NextResponse.json(err.body, { status: err.status });
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../src/modules/translations/api/%5BentityType%5D/%5BentityId%5D/route.ts"],
4
- "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport { resolveTranslationsRouteContext } from '@open-mercato/core/modules/translations/api/context'\nimport { translationBodySchema, entityTypeParamSchema, entityIdParamSchema } from '@open-mercato/core/modules/translations/data/validators'\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport type { OpenApiMethodDoc, OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\n\nconst paramsSchema = z.object({\n entityType: entityTypeParamSchema,\n entityId: entityIdParamSchema,\n})\n\nexport const metadata = {\n GET: { requireAuth: true, requireFeatures: ['translations.view'] },\n PUT: { requireAuth: true, requireFeatures: ['translations.manage'] },\n DELETE: { requireAuth: true, requireFeatures: ['translations.manage'] },\n}\n\nexport async function GET(req: Request, ctx: { params?: { entityType?: string; entityId?: string } }) {\n try {\n const context = await resolveTranslationsRouteContext(req)\n const { entityType, entityId } = paramsSchema.parse({\n entityType: ctx.params?.entityType,\n entityId: ctx.params?.entityId,\n })\n\n const row = await context.knex('entity_translations')\n .where({\n entity_type: entityType,\n entity_id: entityId,\n })\n .andWhereRaw('tenant_id is not distinct from ?', [context.tenantId])\n .andWhereRaw('organization_id is not distinct from ?', [context.organizationId])\n .first()\n\n if (!row) {\n return NextResponse.json({ error: 'Not found' }, { status: 404 })\n }\n\n return NextResponse.json({\n entityType: row.entity_type,\n entityId: row.entity_id,\n translations: row.translations,\n createdAt: row.created_at,\n updatedAt: row.updated_at,\n })\n } catch (err) {\n if (err instanceof CrudHttpError) {\n return NextResponse.json(err.body, { status: err.status })\n }\n if (err instanceof z.ZodError) {\n return NextResponse.json({ error: 'Invalid parameters', details: err.issues }, { status: 400 })\n }\n console.error('[translations/:entityType/:entityId.GET] Unexpected error', err)\n return NextResponse.json({ error: 'Internal server error' }, { status: 500 })\n }\n}\n\nexport async function PUT(req: Request, ctx: { params?: { entityType?: string; entityId?: string } }) {\n try {\n const context = await resolveTranslationsRouteContext(req)\n const { entityType, entityId } = paramsSchema.parse({\n entityType: ctx.params?.entityType,\n entityId: ctx.params?.entityId,\n })\n\n const rawBody = await req.json().catch(() => ({}))\n const translations = translationBodySchema.parse(rawBody)\n\n const existing = await context.knex('entity_translations')\n .where({\n entity_type: entityType,\n entity_id: entityId,\n })\n .andWhereRaw('tenant_id is not distinct from ?', [context.tenantId])\n .andWhereRaw('organization_id is not distinct from ?', [context.organizationId])\n .first()\n\n const now = context.knex.fn.now()\n\n if (existing) {\n await context.knex('entity_translations')\n .where({ id: existing.id })\n .update({\n translations,\n updated_at: now,\n })\n } else {\n await context.knex('entity_translations').insert({\n entity_type: entityType,\n entity_id: entityId,\n organization_id: context.organizationId,\n tenant_id: context.tenantId,\n translations,\n created_at: now,\n updated_at: now,\n })\n }\n\n try {\n const bus = context.container.resolve<{ emitEvent: (event: string, payload: unknown) => Promise<void> }>('eventBus')\n await bus.emitEvent('translations.translation.updated', {\n entityType,\n entityId,\n organizationId: context.organizationId,\n tenantId: context.tenantId,\n })\n } catch (err) {\n console.warn('[translations] Failed to emit translations.translation.updated:', err instanceof Error ? err.message : 'unknown')\n }\n\n const row = await context.knex('entity_translations')\n .where({\n entity_type: entityType,\n entity_id: entityId,\n })\n .andWhereRaw('tenant_id is not distinct from ?', [context.tenantId])\n .andWhereRaw('organization_id is not distinct from ?', [context.organizationId])\n .first()\n\n return NextResponse.json({\n entityType: row.entity_type,\n entityId: row.entity_id,\n translations: row.translations,\n createdAt: row.created_at,\n updatedAt: row.updated_at,\n })\n } catch (err) {\n if (err instanceof CrudHttpError) {\n return NextResponse.json(err.body, { status: err.status })\n }\n if (err instanceof z.ZodError) {\n return NextResponse.json({ error: 'Validation failed', details: err.issues }, { status: 400 })\n }\n console.error('[translations/:entityType/:entityId.PUT] Unexpected error', err)\n return NextResponse.json({ error: 'Internal server error' }, { status: 500 })\n }\n}\n\nexport async function DELETE(req: Request, ctx: { params?: { entityType?: string; entityId?: string } }) {\n try {\n const context = await resolveTranslationsRouteContext(req)\n const { entityType, entityId } = paramsSchema.parse({\n entityType: ctx.params?.entityType,\n entityId: ctx.params?.entityId,\n })\n\n await context.knex('entity_translations')\n .where({\n entity_type: entityType,\n entity_id: entityId,\n })\n .andWhereRaw('tenant_id is not distinct from ?', [context.tenantId])\n .andWhereRaw('organization_id is not distinct from ?', [context.organizationId])\n .del()\n\n try {\n const bus = context.container.resolve<{ emitEvent: (event: string, payload: unknown) => Promise<void> }>('eventBus')\n await bus.emitEvent('translations.translation.deleted', {\n entityType,\n entityId,\n organizationId: context.organizationId,\n tenantId: context.tenantId,\n })\n } catch (err) {\n console.warn('[translations] Failed to emit translations.translation.deleted:', err instanceof Error ? err.message : 'unknown')\n }\n\n return new NextResponse(null, { status: 204 })\n } catch (err) {\n if (err instanceof CrudHttpError) {\n return NextResponse.json(err.body, { status: err.status })\n }\n if (err instanceof z.ZodError) {\n return NextResponse.json({ error: 'Invalid parameters', details: err.issues }, { status: 400 })\n }\n console.error('[translations/:entityType/:entityId.DELETE] Unexpected error', err)\n return NextResponse.json({ error: 'Internal server error' }, { status: 500 })\n }\n}\n\nconst translationsTag = 'Translations'\n\nconst getDoc: OpenApiMethodDoc = {\n summary: 'Get entity translations',\n description: 'Returns the full translation record for a single entity.',\n tags: [translationsTag],\n responses: [\n { status: 200, description: 'Translation record found.' },\n ],\n errors: [\n { status: 401, description: 'Authentication required' },\n { status: 404, description: 'No translations found for this entity' },\n ],\n}\n\nconst putDoc: OpenApiMethodDoc = {\n summary: 'Create or update entity translations',\n description: 'Full replacement of translations JSONB for an entity.',\n tags: [translationsTag],\n responses: [\n { status: 200, description: 'Translations saved.' },\n ],\n errors: [\n { status: 400, description: 'Validation failed' },\n { status: 401, description: 'Authentication required' },\n ],\n}\n\nconst deleteDoc: OpenApiMethodDoc = {\n summary: 'Delete entity translations',\n description: 'Removes all translations for an entity.',\n tags: [translationsTag],\n responses: [\n { status: 204, description: 'Translations deleted.' },\n ],\n errors: [\n { status: 401, description: 'Authentication required' },\n ],\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: translationsTag,\n summary: 'Entity translation resource',\n pathParams: paramsSchema,\n methods: {\n GET: getDoc,\n PUT: putDoc,\n DELETE: deleteDoc,\n },\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport { resolveTranslationsRouteContext } from '@open-mercato/core/modules/translations/api/context'\nimport { translationBodySchema, entityTypeParamSchema, entityIdParamSchema } from '@open-mercato/core/modules/translations/data/validators'\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { CommandBus } from '@open-mercato/shared/lib/commands'\nimport { serializeOperationMetadata } from '@open-mercato/shared/lib/commands/operationMetadata'\nimport type { OpenApiMethodDoc, OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\n\nconst paramsSchema = z.object({\n entityType: entityTypeParamSchema,\n entityId: entityIdParamSchema,\n})\n\nexport const metadata = {\n GET: { requireAuth: true, requireFeatures: ['translations.view'] },\n PUT: { requireAuth: true, requireFeatures: ['translations.manage'] },\n DELETE: { requireAuth: true, requireFeatures: ['translations.manage'] },\n}\n\nexport async function GET(req: Request, ctx: { params?: { entityType?: string; entityId?: string } }) {\n try {\n const context = await resolveTranslationsRouteContext(req)\n const { entityType, entityId } = paramsSchema.parse({\n entityType: ctx.params?.entityType,\n entityId: ctx.params?.entityId,\n })\n\n const row = await context.knex('entity_translations')\n .where({\n entity_type: entityType,\n entity_id: entityId,\n })\n .andWhereRaw('tenant_id is not distinct from ?', [context.tenantId])\n .andWhereRaw('organization_id is not distinct from ?', [context.organizationId])\n .first()\n\n if (!row) {\n return NextResponse.json({ error: 'Not found' }, { status: 404 })\n }\n\n return NextResponse.json({\n entityType: row.entity_type,\n entityId: row.entity_id,\n translations: row.translations,\n createdAt: row.created_at,\n updatedAt: row.updated_at,\n })\n } catch (err) {\n if (err instanceof CrudHttpError) {\n return NextResponse.json(err.body, { status: err.status })\n }\n if (err instanceof z.ZodError) {\n return NextResponse.json({ error: 'Invalid parameters', details: err.issues }, { status: 400 })\n }\n console.error('[translations/:entityType/:entityId.GET] Unexpected error', err)\n return NextResponse.json({ error: 'Internal server error' }, { status: 500 })\n }\n}\n\nexport async function PUT(req: Request, ctx: { params?: { entityType?: string; entityId?: string } }) {\n try {\n const context = await resolveTranslationsRouteContext(req)\n const { entityType, entityId } = paramsSchema.parse({\n entityType: ctx.params?.entityType,\n entityId: ctx.params?.entityId,\n })\n\n const rawBody = await req.json().catch(() => ({}))\n const translations = translationBodySchema.parse(rawBody)\n\n const commandBus = context.container.resolve('commandBus') as CommandBus\n const { result, logEntry } = await commandBus.execute<\n { entityType: string; entityId: string; translations: typeof translations; organizationId: string | null; tenantId: string },\n { rowId: string }\n >('translations.translation.save', {\n input: {\n entityType,\n entityId,\n translations,\n organizationId: context.organizationId,\n tenantId: context.tenantId,\n },\n ctx: context.commandCtx,\n })\n\n const row = await context.knex('entity_translations')\n .where({ id: result.rowId })\n .first()\n\n const response = NextResponse.json({\n entityType: row.entity_type,\n entityId: row.entity_id,\n translations: row.translations,\n createdAt: row.created_at,\n updatedAt: row.updated_at,\n })\n\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 ?? 'translations.translation',\n resourceId: logEntry.resourceId ?? result.rowId,\n executedAt: logEntry.createdAt instanceof Date\n ? logEntry.createdAt.toISOString()\n : typeof logEntry.createdAt === 'string'\n ? logEntry.createdAt\n : new Date().toISOString(),\n }),\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 if (err instanceof z.ZodError) {\n return NextResponse.json({ error: 'Validation failed', details: err.issues }, { status: 400 })\n }\n console.error('[translations/:entityType/:entityId.PUT] Unexpected error', err)\n return NextResponse.json({ error: 'Internal server error' }, { status: 500 })\n }\n}\n\nexport async function DELETE(req: Request, ctx: { params?: { entityType?: string; entityId?: string } }) {\n try {\n const context = await resolveTranslationsRouteContext(req)\n const { entityType, entityId } = paramsSchema.parse({\n entityType: ctx.params?.entityType,\n entityId: ctx.params?.entityId,\n })\n\n const commandBus = context.container.resolve('commandBus') as CommandBus\n const { logEntry } = await commandBus.execute<\n { entityType: string; entityId: string; organizationId: string | null; tenantId: string },\n { deleted: boolean }\n >('translations.translation.delete', {\n input: {\n entityType,\n entityId,\n organizationId: context.organizationId,\n tenantId: context.tenantId,\n },\n ctx: context.commandCtx,\n })\n\n const response = new NextResponse(null, { status: 204 })\n\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 ?? 'translations.translation',\n resourceId: logEntry.resourceId ?? null,\n executedAt: logEntry.createdAt instanceof Date\n ? logEntry.createdAt.toISOString()\n : typeof logEntry.createdAt === 'string'\n ? logEntry.createdAt\n : new Date().toISOString(),\n }),\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 if (err instanceof z.ZodError) {\n return NextResponse.json({ error: 'Invalid parameters', details: err.issues }, { status: 400 })\n }\n console.error('[translations/:entityType/:entityId.DELETE] Unexpected error', err)\n return NextResponse.json({ error: 'Internal server error' }, { status: 500 })\n }\n}\n\nconst translationsTag = 'Translations'\n\nconst getDoc: OpenApiMethodDoc = {\n summary: 'Get entity translations',\n description: 'Returns the full translation record for a single entity.',\n tags: [translationsTag],\n responses: [\n { status: 200, description: 'Translation record found.' },\n ],\n errors: [\n { status: 401, description: 'Authentication required' },\n { status: 404, description: 'No translations found for this entity' },\n ],\n}\n\nconst putDoc: OpenApiMethodDoc = {\n summary: 'Create or update entity translations',\n description: 'Full replacement of translations JSONB for an entity.',\n tags: [translationsTag],\n responses: [\n { status: 200, description: 'Translations saved.' },\n ],\n errors: [\n { status: 400, description: 'Validation failed' },\n { status: 401, description: 'Authentication required' },\n ],\n}\n\nconst deleteDoc: OpenApiMethodDoc = {\n summary: 'Delete entity translations',\n description: 'Removes all translations for an entity.',\n tags: [translationsTag],\n responses: [\n { status: 204, description: 'Translations deleted.' },\n ],\n errors: [\n { status: 401, description: 'Authentication required' },\n ],\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: translationsTag,\n summary: 'Entity translation resource',\n pathParams: paramsSchema,\n methods: {\n GET: getDoc,\n PUT: putDoc,\n DELETE: deleteDoc,\n },\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -13,13 +13,22 @@ async function resolveTranslationsRouteContext(req) {
13
13
  const knex = em.getConnection().getKnex();
14
14
  const tenantId = scope?.tenantId ?? auth.tenantId;
15
15
  const organizationId = scope?.selectedId ?? auth.orgId ?? null;
16
+ const commandCtx = {
17
+ container,
18
+ auth,
19
+ organizationScope: scope,
20
+ selectedOrganizationId: organizationId,
21
+ organizationIds: scope?.filterIds ?? (auth.orgId ? [auth.orgId] : null),
22
+ request: req
23
+ };
16
24
  return {
17
25
  container,
18
26
  auth,
19
27
  em,
20
28
  knex,
21
29
  organizationId,
22
- tenantId
30
+ tenantId,
31
+ commandCtx
23
32
  };
24
33
  }
25
34
  export {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/translations/api/context.ts"],
4
- "sourcesContent": ["import { 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 { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport type { AwilixContainer } from 'awilix'\nimport type { Knex } from 'knex'\n\nexport type TranslationsRouteContext = {\n container: AwilixContainer\n auth: NonNullable<Awaited<ReturnType<typeof getAuthFromRequest>>>\n em: EntityManager\n knex: Knex\n organizationId: string | null\n tenantId: string\n}\n\nexport async function resolveTranslationsRouteContext(req: Request): Promise<TranslationsRouteContext> {\n const container = await createRequestContainer()\n const auth = await getAuthFromRequest(req)\n if (!auth || !auth.tenantId) {\n throw new CrudHttpError(401, { error: 'Unauthorized' })\n }\n\n const scope = await resolveOrganizationScopeForRequest({ container, auth, request: req })\n const em = container.resolve('em') as EntityManager\n const knex = (em as unknown as { getConnection(): { getKnex(): Knex } }).getConnection().getKnex()\n const tenantId: string = scope?.tenantId ?? auth.tenantId\n const organizationId = scope?.selectedId ?? auth.orgId ?? null\n\n return {\n container,\n auth,\n em,\n knex,\n organizationId,\n tenantId,\n }\n}\n"],
5
- "mappings": "AAAA,SAAS,8BAA8B;AACvC,SAAS,0BAA0B;AACnC,SAAS,0CAA0C;AACnD,SAAS,qBAAqB;AAc9B,eAAsB,gCAAgC,KAAiD;AACrG,QAAM,YAAY,MAAM,uBAAuB;AAC/C,QAAM,OAAO,MAAM,mBAAmB,GAAG;AACzC,MAAI,CAAC,QAAQ,CAAC,KAAK,UAAU;AAC3B,UAAM,IAAI,cAAc,KAAK,EAAE,OAAO,eAAe,CAAC;AAAA,EACxD;AAEA,QAAM,QAAQ,MAAM,mCAAmC,EAAE,WAAW,MAAM,SAAS,IAAI,CAAC;AACxF,QAAM,KAAK,UAAU,QAAQ,IAAI;AACjC,QAAM,OAAQ,GAA2D,cAAc,EAAE,QAAQ;AACjG,QAAM,WAAmB,OAAO,YAAY,KAAK;AACjD,QAAM,iBAAiB,OAAO,cAAc,KAAK,SAAS;AAE1D,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;",
4
+ "sourcesContent": ["import { 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 { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport type { CommandRuntimeContext } from '@open-mercato/shared/lib/commands'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport type { AwilixContainer } from 'awilix'\nimport type { Knex } from 'knex'\n\nexport type TranslationsRouteContext = {\n container: AwilixContainer\n auth: NonNullable<Awaited<ReturnType<typeof getAuthFromRequest>>>\n em: EntityManager\n knex: Knex\n organizationId: string | null\n tenantId: string\n commandCtx: CommandRuntimeContext\n}\n\nexport async function resolveTranslationsRouteContext(req: Request): Promise<TranslationsRouteContext> {\n const container = await createRequestContainer()\n const auth = await getAuthFromRequest(req)\n if (!auth || !auth.tenantId) {\n throw new CrudHttpError(401, { error: 'Unauthorized' })\n }\n\n const scope = await resolveOrganizationScopeForRequest({ container, auth, request: req })\n const em = container.resolve('em') as EntityManager\n const knex = (em as unknown as { getConnection(): { getKnex(): Knex } }).getConnection().getKnex()\n const tenantId: string = scope?.tenantId ?? auth.tenantId\n const organizationId = scope?.selectedId ?? auth.orgId ?? null\n\n const commandCtx: 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 return {\n container,\n auth,\n em,\n knex,\n organizationId,\n tenantId,\n commandCtx,\n }\n}\n"],
5
+ "mappings": "AAAA,SAAS,8BAA8B;AACvC,SAAS,0BAA0B;AACnC,SAAS,0CAA0C;AACnD,SAAS,qBAAqB;AAgB9B,eAAsB,gCAAgC,KAAiD;AACrG,QAAM,YAAY,MAAM,uBAAuB;AAC/C,QAAM,OAAO,MAAM,mBAAmB,GAAG;AACzC,MAAI,CAAC,QAAQ,CAAC,KAAK,UAAU;AAC3B,UAAM,IAAI,cAAc,KAAK,EAAE,OAAO,eAAe,CAAC;AAAA,EACxD;AAEA,QAAM,QAAQ,MAAM,mCAAmC,EAAE,WAAW,MAAM,SAAS,IAAI,CAAC;AACxF,QAAM,KAAK,UAAU,QAAQ,IAAI;AACjC,QAAM,OAAQ,GAA2D,cAAc,EAAE,QAAQ;AACjG,QAAM,WAAmB,OAAO,YAAY,KAAK;AACjD,QAAM,iBAAiB,OAAO,cAAc,KAAK,SAAS;AAE1D,QAAM,aAAoC;AAAA,IACxC;AAAA,IACA;AAAA,IACA,mBAAmB;AAAA,IACnB,wBAAwB;AAAA,IACxB,iBAAiB,OAAO,cAAc,KAAK,QAAQ,CAAC,KAAK,KAAK,IAAI;AAAA,IAClE,SAAS;AAAA,EACX;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;",
6
6
  "names": []
7
7
  }
@@ -0,0 +1,2 @@
1
+ import "./translations.js";
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/modules/translations/commands/index.ts"],
4
+ "sourcesContent": ["import './translations'\n"],
5
+ "mappings": "AAAA,OAAO;",
6
+ "names": []
7
+ }
@@ -0,0 +1,160 @@
1
+ import { registerCommand } from "@open-mercato/shared/lib/commands";
2
+ import { ensureTenantScope } from "@open-mercato/shared/lib/commands/scope";
3
+ import { extractUndoPayload } from "@open-mercato/shared/lib/commands/undo";
4
+ import { resolveTranslations } from "@open-mercato/shared/lib/i18n/server";
5
+ import { emitTranslationsEvent } from "../events.js";
6
+ function resolveKnex(ctx) {
7
+ const em = ctx.container.resolve("em");
8
+ return em.getConnection().getKnex();
9
+ }
10
+ async function loadTranslationSnapshot(knex, entityType, entityId, tenantId, organizationId) {
11
+ const row = await knex("entity_translations").where({ entity_type: entityType, entity_id: entityId }).andWhereRaw("tenant_id is not distinct from ?", [tenantId]).andWhereRaw("organization_id is not distinct from ?", [organizationId]).first();
12
+ if (!row) return null;
13
+ return {
14
+ id: row.id,
15
+ entityType: row.entity_type,
16
+ entityId: row.entity_id,
17
+ translations: row.translations ?? null,
18
+ organizationId: row.organization_id ?? null,
19
+ tenantId: row.tenant_id
20
+ };
21
+ }
22
+ const saveTranslationCommand = {
23
+ id: "translations.translation.save",
24
+ async prepare(input, ctx) {
25
+ ensureTenantScope(ctx, input.tenantId);
26
+ const knex = resolveKnex(ctx);
27
+ const snapshot = await loadTranslationSnapshot(knex, input.entityType, input.entityId, input.tenantId, input.organizationId);
28
+ return { before: snapshot };
29
+ },
30
+ async execute(input, ctx) {
31
+ const knex = resolveKnex(ctx);
32
+ const existing = await knex("entity_translations").where({ entity_type: input.entityType, entity_id: input.entityId }).andWhereRaw("tenant_id is not distinct from ?", [input.tenantId]).andWhereRaw("organization_id is not distinct from ?", [input.organizationId]).first();
33
+ const now = knex.fn.now();
34
+ if (existing) {
35
+ await knex("entity_translations").where({ id: existing.id }).update({ translations: input.translations, updated_at: now });
36
+ } else {
37
+ await knex("entity_translations").insert({
38
+ entity_type: input.entityType,
39
+ entity_id: input.entityId,
40
+ organization_id: input.organizationId,
41
+ tenant_id: input.tenantId,
42
+ translations: input.translations,
43
+ created_at: now,
44
+ updated_at: now
45
+ });
46
+ }
47
+ await emitTranslationsEvent("translations.translation.updated", {
48
+ entityType: input.entityType,
49
+ entityId: input.entityId,
50
+ organizationId: input.organizationId,
51
+ tenantId: input.tenantId
52
+ }, { persistent: true }).catch(() => void 0);
53
+ const saved = await knex("entity_translations").where({ entity_type: input.entityType, entity_id: input.entityId }).andWhereRaw("tenant_id is not distinct from ?", [input.tenantId]).andWhereRaw("organization_id is not distinct from ?", [input.organizationId]).first();
54
+ return { rowId: saved.id };
55
+ },
56
+ async captureAfter(input, _result, ctx) {
57
+ const knex = resolveKnex(ctx);
58
+ return await loadTranslationSnapshot(knex, input.entityType, input.entityId, input.tenantId, input.organizationId);
59
+ },
60
+ async buildLog({ snapshots, result }) {
61
+ const { translate } = await resolveTranslations();
62
+ const before = snapshots.before;
63
+ const after = snapshots.after;
64
+ return {
65
+ actionLabel: translate("translations.audit.save", "Save translation"),
66
+ resourceKind: "translations.translation",
67
+ resourceId: result.rowId,
68
+ tenantId: after?.tenantId ?? before?.tenantId ?? null,
69
+ organizationId: after?.organizationId ?? before?.organizationId ?? null,
70
+ snapshotBefore: before ?? null,
71
+ snapshotAfter: after ?? null,
72
+ payload: {
73
+ undo: { before: before ?? null, after: after ?? null }
74
+ }
75
+ };
76
+ },
77
+ async undo({ logEntry, ctx }) {
78
+ const payload = extractUndoPayload(logEntry);
79
+ const before = payload?.before ?? null;
80
+ const knex = resolveKnex(ctx);
81
+ if (!before || !before.translations) {
82
+ const resourceId = logEntry?.resourceId;
83
+ if (resourceId) {
84
+ await knex("entity_translations").where({ id: resourceId }).del();
85
+ }
86
+ } else {
87
+ const existing = await knex("entity_translations").where({ entity_type: before.entityType, entity_id: before.entityId }).andWhereRaw("tenant_id is not distinct from ?", [before.tenantId]).andWhereRaw("organization_id is not distinct from ?", [before.organizationId]).first();
88
+ if (existing) {
89
+ await knex("entity_translations").where({ id: existing.id }).update({ translations: before.translations, updated_at: knex.fn.now() });
90
+ } else {
91
+ await knex("entity_translations").insert({
92
+ entity_type: before.entityType,
93
+ entity_id: before.entityId,
94
+ organization_id: before.organizationId,
95
+ tenant_id: before.tenantId,
96
+ translations: before.translations,
97
+ created_at: knex.fn.now(),
98
+ updated_at: knex.fn.now()
99
+ });
100
+ }
101
+ }
102
+ }
103
+ };
104
+ const deleteTranslationCommand = {
105
+ id: "translations.translation.delete",
106
+ async prepare(input, ctx) {
107
+ ensureTenantScope(ctx, input.tenantId);
108
+ const knex = resolveKnex(ctx);
109
+ const snapshot = await loadTranslationSnapshot(knex, input.entityType, input.entityId, input.tenantId, input.organizationId);
110
+ return { before: snapshot };
111
+ },
112
+ async execute(input, ctx) {
113
+ const knex = resolveKnex(ctx);
114
+ const count = await knex("entity_translations").where({ entity_type: input.entityType, entity_id: input.entityId }).andWhereRaw("tenant_id is not distinct from ?", [input.tenantId]).andWhereRaw("organization_id is not distinct from ?", [input.organizationId]).del();
115
+ await emitTranslationsEvent("translations.translation.deleted", {
116
+ entityType: input.entityType,
117
+ entityId: input.entityId,
118
+ organizationId: input.organizationId,
119
+ tenantId: input.tenantId
120
+ }, { persistent: true }).catch(() => void 0);
121
+ return { deleted: count > 0 };
122
+ },
123
+ async buildLog({ snapshots }) {
124
+ const before = snapshots.before;
125
+ if (!before) return null;
126
+ const { translate } = await resolveTranslations();
127
+ return {
128
+ actionLabel: translate("translations.audit.delete", "Delete translation"),
129
+ resourceKind: "translations.translation",
130
+ resourceId: before.id ?? void 0,
131
+ tenantId: before.tenantId,
132
+ organizationId: before.organizationId,
133
+ snapshotBefore: before,
134
+ payload: {
135
+ undo: { before }
136
+ }
137
+ };
138
+ },
139
+ async undo({ logEntry, ctx }) {
140
+ const payload = extractUndoPayload(logEntry);
141
+ const before = payload?.before;
142
+ if (!before || !before.translations) return;
143
+ const knex = resolveKnex(ctx);
144
+ const existing = await knex("entity_translations").where({ entity_type: before.entityType, entity_id: before.entityId }).andWhereRaw("tenant_id is not distinct from ?", [before.tenantId]).andWhereRaw("organization_id is not distinct from ?", [before.organizationId]).first();
145
+ if (!existing) {
146
+ await knex("entity_translations").insert({
147
+ entity_type: before.entityType,
148
+ entity_id: before.entityId,
149
+ organization_id: before.organizationId,
150
+ tenant_id: before.tenantId,
151
+ translations: before.translations,
152
+ created_at: knex.fn.now(),
153
+ updated_at: knex.fn.now()
154
+ });
155
+ }
156
+ }
157
+ };
158
+ registerCommand(saveTranslationCommand);
159
+ registerCommand(deleteTranslationCommand);
160
+ //# sourceMappingURL=translations.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/modules/translations/commands/translations.ts"],
4
+ "sourcesContent": ["import { registerCommand } from '@open-mercato/shared/lib/commands'\nimport type { CommandHandler, CommandRuntimeContext } from '@open-mercato/shared/lib/commands'\nimport { ensureTenantScope } from '@open-mercato/shared/lib/commands/scope'\nimport { extractUndoPayload } from '@open-mercato/shared/lib/commands/undo'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport type { Knex } from 'knex'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { emitTranslationsEvent } from '../events'\n\ntype TranslationSnapshot = {\n id: string | null\n entityType: string\n entityId: string\n translations: Record<string, Record<string, string | null>> | null\n organizationId: string | null\n tenantId: string\n}\n\ntype TranslationUndoPayload = {\n before?: TranslationSnapshot | null\n after?: TranslationSnapshot | null\n}\n\ntype SaveInput = {\n entityType: string\n entityId: string\n translations: Record<string, Record<string, string | null>>\n organizationId: string | null\n tenantId: string\n}\n\ntype DeleteInput = {\n entityType: string\n entityId: string\n organizationId: string | null\n tenantId: string\n}\n\nfunction resolveKnex(ctx: CommandRuntimeContext): Knex {\n const em = ctx.container.resolve('em') as EntityManager\n return (em as unknown as { getConnection(): { getKnex(): Knex } }).getConnection().getKnex()\n}\n\nasync function loadTranslationSnapshot(\n knex: Knex,\n entityType: string,\n entityId: string,\n tenantId: string,\n organizationId: string | null,\n): Promise<TranslationSnapshot | null> {\n const row = await knex('entity_translations')\n .where({ entity_type: entityType, entity_id: entityId })\n .andWhereRaw('tenant_id is not distinct from ?', [tenantId])\n .andWhereRaw('organization_id is not distinct from ?', [organizationId])\n .first()\n\n if (!row) return null\n return {\n id: row.id,\n entityType: row.entity_type,\n entityId: row.entity_id,\n translations: row.translations ?? null,\n organizationId: row.organization_id ?? null,\n tenantId: row.tenant_id,\n }\n}\n\nconst saveTranslationCommand: CommandHandler<SaveInput, { rowId: string }> = {\n id: 'translations.translation.save',\n\n async prepare(input, ctx) {\n ensureTenantScope(ctx, input.tenantId)\n const knex = resolveKnex(ctx)\n const snapshot = await loadTranslationSnapshot(knex, input.entityType, input.entityId, input.tenantId, input.organizationId)\n return { before: snapshot }\n },\n\n async execute(input, ctx) {\n const knex = resolveKnex(ctx)\n const existing = await knex('entity_translations')\n .where({ entity_type: input.entityType, entity_id: input.entityId })\n .andWhereRaw('tenant_id is not distinct from ?', [input.tenantId])\n .andWhereRaw('organization_id is not distinct from ?', [input.organizationId])\n .first()\n\n const now = knex.fn.now()\n\n if (existing) {\n await knex('entity_translations')\n .where({ id: existing.id })\n .update({ translations: input.translations, updated_at: now })\n } else {\n await knex('entity_translations').insert({\n entity_type: input.entityType,\n entity_id: input.entityId,\n organization_id: input.organizationId,\n tenant_id: input.tenantId,\n translations: input.translations,\n created_at: now,\n updated_at: now,\n })\n }\n\n await emitTranslationsEvent('translations.translation.updated', {\n entityType: input.entityType,\n entityId: input.entityId,\n organizationId: input.organizationId,\n tenantId: input.tenantId,\n }, { persistent: true }).catch(() => undefined)\n\n const saved = await knex('entity_translations')\n .where({ entity_type: input.entityType, entity_id: input.entityId })\n .andWhereRaw('tenant_id is not distinct from ?', [input.tenantId])\n .andWhereRaw('organization_id is not distinct from ?', [input.organizationId])\n .first()\n\n return { rowId: saved.id }\n },\n\n async captureAfter(input, _result, ctx) {\n const knex = resolveKnex(ctx)\n return await loadTranslationSnapshot(knex, input.entityType, input.entityId, input.tenantId, input.organizationId)\n },\n\n async buildLog({ snapshots, result }) {\n const { translate } = await resolveTranslations()\n const before = snapshots.before as TranslationSnapshot | null | undefined\n const after = snapshots.after as TranslationSnapshot | null | undefined\n return {\n actionLabel: translate('translations.audit.save', 'Save translation'),\n resourceKind: 'translations.translation',\n resourceId: result.rowId,\n tenantId: after?.tenantId ?? before?.tenantId ?? null,\n organizationId: after?.organizationId ?? before?.organizationId ?? null,\n snapshotBefore: before ?? null,\n snapshotAfter: after ?? null,\n payload: {\n undo: { before: before ?? null, after: after ?? null } satisfies TranslationUndoPayload,\n },\n }\n },\n\n async undo({ logEntry, ctx }) {\n const payload = extractUndoPayload<TranslationUndoPayload>(logEntry)\n const before = payload?.before ?? null\n const knex = resolveKnex(ctx)\n\n if (!before || !before.translations) {\n // Was a create \u2014 delete the record\n const resourceId = logEntry?.resourceId\n if (resourceId) {\n await knex('entity_translations').where({ id: resourceId }).del()\n }\n } else {\n // Was an update \u2014 restore previous translations\n const existing = await knex('entity_translations')\n .where({ entity_type: before.entityType, entity_id: before.entityId })\n .andWhereRaw('tenant_id is not distinct from ?', [before.tenantId])\n .andWhereRaw('organization_id is not distinct from ?', [before.organizationId])\n .first()\n\n if (existing) {\n await knex('entity_translations')\n .where({ id: existing.id })\n .update({ translations: before.translations, updated_at: knex.fn.now() })\n } else {\n await knex('entity_translations').insert({\n entity_type: before.entityType,\n entity_id: before.entityId,\n organization_id: before.organizationId,\n tenant_id: before.tenantId,\n translations: before.translations,\n created_at: knex.fn.now(),\n updated_at: knex.fn.now(),\n })\n }\n }\n },\n}\n\nconst deleteTranslationCommand: CommandHandler<DeleteInput, { deleted: boolean }> = {\n id: 'translations.translation.delete',\n\n async prepare(input, ctx) {\n ensureTenantScope(ctx, input.tenantId)\n const knex = resolveKnex(ctx)\n const snapshot = await loadTranslationSnapshot(knex, input.entityType, input.entityId, input.tenantId, input.organizationId)\n return { before: snapshot }\n },\n\n async execute(input, ctx) {\n const knex = resolveKnex(ctx)\n const count = await knex('entity_translations')\n .where({ entity_type: input.entityType, entity_id: input.entityId })\n .andWhereRaw('tenant_id is not distinct from ?', [input.tenantId])\n .andWhereRaw('organization_id is not distinct from ?', [input.organizationId])\n .del()\n\n await emitTranslationsEvent('translations.translation.deleted', {\n entityType: input.entityType,\n entityId: input.entityId,\n organizationId: input.organizationId,\n tenantId: input.tenantId,\n }, { persistent: true }).catch(() => undefined)\n\n return { deleted: count > 0 }\n },\n\n async buildLog({ snapshots }) {\n const before = snapshots.before as TranslationSnapshot | null | undefined\n if (!before) return null\n const { translate } = await resolveTranslations()\n return {\n actionLabel: translate('translations.audit.delete', 'Delete translation'),\n resourceKind: 'translations.translation',\n resourceId: before.id ?? undefined,\n tenantId: before.tenantId,\n organizationId: before.organizationId,\n snapshotBefore: before,\n payload: {\n undo: { before } satisfies TranslationUndoPayload,\n },\n }\n },\n\n async undo({ logEntry, ctx }) {\n const payload = extractUndoPayload<TranslationUndoPayload>(logEntry)\n const before = payload?.before\n if (!before || !before.translations) return\n const knex = resolveKnex(ctx)\n\n const existing = await knex('entity_translations')\n .where({ entity_type: before.entityType, entity_id: before.entityId })\n .andWhereRaw('tenant_id is not distinct from ?', [before.tenantId])\n .andWhereRaw('organization_id is not distinct from ?', [before.organizationId])\n .first()\n\n if (!existing) {\n await knex('entity_translations').insert({\n entity_type: before.entityType,\n entity_id: before.entityId,\n organization_id: before.organizationId,\n tenant_id: before.tenantId,\n translations: before.translations,\n created_at: knex.fn.now(),\n updated_at: knex.fn.now(),\n })\n }\n },\n}\n\nregisterCommand(saveTranslationCommand)\nregisterCommand(deleteTranslationCommand)\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -1,3 +1,4 @@
1
+ import "./commands/index.js";
1
2
  const metadata = {
2
3
  name: "translations",
3
4
  title: "Entity Translations",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/modules/translations/index.ts"],
4
- "sourcesContent": ["import type { ModuleInfo } from '@open-mercato/shared/modules/registry'\n\nexport const metadata: ModuleInfo = {\n name: 'translations',\n title: 'Entity Translations',\n version: '0.1.0',\n description: 'System-wide entity translation storage and locale overlay for CRUD responses.',\n author: 'Open Mercato Team',\n license: 'Proprietary',\n}\n"],
5
- "mappings": "AAEO,MAAM,WAAuB;AAAA,EAClC,MAAM;AAAA,EACN,OAAO;AAAA,EACP,SAAS;AAAA,EACT,aAAa;AAAA,EACb,QAAQ;AAAA,EACR,SAAS;AACX;",
4
+ "sourcesContent": ["import type { ModuleInfo } from '@open-mercato/shared/modules/registry'\nimport './commands'\n\nexport const metadata: ModuleInfo = {\n name: 'translations',\n title: 'Entity Translations',\n version: '0.1.0',\n description: 'System-wide entity translation storage and locale overlay for CRUD responses.',\n author: 'Open Mercato Team',\n license: 'Proprietary',\n}\n"],
5
+ "mappings": "AACA,OAAO;AAEA,MAAM,WAAuB;AAAA,EAClC,MAAM;AAAA,EACN,OAAO;AAAA,EACP,SAAS;AAAA,EACT,aAAa;AAAA,EACb,QAAQ;AAAA,EACR,SAAS;AACX;",
6
6
  "names": []
7
7
  }
@@ -0,0 +1,14 @@
1
+ import { Migration } from "@mikro-orm/migrations";
2
+ class Migration20260222205305 extends Migration {
3
+ async up() {
4
+ this.addSql(`create table if not exists "workflow_event_triggers" ("id" uuid not null default gen_random_uuid(), "name" varchar(255) not null, "description" text null, "workflow_definition_id" uuid not null, "event_pattern" varchar(255) not null, "config" jsonb null, "enabled" boolean not null default true, "priority" int not null default 0, "tenant_id" uuid not null, "organization_id" uuid not null, "created_by" varchar(255) null, "updated_by" varchar(255) null, "created_at" timestamptz not null, "updated_at" timestamptz not null, "deleted_at" timestamptz null, constraint "workflow_event_triggers_pkey" primary key ("id"));`);
5
+ this.addSql(`create index if not exists "workflow_event_triggers_enabled_priority_idx" on "workflow_event_triggers" ("enabled", "priority");`);
6
+ this.addSql(`create index if not exists "workflow_event_triggers_tenant_org_idx" on "workflow_event_triggers" ("tenant_id", "organization_id");`);
7
+ this.addSql(`create index if not exists "workflow_event_triggers_definition_idx" on "workflow_event_triggers" ("workflow_definition_id");`);
8
+ this.addSql(`create index if not exists "workflow_event_triggers_event_pattern_idx" on "workflow_event_triggers" ("event_pattern", "enabled");`);
9
+ }
10
+ }
11
+ export {
12
+ Migration20260222205305
13
+ };
14
+ //# sourceMappingURL=Migration20260222205305.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/modules/workflows/migrations/Migration20260222205305.ts"],
4
+ "sourcesContent": ["import { Migration } from '@mikro-orm/migrations';\n\nexport class Migration20260222205305 extends Migration {\n\n override async up(): Promise<void> {\n this.addSql(`create table if not exists \"workflow_event_triggers\" (\"id\" uuid not null default gen_random_uuid(), \"name\" varchar(255) not null, \"description\" text null, \"workflow_definition_id\" uuid not null, \"event_pattern\" varchar(255) not null, \"config\" jsonb null, \"enabled\" boolean not null default true, \"priority\" int not null default 0, \"tenant_id\" uuid not null, \"organization_id\" uuid not null, \"created_by\" varchar(255) null, \"updated_by\" varchar(255) null, \"created_at\" timestamptz not null, \"updated_at\" timestamptz not null, \"deleted_at\" timestamptz null, constraint \"workflow_event_triggers_pkey\" primary key (\"id\"));`);\n this.addSql(`create index if not exists \"workflow_event_triggers_enabled_priority_idx\" on \"workflow_event_triggers\" (\"enabled\", \"priority\");`);\n this.addSql(`create index if not exists \"workflow_event_triggers_tenant_org_idx\" on \"workflow_event_triggers\" (\"tenant_id\", \"organization_id\");`);\n this.addSql(`create index if not exists \"workflow_event_triggers_definition_idx\" on \"workflow_event_triggers\" (\"workflow_definition_id\");`);\n this.addSql(`create index if not exists \"workflow_event_triggers_event_pattern_idx\" on \"workflow_event_triggers\" (\"event_pattern\", \"enabled\");`);\n }\n\n}\n"],
5
+ "mappings": "AAAA,SAAS,iBAAiB;AAEnB,MAAM,gCAAgC,UAAU;AAAA,EAErD,MAAe,KAAoB;AACjC,SAAK,OAAO,4mBAA4mB;AACxnB,SAAK,OAAO,iIAAiI;AAC7I,SAAK,OAAO,oIAAoI;AAChJ,SAAK,OAAO,8HAA8H;AAC1I,SAAK,OAAO,mIAAmI;AAAA,EACjJ;AAEF;",
6
+ "names": []
7
+ }
@@ -5,6 +5,8 @@ export const title = 'title'
5
5
  export const description = 'description'
6
6
  export const status = 'status'
7
7
  export const pipeline_stage = 'pipeline_stage'
8
+ export const pipeline_id = 'pipeline_id'
9
+ export const pipeline_stage_id = 'pipeline_stage_id'
8
10
  export const value_amount = 'value_amount'
9
11
  export const value_currency = 'value_currency'
10
12
  export const probability = 'probability'
@@ -0,0 +1,7 @@
1
+ export const id = 'id'
2
+ export const organization_id = 'organization_id'
3
+ export const tenant_id = 'tenant_id'
4
+ export const name = 'name'
5
+ export const is_default = 'is_default'
6
+ export const created_at = 'created_at'
7
+ export const updated_at = 'updated_at'
@@ -0,0 +1,8 @@
1
+ export const id = 'id'
2
+ export const organization_id = 'organization_id'
3
+ export const tenant_id = 'tenant_id'
4
+ export const pipeline_id = 'pipeline_id'
5
+ export const name = 'name'
6
+ export const position = 'position'
7
+ export const created_at = 'created_at'
8
+ export const updated_at = 'updated_at'
@@ -63,6 +63,8 @@ export const E = {
63
63
  "customer_tag": "customers:customer_tag",
64
64
  "customer_tag_assignment": "customers:customer_tag_assignment",
65
65
  "customer_dictionary_entry": "customers:customer_dictionary_entry",
66
+ "customer_pipeline": "customers:customer_pipeline",
67
+ "customer_pipeline_stage": "customers:customer_pipeline_stage",
66
68
  "customer_todo_link": "customers:customer_todo_link"
67
69
  },
68
70
  "perspectives": {
@@ -35,6 +35,8 @@ import * as customer_deal_person_link from './entities/customer_deal_person_link
35
35
  import * as customer_dictionary_entry from './entities/customer_dictionary_entry/index'
36
36
  import * as customer_entity from './entities/customer_entity/index'
37
37
  import * as customer_person_profile from './entities/customer_person_profile/index'
38
+ import * as customer_pipeline from './entities/customer_pipeline/index'
39
+ import * as customer_pipeline_stage from './entities/customer_pipeline_stage/index'
38
40
  import * as customer_settings from './entities/customer_settings/index'
39
41
  import * as customer_tag from './entities/customer_tag/index'
40
42
  import * as customer_tag_assignment from './entities/customer_tag_assignment/index'
@@ -169,6 +171,8 @@ export const entityFieldsRegistry: Record<string, Record<string, string>> = {
169
171
  customer_dictionary_entry,
170
172
  customer_entity,
171
173
  customer_person_profile,
174
+ customer_pipeline,
175
+ customer_pipeline_stage,
172
176
  customer_settings,
173
177
  customer_tag,
174
178
  customer_tag_assignment,