@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.
- package/dist/generated/entities/customer_deal/index.js +4 -0
- package/dist/generated/entities/customer_deal/index.js.map +2 -2
- package/dist/generated/entities/customer_pipeline/index.js +17 -0
- package/dist/generated/entities/customer_pipeline/index.js.map +7 -0
- package/dist/generated/entities/customer_pipeline_stage/index.js +19 -0
- package/dist/generated/entities/customer_pipeline_stage/index.js.map +7 -0
- package/dist/generated/entities.ids.generated.js +2 -0
- package/dist/generated/entities.ids.generated.js.map +2 -2
- package/dist/generated/entity-fields-registry.js +4 -0
- package/dist/generated/entity-fields-registry.js.map +2 -2
- package/dist/modules/customers/acl.js +2 -0
- package/dist/modules/customers/acl.js.map +2 -2
- package/dist/modules/customers/api/deals/[id]/route.js +4 -0
- package/dist/modules/customers/api/deals/[id]/route.js.map +2 -2
- package/dist/modules/customers/api/deals/route.js +12 -0
- package/dist/modules/customers/api/deals/route.js.map +2 -2
- package/dist/modules/customers/api/dictionaries/[kind]/route.js +20 -1
- package/dist/modules/customers/api/dictionaries/[kind]/route.js.map +2 -2
- package/dist/modules/customers/api/pipeline-stages/reorder/route.js +69 -0
- package/dist/modules/customers/api/pipeline-stages/reorder/route.js.map +7 -0
- package/dist/modules/customers/api/pipeline-stages/route.js +275 -0
- package/dist/modules/customers/api/pipeline-stages/route.js.map +7 -0
- package/dist/modules/customers/api/pipelines/route.js +245 -0
- package/dist/modules/customers/api/pipelines/route.js.map +7 -0
- package/dist/modules/customers/backend/config/customers/page.js +2 -0
- package/dist/modules/customers/backend/config/customers/page.js.map +2 -2
- package/dist/modules/customers/backend/config/customers/pipeline-stages/page.js +439 -0
- package/dist/modules/customers/backend/config/customers/pipeline-stages/page.js.map +7 -0
- package/dist/modules/customers/backend/config/customers/pipeline-stages/page.meta.js +17 -0
- package/dist/modules/customers/backend/config/customers/pipeline-stages/page.meta.js.map +7 -0
- package/dist/modules/customers/backend/customers/deals/[id]/page.js +19 -1
- package/dist/modules/customers/backend/customers/deals/[id]/page.js.map +2 -2
- package/dist/modules/customers/backend/customers/deals/page.js +35 -1
- package/dist/modules/customers/backend/customers/deals/page.js.map +2 -2
- package/dist/modules/customers/backend/customers/deals/pipeline/page.js +102 -74
- package/dist/modules/customers/backend/customers/deals/pipeline/page.js.map +2 -2
- package/dist/modules/customers/cli.js +28 -2
- package/dist/modules/customers/cli.js.map +2 -2
- package/dist/modules/customers/commands/deals.js +34 -2
- package/dist/modules/customers/commands/deals.js.map +2 -2
- package/dist/modules/customers/commands/index.js +2 -0
- package/dist/modules/customers/commands/index.js.map +2 -2
- package/dist/modules/customers/commands/pipeline-stages.js +126 -0
- package/dist/modules/customers/commands/pipeline-stages.js.map +7 -0
- package/dist/modules/customers/commands/pipelines.js +87 -0
- package/dist/modules/customers/commands/pipelines.js.map +7 -0
- package/dist/modules/customers/components/DictionarySettings.js +0 -5
- package/dist/modules/customers/components/DictionarySettings.js.map +2 -2
- package/dist/modules/customers/components/PipelineSettings.js +474 -0
- package/dist/modules/customers/components/PipelineSettings.js.map +7 -0
- package/dist/modules/customers/components/detail/DealForm.js +84 -12
- package/dist/modules/customers/components/detail/DealForm.js.map +2 -2
- package/dist/modules/customers/data/entities.js +78 -0
- package/dist/modules/customers/data/entities.js.map +2 -2
- package/dist/modules/customers/data/validators.js +44 -0
- package/dist/modules/customers/data/validators.js.map +2 -2
- package/dist/modules/customers/migrations/Migration20260218191730.js +77 -0
- package/dist/modules/customers/migrations/Migration20260218191730.js.map +7 -0
- package/dist/modules/customers/setup.js +7 -3
- package/dist/modules/customers/setup.js.map +2 -2
- package/dist/modules/translations/api/[entityType]/[entityId]/route.js +46 -44
- package/dist/modules/translations/api/[entityType]/[entityId]/route.js.map +2 -2
- package/dist/modules/translations/api/context.js +10 -1
- package/dist/modules/translations/api/context.js.map +2 -2
- package/dist/modules/translations/commands/index.js +2 -0
- package/dist/modules/translations/commands/index.js.map +7 -0
- package/dist/modules/translations/commands/translations.js +160 -0
- package/dist/modules/translations/commands/translations.js.map +7 -0
- package/dist/modules/translations/index.js +1 -0
- package/dist/modules/translations/index.js.map +2 -2
- package/dist/modules/workflows/migrations/Migration20260222205305.js +14 -0
- package/dist/modules/workflows/migrations/Migration20260222205305.js.map +7 -0
- package/generated/entities/customer_deal/index.ts +2 -0
- package/generated/entities/customer_pipeline/index.ts +7 -0
- package/generated/entities/customer_pipeline_stage/index.ts +8 -0
- package/generated/entities.ids.generated.ts +2 -0
- package/generated/entity-fields-registry.ts +4 -0
- package/package.json +2 -2
- package/src/modules/customers/acl.ts +2 -0
- package/src/modules/customers/api/deals/[id]/route.ts +4 -0
- package/src/modules/customers/api/deals/route.ts +12 -0
- package/src/modules/customers/api/dictionaries/[kind]/route.ts +21 -1
- package/src/modules/customers/api/pipeline-stages/reorder/route.ts +71 -0
- package/src/modules/customers/api/pipeline-stages/route.ts +296 -0
- package/src/modules/customers/api/pipelines/route.ts +261 -0
- package/src/modules/customers/backend/config/customers/page.tsx +2 -0
- package/src/modules/customers/backend/config/customers/pipeline-stages/page.meta.ts +13 -0
- package/src/modules/customers/backend/config/customers/pipeline-stages/page.tsx +512 -0
- package/src/modules/customers/backend/customers/deals/[id]/page.tsx +21 -1
- package/src/modules/customers/backend/customers/deals/page.tsx +33 -1
- package/src/modules/customers/backend/customers/deals/pipeline/page.tsx +119 -79
- package/src/modules/customers/cli.ts +29 -1
- package/src/modules/customers/commands/deals.ts +44 -1
- package/src/modules/customers/commands/index.ts +2 -0
- package/src/modules/customers/commands/pipeline-stages.ts +156 -0
- package/src/modules/customers/commands/pipelines.ts +105 -0
- package/src/modules/customers/components/DictionarySettings.tsx +0 -5
- package/src/modules/customers/components/PipelineSettings.tsx +570 -0
- package/src/modules/customers/components/detail/DealForm.tsx +89 -11
- package/src/modules/customers/data/entities.ts +64 -0
- package/src/modules/customers/data/validators.ts +57 -0
- package/src/modules/customers/i18n/de.json +4 -0
- package/src/modules/customers/i18n/en.json +4 -0
- package/src/modules/customers/i18n/es.json +4 -0
- package/src/modules/customers/i18n/pl.json +5 -1
- package/src/modules/customers/migrations/Migration20260218191730.ts +84 -0
- package/src/modules/customers/setup.ts +5 -1
- package/src/modules/translations/api/[entityType]/[entityId]/route.ts +65 -60
- package/src/modules/translations/api/context.ts +12 -0
- package/src/modules/translations/commands/index.ts +1 -0
- package/src/modules/translations/commands/translations.ts +253 -0
- package/src/modules/translations/index.ts +1 -0
- package/src/modules/workflows/migrations/Migration20260222205305.ts +13 -0
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
import { NextResponse } from 'next/server'
|
|
2
|
+
import { z } from 'zod'
|
|
3
|
+
import { createRequestContainer } from '@open-mercato/shared/lib/di/container'
|
|
4
|
+
import { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'
|
|
5
|
+
import { resolveOrganizationScopeForRequest } from '@open-mercato/core/modules/directory/utils/organizationScope'
|
|
6
|
+
import type { EntityManager } from '@mikro-orm/postgresql'
|
|
7
|
+
import type { CommandRuntimeContext, CommandBus } from '@open-mercato/shared/lib/commands'
|
|
8
|
+
import { CustomerPipeline } from '../../data/entities'
|
|
9
|
+
import {
|
|
10
|
+
pipelineCreateSchema,
|
|
11
|
+
pipelineUpdateSchema,
|
|
12
|
+
pipelineDeleteSchema,
|
|
13
|
+
type PipelineCreateInput,
|
|
14
|
+
type PipelineUpdateInput,
|
|
15
|
+
type PipelineDeleteInput,
|
|
16
|
+
} from '../../data/validators'
|
|
17
|
+
import { withScopedPayload } from '../utils'
|
|
18
|
+
import { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'
|
|
19
|
+
import { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'
|
|
20
|
+
import { serializeOperationMetadata } from '@open-mercato/shared/lib/commands/operationMetadata'
|
|
21
|
+
import type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'
|
|
22
|
+
|
|
23
|
+
export const metadata = {
|
|
24
|
+
GET: { requireAuth: true, requireFeatures: ['customers.pipelines.view'] },
|
|
25
|
+
POST: { requireAuth: true, requireFeatures: ['customers.pipelines.manage'] },
|
|
26
|
+
PUT: { requireAuth: true, requireFeatures: ['customers.pipelines.manage'] },
|
|
27
|
+
DELETE: { requireAuth: true, requireFeatures: ['customers.pipelines.manage'] },
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
async function buildContext(
|
|
31
|
+
req: Request
|
|
32
|
+
): Promise<{ ctx: CommandRuntimeContext; organizationId: string | null; tenantId: string | null }> {
|
|
33
|
+
const container = await createRequestContainer()
|
|
34
|
+
const auth = await getAuthFromRequest(req)
|
|
35
|
+
const { translate } = await resolveTranslations()
|
|
36
|
+
if (!auth) throw new CrudHttpError(401, { error: translate('customers.errors.unauthorized', 'Unauthorized') })
|
|
37
|
+
const scope = await resolveOrganizationScopeForRequest({ container, auth, request: req })
|
|
38
|
+
const ctx: CommandRuntimeContext = {
|
|
39
|
+
container,
|
|
40
|
+
auth,
|
|
41
|
+
organizationScope: scope,
|
|
42
|
+
selectedOrganizationId: scope?.selectedId ?? auth.orgId ?? null,
|
|
43
|
+
organizationIds: scope?.filterIds ?? (auth.orgId ? [auth.orgId] : null),
|
|
44
|
+
request: req,
|
|
45
|
+
}
|
|
46
|
+
const organizationId = scope?.selectedId ?? auth.orgId ?? null
|
|
47
|
+
const tenantId = auth.tenantId ?? null
|
|
48
|
+
return { ctx, organizationId, tenantId }
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export async function GET(req: Request) {
|
|
52
|
+
try {
|
|
53
|
+
const { ctx, organizationId, tenantId } = await buildContext(req)
|
|
54
|
+
if (!organizationId || !tenantId) {
|
|
55
|
+
return NextResponse.json({ error: 'Organization and tenant context required' }, { status: 400 })
|
|
56
|
+
}
|
|
57
|
+
const url = new URL(req.url)
|
|
58
|
+
const isDefaultParam = url.searchParams.get('isDefault')
|
|
59
|
+
|
|
60
|
+
const em = (ctx.container.resolve('em') as EntityManager)
|
|
61
|
+
const where: Record<string, unknown> = { organizationId, tenantId }
|
|
62
|
+
if (isDefaultParam === 'true') where.isDefault = true
|
|
63
|
+
if (isDefaultParam === 'false') where.isDefault = false
|
|
64
|
+
|
|
65
|
+
const pipelines = await em.find(CustomerPipeline, where, { orderBy: { createdAt: 'ASC' } })
|
|
66
|
+
const items = pipelines.map((pipeline) => ({
|
|
67
|
+
id: pipeline.id,
|
|
68
|
+
name: pipeline.name,
|
|
69
|
+
isDefault: pipeline.isDefault,
|
|
70
|
+
organizationId: pipeline.organizationId,
|
|
71
|
+
tenantId: pipeline.tenantId,
|
|
72
|
+
createdAt: pipeline.createdAt,
|
|
73
|
+
updatedAt: pipeline.updatedAt,
|
|
74
|
+
}))
|
|
75
|
+
return NextResponse.json({ items, total: items.length })
|
|
76
|
+
} catch (err) {
|
|
77
|
+
if (err instanceof CrudHttpError) {
|
|
78
|
+
return NextResponse.json(err.body, { status: err.status })
|
|
79
|
+
}
|
|
80
|
+
console.error('customers.pipelines GET failed', err)
|
|
81
|
+
return NextResponse.json({ error: 'Failed to load pipelines' }, { status: 500 })
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export async function POST(req: Request) {
|
|
86
|
+
try {
|
|
87
|
+
const { ctx } = await buildContext(req)
|
|
88
|
+
const body = await req.json().catch(() => ({}))
|
|
89
|
+
const { translate } = await resolveTranslations()
|
|
90
|
+
const scoped = withScopedPayload(body, ctx, translate)
|
|
91
|
+
|
|
92
|
+
const commandBus = (ctx.container.resolve('commandBus') as CommandBus)
|
|
93
|
+
const { result, logEntry } = await commandBus.execute<PipelineCreateInput, { pipelineId: string }>(
|
|
94
|
+
'customers.pipelines.create',
|
|
95
|
+
{ input: pipelineCreateSchema.parse(scoped), ctx },
|
|
96
|
+
)
|
|
97
|
+
const response = NextResponse.json({ id: result?.pipelineId ?? null }, { status: 201 })
|
|
98
|
+
if (logEntry?.undoToken && logEntry?.id && logEntry?.commandId) {
|
|
99
|
+
response.headers.set(
|
|
100
|
+
'x-om-operation',
|
|
101
|
+
serializeOperationMetadata({
|
|
102
|
+
id: logEntry.id,
|
|
103
|
+
undoToken: logEntry.undoToken,
|
|
104
|
+
commandId: logEntry.commandId,
|
|
105
|
+
actionLabel: logEntry.actionLabel ?? null,
|
|
106
|
+
resourceKind: logEntry.resourceKind ?? 'customers.pipeline',
|
|
107
|
+
resourceId: logEntry.resourceId ?? result?.pipelineId ?? null,
|
|
108
|
+
executedAt: logEntry.createdAt instanceof Date ? logEntry.createdAt.toISOString() : undefined,
|
|
109
|
+
})
|
|
110
|
+
)
|
|
111
|
+
}
|
|
112
|
+
return response
|
|
113
|
+
} catch (err) {
|
|
114
|
+
if (err instanceof CrudHttpError) {
|
|
115
|
+
return NextResponse.json(err.body, { status: err.status })
|
|
116
|
+
}
|
|
117
|
+
console.error('customers.pipelines POST failed', err)
|
|
118
|
+
return NextResponse.json({ error: 'Failed to create pipeline' }, { status: 400 })
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export async function PUT(req: Request) {
|
|
123
|
+
try {
|
|
124
|
+
const { ctx } = await buildContext(req)
|
|
125
|
+
const body = await req.json().catch(() => ({}))
|
|
126
|
+
const { translate } = await resolveTranslations()
|
|
127
|
+
const scoped = withScopedPayload(body, ctx, translate)
|
|
128
|
+
|
|
129
|
+
const commandBus = (ctx.container.resolve('commandBus') as CommandBus)
|
|
130
|
+
const { logEntry } = await commandBus.execute<PipelineUpdateInput, void>(
|
|
131
|
+
'customers.pipelines.update',
|
|
132
|
+
{ input: pipelineUpdateSchema.parse(scoped), ctx },
|
|
133
|
+
)
|
|
134
|
+
const response = NextResponse.json({ ok: true })
|
|
135
|
+
if (logEntry?.undoToken && logEntry?.id && logEntry?.commandId) {
|
|
136
|
+
response.headers.set(
|
|
137
|
+
'x-om-operation',
|
|
138
|
+
serializeOperationMetadata({
|
|
139
|
+
id: logEntry.id,
|
|
140
|
+
undoToken: logEntry.undoToken,
|
|
141
|
+
commandId: logEntry.commandId,
|
|
142
|
+
actionLabel: logEntry.actionLabel ?? null,
|
|
143
|
+
resourceKind: logEntry.resourceKind ?? 'customers.pipeline',
|
|
144
|
+
resourceId: logEntry.resourceId ?? null,
|
|
145
|
+
executedAt: logEntry.createdAt instanceof Date ? logEntry.createdAt.toISOString() : undefined,
|
|
146
|
+
})
|
|
147
|
+
)
|
|
148
|
+
}
|
|
149
|
+
return response
|
|
150
|
+
} catch (err) {
|
|
151
|
+
if (err instanceof CrudHttpError) {
|
|
152
|
+
return NextResponse.json(err.body, { status: err.status })
|
|
153
|
+
}
|
|
154
|
+
console.error('customers.pipelines PUT failed', err)
|
|
155
|
+
return NextResponse.json({ error: 'Failed to update pipeline' }, { status: 400 })
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export async function DELETE(req: Request) {
|
|
160
|
+
try {
|
|
161
|
+
const { ctx } = await buildContext(req)
|
|
162
|
+
const body = await req.json().catch(() => ({}))
|
|
163
|
+
const { translate } = await resolveTranslations()
|
|
164
|
+
const scoped = withScopedPayload(body, ctx, translate)
|
|
165
|
+
|
|
166
|
+
const commandBus = (ctx.container.resolve('commandBus') as CommandBus)
|
|
167
|
+
await commandBus.execute<PipelineDeleteInput, void>(
|
|
168
|
+
'customers.pipelines.delete',
|
|
169
|
+
{ input: pipelineDeleteSchema.parse(scoped), ctx },
|
|
170
|
+
)
|
|
171
|
+
return NextResponse.json({ ok: true })
|
|
172
|
+
} catch (err) {
|
|
173
|
+
if (err instanceof CrudHttpError) {
|
|
174
|
+
return NextResponse.json(err.body, { status: err.status })
|
|
175
|
+
}
|
|
176
|
+
console.error('customers.pipelines DELETE failed', err)
|
|
177
|
+
return NextResponse.json({ error: 'Failed to delete pipeline' }, { status: 400 })
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
const pipelineItemSchema = z.object({
|
|
182
|
+
id: z.string().uuid(),
|
|
183
|
+
name: z.string(),
|
|
184
|
+
isDefault: z.boolean(),
|
|
185
|
+
organizationId: z.string().uuid(),
|
|
186
|
+
tenantId: z.string().uuid(),
|
|
187
|
+
createdAt: z.date(),
|
|
188
|
+
updatedAt: z.date(),
|
|
189
|
+
})
|
|
190
|
+
|
|
191
|
+
const pipelineListResponseSchema = z.object({
|
|
192
|
+
items: z.array(pipelineItemSchema),
|
|
193
|
+
total: z.number(),
|
|
194
|
+
})
|
|
195
|
+
|
|
196
|
+
const pipelineCreateResponseSchema = z.object({
|
|
197
|
+
id: z.string().uuid().nullable(),
|
|
198
|
+
})
|
|
199
|
+
|
|
200
|
+
const pipelineOkResponseSchema = z.object({
|
|
201
|
+
ok: z.boolean(),
|
|
202
|
+
})
|
|
203
|
+
|
|
204
|
+
const pipelineErrorSchema = z.object({
|
|
205
|
+
error: z.string(),
|
|
206
|
+
})
|
|
207
|
+
|
|
208
|
+
export const openApi: OpenApiRouteDoc = {
|
|
209
|
+
tag: 'Customers',
|
|
210
|
+
summary: 'Manage customer pipelines',
|
|
211
|
+
methods: {
|
|
212
|
+
GET: {
|
|
213
|
+
summary: 'List pipelines',
|
|
214
|
+
description: 'Returns a list of pipelines scoped to the authenticated organization.',
|
|
215
|
+
query: z.object({ isDefault: z.string().optional() }),
|
|
216
|
+
responses: [
|
|
217
|
+
{ status: 200, description: 'Pipeline list', schema: pipelineListResponseSchema },
|
|
218
|
+
],
|
|
219
|
+
errors: [
|
|
220
|
+
{ status: 401, description: 'Unauthorized', schema: pipelineErrorSchema },
|
|
221
|
+
{ status: 400, description: 'Invalid request', schema: pipelineErrorSchema },
|
|
222
|
+
],
|
|
223
|
+
},
|
|
224
|
+
POST: {
|
|
225
|
+
summary: 'Create pipeline',
|
|
226
|
+
description: 'Creates a new pipeline within the authenticated organization.',
|
|
227
|
+
requestBody: { contentType: 'application/json', schema: pipelineCreateSchema },
|
|
228
|
+
responses: [
|
|
229
|
+
{ status: 201, description: 'Pipeline created', schema: pipelineCreateResponseSchema },
|
|
230
|
+
],
|
|
231
|
+
errors: [
|
|
232
|
+
{ status: 400, description: 'Validation failed', schema: pipelineErrorSchema },
|
|
233
|
+
{ status: 401, description: 'Unauthorized', schema: pipelineErrorSchema },
|
|
234
|
+
],
|
|
235
|
+
},
|
|
236
|
+
PUT: {
|
|
237
|
+
summary: 'Update pipeline',
|
|
238
|
+
description: 'Updates an existing pipeline.',
|
|
239
|
+
requestBody: { contentType: 'application/json', schema: pipelineUpdateSchema },
|
|
240
|
+
responses: [
|
|
241
|
+
{ status: 200, description: 'Pipeline updated', schema: pipelineOkResponseSchema },
|
|
242
|
+
],
|
|
243
|
+
errors: [
|
|
244
|
+
{ status: 400, description: 'Validation failed', schema: pipelineErrorSchema },
|
|
245
|
+
{ status: 404, description: 'Pipeline not found', schema: pipelineErrorSchema },
|
|
246
|
+
],
|
|
247
|
+
},
|
|
248
|
+
DELETE: {
|
|
249
|
+
summary: 'Delete pipeline',
|
|
250
|
+
description: 'Deletes a pipeline. Returns 409 if active deals exist.',
|
|
251
|
+
requestBody: { contentType: 'application/json', schema: pipelineDeleteSchema },
|
|
252
|
+
responses: [
|
|
253
|
+
{ status: 200, description: 'Pipeline deleted', schema: pipelineOkResponseSchema },
|
|
254
|
+
],
|
|
255
|
+
errors: [
|
|
256
|
+
{ status: 409, description: 'Pipeline has active deals', schema: pipelineErrorSchema },
|
|
257
|
+
{ status: 404, description: 'Pipeline not found', schema: pipelineErrorSchema },
|
|
258
|
+
],
|
|
259
|
+
},
|
|
260
|
+
},
|
|
261
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Page, PageBody } from '@open-mercato/ui/backend/Page'
|
|
2
2
|
import DictionarySettings from '../../../components/DictionarySettings'
|
|
3
3
|
import AddressFormatSettings from '../../../components/AddressFormatSettings'
|
|
4
|
+
import PipelineSettings from '../../../components/PipelineSettings'
|
|
4
5
|
|
|
5
6
|
export default function CustomersConfigurationPage() {
|
|
6
7
|
return (
|
|
@@ -8,6 +9,7 @@ export default function CustomersConfigurationPage() {
|
|
|
8
9
|
<PageBody>
|
|
9
10
|
<div className="space-y-8">
|
|
10
11
|
<AddressFormatSettings />
|
|
12
|
+
<PipelineSettings />
|
|
11
13
|
<DictionarySettings />
|
|
12
14
|
</div>
|
|
13
15
|
</PageBody>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export const metadata = {
|
|
2
|
+
requireAuth: true,
|
|
3
|
+
requireFeatures: ['customers.pipelines.manage'],
|
|
4
|
+
pageTitle: 'Pipeline stages',
|
|
5
|
+
pageTitleKey: 'customers.config.nav.pipelineStages',
|
|
6
|
+
pageGroup: 'Module Configs',
|
|
7
|
+
pageGroupKey: 'settings.sections.moduleConfigs',
|
|
8
|
+
pageOrder: 4,
|
|
9
|
+
pageContext: 'settings' as const,
|
|
10
|
+
breadcrumb: [
|
|
11
|
+
{ label: 'Pipeline stages', labelKey: 'customers.config.nav.pipelineStages' },
|
|
12
|
+
],
|
|
13
|
+
} as const
|