@revenexx/studio-integrations 0.5.1 → 0.7.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 +428 -0
- package/README.md +48 -4
- package/dist/module.d.mts +17 -1
- package/dist/module.json +1 -1
- package/dist/module.mjs +10 -2
- package/dist/runtime/components/Integrations/CredentialCreateDialog.vue +43 -41
- package/dist/runtime/components/Integrations/CredentialDetailDialog.vue +123 -50
- package/dist/runtime/components/Integrations/CredentialRevenexxSource.vue +108 -71
- package/dist/runtime/components/Integrations/CredentialTypePicker.vue +12 -15
- package/dist/runtime/components/Integrations/DeadLettersPanel.vue +18 -17
- package/dist/runtime/components/Integrations/EventTypeCombobox.d.vue.ts +1 -0
- package/dist/runtime/components/Integrations/EventTypeCombobox.vue +7 -4
- package/dist/runtime/components/Integrations/EventTypeCombobox.vue.d.ts +1 -0
- package/dist/runtime/components/Integrations/ExpressionInput.d.vue.ts +4 -0
- package/dist/runtime/components/Integrations/ExpressionInput.vue +67 -37
- package/dist/runtime/components/Integrations/ExpressionInput.vue.d.ts +4 -0
- package/dist/runtime/components/Integrations/GroupFrameView.vue +30 -20
- package/dist/runtime/components/Integrations/Layout.vue +48 -4
- package/dist/runtime/components/Integrations/NodeConfigDynamicField.vue +23 -23
- package/dist/runtime/components/Integrations/NodeConfigDynamicSchema.vue +11 -13
- package/dist/runtime/components/Integrations/NodeConfigField.d.vue.ts +2 -0
- package/dist/runtime/components/Integrations/NodeConfigField.vue +187 -93
- package/dist/runtime/components/Integrations/NodeConfigField.vue.d.ts +2 -0
- package/dist/runtime/components/Integrations/NodeInspector.d.vue.ts +6 -0
- package/dist/runtime/components/Integrations/NodeInspector.vue +1157 -376
- package/dist/runtime/components/Integrations/NodeInspector.vue.d.ts +6 -0
- package/dist/runtime/components/Integrations/NodeLogo.d.vue.ts +12 -0
- package/dist/runtime/components/Integrations/NodeLogo.vue +2 -30
- package/dist/runtime/components/Integrations/NodeLogo.vue.d.ts +12 -0
- package/dist/runtime/components/Integrations/NodeMark.d.vue.ts +15 -0
- package/dist/runtime/components/Integrations/NodeMark.vue +79 -0
- package/dist/runtime/components/Integrations/NodeMark.vue.d.ts +15 -0
- package/dist/runtime/components/Integrations/NodeOutputSchema.d.vue.ts +21 -0
- package/dist/runtime/components/Integrations/NodeOutputSchema.vue +43 -0
- package/dist/runtime/components/Integrations/NodeOutputSchema.vue.d.ts +21 -0
- package/dist/runtime/components/Integrations/NodePalette.vue +122 -55
- package/dist/runtime/components/Integrations/QueryBuilder.vue +46 -38
- package/dist/runtime/components/Integrations/RunActionsMenu.vue +38 -12
- package/dist/runtime/components/Integrations/RunDetailPanel.vue +114 -62
- package/dist/runtime/components/Integrations/RunPayloadInput.vue +27 -13
- package/dist/runtime/components/Integrations/RunResponseModal.vue +11 -10
- package/dist/runtime/components/Integrations/RunsTable.d.vue.ts +0 -1
- package/dist/runtime/components/Integrations/RunsTable.vue +38 -16
- package/dist/runtime/components/Integrations/RunsTable.vue.d.ts +0 -1
- package/dist/runtime/components/Integrations/SftpAccountImportDialog.vue +8 -7
- package/dist/runtime/components/Integrations/Sidebar.vue +14 -10
- package/dist/runtime/components/Integrations/StickyNoteView.d.vue.ts +14 -4
- package/dist/runtime/components/Integrations/StickyNoteView.vue +38 -23
- package/dist/runtime/components/Integrations/StickyNoteView.vue.d.ts +14 -4
- package/dist/runtime/components/Integrations/TemplateCard.d.vue.ts +18 -0
- package/dist/runtime/components/Integrations/TemplateCard.vue +119 -0
- package/dist/runtime/components/Integrations/TemplateCard.vue.d.ts +18 -0
- package/dist/runtime/components/Integrations/TemplateFlowPreview.d.vue.ts +2 -2
- package/dist/runtime/components/Integrations/TemplateFlowPreview.vue +16 -18
- package/dist/runtime/components/Integrations/TemplateFlowPreview.vue.d.ts +2 -2
- package/dist/runtime/components/Integrations/WorkflowContextMenu.vue +34 -33
- package/dist/runtime/components/Integrations/WorkflowCreateDialog.vue +52 -67
- package/dist/runtime/components/Integrations/WorkflowDuplicateDialog.vue +8 -7
- package/dist/runtime/components/Integrations/WorkflowEdgeView.d.vue.ts +18 -4
- package/dist/runtime/components/Integrations/WorkflowEdgeView.vue +29 -11
- package/dist/runtime/components/Integrations/WorkflowEdgeView.vue.d.ts +18 -4
- package/dist/runtime/components/Integrations/WorkflowEditor.d.vue.ts +20 -1
- package/dist/runtime/components/Integrations/WorkflowEditor.vue +240 -116
- package/dist/runtime/components/Integrations/WorkflowEditor.vue.d.ts +20 -1
- package/dist/runtime/components/Integrations/WorkflowHistoryPanel.vue +28 -43
- package/dist/runtime/components/Integrations/WorkflowIconPicker.d.vue.ts +19 -1
- package/dist/runtime/components/Integrations/WorkflowIconPicker.vue +17 -7
- package/dist/runtime/components/Integrations/WorkflowIconPicker.vue.d.ts +19 -1
- package/dist/runtime/components/Integrations/WorkflowNodeView.d.vue.ts +4 -0
- package/dist/runtime/components/Integrations/WorkflowNodeView.vue +198 -56
- package/dist/runtime/components/Integrations/WorkflowNodeView.vue.d.ts +4 -0
- package/dist/runtime/components/Integrations/WorkflowRunReplayPanel.vue +56 -59
- package/dist/runtime/components/Integrations/WorkflowSettingsDialog.vue +28 -21
- package/dist/runtime/components/Integrations/WorkflowTagsInput.d.vue.ts +1 -0
- package/dist/runtime/components/Integrations/WorkflowTagsInput.vue +15 -9
- package/dist/runtime/components/Integrations/WorkflowTagsInput.vue.d.ts +1 -0
- package/dist/runtime/components/Integrations/WorkflowTriggerView.vue +15 -10
- package/dist/runtime/components/Integrations/WorkflowVariablesDialog.vue +50 -36
- package/dist/runtime/components/Integrations/WorkflowsTable.vue +126 -48
- package/dist/runtime/composables/integrationsApi.js +4 -2
- package/dist/runtime/composables/useCredentialTypes.js +8 -6
- package/dist/runtime/composables/useIntegrationsAudit.js +4 -3
- package/dist/runtime/composables/useIntegrationsCredentials.js +10 -8
- package/dist/runtime/composables/useIntegrationsDeadLetters.js +7 -6
- package/dist/runtime/composables/useIntegrationsFocus.d.ts +12 -0
- package/dist/runtime/composables/useIntegrationsLocale.d.ts +26 -0
- package/dist/runtime/composables/useIntegrationsLocale.js +8 -0
- package/dist/runtime/composables/useIntegrationsNodes.js +7 -5
- package/dist/runtime/composables/useIntegrationsRealtime.d.ts +149 -0
- package/dist/runtime/composables/useIntegrationsRealtime.js +80 -0
- package/dist/runtime/composables/useIntegrationsRevisions.js +9 -7
- package/dist/runtime/composables/useIntegrationsRuns.d.ts +1 -1
- package/dist/runtime/composables/useIntegrationsRuns.js +14 -7
- package/dist/runtime/composables/useIntegrationsSchedules.d.ts +4 -0
- package/dist/runtime/composables/useIntegrationsSchedules.js +10 -3
- package/dist/runtime/composables/useIntegrationsSecrets.js +14 -9
- package/dist/runtime/composables/useIntegrationsT.d.ts +35 -0
- package/dist/runtime/composables/useIntegrationsT.js +9 -0
- package/dist/runtime/composables/useIntegrationsTemplates.d.ts +3 -3
- package/dist/runtime/composables/useIntegrationsTemplates.js +28 -16
- package/dist/runtime/composables/useIntegrationsTriggers.js +10 -8
- package/dist/runtime/composables/useIntegrationsWorkflows.js +13 -7
- package/dist/runtime/composables/useLoadGate.d.ts +17 -0
- package/dist/runtime/composables/useLoadGate.js +8 -0
- package/dist/runtime/composables/useNodeConfigResolve.js +2 -1
- package/dist/runtime/data/branchReorder.d.ts +37 -0
- package/dist/runtime/data/branchReorder.js +71 -0
- package/dist/runtime/data/handleId.d.ts +23 -0
- package/dist/runtime/data/handleId.js +6 -0
- package/dist/runtime/data/integrationNodeCatalog.d.ts +41 -0
- package/dist/runtime/data/integrationNodeCatalog.js +14 -0
- package/dist/runtime/data/integrationTemplates.d.ts +48 -0
- package/dist/runtime/data/integrationTemplates.js +21 -0
- package/dist/runtime/data/integrationWorkflowSamples.js +1 -1
- package/dist/runtime/data/nodeIcons.d.ts +2 -0
- package/dist/runtime/data/nodeIcons.js +73 -0
- package/dist/runtime/data/nodeInputData.d.ts +16 -4
- package/dist/runtime/data/nodeInputData.js +4 -0
- package/dist/runtime/data/payloadForm.d.ts +9 -2
- package/dist/runtime/data/payloadForm.js +29 -4
- package/dist/runtime/data/portTone.d.ts +17 -0
- package/dist/runtime/data/portTone.js +7 -0
- package/dist/runtime/data/webhookAuth.d.ts +18 -0
- package/dist/runtime/data/webhookAuth.js +19 -0
- package/dist/runtime/data/workflowIconCatalog.js +4 -6
- package/dist/runtime/i18n/messages/de.json +1207 -0
- package/dist/runtime/i18n/messages/en.json +1207 -0
- package/dist/runtime/i18n/translate.d.ts +83 -0
- package/dist/runtime/i18n/translate.js +41 -0
- package/dist/runtime/pages/integrations/audit.vue +140 -37
- package/dist/runtime/pages/integrations/credentials/oauth-complete.vue +9 -6
- package/dist/runtime/pages/integrations/credentials.vue +180 -64
- package/dist/runtime/pages/integrations/index.vue +91 -92
- package/dist/runtime/pages/integrations/runs.vue +33 -21
- package/dist/runtime/pages/integrations/schedules.vue +88 -23
- package/dist/runtime/pages/integrations/secrets.vue +57 -35
- package/dist/runtime/pages/integrations/templates/[slug].vue +119 -88
- package/dist/runtime/pages/integrations/templates/index.vue +82 -103
- package/dist/runtime/pages/integrations/workflows/[id].vue +344 -91
- package/dist/runtime/pages/integrations/workflows/index.vue +23 -5
- package/dist/runtime/plugins/locale.d.ts +10 -0
- package/dist/runtime/plugins/locale.js +6 -0
- package/dist/runtime/plugins/realtime.client.d.ts +32 -0
- package/dist/runtime/plugins/realtime.client.js +4 -0
- package/dist/runtime/types/integrations.d.ts +65 -6
- package/dist/runtime/utils/apiError.d.ts +47 -0
- package/dist/runtime/utils/apiError.js +32 -0
- package/dist/runtime/utils/cockpitSources.d.ts +153 -0
- package/dist/runtime/utils/cockpitSources.js +14 -0
- package/dist/runtime/utils/credentialState.d.ts +24 -1
- package/dist/runtime/utils/credentialState.js +26 -11
- package/dist/runtime/utils/loadGate.d.ts +61 -0
- package/dist/runtime/utils/loadGate.js +22 -0
- package/dist/runtime/utils/localize.d.ts +46 -4
- package/dist/runtime/utils/localize.js +34 -7
- package/dist/runtime/utils/notify.d.ts +59 -0
- package/dist/runtime/utils/notify.js +21 -0
- package/dist/runtime/utils/pollBudget.d.ts +72 -0
- package/dist/runtime/utils/pollBudget.js +17 -0
- package/dist/runtime/utils/revenexxCredentialSource.d.ts +26 -0
- package/dist/runtime/utils/revenexxCredentialSource.js +8 -0
- package/dist/runtime/utils/schedule.d.ts +116 -0
- package/dist/runtime/utils/schedule.js +219 -0
- package/dist/runtime/utils/statusStyle.d.ts +26 -2
- package/dist/runtime/utils/statusStyle.js +9 -1
- package/dist/runtime/utils/validationFindings.d.ts +101 -0
- package/dist/runtime/utils/validationFindings.js +54 -0
- package/dist/runtime/utils/whenIdle.d.ts +42 -0
- package/dist/runtime/utils/whenIdle.js +12 -0
- package/dist/types.d.mts +2 -6
- package/package.json +10 -3
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
import { computed, markRaw, nextTick, onBeforeUnmount, onMounted, provide, ref, watch } from "vue";
|
|
3
3
|
import {
|
|
4
4
|
MarkerType,
|
|
5
|
-
PanOnScrollMode,
|
|
6
5
|
VueFlow,
|
|
7
6
|
useVueFlow
|
|
8
7
|
} from "@vue-flow/core";
|
|
@@ -27,6 +26,7 @@ import StickyNoteView from "./StickyNoteView.vue";
|
|
|
27
26
|
import GroupFrameView from "./GroupFrameView.vue";
|
|
28
27
|
import WorkflowEdgeView from "./WorkflowEdgeView.vue";
|
|
29
28
|
import { findManifest } from "../../data/integrationNodeCatalog";
|
|
29
|
+
import { SAFETY_POLL_MS } from "../../utils/pollBudget";
|
|
30
30
|
import { runStatusLabel, statusClass } from "../../utils/statusStyle";
|
|
31
31
|
const props = defineProps({
|
|
32
32
|
workflow: { type: Object, required: true },
|
|
@@ -34,9 +34,14 @@ const props = defineProps({
|
|
|
34
34
|
single: { type: Boolean, required: false }
|
|
35
35
|
});
|
|
36
36
|
const { loadForWorkflow, getSteps } = useIntegrationsRuns();
|
|
37
|
+
const t = useIntegrationsT();
|
|
37
38
|
const TERMINAL = /* @__PURE__ */ new Set(["completed", "succeeded", "failed", "cancelled", "terminated"]);
|
|
38
39
|
const STEPS_POLL_MS = 4e3;
|
|
39
40
|
const PLAY_INTERVAL_MS = 3e3;
|
|
41
|
+
const { delivering: realtimeDelivering, runTick, lastRunEvent } = useIntegrationsRealtime();
|
|
42
|
+
function stepsPollDelay() {
|
|
43
|
+
return realtimeDelivering.value ? SAFETY_POLL_MS : STEPS_POLL_MS;
|
|
44
|
+
}
|
|
40
45
|
const runs = ref([]);
|
|
41
46
|
const runsPage = ref(1);
|
|
42
47
|
const runsLastPage = ref(1);
|
|
@@ -54,7 +59,7 @@ async function loadRuns(reset = false) {
|
|
|
54
59
|
runsPage.value = res.page;
|
|
55
60
|
runsLastPage.value = res.lastPage;
|
|
56
61
|
} catch (e) {
|
|
57
|
-
toast.error(e?.message ?? "
|
|
62
|
+
toast.error(e?.message ?? t("integration.replay.toast.loadRunsFailed"));
|
|
58
63
|
} finally {
|
|
59
64
|
runsLoading.value = false;
|
|
60
65
|
}
|
|
@@ -92,16 +97,23 @@ async function loadSteps(runId, isPoll = false) {
|
|
|
92
97
|
if (runStatus.value && !TERMINAL.has(runStatus.value)) {
|
|
93
98
|
pollTimer = window.setTimeout(() => {
|
|
94
99
|
void loadSteps(runId, true);
|
|
95
|
-
},
|
|
100
|
+
}, stepsPollDelay());
|
|
96
101
|
}
|
|
97
102
|
} catch (e) {
|
|
98
103
|
if (selectedRunId.value === runId) {
|
|
99
|
-
stepsError.value = e?.message ?? "
|
|
104
|
+
stepsError.value = e?.message ?? t("integration.replay.error.loadStepsFailed");
|
|
100
105
|
}
|
|
101
106
|
} finally {
|
|
102
107
|
if (!isPoll) stepsLoading.value = false;
|
|
103
108
|
}
|
|
104
109
|
}
|
|
110
|
+
watch(runTick, () => {
|
|
111
|
+
const runId = selectedRunId.value;
|
|
112
|
+
if (!runId) return;
|
|
113
|
+
const event = lastRunEvent.value;
|
|
114
|
+
if (event && event.topicId !== runId) return;
|
|
115
|
+
void loadSteps(runId, true);
|
|
116
|
+
});
|
|
105
117
|
function selectRun(id) {
|
|
106
118
|
if (selectedRunId.value === id) return;
|
|
107
119
|
selectedRunId.value = id;
|
|
@@ -150,9 +162,7 @@ preview.onPaneReady(() => {
|
|
|
150
162
|
preview.updateNodeInternals(vfNodes.value.map((n) => n.id));
|
|
151
163
|
void preview.fitView({ padding: 0.2, maxZoom: 1 });
|
|
152
164
|
});
|
|
153
|
-
provide("workflow-node-actions",
|
|
154
|
-
}, remove: () => {
|
|
155
|
-
} });
|
|
165
|
+
provide("workflow-node-actions", null);
|
|
156
166
|
const dynamicOutputs = ref({});
|
|
157
167
|
const dynamicOutputNodes = computed(() => props.workflow.blob.nodes.filter((n) => {
|
|
158
168
|
const m = findManifest(n.nodeSlug, n.nodeVersion);
|
|
@@ -176,22 +186,9 @@ const finalStatus = computed(() => {
|
|
|
176
186
|
return m;
|
|
177
187
|
});
|
|
178
188
|
const runNodeIds = computed(() => new Set(steps.value.map((s) => s.node_id)));
|
|
179
|
-
const triggerIds = computed(() => new Set(props.workflow.blob.triggers.map((
|
|
189
|
+
const triggerIds = computed(() => new Set(props.workflow.blob.triggers.map((t2) => t2.id)));
|
|
180
190
|
function edgeColorByKind(kind) {
|
|
181
|
-
|
|
182
|
-
case "success":
|
|
183
|
-
return "#10b981";
|
|
184
|
-
case "error":
|
|
185
|
-
return "#f43f5e";
|
|
186
|
-
case "branch-true":
|
|
187
|
-
return "#10b981";
|
|
188
|
-
case "branch-false":
|
|
189
|
-
return "#f59e0b";
|
|
190
|
-
case "branch":
|
|
191
|
-
return "#8b5cf6";
|
|
192
|
-
default:
|
|
193
|
-
return "#94a3b8";
|
|
194
|
-
}
|
|
191
|
+
return kind === "error" ? "#cbd5e1" : "#94a3b8";
|
|
195
192
|
}
|
|
196
193
|
const GROUP_PAD = 24;
|
|
197
194
|
const GROUP_LABEL_ROOM = 26;
|
|
@@ -246,7 +243,7 @@ const vfNodes = computed(() => {
|
|
|
246
243
|
const posOf = (id) => {
|
|
247
244
|
const wn = blob.nodes.find((n) => n.id === id);
|
|
248
245
|
if (wn) return { x: wn.position?.x ?? 0, y: wn.position?.y ?? 0 };
|
|
249
|
-
const ti = blob.triggers.findIndex((
|
|
246
|
+
const ti = blob.triggers.findIndex((t2) => t2.id === id);
|
|
250
247
|
if (ti >= 0) return triggerPos(id, ti);
|
|
251
248
|
return { x: 0, y: 0 };
|
|
252
249
|
};
|
|
@@ -260,17 +257,17 @@ const vfNodes = computed(() => {
|
|
|
260
257
|
zIndex: -100 + i,
|
|
261
258
|
draggable: false,
|
|
262
259
|
class: "replay-dim",
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
260
|
+
// No callbacks: that absence is what makes the note read-only — it then
|
|
261
|
+
// draws no toolbar, no resize handle and does not open for typing (PO-284).
|
|
262
|
+
data: { sticky: s }
|
|
266
263
|
})),
|
|
267
|
-
...blob.triggers.map((
|
|
268
|
-
id:
|
|
264
|
+
...blob.triggers.map((t2, i) => ({
|
|
265
|
+
id: t2.id,
|
|
269
266
|
type: "trigger",
|
|
270
|
-
position: triggerPos(
|
|
267
|
+
position: triggerPos(t2.id, i),
|
|
271
268
|
draggable: false,
|
|
272
|
-
class: replayNodeClass(
|
|
273
|
-
data:
|
|
269
|
+
class: replayNodeClass(t2.id, "trigger"),
|
|
270
|
+
data: t2
|
|
274
271
|
})),
|
|
275
272
|
...blob.nodes.map((n) => ({
|
|
276
273
|
id: n.id,
|
|
@@ -290,7 +287,7 @@ const vfEdges = computed(() => {
|
|
|
290
287
|
const nodePos = (id) => {
|
|
291
288
|
const wn = blob.nodes.find((n) => n.id === id);
|
|
292
289
|
if (wn) return { x: wn.position?.x ?? 0, y: wn.position?.y ?? 0 };
|
|
293
|
-
const ti = blob.triggers.findIndex((
|
|
290
|
+
const ti = blob.triggers.findIndex((t2) => t2.id === id);
|
|
294
291
|
if (ti >= 0) return blob.triggerPositions?.[id] ?? { x: -40, y: 60 + ti * 140 };
|
|
295
292
|
return { x: 0, y: 0 };
|
|
296
293
|
};
|
|
@@ -304,6 +301,7 @@ const vfEdges = computed(() => {
|
|
|
304
301
|
if (output === "true") return "branch-true";
|
|
305
302
|
if (output === "false") return "branch-false";
|
|
306
303
|
if (output === "error") return "error";
|
|
304
|
+
if (output === "done") return "success";
|
|
307
305
|
const outs = [...manifest.outputs ?? [], ...dynamicOutputs.value[sourceNodeId] ?? []];
|
|
308
306
|
const portDef = outs.find((o) => o.name === output);
|
|
309
307
|
if (portDef) {
|
|
@@ -323,7 +321,7 @@ const vfEdges = computed(() => {
|
|
|
323
321
|
const kind = edgeKind(e.from.nodeId, e.from.output);
|
|
324
322
|
const wn = blob.nodes.find((n) => n.id === e.from.nodeId);
|
|
325
323
|
const manifest = wn ? findManifest(wn.nodeSlug, wn.nodeVersion) : null;
|
|
326
|
-
const isBranchPort = (manifest?.outputs ?? []).some((o) => o.sourceFromConfig) || ["true", "false"
|
|
324
|
+
const isBranchPort = (manifest?.outputs ?? []).some((o) => o.sourceFromConfig) || ["true", "false"].includes(e.from.output);
|
|
327
325
|
const srcSide = pickSourceSide(e.from.nodeId, e.to.nodeId);
|
|
328
326
|
const sourceRan = triggerIds.value.has(e.from.nodeId) ? steps.value.length > 0 : runNodeIds.value.has(e.from.nodeId);
|
|
329
327
|
const traversed = sourceRan && runNodeIds.value.has(e.to.nodeId);
|
|
@@ -451,7 +449,6 @@ watch(() => props.initialRunId, (id) => {
|
|
|
451
449
|
:nodes-draggable="false"
|
|
452
450
|
:nodes-connectable="false"
|
|
453
451
|
:edges-updatable="false"
|
|
454
|
-
:pan-on-scroll-mode="PanOnScrollMode.Free"
|
|
455
452
|
:delete-key-code="null"
|
|
456
453
|
:min-zoom="0.25"
|
|
457
454
|
:max-zoom="2"
|
|
@@ -474,12 +471,12 @@ watch(() => props.initialRunId, (id) => {
|
|
|
474
471
|
<!-- Which run is on the canvas -->
|
|
475
472
|
<div class="absolute top-3 left-3 z-10 flex items-center gap-2 rounded-lg border border-black/[0.08] bg-white/95 px-2.5 py-1.5 text-sm shadow-sm backdrop-blur-sm">
|
|
476
473
|
<Play :size="13" class="text-muted-foreground" />
|
|
477
|
-
<span class="font-medium">{{ selectedRunId ?
|
|
474
|
+
<span class="font-medium">{{ selectedRunId ? t("integration.replay.canvas.runLabel", { id: selectedRunId }) : t("integration.replay.canvas.pickRun") }}</span>
|
|
478
475
|
<Badge v-if="runStatus" variant="outline" class="border text-xs font-medium uppercase" :class="statusClass(runStatus)">
|
|
479
476
|
{{ runStatusLabel(runStatus) }}
|
|
480
477
|
</Badge>
|
|
481
478
|
<Badge v-if="stepsSource" variant="outline" class="border text-xs font-medium uppercase text-muted-foreground">
|
|
482
|
-
{{ stepsSource === "temporal" ? "
|
|
479
|
+
{{ stepsSource === "temporal" ? t("integration.replay.badge.live") : t("integration.replay.badge.snapshot") }}
|
|
483
480
|
</Badge>
|
|
484
481
|
<Loader2 v-if="stepsLoading" :size="13" class="animate-spin text-muted-foreground" />
|
|
485
482
|
</div>
|
|
@@ -495,14 +492,14 @@ watch(() => props.initialRunId, (id) => {
|
|
|
495
492
|
<div class="flex items-center gap-1">
|
|
496
493
|
<Button variant="ghost" size="sm" class="h-7" :disabled="safeCursor === 0" @click="seek(safeCursor - 1)">
|
|
497
494
|
<Prev :size="13" />
|
|
498
|
-
|
|
495
|
+
{{ t("integration.replay.transport.action.prev") }}
|
|
499
496
|
</Button>
|
|
500
497
|
<Button size="sm" class="h-7" @click="togglePlay">
|
|
501
498
|
<component :is="playing ? Pause : Play" :size="13" class="mr-1" />
|
|
502
|
-
{{ playing ? "
|
|
499
|
+
{{ playing ? t("integration.replay.transport.action.pause") : t("integration.replay.transport.action.play") }}
|
|
503
500
|
</Button>
|
|
504
501
|
<Button variant="ghost" size="sm" class="h-7" :disabled="safeCursor >= steps.length - 1" @click="seek(safeCursor + 1)">
|
|
505
|
-
|
|
502
|
+
{{ t("integration.replay.transport.action.next") }}
|
|
506
503
|
<Next :size="13" />
|
|
507
504
|
</Button>
|
|
508
505
|
</div>
|
|
@@ -513,17 +510,17 @@ watch(() => props.initialRunId, (id) => {
|
|
|
513
510
|
:max="steps.length - 1"
|
|
514
511
|
:value="safeCursor"
|
|
515
512
|
class="h-1.5 flex-1 cursor-pointer accent-primary"
|
|
516
|
-
aria-label="
|
|
513
|
+
:aria-label="t('integration.replay.transport.scrubberAria')"
|
|
517
514
|
@input="seek(Number($event.target.value))"
|
|
518
515
|
>
|
|
519
516
|
|
|
520
517
|
<span class="shrink-0 text-sm tabular-nums text-muted-foreground">
|
|
521
|
-
|
|
518
|
+
{{ t("integration.replay.transport.stepCounter", { current: safeCursor + 1, total: steps.length }) }}
|
|
522
519
|
<span class="font-mono">· {{ activeStepTitle }}</span>
|
|
523
520
|
</span>
|
|
524
521
|
</template>
|
|
525
522
|
<p v-else-if="!stepsLoading" class="text-sm text-muted-foreground">
|
|
526
|
-
{{ selectedRunId ? "
|
|
523
|
+
{{ selectedRunId ? t("integration.replay.state.noSteps") : t("integration.replay.state.pickRunHint") }}
|
|
527
524
|
</p>
|
|
528
525
|
</div>
|
|
529
526
|
</div>
|
|
@@ -533,10 +530,10 @@ watch(() => props.initialRunId, (id) => {
|
|
|
533
530
|
<aside class="flex w-[32rem] shrink-0 flex-col border-l border-black/[0.06] bg-white">
|
|
534
531
|
<div class="flex items-center gap-2 border-b border-black/[0.06] px-3 py-2">
|
|
535
532
|
<div class="min-w-0 flex-1">
|
|
536
|
-
<div class="truncate font-mono text-sm font-medium">{{ activeStepTitle || "
|
|
533
|
+
<div class="truncate font-mono text-sm font-medium">{{ activeStepTitle || t("integration.replay.debug.titleFallback") }}</div>
|
|
537
534
|
<div class="text-xs tabular-nums text-muted-foreground">
|
|
538
|
-
<template v-if="steps.length">
|
|
539
|
-
<template v-else>
|
|
535
|
+
<template v-if="steps.length">{{ t("integration.replay.debug.stepCounter", { current: safeCursor + 1, total: steps.length }) }}</template>
|
|
536
|
+
<template v-else>{{ t("integration.replay.debug.noStepSelected") }}</template>
|
|
540
537
|
</div>
|
|
541
538
|
</div>
|
|
542
539
|
<Badge v-if="activeStep" variant="outline" class="shrink-0 border text-xs font-medium uppercase" :class="statusClass(activeStep.status)">
|
|
@@ -547,59 +544,59 @@ watch(() => props.initialRunId, (id) => {
|
|
|
547
544
|
<div v-if="activeStep" class="min-h-0 flex-1 space-y-4 overflow-y-auto p-3">
|
|
548
545
|
<div class="grid grid-cols-2 gap-x-4 gap-y-2 text-sm">
|
|
549
546
|
<div>
|
|
550
|
-
<div class="text-mono text-muted-foreground">
|
|
547
|
+
<div class="text-mono text-muted-foreground">{{ t("integration.replay.debug.field.attempt") }}</div>
|
|
551
548
|
<div class="tabular-nums">{{ activeStep.attempt + 1 }}</div>
|
|
552
549
|
</div>
|
|
553
550
|
<div>
|
|
554
|
-
<div class="text-mono text-muted-foreground">
|
|
551
|
+
<div class="text-mono text-muted-foreground">{{ t("integration.replay.debug.field.duration") }}</div>
|
|
555
552
|
<div class="tabular-nums">{{ formatDuration(activeStep.duration_ms) }}</div>
|
|
556
553
|
</div>
|
|
557
554
|
<div>
|
|
558
|
-
<div class="text-mono text-muted-foreground">
|
|
555
|
+
<div class="text-mono text-muted-foreground">{{ t("integration.replay.debug.field.started") }}</div>
|
|
559
556
|
<div class="tabular-nums">{{ formatTime(activeStep.started_at) }}</div>
|
|
560
557
|
</div>
|
|
561
558
|
<div>
|
|
562
|
-
<div class="text-mono text-muted-foreground">
|
|
559
|
+
<div class="text-mono text-muted-foreground">{{ t("integration.replay.debug.field.completed") }}</div>
|
|
563
560
|
<div class="tabular-nums">{{ formatTime(activeStep.completed_at) }}</div>
|
|
564
561
|
</div>
|
|
565
562
|
</div>
|
|
566
563
|
|
|
567
564
|
<div v-if="activeStep.branch" class="text-sm">
|
|
568
|
-
<div class="text-mono text-muted-foreground mb-1">
|
|
565
|
+
<div class="text-mono text-muted-foreground mb-1">{{ t("integration.replay.debug.field.branch") }}</div>
|
|
569
566
|
<code class="rounded bg-black/[0.05] px-1.5 py-0.5 font-mono">{{ activeStep.branch }}</code>
|
|
570
567
|
</div>
|
|
571
568
|
|
|
572
569
|
<div v-if="activeStep.error" class="text-sm">
|
|
573
|
-
<div class="text-mono text-muted-foreground mb-1">
|
|
570
|
+
<div class="text-mono text-muted-foreground mb-1">{{ t("integration.replay.debug.field.error") }}</div>
|
|
574
571
|
<pre class="max-h-40 overflow-auto rounded-md border border-rose-500/20 bg-rose-500/5 p-2 font-mono text-sm text-rose-700">{{ activeStep.error }}</pre>
|
|
575
572
|
</div>
|
|
576
573
|
|
|
577
574
|
<div v-if="activeStep.input !== void 0 && activeStep.input !== null" class="text-sm">
|
|
578
|
-
<div class="text-mono text-muted-foreground mb-1">
|
|
575
|
+
<div class="text-mono text-muted-foreground mb-1">{{ t("integration.replay.debug.field.input") }}</div>
|
|
579
576
|
<pre class="max-h-56 overflow-auto rounded-md border border-black/[0.06] bg-muted/20 p-2 font-mono text-sm">{{ pretty(activeStep.input) }}</pre>
|
|
580
577
|
</div>
|
|
581
578
|
|
|
582
579
|
<div v-if="activeStep.config !== void 0 && activeStep.config !== null" class="text-sm">
|
|
583
|
-
<div class="text-mono text-muted-foreground mb-1">
|
|
580
|
+
<div class="text-mono text-muted-foreground mb-1">{{ t("integration.replay.debug.field.config") }}</div>
|
|
584
581
|
<pre class="max-h-56 overflow-auto rounded-md border border-black/[0.06] bg-muted/20 p-2 font-mono text-sm">{{ pretty(activeStep.config) }}</pre>
|
|
585
582
|
</div>
|
|
586
583
|
|
|
587
584
|
<div v-if="activeStep.output !== void 0 && activeStep.output !== null" class="text-sm">
|
|
588
|
-
<div class="text-mono text-muted-foreground mb-1">
|
|
585
|
+
<div class="text-mono text-muted-foreground mb-1">{{ t("integration.replay.debug.field.output") }}</div>
|
|
589
586
|
<pre class="max-h-56 overflow-auto rounded-md border border-black/[0.06] bg-muted/20 p-2 font-mono text-sm">{{ pretty(activeStep.output) }}</pre>
|
|
590
587
|
</div>
|
|
591
588
|
</div>
|
|
592
589
|
<p v-else class="p-4 text-sm text-muted-foreground">
|
|
593
|
-
|
|
590
|
+
{{ t("integration.replay.debug.emptyHint") }}
|
|
594
591
|
</p>
|
|
595
592
|
</aside>
|
|
596
593
|
|
|
597
594
|
<!-- Run list — hidden in single mode (the run detail replays ONE run). -->
|
|
598
595
|
<aside v-if="!single" class="flex w-80 shrink-0 flex-col border-l border-black/[0.06] bg-white">
|
|
599
596
|
<div class="border-b border-black/[0.06] px-3 py-2">
|
|
600
|
-
<div class="text-sm font-semibold">
|
|
597
|
+
<div class="text-sm font-semibold">{{ t("integration.replay.runsList.heading") }}</div>
|
|
601
598
|
<p class="text-sm text-muted-foreground">
|
|
602
|
-
|
|
599
|
+
{{ t("integration.replay.runsList.description") }}
|
|
603
600
|
</p>
|
|
604
601
|
</div>
|
|
605
602
|
|
|
@@ -628,7 +625,7 @@ watch(() => props.initialRunId, (id) => {
|
|
|
628
625
|
</button>
|
|
629
626
|
|
|
630
627
|
<p v-if="!runs.length && !runsLoading" class="px-2 py-6 text-center text-sm text-muted-foreground">
|
|
631
|
-
|
|
628
|
+
{{ t("integration.replay.runsList.empty") }}
|
|
632
629
|
</p>
|
|
633
630
|
|
|
634
631
|
<div v-if="runsLoading" class="flex justify-center py-3">
|
|
@@ -641,7 +638,7 @@ watch(() => props.initialRunId, (id) => {
|
|
|
641
638
|
class="w-full text-sm"
|
|
642
639
|
@click="loadMoreRuns"
|
|
643
640
|
>
|
|
644
|
-
|
|
641
|
+
{{ t("integration.replay.runsList.loadMore") }}
|
|
645
642
|
</Button>
|
|
646
643
|
</div>
|
|
647
644
|
</aside>
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
DialogHeader,
|
|
10
10
|
DialogTitle,
|
|
11
11
|
Input,
|
|
12
|
+
Label,
|
|
12
13
|
Select,
|
|
13
14
|
SelectContent,
|
|
14
15
|
SelectItem,
|
|
@@ -28,6 +29,7 @@ const props = defineProps({
|
|
|
28
29
|
tagSuggestions: { type: Array, required: false }
|
|
29
30
|
});
|
|
30
31
|
const emit = defineEmits(["update:open", "change-mode", "save-meta"]);
|
|
32
|
+
const t = useIntegrationsT();
|
|
31
33
|
const nameDraft = ref("");
|
|
32
34
|
const descriptionDraft = ref("");
|
|
33
35
|
const tagsDraft = ref([]);
|
|
@@ -53,23 +55,25 @@ function formatDateTime(iso) {
|
|
|
53
55
|
<Dialog :open="open" @update:open="(v) => emit('update:open', v)">
|
|
54
56
|
<DialogContent class="sm:max-w-lg">
|
|
55
57
|
<DialogHeader>
|
|
56
|
-
<DialogTitle>
|
|
58
|
+
<DialogTitle>{{ t("integration.workflows.settings.dialog.title") }}</DialogTitle>
|
|
57
59
|
<DialogDescription class="text-sm">
|
|
58
|
-
|
|
60
|
+
{{ t("integration.workflows.settings.dialog.description", { name: workflow.name }) }}
|
|
59
61
|
</DialogDescription>
|
|
60
62
|
</DialogHeader>
|
|
61
63
|
|
|
62
64
|
<div class="space-y-4 text-sm">
|
|
63
65
|
<div class="rounded-lg border border-black/[0.06] bg-white p-4 space-y-3">
|
|
64
|
-
<div class="text-mono text-muted-foreground">
|
|
66
|
+
<div class="text-mono text-muted-foreground">{{ t("integration.workflows.settings.section.execution") }}</div>
|
|
65
67
|
<div class="flex items-center gap-2">
|
|
66
68
|
<Select
|
|
67
69
|
:model-value="modeDraft"
|
|
68
70
|
:disabled="savingMode"
|
|
69
71
|
@update:model-value="(v) => emit('change-mode', v)"
|
|
70
72
|
>
|
|
71
|
-
|
|
72
|
-
|
|
73
|
+
<!-- Named explicitly: the section caption above says "Execution",
|
|
74
|
+
which is not a name the control itself carries. -->
|
|
75
|
+
<SelectTrigger :aria-label="t('integration.workflows.settings.field.executionMode.aria')" class="h-8 w-full bg-white text-sm">
|
|
76
|
+
<SelectValue :placeholder="t('integration.workflows.settings.field.executionMode.placeholder')" />
|
|
73
77
|
</SelectTrigger>
|
|
74
78
|
<SelectContent>
|
|
75
79
|
<SelectItem v-for="m in modes" :key="m.value" :value="m.value">
|
|
@@ -85,22 +89,25 @@ function formatDateTime(iso) {
|
|
|
85
89
|
</div>
|
|
86
90
|
|
|
87
91
|
<div class="rounded-lg border border-black/[0.06] bg-white p-4 space-y-3">
|
|
88
|
-
<div class="text-mono text-muted-foreground">
|
|
92
|
+
<div class="text-mono text-muted-foreground">{{ t("integration.workflows.settings.section.appearance") }}</div>
|
|
93
|
+
<!-- Real labels, not styled captions: each one names its own control
|
|
94
|
+
(`for`/`id`), otherwise a screen reader reads four unlabelled
|
|
95
|
+
fields in a row. -->
|
|
89
96
|
<div class="space-y-1.5">
|
|
90
|
-
<
|
|
91
|
-
<Input v-model="nameDraft" class="bg-white" placeholder="
|
|
97
|
+
<Label for="wf-set-name" class="text-sm font-normal text-muted-foreground">{{ t("integration.workflows.settings.field.name.label") }}</Label>
|
|
98
|
+
<Input id="wf-set-name" v-model="nameDraft" class="bg-white" :placeholder="t('integration.workflows.settings.field.name.placeholder')" />
|
|
92
99
|
</div>
|
|
93
100
|
<div class="space-y-1.5">
|
|
94
|
-
<
|
|
95
|
-
<Textarea v-model="descriptionDraft" rows="2" class="bg-white" placeholder="
|
|
101
|
+
<Label for="wf-set-desc" class="text-sm font-normal text-muted-foreground">{{ t("integration.workflows.settings.field.description.label") }}</Label>
|
|
102
|
+
<Textarea id="wf-set-desc" v-model="descriptionDraft" rows="2" class="bg-white" :placeholder="t('integration.workflows.settings.field.description.placeholder')" />
|
|
96
103
|
</div>
|
|
97
104
|
<div class="space-y-1.5">
|
|
98
|
-
<
|
|
99
|
-
<WorkflowTagsInput v-model="tagsDraft" :suggestions="tagSuggestions ?? []" />
|
|
105
|
+
<Label for="wf-set-tags" class="text-sm font-normal text-muted-foreground">{{ t("integration.workflows.settings.field.tags.label") }}</Label>
|
|
106
|
+
<WorkflowTagsInput v-model="tagsDraft" input-id="wf-set-tags" :suggestions="tagSuggestions ?? []" />
|
|
100
107
|
</div>
|
|
101
108
|
<div class="space-y-1.5">
|
|
102
|
-
<
|
|
103
|
-
<WorkflowIconPicker v-model="iconDraft" />
|
|
109
|
+
<Label id="wf-set-icon-label" class="text-sm font-normal text-muted-foreground">{{ t("integration.workflows.settings.field.icon.label") }}</Label>
|
|
110
|
+
<WorkflowIconPicker v-model="iconDraft" trigger-labelledby="wf-set-icon-label" />
|
|
104
111
|
</div>
|
|
105
112
|
<div class="flex justify-end">
|
|
106
113
|
<Button
|
|
@@ -110,23 +117,23 @@ function formatDateTime(iso) {
|
|
|
110
117
|
@click="emit('save-meta', { name: nameDraft.trim(), description: descriptionDraft.trim(), tags: tagsDraft, icon: iconDraft })"
|
|
111
118
|
>
|
|
112
119
|
<Loader2 v-if="savingMeta" :size="12" class="animate-spin" />
|
|
113
|
-
|
|
120
|
+
{{ t("integration.workflows.settings.action.save") }}
|
|
114
121
|
</Button>
|
|
115
122
|
</div>
|
|
116
123
|
</div>
|
|
117
124
|
|
|
118
125
|
<div class="rounded-lg border border-black/[0.06] bg-white p-4 space-y-2">
|
|
119
|
-
<div class="text-mono text-muted-foreground">
|
|
126
|
+
<div class="text-mono text-muted-foreground">{{ t("integration.workflows.settings.section.metadata") }}</div>
|
|
120
127
|
<div class="grid grid-cols-[110px_1fr] gap-x-3 gap-y-2">
|
|
121
|
-
<span class="text-muted-foreground">
|
|
128
|
+
<span class="text-muted-foreground">{{ t("integration.workflows.settings.meta.id") }}</span>
|
|
122
129
|
<span class="font-mono">{{ workflow.id }}</span>
|
|
123
|
-
<span class="text-muted-foreground">
|
|
130
|
+
<span class="text-muted-foreground">{{ t("integration.workflows.settings.meta.definition") }}</span>
|
|
124
131
|
<span class="font-mono">{{ workflow.blob_definition_version }}</span>
|
|
125
|
-
<span class="text-muted-foreground">
|
|
132
|
+
<span class="text-muted-foreground">{{ t("integration.workflows.settings.meta.revision") }}</span>
|
|
126
133
|
<span class="font-mono">r{{ workflow.revision }}</span>
|
|
127
|
-
<span class="text-muted-foreground">
|
|
134
|
+
<span class="text-muted-foreground">{{ t("integration.workflows.settings.meta.created") }}</span>
|
|
128
135
|
<span>{{ formatDateTime(workflow.created_at) }}</span>
|
|
129
|
-
<span class="text-muted-foreground">
|
|
136
|
+
<span class="text-muted-foreground">{{ t("integration.workflows.settings.meta.updated") }}</span>
|
|
130
137
|
<span>{{ formatDateTime(workflow.updated_at) }}</span>
|
|
131
138
|
</div>
|
|
132
139
|
</div>
|
|
@@ -2,8 +2,10 @@
|
|
|
2
2
|
import { ref, useId } from "vue";
|
|
3
3
|
const model = defineModel({ type: Array, ...{ default: () => [] } });
|
|
4
4
|
const props = defineProps({
|
|
5
|
-
suggestions: { type: Array, required: false, default: () => [] }
|
|
5
|
+
suggestions: { type: Array, required: false, default: () => [] },
|
|
6
|
+
inputId: { type: String, required: false }
|
|
6
7
|
});
|
|
8
|
+
const t = useIntegrationsT();
|
|
7
9
|
const draft = ref("");
|
|
8
10
|
const inputRef = ref(null);
|
|
9
11
|
const listId = useId();
|
|
@@ -14,7 +16,7 @@ function commit() {
|
|
|
14
16
|
if (!model.value.includes(tag)) model.value = [...model.value, tag];
|
|
15
17
|
}
|
|
16
18
|
function remove(tag) {
|
|
17
|
-
model.value = model.value.filter((
|
|
19
|
+
model.value = model.value.filter((t2) => t2 !== tag);
|
|
18
20
|
}
|
|
19
21
|
function onKeydown(e) {
|
|
20
22
|
if (e.key === "Enter" || e.key === ",") {
|
|
@@ -28,31 +30,35 @@ function onKeydown(e) {
|
|
|
28
30
|
|
|
29
31
|
<template>
|
|
30
32
|
<div
|
|
31
|
-
class="flex min-h-9 cursor-text flex-wrap items-center gap-1 rounded-md border border-black/[0.08] bg-white px-2 py-1.5"
|
|
33
|
+
class="flex min-h-9 cursor-text flex-wrap items-center gap-1 rounded-md border border-black/[0.08] bg-white px-2 py-1.5 focus-within:ring-2 focus-within:ring-primary/40"
|
|
32
34
|
@click="inputRef?.focus()"
|
|
33
35
|
>
|
|
34
36
|
<span
|
|
35
|
-
v-for="
|
|
36
|
-
:key="
|
|
37
|
+
v-for="tag in model"
|
|
38
|
+
:key="tag"
|
|
37
39
|
class="inline-flex items-center gap-1 rounded-md border border-black/[0.06] bg-muted/50 px-1.5 py-0.5 text-xs text-foreground"
|
|
38
40
|
>
|
|
39
|
-
{{
|
|
41
|
+
{{ tag }}
|
|
40
42
|
<button
|
|
41
43
|
type="button"
|
|
42
44
|
class="text-muted-foreground transition hover:text-foreground"
|
|
43
|
-
:title="
|
|
44
|
-
|
|
45
|
+
:title="t('integration.workflows.tagsInput.action.remove', { tag })"
|
|
46
|
+
:aria-label="t('integration.workflows.tagsInput.action.remove', { tag })"
|
|
47
|
+
@click.stop="remove(tag)"
|
|
45
48
|
>
|
|
46
49
|
×
|
|
47
50
|
</button>
|
|
48
51
|
</span>
|
|
52
|
+
<!-- No ring of its own: the wrapper above is the field the user sees, and
|
|
53
|
+
it carries the focus ring via focus-within. -->
|
|
49
54
|
<input
|
|
55
|
+
:id="props.inputId"
|
|
50
56
|
ref="inputRef"
|
|
51
57
|
v-model="draft"
|
|
52
58
|
type="text"
|
|
53
59
|
:list="listId"
|
|
54
60
|
class="h-6 min-w-24 flex-1 bg-transparent text-sm focus:outline-none"
|
|
55
|
-
:placeholder="model.length ? '
|
|
61
|
+
:placeholder="model.length ? t('integration.workflows.tagsInput.placeholder.add') : t('integration.workflows.tagsInput.placeholder.addFirst')"
|
|
56
62
|
@keydown="onKeydown"
|
|
57
63
|
@blur="commit()"
|
|
58
64
|
>
|
|
@@ -2,7 +2,10 @@
|
|
|
2
2
|
import { computed, ref } from "vue";
|
|
3
3
|
import { Handle, Position, useVueFlow } from "@vue-flow/core";
|
|
4
4
|
import { NodeToolbar } from "@vue-flow/node-toolbar";
|
|
5
|
-
import { ClockCircle as Clock, Play, TrashBinMinimalistic as
|
|
5
|
+
import { ClockCircle as Clock, Play, TrashBinMinimalistic as TrashBin, LinkRound as Webhook, Bolt } from "#solar-icons";
|
|
6
|
+
import { resolveDisplayName } from "../../utils/localize";
|
|
7
|
+
import { triggerTypeLabel } from "../../data/integrationNodeCatalog";
|
|
8
|
+
import { describeCron } from "../../utils/schedule";
|
|
6
9
|
const props = defineProps({
|
|
7
10
|
id: { type: String, required: true },
|
|
8
11
|
data: { type: Object, required: true },
|
|
@@ -10,20 +13,21 @@ const props = defineProps({
|
|
|
10
13
|
});
|
|
11
14
|
defineOptions({ inheritAttrs: false });
|
|
12
15
|
defineEmits(["updateNodeInternals"]);
|
|
16
|
+
const t = useIntegrationsT();
|
|
13
17
|
const icon = computed(() => {
|
|
14
18
|
return props.data.type === "schedule" ? Clock : props.data.type === "webhook" ? Webhook : props.data.type === "event" ? Bolt : Play;
|
|
15
19
|
});
|
|
16
20
|
const subtitle = computed(() => {
|
|
17
21
|
const c = props.data.config;
|
|
18
|
-
if (props.data.type === "schedule") return c.cron ??
|
|
22
|
+
if (props.data.type === "schedule") return describeCron(c.cron) ?? c.cron ?? t("integration.canvas.trigger.subtitle.noSchedule");
|
|
19
23
|
if (props.data.type === "webhook") return `${c.method ?? "POST"} ${c.path ?? "/"}`;
|
|
20
|
-
if (props.data.type === "event") return c.event_type ?
|
|
21
|
-
return "
|
|
24
|
+
if (props.data.type === "event") return c.event_type ? t("integration.canvas.trigger.subtitle.onEvent", { event: c.event_type }) : t("integration.canvas.trigger.subtitle.noEvent");
|
|
25
|
+
return t("integration.canvas.trigger.subtitle.onDemand");
|
|
22
26
|
});
|
|
23
27
|
const title = computed(() => {
|
|
24
28
|
const n = props.data.name;
|
|
25
|
-
if (!n) return
|
|
26
|
-
return
|
|
29
|
+
if (!n) return triggerTypeLabel(props.data.type);
|
|
30
|
+
return resolveDisplayName(n);
|
|
27
31
|
});
|
|
28
32
|
const hovered = ref(false);
|
|
29
33
|
let hoverTimer = null;
|
|
@@ -52,10 +56,11 @@ function sideToPosition(s) {
|
|
|
52
56
|
<button
|
|
53
57
|
type="button"
|
|
54
58
|
class="flex h-6 items-center gap-1 rounded px-1.5 text-sm text-rose-600 hover:bg-rose-50"
|
|
55
|
-
title="
|
|
59
|
+
:title="t('integration.common.action.delete')"
|
|
60
|
+
:aria-label="t('integration.canvas.trigger.action.deleteAria')"
|
|
56
61
|
@click="remove"
|
|
57
62
|
>
|
|
58
|
-
<
|
|
63
|
+
<TrashBin :size="11" />
|
|
59
64
|
</button>
|
|
60
65
|
</NodeToolbar>
|
|
61
66
|
|
|
@@ -84,8 +89,8 @@ function sideToPosition(s) {
|
|
|
84
89
|
</div>
|
|
85
90
|
</div>
|
|
86
91
|
<div class="flex items-center justify-between border-t border-emerald-500/15 bg-emerald-500/[0.04] rounded-b-md px-3 py-1 text-mono text-emerald-700/80 font-semibold">
|
|
87
|
-
<span>
|
|
88
|
-
<span v-if="data.active === false" class="text-amber-600">
|
|
92
|
+
<span>{{ t("integration.canvas.badge.trigger") }} · {{ data.type }}</span>
|
|
93
|
+
<span v-if="data.active === false" class="text-amber-600">{{ t("integration.canvas.trigger.badge.inactive") }}</span>
|
|
89
94
|
</div>
|
|
90
95
|
</div>
|
|
91
96
|
|