@revenexx/studio-integrations 0.6.0 → 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 +227 -0
- package/README.md +31 -0
- 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 +29 -28
- package/dist/runtime/components/Integrations/CredentialDetailDialog.vue +38 -37
- package/dist/runtime/components/Integrations/CredentialRevenexxSource.vue +58 -58
- package/dist/runtime/components/Integrations/CredentialTypePicker.vue +10 -9
- package/dist/runtime/components/Integrations/DeadLettersPanel.vue +18 -17
- package/dist/runtime/components/Integrations/EventTypeCombobox.vue +4 -3
- package/dist/runtime/components/Integrations/ExpressionInput.vue +16 -15
- package/dist/runtime/components/Integrations/GroupFrameView.vue +29 -19
- package/dist/runtime/components/Integrations/Layout.vue +28 -3
- package/dist/runtime/components/Integrations/NodeConfigDynamicField.vue +15 -14
- package/dist/runtime/components/Integrations/NodeConfigDynamicSchema.vue +9 -8
- package/dist/runtime/components/Integrations/NodeConfigField.vue +43 -42
- package/dist/runtime/components/Integrations/NodeInspector.vue +225 -216
- package/dist/runtime/components/Integrations/NodeOutputSchema.d.vue.ts +1 -1
- package/dist/runtime/components/Integrations/NodeOutputSchema.vue +3 -2
- package/dist/runtime/components/Integrations/NodeOutputSchema.vue.d.ts +1 -1
- package/dist/runtime/components/Integrations/NodePalette.vue +30 -29
- package/dist/runtime/components/Integrations/QueryBuilder.vue +44 -43
- package/dist/runtime/components/Integrations/RunActionsMenu.vue +36 -12
- package/dist/runtime/components/Integrations/RunDetailPanel.vue +114 -62
- package/dist/runtime/components/Integrations/RunPayloadInput.vue +14 -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 +37 -23
- package/dist/runtime/components/Integrations/StickyNoteView.vue.d.ts +14 -4
- package/dist/runtime/components/Integrations/TemplateCard.vue +16 -6
- package/dist/runtime/components/Integrations/TemplateFlowPreview.vue +10 -9
- package/dist/runtime/components/Integrations/WorkflowContextMenu.vue +34 -33
- package/dist/runtime/components/Integrations/WorkflowCreateDialog.vue +36 -35
- package/dist/runtime/components/Integrations/WorkflowDuplicateDialog.vue +8 -7
- package/dist/runtime/components/Integrations/WorkflowEdgeView.vue +6 -4
- package/dist/runtime/components/Integrations/WorkflowEditor.vue +82 -79
- package/dist/runtime/components/Integrations/WorkflowHistoryPanel.vue +24 -23
- package/dist/runtime/components/Integrations/WorkflowIconPicker.vue +7 -6
- package/dist/runtime/components/Integrations/WorkflowNodeView.vue +19 -20
- package/dist/runtime/components/Integrations/WorkflowRunReplayPanel.vue +52 -39
- package/dist/runtime/components/Integrations/WorkflowSettingsDialog.vue +20 -19
- package/dist/runtime/components/Integrations/WorkflowTagsInput.vue +9 -8
- package/dist/runtime/components/Integrations/WorkflowTriggerView.vue +12 -9
- package/dist/runtime/components/Integrations/WorkflowVariablesDialog.vue +27 -26
- package/dist/runtime/components/Integrations/WorkflowsTable.vue +83 -45
- package/dist/runtime/composables/integrationsApi.js +4 -2
- package/dist/runtime/composables/useCredentialTypes.js +4 -2
- package/dist/runtime/composables/useIntegrationsCredentials.js +6 -4
- 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 +3 -1
- 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 +3 -1
- package/dist/runtime/composables/useIntegrationsSecrets.js +4 -2
- 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 +16 -8
- package/dist/runtime/composables/useIntegrationsTriggers.js +4 -2
- package/dist/runtime/composables/useIntegrationsWorkflows.js +5 -3
- package/dist/runtime/composables/useNodeConfigResolve.js +2 -1
- package/dist/runtime/data/integrationNodeCatalog.d.ts +41 -0
- package/dist/runtime/data/integrationNodeCatalog.js +14 -0
- package/dist/runtime/data/integrationTemplates.d.ts +9 -0
- package/dist/runtime/data/integrationTemplates.js +7 -1
- package/dist/runtime/data/payloadForm.js +2 -1
- package/dist/runtime/data/workflowIconCatalog.js +2 -1
- 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 +52 -34
- package/dist/runtime/pages/integrations/credentials/oauth-complete.vue +3 -2
- package/dist/runtime/pages/integrations/credentials.vue +70 -52
- package/dist/runtime/pages/integrations/index.vue +50 -56
- package/dist/runtime/pages/integrations/runs.vue +33 -21
- package/dist/runtime/pages/integrations/schedules.vue +49 -28
- package/dist/runtime/pages/integrations/secrets.vue +34 -34
- package/dist/runtime/pages/integrations/templates/[slug].vue +77 -68
- package/dist/runtime/pages/integrations/templates/index.vue +35 -27
- package/dist/runtime/pages/integrations/workflows/[id].vue +164 -99
- package/dist/runtime/pages/integrations/workflows/index.vue +10 -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/utils/credentialState.d.ts +24 -1
- package/dist/runtime/utils/credentialState.js +26 -11
- package/dist/runtime/utils/localize.d.ts +18 -5
- package/dist/runtime/utils/localize.js +32 -9
- package/dist/runtime/utils/pollBudget.d.ts +72 -0
- package/dist/runtime/utils/pollBudget.js +17 -0
- package/dist/runtime/utils/schedule.d.ts +0 -25
- package/dist/runtime/utils/schedule.js +37 -25
- package/dist/runtime/utils/statusStyle.d.ts +26 -2
- package/dist/runtime/utils/statusStyle.js +9 -1
- package/dist/runtime/utils/validationFindings.d.ts +2 -2
- package/dist/runtime/utils/validationFindings.js +4 -3
- package/dist/types.d.mts +2 -6
- package/package.json +3 -2
|
@@ -17,23 +17,24 @@ const props = defineProps({
|
|
|
17
17
|
runBusy: { type: Boolean, required: false }
|
|
18
18
|
});
|
|
19
19
|
const emit = defineEmits(["action", "update:open"]);
|
|
20
|
+
const t = useIntegrationsT();
|
|
20
21
|
const isMac = typeof navigator !== "undefined" && /Mac|iPhone|iPad/.test(navigator.platform ?? "");
|
|
21
22
|
function formatShortcut(tokens) {
|
|
22
23
|
const parts = tokens.split("+");
|
|
23
24
|
const out = [];
|
|
24
25
|
for (const raw of parts) {
|
|
25
|
-
const
|
|
26
|
-
if (
|
|
27
|
-
else if (
|
|
28
|
-
else if (
|
|
29
|
-
else if (
|
|
30
|
-
else if (
|
|
31
|
-
else if (
|
|
32
|
-
else if (
|
|
33
|
-
else if (
|
|
34
|
-
else if (
|
|
35
|
-
else if (
|
|
36
|
-
else out.push(
|
|
26
|
+
const t2 = raw.toLowerCase();
|
|
27
|
+
if (t2 === "mod") out.push(isMac ? "\u2318" : "Ctrl");
|
|
28
|
+
else if (t2 === "shift") out.push("\u21E7");
|
|
29
|
+
else if (t2 === "alt") out.push(isMac ? "\u2325" : "Alt");
|
|
30
|
+
else if (t2 === "enter") out.push("\u21B5");
|
|
31
|
+
else if (t2 === "backspace") out.push("\u232B");
|
|
32
|
+
else if (t2 === "delete") out.push("Del");
|
|
33
|
+
else if (t2 === "escape") out.push("Esc");
|
|
34
|
+
else if (t2 === "tab") out.push("Tab");
|
|
35
|
+
else if (t2 === "space") out.push("Space");
|
|
36
|
+
else if (t2 === "f2") out.push("F2");
|
|
37
|
+
else out.push(t2.toUpperCase());
|
|
37
38
|
}
|
|
38
39
|
return isMac ? out.join("") : out.join("+");
|
|
39
40
|
}
|
|
@@ -42,19 +43,19 @@ const sections = computed(() => {
|
|
|
42
43
|
if (props.kind === "node") {
|
|
43
44
|
return [
|
|
44
45
|
[
|
|
45
|
-
{ id: "open", label: "
|
|
46
|
-
{ id: "rename", label: "
|
|
46
|
+
{ id: "open", label: t("integration.canvas.contextMenu.item.open"), shortcut: "enter" },
|
|
47
|
+
{ id: "rename", label: t("integration.canvas.contextMenu.item.rename"), shortcut: "f2" }
|
|
47
48
|
],
|
|
48
49
|
[
|
|
49
|
-
{ id: "copy", label: "
|
|
50
|
-
{ id: "duplicate", label: "
|
|
50
|
+
{ id: "copy", label: t("integration.canvas.contextMenu.item.copy"), shortcut: "mod+c", disabled: props.targetIsTrigger },
|
|
51
|
+
{ id: "duplicate", label: t("integration.canvas.contextMenu.item.duplicate"), shortcut: "mod+d", disabled: props.targetIsTrigger }
|
|
51
52
|
],
|
|
52
53
|
[
|
|
53
|
-
{ id: "run", label: "
|
|
54
|
-
{ id: "tidy-up", label: "
|
|
54
|
+
{ id: "run", label: t("integration.canvas.contextMenu.item.runWorkflow"), shortcut: "mod+enter", disabled: props.runBusy },
|
|
55
|
+
{ id: "tidy-up", label: t("integration.canvas.contextMenu.item.tidyUp"), shortcut: "shift+alt+t" }
|
|
55
56
|
],
|
|
56
57
|
[
|
|
57
|
-
{ id: "delete", label: "
|
|
58
|
+
{ id: "delete", label: t("integration.common.action.delete"), shortcut: "backspace", danger: true }
|
|
58
59
|
]
|
|
59
60
|
];
|
|
60
61
|
}
|
|
@@ -62,37 +63,37 @@ const sections = computed(() => {
|
|
|
62
63
|
const n = props.selectionCount;
|
|
63
64
|
return [
|
|
64
65
|
[
|
|
65
|
-
{ id: "copy", label:
|
|
66
|
-
{ id: "duplicate", label:
|
|
66
|
+
{ id: "copy", label: t("integration.canvas.contextMenu.item.copyNodes", n), shortcut: "mod+c" },
|
|
67
|
+
{ id: "duplicate", label: t("integration.canvas.contextMenu.item.duplicateNodes", n), shortcut: "mod+d" }
|
|
67
68
|
],
|
|
68
69
|
[
|
|
69
|
-
{ id: "group", label:
|
|
70
|
-
{ id: "tidy-up", label: "
|
|
71
|
-
{ id: "select-all", label: "
|
|
72
|
-
{ id: "clear-selection", label: "
|
|
70
|
+
{ id: "group", label: t("integration.canvas.contextMenu.item.groupNodes", n), shortcut: "mod+g" },
|
|
71
|
+
{ id: "tidy-up", label: t("integration.canvas.contextMenu.item.tidyUp"), shortcut: "shift+alt+t" },
|
|
72
|
+
{ id: "select-all", label: t("integration.canvas.contextMenu.item.selectAll"), shortcut: "mod+a", disabled: allSelected.value },
|
|
73
|
+
{ id: "clear-selection", label: t("integration.canvas.contextMenu.item.clearSelection"), shortcut: "escape" }
|
|
73
74
|
],
|
|
74
75
|
[
|
|
75
|
-
{ id: "delete", label:
|
|
76
|
+
{ id: "delete", label: t("integration.canvas.contextMenu.item.deleteNodes", n), shortcut: "backspace", danger: true }
|
|
76
77
|
]
|
|
77
78
|
];
|
|
78
79
|
}
|
|
79
80
|
return [
|
|
80
81
|
[
|
|
81
|
-
{ id: "add-node", label: "
|
|
82
|
-
{ id: "add-sticky", label: "
|
|
83
|
-
{ id: "paste", label: "
|
|
82
|
+
{ id: "add-node", label: t("integration.canvas.contextMenu.item.addNode"), shortcut: "tab" },
|
|
83
|
+
{ id: "add-sticky", label: t("integration.canvas.contextMenu.item.addSticky"), shortcut: "shift+s" },
|
|
84
|
+
{ id: "paste", label: t("integration.canvas.contextMenu.item.paste"), shortcut: "mod+v" }
|
|
84
85
|
],
|
|
85
86
|
[
|
|
86
|
-
{ id: "tidy-up", label: "
|
|
87
|
-
{ id: "fit-view", label: "
|
|
87
|
+
{ id: "tidy-up", label: t("integration.canvas.contextMenu.item.tidyUp"), shortcut: "shift+alt+t" },
|
|
88
|
+
{ id: "fit-view", label: t("integration.canvas.contextMenu.item.fitView"), shortcut: "1" }
|
|
88
89
|
],
|
|
89
90
|
[
|
|
90
|
-
{ id: "select-all", label: "
|
|
91
|
+
{ id: "select-all", label: t("integration.canvas.contextMenu.item.selectAll"), shortcut: "mod+a", disabled: allSelected.value || props.totalNodes === 0 }
|
|
91
92
|
]
|
|
92
93
|
];
|
|
93
94
|
});
|
|
94
95
|
const headerLabel = computed(
|
|
95
|
-
() => props.kind === "selection" ?
|
|
96
|
+
() => props.kind === "selection" ? t("integration.canvas.contextMenu.header.selectionCount", props.selectionCount) : null
|
|
96
97
|
);
|
|
97
98
|
function onSelect(item) {
|
|
98
99
|
if (item.disabled) return;
|
|
@@ -25,14 +25,15 @@ import WorkflowIconPicker from "./WorkflowIconPicker.vue";
|
|
|
25
25
|
import WorkflowTagsInput from "./WorkflowTagsInput.vue";
|
|
26
26
|
import { resolveLocalized } from "../../utils/localize";
|
|
27
27
|
const open = defineModel("open", { type: Boolean, ...{ default: false } });
|
|
28
|
+
const t = useIntegrationsT();
|
|
28
29
|
const router = useRouter();
|
|
29
30
|
const { create, workflows } = useIntegrationsWorkflows();
|
|
30
31
|
const { templates, loading: templatesLoading, error: templatesError, load: loadTemplates, ensureDetails, instantiate } = useIntegrationsTemplates();
|
|
31
32
|
const mode = ref("blank");
|
|
32
|
-
const modeTabs = [
|
|
33
|
-
{ key: "blank", label: "
|
|
34
|
-
{ key: "template", label: "
|
|
35
|
-
];
|
|
33
|
+
const modeTabs = computed(() => [
|
|
34
|
+
{ key: "blank", label: t("integration.workflows.create.tab.blank") },
|
|
35
|
+
{ key: "template", label: t("integration.workflows.create.tab.template") }
|
|
36
|
+
]);
|
|
36
37
|
function iconFor(name2) {
|
|
37
38
|
if (!name2) return Lucide.Box;
|
|
38
39
|
return Lucide[name2] || Lucide.Box;
|
|
@@ -45,18 +46,18 @@ const tags = ref([]);
|
|
|
45
46
|
const icon = ref(null);
|
|
46
47
|
const tagSuggestions = computed(() => {
|
|
47
48
|
const all = /* @__PURE__ */ new Set();
|
|
48
|
-
for (const wf of workflows.value) for (const
|
|
49
|
+
for (const wf of workflows.value) for (const t2 of wf.blob.tags ?? []) all.add(t2);
|
|
49
50
|
return [...all].sort();
|
|
50
51
|
});
|
|
51
52
|
const submitting = ref(false);
|
|
52
53
|
const error = ref(null);
|
|
53
|
-
const executionModes = [
|
|
54
|
-
{ key: "async_only", label: "
|
|
55
|
-
{ key: "sync_only", label: "
|
|
56
|
-
{ key: "caller_decides", label: "
|
|
57
|
-
];
|
|
58
|
-
const selectedTrigger = computed(() => TRIGGER_TEMPLATES.find((
|
|
59
|
-
const selectedMode = computed(() => executionModes.find((m) => m.key === executionMode.value) ?? executionModes[0]);
|
|
54
|
+
const executionModes = computed(() => [
|
|
55
|
+
{ key: "async_only", label: t("integration.workflows.create.executionMode.asyncOnly.label"), hint: t("integration.workflows.create.executionMode.asyncOnly.hint"), icon: "Inbox" },
|
|
56
|
+
{ key: "sync_only", label: t("integration.workflows.create.executionMode.syncOnly.label"), hint: t("integration.workflows.create.executionMode.syncOnly.hint"), icon: "Timer" },
|
|
57
|
+
{ key: "caller_decides", label: t("integration.workflows.create.executionMode.callerDecides.label"), hint: t("integration.workflows.create.executionMode.callerDecides.hint"), icon: "SlidersHorizontal" }
|
|
58
|
+
]);
|
|
59
|
+
const selectedTrigger = computed(() => TRIGGER_TEMPLATES.find((tt) => tt.type === triggerType.value) ?? null);
|
|
60
|
+
const selectedMode = computed(() => executionModes.value.find((m) => m.key === executionMode.value) ?? executionModes.value[0]);
|
|
60
61
|
watch(open, (v) => {
|
|
61
62
|
if (!v) return;
|
|
62
63
|
mode.value = "blank";
|
|
@@ -72,13 +73,13 @@ watch(open, (v) => {
|
|
|
72
73
|
});
|
|
73
74
|
async function submit() {
|
|
74
75
|
if (!name.value.trim()) {
|
|
75
|
-
error.value = "
|
|
76
|
+
error.value = t("integration.workflows.create.error.nameRequired");
|
|
76
77
|
return;
|
|
77
78
|
}
|
|
78
79
|
submitting.value = true;
|
|
79
80
|
error.value = null;
|
|
80
81
|
try {
|
|
81
|
-
const tpl = TRIGGER_TEMPLATES.find((
|
|
82
|
+
const tpl = TRIGGER_TEMPLATES.find((tt) => tt.type === triggerType.value);
|
|
82
83
|
const handle = globalThis.crypto?.randomUUID?.() ?? `trg-${Date.now()}`;
|
|
83
84
|
const blob = {
|
|
84
85
|
nodeManifestVersion: "v0-draft",
|
|
@@ -100,7 +101,7 @@ async function submit() {
|
|
|
100
101
|
open.value = false;
|
|
101
102
|
await router.push(`/integrations/workflows/${created.id}`);
|
|
102
103
|
} catch (e) {
|
|
103
|
-
error.value = e.message || "
|
|
104
|
+
error.value = e.message || t("integration.workflows.create.error.generic");
|
|
104
105
|
} finally {
|
|
105
106
|
submitting.value = false;
|
|
106
107
|
}
|
|
@@ -108,12 +109,12 @@ async function submit() {
|
|
|
108
109
|
const instantiatingSlug = ref(null);
|
|
109
110
|
const templateSearch = ref("");
|
|
110
111
|
watch(() => templates.value.length, (n) => {
|
|
111
|
-
if (n) void ensureDetails(templates.value.map((
|
|
112
|
+
if (n) void ensureDetails(templates.value.map((tpl) => tpl.slug));
|
|
112
113
|
}, { immediate: true });
|
|
113
114
|
const filteredTemplates = computed(() => {
|
|
114
115
|
const q = templateSearch.value.trim().toLowerCase();
|
|
115
116
|
if (!q) return templates.value;
|
|
116
|
-
return templates.value.filter((
|
|
117
|
+
return templates.value.filter((tpl) => `${tpl.name} ${tpl.description} ${tpl.category}`.toLowerCase().includes(q));
|
|
117
118
|
});
|
|
118
119
|
async function useTemplate(slug) {
|
|
119
120
|
if (instantiatingSlug.value) return;
|
|
@@ -124,7 +125,7 @@ async function useTemplate(slug) {
|
|
|
124
125
|
open.value = false;
|
|
125
126
|
await router.push(`/integrations/workflows/${id}`);
|
|
126
127
|
} catch (e) {
|
|
127
|
-
toast.error(e?.message ?? "
|
|
128
|
+
toast.error(e?.message ?? t("integration.workflows.create.toast.templateError"));
|
|
128
129
|
} finally {
|
|
129
130
|
instantiatingSlug.value = null;
|
|
130
131
|
}
|
|
@@ -135,7 +136,7 @@ async function useTemplate(slug) {
|
|
|
135
136
|
<Dialog v-model:open="open">
|
|
136
137
|
<DialogContent class="sm:max-w-4xl">
|
|
137
138
|
<DialogHeader>
|
|
138
|
-
<DialogTitle>
|
|
139
|
+
<DialogTitle>{{ t("integration.workflows.create.dialog.title") }}</DialogTitle>
|
|
139
140
|
</DialogHeader>
|
|
140
141
|
|
|
141
142
|
<!-- Switcher: the same TabBar the studio surfaces use. -->
|
|
@@ -146,33 +147,33 @@ async function useTemplate(slug) {
|
|
|
146
147
|
<form v-if="mode === 'blank'" class="-m-1 max-h-[65vh] space-y-3 overflow-y-auto p-1" @submit.prevent="submit">
|
|
147
148
|
<!-- Two-column rows: label rail left, field right. -->
|
|
148
149
|
<div class="grid grid-cols-[130px_1fr] items-center gap-x-4">
|
|
149
|
-
<Label for="wf-name">
|
|
150
|
-
<Input id="wf-name" v-model="name" placeholder="
|
|
150
|
+
<Label for="wf-name">{{ t("integration.workflows.create.field.name.label") }}</Label>
|
|
151
|
+
<Input id="wf-name" v-model="name" :placeholder="t('integration.workflows.create.field.name.placeholder')" class="bg-white" />
|
|
151
152
|
</div>
|
|
152
153
|
|
|
153
154
|
<div class="grid grid-cols-[130px_1fr] items-start gap-x-4">
|
|
154
|
-
<Label for="wf-desc" class="pt-2.5">
|
|
155
|
+
<Label for="wf-desc" class="pt-2.5">{{ t("integration.workflows.create.field.description.label") }}</Label>
|
|
155
156
|
<Textarea
|
|
156
157
|
id="wf-desc"
|
|
157
158
|
v-model="description"
|
|
158
159
|
rows="2"
|
|
159
|
-
placeholder="
|
|
160
|
+
:placeholder="t('integration.workflows.create.field.description.placeholder')"
|
|
160
161
|
class="bg-white"
|
|
161
162
|
/>
|
|
162
163
|
</div>
|
|
163
164
|
|
|
164
165
|
<div class="grid grid-cols-[130px_1fr] items-center gap-x-4">
|
|
165
|
-
<Label for="wf-tags">
|
|
166
|
+
<Label for="wf-tags">{{ t("integration.workflows.create.field.tags.label") }}</Label>
|
|
166
167
|
<WorkflowTagsInput v-model="tags" input-id="wf-tags" :suggestions="tagSuggestions" />
|
|
167
168
|
</div>
|
|
168
169
|
|
|
169
170
|
<div class="grid grid-cols-[130px_1fr] items-center gap-x-4">
|
|
170
|
-
<Label id="wf-icon-label">
|
|
171
|
+
<Label id="wf-icon-label">{{ t("integration.workflows.create.field.icon.label") }}</Label>
|
|
171
172
|
<WorkflowIconPicker v-model="icon" trigger-labelledby="wf-icon-label" />
|
|
172
173
|
</div>
|
|
173
174
|
|
|
174
175
|
<div class="grid grid-cols-[130px_1fr] items-center gap-x-4">
|
|
175
|
-
<Label for="wf-trigger">
|
|
176
|
+
<Label for="wf-trigger">{{ t("integration.workflows.create.field.trigger.label") }}</Label>
|
|
176
177
|
<Select v-model="triggerType">
|
|
177
178
|
<SelectTrigger id="wf-trigger" class="w-full bg-white">
|
|
178
179
|
<SelectValue>
|
|
@@ -202,7 +203,7 @@ async function useTemplate(slug) {
|
|
|
202
203
|
</div>
|
|
203
204
|
|
|
204
205
|
<div class="grid grid-cols-[130px_1fr] items-center gap-x-4">
|
|
205
|
-
<Label for="wf-exec-mode">
|
|
206
|
+
<Label for="wf-exec-mode">{{ t("integration.workflows.create.field.executionMode.label") }}</Label>
|
|
206
207
|
<Select v-model="executionMode">
|
|
207
208
|
<SelectTrigger id="wf-exec-mode" class="w-full bg-white">
|
|
208
209
|
<SelectValue>
|
|
@@ -238,10 +239,10 @@ async function useTemplate(slug) {
|
|
|
238
239
|
<div class="flex gap-2 pt-1">
|
|
239
240
|
<Button type="submit" :disabled="submitting" class="h-8 text-sm">
|
|
240
241
|
<Loader2 v-if="submitting" :size="14" class="animate-spin" />
|
|
241
|
-
{{ submitting ? "
|
|
242
|
+
{{ submitting ? t("integration.workflows.create.action.submitting") : t("integration.workflows.create.action.submit") }}
|
|
242
243
|
</Button>
|
|
243
244
|
<Button type="button" variant="outline" class="h-8 text-sm" @click="open = false">
|
|
244
|
-
|
|
245
|
+
{{ t("integration.common.action.cancel") }}
|
|
245
246
|
</Button>
|
|
246
247
|
</div>
|
|
247
248
|
</form>
|
|
@@ -253,8 +254,8 @@ async function useTemplate(slug) {
|
|
|
253
254
|
<input
|
|
254
255
|
v-model="templateSearch"
|
|
255
256
|
type="text"
|
|
256
|
-
aria-label="
|
|
257
|
-
placeholder="
|
|
257
|
+
:aria-label="t('integration.workflows.create.template.search.aria')"
|
|
258
|
+
:placeholder="t('integration.workflows.create.template.search.placeholder')"
|
|
258
259
|
class="h-8 w-full rounded-md border border-black/[0.08] bg-white pl-7 pr-2 text-sm focus:outline-none focus:ring-2 focus:ring-emerald-500/40"
|
|
259
260
|
>
|
|
260
261
|
</div>
|
|
@@ -273,10 +274,10 @@ async function useTemplate(slug) {
|
|
|
273
274
|
{{ templatesError }}
|
|
274
275
|
</div>
|
|
275
276
|
<p v-else-if="!templates.length" class="p-6 text-center text-sm text-muted-foreground">
|
|
276
|
-
|
|
277
|
+
{{ t("integration.workflows.create.template.empty") }}
|
|
277
278
|
</p>
|
|
278
279
|
<p v-else-if="!filteredTemplates.length" class="p-6 text-center text-sm text-muted-foreground">
|
|
279
|
-
|
|
280
|
+
{{ t("integration.workflows.create.template.noMatch", { query: templateSearch }) }}
|
|
280
281
|
</p>
|
|
281
282
|
|
|
282
283
|
<div v-else class="grid gap-2.5 sm:grid-cols-2 lg:grid-cols-3">
|
|
@@ -294,10 +295,10 @@ async function useTemplate(slug) {
|
|
|
294
295
|
<div class="flex items-center justify-between pt-1">
|
|
295
296
|
<NuxtLink to="/integrations/templates" class="inline-flex items-center gap-1 text-sm text-muted-foreground transition-colors hover:text-primary" @click="open = false">
|
|
296
297
|
<Sparkles :size="13" />
|
|
297
|
-
|
|
298
|
+
{{ t("integration.workflows.create.template.browseAll") }}
|
|
298
299
|
</NuxtLink>
|
|
299
300
|
<Button type="button" variant="outline" class="h-8 text-sm" @click="open = false">
|
|
300
|
-
|
|
301
|
+
{{ t("integration.common.action.cancel") }}
|
|
301
302
|
</Button>
|
|
302
303
|
</div>
|
|
303
304
|
</div>
|
|
@@ -8,13 +8,14 @@ const props = defineProps({
|
|
|
8
8
|
workflow: { type: [Object, null], required: true }
|
|
9
9
|
});
|
|
10
10
|
const emit = defineEmits(["duplicated"]);
|
|
11
|
+
const t = useIntegrationsT();
|
|
11
12
|
const { duplicate } = useIntegrationsWorkflows();
|
|
12
13
|
const name = ref("");
|
|
13
14
|
const busy = ref(false);
|
|
14
15
|
const error = ref(null);
|
|
15
16
|
watch(open, (o) => {
|
|
16
17
|
if (o) {
|
|
17
|
-
name.value = props.workflow ?
|
|
18
|
+
name.value = props.workflow ? t("integration.workflows.duplicate.field.name.default", { name: props.workflow.name }) : "";
|
|
18
19
|
error.value = null;
|
|
19
20
|
}
|
|
20
21
|
});
|
|
@@ -28,7 +29,7 @@ async function submit() {
|
|
|
28
29
|
open.value = false;
|
|
29
30
|
emit("duplicated", copy);
|
|
30
31
|
} catch (e) {
|
|
31
|
-
error.value = e?.message || "
|
|
32
|
+
error.value = e?.message || t("integration.workflows.duplicate.error.generic");
|
|
32
33
|
} finally {
|
|
33
34
|
busy.value = false;
|
|
34
35
|
}
|
|
@@ -39,23 +40,23 @@ async function submit() {
|
|
|
39
40
|
<Dialog v-model:open="open">
|
|
40
41
|
<DialogContent class="sm:max-w-md">
|
|
41
42
|
<DialogHeader>
|
|
42
|
-
<DialogTitle>
|
|
43
|
+
<DialogTitle>{{ t("integration.workflows.duplicate.dialog.title") }}</DialogTitle>
|
|
43
44
|
<DialogDescription class="text-sm">
|
|
44
|
-
|
|
45
|
+
{{ t("integration.workflows.duplicate.dialog.description", { name: props.workflow?.name ?? "" }) }}
|
|
45
46
|
</DialogDescription>
|
|
46
47
|
</DialogHeader>
|
|
47
48
|
|
|
48
49
|
<form class="space-y-2" @submit.prevent="submit">
|
|
49
|
-
<label class="text-sm font-medium" for="wf-duplicate-name">
|
|
50
|
+
<label class="text-sm font-medium" for="wf-duplicate-name">{{ t("integration.workflows.duplicate.field.name.label") }}</label>
|
|
50
51
|
<Input id="wf-duplicate-name" v-model="name" autofocus :disabled="busy" />
|
|
51
52
|
<p v-if="error" class="text-sm text-rose-600">{{ error }}</p>
|
|
52
53
|
</form>
|
|
53
54
|
|
|
54
55
|
<DialogFooter>
|
|
55
|
-
<Button variant="outline" size="sm" class="h-8 text-sm" :disabled="busy" @click="open = false">
|
|
56
|
+
<Button variant="outline" size="sm" class="h-8 text-sm" :disabled="busy" @click="open = false">{{ t("integration.common.action.cancel") }}</Button>
|
|
56
57
|
<Button size="sm" class="h-8 text-sm" :disabled="!canSubmit" @click="submit">
|
|
57
58
|
<Loader2 v-if="busy" :size="14" class="animate-spin" />
|
|
58
|
-
|
|
59
|
+
{{ t("integration.workflows.action.duplicate") }}
|
|
59
60
|
</Button>
|
|
60
61
|
</DialogFooter>
|
|
61
62
|
</DialogContent>
|
|
@@ -6,7 +6,8 @@ import {
|
|
|
6
6
|
getSmoothStepPath,
|
|
7
7
|
useVueFlow
|
|
8
8
|
} from "@vue-flow/core";
|
|
9
|
-
import { Plus
|
|
9
|
+
import { Plus } from "lucide-vue-next";
|
|
10
|
+
import { TrashBinMinimalistic as TrashBin } from "#solar-icons";
|
|
10
11
|
const props = defineProps({
|
|
11
12
|
id: { type: String, required: true },
|
|
12
13
|
sourceNode: { type: Object, required: true },
|
|
@@ -39,6 +40,7 @@ const props = defineProps({
|
|
|
39
40
|
targetX: { type: Number, required: true },
|
|
40
41
|
targetY: { type: Number, required: true }
|
|
41
42
|
});
|
|
43
|
+
const t = useIntegrationsT();
|
|
42
44
|
const flow = useVueFlow();
|
|
43
45
|
const edgeActions = inject("workflow-edge-actions", null);
|
|
44
46
|
const centerY = computed(() => {
|
|
@@ -145,7 +147,7 @@ function spliceNode() {
|
|
|
145
147
|
<button
|
|
146
148
|
type="button"
|
|
147
149
|
class="flex size-5 items-center justify-center rounded text-muted-foreground transition hover:bg-muted/60 hover:text-foreground"
|
|
148
|
-
title="
|
|
150
|
+
:title="t('integration.canvas.edge.action.insertNode')"
|
|
149
151
|
@click.stop="spliceNode"
|
|
150
152
|
>
|
|
151
153
|
<Plus :size="12" />
|
|
@@ -154,10 +156,10 @@ function spliceNode() {
|
|
|
154
156
|
<button
|
|
155
157
|
type="button"
|
|
156
158
|
class="flex size-5 items-center justify-center rounded text-muted-foreground transition hover:bg-rose-50 hover:text-rose-600"
|
|
157
|
-
title="
|
|
159
|
+
:title="t('integration.canvas.edge.action.delete')"
|
|
158
160
|
@click.stop="removeEdge"
|
|
159
161
|
>
|
|
160
|
-
<
|
|
162
|
+
<TrashBin :size="12" />
|
|
161
163
|
</button>
|
|
162
164
|
</div>
|
|
163
165
|
</EdgeLabelRenderer>
|