@revenexx/studio-integrations 0.7.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.
- package/CHANGELOG.md +416 -0
- package/dist/module.json +1 -1
- package/dist/module.mjs +2 -1
- package/dist/runtime/components/Integrations/CredentialCreateDialog.vue +4 -1
- package/dist/runtime/components/Integrations/CredentialDetailDialog.vue +8 -2
- package/dist/runtime/components/Integrations/ExpressionInput.vue +5 -0
- package/dist/runtime/components/Integrations/NodeConfigDynamicField.d.vue.ts +3 -0
- package/dist/runtime/components/Integrations/NodeConfigDynamicField.vue +16 -3
- package/dist/runtime/components/Integrations/NodeConfigDynamicField.vue.d.ts +3 -0
- package/dist/runtime/components/Integrations/NodeConfigDynamicSchema.vue +1 -0
- package/dist/runtime/components/Integrations/NodeConfigField.d.vue.ts +3 -0
- package/dist/runtime/components/Integrations/NodeConfigField.vue +2 -0
- package/dist/runtime/components/Integrations/NodeConfigField.vue.d.ts +3 -0
- package/dist/runtime/components/Integrations/NodeInspector.d.vue.ts +2 -2
- package/dist/runtime/components/Integrations/NodeInspector.vue +95 -10
- package/dist/runtime/components/Integrations/NodeInspector.vue.d.ts +2 -2
- package/dist/runtime/components/Integrations/NodeOutputSchema.d.vue.ts +2 -1
- package/dist/runtime/components/Integrations/NodeOutputSchema.vue +34 -8
- package/dist/runtime/components/Integrations/NodeOutputSchema.vue.d.ts +2 -1
- package/dist/runtime/components/Integrations/NodePalette.vue +12 -8
- package/dist/runtime/components/Integrations/StickyNoteView.vue +45 -11
- package/dist/runtime/components/Integrations/SurfaceCard.d.vue.ts +50 -0
- package/dist/runtime/components/Integrations/SurfaceCard.vue +55 -0
- package/dist/runtime/components/Integrations/SurfaceCard.vue.d.ts +50 -0
- package/dist/runtime/components/Integrations/TemplateCard.vue +3 -3
- package/dist/runtime/components/Integrations/TemplateFlowPreview.vue +12 -3
- package/dist/runtime/components/Integrations/WorkflowCreateDialog.vue +5 -4
- package/dist/runtime/components/Integrations/WorkflowEditor.d.vue.ts +16 -0
- package/dist/runtime/components/Integrations/WorkflowEditor.vue +52 -11
- package/dist/runtime/components/Integrations/WorkflowEditor.vue.d.ts +16 -0
- package/dist/runtime/components/Integrations/WorkflowHistoryPanel.d.vue.ts +6 -0
- package/dist/runtime/components/Integrations/WorkflowHistoryPanel.vue +4 -2
- package/dist/runtime/components/Integrations/WorkflowHistoryPanel.vue.d.ts +6 -0
- package/dist/runtime/components/Integrations/WorkflowNodeView.vue +59 -28
- package/dist/runtime/components/Integrations/WorkflowRunReplayPanel.vue +3 -4
- package/dist/runtime/components/Integrations/WorkflowSettingsDialog.d.vue.ts +16 -17
- package/dist/runtime/components/Integrations/WorkflowSettingsDialog.vue +37 -26
- package/dist/runtime/components/Integrations/WorkflowSettingsDialog.vue.d.ts +16 -17
- package/dist/runtime/components/Integrations/WorkflowTriggerView.vue +4 -5
- package/dist/runtime/components/Integrations/WorkflowVariablesDialog.vue +53 -14
- package/dist/runtime/composables/useIntegrationsCredentials.d.ts +11 -0
- package/dist/runtime/composables/useIntegrationsCredentials.js +2 -2
- package/dist/runtime/composables/useIntegrationsNodes.js +11 -1
- package/dist/runtime/composables/useIntegrationsSchedules.d.ts +3 -2
- package/dist/runtime/composables/useIntegrationsTriggers.d.ts +23 -3
- package/dist/runtime/composables/useIntegrationsTriggers.js +18 -7
- package/dist/runtime/composables/useIntegrationsWorkflows.d.ts +10 -1
- package/dist/runtime/composables/useIntegrationsWorkflows.js +25 -4
- package/dist/runtime/composables/useNodeConfigResolve.js +2 -1
- package/dist/runtime/data/integrationNodeCatalog.d.ts +22 -1
- package/dist/runtime/data/integrationNodeCatalog.js +8 -4
- package/dist/runtime/data/nodeInputData.d.ts +58 -1
- package/dist/runtime/data/nodeInputData.js +30 -5
- package/dist/runtime/data/triggerGlyphs.d.ts +47 -0
- package/dist/runtime/data/triggerGlyphs.js +11 -0
- package/dist/runtime/i18n/messages/de.json +20 -3
- package/dist/runtime/i18n/messages/en.json +20 -3
- package/dist/runtime/pages/integrations/credentials.vue +4 -1
- package/dist/runtime/pages/integrations/index.vue +16 -17
- package/dist/runtime/pages/integrations/templates/[slug].vue +13 -5
- package/dist/runtime/pages/integrations/workflows/[id].vue +193 -104
- package/dist/runtime/types/integrations.d.ts +25 -1
- package/dist/runtime/utils/cockpitSources.d.ts +22 -3
- package/dist/runtime/utils/credentialState.d.ts +6 -1
- package/dist/runtime/utils/credentialState.js +1 -1
- package/dist/runtime/utils/localize.d.ts +34 -0
- package/dist/runtime/utils/localize.js +22 -7
- package/dist/runtime/utils/pollBudget.d.ts +1 -1
- package/dist/runtime/utils/servedPaths.d.ts +22 -0
- package/dist/runtime/utils/servedPaths.js +3 -0
- package/package.json +1 -1
|
@@ -59,6 +59,28 @@ export declare const TRIGGER_TYPE_WORDS: readonly ["manual", "schedule", "webhoo
|
|
|
59
59
|
* key itself on screen, because a missing message resolves to its own name.
|
|
60
60
|
*/
|
|
61
61
|
export declare function triggerTypeLabel(type: TriggerType | string | null | undefined): string;
|
|
62
|
+
/**
|
|
63
|
+
* Whether this exact node version can be executed — its code was built, so
|
|
64
|
+
* `execute:test` and `config:resolve` will not be refused (PO-338).
|
|
65
|
+
*
|
|
66
|
+
* Deliberately NOT `findManifest(...)?.__hasBundle !== false`: that helper falls
|
|
67
|
+
* back to the newest registered version when the pinned one is absent, so a node
|
|
68
|
+
* pinned to a withdrawn version would inherit a stranger's build state and every
|
|
69
|
+
* surface would then say something about a version the catalogue does not have.
|
|
70
|
+
* Only an exact match may answer, and anything else — unknown version, catalogue
|
|
71
|
+
* not loaded yet, a host whose API does not report the field — reads as
|
|
72
|
+
* executable, because the alternative is a studio that hides working nodes.
|
|
73
|
+
*/
|
|
74
|
+
export declare function isVersionExecutable(slug: string, version: string): boolean;
|
|
75
|
+
/**
|
|
76
|
+
* The newest version of this node that can be executed, or `null` when none can.
|
|
77
|
+
*
|
|
78
|
+
* What a surface tells the operator to move to. The catalogue is semver-descending
|
|
79
|
+
* within a slug, so the first executable entry is the newest one. `null` where the
|
|
80
|
+
* whole node is unusable — then there is nothing to point at and the surface says
|
|
81
|
+
* only what is wrong.
|
|
82
|
+
*/
|
|
83
|
+
export declare function newestExecutableVersion(slug: string): string | null;
|
|
62
84
|
export declare function findManifest(slug: string, version?: string): NodeManifest | null;
|
|
63
85
|
export declare const TRIGGER_OUTPUT_PORT = "default";
|
|
64
86
|
export interface TriggerTemplate {
|
|
@@ -75,7 +97,6 @@ export interface TriggerTemplate {
|
|
|
75
97
|
en: string;
|
|
76
98
|
de: string;
|
|
77
99
|
};
|
|
78
|
-
icon: string;
|
|
79
100
|
/** Default config the canvas seeds when the user drops the tile. */
|
|
80
101
|
defaultConfig: Record<string, unknown>;
|
|
81
102
|
}
|
|
@@ -26,6 +26,14 @@ export function triggerTypeLabel(type) {
|
|
|
26
26
|
if (!TRIGGER_TYPE_WORD_SET.has(kind)) return kind;
|
|
27
27
|
return useIntegrationsT()(`integration.common.triggerType.${kind}`);
|
|
28
28
|
}
|
|
29
|
+
export function isVersionExecutable(slug, version) {
|
|
30
|
+
const exact = INTEGRATION_NODE_CATALOG.find((m) => m.slug === slug && m.version === version);
|
|
31
|
+
return exact ? exact.__hasBundle !== false : true;
|
|
32
|
+
}
|
|
33
|
+
export function newestExecutableVersion(slug) {
|
|
34
|
+
const hit = INTEGRATION_NODE_CATALOG.find((m) => m.slug === slug && m.__hasBundle !== false);
|
|
35
|
+
return hit?.version ?? null;
|
|
36
|
+
}
|
|
29
37
|
export function findManifest(slug, version) {
|
|
30
38
|
const matches = INTEGRATION_NODE_CATALOG.filter((m) => m.slug === slug);
|
|
31
39
|
if (!matches.length) return null;
|
|
@@ -42,7 +50,6 @@ export const TRIGGER_TEMPLATES = [
|
|
|
42
50
|
en: "Start the workflow yourself with the Run button or an API call.",
|
|
43
51
|
de: "Starte den Workflow selbst \xFCber den Run-Button oder einen API-Aufruf."
|
|
44
52
|
},
|
|
45
|
-
icon: "Play",
|
|
46
53
|
defaultConfig: {}
|
|
47
54
|
},
|
|
48
55
|
{
|
|
@@ -53,7 +60,6 @@ export const TRIGGER_TEMPLATES = [
|
|
|
53
60
|
en: "Runs automatically on a schedule you define, for example every night.",
|
|
54
61
|
de: "L\xE4uft automatisch nach deinem Zeitplan, zum Beispiel jede Nacht."
|
|
55
62
|
},
|
|
56
|
-
icon: "Clock",
|
|
57
63
|
defaultConfig: { cron: "0 * * * *", timezone: "Europe/Berlin" }
|
|
58
64
|
},
|
|
59
65
|
{
|
|
@@ -64,7 +70,6 @@ export const TRIGGER_TEMPLATES = [
|
|
|
64
70
|
en: "Starts when an external system calls this workflow's URL.",
|
|
65
71
|
de: "Startet, wenn ein externes System die URL dieses Workflows aufruft."
|
|
66
72
|
},
|
|
67
|
-
icon: "Webhook",
|
|
68
73
|
defaultConfig: { path: "/incoming", method: "POST" }
|
|
69
74
|
},
|
|
70
75
|
{
|
|
@@ -75,7 +80,6 @@ export const TRIGGER_TEMPLATES = [
|
|
|
75
80
|
en: "Starts when something happens on the platform, for example a new customer.",
|
|
76
81
|
de: "Startet, wenn auf der Plattform etwas passiert, zum Beispiel ein neuer Kunde."
|
|
77
82
|
},
|
|
78
|
-
icon: "Zap",
|
|
79
83
|
defaultConfig: { event_type: "" }
|
|
80
84
|
}
|
|
81
85
|
];
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { NodeManifest, NodeManifestOutput, OutputKind, WorkflowEdge, WorkflowNode } from '../types/integrations.js';
|
|
1
|
+
import type { LocalizedString, NodeManifest, NodeManifestOutput, OutputKind, WorkflowEdge, WorkflowNode } from '../types/integrations.js';
|
|
2
2
|
/** `${{ nodes.<id>.outputs.<port> }}`, optionally narrowed to one field. */
|
|
3
3
|
export declare function nodeOutputExpr(nodeId: string, port: string, field?: string): string;
|
|
4
4
|
/** `${{ trigger.payload }}`, optionally narrowed to a path inside it. */
|
|
@@ -20,13 +20,70 @@ export interface FieldEntry {
|
|
|
20
20
|
name: string;
|
|
21
21
|
/** Declared data type (`string`, `number`, `boolean`, …) when the manifest carries one. */
|
|
22
22
|
dataType: string | null;
|
|
23
|
+
/**
|
|
24
|
+
* What the publisher says this field holds, unresolved. Kept in the shape the
|
|
25
|
+
* manifest sent it so the language is chosen where it is drawn — see the note
|
|
26
|
+
* on {@link PortEntry.description}.
|
|
27
|
+
*/
|
|
28
|
+
description?: LocalizedString;
|
|
23
29
|
}
|
|
24
30
|
export interface PortEntry {
|
|
25
31
|
name: string;
|
|
26
32
|
kind: OutputKind;
|
|
27
33
|
/** Declared payload fields of this port (schema; may be empty). */
|
|
28
34
|
fields: FieldEntry[];
|
|
35
|
+
/**
|
|
36
|
+
* What the publisher says flows through this port, **unresolved** — the
|
|
37
|
+
* localised map as the manifest sent it, not a string.
|
|
38
|
+
*
|
|
39
|
+
* Resolving here would pick the language at the moment the ports are derived,
|
|
40
|
+
* and these helpers are pure by design: their tests would then depend on the
|
|
41
|
+
* studio's locale, and a language switched afterwards would not reach a port
|
|
42
|
+
* list already computed. So the raw value travels and whoever draws it calls
|
|
43
|
+
* the resolver, which is how every other manifest string in the studio is
|
|
44
|
+
* read. Absent rather than null when the publisher wrote none: most do not,
|
|
45
|
+
* and an optional key keeps the derivation's shape honest about that.
|
|
46
|
+
*/
|
|
47
|
+
description?: LocalizedString;
|
|
29
48
|
}
|
|
49
|
+
export interface InputPortEntry {
|
|
50
|
+
name: string;
|
|
51
|
+
/** Declared data type of what the node accepts here. */
|
|
52
|
+
dataType: string | null;
|
|
53
|
+
/**
|
|
54
|
+
* Whether an upstream connection is mandatory.
|
|
55
|
+
*
|
|
56
|
+
* Most manifests leave the key out, so the default decides what nearly every
|
|
57
|
+
* node claims. It is **the service's**, not ours: the node-manifest schema
|
|
58
|
+
* documents `required` as *"Whether an upstream connection is mandatory.
|
|
59
|
+
* Defaults to true."*, so an absent key is read as required and the chip says
|
|
60
|
+
* so. Reading it the other way would have the studio call a mandatory input
|
|
61
|
+
* optional on the strength of a key nobody wrote.
|
|
62
|
+
*
|
|
63
|
+
* It is a statement about what the type **declares**, not a rule anything
|
|
64
|
+
* enforces — nothing in the studio blocks a save over it, and the service's own
|
|
65
|
+
* blob validation checks that a named port exists rather than that it is fed.
|
|
66
|
+
*/
|
|
67
|
+
required: boolean;
|
|
68
|
+
/** What the publisher says the node expects, unresolved — see {@link PortEntry.description}. */
|
|
69
|
+
description?: LocalizedString;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* What a node accepts, read from the manifest's `inputs` map.
|
|
73
|
+
*
|
|
74
|
+
* **Two wire shapes, one of them a trap.** A node with named ports arrives as an
|
|
75
|
+
* object; a node that accepts nothing arrives as `[]`, because the service
|
|
76
|
+
* encodes an empty PHP array as a JSON array rather than an object. So the empty
|
|
77
|
+
* case has a different JSON *type* from the populated one, and reading it with
|
|
78
|
+
* `Object.entries` alone would work by luck rather than by intent. Both land here
|
|
79
|
+
* as an empty list.
|
|
80
|
+
*
|
|
81
|
+
* The studio read this under a singular `input` key until PO-216 — a shape the
|
|
82
|
+
* service has never sent and its schema forbids, so every input a node declared
|
|
83
|
+
* was invisible: the sentence, the data type, and whether a connection was
|
|
84
|
+
* mandatory at all.
|
|
85
|
+
*/
|
|
86
|
+
export declare function inputPortsForManifest(manifest: NodeManifest | null | undefined): InputPortEntry[];
|
|
30
87
|
/**
|
|
31
88
|
* A manifest declaring zero output ports describes a TERMINAL node (PO-201):
|
|
32
89
|
* the run ends there, so it gets no output handle and no edge may leave it.
|
|
@@ -37,13 +37,38 @@ export function collectUpstreamNodes(selectedId, nodes, edges) {
|
|
|
37
37
|
function fieldEntries(raw) {
|
|
38
38
|
if (Array.isArray(raw)) return raw.map((v) => ({ name: String(v), dataType: null }));
|
|
39
39
|
if (raw && typeof raw === "object") {
|
|
40
|
-
return Object.entries(raw).map(([name, spec]) =>
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
40
|
+
return Object.entries(raw).map(([name, spec]) => {
|
|
41
|
+
const s = spec && typeof spec === "object" ? spec : null;
|
|
42
|
+
return {
|
|
43
|
+
name,
|
|
44
|
+
dataType: typeof s?.dataType === "string" ? s.dataType : null,
|
|
45
|
+
...describedBy(s?.description)
|
|
46
|
+
};
|
|
47
|
+
});
|
|
44
48
|
}
|
|
45
49
|
return [];
|
|
46
50
|
}
|
|
51
|
+
function describedBy(raw) {
|
|
52
|
+
if (typeof raw === "string") return raw.trim() ? { description: raw.trim() } : {};
|
|
53
|
+
if (raw && typeof raw === "object" && !Array.isArray(raw)) {
|
|
54
|
+
const entries = Object.entries(raw).filter((e) => typeof e[1] === "string" && e[1].trim() !== "").map(([k, v]) => [k, v.trim()]);
|
|
55
|
+
return entries.length ? { description: Object.fromEntries(entries) } : {};
|
|
56
|
+
}
|
|
57
|
+
return {};
|
|
58
|
+
}
|
|
59
|
+
export function inputPortsForManifest(manifest) {
|
|
60
|
+
const raw = manifest?.inputs;
|
|
61
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return [];
|
|
62
|
+
return Object.entries(raw).map(([name, spec]) => {
|
|
63
|
+
const s = spec && typeof spec === "object" ? spec : null;
|
|
64
|
+
return {
|
|
65
|
+
name,
|
|
66
|
+
dataType: typeof s?.dataType === "string" ? s.dataType : null,
|
|
67
|
+
required: s?.required !== false,
|
|
68
|
+
...describedBy(s?.description)
|
|
69
|
+
};
|
|
70
|
+
});
|
|
71
|
+
}
|
|
47
72
|
export function isTerminalManifest(manifest) {
|
|
48
73
|
return !!manifest && Array.isArray(manifest.outputs) && !manifest.outputs.length;
|
|
49
74
|
}
|
|
@@ -53,7 +78,7 @@ export function portsForNode(node, manifest, resolvedOutputs) {
|
|
|
53
78
|
const ports = [];
|
|
54
79
|
for (const o of list) {
|
|
55
80
|
if (o.name) {
|
|
56
|
-
ports.push({ name: o.name, kind: o.kind, fields: fieldEntries(o.fields) });
|
|
81
|
+
ports.push({ name: o.name, kind: o.kind, fields: fieldEntries(o.fields), ...describedBy(o.description) });
|
|
57
82
|
} else if (o.sourceFromConfig) {
|
|
58
83
|
const raw = node.config?.[o.sourceFromConfig];
|
|
59
84
|
const values = Array.isArray(raw) ? raw : [];
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { Component } from 'vue';
|
|
2
|
+
import type { TriggerType } from '../types/integrations.js';
|
|
3
|
+
/**
|
|
4
|
+
* Trigger type → glyph, for every surface that draws one.
|
|
5
|
+
*
|
|
6
|
+
* Eight files drew a trigger's glyph, and they did it two ways. Five spelled the
|
|
7
|
+
* mapping out — the canvas card and the run replay panel as ternaries, the node
|
|
8
|
+
* inspector's header, the template card and the template detail page as records —
|
|
9
|
+
* and three did not carry a mapping at all: they resolved a glyph from Lucide by
|
|
10
|
+
* *name*, so no import mentioned one, in the node palette's trigger tiles (through
|
|
11
|
+
* `nodeIcon`), the create dialog's trigger select and the template flow preview
|
|
12
|
+
* (each through a local `iconFor`).
|
|
13
|
+
*
|
|
14
|
+
* They did not agree: a webhook was a globe on the templates page, a link on the
|
|
15
|
+
* canvas and Lucide's pretzel in the palette, so the same trigger changed shape
|
|
16
|
+
* three times on the way from picking it to seeing it. The replay panel's ternary
|
|
17
|
+
* had no `event` branch at all, so an event-triggered run drew the manual play
|
|
18
|
+
* button — wrong information, not a mismatch (PO-329).
|
|
19
|
+
*
|
|
20
|
+
* One map instead, and the reason it can be Solar's: `TRIGGER_TEMPLATES` is
|
|
21
|
+
* this repository's own catalogue, not a node manifest, so its `icon` strings
|
|
22
|
+
* ('Play', 'Clock', 'Webhook', 'Zap') were Lucide names by habit rather than by
|
|
23
|
+
* contract. The node-type icon map (`nodeIcons.ts`) is a different thing and
|
|
24
|
+
* stays Lucide deliberately — see `docs/icons.md`.
|
|
25
|
+
*/
|
|
26
|
+
export declare const TRIGGER_GLYPHS: Record<TriggerType, Component>;
|
|
27
|
+
/**
|
|
28
|
+
* The glyph for a trigger type.
|
|
29
|
+
*
|
|
30
|
+
* The parameter is deliberately wider than `TriggerType`: some callers read the
|
|
31
|
+
* type out of a template blob or a run record, where the service may name a kind
|
|
32
|
+
* the studio has no word for yet — `templates/[slug].vue` is built for exactly
|
|
33
|
+
* that case and says so.
|
|
34
|
+
*
|
|
35
|
+
* Such a kind falls back to the **bolt**, not to the manual play button, which is
|
|
36
|
+
* what the templates surfaces did before this map existed. A play button asserts
|
|
37
|
+
* "you start this yourself", and for an unknown kind that is a claim rather than a
|
|
38
|
+
* gap; the bolt says only that something fires it.
|
|
39
|
+
*
|
|
40
|
+
* `Object.hasOwn`, not `?? Bolt` on its own: a plain object inherits
|
|
41
|
+
* `Object.prototype`, so a service that named a trigger kind `constructor` or
|
|
42
|
+
* `toString` would resolve to a function, the `??` would never fire, and the card
|
|
43
|
+
* would try to render `<component :is="Object">`. The `Lucide[name]` lookups this
|
|
44
|
+
* map replaced could not do that — a module namespace object has a null prototype
|
|
45
|
+
* — so the guard is what keeps the docstring above true rather than nearly true.
|
|
46
|
+
*/
|
|
47
|
+
export declare function triggerGlyph(type?: string | null): Component;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Bolt, ClockCircle, LinkRound, Play } from "#solar-icons";
|
|
2
|
+
export const TRIGGER_GLYPHS = {
|
|
3
|
+
manual: Play,
|
|
4
|
+
schedule: ClockCircle,
|
|
5
|
+
webhook: LinkRound,
|
|
6
|
+
event: Bolt
|
|
7
|
+
};
|
|
8
|
+
export function triggerGlyph(type) {
|
|
9
|
+
const key = String(type);
|
|
10
|
+
return Object.hasOwn(TRIGGER_GLYPHS, key) ? TRIGGER_GLYPHS[key] : Bolt;
|
|
11
|
+
}
|
|
@@ -70,6 +70,7 @@
|
|
|
70
70
|
"integration.canvas.node.action.duplicate": "Duplizieren",
|
|
71
71
|
"integration.canvas.node.action.duplicateAria": "Node duplizieren",
|
|
72
72
|
"integration.canvas.node.badge.invalidConfig": "Konfiguration unvollständig — {n} Einstellung muss noch behoben werden: {fields} | Konfiguration unvollständig — {n} Einstellungen müssen noch behoben werden: {fields}",
|
|
73
|
+
"integration.canvas.node.badge.noBuiltCode": "Version {version} ist nicht einsatzbereit — dieser Schritt lässt sich nicht testen",
|
|
73
74
|
"integration.canvas.node.handle.input": "Eingang",
|
|
74
75
|
"integration.canvas.node.meta.versionTitle": "Version {version}",
|
|
75
76
|
"integration.canvas.node.note.heading": "Notiz",
|
|
@@ -281,6 +282,7 @@
|
|
|
281
282
|
"integration.dashboard.surface.io.label": "Import & Export",
|
|
282
283
|
"integration.dashboard.surface.sftp.hint": "Verwaltete SFTP-Endpoints für den dateibasierten Austausch.",
|
|
283
284
|
"integration.dashboard.surface.sftp.label": "SFTP-Server",
|
|
285
|
+
"integration.dashboard.surface.unavailable": "Hier nicht verfügbar — {surface} finden Sie im Revenexx Cockpit.",
|
|
284
286
|
"integration.dashboard.surface.webhooks.hint": "Eingehende HTTP-Endpoints, die Workflows auslösen.",
|
|
285
287
|
"integration.dashboard.surface.webhooks.label": "Webhooks",
|
|
286
288
|
"integration.dashboard.templates.empty": "Noch keine Vorlagen registriert.",
|
|
@@ -302,7 +304,6 @@
|
|
|
302
304
|
"integration.deadLetters.toast.replay.done": "Wiederholt",
|
|
303
305
|
"integration.deadLetters.toast.replay.error": "Wiederholen fehlgeschlagen.",
|
|
304
306
|
"integration.deadLetters.toast.replay.started": "Wiederholt — Run #{id} gestartet",
|
|
305
|
-
"integration.editor.action.activate.label": "Aktivieren",
|
|
306
307
|
"integration.editor.action.activate.title": "Workflow aktivieren",
|
|
307
308
|
"integration.editor.action.activate.title.invalid": "{n} Node-Einstellung vor der Aktivierung beheben | {n} Node-Einstellungen vor der Aktivierung beheben",
|
|
308
309
|
"integration.editor.action.backToWorkflow": "Zurück zum Workflow",
|
|
@@ -310,11 +311,11 @@
|
|
|
310
311
|
"integration.editor.action.discard.title": "Nicht gespeicherte Änderungen verwerfen",
|
|
311
312
|
"integration.editor.action.duplicate.label": "Duplizieren",
|
|
312
313
|
"integration.editor.action.duplicate.title": "Diesen Workflow duplizieren",
|
|
313
|
-
"integration.editor.action.pause.label": "Pausieren",
|
|
314
314
|
"integration.editor.action.pause.title": "Workflow pausieren",
|
|
315
315
|
"integration.editor.action.retry": "Erneut versuchen",
|
|
316
316
|
"integration.editor.action.save.label": "Speichern",
|
|
317
317
|
"integration.editor.action.save.saving": "Wird gespeichert…",
|
|
318
|
+
"integration.editor.action.state.label": "Workflow-Status",
|
|
318
319
|
"integration.editor.activate.error.generic": "Der Workflow-Status konnte nicht geändert werden.",
|
|
319
320
|
"integration.editor.activate.error.needsFixing": "Kann nicht aktiviert werden — {n} Einstellung muss noch behoben werden (siehe Banner oben). | Kann nicht aktiviert werden — {n} Einstellungen müssen noch behoben werden (siehe Banner oben).",
|
|
320
321
|
"integration.editor.activate.error.validationIssues": "Kann nicht aktiviert werden — {n} Validierungsproblem zu beheben. | Kann nicht aktiviert werden — {n} Validierungsprobleme zu beheben.",
|
|
@@ -360,6 +361,10 @@
|
|
|
360
361
|
"integration.editor.head.title": "{name} · Revenexx Cockpit",
|
|
361
362
|
"integration.editor.head.untitled": "Workflow",
|
|
362
363
|
"integration.editor.header.executionMode.title": "Ausführungsmodus: {mode}",
|
|
364
|
+
"integration.editor.leave.dialog.description": "Dieser Workflow hat Änderungen, die noch nicht gespeichert sind. Wer ihn jetzt verlässt, verwirft sie.",
|
|
365
|
+
"integration.editor.leave.dialog.discard": "Verwerfen und verlassen",
|
|
366
|
+
"integration.editor.leave.dialog.stay": "Weiter bearbeiten",
|
|
367
|
+
"integration.editor.leave.dialog.title": "Ohne Speichern verlassen?",
|
|
363
368
|
"integration.editor.minimap.action.bottomLeft": "Unten links",
|
|
364
369
|
"integration.editor.minimap.action.bottomRight": "Unten rechts",
|
|
365
370
|
"integration.editor.minimap.action.collapse.title": "Minimap einklappen",
|
|
@@ -453,6 +458,7 @@
|
|
|
453
458
|
"integration.history.badge.paused": "Pausiert",
|
|
454
459
|
"integration.history.badge.pausedHint": "Workflow ist pausiert; beim Aktivieren läuft diese Revision.",
|
|
455
460
|
"integration.history.confirm.restore": "Revision {revision} wiederherstellen? Die aktuelle Version wird zuerst archiviert, dann wird der Workflow aus dieser Momentaufnahme neu aufgebaut.",
|
|
461
|
+
"integration.history.confirm.restoreWithDraft": "Revision {revision} wiederherstellen? Die aktuelle Version wird zuerst archiviert, dann wird der Workflow aus dieser Momentaufnahme neu aufgebaut — und die nicht gespeicherten Änderungen im Editor werden verworfen.",
|
|
456
462
|
"integration.history.heading.description": "Jedes Speichern archiviert die vorherige Revision; die neueste ist die, die läuft, solange der Workflow aktiv ist.",
|
|
457
463
|
"integration.history.heading.title": "Versionsverlauf",
|
|
458
464
|
"integration.history.list.revisionLabel": "Revision {revision}",
|
|
@@ -499,6 +505,7 @@
|
|
|
499
505
|
"integration.nodeConfig.dynamicField.reloading": "wird neu geladen…",
|
|
500
506
|
"integration.nodeConfig.dynamicField.resolving": "Optionen werden geladen…",
|
|
501
507
|
"integration.nodeConfig.dynamicField.searching": "wird gesucht…",
|
|
508
|
+
"integration.nodeConfig.dynamicField.versionNotReady": "Keine Auswahl möglich, solange diese Version nicht einsatzbereit ist.",
|
|
502
509
|
"integration.nodeConfig.dynamicSchema.depthCapped": "verschachteltes dynamisches Schema wird nicht weiter aufgeklappt",
|
|
503
510
|
"integration.nodeConfig.dynamicSchema.empty": "Keine Felder geladen.",
|
|
504
511
|
"integration.nodeConfig.dynamicSchema.fieldCollision": "geladenes Feld '{key}' kollidiert mit einem statischen Konfigurationsfeld — übersprungen",
|
|
@@ -506,6 +513,7 @@
|
|
|
506
513
|
"integration.nodeConfig.dynamicSchema.resolving": "Felder werden geladen…",
|
|
507
514
|
"integration.nodeConfig.expression.badge.title": "Akzeptiert ${{ … }}-Ausdrücke",
|
|
508
515
|
"integration.nodeConfig.expression.insertTitle": "Ausdruck einfügen",
|
|
516
|
+
"integration.nodeConfig.expression.picker.aria": "Verfügbare Daten für diese Einstellung",
|
|
509
517
|
"integration.nodeConfig.expression.picker.empty": "Nichts entspricht „{query}“.",
|
|
510
518
|
"integration.nodeConfig.expression.picker.filterAria": "Verfügbare Daten filtern",
|
|
511
519
|
"integration.nodeConfig.expression.picker.filterPlaceholder": "Filtern…",
|
|
@@ -575,6 +583,8 @@
|
|
|
575
583
|
"integration.nodeConfig.validation.min": "Muss ≥ {min} sein",
|
|
576
584
|
"integration.nodeConfig.validation.minLength": "Muss mindestens {min} Zeichen lang sein",
|
|
577
585
|
"integration.nodeConfig.validation.pattern": "Entspricht nicht dem erforderlichen Format",
|
|
586
|
+
"integration.nodeInspector.dialogTitle": "{name} konfigurieren",
|
|
587
|
+
"integration.nodeInspector.footer.done": "Fertig",
|
|
578
588
|
"integration.nodeInspector.footer.removeFromWorkflow": "Aus Workflow entfernen",
|
|
579
589
|
"integration.nodeInspector.footer.unsavedChanges": "Ungespeicherte Änderungen",
|
|
580
590
|
"integration.nodeInspector.header.groupNode": "Node",
|
|
@@ -585,6 +595,9 @@
|
|
|
585
595
|
"integration.nodeInspector.header.previousStep": "Vorheriger Schritt (speichert Änderungen)",
|
|
586
596
|
"integration.nodeInspector.header.whatThisDoes": "Was dieser Node macht",
|
|
587
597
|
"integration.nodeInspector.input.description": "Verfügbare Daten. Klicken Sie auf einen Wert, um ihn in das fokussierte Feld einzufügen, oder ziehen Sie ihn hinein.",
|
|
598
|
+
"integration.nodeInspector.input.expects.heading": "Erwartet:",
|
|
599
|
+
"integration.nodeInspector.input.expects.optional": "optional",
|
|
600
|
+
"integration.nodeInspector.input.expects.required": "erforderlich",
|
|
588
601
|
"integration.nodeInspector.input.noUpstreamForTrigger": "Trigger starten den Workflow, daher gibt es keine vorgelagerten Daten. Workflow-Variablen sind überall verfügbar.",
|
|
589
602
|
"integration.nodeInspector.input.nodes.empty": "Noch keine vorgelagerten Nodes. Verbinden Sie einen Node mit diesem, um dessen Ausgabe hier zu referenzieren.",
|
|
590
603
|
"integration.nodeInspector.input.nodes.heading": "Nodes",
|
|
@@ -606,6 +619,8 @@
|
|
|
606
619
|
"integration.nodeInspector.note.addLabel": "Notiz hinzufügen",
|
|
607
620
|
"integration.nodeInspector.note.label": "Notiz",
|
|
608
621
|
"integration.nodeInspector.note.placeholder": "Warum dieser Node hier ist…",
|
|
622
|
+
"integration.nodeInspector.notice.versionNotReady.none": "Version {version} dieses Nodes ist nicht mehr einsatzbereit, und es liegt keine einsatzbereite Version vor. Dieser Schritt lässt sich nicht testen, und Auswahllisten in seinen Einstellungen bleiben leer.",
|
|
623
|
+
"integration.nodeInspector.notice.versionNotReady.upgrade": "Version {version} dieses Nodes ist nicht mehr einsatzbereit. Um den Node weiter zu verwenden, ist ein Upgrade auf Version {ready} notwendig.",
|
|
609
624
|
"integration.nodeInspector.output.description": "Was nachgelagerte Nodes von diesem erhalten. Deklariert vom Node-Typ; wird mit echten Werten gefüllt, sobald der Schritt gelaufen ist.",
|
|
610
625
|
"integration.nodeInspector.output.pin.editAction": "Angeheftetes Beispiel bearbeiten",
|
|
611
626
|
"integration.nodeInspector.output.pin.editorAria": "Angeheftete Beispielausgabe",
|
|
@@ -998,6 +1013,7 @@
|
|
|
998
1013
|
"integration.templates.detail.credentials.title": "Zugangsdaten",
|
|
999
1014
|
"integration.templates.detail.fit.anyIndustry": "Jede Branche",
|
|
1000
1015
|
"integration.templates.detail.fit.title": "Gut geeignet für",
|
|
1016
|
+
"integration.templates.detail.flow.ariaLabel": "Der Ablauf, den diese Vorlage aufbaut",
|
|
1001
1017
|
"integration.templates.detail.head.fallbackName": "Vorlage",
|
|
1002
1018
|
"integration.templates.detail.head.title": "{name} · Integration Studio",
|
|
1003
1019
|
"integration.templates.detail.install.createOneLink": "Jetzt anlegen",
|
|
@@ -1119,7 +1135,7 @@
|
|
|
1119
1135
|
"integration.workflows.list.error.title": "Workflows konnten nicht geladen werden",
|
|
1120
1136
|
"integration.workflows.list.head.title": "Workflows · Revenexx Cockpit",
|
|
1121
1137
|
"integration.workflows.list.header.title": "Workflows",
|
|
1122
|
-
"integration.workflows.settings.action.
|
|
1138
|
+
"integration.workflows.settings.action.done": "Fertig",
|
|
1123
1139
|
"integration.workflows.settings.dialog.description": "Ausführungsverhalten und Metadaten für „{name}“.",
|
|
1124
1140
|
"integration.workflows.settings.dialog.title": "Workflow-Einstellungen",
|
|
1125
1141
|
"integration.workflows.settings.field.description.label": "Beschreibung",
|
|
@@ -1184,6 +1200,7 @@
|
|
|
1184
1200
|
"integration.workflows.variables.action.add": "Hinzufügen",
|
|
1185
1201
|
"integration.workflows.variables.action.copyUsage.title": "Kopieren: ${{ vars.{key} }}",
|
|
1186
1202
|
"integration.workflows.variables.action.delete": "Variable löschen",
|
|
1203
|
+
"integration.workflows.variables.action.done": "Fertig",
|
|
1187
1204
|
"integration.workflows.variables.dialog.description.after": "Änderungen werden beim Speichern des Workflows übernommen.",
|
|
1188
1205
|
"integration.workflows.variables.dialog.description.before": "Wiederverwendbare Werte für diesen Workflow — in jedem Konfigurationsfeld referenzierbar als",
|
|
1189
1206
|
"integration.workflows.variables.dialog.title": "Workflow-Variablen",
|
|
@@ -70,6 +70,7 @@
|
|
|
70
70
|
"integration.canvas.node.action.duplicate": "Duplicate",
|
|
71
71
|
"integration.canvas.node.action.duplicateAria": "Duplicate node",
|
|
72
72
|
"integration.canvas.node.badge.invalidConfig": "Config incomplete — {n} setting still needs fixing: {fields} | Config incomplete — {n} settings still need fixing: {fields}",
|
|
73
|
+
"integration.canvas.node.badge.noBuiltCode": "Version {version} is not ready to use — this step cannot be tested",
|
|
73
74
|
"integration.canvas.node.handle.input": "Input",
|
|
74
75
|
"integration.canvas.node.meta.versionTitle": "Version {version}",
|
|
75
76
|
"integration.canvas.node.note.heading": "Note",
|
|
@@ -281,6 +282,7 @@
|
|
|
281
282
|
"integration.dashboard.surface.io.label": "Import & Export",
|
|
282
283
|
"integration.dashboard.surface.sftp.hint": "Managed SFTP endpoints for file-based exchange.",
|
|
283
284
|
"integration.dashboard.surface.sftp.label": "SFTP Server",
|
|
285
|
+
"integration.dashboard.surface.unavailable": "Not available here — you will find {surface} in the Revenexx Cockpit.",
|
|
284
286
|
"integration.dashboard.surface.webhooks.hint": "Inbound HTTP endpoints that trigger workflows.",
|
|
285
287
|
"integration.dashboard.surface.webhooks.label": "Webhooks",
|
|
286
288
|
"integration.dashboard.templates.empty": "No templates registered yet.",
|
|
@@ -302,7 +304,6 @@
|
|
|
302
304
|
"integration.deadLetters.toast.replay.done": "Replayed",
|
|
303
305
|
"integration.deadLetters.toast.replay.error": "Could not replay.",
|
|
304
306
|
"integration.deadLetters.toast.replay.started": "Replayed — started run #{id}",
|
|
305
|
-
"integration.editor.action.activate.label": "Activate",
|
|
306
307
|
"integration.editor.action.activate.title": "Activate this workflow",
|
|
307
308
|
"integration.editor.action.activate.title.invalid": "Fix {n} node setting before activating | Fix {n} node settings before activating",
|
|
308
309
|
"integration.editor.action.backToWorkflow": "Back to Workflow",
|
|
@@ -310,11 +311,11 @@
|
|
|
310
311
|
"integration.editor.action.discard.title": "Discard unsaved changes",
|
|
311
312
|
"integration.editor.action.duplicate.label": "Duplicate",
|
|
312
313
|
"integration.editor.action.duplicate.title": "Duplicate this workflow",
|
|
313
|
-
"integration.editor.action.pause.label": "Pause",
|
|
314
314
|
"integration.editor.action.pause.title": "Pause this workflow",
|
|
315
315
|
"integration.editor.action.retry": "Retry",
|
|
316
316
|
"integration.editor.action.save.label": "Save",
|
|
317
317
|
"integration.editor.action.save.saving": "Saving…",
|
|
318
|
+
"integration.editor.action.state.label": "Workflow status",
|
|
318
319
|
"integration.editor.activate.error.generic": "Could not change the workflow status.",
|
|
319
320
|
"integration.editor.activate.error.needsFixing": "Can’t activate — {n} setting still needs fixing (see the banner above). | Can’t activate — {n} settings still need fixing (see the banner above).",
|
|
320
321
|
"integration.editor.activate.error.validationIssues": "Can’t activate — {n} validation issue to fix. | Can’t activate — {n} validation issues to fix.",
|
|
@@ -360,6 +361,10 @@
|
|
|
360
361
|
"integration.editor.head.title": "{name} · Revenexx Cockpit",
|
|
361
362
|
"integration.editor.head.untitled": "Workflow",
|
|
362
363
|
"integration.editor.header.executionMode.title": "Execution mode: {mode}",
|
|
364
|
+
"integration.editor.leave.dialog.description": "This workflow has changes that have not been saved yet. Leaving now discards them.",
|
|
365
|
+
"integration.editor.leave.dialog.discard": "Discard and leave",
|
|
366
|
+
"integration.editor.leave.dialog.stay": "Keep editing",
|
|
367
|
+
"integration.editor.leave.dialog.title": "Leave without saving?",
|
|
363
368
|
"integration.editor.minimap.action.bottomLeft": "Bottom left",
|
|
364
369
|
"integration.editor.minimap.action.bottomRight": "Bottom right",
|
|
365
370
|
"integration.editor.minimap.action.collapse.title": "Collapse the minimap",
|
|
@@ -453,6 +458,7 @@
|
|
|
453
458
|
"integration.history.badge.paused": "Paused",
|
|
454
459
|
"integration.history.badge.pausedHint": "Workflow is paused; activating runs this revision.",
|
|
455
460
|
"integration.history.confirm.restore": "Restore revision {revision}? The current version is archived first, then the workflow is rebuilt from this snapshot.",
|
|
461
|
+
"integration.history.confirm.restoreWithDraft": "Restore revision {revision}? The current version is archived first, then the workflow is rebuilt from this snapshot — and the unsaved changes in the editor are discarded.",
|
|
456
462
|
"integration.history.heading.description": "Every save archives the previous revision; the newest one is what runs while the workflow is active.",
|
|
457
463
|
"integration.history.heading.title": "Version history",
|
|
458
464
|
"integration.history.list.revisionLabel": "Revision {revision}",
|
|
@@ -499,6 +505,7 @@
|
|
|
499
505
|
"integration.nodeConfig.dynamicField.reloading": "reloading…",
|
|
500
506
|
"integration.nodeConfig.dynamicField.resolving": "resolving options…",
|
|
501
507
|
"integration.nodeConfig.dynamicField.searching": "searching…",
|
|
508
|
+
"integration.nodeConfig.dynamicField.versionNotReady": "No choices while this version is not ready to use.",
|
|
502
509
|
"integration.nodeConfig.dynamicSchema.depthCapped": "nested dynamic-schema is not expanded further",
|
|
503
510
|
"integration.nodeConfig.dynamicSchema.empty": "No fields resolved.",
|
|
504
511
|
"integration.nodeConfig.dynamicSchema.fieldCollision": "resolved field '{key}' collides with a static config field — skipped",
|
|
@@ -506,6 +513,7 @@
|
|
|
506
513
|
"integration.nodeConfig.dynamicSchema.resolving": "Resolving fields…",
|
|
507
514
|
"integration.nodeConfig.expression.badge.title": "Accepts ${{ … }} expressions",
|
|
508
515
|
"integration.nodeConfig.expression.insertTitle": "Insert expression",
|
|
516
|
+
"integration.nodeConfig.expression.picker.aria": "Available data for this setting",
|
|
509
517
|
"integration.nodeConfig.expression.picker.empty": "Nothing matches “{query}”.",
|
|
510
518
|
"integration.nodeConfig.expression.picker.filterAria": "Filter available data",
|
|
511
519
|
"integration.nodeConfig.expression.picker.filterPlaceholder": "Filter…",
|
|
@@ -575,6 +583,8 @@
|
|
|
575
583
|
"integration.nodeConfig.validation.min": "Must be ≥ {min}",
|
|
576
584
|
"integration.nodeConfig.validation.minLength": "Must be at least {min} characters",
|
|
577
585
|
"integration.nodeConfig.validation.pattern": "Does not match the required format",
|
|
586
|
+
"integration.nodeInspector.dialogTitle": "Configure {name}",
|
|
587
|
+
"integration.nodeInspector.footer.done": "Done",
|
|
578
588
|
"integration.nodeInspector.footer.removeFromWorkflow": "Remove from workflow",
|
|
579
589
|
"integration.nodeInspector.footer.unsavedChanges": "Unsaved changes",
|
|
580
590
|
"integration.nodeInspector.header.groupNode": "Node",
|
|
@@ -585,6 +595,9 @@
|
|
|
585
595
|
"integration.nodeInspector.header.previousStep": "Previous step (saves changes)",
|
|
586
596
|
"integration.nodeInspector.header.whatThisDoes": "What this node does",
|
|
587
597
|
"integration.nodeInspector.input.description": "Available data. Click a value to insert it into the focused field, or drag it onto one.",
|
|
598
|
+
"integration.nodeInspector.input.expects.heading": "Expects:",
|
|
599
|
+
"integration.nodeInspector.input.expects.optional": "optional",
|
|
600
|
+
"integration.nodeInspector.input.expects.required": "required",
|
|
588
601
|
"integration.nodeInspector.input.noUpstreamForTrigger": "Triggers start the workflow, so there is no upstream data. Workflow variables are available everywhere.",
|
|
589
602
|
"integration.nodeInspector.input.nodes.empty": "No upstream nodes yet. Connect a node into this one to reference its output here.",
|
|
590
603
|
"integration.nodeInspector.input.nodes.heading": "Nodes",
|
|
@@ -606,6 +619,8 @@
|
|
|
606
619
|
"integration.nodeInspector.note.addLabel": "Add note",
|
|
607
620
|
"integration.nodeInspector.note.label": "Note",
|
|
608
621
|
"integration.nodeInspector.note.placeholder": "Why this node is here…",
|
|
622
|
+
"integration.nodeInspector.notice.versionNotReady.none": "Version {version} of this node is no longer usable, and no version of it is. This step cannot be tested, and the choice lists in its settings stay empty.",
|
|
623
|
+
"integration.nodeInspector.notice.versionNotReady.upgrade": "Version {version} of this node is no longer usable. To keep using this step, it has to be upgraded to version {ready}.",
|
|
609
624
|
"integration.nodeInspector.output.description": "What downstream nodes receive from this one. Declared by the node type; filled with real values once the step has run.",
|
|
610
625
|
"integration.nodeInspector.output.pin.editAction": "Edit pinned sample",
|
|
611
626
|
"integration.nodeInspector.output.pin.editorAria": "Pinned sample output",
|
|
@@ -998,6 +1013,7 @@
|
|
|
998
1013
|
"integration.templates.detail.credentials.title": "Credentials",
|
|
999
1014
|
"integration.templates.detail.fit.anyIndustry": "Any industry",
|
|
1000
1015
|
"integration.templates.detail.fit.title": "Good fit for",
|
|
1016
|
+
"integration.templates.detail.flow.ariaLabel": "The flow this template builds",
|
|
1001
1017
|
"integration.templates.detail.head.fallbackName": "Template",
|
|
1002
1018
|
"integration.templates.detail.head.title": "{name} · Integration Studio",
|
|
1003
1019
|
"integration.templates.detail.install.createOneLink": "Create one",
|
|
@@ -1119,7 +1135,7 @@
|
|
|
1119
1135
|
"integration.workflows.list.error.title": "Could not load workflows",
|
|
1120
1136
|
"integration.workflows.list.head.title": "Workflows · Revenexx Cockpit",
|
|
1121
1137
|
"integration.workflows.list.header.title": "Workflows",
|
|
1122
|
-
"integration.workflows.settings.action.
|
|
1138
|
+
"integration.workflows.settings.action.done": "Done",
|
|
1123
1139
|
"integration.workflows.settings.dialog.description": "Execution behaviour and metadata for “{name}”.",
|
|
1124
1140
|
"integration.workflows.settings.dialog.title": "Workflow settings",
|
|
1125
1141
|
"integration.workflows.settings.field.description.label": "Description",
|
|
@@ -1184,6 +1200,7 @@
|
|
|
1184
1200
|
"integration.workflows.variables.action.add": "Add",
|
|
1185
1201
|
"integration.workflows.variables.action.copyUsage.title": "Copy ${{ vars.{key} }}",
|
|
1186
1202
|
"integration.workflows.variables.action.delete": "Delete variable",
|
|
1203
|
+
"integration.workflows.variables.action.done": "Done",
|
|
1187
1204
|
"integration.workflows.variables.dialog.description.after": "Changes persist with the workflow Save.",
|
|
1188
1205
|
"integration.workflows.variables.dialog.description.before": "Reusable values for this workflow — reference them in any config field as",
|
|
1189
1206
|
"integration.workflows.variables.dialog.title": "Workflow variables",
|
|
@@ -442,7 +442,10 @@ async function confirmDelete() {
|
|
|
442
442
|
class="rounded-md px-3 py-2 text-sm"
|
|
443
443
|
:class="testResult.ok ? 'bg-emerald-500/10 text-emerald-700' : 'bg-rose-500/10 text-rose-700'"
|
|
444
444
|
>
|
|
445
|
-
|
|
445
|
+
<!-- What the credential type said, whichever way the test went. The fixed
|
|
446
|
+
sentences are the fallback for a type that reports nothing, not the
|
|
447
|
+
first choice (specs/credential-test.md). -->
|
|
448
|
+
{{ testResult.ok ? testResult.message ?? t("integration.credentials.shared.test.ok") : testResult.error ?? testResult.message ?? t("integration.credentials.shared.test.failed") }}
|
|
446
449
|
</div>
|
|
447
450
|
|
|
448
451
|
</div>
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
<script setup>
|
|
2
2
|
import { computed, ref, watch } from "vue";
|
|
3
|
+
import SurfaceCard from "../../components/Integrations/SurfaceCard.vue";
|
|
3
4
|
import TemplateCard from "../../components/Integrations/TemplateCard.vue";
|
|
4
5
|
import WorkflowCreateDialog from "../../components/Integrations/WorkflowCreateDialog.vue";
|
|
5
6
|
import { Pulse as Activity, DangerTriangle as AlertTriangle, ArrowRight, Box as Boxes, ClockCircle as Clock, Database, Layers, AddCircle as Plus, Radio, MagicStick as Sparkles, LinkRound as Webhook, Programming as Workflow } from "#solar-icons";
|
|
6
7
|
import { Badge, Button } from "@revenexx/studio";
|
|
8
|
+
import { servedPaths } from "../../utils/servedPaths";
|
|
7
9
|
import { runStatusLabel, statusClass } from "../../utils/statusStyle";
|
|
8
10
|
const t = useIntegrationsT();
|
|
9
11
|
useHead({ title: () => t("integration.dashboard.head.title") });
|
|
@@ -89,6 +91,11 @@ const surfaces = computed(() => [
|
|
|
89
91
|
{ label: t("integration.dashboard.surface.events.label"), to: "/integrations/events", icon: Radio, hint: t("integration.dashboard.surface.events.hint") },
|
|
90
92
|
{ label: t("integration.dashboard.surface.webhooks.label"), to: "/integrations/webhooks", icon: Webhook, hint: t("integration.dashboard.surface.webhooks.hint") }
|
|
91
93
|
]);
|
|
94
|
+
const router = useRouter();
|
|
95
|
+
function surfaceCards() {
|
|
96
|
+
const served = servedPaths(router.getRoutes().map((route) => route.path));
|
|
97
|
+
return surfaces.value.map((sfc) => ({ ...sfc, available: served.has(sfc.to) }));
|
|
98
|
+
}
|
|
92
99
|
</script>
|
|
93
100
|
|
|
94
101
|
<template>
|
|
@@ -258,25 +265,17 @@ const surfaces = computed(() => [
|
|
|
258
265
|
</div>
|
|
259
266
|
</div>
|
|
260
267
|
|
|
261
|
-
<!-- Data exchange & events —
|
|
268
|
+
<!-- Data exchange & events — surfaces beyond workflows, and the only cards
|
|
269
|
+
here that lead OUT of this module: all four are the cockpit's pages.
|
|
270
|
+
Where the host does not have them the card stays and says so, rather
|
|
271
|
+
than leading into a 404 (PO-223, `SurfaceCard.vue`). -->
|
|
262
272
|
<div class="grid gap-2 sm:grid-cols-2 lg:grid-cols-4">
|
|
263
|
-
<
|
|
264
|
-
v-for="sfc in
|
|
273
|
+
<SurfaceCard
|
|
274
|
+
v-for="sfc in surfaceCards()"
|
|
265
275
|
:key="sfc.to"
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
<div class="flex items-center gap-2">
|
|
270
|
-
<span class="flex size-6 items-center justify-center rounded-md bg-slate-100 text-slate-500">
|
|
271
|
-
<component :is="sfc.icon" :size="12" />
|
|
272
|
-
</span>
|
|
273
|
-
<span class="min-w-0 flex-1 truncate text-sm font-semibold">{{ sfc.label }}</span>
|
|
274
|
-
<ArrowRight :size="11" class="text-muted-foreground transition-transform group-hover:translate-x-0.5" />
|
|
275
|
-
</div>
|
|
276
|
-
<p class="mt-2 text-sm text-muted-foreground">
|
|
277
|
-
{{ sfc.hint }}
|
|
278
|
-
</p>
|
|
279
|
-
</NuxtLink>
|
|
276
|
+
v-bind="sfc"
|
|
277
|
+
:note="t('integration.dashboard.surface.unavailable', { surface: sfc.label })"
|
|
278
|
+
/>
|
|
280
279
|
</div>
|
|
281
280
|
|
|
282
281
|
<!-- Named for the offer it makes, not for a ranking nothing measures
|