@open-mercato/core 0.6.6-develop.6204.1.30b1f58642 → 0.6.6-develop.6227.1.2695efff30
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +1 -1
- package/dist/modules/currencies/backend/currencies/[id]/page.js +33 -8
- package/dist/modules/currencies/backend/currencies/[id]/page.js.map +2 -2
- package/dist/modules/currencies/backend/currencies/page.js +63 -27
- package/dist/modules/currencies/backend/currencies/page.js.map +2 -2
- package/dist/modules/currencies/backend/exchange-rates/page.js +35 -14
- package/dist/modules/currencies/backend/exchange-rates/page.js.map +2 -2
- package/dist/modules/currencies/components/CurrencyFetchingConfig.js +104 -41
- package/dist/modules/currencies/components/CurrencyFetchingConfig.js.map +2 -2
- package/dist/modules/customer_accounts/api/admin/roles/[id]/acl.js +57 -8
- package/dist/modules/customer_accounts/api/admin/roles/[id]/acl.js.map +2 -2
- package/dist/modules/customer_accounts/backend/customer_accounts/roles/[id]/page.js +5 -2
- package/dist/modules/customer_accounts/backend/customer_accounts/roles/[id]/page.js.map +2 -2
- package/dist/modules/customer_accounts/backend/customer_accounts/roles/page.js +30 -14
- package/dist/modules/customer_accounts/backend/customer_accounts/roles/page.js.map +2 -2
- package/dist/modules/customer_accounts/widgets/injection/account-status/widget.client.js +29 -19
- package/dist/modules/customer_accounts/widgets/injection/account-status/widget.client.js.map +2 -2
- package/dist/modules/customers/backend/config/customers/pipeline-stages/page.js +166 -64
- package/dist/modules/customers/backend/config/customers/pipeline-stages/page.js.map +2 -2
- package/dist/modules/customers/components/AddressFormatSettings.js +31 -16
- package/dist/modules/customers/components/AddressFormatSettings.js.map +2 -2
- package/dist/modules/customers/components/DictionarySettings.js +57 -31
- package/dist/modules/customers/components/DictionarySettings.js.map +2 -2
- package/dist/modules/customers/components/PipelineSettings.js +156 -76
- package/dist/modules/customers/components/PipelineSettings.js.map +2 -2
- package/dist/modules/customers/components/calendar/AgendaList.js +10 -4
- package/dist/modules/customers/components/calendar/AgendaList.js.map +2 -2
- package/dist/modules/customers/components/calendar/CalendarSettingsModal.js +18 -2
- package/dist/modules/customers/components/calendar/CalendarSettingsModal.js.map +2 -2
- package/dist/modules/customers/components/calendar/MonthGrid.js +5 -2
- package/dist/modules/customers/components/calendar/MonthGrid.js.map +2 -2
- package/dist/modules/customers/components/calendar/editor/PeopleField.js +6 -0
- package/dist/modules/customers/components/calendar/editor/PeopleField.js.map +2 -2
- package/dist/modules/customers/components/calendar/editor/RelatedToField.js +2 -0
- package/dist/modules/customers/components/calendar/editor/RelatedToField.js.map +2 -2
- package/dist/modules/customers/components/calendar/editor/ScheduleSection.js +4 -1
- package/dist/modules/customers/components/calendar/editor/ScheduleSection.js.map +2 -2
- package/dist/modules/customers/lib/calendar/labels.js +33 -0
- package/dist/modules/customers/lib/calendar/labels.js.map +7 -0
- package/dist/modules/dashboards/components/WidgetVisibilityEditor.js +32 -11
- package/dist/modules/dashboards/components/WidgetVisibilityEditor.js.map +2 -2
- package/dist/modules/data_sync/api/mappings/[id]/route.js +59 -0
- package/dist/modules/data_sync/api/mappings/[id]/route.js.map +2 -2
- package/dist/modules/data_sync/api/mappings/route.js +44 -0
- package/dist/modules/data_sync/api/mappings/route.js.map +2 -2
- package/dist/modules/data_sync/api/run.js +31 -0
- package/dist/modules/data_sync/api/run.js.map +2 -2
- package/dist/modules/data_sync/api/runs/[id]/cancel.js +31 -0
- package/dist/modules/data_sync/api/runs/[id]/cancel.js.map +2 -2
- package/dist/modules/data_sync/api/runs/[id]/retry.js +31 -0
- package/dist/modules/data_sync/api/runs/[id]/retry.js.map +2 -2
- package/dist/modules/data_sync/api/schedules/[id]/route.js +59 -3
- package/dist/modules/data_sync/api/schedules/[id]/route.js.map +2 -2
- package/dist/modules/data_sync/api/schedules/route.js +33 -4
- package/dist/modules/data_sync/api/schedules/route.js.map +2 -2
- package/dist/modules/dictionaries/components/DictionariesManager.js +7 -4
- package/dist/modules/dictionaries/components/DictionariesManager.js.map +2 -2
- package/dist/modules/directory/api/organization-branding/route.js +54 -2
- package/dist/modules/directory/api/organization-branding/route.js.map +2 -2
- package/dist/modules/directory/backend/directory/branding/page.js +15 -9
- package/dist/modules/directory/backend/directory/branding/page.js.map +2 -2
- package/dist/modules/directory/backend/directory/organizations/page.js +28 -9
- package/dist/modules/directory/backend/directory/organizations/page.js.map +2 -2
- package/dist/modules/directory/backend/directory/tenants/page.js +29 -13
- package/dist/modules/directory/backend/directory/tenants/page.js.map +2 -2
- package/dist/modules/entities/api/definitions.batch.js +15 -0
- package/dist/modules/entities/api/definitions.batch.js.map +2 -2
- package/dist/modules/entities/api/definitions.js +26 -0
- package/dist/modules/entities/api/definitions.js.map +2 -2
- package/dist/modules/entities/api/definitions.mutation-guard.js +57 -0
- package/dist/modules/entities/api/definitions.mutation-guard.js.map +7 -0
- package/dist/modules/entities/api/definitions.restore.js +15 -0
- package/dist/modules/entities/api/definitions.restore.js.map +2 -2
- package/dist/modules/entities/api/entities.js +31 -3
- package/dist/modules/entities/api/entities.js.map +2 -2
- package/dist/modules/entities/api/records.js +18 -0
- package/dist/modules/entities/api/records.js.map +2 -2
- package/dist/modules/entities/backend/entities/user/[entityId]/records/page.js +28 -10
- package/dist/modules/entities/backend/entities/user/[entityId]/records/page.js.map +2 -2
- package/dist/modules/feature_toggles/api/overrides/route.js +38 -1
- package/dist/modules/feature_toggles/api/overrides/route.js.map +2 -2
- package/dist/modules/inbox_ops/api/emails/[id]/route.js +30 -0
- package/dist/modules/inbox_ops/api/emails/[id]/route.js.map +2 -2
- package/dist/modules/inbox_ops/api/proposals/[id]/accept-all/route.js +30 -0
- package/dist/modules/inbox_ops/api/proposals/[id]/accept-all/route.js.map +2 -2
- package/dist/modules/inbox_ops/api/proposals/[id]/actions/[actionId]/route.js +31 -0
- package/dist/modules/inbox_ops/api/proposals/[id]/actions/[actionId]/route.js.map +2 -2
- package/dist/modules/inbox_ops/api/proposals/[id]/categorize/route.js +31 -0
- package/dist/modules/inbox_ops/api/proposals/[id]/categorize/route.js.map +2 -2
- package/dist/modules/messages/components/message-detail/hooks/useMessageDetailsActions.js +68 -39
- package/dist/modules/messages/components/message-detail/hooks/useMessageDetailsActions.js.map +2 -2
- package/dist/modules/notifications/frontend/NotificationSettingsPageClient.js +14 -4
- package/dist/modules/notifications/frontend/NotificationSettingsPageClient.js.map +2 -2
- package/dist/modules/payment_gateways/api/cancel/route.js +37 -0
- package/dist/modules/payment_gateways/api/cancel/route.js.map +2 -2
- package/dist/modules/payment_gateways/api/capture/route.js +37 -0
- package/dist/modules/payment_gateways/api/capture/route.js.map +2 -2
- package/dist/modules/payment_gateways/api/guards.js +31 -0
- package/dist/modules/payment_gateways/api/guards.js.map +7 -0
- package/dist/modules/payment_gateways/api/refund/route.js +37 -0
- package/dist/modules/payment_gateways/api/refund/route.js.map +2 -2
- package/dist/modules/payment_gateways/api/sessions/route.js +37 -0
- package/dist/modules/payment_gateways/api/sessions/route.js.map +2 -2
- package/dist/modules/planner/api/availability-date-specific.js +11 -1
- package/dist/modules/planner/api/availability-date-specific.js.map +2 -2
- package/dist/modules/planner/backend/planner/availability-rulesets/page.js +20 -3
- package/dist/modules/planner/backend/planner/availability-rulesets/page.js.map +2 -2
- package/dist/modules/planner/commands/availability-date-specific.js +16 -0
- package/dist/modules/planner/commands/availability-date-specific.js.map +2 -2
- package/dist/modules/planner/components/AvailabilityRulesEditor.js +109 -40
- package/dist/modules/planner/components/AvailabilityRulesEditor.js.map +2 -2
- package/dist/modules/query_index/api/purge.js +35 -3
- package/dist/modules/query_index/api/purge.js.map +2 -2
- package/dist/modules/query_index/api/reindex.js +41 -3
- package/dist/modules/query_index/api/reindex.js.map +2 -2
- package/dist/modules/query_index/components/QueryIndexesTable.js +57 -24
- package/dist/modules/query_index/components/QueryIndexesTable.js.map +2 -2
- package/dist/modules/shipping_carriers/api/shipments/route.js +31 -0
- package/dist/modules/shipping_carriers/api/shipments/route.js.map +2 -2
- package/dist/modules/shipping_carriers/api/tracking/refresh/route.js +55 -0
- package/dist/modules/shipping_carriers/api/tracking/refresh/route.js.map +7 -0
- package/dist/modules/shipping_carriers/data/validators.js +6 -1
- package/dist/modules/shipping_carriers/data/validators.js.map +2 -2
- package/dist/modules/shipping_carriers/lib/shipment-wizard/hooks/useShipmentWizard.js +28 -8
- package/dist/modules/shipping_carriers/lib/shipment-wizard/hooks/useShipmentWizard.js.map +2 -2
- package/dist/modules/shipping_carriers/lib/shipping-service.js +37 -7
- package/dist/modules/shipping_carriers/lib/shipping-service.js.map +2 -2
- package/dist/modules/shipping_carriers/workers/status-poller.js +1 -1
- package/dist/modules/shipping_carriers/workers/status-poller.js.map +2 -2
- package/dist/modules/translations/components/TranslationManager.js +49 -20
- package/dist/modules/translations/components/TranslationManager.js.map +2 -2
- package/package.json +7 -7
- package/src/modules/currencies/backend/currencies/[id]/page.tsx +40 -10
- package/src/modules/currencies/backend/currencies/page.tsx +68 -29
- package/src/modules/currencies/backend/exchange-rates/page.tsx +40 -15
- package/src/modules/currencies/components/CurrencyFetchingConfig.tsx +110 -41
- package/src/modules/customer_accounts/api/admin/roles/[id]/acl.ts +69 -7
- package/src/modules/customer_accounts/backend/customer_accounts/roles/[id]/page.tsx +16 -8
- package/src/modules/customer_accounts/backend/customer_accounts/roles/page.tsx +32 -14
- package/src/modules/customer_accounts/widgets/injection/account-status/widget.client.tsx +32 -20
- package/src/modules/customers/backend/config/customers/pipeline-stages/page.tsx +171 -64
- package/src/modules/customers/components/AddressFormatSettings.tsx +39 -19
- package/src/modules/customers/components/DictionarySettings.tsx +63 -29
- package/src/modules/customers/components/PipelineSettings.tsx +165 -80
- package/src/modules/customers/components/calendar/AgendaList.tsx +13 -8
- package/src/modules/customers/components/calendar/CalendarSettingsModal.tsx +16 -2
- package/src/modules/customers/components/calendar/MonthGrid.tsx +5 -2
- package/src/modules/customers/components/calendar/editor/PeopleField.tsx +6 -0
- package/src/modules/customers/components/calendar/editor/RelatedToField.tsx +2 -0
- package/src/modules/customers/components/calendar/editor/ScheduleSection.tsx +7 -0
- package/src/modules/customers/i18n/de.json +11 -7
- package/src/modules/customers/i18n/en.json +6 -2
- package/src/modules/customers/i18n/es.json +11 -7
- package/src/modules/customers/i18n/pl.json +11 -7
- package/src/modules/customers/lib/calendar/labels.ts +42 -0
- package/src/modules/dashboards/components/WidgetVisibilityEditor.tsx +39 -11
- package/src/modules/data_sync/api/mappings/[id]/route.ts +63 -0
- package/src/modules/data_sync/api/mappings/route.ts +48 -0
- package/src/modules/data_sync/api/run.ts +33 -0
- package/src/modules/data_sync/api/runs/[id]/cancel.ts +34 -0
- package/src/modules/data_sync/api/runs/[id]/retry.ts +33 -0
- package/src/modules/data_sync/api/schedules/[id]/route.ts +64 -2
- package/src/modules/data_sync/api/schedules/route.ts +36 -4
- package/src/modules/dictionaries/components/DictionariesManager.tsx +7 -4
- package/src/modules/directory/api/organization-branding/route.ts +61 -0
- package/src/modules/directory/backend/directory/branding/page.tsx +16 -10
- package/src/modules/directory/backend/directory/organizations/page.tsx +35 -8
- package/src/modules/directory/backend/directory/tenants/page.tsx +37 -13
- package/src/modules/entities/api/definitions.batch.ts +17 -0
- package/src/modules/entities/api/definitions.mutation-guard.ts +80 -0
- package/src/modules/entities/api/definitions.restore.ts +17 -0
- package/src/modules/entities/api/definitions.ts +30 -0
- package/src/modules/entities/api/entities.ts +35 -3
- package/src/modules/entities/api/records.ts +20 -0
- package/src/modules/entities/backend/entities/user/[entityId]/records/page.tsx +33 -10
- package/src/modules/feature_toggles/api/overrides/route.ts +44 -1
- package/src/modules/inbox_ops/api/emails/[id]/route.ts +32 -0
- package/src/modules/inbox_ops/api/proposals/[id]/accept-all/route.ts +32 -0
- package/src/modules/inbox_ops/api/proposals/[id]/actions/[actionId]/route.ts +33 -0
- package/src/modules/inbox_ops/api/proposals/[id]/categorize/route.ts +33 -0
- package/src/modules/messages/components/message-detail/hooks/useMessageDetailsActions.ts +80 -42
- package/src/modules/notifications/frontend/NotificationSettingsPageClient.tsx +21 -4
- package/src/modules/payment_gateways/api/cancel/route.ts +39 -0
- package/src/modules/payment_gateways/api/capture/route.ts +39 -0
- package/src/modules/payment_gateways/api/guards.ts +59 -0
- package/src/modules/payment_gateways/api/refund/route.ts +39 -0
- package/src/modules/payment_gateways/api/sessions/route.ts +40 -0
- package/src/modules/planner/api/availability-date-specific.ts +10 -0
- package/src/modules/planner/backend/planner/availability-rulesets/page.tsx +26 -5
- package/src/modules/planner/commands/availability-date-specific.ts +24 -0
- package/src/modules/planner/components/AvailabilityRulesEditor.tsx +122 -41
- package/src/modules/query_index/api/purge.ts +37 -3
- package/src/modules/query_index/api/reindex.ts +43 -3
- package/src/modules/query_index/components/QueryIndexesTable.tsx +66 -24
- package/src/modules/shipping_carriers/api/shipments/route.ts +31 -0
- package/src/modules/shipping_carriers/api/tracking/refresh/route.ts +53 -0
- package/src/modules/shipping_carriers/data/validators.ts +5 -0
- package/src/modules/shipping_carriers/lib/shipment-wizard/hooks/useShipmentWizard.ts +29 -8
- package/src/modules/shipping_carriers/lib/shipping-service.ts +43 -7
- package/src/modules/shipping_carriers/workers/status-poller.ts +1 -1
- package/src/modules/translations/components/TranslationManager.tsx +65 -21
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../src/modules/data_sync/api/runs/%5Bid%5D/cancel.ts"],
|
|
4
|
-
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport type { IntegrationLogService } from '../../../../integrations/lib/log-service'\nimport type { IntegrationStateService } from '../../../../integrations/lib/state-service'\nimport type { ProgressService } from '../../../../progress/lib/progressService'\nimport type { SyncRunService } from '../../../lib/sync-run-service'\n\nconst paramsSchema = z.object({ id: z.string().uuid() })\n\nexport const metadata = {\n POST: { requireAuth: true, requireFeatures: ['data_sync.run'] },\n}\n\nexport const openApi = {\n tags: ['DataSync'],\n summary: 'Cancel a running sync',\n}\n\nexport async function POST(req: Request, ctx: { params?: Promise<{ id?: string }> | { id?: string } }) {\n const auth = await getAuthFromRequest(req)\n if (!auth?.tenantId || !auth.orgId) {\n return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n }\n\n const rawParams = (ctx.params && typeof (ctx.params as Promise<unknown>).then === 'function')\n ? await (ctx.params as Promise<{ id?: string }>)\n : (ctx.params as { id?: string } | undefined)\n\n const parsed = paramsSchema.safeParse(rawParams)\n if (!parsed.success) {\n return NextResponse.json({ error: 'Invalid run id' }, { status: 400 })\n }\n\n const container = await createRequestContainer()\n const syncRunService = container.resolve('dataSyncRunService') as SyncRunService\n const progressService = container.resolve('progressService') as ProgressService\n const integrationLogService = container.resolve('integrationLogService') as IntegrationLogService\n const integrationStateService = container.resolve('integrationStateService') as IntegrationStateService\n const scope = { organizationId: auth.orgId as string, tenantId: auth.tenantId }\n\n const run = await syncRunService.getRun(parsed.data.id, scope)\n if (!run) {\n return NextResponse.json({ error: 'Run not found' }, { status: 404 })\n }\n if (run.status !== 'running' && run.status !== 'pending') {\n return NextResponse.json({ error: 'Only pending or running runs can be cancelled' }, { status: 409 })\n }\n\n const progressCtx = {\n tenantId: auth.tenantId,\n organizationId: auth.orgId,\n userId: auth.sub,\n }\n\n if (run.progressJobId) {\n try {\n await progressService.markCancelled(run.progressJobId, progressCtx)\n } catch (error) {\n const job = await progressService.getJob(run.progressJobId, progressCtx)\n const cancelRequested = job && (job.status === 'running' || job.status === 'cancelled')\n ? await progressService.isCancellationRequested(run.progressJobId, progressCtx.tenantId)\n : false\n\n if (job?.status !== 'cancelled' && !cancelRequested) {\n throw error\n }\n }\n }\n\n await syncRunService.markStatus(run.id, 'cancelled', scope)\n await integrationStateService.upsert(run.integrationId, {\n lastHealthStatus: 'degraded',\n lastHealthCheckedAt: new Date(),\n }, scope)\n await integrationLogService.write({\n integrationId: run.integrationId,\n runId: run.id,\n level: 'warn',\n message: 'Sync run cancelled',\n payload: {\n operationalStatus: 'cancelled',\n summary: 'The sync run was cancelled before completion.',\n },\n }, scope)\n return NextResponse.json({ ok: true })\n}\n"],
|
|
5
|
-
"mappings": "AAAA,SAAS,oBAAoB;AAC7B,SAAS,SAAS;AAClB,SAAS,0BAA0B;AACnC,SAAS,8BAA8B;
|
|
4
|
+
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport type { IntegrationLogService } from '../../../../integrations/lib/log-service'\nimport type { IntegrationStateService } from '../../../../integrations/lib/state-service'\nimport type { ProgressService } from '../../../../progress/lib/progressService'\nimport type { SyncRunService } from '../../../lib/sync-run-service'\nimport {\n runCrudMutationGuardAfterSuccess,\n validateCrudMutationGuard,\n} from '@open-mercato/shared/lib/crud/mutation-guard'\n\nconst paramsSchema = z.object({ id: z.string().uuid() })\n\nexport const metadata = {\n POST: { requireAuth: true, requireFeatures: ['data_sync.run'] },\n}\n\nexport const openApi = {\n tags: ['DataSync'],\n summary: 'Cancel a running sync',\n}\n\nexport async function POST(req: Request, ctx: { params?: Promise<{ id?: string }> | { id?: string } }) {\n const auth = await getAuthFromRequest(req)\n if (!auth?.tenantId || !auth.orgId) {\n return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n }\n\n const rawParams = (ctx.params && typeof (ctx.params as Promise<unknown>).then === 'function')\n ? await (ctx.params as Promise<{ id?: string }>)\n : (ctx.params as { id?: string } | undefined)\n\n const parsed = paramsSchema.safeParse(rawParams)\n if (!parsed.success) {\n return NextResponse.json({ error: 'Invalid run id' }, { status: 400 })\n }\n\n const container = await createRequestContainer()\n const syncRunService = container.resolve('dataSyncRunService') as SyncRunService\n const progressService = container.resolve('progressService') as ProgressService\n const integrationLogService = container.resolve('integrationLogService') as IntegrationLogService\n const integrationStateService = container.resolve('integrationStateService') as IntegrationStateService\n const scope = { organizationId: auth.orgId as string, tenantId: auth.tenantId }\n\n const run = await syncRunService.getRun(parsed.data.id, scope)\n if (!run) {\n return NextResponse.json({ error: 'Run not found' }, { status: 404 })\n }\n if (run.status !== 'running' && run.status !== 'pending') {\n return NextResponse.json({ error: 'Only pending or running runs can be cancelled' }, { status: 409 })\n }\n\n const guardResult = await validateCrudMutationGuard(container, {\n tenantId: auth.tenantId,\n organizationId: scope.organizationId,\n userId: auth.sub,\n resourceKind: 'data_sync.run',\n resourceId: run.id,\n operation: 'custom',\n requestMethod: req.method,\n requestHeaders: req.headers,\n mutationPayload: { action: 'cancel' },\n })\n if (guardResult && !guardResult.ok) {\n return NextResponse.json(guardResult.body, { status: guardResult.status })\n }\n\n const progressCtx = {\n tenantId: auth.tenantId,\n organizationId: auth.orgId,\n userId: auth.sub,\n }\n\n if (run.progressJobId) {\n try {\n await progressService.markCancelled(run.progressJobId, progressCtx)\n } catch (error) {\n const job = await progressService.getJob(run.progressJobId, progressCtx)\n const cancelRequested = job && (job.status === 'running' || job.status === 'cancelled')\n ? await progressService.isCancellationRequested(run.progressJobId, progressCtx.tenantId)\n : false\n\n if (job?.status !== 'cancelled' && !cancelRequested) {\n throw error\n }\n }\n }\n\n await syncRunService.markStatus(run.id, 'cancelled', scope)\n await integrationStateService.upsert(run.integrationId, {\n lastHealthStatus: 'degraded',\n lastHealthCheckedAt: new Date(),\n }, scope)\n await integrationLogService.write({\n integrationId: run.integrationId,\n runId: run.id,\n level: 'warn',\n message: 'Sync run cancelled',\n payload: {\n operationalStatus: 'cancelled',\n summary: 'The sync run was cancelled before completion.',\n },\n }, scope)\n\n if (guardResult?.ok && guardResult.shouldRunAfterSuccess) {\n await runCrudMutationGuardAfterSuccess(container, {\n tenantId: auth.tenantId,\n organizationId: scope.organizationId,\n userId: auth.sub,\n resourceKind: 'data_sync.run',\n resourceId: run.id,\n operation: 'custom',\n requestMethod: req.method,\n requestHeaders: req.headers,\n metadata: guardResult.metadata ?? null,\n })\n }\n\n return NextResponse.json({ ok: true })\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -5,6 +5,10 @@ import { createRequestContainer } from "@open-mercato/shared/lib/di/container";
|
|
|
5
5
|
import { readJsonSafe } from "@open-mercato/shared/lib/http/readJsonSafe";
|
|
6
6
|
import { retrySyncSchema } from "../../../data/validators.js";
|
|
7
7
|
import { startDataSyncRun } from "../../../lib/start-run.js";
|
|
8
|
+
import {
|
|
9
|
+
runCrudMutationGuardAfterSuccess,
|
|
10
|
+
validateCrudMutationGuard
|
|
11
|
+
} from "@open-mercato/shared/lib/crud/mutation-guard";
|
|
8
12
|
const paramsSchema = z.object({ id: z.string().uuid() });
|
|
9
13
|
const metadata = {
|
|
10
14
|
POST: { requireAuth: true, requireFeatures: ["data_sync.run"] }
|
|
@@ -48,6 +52,20 @@ async function POST(req, ctx) {
|
|
|
48
52
|
if (overlap) {
|
|
49
53
|
return NextResponse.json({ error: "A sync run is already in progress for this integration and entity direction" }, { status: 409 });
|
|
50
54
|
}
|
|
55
|
+
const guardResult = await validateCrudMutationGuard(container, {
|
|
56
|
+
tenantId: auth.tenantId,
|
|
57
|
+
organizationId: scope.organizationId,
|
|
58
|
+
userId: auth.sub,
|
|
59
|
+
resourceKind: "data_sync.run",
|
|
60
|
+
resourceId: previous.id,
|
|
61
|
+
operation: "custom",
|
|
62
|
+
requestMethod: req.method,
|
|
63
|
+
requestHeaders: req.headers,
|
|
64
|
+
mutationPayload: { action: "retry", ...parsedBody.data }
|
|
65
|
+
});
|
|
66
|
+
if (guardResult && !guardResult.ok) {
|
|
67
|
+
return NextResponse.json(guardResult.body, { status: guardResult.status });
|
|
68
|
+
}
|
|
51
69
|
const cursor = parsedBody.data.fromBeginning ? null : previous.cursor ?? await syncRunService.resolveCursor(previous.integrationId, previous.entityType, previous.direction, scope);
|
|
52
70
|
const { run, progressJob } = await startDataSyncRun({
|
|
53
71
|
syncRunService,
|
|
@@ -68,6 +86,19 @@ async function POST(req, ctx) {
|
|
|
68
86
|
}
|
|
69
87
|
}
|
|
70
88
|
});
|
|
89
|
+
if (guardResult?.ok && guardResult.shouldRunAfterSuccess) {
|
|
90
|
+
await runCrudMutationGuardAfterSuccess(container, {
|
|
91
|
+
tenantId: auth.tenantId,
|
|
92
|
+
organizationId: scope.organizationId,
|
|
93
|
+
userId: auth.sub,
|
|
94
|
+
resourceKind: "data_sync.run",
|
|
95
|
+
resourceId: run.id,
|
|
96
|
+
operation: "custom",
|
|
97
|
+
requestMethod: req.method,
|
|
98
|
+
requestHeaders: req.headers,
|
|
99
|
+
metadata: guardResult.metadata ?? null
|
|
100
|
+
});
|
|
101
|
+
}
|
|
71
102
|
return NextResponse.json({ id: run.id, progressJobId: progressJob?.id ?? null }, { status: 201 });
|
|
72
103
|
}
|
|
73
104
|
export {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../src/modules/data_sync/api/runs/%5Bid%5D/retry.ts"],
|
|
4
|
-
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\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 type { ProgressService } from '../../../../progress/lib/progressService'\nimport type { SyncRunService } from '../../../lib/sync-run-service'\nimport { retrySyncSchema } from '../../../data/validators'\nimport { startDataSyncRun } from '../../../lib/start-run'\n\nconst paramsSchema = z.object({ id: z.string().uuid() })\n\nexport const metadata = {\n POST: { requireAuth: true, requireFeatures: ['data_sync.run'] },\n}\n\nexport const openApi = {\n tags: ['DataSync'],\n summary: 'Retry a failed sync run',\n}\n\nexport async function POST(req: Request, ctx: { params?: Promise<{ id?: string }> | { id?: string } }) {\n const auth = await getAuthFromRequest(req)\n if (!auth?.tenantId || !auth.orgId) {\n return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n }\n\n const rawParams = (ctx.params && typeof (ctx.params as Promise<unknown>).then === 'function')\n ? await (ctx.params as Promise<{ id?: string }>)\n : (ctx.params as { id?: string } | undefined)\n\n const parsedParams = paramsSchema.safeParse(rawParams)\n if (!parsedParams.success) {\n return NextResponse.json({ error: 'Invalid run id' }, { status: 400 })\n }\n\n const payload = await readJsonSafe(req)\n const parsedBody = retrySyncSchema.safeParse(payload ?? {})\n if (!parsedBody.success) {\n return NextResponse.json({ error: 'Invalid payload', details: parsedBody.error.flatten() }, { status: 422 })\n }\n\n const container = await createRequestContainer()\n const syncRunService = container.resolve('dataSyncRunService') as SyncRunService\n const progressService = container.resolve('progressService') as ProgressService\n const scope = { organizationId: auth.orgId as string, tenantId: auth.tenantId }\n\n const previous = await syncRunService.getRun(parsedParams.data.id, scope)\n if (!previous) {\n return NextResponse.json({ error: 'Run not found' }, { status: 404 })\n }\n if (previous.status !== 'failed' && previous.status !== 'cancelled') {\n return NextResponse.json({ error: 'Only failed or cancelled runs can be retried' }, { status: 409 })\n }\n\n const overlap = await syncRunService.findRunningOverlap(\n previous.integrationId,\n previous.entityType,\n previous.direction,\n scope,\n )\n if (overlap) {\n return NextResponse.json({ error: 'A sync run is already in progress for this integration and entity direction' }, { status: 409 })\n }\n\n const cursor = parsedBody.data.fromBeginning\n ? null\n : previous.cursor ?? await syncRunService.resolveCursor(previous.integrationId, previous.entityType, previous.direction, scope)\n\n const { run, progressJob } = await startDataSyncRun({\n syncRunService,\n progressService,\n scope: {\n ...scope,\n userId: auth.sub,\n },\n input: {\n integrationId: previous.integrationId,\n entityType: previous.entityType,\n direction: previous.direction,\n cursor,\n triggeredBy: auth.sub,\n batchSize: 100,\n progressJob: {\n name: `Retry data sync ${previous.integrationId} \u2014 ${previous.entityType}`,\n },\n },\n })\n\n return NextResponse.json({ id: run.id, progressJobId: progressJob?.id ?? null }, { status: 201 })\n}\n"],
|
|
5
|
-
"mappings": "AAAA,SAAS,oBAAoB;AAC7B,SAAS,SAAS;AAClB,SAAS,0BAA0B;AACnC,SAAS,8BAA8B;AACvC,SAAS,oBAAoB;AAG7B,SAAS,uBAAuB;AAChC,SAAS,wBAAwB;
|
|
4
|
+
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\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 type { ProgressService } from '../../../../progress/lib/progressService'\nimport type { SyncRunService } from '../../../lib/sync-run-service'\nimport { retrySyncSchema } from '../../../data/validators'\nimport { startDataSyncRun } from '../../../lib/start-run'\nimport {\n runCrudMutationGuardAfterSuccess,\n validateCrudMutationGuard,\n} from '@open-mercato/shared/lib/crud/mutation-guard'\n\nconst paramsSchema = z.object({ id: z.string().uuid() })\n\nexport const metadata = {\n POST: { requireAuth: true, requireFeatures: ['data_sync.run'] },\n}\n\nexport const openApi = {\n tags: ['DataSync'],\n summary: 'Retry a failed sync run',\n}\n\nexport async function POST(req: Request, ctx: { params?: Promise<{ id?: string }> | { id?: string } }) {\n const auth = await getAuthFromRequest(req)\n if (!auth?.tenantId || !auth.orgId) {\n return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n }\n\n const rawParams = (ctx.params && typeof (ctx.params as Promise<unknown>).then === 'function')\n ? await (ctx.params as Promise<{ id?: string }>)\n : (ctx.params as { id?: string } | undefined)\n\n const parsedParams = paramsSchema.safeParse(rawParams)\n if (!parsedParams.success) {\n return NextResponse.json({ error: 'Invalid run id' }, { status: 400 })\n }\n\n const payload = await readJsonSafe(req)\n const parsedBody = retrySyncSchema.safeParse(payload ?? {})\n if (!parsedBody.success) {\n return NextResponse.json({ error: 'Invalid payload', details: parsedBody.error.flatten() }, { status: 422 })\n }\n\n const container = await createRequestContainer()\n const syncRunService = container.resolve('dataSyncRunService') as SyncRunService\n const progressService = container.resolve('progressService') as ProgressService\n const scope = { organizationId: auth.orgId as string, tenantId: auth.tenantId }\n\n const previous = await syncRunService.getRun(parsedParams.data.id, scope)\n if (!previous) {\n return NextResponse.json({ error: 'Run not found' }, { status: 404 })\n }\n if (previous.status !== 'failed' && previous.status !== 'cancelled') {\n return NextResponse.json({ error: 'Only failed or cancelled runs can be retried' }, { status: 409 })\n }\n\n const overlap = await syncRunService.findRunningOverlap(\n previous.integrationId,\n previous.entityType,\n previous.direction,\n scope,\n )\n if (overlap) {\n return NextResponse.json({ error: 'A sync run is already in progress for this integration and entity direction' }, { status: 409 })\n }\n\n const guardResult = await validateCrudMutationGuard(container, {\n tenantId: auth.tenantId,\n organizationId: scope.organizationId,\n userId: auth.sub,\n resourceKind: 'data_sync.run',\n resourceId: previous.id,\n operation: 'custom',\n requestMethod: req.method,\n requestHeaders: req.headers,\n mutationPayload: { action: 'retry', ...parsedBody.data },\n })\n if (guardResult && !guardResult.ok) {\n return NextResponse.json(guardResult.body, { status: guardResult.status })\n }\n\n const cursor = parsedBody.data.fromBeginning\n ? null\n : previous.cursor ?? await syncRunService.resolveCursor(previous.integrationId, previous.entityType, previous.direction, scope)\n\n const { run, progressJob } = await startDataSyncRun({\n syncRunService,\n progressService,\n scope: {\n ...scope,\n userId: auth.sub,\n },\n input: {\n integrationId: previous.integrationId,\n entityType: previous.entityType,\n direction: previous.direction,\n cursor,\n triggeredBy: auth.sub,\n batchSize: 100,\n progressJob: {\n name: `Retry data sync ${previous.integrationId} \u2014 ${previous.entityType}`,\n },\n },\n })\n\n if (guardResult?.ok && guardResult.shouldRunAfterSuccess) {\n await runCrudMutationGuardAfterSuccess(container, {\n tenantId: auth.tenantId,\n organizationId: scope.organizationId,\n userId: auth.sub,\n resourceKind: 'data_sync.run',\n resourceId: run.id,\n operation: 'custom',\n requestMethod: req.method,\n requestHeaders: req.headers,\n metadata: guardResult.metadata ?? null,\n })\n }\n\n return NextResponse.json({ id: run.id, progressJobId: progressJob?.id ?? null }, { status: 201 })\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -5,6 +5,10 @@ import { createRequestContainer } from "@open-mercato/shared/lib/di/container";
|
|
|
5
5
|
import { readJsonSafe } from "@open-mercato/shared/lib/http/readJsonSafe";
|
|
6
6
|
import { updateSyncScheduleSchema } from "../../../data/validators.js";
|
|
7
7
|
import { serializeSchedule } from "../serialize.js";
|
|
8
|
+
import {
|
|
9
|
+
runCrudMutationGuardAfterSuccess,
|
|
10
|
+
validateCrudMutationGuard
|
|
11
|
+
} from "@open-mercato/shared/lib/crud/mutation-guard";
|
|
8
12
|
const paramsSchema = z.object({
|
|
9
13
|
id: z.string().uuid()
|
|
10
14
|
});
|
|
@@ -60,6 +64,20 @@ async function PUT(req, ctx) {
|
|
|
60
64
|
if (!current) {
|
|
61
65
|
return NextResponse.json({ error: "Schedule not found" }, { status: 404 });
|
|
62
66
|
}
|
|
67
|
+
const guardResult = await validateCrudMutationGuard(container, {
|
|
68
|
+
tenantId: auth.tenantId,
|
|
69
|
+
organizationId: scope.organizationId,
|
|
70
|
+
userId: auth.sub,
|
|
71
|
+
resourceKind: "data_sync.schedule",
|
|
72
|
+
resourceId: current.id,
|
|
73
|
+
operation: "update",
|
|
74
|
+
requestMethod: req.method,
|
|
75
|
+
requestHeaders: req.headers,
|
|
76
|
+
mutationPayload: parsed.data
|
|
77
|
+
});
|
|
78
|
+
if (guardResult && !guardResult.ok) {
|
|
79
|
+
return NextResponse.json(guardResult.body, { status: guardResult.status });
|
|
80
|
+
}
|
|
63
81
|
try {
|
|
64
82
|
const schedule = await scheduleService.saveSchedule({
|
|
65
83
|
id: current.id,
|
|
@@ -72,6 +90,19 @@ async function PUT(req, ctx) {
|
|
|
72
90
|
fullSync: parsed.data.fullSync ?? current.fullSync,
|
|
73
91
|
isEnabled: parsed.data.isEnabled ?? current.isEnabled
|
|
74
92
|
}, scope);
|
|
93
|
+
if (guardResult?.ok && guardResult.shouldRunAfterSuccess) {
|
|
94
|
+
await runCrudMutationGuardAfterSuccess(container, {
|
|
95
|
+
tenantId: auth.tenantId,
|
|
96
|
+
organizationId: scope.organizationId,
|
|
97
|
+
userId: auth.sub,
|
|
98
|
+
resourceKind: "data_sync.schedule",
|
|
99
|
+
resourceId: schedule.id,
|
|
100
|
+
operation: "update",
|
|
101
|
+
requestMethod: req.method,
|
|
102
|
+
requestHeaders: req.headers,
|
|
103
|
+
metadata: guardResult.metadata ?? null
|
|
104
|
+
});
|
|
105
|
+
}
|
|
75
106
|
return NextResponse.json(serializeSchedule(schedule));
|
|
76
107
|
} catch (error) {
|
|
77
108
|
const message = error instanceof Error ? error.message : "Failed to update sync schedule";
|
|
@@ -90,13 +121,38 @@ async function DELETE(req, ctx) {
|
|
|
90
121
|
}
|
|
91
122
|
const container = await createRequestContainer();
|
|
92
123
|
const scheduleService = container.resolve("dataSyncScheduleService");
|
|
93
|
-
const
|
|
94
|
-
|
|
95
|
-
tenantId: auth.tenantId
|
|
124
|
+
const scope = { organizationId: auth.orgId, tenantId: auth.tenantId };
|
|
125
|
+
const guardResult = await validateCrudMutationGuard(container, {
|
|
126
|
+
tenantId: auth.tenantId,
|
|
127
|
+
organizationId: scope.organizationId,
|
|
128
|
+
userId: auth.sub,
|
|
129
|
+
resourceKind: "data_sync.schedule",
|
|
130
|
+
resourceId: parsedParams.data.id,
|
|
131
|
+
operation: "delete",
|
|
132
|
+
requestMethod: req.method,
|
|
133
|
+
requestHeaders: req.headers,
|
|
134
|
+
mutationPayload: null
|
|
96
135
|
});
|
|
136
|
+
if (guardResult && !guardResult.ok) {
|
|
137
|
+
return NextResponse.json(guardResult.body, { status: guardResult.status });
|
|
138
|
+
}
|
|
139
|
+
const deleted = await scheduleService.deleteSchedule(parsedParams.data.id, scope);
|
|
97
140
|
if (!deleted) {
|
|
98
141
|
return NextResponse.json({ error: "Schedule not found" }, { status: 404 });
|
|
99
142
|
}
|
|
143
|
+
if (guardResult?.ok && guardResult.shouldRunAfterSuccess) {
|
|
144
|
+
await runCrudMutationGuardAfterSuccess(container, {
|
|
145
|
+
tenantId: auth.tenantId,
|
|
146
|
+
organizationId: scope.organizationId,
|
|
147
|
+
userId: auth.sub,
|
|
148
|
+
resourceKind: "data_sync.schedule",
|
|
149
|
+
resourceId: parsedParams.data.id,
|
|
150
|
+
operation: "delete",
|
|
151
|
+
requestMethod: req.method,
|
|
152
|
+
requestHeaders: req.headers,
|
|
153
|
+
metadata: guardResult.metadata ?? null
|
|
154
|
+
});
|
|
155
|
+
}
|
|
100
156
|
return NextResponse.json({ deleted: true });
|
|
101
157
|
}
|
|
102
158
|
export {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../src/modules/data_sync/api/schedules/%5Bid%5D/route.ts"],
|
|
4
|
-
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\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 { updateSyncScheduleSchema } from '../../../data/validators'\nimport type { SyncScheduleService } from '../../../lib/sync-schedule-service'\nimport { serializeSchedule } from '../serialize'\n\nconst paramsSchema = z.object({\n id: z.string().uuid(),\n})\n\nexport const metadata = {\n GET: { requireAuth: true, requireFeatures: ['data_sync.configure'] },\n PUT: { requireAuth: true, requireFeatures: ['data_sync.configure'] },\n DELETE: { requireAuth: true, requireFeatures: ['data_sync.configure'] },\n}\n\nexport const openApi = {\n tags: ['DataSync'],\n summary: 'Manage a sync schedule',\n}\n\nexport async function GET(req: Request, ctx: { params?: Promise<{ id?: string }> | { id?: string } }) {\n const auth = await getAuthFromRequest(req)\n if (!auth?.tenantId || !auth.orgId) {\n return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n }\n\n const rawParams = (ctx.params && typeof (ctx.params as Promise<unknown>).then === 'function')\n ? await (ctx.params as Promise<{ id?: string }>)\n : (ctx.params as { id?: string } | undefined)\n\n const parsedParams = paramsSchema.safeParse(rawParams)\n if (!parsedParams.success) {\n return NextResponse.json({ error: 'Invalid schedule id' }, { status: 400 })\n }\n\n const container = await createRequestContainer()\n const scheduleService = container.resolve('dataSyncScheduleService') as SyncScheduleService\n const schedule = await scheduleService.getById(parsedParams.data.id, {\n organizationId: auth.orgId as string,\n tenantId: auth.tenantId,\n })\n\n if (!schedule) {\n return NextResponse.json({ error: 'Schedule not found' }, { status: 404 })\n }\n\n return NextResponse.json(serializeSchedule(schedule))\n}\n\nexport async function PUT(req: Request, ctx: { params?: Promise<{ id?: string }> | { id?: string } }) {\n const auth = await getAuthFromRequest(req)\n if (!auth?.tenantId || !auth.orgId) {\n return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n }\n\n const rawParams = (ctx.params && typeof (ctx.params as Promise<unknown>).then === 'function')\n ? await (ctx.params as Promise<{ id?: string }>)\n : (ctx.params as { id?: string } | undefined)\n\n const parsedParams = paramsSchema.safeParse(rawParams)\n if (!parsedParams.success) {\n return NextResponse.json({ error: 'Invalid schedule id' }, { status: 400 })\n }\n\n const payload = await readJsonSafe(req)\n const parsed = updateSyncScheduleSchema.safeParse(payload)\n if (!parsed.success) {\n return NextResponse.json({ error: 'Invalid payload', details: parsed.error.flatten() }, { status: 422 })\n }\n\n const container = await createRequestContainer()\n const scheduleService = container.resolve('dataSyncScheduleService') as SyncScheduleService\n const scope = { organizationId: auth.orgId as string, tenantId: auth.tenantId }\n const current = await scheduleService.getById(parsedParams.data.id, scope)\n\n if (!current) {\n return NextResponse.json({ error: 'Schedule not found' }, { status: 404 })\n }\n\n try {\n const schedule = await scheduleService.saveSchedule({\n id: current.id,\n integrationId: parsed.data.integrationId ?? current.integrationId,\n entityType: parsed.data.entityType ?? current.entityType,\n direction: parsed.data.direction ?? current.direction,\n scheduleType: parsed.data.scheduleType ?? current.scheduleType,\n scheduleValue: parsed.data.scheduleValue ?? current.scheduleValue,\n timezone: parsed.data.timezone ?? current.timezone,\n fullSync: parsed.data.fullSync ?? current.fullSync,\n isEnabled: parsed.data.isEnabled ?? current.isEnabled,\n }, scope)\n return NextResponse.json(serializeSchedule(schedule))\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Failed to update sync schedule'\n return NextResponse.json({ error: message }, { status: 422 })\n }\n}\n\nexport async function DELETE(req: Request, ctx: { params?: Promise<{ id?: string }> | { id?: string } }) {\n const auth = await getAuthFromRequest(req)\n if (!auth?.tenantId || !auth.orgId) {\n return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n }\n\n const rawParams = (ctx.params && typeof (ctx.params as Promise<unknown>).then === 'function')\n ? await (ctx.params as Promise<{ id?: string }>)\n : (ctx.params as { id?: string } | undefined)\n\n const parsedParams = paramsSchema.safeParse(rawParams)\n if (!parsedParams.success) {\n return NextResponse.json({ error: 'Invalid schedule id' }, { status: 400 })\n }\n\n const container = await createRequestContainer()\n const scheduleService = container.resolve('dataSyncScheduleService') as SyncScheduleService\n const
|
|
5
|
-
"mappings": "AAAA,SAAS,oBAAoB;AAC7B,SAAS,SAAS;AAClB,SAAS,0BAA0B;AACnC,SAAS,8BAA8B;AACvC,SAAS,oBAAoB;AAC7B,SAAS,gCAAgC;AAEzC,SAAS,yBAAyB;
|
|
4
|
+
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\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 { updateSyncScheduleSchema } from '../../../data/validators'\nimport type { SyncScheduleService } from '../../../lib/sync-schedule-service'\nimport { serializeSchedule } from '../serialize'\nimport {\n runCrudMutationGuardAfterSuccess,\n validateCrudMutationGuard,\n} from '@open-mercato/shared/lib/crud/mutation-guard'\n\nconst paramsSchema = z.object({\n id: z.string().uuid(),\n})\n\nexport const metadata = {\n GET: { requireAuth: true, requireFeatures: ['data_sync.configure'] },\n PUT: { requireAuth: true, requireFeatures: ['data_sync.configure'] },\n DELETE: { requireAuth: true, requireFeatures: ['data_sync.configure'] },\n}\n\nexport const openApi = {\n tags: ['DataSync'],\n summary: 'Manage a sync schedule',\n}\n\nexport async function GET(req: Request, ctx: { params?: Promise<{ id?: string }> | { id?: string } }) {\n const auth = await getAuthFromRequest(req)\n if (!auth?.tenantId || !auth.orgId) {\n return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n }\n\n const rawParams = (ctx.params && typeof (ctx.params as Promise<unknown>).then === 'function')\n ? await (ctx.params as Promise<{ id?: string }>)\n : (ctx.params as { id?: string } | undefined)\n\n const parsedParams = paramsSchema.safeParse(rawParams)\n if (!parsedParams.success) {\n return NextResponse.json({ error: 'Invalid schedule id' }, { status: 400 })\n }\n\n const container = await createRequestContainer()\n const scheduleService = container.resolve('dataSyncScheduleService') as SyncScheduleService\n const schedule = await scheduleService.getById(parsedParams.data.id, {\n organizationId: auth.orgId as string,\n tenantId: auth.tenantId,\n })\n\n if (!schedule) {\n return NextResponse.json({ error: 'Schedule not found' }, { status: 404 })\n }\n\n return NextResponse.json(serializeSchedule(schedule))\n}\n\nexport async function PUT(req: Request, ctx: { params?: Promise<{ id?: string }> | { id?: string } }) {\n const auth = await getAuthFromRequest(req)\n if (!auth?.tenantId || !auth.orgId) {\n return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n }\n\n const rawParams = (ctx.params && typeof (ctx.params as Promise<unknown>).then === 'function')\n ? await (ctx.params as Promise<{ id?: string }>)\n : (ctx.params as { id?: string } | undefined)\n\n const parsedParams = paramsSchema.safeParse(rawParams)\n if (!parsedParams.success) {\n return NextResponse.json({ error: 'Invalid schedule id' }, { status: 400 })\n }\n\n const payload = await readJsonSafe(req)\n const parsed = updateSyncScheduleSchema.safeParse(payload)\n if (!parsed.success) {\n return NextResponse.json({ error: 'Invalid payload', details: parsed.error.flatten() }, { status: 422 })\n }\n\n const container = await createRequestContainer()\n const scheduleService = container.resolve('dataSyncScheduleService') as SyncScheduleService\n const scope = { organizationId: auth.orgId as string, tenantId: auth.tenantId }\n const current = await scheduleService.getById(parsedParams.data.id, scope)\n\n if (!current) {\n return NextResponse.json({ error: 'Schedule not found' }, { status: 404 })\n }\n\n const guardResult = await validateCrudMutationGuard(container, {\n tenantId: auth.tenantId,\n organizationId: scope.organizationId,\n userId: auth.sub,\n resourceKind: 'data_sync.schedule',\n resourceId: current.id,\n operation: 'update',\n requestMethod: req.method,\n requestHeaders: req.headers,\n mutationPayload: parsed.data,\n })\n if (guardResult && !guardResult.ok) {\n return NextResponse.json(guardResult.body, { status: guardResult.status })\n }\n\n try {\n const schedule = await scheduleService.saveSchedule({\n id: current.id,\n integrationId: parsed.data.integrationId ?? current.integrationId,\n entityType: parsed.data.entityType ?? current.entityType,\n direction: parsed.data.direction ?? current.direction,\n scheduleType: parsed.data.scheduleType ?? current.scheduleType,\n scheduleValue: parsed.data.scheduleValue ?? current.scheduleValue,\n timezone: parsed.data.timezone ?? current.timezone,\n fullSync: parsed.data.fullSync ?? current.fullSync,\n isEnabled: parsed.data.isEnabled ?? current.isEnabled,\n }, scope)\n\n if (guardResult?.ok && guardResult.shouldRunAfterSuccess) {\n await runCrudMutationGuardAfterSuccess(container, {\n tenantId: auth.tenantId,\n organizationId: scope.organizationId,\n userId: auth.sub,\n resourceKind: 'data_sync.schedule',\n resourceId: schedule.id,\n operation: 'update',\n requestMethod: req.method,\n requestHeaders: req.headers,\n metadata: guardResult.metadata ?? null,\n })\n }\n\n return NextResponse.json(serializeSchedule(schedule))\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Failed to update sync schedule'\n return NextResponse.json({ error: message }, { status: 422 })\n }\n}\n\nexport async function DELETE(req: Request, ctx: { params?: Promise<{ id?: string }> | { id?: string } }) {\n const auth = await getAuthFromRequest(req)\n if (!auth?.tenantId || !auth.orgId) {\n return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n }\n\n const rawParams = (ctx.params && typeof (ctx.params as Promise<unknown>).then === 'function')\n ? await (ctx.params as Promise<{ id?: string }>)\n : (ctx.params as { id?: string } | undefined)\n\n const parsedParams = paramsSchema.safeParse(rawParams)\n if (!parsedParams.success) {\n return NextResponse.json({ error: 'Invalid schedule id' }, { status: 400 })\n }\n\n const container = await createRequestContainer()\n const scheduleService = container.resolve('dataSyncScheduleService') as SyncScheduleService\n const scope = { organizationId: auth.orgId as string, tenantId: auth.tenantId }\n\n const guardResult = await validateCrudMutationGuard(container, {\n tenantId: auth.tenantId,\n organizationId: scope.organizationId,\n userId: auth.sub,\n resourceKind: 'data_sync.schedule',\n resourceId: parsedParams.data.id,\n operation: 'delete',\n requestMethod: req.method,\n requestHeaders: req.headers,\n mutationPayload: null,\n })\n if (guardResult && !guardResult.ok) {\n return NextResponse.json(guardResult.body, { status: guardResult.status })\n }\n\n const deleted = await scheduleService.deleteSchedule(parsedParams.data.id, scope)\n\n if (!deleted) {\n return NextResponse.json({ error: 'Schedule not found' }, { status: 404 })\n }\n\n if (guardResult?.ok && guardResult.shouldRunAfterSuccess) {\n await runCrudMutationGuardAfterSuccess(container, {\n tenantId: auth.tenantId,\n organizationId: scope.organizationId,\n userId: auth.sub,\n resourceKind: 'data_sync.schedule',\n resourceId: parsedParams.data.id,\n operation: 'delete',\n requestMethod: req.method,\n requestHeaders: req.headers,\n metadata: guardResult.metadata ?? null,\n })\n }\n\n return NextResponse.json({ deleted: true })\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -6,6 +6,10 @@ import { createSyncScheduleSchema, listSyncSchedulesQuerySchema } from "../../da
|
|
|
6
6
|
import { serializeSchedule } from "./serialize.js";
|
|
7
7
|
import { readOptimisticLockExpected } from "@open-mercato/shared/lib/crud/optimistic-lock-command";
|
|
8
8
|
import { isCrudHttpError } from "@open-mercato/shared/lib/crud/errors";
|
|
9
|
+
import {
|
|
10
|
+
runCrudMutationGuardAfterSuccess,
|
|
11
|
+
validateCrudMutationGuard
|
|
12
|
+
} from "@open-mercato/shared/lib/crud/mutation-guard";
|
|
9
13
|
const metadata = {
|
|
10
14
|
GET: { requireAuth: true, requireFeatures: ["data_sync.configure"] },
|
|
11
15
|
POST: { requireAuth: true, requireFeatures: ["data_sync.configure"] }
|
|
@@ -54,14 +58,39 @@ async function POST(req) {
|
|
|
54
58
|
}
|
|
55
59
|
const container = await createRequestContainer();
|
|
56
60
|
const scheduleService = container.resolve("dataSyncScheduleService");
|
|
61
|
+
const scope = { organizationId: auth.orgId, tenantId: auth.tenantId };
|
|
62
|
+
const guardResult = await validateCrudMutationGuard(container, {
|
|
63
|
+
tenantId: auth.tenantId,
|
|
64
|
+
organizationId: scope.organizationId,
|
|
65
|
+
userId: auth.sub,
|
|
66
|
+
resourceKind: "data_sync.schedule",
|
|
67
|
+
resourceId: scope.organizationId,
|
|
68
|
+
operation: "create",
|
|
69
|
+
requestMethod: req.method,
|
|
70
|
+
requestHeaders: req.headers,
|
|
71
|
+
mutationPayload: parsed.data
|
|
72
|
+
});
|
|
73
|
+
if (guardResult && !guardResult.ok) {
|
|
74
|
+
return NextResponse.json(guardResult.body, { status: guardResult.status });
|
|
75
|
+
}
|
|
57
76
|
try {
|
|
58
77
|
const schedule = await scheduleService.saveSchedule({
|
|
59
78
|
...parsed.data,
|
|
60
79
|
expectedUpdatedAt: readOptimisticLockExpected(req)
|
|
61
|
-
},
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
80
|
+
}, scope);
|
|
81
|
+
if (guardResult?.ok && guardResult.shouldRunAfterSuccess) {
|
|
82
|
+
await runCrudMutationGuardAfterSuccess(container, {
|
|
83
|
+
tenantId: auth.tenantId,
|
|
84
|
+
organizationId: scope.organizationId,
|
|
85
|
+
userId: auth.sub,
|
|
86
|
+
resourceKind: "data_sync.schedule",
|
|
87
|
+
resourceId: schedule.id,
|
|
88
|
+
operation: "create",
|
|
89
|
+
requestMethod: req.method,
|
|
90
|
+
requestHeaders: req.headers,
|
|
91
|
+
metadata: guardResult.metadata ?? null
|
|
92
|
+
});
|
|
93
|
+
}
|
|
65
94
|
return NextResponse.json(serializeSchedule(schedule), { status: 201 });
|
|
66
95
|
} catch (error) {
|
|
67
96
|
if (isCrudHttpError(error)) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../src/modules/data_sync/api/schedules/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 { createSyncScheduleSchema, listSyncSchedulesQuerySchema } from '../../data/validators'\nimport type { SyncScheduleService } from '../../lib/sync-schedule-service'\nimport { serializeSchedule } from './serialize'\nimport { readOptimisticLockExpected } from '@open-mercato/shared/lib/crud/optimistic-lock-command'\nimport { isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'\n\nexport const metadata = {\n GET: { requireAuth: true, requireFeatures: ['data_sync.configure'] },\n POST: { requireAuth: true, requireFeatures: ['data_sync.configure'] },\n}\n\nexport const openApi = {\n tags: ['DataSync'],\n summary: 'List or create sync schedules',\n}\n\nexport async function GET(req: Request) {\n const auth = await getAuthFromRequest(req)\n if (!auth?.tenantId || !auth.orgId) {\n return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n }\n\n const url = new URL(req.url)\n const parsed = listSyncSchedulesQuerySchema.safeParse({\n integrationId: url.searchParams.get('integrationId') ?? undefined,\n entityType: url.searchParams.get('entityType') ?? undefined,\n direction: url.searchParams.get('direction') ?? undefined,\n page: url.searchParams.get('page') ?? undefined,\n pageSize: url.searchParams.get('pageSize') ?? undefined,\n })\n\n if (!parsed.success) {\n return NextResponse.json({ error: 'Invalid query', details: parsed.error.flatten() }, { status: 400 })\n }\n\n const container = await createRequestContainer()\n const scheduleService = container.resolve('dataSyncScheduleService') as SyncScheduleService\n const scope = { organizationId: auth.orgId as string, tenantId: auth.tenantId }\n const { items, total } = await scheduleService.listSchedules(parsed.data, scope)\n\n return NextResponse.json({\n items: items.map(serializeSchedule),\n total,\n page: parsed.data.page,\n pageSize: parsed.data.pageSize,\n totalPages: Math.max(1, Math.ceil(total / parsed.data.pageSize)),\n })\n}\n\nexport async function POST(req: Request) {\n const auth = await getAuthFromRequest(req)\n if (!auth?.tenantId || !auth.orgId) {\n return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n }\n\n const payload = await readJsonSafe(req)\n const parsed = createSyncScheduleSchema.safeParse(payload)\n if (!parsed.success) {\n return NextResponse.json({ error: 'Invalid payload', details: parsed.error.flatten() }, { status: 422 })\n }\n\n const container = await createRequestContainer()\n const scheduleService = container.resolve('dataSyncScheduleService') as SyncScheduleService\n\n try {\n const schedule = await scheduleService.saveSchedule({\n ...parsed.data,\n expectedUpdatedAt: readOptimisticLockExpected(req),\n }, {\n organizationId: auth.
|
|
5
|
-
"mappings": "AAAA,SAAS,oBAAoB;AAC7B,SAAS,0BAA0B;AACnC,SAAS,8BAA8B;AACvC,SAAS,oBAAoB;AAC7B,SAAS,0BAA0B,oCAAoC;AAEvE,SAAS,yBAAyB;AAClC,SAAS,kCAAkC;AAC3C,SAAS,uBAAuB;
|
|
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 { createSyncScheduleSchema, listSyncSchedulesQuerySchema } from '../../data/validators'\nimport type { SyncScheduleService } from '../../lib/sync-schedule-service'\nimport { serializeSchedule } from './serialize'\nimport { readOptimisticLockExpected } from '@open-mercato/shared/lib/crud/optimistic-lock-command'\nimport { isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport {\n runCrudMutationGuardAfterSuccess,\n validateCrudMutationGuard,\n} from '@open-mercato/shared/lib/crud/mutation-guard'\n\nexport const metadata = {\n GET: { requireAuth: true, requireFeatures: ['data_sync.configure'] },\n POST: { requireAuth: true, requireFeatures: ['data_sync.configure'] },\n}\n\nexport const openApi = {\n tags: ['DataSync'],\n summary: 'List or create sync schedules',\n}\n\nexport async function GET(req: Request) {\n const auth = await getAuthFromRequest(req)\n if (!auth?.tenantId || !auth.orgId) {\n return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n }\n\n const url = new URL(req.url)\n const parsed = listSyncSchedulesQuerySchema.safeParse({\n integrationId: url.searchParams.get('integrationId') ?? undefined,\n entityType: url.searchParams.get('entityType') ?? undefined,\n direction: url.searchParams.get('direction') ?? undefined,\n page: url.searchParams.get('page') ?? undefined,\n pageSize: url.searchParams.get('pageSize') ?? undefined,\n })\n\n if (!parsed.success) {\n return NextResponse.json({ error: 'Invalid query', details: parsed.error.flatten() }, { status: 400 })\n }\n\n const container = await createRequestContainer()\n const scheduleService = container.resolve('dataSyncScheduleService') as SyncScheduleService\n const scope = { organizationId: auth.orgId as string, tenantId: auth.tenantId }\n const { items, total } = await scheduleService.listSchedules(parsed.data, scope)\n\n return NextResponse.json({\n items: items.map(serializeSchedule),\n total,\n page: parsed.data.page,\n pageSize: parsed.data.pageSize,\n totalPages: Math.max(1, Math.ceil(total / parsed.data.pageSize)),\n })\n}\n\nexport async function POST(req: Request) {\n const auth = await getAuthFromRequest(req)\n if (!auth?.tenantId || !auth.orgId) {\n return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n }\n\n const payload = await readJsonSafe(req)\n const parsed = createSyncScheduleSchema.safeParse(payload)\n if (!parsed.success) {\n return NextResponse.json({ error: 'Invalid payload', details: parsed.error.flatten() }, { status: 422 })\n }\n\n const container = await createRequestContainer()\n const scheduleService = container.resolve('dataSyncScheduleService') as SyncScheduleService\n const scope = { organizationId: auth.orgId as string, tenantId: auth.tenantId }\n\n const guardResult = await validateCrudMutationGuard(container, {\n tenantId: auth.tenantId,\n organizationId: scope.organizationId,\n userId: auth.sub,\n resourceKind: 'data_sync.schedule',\n resourceId: scope.organizationId,\n operation: 'create',\n requestMethod: req.method,\n requestHeaders: req.headers,\n mutationPayload: parsed.data,\n })\n if (guardResult && !guardResult.ok) {\n return NextResponse.json(guardResult.body, { status: guardResult.status })\n }\n\n try {\n const schedule = await scheduleService.saveSchedule({\n ...parsed.data,\n expectedUpdatedAt: readOptimisticLockExpected(req),\n }, scope)\n\n if (guardResult?.ok && guardResult.shouldRunAfterSuccess) {\n await runCrudMutationGuardAfterSuccess(container, {\n tenantId: auth.tenantId,\n organizationId: scope.organizationId,\n userId: auth.sub,\n resourceKind: 'data_sync.schedule',\n resourceId: schedule.id,\n operation: 'create',\n requestMethod: req.method,\n requestHeaders: req.headers,\n metadata: guardResult.metadata ?? null,\n })\n }\n\n return NextResponse.json(serializeSchedule(schedule), { status: 201 })\n } catch (error) {\n if (isCrudHttpError(error)) {\n return NextResponse.json(error.body, { status: error.status })\n }\n const message = error instanceof Error ? error.message : 'Failed to save sync schedule'\n return NextResponse.json({ error: message }, { status: 422 })\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -81,16 +81,19 @@ function DictionariesManager() {
|
|
|
81
81
|
})) : [];
|
|
82
82
|
const filtered = list.filter((dictionary) => dictionary.managerVisibility !== "hidden");
|
|
83
83
|
setItems(filtered);
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
84
|
+
setSelectedId((current) => {
|
|
85
|
+
if (current && filtered.some((dict) => dict.id === current)) {
|
|
86
|
+
return current;
|
|
87
|
+
}
|
|
88
|
+
return filtered.length ? filtered[0].id : null;
|
|
89
|
+
});
|
|
87
90
|
} catch (err) {
|
|
88
91
|
console.error("Failed to load dictionaries", err);
|
|
89
92
|
flash(t("dictionaries.config.error.load", "Failed to load dictionaries."), "error");
|
|
90
93
|
} finally {
|
|
91
94
|
setLoading(false);
|
|
92
95
|
}
|
|
93
|
-
}, [
|
|
96
|
+
}, [t]);
|
|
94
97
|
React.useEffect(() => {
|
|
95
98
|
loadDictionaries().catch(() => {
|
|
96
99
|
});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/modules/dictionaries/components/DictionariesManager.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { useSearchParams } from 'next/navigation'\nimport { Book, Plus, Pencil, Trash2 } from 'lucide-react'\nimport { EmptyState } from '@open-mercato/ui/primitives/empty-state'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle } from '@open-mercato/ui/primitives/dialog'\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '@open-mercato/ui/primitives/select'\nimport { Spinner } from '@open-mercato/ui/primitives/spinner'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { apiCall, withScopedApiRequestHeaders } from '@open-mercato/ui/backend/utils/apiCall'\nimport { buildOptimisticLockHeader } from '@open-mercato/ui/backend/utils/optimisticLock'\nimport { surfaceRecordConflict } from '@open-mercato/ui/backend/conflicts'\nimport { useGuardedMutation } from '@open-mercato/ui/backend/injection/useGuardedMutation'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { useConfirmDialog } from '@open-mercato/ui/backend/confirm-dialog'\nimport { DictionaryEntriesEditor } from './DictionaryEntriesEditor'\nimport {\n DEFAULT_DICTIONARY_ENTRY_SORT_MODE,\n dictionaryEntrySortModes,\n type DictionaryEntrySortMode,\n} from '../lib/entrySort'\n\nexport type DictionarySummary = {\n id: string\n key: string\n name: string\n description?: string | null\n isSystem?: boolean\n isActive?: boolean\n entrySortMode: DictionaryEntrySortMode\n organizationId: string\n isInherited: boolean\n managerVisibility: 'default' | 'hidden'\n updatedAt?: string | null\n}\n\ntype DialogState = {\n mode: 'create' | 'edit'\n dictionary?: DictionarySummary\n}\n\ntype DictionaryFormState = {\n key: string\n name: string\n description: string\n entrySortMode: DictionaryEntrySortMode\n}\n\nexport function DictionariesManager() {\n const t = useT()\n const { confirm, ConfirmDialogElement } = useConfirmDialog()\n const searchParams = useSearchParams()\n const [items, setItems] = React.useState<DictionarySummary[]>([])\n const [selectedId, setSelectedId] = React.useState<string | null>(null)\n const [loading, setLoading] = React.useState(true)\n const [dialog, setDialog] = React.useState<DialogState | null>(null)\n const [form, setForm] = React.useState<DictionaryFormState>({\n key: '',\n name: '',\n description: '',\n entrySortMode: DEFAULT_DICTIONARY_ENTRY_SORT_MODE,\n })\n const [errors, setErrors] = React.useState<{ key?: string; name?: string }>({})\n const [submitting, setSubmitting] = React.useState(false)\n const [deleting, setDeleting] = React.useState<string | null>(null)\n const selectionFromQueryApplied = React.useRef(false)\n const { runMutation, retryLastMutation } = useGuardedMutation<{\n formId: string\n resourceKind: string\n resourceId?: string\n retryLastMutation: () => Promise<boolean>\n }>({\n contextId: 'dictionaries:dictionary',\n blockedMessage: t('ui.forms.flash.saveBlocked', 'Save blocked by validation'),\n })\n const inheritedManageMessage = t('dictionaries.config.error.inheritedManage', 'Inherited dictionaries must be managed at the parent organization.')\n const requestedDictionaryId = searchParams?.get('dictionaryId') ?? null\n const requestedDictionaryKey = searchParams?.get('key')?.trim().toLowerCase() ?? null\n const entrySortOptions = React.useMemo(\n () => dictionaryEntrySortModes.map((mode) => ({\n value: mode,\n label:\n mode === 'label_asc'\n ? t('dictionaries.config.sortModes.labelAsc', 'A to Z')\n : mode === 'label_desc'\n ? t('dictionaries.config.sortModes.labelDesc', 'Z to A')\n : mode === 'value_asc'\n ? t('dictionaries.config.sortModes.valueAsc', 'Value A to Z')\n : mode === 'value_desc'\n ? t('dictionaries.config.sortModes.valueDesc', 'Value Z to A')\n : mode === 'created_at_asc'\n ? t('dictionaries.config.sortModes.createdAtAsc', 'Oldest first')\n : t('dictionaries.config.sortModes.createdAtDesc', 'Newest first'),\n })),\n [t],\n )\n\n const loadDictionaries = React.useCallback(async () => {\n setLoading(true)\n try {\n const call = await apiCall<{ items?: unknown[]; error?: string }>('/api/dictionaries')\n if (!call.ok) {\n throw new Error(typeof call.result?.error === 'string' ? call.result.error : 'Failed to load dictionaries')\n }\n const resultItems = Array.isArray(call.result?.items) ? call.result!.items : []\n const list: DictionarySummary[] = Array.isArray(resultItems)\n ? resultItems.map((item: any): DictionarySummary => ({\n id: String(item.id),\n key: String(item.key),\n name: String(item.name ?? item.key),\n description: typeof item.description === 'string' ? item.description : null,\n isSystem: Boolean(item.isSystem),\n isActive: item.isActive !== false,\n entrySortMode: dictionaryEntrySortModes.includes(item.entrySortMode as DictionaryEntrySortMode)\n ? (item.entrySortMode as DictionaryEntrySortMode)\n : DEFAULT_DICTIONARY_ENTRY_SORT_MODE,\n organizationId: typeof item.organizationId === 'string' ? item.organizationId : '',\n isInherited: item.isInherited === true,\n managerVisibility:\n item.managerVisibility === 'hidden' ? 'hidden' : 'default',\n updatedAt: typeof item.updatedAt === 'string' ? item.updatedAt : null,\n }))\n : []\n const filtered = list.filter((dictionary: DictionarySummary) => dictionary.managerVisibility !== 'hidden')\n setItems(filtered)\n if (!filtered.find((dict: DictionarySummary) => dict.id === selectedId)) {\n setSelectedId(filtered.length ? filtered[0].id : null)\n }\n } catch (err) {\n console.error('Failed to load dictionaries', err)\n flash(t('dictionaries.config.error.load', 'Failed to load dictionaries.'), 'error')\n } finally {\n setLoading(false)\n }\n }, [selectedId, t])\n\n React.useEffect(() => {\n loadDictionaries().catch(() => {})\n }, [loadDictionaries])\n\n React.useEffect(() => {\n if (selectionFromQueryApplied.current) return\n if (!items.length) return\n if (!requestedDictionaryId && !requestedDictionaryKey) return\n if (requestedDictionaryId) {\n const match = items.find((dictionary) => dictionary.id === requestedDictionaryId)\n if (match && selectedId !== match.id) {\n setSelectedId(match.id)\n }\n selectionFromQueryApplied.current = true\n return\n }\n if (requestedDictionaryKey) {\n const match = items.find((dictionary) => dictionary.key.toLowerCase() === requestedDictionaryKey)\n if (match && selectedId !== match.id) {\n setSelectedId(match.id)\n }\n selectionFromQueryApplied.current = true\n }\n }, [items, requestedDictionaryId, requestedDictionaryKey, selectedId])\n\n const openCreateDialog = React.useCallback(() => {\n setForm({\n key: '',\n name: '',\n description: '',\n entrySortMode: DEFAULT_DICTIONARY_ENTRY_SORT_MODE,\n })\n setDialog({ mode: 'create' })\n setErrors({})\n }, [])\n\n const openEditDialog = React.useCallback((dictionary: DictionarySummary) => {\n if (dictionary.isInherited) {\n flash(inheritedManageMessage, 'info')\n return\n }\n setForm({\n key: dictionary.key,\n name: dictionary.name,\n description: dictionary.description ?? '',\n entrySortMode: dictionary.entrySortMode,\n })\n setDialog({ mode: 'edit', dictionary })\n setErrors({})\n }, [inheritedManageMessage])\n\n const closeDialog = React.useCallback(() => {\n setDialog(null)\n setForm({\n key: '',\n name: '',\n description: '',\n entrySortMode: DEFAULT_DICTIONARY_ENTRY_SORT_MODE,\n })\n setErrors({})\n }, [])\n\n const handleSubmit = React.useCallback(async () => {\n if (!dialog) return\n if (dialog.mode === 'edit' && dialog.dictionary?.isInherited) {\n flash(inheritedManageMessage, 'info')\n return\n }\n const trimmedKey = form.key.trim()\n const trimmedName = form.name.trim()\n const nextErrors: { key?: string; name?: string } = {}\n if (!trimmedKey) {\n nextErrors.key = t('dictionaries.config.dialog.keyErrorRequired', 'Key is required.')\n } else if (trimmedKey.length > 100) {\n nextErrors.key = t('dictionaries.config.dialog.keyErrorLength', 'Key must be at most 100 characters long.')\n } else if (!/^[a-z0-9][a-z0-9_-]*$/.test(trimmedKey)) {\n nextErrors.key = t('dictionaries.config.dialog.keyErrorPattern', 'Use lowercase letters, numbers, hyphen, or underscore.')\n }\n if (!trimmedName) {\n nextErrors.name = t('dictionaries.config.dialog.nameErrorRequired', 'Name is required.')\n }\n if (nextErrors.key || nextErrors.name) {\n setErrors(nextErrors)\n return\n }\n setSubmitting(true)\n try {\n const payload = {\n key: trimmedKey,\n name: trimmedName,\n description: form.description.trim() || undefined,\n entrySortMode: form.entrySortMode,\n }\n if (dialog.mode === 'create') {\n await runMutation({\n operation: async () => {\n const call = await apiCall<Record<string, unknown>>('/api/dictionaries', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(payload),\n })\n if (!call.ok) {\n throw new Error(typeof call.result?.error === 'string' ? call.result.error : 'Failed to create dictionary')\n }\n return call\n },\n context: {\n formId: 'dictionaries:dictionary',\n resourceKind: 'dictionaries.dictionary',\n retryLastMutation,\n },\n mutationPayload: payload,\n })\n flash(t('dictionaries.config.success.create', 'Dictionary created.'), 'success')\n } else if (dialog.dictionary) {\n const dictionaryId = dialog.dictionary.id\n const expectedUpdatedAt = dialog.dictionary.updatedAt\n await runMutation({\n operation: () =>\n withScopedApiRequestHeaders(\n buildOptimisticLockHeader(expectedUpdatedAt),\n async () => {\n const call = await apiCall<Record<string, unknown>>(`/api/dictionaries/${dictionaryId}`, {\n method: 'PATCH',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(payload),\n })\n if (!call.ok) {\n throw Object.assign(\n new Error(typeof call.result?.error === 'string' ? call.result.error : 'Failed to update dictionary'),\n { status: call.status, ...(call.result && typeof call.result === 'object' ? call.result : {}) },\n )\n }\n return call\n },\n ),\n context: {\n formId: 'dictionaries:dictionary',\n resourceKind: 'dictionaries.dictionary',\n resourceId: dictionaryId,\n retryLastMutation,\n },\n mutationPayload: payload,\n })\n flash(t('dictionaries.config.success.update', 'Dictionary updated.'), 'success')\n }\n closeDialog()\n await loadDictionaries()\n setErrors({})\n } catch (err) {\n if (surfaceRecordConflict(err, t)) {\n return\n }\n console.error('Failed to save dictionary', err)\n flash(t('dictionaries.config.error.save', 'Failed to save dictionary.'), 'error')\n } finally {\n setSubmitting(false)\n }\n }, [closeDialog, dialog, form.description, form.entrySortMode, form.key, form.name, inheritedManageMessage, loadDictionaries, runMutation, retryLastMutation, t])\n\n const handleDelete = React.useCallback(\n async (dictionary: DictionarySummary) => {\n if (dictionary.isInherited) {\n flash(inheritedManageMessage, 'info')\n return\n }\n if (dictionary.isSystem) {\n flash(t('dictionaries.config.error.system', 'System dictionaries cannot be deleted.'), 'error')\n return\n }\n const rawConfirm = t('dictionaries.config.delete.confirm', { name: dictionary.name })\n const confirmMessage = rawConfirm && rawConfirm !== 'dictionaries.config.delete.confirm'\n ? rawConfirm\n : `Delete dictionary \"${dictionary.name}\"?`\n const confirmed = await confirm({\n title: confirmMessage,\n variant: 'destructive',\n })\n if (!confirmed) return\n setDeleting(dictionary.id)\n try {\n await runMutation({\n operation: () =>\n withScopedApiRequestHeaders(\n buildOptimisticLockHeader(dictionary.updatedAt),\n async () => {\n const call = await apiCall<Record<string, unknown>>(`/api/dictionaries/${dictionary.id}`, { method: 'DELETE' })\n if (!call.ok) {\n throw Object.assign(\n new Error(typeof call.result?.error === 'string' ? call.result.error : 'Failed to delete dictionary'),\n { status: call.status, ...(call.result && typeof call.result === 'object' ? call.result : {}) },\n )\n }\n return call\n },\n ),\n context: {\n formId: 'dictionaries:dictionary',\n resourceKind: 'dictionaries.dictionary',\n resourceId: dictionary.id,\n retryLastMutation,\n },\n mutationPayload: { id: dictionary.id },\n })\n flash(t('dictionaries.config.success.delete', 'Dictionary deleted.'), 'success')\n await loadDictionaries()\n } catch (err) {\n if (surfaceRecordConflict(err, t)) {\n return\n }\n console.error('Failed to delete dictionary', err)\n flash(t('dictionaries.config.error.delete', 'Failed to delete dictionary.'), 'error')\n } finally {\n setDeleting(null)\n }\n },\n [confirm, inheritedManageMessage, loadDictionaries, runMutation, retryLastMutation, t],\n )\n\n const selectedDictionary = items.find((item) => item.id === selectedId) ?? null\n\n return (\n <div className=\"grid gap-6 lg:grid-cols-[320px_1fr]\">\n <div className=\"rounded-lg border bg-card p-4 shadow-sm\">\n <div className=\"flex items-center justify-between\">\n <h2 className=\"text-base font-semibold\">\n {t('dictionaries.config.list.title', 'Dictionaries')}\n </h2>\n <Button type=\"button\" size=\"sm\" onClick={openCreateDialog}>\n <Plus className=\"mr-2 h-4 w-4\" />\n {t('dictionaries.config.list.add', 'New dictionary')}\n </Button>\n </div>\n <div className=\"mt-4 space-y-2\">\n {loading ? (\n <div className=\"flex items-center gap-2 text-sm text-muted-foreground\">\n <Spinner className=\"h-4 w-4\" />\n {t('dictionaries.config.list.loading', 'Loading dictionaries\u2026')}\n </div>\n ) : items.length === 0 ? (\n <p className=\"text-sm text-muted-foreground\">\n {t('dictionaries.config.list.empty', 'No dictionaries yet. Create one to get started.')}\n </p>\n ) : (\n <ul className=\"space-y-1\">\n {items.map((dictionary) => (\n <li key={dictionary.id}>\n <div\n role=\"button\"\n tabIndex={0}\n aria-pressed={dictionary.id === selectedId}\n className={`flex w-full cursor-pointer select-none items-center justify-between rounded border px-3 py-2 text-left text-sm transition ${\n dictionary.id === selectedId ? 'border-primary bg-primary/5 text-primary' : 'border-border hover:bg-muted'\n }`}\n onClick={() => setSelectedId(dictionary.id)}\n onKeyDown={(event) => {\n if (event.key === 'Enter' || event.key === ' ') {\n event.preventDefault()\n setSelectedId(dictionary.id)\n }\n }}\n >\n <div>\n <div className=\"flex items-center gap-2 font-medium\">\n <span>{dictionary.name}</span>\n {dictionary.isInherited ? (\n <span className=\"rounded-full border border-border px-2 py-0.5 text-overline font-normal uppercase tracking-wide text-muted-foreground\">\n {t('dictionaries.config.list.inherited', 'Inherited')}\n </span>\n ) : null}\n </div>\n <div className=\"text-xs text-muted-foreground\">{dictionary.key}</div>\n </div>\n <div className=\"flex items-center gap-2\">\n <Button\n type=\"button\"\n size=\"icon\"\n variant=\"ghost\"\n disabled={dictionary.isInherited}\n title={dictionary.isInherited ? inheritedManageMessage : undefined}\n onClick={(event) => {\n event.stopPropagation()\n openEditDialog(dictionary)\n }}\n >\n <Pencil className=\"h-4 w-4\" />\n </Button>\n <Button\n type=\"button\"\n size=\"icon\"\n variant=\"ghost\"\n disabled={dictionary.isInherited || deleting === dictionary.id}\n title={dictionary.isInherited ? inheritedManageMessage : undefined}\n onClick={(event) => {\n event.stopPropagation()\n handleDelete(dictionary)\n }}\n >\n <Trash2 className=\"h-4 w-4\" />\n </Button>\n </div>\n </div>\n </li>\n ))}\n </ul>\n )}\n </div>\n </div>\n <div>\n {selectedDictionary ? (\n <DictionaryEntriesEditor\n dictionaryId={selectedDictionary.id}\n dictionaryName={selectedDictionary.name}\n readOnly={selectedDictionary.isInherited}\n />\n ) : (\n <EmptyState\n icon={<Book className=\"h-8 w-8\" aria-hidden=\"true\" />}\n title={t('dictionaries.config.entries.placeholder', 'Select a dictionary to manage its entries.')}\n className=\"h-full\"\n />\n )}\n </div>\n\n <Dialog open={dialog != null} onOpenChange={(open) => (open ? undefined : closeDialog())}>\n <DialogContent>\n <DialogHeader>\n <DialogTitle>\n {dialog?.mode === 'create'\n ? t('dictionaries.config.dialog.createTitle', 'Create dictionary')\n : t('dictionaries.config.dialog.editTitle', 'Edit dictionary')}\n </DialogTitle>\n </DialogHeader>\n <div className=\"space-y-4\">\n <div className=\"space-y-2\">\n <label className=\"text-sm font-medium\">{t('dictionaries.config.dialog.keyLabel', 'Key')}</label>\n <input\n value={form.key}\n onChange={(event) => {\n const next = event.target.value\n setForm((prev) => ({ ...prev, key: next }))\n if (errors.key) setErrors((prev) => ({ ...prev, key: undefined }))\n }}\n placeholder={t('dictionaries.config.dialog.keyPlaceholder', 'slug_name')}\n disabled={dialog?.mode === 'edit'}\n className={`w-full rounded border px-3 py-2 text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:cursor-not-allowed disabled:bg-muted ${errors.key ? 'border-destructive focus-visible:ring-destructive' : ''}`}\n aria-invalid={errors.key ? 'true' : 'false'}\n aria-describedby=\"dictionary-key-hint\"\n />\n <p\n id=\"dictionary-key-hint\"\n className={`text-xs ${errors.key ? 'text-destructive' : 'text-muted-foreground'}`}\n >\n {errors.key ?? t('dictionaries.config.dialog.keyHint', 'Use lowercase letters, numbers, hyphen, or underscore.')}\n </p>\n </div>\n <div className=\"space-y-2\">\n <label className=\"text-sm font-medium\">{t('dictionaries.config.dialog.nameLabel', 'Name')}</label>\n <input\n value={form.name}\n onChange={(event) => {\n const next = event.target.value\n setForm((prev) => ({ ...prev, name: next }))\n if (errors.name) setErrors((prev) => ({ ...prev, name: undefined }))\n }}\n placeholder={t('dictionaries.config.dialog.namePlaceholder', 'Display name')}\n className={`w-full rounded border px-3 py-2 text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring ${errors.name ? 'border-destructive focus-visible:ring-destructive' : ''}`}\n aria-invalid={errors.name ? 'true' : 'false'}\n />\n {errors.name ? (\n <p className=\"text-xs text-destructive\">{errors.name}</p>\n ) : null}\n </div>\n <div className=\"space-y-2\">\n <label className=\"text-sm font-medium\">{t('dictionaries.config.dialog.descriptionLabel', 'Description')}</label>\n <textarea\n value={form.description}\n onChange={(event) => setForm((prev) => ({ ...prev, description: event.target.value }))}\n className=\"min-h-[120px] w-full rounded border px-3 py-2 text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring\"\n placeholder={t('dictionaries.config.dialog.descriptionPlaceholder', 'Explain how this dictionary is used (optional).')}\n />\n </div>\n <div className=\"space-y-2\">\n <label className=\"text-sm font-medium\">{t('dictionaries.config.dialog.entrySortModeLabel', 'Entry sort order')}</label>\n <Select\n value={form.entrySortMode}\n onValueChange={(next) => setForm((prev) => ({\n ...prev,\n entrySortMode: dictionaryEntrySortModes.includes(next as DictionaryEntrySortMode)\n ? (next as DictionaryEntrySortMode)\n : DEFAULT_DICTIONARY_ENTRY_SORT_MODE,\n }))}\n >\n <SelectTrigger>\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {entrySortOptions.map((option) => (\n <SelectItem key={option.value} value={option.value}>\n {option.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n <p className=\"text-xs text-muted-foreground\">\n {t('dictionaries.config.dialog.entrySortModeHelp', 'Controls the order returned by dictionary entry APIs and dropdowns.')}\n </p>\n </div>\n </div>\n <DialogFooter>\n <Button type=\"button\" variant=\"ghost\" onClick={closeDialog} disabled={submitting}>\n {t('dictionaries.config.dialog.cancel', 'Cancel')}\n </Button>\n <Button type=\"button\" onClick={handleSubmit} disabled={submitting}>\n {submitting ? <Spinner className=\"mr-2 h-4 w-4\" /> : null}\n {t('dictionaries.config.dialog.save', 'Save')}\n </Button>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n {ConfirmDialogElement}\n </div>\n )\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { useSearchParams } from 'next/navigation'\nimport { Book, Plus, Pencil, Trash2 } from 'lucide-react'\nimport { EmptyState } from '@open-mercato/ui/primitives/empty-state'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle } from '@open-mercato/ui/primitives/dialog'\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '@open-mercato/ui/primitives/select'\nimport { Spinner } from '@open-mercato/ui/primitives/spinner'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { apiCall, withScopedApiRequestHeaders } from '@open-mercato/ui/backend/utils/apiCall'\nimport { buildOptimisticLockHeader } from '@open-mercato/ui/backend/utils/optimisticLock'\nimport { surfaceRecordConflict } from '@open-mercato/ui/backend/conflicts'\nimport { useGuardedMutation } from '@open-mercato/ui/backend/injection/useGuardedMutation'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { useConfirmDialog } from '@open-mercato/ui/backend/confirm-dialog'\nimport { DictionaryEntriesEditor } from './DictionaryEntriesEditor'\nimport {\n DEFAULT_DICTIONARY_ENTRY_SORT_MODE,\n dictionaryEntrySortModes,\n type DictionaryEntrySortMode,\n} from '../lib/entrySort'\n\nexport type DictionarySummary = {\n id: string\n key: string\n name: string\n description?: string | null\n isSystem?: boolean\n isActive?: boolean\n entrySortMode: DictionaryEntrySortMode\n organizationId: string\n isInherited: boolean\n managerVisibility: 'default' | 'hidden'\n updatedAt?: string | null\n}\n\ntype DialogState = {\n mode: 'create' | 'edit'\n dictionary?: DictionarySummary\n}\n\ntype DictionaryFormState = {\n key: string\n name: string\n description: string\n entrySortMode: DictionaryEntrySortMode\n}\n\nexport function DictionariesManager() {\n const t = useT()\n const { confirm, ConfirmDialogElement } = useConfirmDialog()\n const searchParams = useSearchParams()\n const [items, setItems] = React.useState<DictionarySummary[]>([])\n const [selectedId, setSelectedId] = React.useState<string | null>(null)\n const [loading, setLoading] = React.useState(true)\n const [dialog, setDialog] = React.useState<DialogState | null>(null)\n const [form, setForm] = React.useState<DictionaryFormState>({\n key: '',\n name: '',\n description: '',\n entrySortMode: DEFAULT_DICTIONARY_ENTRY_SORT_MODE,\n })\n const [errors, setErrors] = React.useState<{ key?: string; name?: string }>({})\n const [submitting, setSubmitting] = React.useState(false)\n const [deleting, setDeleting] = React.useState<string | null>(null)\n const selectionFromQueryApplied = React.useRef(false)\n const { runMutation, retryLastMutation } = useGuardedMutation<{\n formId: string\n resourceKind: string\n resourceId?: string\n retryLastMutation: () => Promise<boolean>\n }>({\n contextId: 'dictionaries:dictionary',\n blockedMessage: t('ui.forms.flash.saveBlocked', 'Save blocked by validation'),\n })\n const inheritedManageMessage = t('dictionaries.config.error.inheritedManage', 'Inherited dictionaries must be managed at the parent organization.')\n const requestedDictionaryId = searchParams?.get('dictionaryId') ?? null\n const requestedDictionaryKey = searchParams?.get('key')?.trim().toLowerCase() ?? null\n const entrySortOptions = React.useMemo(\n () => dictionaryEntrySortModes.map((mode) => ({\n value: mode,\n label:\n mode === 'label_asc'\n ? t('dictionaries.config.sortModes.labelAsc', 'A to Z')\n : mode === 'label_desc'\n ? t('dictionaries.config.sortModes.labelDesc', 'Z to A')\n : mode === 'value_asc'\n ? t('dictionaries.config.sortModes.valueAsc', 'Value A to Z')\n : mode === 'value_desc'\n ? t('dictionaries.config.sortModes.valueDesc', 'Value Z to A')\n : mode === 'created_at_asc'\n ? t('dictionaries.config.sortModes.createdAtAsc', 'Oldest first')\n : t('dictionaries.config.sortModes.createdAtDesc', 'Newest first'),\n })),\n [t],\n )\n\n const loadDictionaries = React.useCallback(async () => {\n setLoading(true)\n try {\n const call = await apiCall<{ items?: unknown[]; error?: string }>('/api/dictionaries')\n if (!call.ok) {\n throw new Error(typeof call.result?.error === 'string' ? call.result.error : 'Failed to load dictionaries')\n }\n const resultItems = Array.isArray(call.result?.items) ? call.result!.items : []\n const list: DictionarySummary[] = Array.isArray(resultItems)\n ? resultItems.map((item: any): DictionarySummary => ({\n id: String(item.id),\n key: String(item.key),\n name: String(item.name ?? item.key),\n description: typeof item.description === 'string' ? item.description : null,\n isSystem: Boolean(item.isSystem),\n isActive: item.isActive !== false,\n entrySortMode: dictionaryEntrySortModes.includes(item.entrySortMode as DictionaryEntrySortMode)\n ? (item.entrySortMode as DictionaryEntrySortMode)\n : DEFAULT_DICTIONARY_ENTRY_SORT_MODE,\n organizationId: typeof item.organizationId === 'string' ? item.organizationId : '',\n isInherited: item.isInherited === true,\n managerVisibility:\n item.managerVisibility === 'hidden' ? 'hidden' : 'default',\n updatedAt: typeof item.updatedAt === 'string' ? item.updatedAt : null,\n }))\n : []\n const filtered = list.filter((dictionary: DictionarySummary) => dictionary.managerVisibility !== 'hidden')\n setItems(filtered)\n setSelectedId((current) => {\n if (current && filtered.some((dict: DictionarySummary) => dict.id === current)) {\n return current\n }\n return filtered.length ? filtered[0].id : null\n })\n } catch (err) {\n console.error('Failed to load dictionaries', err)\n flash(t('dictionaries.config.error.load', 'Failed to load dictionaries.'), 'error')\n } finally {\n setLoading(false)\n }\n }, [t])\n\n React.useEffect(() => {\n loadDictionaries().catch(() => {})\n }, [loadDictionaries])\n\n React.useEffect(() => {\n if (selectionFromQueryApplied.current) return\n if (!items.length) return\n if (!requestedDictionaryId && !requestedDictionaryKey) return\n if (requestedDictionaryId) {\n const match = items.find((dictionary) => dictionary.id === requestedDictionaryId)\n if (match && selectedId !== match.id) {\n setSelectedId(match.id)\n }\n selectionFromQueryApplied.current = true\n return\n }\n if (requestedDictionaryKey) {\n const match = items.find((dictionary) => dictionary.key.toLowerCase() === requestedDictionaryKey)\n if (match && selectedId !== match.id) {\n setSelectedId(match.id)\n }\n selectionFromQueryApplied.current = true\n }\n }, [items, requestedDictionaryId, requestedDictionaryKey, selectedId])\n\n const openCreateDialog = React.useCallback(() => {\n setForm({\n key: '',\n name: '',\n description: '',\n entrySortMode: DEFAULT_DICTIONARY_ENTRY_SORT_MODE,\n })\n setDialog({ mode: 'create' })\n setErrors({})\n }, [])\n\n const openEditDialog = React.useCallback((dictionary: DictionarySummary) => {\n if (dictionary.isInherited) {\n flash(inheritedManageMessage, 'info')\n return\n }\n setForm({\n key: dictionary.key,\n name: dictionary.name,\n description: dictionary.description ?? '',\n entrySortMode: dictionary.entrySortMode,\n })\n setDialog({ mode: 'edit', dictionary })\n setErrors({})\n }, [inheritedManageMessage])\n\n const closeDialog = React.useCallback(() => {\n setDialog(null)\n setForm({\n key: '',\n name: '',\n description: '',\n entrySortMode: DEFAULT_DICTIONARY_ENTRY_SORT_MODE,\n })\n setErrors({})\n }, [])\n\n const handleSubmit = React.useCallback(async () => {\n if (!dialog) return\n if (dialog.mode === 'edit' && dialog.dictionary?.isInherited) {\n flash(inheritedManageMessage, 'info')\n return\n }\n const trimmedKey = form.key.trim()\n const trimmedName = form.name.trim()\n const nextErrors: { key?: string; name?: string } = {}\n if (!trimmedKey) {\n nextErrors.key = t('dictionaries.config.dialog.keyErrorRequired', 'Key is required.')\n } else if (trimmedKey.length > 100) {\n nextErrors.key = t('dictionaries.config.dialog.keyErrorLength', 'Key must be at most 100 characters long.')\n } else if (!/^[a-z0-9][a-z0-9_-]*$/.test(trimmedKey)) {\n nextErrors.key = t('dictionaries.config.dialog.keyErrorPattern', 'Use lowercase letters, numbers, hyphen, or underscore.')\n }\n if (!trimmedName) {\n nextErrors.name = t('dictionaries.config.dialog.nameErrorRequired', 'Name is required.')\n }\n if (nextErrors.key || nextErrors.name) {\n setErrors(nextErrors)\n return\n }\n setSubmitting(true)\n try {\n const payload = {\n key: trimmedKey,\n name: trimmedName,\n description: form.description.trim() || undefined,\n entrySortMode: form.entrySortMode,\n }\n if (dialog.mode === 'create') {\n await runMutation({\n operation: async () => {\n const call = await apiCall<Record<string, unknown>>('/api/dictionaries', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(payload),\n })\n if (!call.ok) {\n throw new Error(typeof call.result?.error === 'string' ? call.result.error : 'Failed to create dictionary')\n }\n return call\n },\n context: {\n formId: 'dictionaries:dictionary',\n resourceKind: 'dictionaries.dictionary',\n retryLastMutation,\n },\n mutationPayload: payload,\n })\n flash(t('dictionaries.config.success.create', 'Dictionary created.'), 'success')\n } else if (dialog.dictionary) {\n const dictionaryId = dialog.dictionary.id\n const expectedUpdatedAt = dialog.dictionary.updatedAt\n await runMutation({\n operation: () =>\n withScopedApiRequestHeaders(\n buildOptimisticLockHeader(expectedUpdatedAt),\n async () => {\n const call = await apiCall<Record<string, unknown>>(`/api/dictionaries/${dictionaryId}`, {\n method: 'PATCH',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(payload),\n })\n if (!call.ok) {\n throw Object.assign(\n new Error(typeof call.result?.error === 'string' ? call.result.error : 'Failed to update dictionary'),\n { status: call.status, ...(call.result && typeof call.result === 'object' ? call.result : {}) },\n )\n }\n return call\n },\n ),\n context: {\n formId: 'dictionaries:dictionary',\n resourceKind: 'dictionaries.dictionary',\n resourceId: dictionaryId,\n retryLastMutation,\n },\n mutationPayload: payload,\n })\n flash(t('dictionaries.config.success.update', 'Dictionary updated.'), 'success')\n }\n closeDialog()\n await loadDictionaries()\n setErrors({})\n } catch (err) {\n if (surfaceRecordConflict(err, t)) {\n return\n }\n console.error('Failed to save dictionary', err)\n flash(t('dictionaries.config.error.save', 'Failed to save dictionary.'), 'error')\n } finally {\n setSubmitting(false)\n }\n }, [closeDialog, dialog, form.description, form.entrySortMode, form.key, form.name, inheritedManageMessage, loadDictionaries, runMutation, retryLastMutation, t])\n\n const handleDelete = React.useCallback(\n async (dictionary: DictionarySummary) => {\n if (dictionary.isInherited) {\n flash(inheritedManageMessage, 'info')\n return\n }\n if (dictionary.isSystem) {\n flash(t('dictionaries.config.error.system', 'System dictionaries cannot be deleted.'), 'error')\n return\n }\n const rawConfirm = t('dictionaries.config.delete.confirm', { name: dictionary.name })\n const confirmMessage = rawConfirm && rawConfirm !== 'dictionaries.config.delete.confirm'\n ? rawConfirm\n : `Delete dictionary \"${dictionary.name}\"?`\n const confirmed = await confirm({\n title: confirmMessage,\n variant: 'destructive',\n })\n if (!confirmed) return\n setDeleting(dictionary.id)\n try {\n await runMutation({\n operation: () =>\n withScopedApiRequestHeaders(\n buildOptimisticLockHeader(dictionary.updatedAt),\n async () => {\n const call = await apiCall<Record<string, unknown>>(`/api/dictionaries/${dictionary.id}`, { method: 'DELETE' })\n if (!call.ok) {\n throw Object.assign(\n new Error(typeof call.result?.error === 'string' ? call.result.error : 'Failed to delete dictionary'),\n { status: call.status, ...(call.result && typeof call.result === 'object' ? call.result : {}) },\n )\n }\n return call\n },\n ),\n context: {\n formId: 'dictionaries:dictionary',\n resourceKind: 'dictionaries.dictionary',\n resourceId: dictionary.id,\n retryLastMutation,\n },\n mutationPayload: { id: dictionary.id },\n })\n flash(t('dictionaries.config.success.delete', 'Dictionary deleted.'), 'success')\n await loadDictionaries()\n } catch (err) {\n if (surfaceRecordConflict(err, t)) {\n return\n }\n console.error('Failed to delete dictionary', err)\n flash(t('dictionaries.config.error.delete', 'Failed to delete dictionary.'), 'error')\n } finally {\n setDeleting(null)\n }\n },\n [confirm, inheritedManageMessage, loadDictionaries, runMutation, retryLastMutation, t],\n )\n\n const selectedDictionary = items.find((item) => item.id === selectedId) ?? null\n\n return (\n <div className=\"grid gap-6 lg:grid-cols-[320px_1fr]\">\n <div className=\"rounded-lg border bg-card p-4 shadow-sm\">\n <div className=\"flex items-center justify-between\">\n <h2 className=\"text-base font-semibold\">\n {t('dictionaries.config.list.title', 'Dictionaries')}\n </h2>\n <Button type=\"button\" size=\"sm\" onClick={openCreateDialog}>\n <Plus className=\"mr-2 h-4 w-4\" />\n {t('dictionaries.config.list.add', 'New dictionary')}\n </Button>\n </div>\n <div className=\"mt-4 space-y-2\">\n {loading ? (\n <div className=\"flex items-center gap-2 text-sm text-muted-foreground\">\n <Spinner className=\"h-4 w-4\" />\n {t('dictionaries.config.list.loading', 'Loading dictionaries\u2026')}\n </div>\n ) : items.length === 0 ? (\n <p className=\"text-sm text-muted-foreground\">\n {t('dictionaries.config.list.empty', 'No dictionaries yet. Create one to get started.')}\n </p>\n ) : (\n <ul className=\"space-y-1\">\n {items.map((dictionary) => (\n <li key={dictionary.id}>\n <div\n role=\"button\"\n tabIndex={0}\n aria-pressed={dictionary.id === selectedId}\n className={`flex w-full cursor-pointer select-none items-center justify-between rounded border px-3 py-2 text-left text-sm transition ${\n dictionary.id === selectedId ? 'border-primary bg-primary/5 text-primary' : 'border-border hover:bg-muted'\n }`}\n onClick={() => setSelectedId(dictionary.id)}\n onKeyDown={(event) => {\n if (event.key === 'Enter' || event.key === ' ') {\n event.preventDefault()\n setSelectedId(dictionary.id)\n }\n }}\n >\n <div>\n <div className=\"flex items-center gap-2 font-medium\">\n <span>{dictionary.name}</span>\n {dictionary.isInherited ? (\n <span className=\"rounded-full border border-border px-2 py-0.5 text-overline font-normal uppercase tracking-wide text-muted-foreground\">\n {t('dictionaries.config.list.inherited', 'Inherited')}\n </span>\n ) : null}\n </div>\n <div className=\"text-xs text-muted-foreground\">{dictionary.key}</div>\n </div>\n <div className=\"flex items-center gap-2\">\n <Button\n type=\"button\"\n size=\"icon\"\n variant=\"ghost\"\n disabled={dictionary.isInherited}\n title={dictionary.isInherited ? inheritedManageMessage : undefined}\n onClick={(event) => {\n event.stopPropagation()\n openEditDialog(dictionary)\n }}\n >\n <Pencil className=\"h-4 w-4\" />\n </Button>\n <Button\n type=\"button\"\n size=\"icon\"\n variant=\"ghost\"\n disabled={dictionary.isInherited || deleting === dictionary.id}\n title={dictionary.isInherited ? inheritedManageMessage : undefined}\n onClick={(event) => {\n event.stopPropagation()\n handleDelete(dictionary)\n }}\n >\n <Trash2 className=\"h-4 w-4\" />\n </Button>\n </div>\n </div>\n </li>\n ))}\n </ul>\n )}\n </div>\n </div>\n <div>\n {selectedDictionary ? (\n <DictionaryEntriesEditor\n dictionaryId={selectedDictionary.id}\n dictionaryName={selectedDictionary.name}\n readOnly={selectedDictionary.isInherited}\n />\n ) : (\n <EmptyState\n icon={<Book className=\"h-8 w-8\" aria-hidden=\"true\" />}\n title={t('dictionaries.config.entries.placeholder', 'Select a dictionary to manage its entries.')}\n className=\"h-full\"\n />\n )}\n </div>\n\n <Dialog open={dialog != null} onOpenChange={(open) => (open ? undefined : closeDialog())}>\n <DialogContent>\n <DialogHeader>\n <DialogTitle>\n {dialog?.mode === 'create'\n ? t('dictionaries.config.dialog.createTitle', 'Create dictionary')\n : t('dictionaries.config.dialog.editTitle', 'Edit dictionary')}\n </DialogTitle>\n </DialogHeader>\n <div className=\"space-y-4\">\n <div className=\"space-y-2\">\n <label className=\"text-sm font-medium\">{t('dictionaries.config.dialog.keyLabel', 'Key')}</label>\n <input\n value={form.key}\n onChange={(event) => {\n const next = event.target.value\n setForm((prev) => ({ ...prev, key: next }))\n if (errors.key) setErrors((prev) => ({ ...prev, key: undefined }))\n }}\n placeholder={t('dictionaries.config.dialog.keyPlaceholder', 'slug_name')}\n disabled={dialog?.mode === 'edit'}\n className={`w-full rounded border px-3 py-2 text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:cursor-not-allowed disabled:bg-muted ${errors.key ? 'border-destructive focus-visible:ring-destructive' : ''}`}\n aria-invalid={errors.key ? 'true' : 'false'}\n aria-describedby=\"dictionary-key-hint\"\n />\n <p\n id=\"dictionary-key-hint\"\n className={`text-xs ${errors.key ? 'text-destructive' : 'text-muted-foreground'}`}\n >\n {errors.key ?? t('dictionaries.config.dialog.keyHint', 'Use lowercase letters, numbers, hyphen, or underscore.')}\n </p>\n </div>\n <div className=\"space-y-2\">\n <label className=\"text-sm font-medium\">{t('dictionaries.config.dialog.nameLabel', 'Name')}</label>\n <input\n value={form.name}\n onChange={(event) => {\n const next = event.target.value\n setForm((prev) => ({ ...prev, name: next }))\n if (errors.name) setErrors((prev) => ({ ...prev, name: undefined }))\n }}\n placeholder={t('dictionaries.config.dialog.namePlaceholder', 'Display name')}\n className={`w-full rounded border px-3 py-2 text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring ${errors.name ? 'border-destructive focus-visible:ring-destructive' : ''}`}\n aria-invalid={errors.name ? 'true' : 'false'}\n />\n {errors.name ? (\n <p className=\"text-xs text-destructive\">{errors.name}</p>\n ) : null}\n </div>\n <div className=\"space-y-2\">\n <label className=\"text-sm font-medium\">{t('dictionaries.config.dialog.descriptionLabel', 'Description')}</label>\n <textarea\n value={form.description}\n onChange={(event) => setForm((prev) => ({ ...prev, description: event.target.value }))}\n className=\"min-h-[120px] w-full rounded border px-3 py-2 text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring\"\n placeholder={t('dictionaries.config.dialog.descriptionPlaceholder', 'Explain how this dictionary is used (optional).')}\n />\n </div>\n <div className=\"space-y-2\">\n <label className=\"text-sm font-medium\">{t('dictionaries.config.dialog.entrySortModeLabel', 'Entry sort order')}</label>\n <Select\n value={form.entrySortMode}\n onValueChange={(next) => setForm((prev) => ({\n ...prev,\n entrySortMode: dictionaryEntrySortModes.includes(next as DictionaryEntrySortMode)\n ? (next as DictionaryEntrySortMode)\n : DEFAULT_DICTIONARY_ENTRY_SORT_MODE,\n }))}\n >\n <SelectTrigger>\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {entrySortOptions.map((option) => (\n <SelectItem key={option.value} value={option.value}>\n {option.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n <p className=\"text-xs text-muted-foreground\">\n {t('dictionaries.config.dialog.entrySortModeHelp', 'Controls the order returned by dictionary entry APIs and dropdowns.')}\n </p>\n </div>\n </div>\n <DialogFooter>\n <Button type=\"button\" variant=\"ghost\" onClick={closeDialog} disabled={submitting}>\n {t('dictionaries.config.dialog.cancel', 'Cancel')}\n </Button>\n <Button type=\"button\" onClick={handleSubmit} disabled={submitting}>\n {submitting ? <Spinner className=\"mr-2 h-4 w-4\" /> : null}\n {t('dictionaries.config.dialog.save', 'Save')}\n </Button>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n {ConfirmDialogElement}\n </div>\n )\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|