@revenexx/studio-integrations 0.6.0 → 1.0.0

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 (137) hide show
  1. package/CHANGELOG.md +643 -0
  2. package/README.md +31 -0
  3. package/dist/module.d.mts +17 -1
  4. package/dist/module.json +1 -1
  5. package/dist/module.mjs +12 -3
  6. package/dist/runtime/components/Integrations/CredentialCreateDialog.vue +32 -28
  7. package/dist/runtime/components/Integrations/CredentialDetailDialog.vue +45 -38
  8. package/dist/runtime/components/Integrations/CredentialRevenexxSource.vue +58 -58
  9. package/dist/runtime/components/Integrations/CredentialTypePicker.vue +10 -9
  10. package/dist/runtime/components/Integrations/DeadLettersPanel.vue +18 -17
  11. package/dist/runtime/components/Integrations/EventTypeCombobox.vue +4 -3
  12. package/dist/runtime/components/Integrations/ExpressionInput.vue +21 -15
  13. package/dist/runtime/components/Integrations/GroupFrameView.vue +29 -19
  14. package/dist/runtime/components/Integrations/Layout.vue +28 -3
  15. package/dist/runtime/components/Integrations/NodeConfigDynamicField.d.vue.ts +3 -0
  16. package/dist/runtime/components/Integrations/NodeConfigDynamicField.vue +30 -16
  17. package/dist/runtime/components/Integrations/NodeConfigDynamicField.vue.d.ts +3 -0
  18. package/dist/runtime/components/Integrations/NodeConfigDynamicSchema.vue +10 -8
  19. package/dist/runtime/components/Integrations/NodeConfigField.d.vue.ts +3 -0
  20. package/dist/runtime/components/Integrations/NodeConfigField.vue +45 -42
  21. package/dist/runtime/components/Integrations/NodeConfigField.vue.d.ts +3 -0
  22. package/dist/runtime/components/Integrations/NodeInspector.d.vue.ts +2 -2
  23. package/dist/runtime/components/Integrations/NodeInspector.vue +315 -221
  24. package/dist/runtime/components/Integrations/NodeInspector.vue.d.ts +2 -2
  25. package/dist/runtime/components/Integrations/NodeOutputSchema.d.vue.ts +2 -1
  26. package/dist/runtime/components/Integrations/NodeOutputSchema.vue +37 -10
  27. package/dist/runtime/components/Integrations/NodeOutputSchema.vue.d.ts +2 -1
  28. package/dist/runtime/components/Integrations/NodePalette.vue +40 -35
  29. package/dist/runtime/components/Integrations/QueryBuilder.vue +44 -43
  30. package/dist/runtime/components/Integrations/RunActionsMenu.vue +36 -12
  31. package/dist/runtime/components/Integrations/RunDetailPanel.vue +114 -62
  32. package/dist/runtime/components/Integrations/RunPayloadInput.vue +14 -13
  33. package/dist/runtime/components/Integrations/RunResponseModal.vue +11 -10
  34. package/dist/runtime/components/Integrations/RunsTable.d.vue.ts +0 -1
  35. package/dist/runtime/components/Integrations/RunsTable.vue +38 -16
  36. package/dist/runtime/components/Integrations/RunsTable.vue.d.ts +0 -1
  37. package/dist/runtime/components/Integrations/SftpAccountImportDialog.vue +8 -7
  38. package/dist/runtime/components/Integrations/Sidebar.vue +14 -10
  39. package/dist/runtime/components/Integrations/StickyNoteView.d.vue.ts +14 -4
  40. package/dist/runtime/components/Integrations/StickyNoteView.vue +82 -34
  41. package/dist/runtime/components/Integrations/StickyNoteView.vue.d.ts +14 -4
  42. package/dist/runtime/components/Integrations/SurfaceCard.d.vue.ts +50 -0
  43. package/dist/runtime/components/Integrations/SurfaceCard.vue +55 -0
  44. package/dist/runtime/components/Integrations/SurfaceCard.vue.d.ts +50 -0
  45. package/dist/runtime/components/Integrations/TemplateCard.vue +18 -8
  46. package/dist/runtime/components/Integrations/TemplateFlowPreview.vue +22 -12
  47. package/dist/runtime/components/Integrations/WorkflowContextMenu.vue +34 -33
  48. package/dist/runtime/components/Integrations/WorkflowCreateDialog.vue +41 -39
  49. package/dist/runtime/components/Integrations/WorkflowDuplicateDialog.vue +8 -7
  50. package/dist/runtime/components/Integrations/WorkflowEdgeView.vue +6 -4
  51. package/dist/runtime/components/Integrations/WorkflowEditor.d.vue.ts +16 -0
  52. package/dist/runtime/components/Integrations/WorkflowEditor.vue +131 -87
  53. package/dist/runtime/components/Integrations/WorkflowEditor.vue.d.ts +16 -0
  54. package/dist/runtime/components/Integrations/WorkflowHistoryPanel.d.vue.ts +6 -0
  55. package/dist/runtime/components/Integrations/WorkflowHistoryPanel.vue +27 -24
  56. package/dist/runtime/components/Integrations/WorkflowHistoryPanel.vue.d.ts +6 -0
  57. package/dist/runtime/components/Integrations/WorkflowIconPicker.vue +7 -6
  58. package/dist/runtime/components/Integrations/WorkflowNodeView.vue +76 -46
  59. package/dist/runtime/components/Integrations/WorkflowRunReplayPanel.vue +55 -43
  60. package/dist/runtime/components/Integrations/WorkflowSettingsDialog.d.vue.ts +16 -17
  61. package/dist/runtime/components/Integrations/WorkflowSettingsDialog.vue +55 -43
  62. package/dist/runtime/components/Integrations/WorkflowSettingsDialog.vue.d.ts +16 -17
  63. package/dist/runtime/components/Integrations/WorkflowTagsInput.vue +9 -8
  64. package/dist/runtime/components/Integrations/WorkflowTriggerView.vue +15 -13
  65. package/dist/runtime/components/Integrations/WorkflowVariablesDialog.vue +79 -39
  66. package/dist/runtime/components/Integrations/WorkflowsTable.vue +83 -45
  67. package/dist/runtime/composables/integrationsApi.js +4 -2
  68. package/dist/runtime/composables/useCredentialTypes.js +4 -2
  69. package/dist/runtime/composables/useIntegrationsCredentials.d.ts +11 -0
  70. package/dist/runtime/composables/useIntegrationsCredentials.js +8 -6
  71. package/dist/runtime/composables/useIntegrationsFocus.d.ts +12 -0
  72. package/dist/runtime/composables/useIntegrationsLocale.d.ts +26 -0
  73. package/dist/runtime/composables/useIntegrationsLocale.js +8 -0
  74. package/dist/runtime/composables/useIntegrationsNodes.js +14 -2
  75. package/dist/runtime/composables/useIntegrationsRealtime.d.ts +149 -0
  76. package/dist/runtime/composables/useIntegrationsRealtime.js +80 -0
  77. package/dist/runtime/composables/useIntegrationsRevisions.js +9 -7
  78. package/dist/runtime/composables/useIntegrationsRuns.d.ts +1 -1
  79. package/dist/runtime/composables/useIntegrationsRuns.js +3 -1
  80. package/dist/runtime/composables/useIntegrationsSchedules.d.ts +3 -2
  81. package/dist/runtime/composables/useIntegrationsSecrets.js +4 -2
  82. package/dist/runtime/composables/useIntegrationsT.d.ts +35 -0
  83. package/dist/runtime/composables/useIntegrationsT.js +9 -0
  84. package/dist/runtime/composables/useIntegrationsTemplates.d.ts +3 -3
  85. package/dist/runtime/composables/useIntegrationsTemplates.js +16 -8
  86. package/dist/runtime/composables/useIntegrationsTriggers.d.ts +23 -3
  87. package/dist/runtime/composables/useIntegrationsTriggers.js +21 -8
  88. package/dist/runtime/composables/useIntegrationsWorkflows.d.ts +10 -1
  89. package/dist/runtime/composables/useIntegrationsWorkflows.js +30 -7
  90. package/dist/runtime/composables/useNodeConfigResolve.js +4 -2
  91. package/dist/runtime/data/integrationNodeCatalog.d.ts +63 -1
  92. package/dist/runtime/data/integrationNodeCatalog.js +22 -4
  93. package/dist/runtime/data/integrationTemplates.d.ts +9 -0
  94. package/dist/runtime/data/integrationTemplates.js +7 -1
  95. package/dist/runtime/data/nodeInputData.d.ts +58 -1
  96. package/dist/runtime/data/nodeInputData.js +30 -5
  97. package/dist/runtime/data/payloadForm.js +2 -1
  98. package/dist/runtime/data/triggerGlyphs.d.ts +47 -0
  99. package/dist/runtime/data/triggerGlyphs.js +11 -0
  100. package/dist/runtime/data/workflowIconCatalog.js +2 -1
  101. package/dist/runtime/i18n/messages/de.json +1224 -0
  102. package/dist/runtime/i18n/messages/en.json +1224 -0
  103. package/dist/runtime/i18n/translate.d.ts +83 -0
  104. package/dist/runtime/i18n/translate.js +41 -0
  105. package/dist/runtime/pages/integrations/audit.vue +52 -34
  106. package/dist/runtime/pages/integrations/credentials/oauth-complete.vue +3 -2
  107. package/dist/runtime/pages/integrations/credentials.vue +73 -52
  108. package/dist/runtime/pages/integrations/index.vue +66 -73
  109. package/dist/runtime/pages/integrations/runs.vue +33 -21
  110. package/dist/runtime/pages/integrations/schedules.vue +49 -28
  111. package/dist/runtime/pages/integrations/secrets.vue +34 -34
  112. package/dist/runtime/pages/integrations/templates/[slug].vue +89 -72
  113. package/dist/runtime/pages/integrations/templates/index.vue +35 -27
  114. package/dist/runtime/pages/integrations/workflows/[id].vue +351 -197
  115. package/dist/runtime/pages/integrations/workflows/index.vue +10 -5
  116. package/dist/runtime/plugins/locale.d.ts +10 -0
  117. package/dist/runtime/plugins/locale.js +6 -0
  118. package/dist/runtime/plugins/realtime.client.d.ts +32 -0
  119. package/dist/runtime/plugins/realtime.client.js +4 -0
  120. package/dist/runtime/types/integrations.d.ts +25 -1
  121. package/dist/runtime/utils/cockpitSources.d.ts +22 -3
  122. package/dist/runtime/utils/credentialState.d.ts +30 -2
  123. package/dist/runtime/utils/credentialState.js +26 -11
  124. package/dist/runtime/utils/localize.d.ts +52 -5
  125. package/dist/runtime/utils/localize.js +47 -9
  126. package/dist/runtime/utils/pollBudget.d.ts +72 -0
  127. package/dist/runtime/utils/pollBudget.js +17 -0
  128. package/dist/runtime/utils/schedule.d.ts +0 -25
  129. package/dist/runtime/utils/schedule.js +37 -25
  130. package/dist/runtime/utils/servedPaths.d.ts +22 -0
  131. package/dist/runtime/utils/servedPaths.js +3 -0
  132. package/dist/runtime/utils/statusStyle.d.ts +26 -2
  133. package/dist/runtime/utils/statusStyle.js +9 -1
  134. package/dist/runtime/utils/validationFindings.d.ts +2 -2
  135. package/dist/runtime/utils/validationFindings.js +4 -3
  136. package/dist/types.d.mts +2 -6
  137. package/package.json +3 -2
@@ -1,6 +1,6 @@
1
1
  <script setup>
2
2
  import { computed, onMounted, ref, watch } from "vue";
3
- import { AddCircle as Plus, TrashBinMinimalistic as Trash2 } from "#solar-icons";
3
+ import { AddCircle as Plus, TrashBinMinimalistic as TrashBin } from "#solar-icons";
4
4
  import {
5
5
  Button,
6
6
  Input,
@@ -23,12 +23,14 @@ const props = defineProps({
23
23
  version: { type: String, required: true },
24
24
  secretKeys: { type: Array, required: true },
25
25
  staticKeys: { type: Array, required: false },
26
+ siblingFields: { type: Array, required: false },
26
27
  depth: { type: Number, required: false },
27
28
  variables: { type: Object, required: false },
28
29
  upstreamOutputs: { type: Array, required: false },
29
30
  refused: { type: String, required: false }
30
31
  });
31
32
  const emit = defineEmits(["set"]);
33
+ const t = useIntegrationsT();
32
34
  const value = computed(() => props.config[props.field.key]);
33
35
  function write(v) {
34
36
  emit("set", props.field.key, v);
@@ -44,18 +46,18 @@ const formatError = computed(() => {
44
46
  const val = props.field.validation;
45
47
  if (!val) return null;
46
48
  if (typeof v === "string") {
47
- if (val.minLength != null && v.length < val.minLength) return `Must be at least ${val.minLength} characters`;
48
- if (val.maxLength != null && v.length > val.maxLength) return `Must be at most ${val.maxLength} characters`;
49
+ if (val.minLength != null && v.length < val.minLength) return t("integration.nodeConfig.validation.minLength", { min: val.minLength });
50
+ if (val.maxLength != null && v.length > val.maxLength) return t("integration.nodeConfig.validation.maxLength", { max: val.maxLength });
49
51
  if (val.pattern) {
50
52
  try {
51
- if (!new RegExp(val.pattern).test(v)) return "Does not match the required format";
53
+ if (!new RegExp(val.pattern).test(v)) return t("integration.nodeConfig.validation.pattern");
52
54
  } catch {
53
55
  }
54
56
  }
55
57
  }
56
58
  if (typeof v === "number") {
57
- if (val.min != null && v < val.min) return `Must be \u2265 ${val.min}`;
58
- if (val.max != null && v > val.max) return `Must be \u2264 ${val.max}`;
59
+ if (val.min != null && v < val.min) return t("integration.nodeConfig.validation.min", { min: val.min });
60
+ if (val.max != null && v > val.max) return t("integration.nodeConfig.validation.max", { max: val.max });
59
61
  }
60
62
  return null;
61
63
  });
@@ -69,9 +71,9 @@ const constraintHint = computed(() => {
69
71
  if (val.min != null) return `\u2265 ${val.min}`;
70
72
  if (val.max != null) return `\u2264 ${val.max}`;
71
73
  } else {
72
- if (val.minLength != null && val.maxLength != null) return `${val.minLength}\u2013${val.maxLength} characters`;
73
- if (val.minLength != null) return `at least ${val.minLength} characters`;
74
- if (val.maxLength != null) return `up to ${val.maxLength} characters`;
74
+ if (val.minLength != null && val.maxLength != null) return t("integration.nodeConfig.hint.charRange", { min: val.minLength, max: val.maxLength });
75
+ if (val.minLength != null) return t("integration.nodeConfig.hint.charMin", { min: val.minLength });
76
+ if (val.maxLength != null) return t("integration.nodeConfig.hint.charMax", { max: val.maxLength });
75
77
  }
76
78
  return "";
77
79
  });
@@ -103,11 +105,11 @@ const bound = computed(() => {
103
105
  const credOptions = computed(
104
106
  () => credsForField(props.field).map((c) => ({ credential: c, state: credentialState(c) }))
105
107
  );
106
- const UNKNOWN_CREDENTIAL = "Unknown credential \u2014 pick again";
108
+ const UNKNOWN_CREDENTIAL = computed(() => t("integration.nodeConfig.credential.unknown"));
107
109
  function credTriggerLabel(field) {
108
110
  const label = resolveLocalized(field.label);
109
- if (bound.value) return `${label}: ${bound.value.credential.name}, ${bound.value.state.label}`;
110
- if (value.value && credentialsLoaded.value) return `${label}: ${UNKNOWN_CREDENTIAL}`;
111
+ if (bound.value) return t("integration.nodeConfig.credential.trigger.bound", { label, name: bound.value.credential.name, state: bound.value.state.label });
112
+ if (value.value && credentialsLoaded.value) return t("integration.nodeConfig.credential.trigger.unknown", { label, unknown: UNKNOWN_CREDENTIAL.value });
111
113
  return label;
112
114
  }
113
115
  const credCreateOpen = ref(false);
@@ -126,8 +128,8 @@ function onCredentialSelect(v) {
126
128
  write(v);
127
129
  }
128
130
  function acceptsSftp(field) {
129
- const t = field.credentialType;
130
- return t === "revenexx:sftp" || Array.isArray(t) && t.includes("revenexx:sftp");
131
+ const t2 = field.credentialType;
132
+ return t2 === "revenexx:sftp" || Array.isArray(t2) && t2.includes("revenexx:sftp");
131
133
  }
132
134
  const EXPR_TOKEN = "${{";
133
135
  const canToggleExpression = computed(
@@ -231,7 +233,7 @@ function writeJson(text) {
231
233
  jsonError.value = null;
232
234
  emit("set", props.field.key, parsed);
233
235
  } catch {
234
- jsonError.value = "Not valid JSON yet \u2014 the value is kept once it parses.";
236
+ jsonError.value = t("integration.nodeConfig.json.invalid");
235
237
  }
236
238
  }
237
239
  function readArray() {
@@ -266,28 +268,28 @@ function setArrayItemField(index, fieldKey, v) {
266
268
  without the required marker, which is a state and not part of a
267
269
  name. -->
268
270
  <Label class="text-mono" :class="invalid ? 'text-rose-600' : 'text-muted-foreground'">
269
- {{ resolveLocalized(field.label) }}<span v-if="field.required" class="text-rose-500" title="Required"> *</span>
271
+ {{ resolveLocalized(field.label) }}<span v-if="field.required" class="text-rose-500" :title="t('integration.nodeConfig.field.requiredMark')"> *</span>
270
272
  </Label>
271
273
  <div class="flex shrink-0 items-center gap-1.5">
272
274
  <div v-if="isObjectField && !showExpression" class="flex items-center rounded border border-black/[0.08] bg-white p-px">
273
275
  <button
274
276
  type="button"
275
277
  :disabled="!flatCapable"
276
- title="Edit as key/value rows"
278
+ :title="t('integration.nodeConfig.object.toggle.fieldsTitle')"
277
279
  class="rounded px-1 py-px text-[10px] font-medium transition disabled:pointer-events-none disabled:opacity-40"
278
280
  :class="objectMode === 'form' ? 'bg-slate-100 text-foreground' : 'text-muted-foreground hover:text-foreground'"
279
281
  @click="objectMode = 'form'"
280
282
  >
281
- Fields
283
+ {{ t("integration.nodeConfig.object.toggle.fields") }}
282
284
  </button>
283
285
  <button
284
286
  type="button"
285
- title="Edit as raw JSON"
287
+ :title="t('integration.nodeConfig.object.toggle.jsonTitle')"
286
288
  class="rounded px-1 py-px text-[10px] font-medium transition"
287
289
  :class="objectMode === 'json' ? 'bg-slate-100 text-foreground' : 'text-muted-foreground hover:text-foreground'"
288
290
  @click="objectMode = 'json'"
289
291
  >
290
- JSON
292
+ {{ t("integration.nodeConfig.object.toggle.json") }}
291
293
  </button>
292
294
  </div>
293
295
  <button
@@ -295,7 +297,7 @@ function setArrayItemField(index, fieldKey, v) {
295
297
  type="button"
296
298
  class="rounded border px-1 py-0.5 font-mono text-[10px] transition"
297
299
  :class="showExpression ? 'border-emerald-500/40 bg-emerald-500/15 text-emerald-700' : 'border-black/[0.08] bg-white text-muted-foreground hover:text-foreground'"
298
- :title="showExpression ? 'Back to the regular control (clears the expression)' : 'Map this value from a previous step instead'"
300
+ :title="showExpression ? t('integration.nodeConfig.expression.toggle.off') : t('integration.nodeConfig.expression.toggle.on')"
299
301
  @click="toggleExpression"
300
302
  >
301
303
  {{ exprHint }}
@@ -303,7 +305,7 @@ function setArrayItemField(index, fieldKey, v) {
303
305
  <span
304
306
  v-else-if="field.expressionAllowed"
305
307
  class="rounded border border-emerald-500/25 bg-emerald-500/10 px-1 py-0.5 font-mono text-[10px] text-emerald-600"
306
- title="Accepts ${{ … }} expressions"
308
+ :title="t('integration.nodeConfig.expression.badge.title')"
307
309
  >
308
310
  {{ exprHint }}
309
311
  </span>
@@ -338,26 +340,26 @@ function setArrayItemField(index, fieldKey, v) {
338
340
  <!-- A repeating row has no room for visible labels, so each control
339
341
  names itself and says which row of which field it belongs to. -->
340
342
  <Input
341
- :aria-label="`${resolveLocalized(field.label)} \u2014 key of row ${idx + 1}`"
343
+ :aria-label="t('integration.nodeConfig.object.row.keyAria', { label: resolveLocalized(field.label), n: idx + 1 })"
342
344
  :model-value="row.key"
343
- placeholder="key"
345
+ :placeholder="t('integration.nodeConfig.object.row.keyPlaceholder')"
344
346
  class="h-7 w-[38%] rounded-md border border-black/[0.08] ring-0 shadow-none bg-white text-sm"
345
347
  @update:model-value="(v) => setKvRow(idx, { key: String(v) })"
346
348
  />
347
349
  <Input
348
- :aria-label="`${resolveLocalized(field.label)} \u2014 value of row ${idx + 1}`"
350
+ :aria-label="t('integration.nodeConfig.object.row.valueAria', { label: resolveLocalized(field.label), n: idx + 1 })"
349
351
  :model-value="row.value"
350
- placeholder="value"
352
+ :placeholder="t('integration.nodeConfig.object.row.valuePlaceholder')"
351
353
  class="h-7 min-w-0 flex-1 rounded-md border border-black/[0.08] ring-0 shadow-none bg-white text-sm"
352
354
  @update:model-value="(v) => setKvRow(idx, { value: String(v) })"
353
355
  />
354
356
  <button
355
357
  type="button"
356
- :aria-label="`Remove row ${idx + 1} of ${resolveLocalized(field.label)}`"
358
+ :aria-label="t('integration.nodeConfig.object.row.removeAria', { label: resolveLocalized(field.label), n: idx + 1 })"
357
359
  class="flex size-6 shrink-0 items-center justify-center rounded text-muted-foreground transition hover:text-rose-600"
358
360
  @click="removeKvRow(idx)"
359
361
  >
360
- <Trash2 :size="11" />
362
+ <TrashBin :size="11" />
361
363
  </button>
362
364
  </div>
363
365
  <Button
@@ -368,7 +370,7 @@ function setArrayItemField(index, fieldKey, v) {
368
370
  @click="addKvRow"
369
371
  >
370
372
  <Plus :size="11" />
371
- Add row
373
+ {{ t("integration.nodeConfig.action.addRow") }}
372
374
  </Button>
373
375
  </div>
374
376
 
@@ -394,6 +396,7 @@ function setArrayItemField(index, fieldKey, v) {
394
396
  :field="field"
395
397
  :config="config"
396
398
  :model-value="value"
399
+ :sibling-fields="siblingFields"
397
400
  @update:model-value="write"
398
401
  />
399
402
 
@@ -464,7 +467,7 @@ function setArrayItemField(index, fieldKey, v) {
464
467
  @update:model-value="(v) => write(v)"
465
468
  >
466
469
  <SelectTrigger :aria-label="resolveLocalized(field.label)" size="sm" class="h-8 w-full rounded-md border border-black/[0.08] ring-0 shadow-none text-sm bg-white">
467
- <SelectValue :placeholder="resolveLocalized(field.placeholder) || 'Pick one'" />
470
+ <SelectValue :placeholder="resolveLocalized(field.placeholder) || t('integration.nodeConfig.select.placeholder')" />
468
471
  </SelectTrigger>
469
472
  <SelectContent>
470
473
  <SelectItem v-for="opt in field.options ?? []" :key="String(opt.value)" :value="String(opt.value)">
@@ -498,7 +501,7 @@ function setArrayItemField(index, fieldKey, v) {
498
501
  @update:model-value="(v) => write(v)"
499
502
  >
500
503
  <SelectTrigger :aria-label="resolveLocalized(field.label)" size="sm" class="h-8 w-full rounded-md border border-black/[0.08] ring-0 shadow-none text-sm bg-white">
501
- <SelectValue placeholder="Pick a secret" />
504
+ <SelectValue :placeholder="t('integration.nodeConfig.secretRef.placeholder')" />
502
505
  </SelectTrigger>
503
506
  <SelectContent>
504
507
  <SelectItem v-for="k in secretKeys" :key="k" :value="k">{{ k }}</SelectItem>
@@ -520,7 +523,7 @@ function setArrayItemField(index, fieldKey, v) {
520
523
  "Revenexx APIConnected". Rendering it here keeps the pill a pill, and
521
524
  the placeholder has to be drawn by hand because a slot replaces the
522
525
  fallback that would otherwise show it. -->
523
- <SelectValue placeholder="Pick a credential">
526
+ <SelectValue :placeholder="t('integration.nodeConfig.credential.placeholder')">
524
527
  <span v-if="bound" class="flex min-w-0 items-center gap-1.5">
525
528
  <span class="min-w-0 truncate">{{ bound.credential.name }}</span>
526
529
  <!-- No `title` on this badge: `SelectValue` renders itself with
@@ -539,7 +542,7 @@ function setArrayItemField(index, fieldKey, v) {
539
542
  while the workflow still runs into the dead reference. `loaded`
540
543
  keeps the wait before the first response out of it. -->
541
544
  <span v-else-if="value && credentialsLoaded" class="text-rose-600">{{ UNKNOWN_CREDENTIAL }}</span>
542
- <span v-else class="text-muted-foreground">Pick a credential</span>
545
+ <span v-else class="text-muted-foreground">{{ t("integration.nodeConfig.credential.placeholder") }}</span>
543
546
  </SelectValue>
544
547
  </SelectTrigger>
545
548
  <SelectContent>
@@ -562,24 +565,24 @@ function setArrayItemField(index, fieldKey, v) {
562
565
  </span>
563
566
  </SelectItem>
564
567
  <div v-if="!credOptions.length" class="px-2 py-1.5 text-sm text-muted-foreground">
565
- No credentials yet.
568
+ {{ t("integration.nodeConfig.credential.empty") }}
566
569
  </div>
567
570
  <SelectSeparator />
568
571
  <SelectItem :value="CRED_ACTION_NEW" class="font-medium text-violet-600 data-[highlighted]:text-violet-700">
569
- New credential
572
+ {{ t("integration.nodeConfig.credential.action.new") }}
570
573
  </SelectItem>
571
574
  <SelectItem v-if="acceptsSftp(field)" :value="CRED_ACTION_SFTP" class="font-medium text-violet-600 data-[highlighted]:text-violet-700">
572
- Import existing Revenue Cloud SFTP account
575
+ {{ t("integration.nodeConfig.credential.action.importSftp") }}
573
576
  </SelectItem>
574
577
  </SelectContent>
575
578
  </Select>
576
579
  <!-- What the state has to add, as text rather than as a tooltip — the shape
577
580
  the detail dialog uses beneath its own badge. -->
578
581
  <p v-if="bound?.state.key === 'needs_reauth'" class="text-sm text-amber-600">
579
- This credential needs reconnect.
582
+ {{ t("integration.nodeConfig.credential.state.needsReauth") }}
580
583
  </p>
581
584
  <p v-else-if="bound?.state.key === 'failed' && bound.state.message" class="text-sm break-words text-rose-600">
582
- Last test failed: {{ bound.state.message }}
585
+ {{ t("integration.nodeConfig.credential.state.failed", { message: bound.state.message }) }}
583
586
  </p>
584
587
  <IntegrationsCredentialCreateDialog
585
588
  v-model:open="credCreateOpen"
@@ -606,11 +609,11 @@ function setArrayItemField(index, fieldKey, v) {
606
609
  </span>
607
610
  <button
608
611
  type="button"
609
- :aria-label="`Remove item ${idx + 1} of ${resolveLocalized(field.label)}`"
612
+ :aria-label="t('integration.nodeConfig.array.removeItemAria', { label: resolveLocalized(field.label), n: idx + 1 })"
610
613
  class="flex size-5 items-center justify-center rounded text-muted-foreground hover:text-rose-600"
611
614
  @click="removeArrayItem(idx)"
612
615
  >
613
- <Trash2 :size="11" />
616
+ <TrashBin :size="11" />
614
617
  </button>
615
618
  </div>
616
619
  <div
@@ -624,7 +627,7 @@ function setArrayItemField(index, fieldKey, v) {
624
627
  <!-- The label above repeats per item, so it cannot own an id; the
625
628
  input carries its own name including the item number. -->
626
629
  <Input
627
- :aria-label="`${resolveLocalized(sub.label)} of item ${idx + 1}`"
630
+ :aria-label="t('integration.nodeConfig.array.subFieldAria', { label: resolveLocalized(sub.label), n: idx + 1 })"
628
631
  :model-value="item[sub.key] ?? ''"
629
632
  :placeholder="resolveLocalized(sub.placeholder)"
630
633
  class="h-7 text-sm bg-white"
@@ -641,7 +644,7 @@ function setArrayItemField(index, fieldKey, v) {
641
644
  @click="pushArrayItem(Object.fromEntries((field.items?.properties ?? []).map((p) => [p.key, ''])))"
642
645
  >
643
646
  <Plus :size="11" />
644
- Add row
647
+ {{ t("integration.nodeConfig.action.addRow") }}
645
648
  </Button>
646
649
  </div>
647
650
 
@@ -6,6 +6,9 @@ type __VLS_Props = {
6
6
  version: string;
7
7
  secretKeys: string[];
8
8
  staticKeys?: string[];
9
+ /** The whole set of settings this one belongs to — a `dynamic` field names the
10
+ * ones it waits for out of it (NodeConfigDynamicField). */
11
+ siblingFields?: ConfigField[];
9
12
  depth?: number;
10
13
  variables?: Record<string, WorkflowVariable>;
11
14
  upstreamOutputs?: Array<{
@@ -28,13 +28,13 @@ type __VLS_Props = {
28
28
  refusedFields?: Record<string, string>;
29
29
  };
30
30
  declare const _default: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
31
- close: () => any;
32
31
  update: (next: WorkflowTrigger | WorkflowNode) => any;
32
+ close: () => any;
33
33
  remove: () => any;
34
34
  navigate: (id: string) => any;
35
35
  }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
36
- onClose?: (() => any) | undefined;
37
36
  onUpdate?: ((next: WorkflowTrigger | WorkflowNode) => any) | undefined;
37
+ onClose?: (() => any) | undefined;
38
38
  onRemove?: (() => any) | undefined;
39
39
  onNavigate?: ((id: string) => any) | undefined;
40
40
  }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;