@open-mercato/core 0.6.8-develop.6992.1.00c90fecff → 0.6.8-develop.6998.1.d2bc46c56c

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/dist/generated/entities/sync_run/index.js +2 -0
  3. package/dist/generated/entities/sync_run/index.js.map +2 -2
  4. package/dist/generated/entity-fields-registry.js +1 -0
  5. package/dist/generated/entity-fields-registry.js.map +2 -2
  6. package/dist/modules/auth/api/sidebar/preferences/route.js +2 -2
  7. package/dist/modules/auth/api/sidebar/preferences/route.js.map +1 -1
  8. package/dist/modules/auth/lib/backendChrome.js +3 -3
  9. package/dist/modules/auth/lib/backendChrome.js.map +1 -1
  10. package/dist/modules/auth/services/sidebarPreferencesService.js +7 -2
  11. package/dist/modules/auth/services/sidebarPreferencesService.js.map +2 -2
  12. package/dist/modules/data_sync/api/options.js +1 -0
  13. package/dist/modules/data_sync/api/options.js.map +2 -2
  14. package/dist/modules/data_sync/api/run.js +15 -1
  15. package/dist/modules/data_sync/api/run.js.map +2 -2
  16. package/dist/modules/data_sync/api/runs/[id]/retry.js +23 -1
  17. package/dist/modules/data_sync/api/runs/[id]/retry.js.map +2 -2
  18. package/dist/modules/data_sync/api/runs/[id]/route.js +1 -0
  19. package/dist/modules/data_sync/api/runs/[id]/route.js.map +2 -2
  20. package/dist/modules/data_sync/backend/data-sync/page.js +60 -17
  21. package/dist/modules/data_sync/backend/data-sync/page.js.map +2 -2
  22. package/dist/modules/data_sync/backend/data-sync/runs/[id]/page.js +34 -2
  23. package/dist/modules/data_sync/backend/data-sync/runs/[id]/page.js.map +2 -2
  24. package/dist/modules/data_sync/components/IntegrationScheduleTab.js +21 -15
  25. package/dist/modules/data_sync/components/IntegrationScheduleTab.js.map +2 -2
  26. package/dist/modules/data_sync/components/RunParameterFields.js +135 -0
  27. package/dist/modules/data_sync/components/RunParameterFields.js.map +7 -0
  28. package/dist/modules/data_sync/data/entities.js +3 -0
  29. package/dist/modules/data_sync/data/entities.js.map +2 -2
  30. package/dist/modules/data_sync/data/validators.js +4 -1
  31. package/dist/modules/data_sync/data/validators.js.map +2 -2
  32. package/dist/modules/data_sync/lib/run-parameters.js +108 -0
  33. package/dist/modules/data_sync/lib/run-parameters.js.map +7 -0
  34. package/dist/modules/data_sync/lib/start-run.js +1 -0
  35. package/dist/modules/data_sync/lib/start-run.js.map +2 -2
  36. package/dist/modules/data_sync/lib/sync-engine.js +4 -2
  37. package/dist/modules/data_sync/lib/sync-engine.js.map +2 -2
  38. package/dist/modules/data_sync/lib/sync-run-service.js +1 -0
  39. package/dist/modules/data_sync/lib/sync-run-service.js.map +2 -2
  40. package/dist/modules/data_sync/migrations/Migration20260810120000.js +13 -0
  41. package/dist/modules/data_sync/migrations/Migration20260810120000.js.map +7 -0
  42. package/dist/modules/data_sync/workers/sync-scheduled.js +23 -2
  43. package/dist/modules/data_sync/workers/sync-scheduled.js.map +2 -2
  44. package/dist/modules/query_index/di.js +125 -48
  45. package/dist/modules/query_index/di.js.map +2 -2
  46. package/dist/modules/sales/api/documents/factory.js +39 -1
  47. package/dist/modules/sales/api/documents/factory.js.map +2 -2
  48. package/dist/modules/sales/commands/documents.js +26 -5
  49. package/dist/modules/sales/commands/documents.js.map +2 -2
  50. package/generated/entities/sync_run/index.ts +1 -0
  51. package/generated/entity-fields-registry.ts +1 -0
  52. package/package.json +7 -7
  53. package/src/modules/auth/api/sidebar/preferences/route.ts +2 -2
  54. package/src/modules/auth/lib/backendChrome.tsx +2 -2
  55. package/src/modules/auth/services/sidebarPreferencesService.ts +22 -3
  56. package/src/modules/data_sync/AGENTS.md +83 -8
  57. package/src/modules/data_sync/api/options.ts +1 -0
  58. package/src/modules/data_sync/api/run.ts +17 -0
  59. package/src/modules/data_sync/api/runs/[id]/retry.ts +32 -1
  60. package/src/modules/data_sync/api/runs/[id]/route.ts +1 -0
  61. package/src/modules/data_sync/backend/data-sync/page.tsx +73 -17
  62. package/src/modules/data_sync/backend/data-sync/runs/[id]/page.tsx +62 -2
  63. package/src/modules/data_sync/components/IntegrationScheduleTab.tsx +28 -15
  64. package/src/modules/data_sync/components/RunParameterFields.tsx +229 -0
  65. package/src/modules/data_sync/data/entities.ts +4 -1
  66. package/src/modules/data_sync/data/validators.ts +3 -0
  67. package/src/modules/data_sync/i18n/de.json +10 -0
  68. package/src/modules/data_sync/i18n/en.json +10 -0
  69. package/src/modules/data_sync/i18n/es.json +10 -0
  70. package/src/modules/data_sync/i18n/ko.json +10 -0
  71. package/src/modules/data_sync/i18n/pl.json +10 -0
  72. package/src/modules/data_sync/lib/adapter.ts +88 -0
  73. package/src/modules/data_sync/lib/run-parameters.ts +196 -0
  74. package/src/modules/data_sync/lib/start-run.ts +2 -0
  75. package/src/modules/data_sync/lib/sync-engine.ts +5 -1
  76. package/src/modules/data_sync/lib/sync-run-service.ts +2 -0
  77. package/src/modules/data_sync/migrations/.snapshot-open-mercato.json +17 -1
  78. package/src/modules/data_sync/migrations/Migration20260810120000.ts +13 -0
  79. package/src/modules/data_sync/workers/sync-scheduled.ts +32 -1
  80. package/src/modules/query_index/di.ts +153 -56
  81. package/src/modules/sales/api/documents/factory.ts +62 -1
  82. package/src/modules/sales/commands/documents.ts +40 -4
  83. package/src/modules/sales/i18n/de.json +1 -0
  84. package/src/modules/sales/i18n/en.json +1 -0
  85. package/src/modules/sales/i18n/es.json +1 -0
  86. package/src/modules/sales/i18n/ko.json +1 -0
  87. package/src/modules/sales/i18n/pl.json +1 -0
@@ -23,6 +23,12 @@ import { Switch } from '@open-mercato/ui/primitives/switch'
23
23
  import { useOrganizationScopeVersion } from '@open-mercato/shared/lib/frontend/useOrganizationScope'
24
24
  import { useT } from '@open-mercato/shared/lib/i18n/context'
25
25
  import { getSyncSummaryVariant } from '../lib/syncRunStatus'
26
+ import type { RunParameter } from '../lib/adapter'
27
+ import { getApplicableRunParameters } from '../lib/run-parameters'
28
+ import {
29
+ buildDefaultRunParameterValues,
30
+ hasRequiredRunParameterWithoutDefault,
31
+ } from './RunParameterFields'
26
32
  import {
27
33
  CalendarClock,
28
34
  Play,
@@ -40,6 +46,7 @@ type SyncOption = {
40
46
  runMode?: 'generic' | 'provider'
41
47
  canStartRun?: boolean
42
48
  supportedEntities: string[]
49
+ runParameters?: RunParameter[]
43
50
  hasCredentials: boolean
44
51
  isEnabled: boolean
45
52
  }
@@ -223,29 +230,35 @@ export function IntegrationScheduleTab(props: IntegrationScheduleTabProps) {
223
230
  return
224
231
  }
225
232
 
233
+ // This table has no room for a parameter form, so a row run submits the
234
+ // adapter's declared defaults. A parameter the operator must fill in has no
235
+ // default to send, so route them to the Data Sync dashboard instead of
236
+ // letting the run API reject the request.
237
+ const runParameters = getApplicableRunParameters(option?.runParameters, direction, entityType)
238
+ if (hasRequiredRunParameterWithoutDefault(runParameters)) {
239
+ flash(t('data_sync.integrationTab.parametersRequired', 'This run needs parameters. Start it from the Data Sync dashboard.'), 'error')
240
+ return
241
+ }
242
+
226
243
  setRunningKey(scheduleKey)
227
244
  try {
228
245
  const scheduleState = schedules[scheduleKey] ?? buildDefaultScheduleState(entityType)
246
+ const requestBody: Record<string, unknown> = {
247
+ integrationId: props.integrationId,
248
+ entityType,
249
+ direction,
250
+ fullSync: scheduleState.fullSync,
251
+ batchSize: 100,
252
+ }
253
+ if (runParameters.length > 0) requestBody.parameters = buildDefaultRunParameterValues(runParameters)
229
254
  const call = await runMutation({
230
255
  // optimistic-lock-exempt: starts a new sync run (create), not a concurrent record edit
231
256
  operation: () => apiCall<{ id: string }>('/api/data_sync/run', {
232
257
  method: 'POST',
233
258
  headers: { 'content-type': 'application/json' },
234
- body: JSON.stringify({
235
- integrationId: props.integrationId,
236
- entityType,
237
- direction,
238
- fullSync: scheduleState.fullSync,
239
- batchSize: 100,
240
- }),
259
+ body: JSON.stringify(requestBody),
241
260
  }, { fallback: null }),
242
- mutationPayload: {
243
- integrationId: props.integrationId,
244
- entityType,
245
- direction,
246
- fullSync: scheduleState.fullSync,
247
- batchSize: 100,
248
- },
261
+ mutationPayload: requestBody,
249
262
  context: {
250
263
  operation: 'create',
251
264
  actionId: 'start-sync-run',
@@ -264,7 +277,7 @@ export function IntegrationScheduleTab(props: IntegrationScheduleTabProps) {
264
277
  } finally {
265
278
  setRunningKey(null)
266
279
  }
267
- }, [option?.canStartRun, props.hasCredentials, props.integrationId, props.isEnabled, runMutation, schedules, t])
280
+ }, [option?.canStartRun, option?.runParameters, props.hasCredentials, props.integrationId, props.isEnabled, runMutation, schedules, t])
268
281
 
269
282
  const handleSaveSchedule = React.useCallback(async (entityType: string, direction: 'import' | 'export', scheduleKey: string) => {
270
283
  const scheduleState = schedules[scheduleKey] ?? buildDefaultScheduleState(entityType)
@@ -0,0 +1,229 @@
1
+ "use client"
2
+
3
+ import * as React from 'react'
4
+ import { Input } from '@open-mercato/ui/primitives/input'
5
+ import { Label } from '@open-mercato/ui/primitives/label'
6
+ import {
7
+ Select,
8
+ SelectContent,
9
+ SelectItem,
10
+ SelectTrigger,
11
+ SelectValue,
12
+ } from '@open-mercato/ui/primitives/select'
13
+ import { Switch } from '@open-mercato/ui/primitives/switch'
14
+ import { useT, type TranslateFn } from '@open-mercato/shared/lib/i18n/context'
15
+ import type { RunParameter } from '../lib/adapter'
16
+
17
+ /**
18
+ * Form-state representation of a run parameter value. Numbers are held as
19
+ * strings while the operator types; the run API coerces them to the declared
20
+ * type via `normalizeRunParameters`.
21
+ */
22
+ export type RunParameterFormValue = string | boolean
23
+
24
+ export function buildDefaultRunParameterValues(
25
+ params: RunParameter[],
26
+ ): Record<string, RunParameterFormValue> {
27
+ const values: Record<string, RunParameterFormValue> = {}
28
+ for (const param of params) {
29
+ if (param.type === 'boolean') {
30
+ values[param.key] = param.defaultValue === true
31
+ } else {
32
+ values[param.key] = param.defaultValue !== undefined && param.defaultValue !== null
33
+ ? String(param.defaultValue)
34
+ : ''
35
+ }
36
+ }
37
+ return values
38
+ }
39
+
40
+ export function buildRunParametersPayload(
41
+ params: RunParameter[],
42
+ values: Record<string, RunParameterFormValue>,
43
+ ): Record<string, RunParameterFormValue> {
44
+ const payload: Record<string, RunParameterFormValue> = {}
45
+ for (const param of params) {
46
+ if (param.key in values) payload[param.key] = values[param.key]
47
+ }
48
+ return payload
49
+ }
50
+
51
+ /**
52
+ * True when a run cannot be started without operator input — a required
53
+ * parameter with no default to fall back on. Surfaces that cannot render the
54
+ * full parameter form (the integration schedule table) use this to point the
55
+ * operator at the Data Sync dashboard instead of failing with a 422.
56
+ *
57
+ * Booleans are excluded: a switch always submits `true` or `false`, so their
58
+ * `required` can never fail (see `RunParameter.required`). Counting them would
59
+ * send the operator away from a button that would in fact have worked.
60
+ */
61
+ export function hasRequiredRunParameterWithoutDefault(params: RunParameter[]): boolean {
62
+ return params.some((param) => param.type !== 'boolean' && param.required && param.defaultValue === undefined)
63
+ }
64
+
65
+ export type RunParameterErrorEntry = {
66
+ key?: string
67
+ code?: string
68
+ params?: { label?: string; type?: string; min?: number; max?: number; options?: string }
69
+ message?: string
70
+ }
71
+
72
+ export type RunFailureBody = {
73
+ error?: string
74
+ details?: { parameters?: RunParameterErrorEntry[] }
75
+ }
76
+
77
+ type Translate = TranslateFn
78
+
79
+ /** Resolves an optional i18n key, falling back to the adapter's literal. */
80
+ export function resolveRunParameterText(
81
+ t: Translate,
82
+ key: string | undefined,
83
+ literal: string | undefined,
84
+ ): string | undefined {
85
+ if (key) {
86
+ const translated = t(key, literal ?? key)
87
+ if (translated && translated !== key) return translated
88
+ }
89
+ return literal
90
+ }
91
+
92
+ const ERROR_FALLBACKS: Record<string, string> = {
93
+ required: '{label} is required.',
94
+ type: '{label} must be a {type}.',
95
+ min: '{label} must be at least {min}.',
96
+ max: '{label} must be at most {max}.',
97
+ select: '{label} must be one of: {options}.',
98
+ }
99
+
100
+ /**
101
+ * Picks the most specific message out of a failed run response and renders it
102
+ * in the operator's language. The run API reports invalid parameters per key
103
+ * under `details.parameters` with a machine-readable `code`; surfacing only the
104
+ * top-level `error` would say "Invalid run parameters" without saying which one
105
+ * or why, and using the server's pre-rendered `message` would put an English
106
+ * sentence next to a translated dashboard.
107
+ */
108
+ export function buildRunFailureMessage(
109
+ failure: RunFailureBody | null | undefined,
110
+ fallback: string,
111
+ t?: Translate,
112
+ ): string {
113
+ const rendered = (failure?.details?.parameters ?? [])
114
+ .map((entry) => {
115
+ if (t && entry?.code && ERROR_FALLBACKS[entry.code]) {
116
+ return t(`data_sync.runParameters.errors.${entry.code}`, ERROR_FALLBACKS[entry.code], {
117
+ ...(entry.params ?? {}),
118
+ })
119
+ }
120
+ return entry?.message
121
+ })
122
+ .filter((message): message is string => typeof message === 'string' && message.trim().length > 0)
123
+ if (rendered.length > 0) return rendered.join(' ')
124
+ return failure?.error ?? fallback
125
+ }
126
+
127
+ export type RetryFailureBody = {
128
+ code?: string
129
+ }
130
+
131
+ /**
132
+ * Retry failures are otherwise indistinguishable to the operator, so both retry
133
+ * buttons flash one generic sentence. The stale-parameter refusal is the one
134
+ * whose remedy is not obvious from context — it is reported with a machine
135
+ * -readable `code` so the UI can name the way out instead of dead-ending.
136
+ */
137
+ export function buildRetryFailureMessage(
138
+ failure: RetryFailureBody | null | undefined,
139
+ t: Translate,
140
+ ): string {
141
+ if (failure?.code === 'parametersStale') {
142
+ return t(
143
+ 'data_sync.runs.detail.retryParametersStale',
144
+ 'The stored run parameters are no longer valid for this integration. Start a new run from the Data Sync dashboard.',
145
+ )
146
+ }
147
+ return t('data_sync.runs.detail.retryError', 'Failed to retry sync run')
148
+ }
149
+
150
+ export type RunParameterFieldsProps = {
151
+ params: RunParameter[]
152
+ values: Record<string, RunParameterFormValue>
153
+ onChange: (key: string, value: RunParameterFormValue) => void
154
+ }
155
+
156
+ export function RunParameterFields({ params, values, onChange }: RunParameterFieldsProps) {
157
+ const t = useT()
158
+ return (
159
+ <div className="grid gap-3 sm:grid-cols-2">
160
+ {params.map((param) => {
161
+ const value = values[param.key]
162
+ const fieldId = `run-param-${param.key}`
163
+ const describedById = `${fieldId}-description`
164
+ const label = resolveRunParameterText(t, param.labelKey, param.label) ?? param.key
165
+ const description = resolveRunParameterText(t, param.descriptionKey, param.description)
166
+ const placeholder = resolveRunParameterText(t, param.placeholderKey, param.placeholder)
167
+ // No required marker on booleans: a switch always submits true/false,
168
+ // so `required` can never fail for them (see RunParameter.required).
169
+ if (param.type === 'boolean') {
170
+ return (
171
+ <div key={param.key} className="rounded-lg border bg-card p-3 sm:col-span-2">
172
+ <div className="flex items-center justify-between gap-3">
173
+ <div className="space-y-1">
174
+ <Label htmlFor={fieldId} className="text-sm font-medium">{label}</Label>
175
+ {description ? (
176
+ <p id={describedById} className="text-xs text-muted-foreground">{description}</p>
177
+ ) : null}
178
+ </div>
179
+ <Switch
180
+ id={fieldId}
181
+ aria-describedby={description ? describedById : undefined}
182
+ checked={value === true}
183
+ onCheckedChange={(checked) => onChange(param.key, checked)}
184
+ />
185
+ </div>
186
+ </div>
187
+ )
188
+ }
189
+ return (
190
+ <div key={param.key} className="space-y-2">
191
+ <Label htmlFor={fieldId} className="text-sm font-medium">
192
+ {label}
193
+ {param.required ? <span className="text-status-error-text"> *</span> : null}
194
+ </Label>
195
+ {param.type === 'select' ? (
196
+ <Select
197
+ value={typeof value === 'string' && value.length > 0 ? value : undefined}
198
+ onValueChange={(next) => onChange(param.key, next ?? '')}
199
+ >
200
+ <SelectTrigger id={fieldId} aria-describedby={description ? describedById : undefined}>
201
+ <SelectValue placeholder={placeholder ?? undefined} />
202
+ </SelectTrigger>
203
+ <SelectContent>
204
+ {(param.options ?? []).map((option) => (
205
+ <SelectItem key={option.value} value={option.value}>
206
+ {option.label ?? option.value}
207
+ </SelectItem>
208
+ ))}
209
+ </SelectContent>
210
+ </Select>
211
+ ) : (
212
+ <Input
213
+ id={fieldId}
214
+ aria-describedby={description ? describedById : undefined}
215
+ value={typeof value === 'string' ? value : ''}
216
+ onChange={(event) => onChange(param.key, event.target.value)}
217
+ placeholder={placeholder ?? undefined}
218
+ inputMode={param.type === 'number' ? 'numeric' : undefined}
219
+ />
220
+ )}
221
+ {description ? (
222
+ <p id={describedById} className="text-xs text-muted-foreground">{description}</p>
223
+ ) : null}
224
+ </div>
225
+ )
226
+ })}
227
+ </div>
228
+ )
229
+ }
@@ -4,7 +4,7 @@ import { Entity, Index, PrimaryKey, Property } from '@mikro-orm/decorators/legac
4
4
  @Entity({ tableName: 'sync_runs' })
5
5
  @Index({ properties: ['integrationId', 'entityType', 'status', 'organizationId', 'tenantId'] })
6
6
  export class SyncRun {
7
- [OptionalProps]?: 'status' | 'cursor' | 'initialCursor' | 'createdCount' | 'updatedCount' | 'skippedCount' | 'failedCount' | 'batchesCompleted' | 'lastError' | 'progressJobId' | 'jobId' | 'triggeredBy' | 'createdAt' | 'updatedAt' | 'deletedAt'
7
+ [OptionalProps]?: 'status' | 'cursor' | 'initialCursor' | 'createdCount' | 'updatedCount' | 'skippedCount' | 'failedCount' | 'batchesCompleted' | 'lastError' | 'progressJobId' | 'jobId' | 'triggeredBy' | 'parameters' | 'createdAt' | 'updatedAt' | 'deletedAt'
8
8
  @PrimaryKey({ type: 'uuid', defaultRaw: 'gen_random_uuid()' })
9
9
  id!: string
10
10
 
@@ -53,6 +53,9 @@ export class SyncRun {
53
53
  @Property({ name: 'triggered_by', type: 'text', nullable: true })
54
54
  triggeredBy?: string | null
55
55
 
56
+ @Property({ name: 'parameters', type: 'json', nullable: true })
57
+ parameters?: Record<string, unknown> | null
58
+
56
59
  @Property({ name: 'organization_id', type: 'uuid' })
57
60
  organizationId!: string
58
61
 
@@ -7,6 +7,9 @@ export const runSyncSchema = z.object({
7
7
  fullSync: z.boolean().default(false),
8
8
  batchSize: z.number().int().min(1).max(1000).default(100),
9
9
  triggeredBy: z.string().optional(),
10
+ // Adapter-declared run parameters. Validated/coerced against the adapter's
11
+ // `runParameters` declaration in the run route; here we only accept a record.
12
+ parameters: z.record(z.string(), z.unknown()).optional(),
10
13
  })
11
14
 
12
15
  export type RunSyncInput = z.infer<typeof runSyncSchema>
@@ -55,6 +55,8 @@
55
55
  "data_sync.dashboard.start.fullSync": "Run as full sync",
56
56
  "data_sync.dashboard.start.fullSyncHelp": "Ignore the saved cursor and process the entire source again for this run.",
57
57
  "data_sync.dashboard.start.invalidBatchSize": "Batch size must be between 1 and 1000.",
58
+ "data_sync.dashboard.start.parameters": "Lauf-Parameter",
59
+ "data_sync.dashboard.start.parametersHelp": "Optionale Werte, die diese Integration für den manuellen Lauf akzeptiert.",
58
60
  "data_sync.dashboard.start.providerManaged": "Diese Integration startet Synchronisierungsläufe über ihren eigenen Einrichtungsablauf. Öffne die Integrationseinstellungen, um fortzufahren.",
59
61
  "data_sync.dashboard.start.runNowDescription": "Use this for the next immediate sync. Batch size and full-sync mode apply only to this manual run.",
60
62
  "data_sync.dashboard.start.runNowFootnote": "Manual runs show progress immediately and land on the run detail page after launch.",
@@ -85,6 +87,7 @@
85
87
  "data_sync.integrationTab.integrationDisabledNotice": "The integration is disabled. You can save schedules now, but runs will stay blocked until the integration is enabled.",
86
88
  "data_sync.integrationTab.loadError": "Failed to load sync schedules.",
87
89
  "data_sync.integrationTab.notAvailable": "This integration is not registered as a data sync provider.",
90
+ "data_sync.integrationTab.parametersRequired": "Dieser Lauf benötigt Parameter. Starte ihn über das Data-Sync-Dashboard.",
88
91
  "data_sync.integrationTab.providerManaged": "Starte diese Integration über ihren anbieterspezifischen Einrichtungsablauf.",
89
92
  "data_sync.integrationTab.providerManagedNotice": "Dieser Anbieter benötigt einen eigenen Einrichtungsablauf, bevor ein Lauf gestartet werden kann. Verwende den Anbieter-Tab auf dieser Seite statt der generischen Zeitpläne.",
90
93
  "data_sync.integrationTab.refresh": "Refresh",
@@ -96,6 +99,11 @@
96
99
  "data_sync.integrationTab.starting": "Starting...",
97
100
  "data_sync.integrationTab.title": "Sync schedules",
98
101
  "data_sync.nav.title": "Datensynchronisation",
102
+ "data_sync.runParameters.errors.max": "{label} darf höchstens {max} betragen.",
103
+ "data_sync.runParameters.errors.min": "{label} muss mindestens {min} betragen.",
104
+ "data_sync.runParameters.errors.required": "{label} ist erforderlich.",
105
+ "data_sync.runParameters.errors.select": "{label} muss einer dieser Werte sein: {options}.",
106
+ "data_sync.runParameters.errors.type": "{label} muss vom Typ {type} sein.",
99
107
  "data_sync.runs.detail.back": "Zurück zu Läufen",
100
108
  "data_sync.runs.detail.cancel": "Abbrechen",
101
109
  "data_sync.runs.detail.cancelError": "Lauf konnte nicht abgebrochen werden",
@@ -112,6 +120,7 @@
112
120
  "data_sync.runs.detail.logs.time": "Zeit",
113
121
  "data_sync.runs.detail.noLogs": "Keine Protokolleinträge",
114
122
  "data_sync.runs.detail.notFound": "Synchronisierungslauf nicht gefunden.",
123
+ "data_sync.runs.detail.parameters": "Lauf-Parameter",
115
124
  "data_sync.runs.detail.progress": "Fortschritt",
116
125
  "data_sync.runs.detail.progress.batches": "{count} Stapel",
117
126
  "data_sync.runs.detail.progress.eta": "{eta} verbleibend",
@@ -120,6 +129,7 @@
120
129
  "data_sync.runs.detail.progress.percent": "{percent}% abgeschlossen",
121
130
  "data_sync.runs.detail.retry": "Wiederholen",
122
131
  "data_sync.runs.detail.retryError": "Lauf konnte nicht wiederholt werden",
132
+ "data_sync.runs.detail.retryParametersStale": "Die gespeicherten Laufparameter sind für diese Integration nicht mehr gültig. Starten Sie einen neuen Lauf über das Data-Sync-Dashboard.",
123
133
  "data_sync.runs.detail.retrySuccess": "Lauf wiederholt",
124
134
  "data_sync.runs.detail.title": "Synchronisierungslauf"
125
135
  }
@@ -55,6 +55,8 @@
55
55
  "data_sync.dashboard.start.fullSync": "Run as full sync",
56
56
  "data_sync.dashboard.start.fullSyncHelp": "Ignore the saved cursor and process the entire source again for this run.",
57
57
  "data_sync.dashboard.start.invalidBatchSize": "Batch size must be between 1 and 1000.",
58
+ "data_sync.dashboard.start.parameters": "Run parameters",
59
+ "data_sync.dashboard.start.parametersHelp": "Optional values this integration accepts for the manual run.",
58
60
  "data_sync.dashboard.start.providerManaged": "This integration starts sync runs from its own setup flow. Open the integration settings page to continue.",
59
61
  "data_sync.dashboard.start.runNowDescription": "Use this for the next immediate sync. Batch size and full-sync mode apply only to this manual run.",
60
62
  "data_sync.dashboard.start.runNowFootnote": "Manual runs show progress immediately and land on the run detail page after launch.",
@@ -85,6 +87,7 @@
85
87
  "data_sync.integrationTab.integrationDisabledNotice": "The integration is disabled. You can save schedules now, but runs will stay blocked until the integration is enabled.",
86
88
  "data_sync.integrationTab.loadError": "Failed to load sync schedules.",
87
89
  "data_sync.integrationTab.notAvailable": "This integration is not registered as a data sync provider.",
90
+ "data_sync.integrationTab.parametersRequired": "This run needs parameters. Start it from the Data Sync dashboard.",
88
91
  "data_sync.integrationTab.providerManaged": "Start this integration from its provider-specific setup flow.",
89
92
  "data_sync.integrationTab.providerManagedNotice": "This provider needs its own setup flow before a run can start. Use the provider tab on this page instead of generic schedules.",
90
93
  "data_sync.integrationTab.refresh": "Refresh",
@@ -96,6 +99,11 @@
96
99
  "data_sync.integrationTab.starting": "Starting...",
97
100
  "data_sync.integrationTab.title": "Sync schedules",
98
101
  "data_sync.nav.title": "Data Sync",
102
+ "data_sync.runParameters.errors.max": "{label} must be at most {max}.",
103
+ "data_sync.runParameters.errors.min": "{label} must be at least {min}.",
104
+ "data_sync.runParameters.errors.required": "{label} is required.",
105
+ "data_sync.runParameters.errors.select": "{label} must be one of: {options}.",
106
+ "data_sync.runParameters.errors.type": "{label} must be a {type}.",
99
107
  "data_sync.runs.detail.back": "Back to Sync Runs",
100
108
  "data_sync.runs.detail.cancel": "Cancel",
101
109
  "data_sync.runs.detail.cancelError": "Failed to cancel sync run",
@@ -112,6 +120,7 @@
112
120
  "data_sync.runs.detail.logs.time": "Time",
113
121
  "data_sync.runs.detail.noLogs": "No log entries",
114
122
  "data_sync.runs.detail.notFound": "Sync run not found.",
123
+ "data_sync.runs.detail.parameters": "Run parameters",
115
124
  "data_sync.runs.detail.progress": "Progress",
116
125
  "data_sync.runs.detail.progress.batches": "{count} batches",
117
126
  "data_sync.runs.detail.progress.eta": "{eta} remaining",
@@ -120,6 +129,7 @@
120
129
  "data_sync.runs.detail.progress.percent": "{percent}% complete",
121
130
  "data_sync.runs.detail.retry": "Retry",
122
131
  "data_sync.runs.detail.retryError": "Failed to retry sync run",
132
+ "data_sync.runs.detail.retryParametersStale": "The stored run parameters are no longer valid for this integration. Start a new run from the Data Sync dashboard.",
123
133
  "data_sync.runs.detail.retrySuccess": "Sync run retried",
124
134
  "data_sync.runs.detail.title": "Sync Run"
125
135
  }
@@ -55,6 +55,8 @@
55
55
  "data_sync.dashboard.start.fullSync": "Run as full sync",
56
56
  "data_sync.dashboard.start.fullSyncHelp": "Ignore the saved cursor and process the entire source again for this run.",
57
57
  "data_sync.dashboard.start.invalidBatchSize": "Batch size must be between 1 and 1000.",
58
+ "data_sync.dashboard.start.parameters": "Parámetros de ejecución",
59
+ "data_sync.dashboard.start.parametersHelp": "Valores opcionales que esta integración acepta para la ejecución manual.",
58
60
  "data_sync.dashboard.start.providerManaged": "Esta integración inicia las sincronizaciones desde su propio flujo de configuración. Abre la página de ajustes de la integración para continuar.",
59
61
  "data_sync.dashboard.start.runNowDescription": "Use this for the next immediate sync. Batch size and full-sync mode apply only to this manual run.",
60
62
  "data_sync.dashboard.start.runNowFootnote": "Manual runs show progress immediately and land on the run detail page after launch.",
@@ -85,6 +87,7 @@
85
87
  "data_sync.integrationTab.integrationDisabledNotice": "The integration is disabled. You can save schedules now, but runs will stay blocked until the integration is enabled.",
86
88
  "data_sync.integrationTab.loadError": "Failed to load sync schedules.",
87
89
  "data_sync.integrationTab.notAvailable": "This integration is not registered as a data sync provider.",
90
+ "data_sync.integrationTab.parametersRequired": "Esta ejecución necesita parámetros. Iníciala desde el panel de Data Sync.",
88
91
  "data_sync.integrationTab.providerManaged": "Inicia esta integración desde su flujo de configuración específico del proveedor.",
89
92
  "data_sync.integrationTab.providerManagedNotice": "Este proveedor necesita su propio flujo de configuración antes de poder iniciar una ejecución. Usa la pestaña del proveedor en esta página en lugar de los calendarios genéricos.",
90
93
  "data_sync.integrationTab.refresh": "Refresh",
@@ -96,6 +99,11 @@
96
99
  "data_sync.integrationTab.starting": "Starting...",
97
100
  "data_sync.integrationTab.title": "Sync schedules",
98
101
  "data_sync.nav.title": "Sincronización de datos",
102
+ "data_sync.runParameters.errors.max": "{label} debe ser como máximo {max}.",
103
+ "data_sync.runParameters.errors.min": "{label} debe ser como mínimo {min}.",
104
+ "data_sync.runParameters.errors.required": "{label} es obligatorio.",
105
+ "data_sync.runParameters.errors.select": "{label} debe ser uno de: {options}.",
106
+ "data_sync.runParameters.errors.type": "{label} debe ser de tipo {type}.",
99
107
  "data_sync.runs.detail.back": "Volver a ejecuciones",
100
108
  "data_sync.runs.detail.cancel": "Cancelar",
101
109
  "data_sync.runs.detail.cancelError": "No se pudo cancelar la ejecución",
@@ -112,6 +120,7 @@
112
120
  "data_sync.runs.detail.logs.time": "Hora",
113
121
  "data_sync.runs.detail.noLogs": "Sin entradas de registro",
114
122
  "data_sync.runs.detail.notFound": "Ejecución de sincronización no encontrada.",
123
+ "data_sync.runs.detail.parameters": "Parámetros de ejecución",
115
124
  "data_sync.runs.detail.progress": "Progreso",
116
125
  "data_sync.runs.detail.progress.batches": "{count} lotes",
117
126
  "data_sync.runs.detail.progress.eta": "Quedan {eta}",
@@ -120,6 +129,7 @@
120
129
  "data_sync.runs.detail.progress.percent": "{percent}% completado",
121
130
  "data_sync.runs.detail.retry": "Reintentar",
122
131
  "data_sync.runs.detail.retryError": "No se pudo reintentar la ejecución",
132
+ "data_sync.runs.detail.retryParametersStale": "Los parámetros de ejecución guardados ya no son válidos para esta integración. Inicia una nueva ejecución desde el panel de Data Sync.",
123
133
  "data_sync.runs.detail.retrySuccess": "Ejecución reintentada",
124
134
  "data_sync.runs.detail.title": "Ejecución de sincronización"
125
135
  }
@@ -55,6 +55,8 @@
55
55
  "data_sync.dashboard.start.fullSync": "전체 동기화로 실행",
56
56
  "data_sync.dashboard.start.fullSyncHelp": "저장된 커서를 무시하고 이 실행에 대해 전체 소스를 다시 처리합니다.",
57
57
  "data_sync.dashboard.start.invalidBatchSize": "배치 크기는 1에서 1000 사이여야 합니다.",
58
+ "data_sync.dashboard.start.parameters": "실행 매개변수",
59
+ "data_sync.dashboard.start.parametersHelp": "이 통합이 수동 실행에 대해 허용하는 선택적 값입니다.",
58
60
  "data_sync.dashboard.start.providerManaged": "이 통합은 자체 설정 흐름에서 동기화 실행을 시작합니다. 계속하려면 통합 설정 페이지를 여세요.",
59
61
  "data_sync.dashboard.start.runNowDescription": "다음 즉시 동기화에 사용하세요. 배치 크기와 전체 동기화 모드는 이 수동 실행에만 적용됩니다.",
60
62
  "data_sync.dashboard.start.runNowFootnote": "수동 실행은 즉시 진행 상황을 표시하며 시작 후 실행 상세 페이지로 이동합니다.",
@@ -85,6 +87,7 @@
85
87
  "data_sync.integrationTab.integrationDisabledNotice": "통합이 비활성화되어 있습니다. 지금 일정을 저장할 수 있지만 통합이 활성화될 때까지 실행이 차단됩니다.",
86
88
  "data_sync.integrationTab.loadError": "동기화 일정을 불러오지 못했습니다.",
87
89
  "data_sync.integrationTab.notAvailable": "이 통합은 데이터 동기화 제공업체로 등록되어 있지 않습니다.",
90
+ "data_sync.integrationTab.parametersRequired": "이 실행에는 매개변수가 필요합니다. 데이터 동기화 대시보드에서 시작하세요.",
88
91
  "data_sync.integrationTab.providerManaged": "이 통합은 제공업체별 설정 흐름에서 시작하세요.",
89
92
  "data_sync.integrationTab.providerManagedNotice": "이 제공업체는 실행을 시작하기 전에 자체 설정 흐름이 필요합니다. 일반 일정 대신 이 페이지의 제공업체 탭을 사용하세요.",
90
93
  "data_sync.integrationTab.refresh": "새로고침",
@@ -96,6 +99,11 @@
96
99
  "data_sync.integrationTab.starting": "시작하는 중...",
97
100
  "data_sync.integrationTab.title": "동기화 일정",
98
101
  "data_sync.nav.title": "데이터 동기화",
102
+ "data_sync.runParameters.errors.max": "{label}은(는) 최대 {max} 이하여야 합니다.",
103
+ "data_sync.runParameters.errors.min": "{label}은(는) 최소 {min} 이상이어야 합니다.",
104
+ "data_sync.runParameters.errors.required": "{label}은(는) 필수입니다.",
105
+ "data_sync.runParameters.errors.select": "{label}은(는) 다음 중 하나여야 합니다: {options}.",
106
+ "data_sync.runParameters.errors.type": "{label}은(는) {type} 형식이어야 합니다.",
99
107
  "data_sync.runs.detail.back": "동기화 실행으로 돌아가기",
100
108
  "data_sync.runs.detail.cancel": "취소",
101
109
  "data_sync.runs.detail.cancelError": "동기화 실행 취소에 실패했습니다",
@@ -112,6 +120,7 @@
112
120
  "data_sync.runs.detail.logs.time": "시간",
113
121
  "data_sync.runs.detail.noLogs": "로그 항목 없음",
114
122
  "data_sync.runs.detail.notFound": "동기화 실행을 찾을 수 없습니다.",
123
+ "data_sync.runs.detail.parameters": "실행 매개변수",
115
124
  "data_sync.runs.detail.progress": "진행 상황",
116
125
  "data_sync.runs.detail.progress.batches": "{count}개 배치",
117
126
  "data_sync.runs.detail.progress.eta": "{eta} 남음",
@@ -120,6 +129,7 @@
120
129
  "data_sync.runs.detail.progress.percent": "{percent}% 완료",
121
130
  "data_sync.runs.detail.retry": "재시도",
122
131
  "data_sync.runs.detail.retryError": "동기화 실행 재시도에 실패했습니다",
132
+ "data_sync.runs.detail.retryParametersStale": "저장된 실행 매개변수가 이 연동에 더 이상 유효하지 않습니다. Data Sync 대시보드에서 새 실행을 시작하세요.",
123
133
  "data_sync.runs.detail.retrySuccess": "동기화 실행이 재시도되었습니다",
124
134
  "data_sync.runs.detail.title": "동기화 실행"
125
135
  }
@@ -55,6 +55,8 @@
55
55
  "data_sync.dashboard.start.fullSync": "Uruchom jako pełną synchronizację",
56
56
  "data_sync.dashboard.start.fullSyncHelp": "Zignoruj zapisany kursor i przetwórz w tym przebiegu całe źródło od nowa.",
57
57
  "data_sync.dashboard.start.invalidBatchSize": "Rozmiar partii musi mieścić się w zakresie od 1 do 1000.",
58
+ "data_sync.dashboard.start.parameters": "Parametry uruchomienia",
59
+ "data_sync.dashboard.start.parametersHelp": "Opcjonalne wartości akceptowane przez tę integrację dla ręcznego uruchomienia.",
58
60
  "data_sync.dashboard.start.providerManaged": "Ta integracja uruchamia synchronizacje z własnego przepływu konfiguracji. Otwórz ustawienia integracji, aby kontynuować.",
59
61
  "data_sync.dashboard.start.runNowDescription": "Użyj tej opcji dla najbliższej natychmiastowej synchronizacji. Rozmiar partii i tryb pełnej synchronizacji dotyczą wyłącznie tego ręcznego przebiegu.",
60
62
  "data_sync.dashboard.start.runNowFootnote": "Ręczne przebiegi od razu pokazują postęp, a po uruchomieniu przenoszą na stronę szczegółów przebiegu.",
@@ -85,6 +87,7 @@
85
87
  "data_sync.integrationTab.integrationDisabledNotice": "Integracja jest wyłączona. Możesz teraz zapisać harmonogramy, ale przebiegi pozostaną zablokowane do czasu włączenia integracji.",
86
88
  "data_sync.integrationTab.loadError": "Nie udało się załadować harmonogramów synchronizacji.",
87
89
  "data_sync.integrationTab.notAvailable": "Ta integracja nie jest zarejestrowana jako dostawca synchronizacji danych.",
90
+ "data_sync.integrationTab.parametersRequired": "Ten przebieg wymaga parametrów. Uruchom go z panelu Data Sync.",
88
91
  "data_sync.integrationTab.providerManaged": "Uruchom tę integrację z jej przepływu konfiguracji dostawcy.",
89
92
  "data_sync.integrationTab.providerManagedNotice": "Ten dostawca wymaga własnego przepływu konfiguracji, zanim będzie można uruchomić synchronizację. Użyj karty dostawcy na tej stronie zamiast ogólnych harmonogramów.",
90
93
  "data_sync.integrationTab.refresh": "Odśwież",
@@ -96,6 +99,11 @@
96
99
  "data_sync.integrationTab.starting": "Uruchamianie...",
97
100
  "data_sync.integrationTab.title": "Harmonogramy synchronizacji",
98
101
  "data_sync.nav.title": "Synchronizacja danych",
102
+ "data_sync.runParameters.errors.max": "Pole {label} może wynosić najwyżej {max}.",
103
+ "data_sync.runParameters.errors.min": "Pole {label} musi wynosić co najmniej {min}.",
104
+ "data_sync.runParameters.errors.required": "Pole {label} jest wymagane.",
105
+ "data_sync.runParameters.errors.select": "Pole {label} musi mieć jedną z wartości: {options}.",
106
+ "data_sync.runParameters.errors.type": "Pole {label} musi mieć typ {type}.",
99
107
  "data_sync.runs.detail.back": "Powrót do przebiegów",
100
108
  "data_sync.runs.detail.cancel": "Anuluj",
101
109
  "data_sync.runs.detail.cancelError": "Nie udało się anulować przebiegu",
@@ -112,6 +120,7 @@
112
120
  "data_sync.runs.detail.logs.time": "Czas",
113
121
  "data_sync.runs.detail.noLogs": "Brak wpisów w logach",
114
122
  "data_sync.runs.detail.notFound": "Nie znaleziono przebiegu synchronizacji.",
123
+ "data_sync.runs.detail.parameters": "Parametry uruchomienia",
115
124
  "data_sync.runs.detail.progress": "Postęp",
116
125
  "data_sync.runs.detail.progress.batches": "{count} partii",
117
126
  "data_sync.runs.detail.progress.eta": "Pozostało {eta}",
@@ -120,6 +129,7 @@
120
129
  "data_sync.runs.detail.progress.percent": "{percent}% ukończono",
121
130
  "data_sync.runs.detail.retry": "Ponów",
122
131
  "data_sync.runs.detail.retryError": "Nie udało się ponowić przebiegu",
132
+ "data_sync.runs.detail.retryParametersStale": "Zapisane parametry uruchomienia nie są już poprawne dla tej integracji. Uruchom nowe zadanie z pulpitu Data Sync.",
123
133
  "data_sync.runs.detail.retrySuccess": "Przebieg ponowiony",
124
134
  "data_sync.runs.detail.title": "Przebieg synchronizacji"
125
135
  }