@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
|
@@ -20,22 +20,20 @@ import {
|
|
|
20
20
|
} from "@revenexx/studio";
|
|
21
21
|
import { toast } from "vue-sonner";
|
|
22
22
|
import { TRIGGER_TEMPLATES } from "../../data/integrationNodeCatalog";
|
|
23
|
-
import
|
|
23
|
+
import TemplateCard from "./TemplateCard.vue";
|
|
24
24
|
import WorkflowIconPicker from "./WorkflowIconPicker.vue";
|
|
25
25
|
import WorkflowTagsInput from "./WorkflowTagsInput.vue";
|
|
26
|
+
import { resolveLocalized } from "../../utils/localize";
|
|
26
27
|
const open = defineModel("open", { type: Boolean, ...{ default: false } });
|
|
28
|
+
const t = useIntegrationsT();
|
|
27
29
|
const router = useRouter();
|
|
28
30
|
const { create, workflows } = useIntegrationsWorkflows();
|
|
29
31
|
const { templates, loading: templatesLoading, error: templatesError, load: loadTemplates, ensureDetails, instantiate } = useIntegrationsTemplates();
|
|
30
32
|
const mode = ref("blank");
|
|
31
|
-
const modeTabs = [
|
|
32
|
-
{ key: "blank", label: "
|
|
33
|
-
{ key: "template", label: "
|
|
34
|
-
];
|
|
35
|
-
function loc(v) {
|
|
36
|
-
if (!v) return "";
|
|
37
|
-
return typeof v === "string" ? v : v.en ?? Object.values(v)[0] ?? "";
|
|
38
|
-
}
|
|
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
|
+
]);
|
|
39
37
|
function iconFor(name2) {
|
|
40
38
|
if (!name2) return Lucide.Box;
|
|
41
39
|
return Lucide[name2] || Lucide.Box;
|
|
@@ -48,18 +46,18 @@ const tags = ref([]);
|
|
|
48
46
|
const icon = ref(null);
|
|
49
47
|
const tagSuggestions = computed(() => {
|
|
50
48
|
const all = /* @__PURE__ */ new Set();
|
|
51
|
-
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);
|
|
52
50
|
return [...all].sort();
|
|
53
51
|
});
|
|
54
52
|
const submitting = ref(false);
|
|
55
53
|
const error = ref(null);
|
|
56
|
-
const executionModes = [
|
|
57
|
-
{ key: "async_only", label: "
|
|
58
|
-
{ key: "sync_only", label: "
|
|
59
|
-
{ key: "caller_decides", label: "
|
|
60
|
-
];
|
|
61
|
-
const selectedTrigger = computed(() => TRIGGER_TEMPLATES.find((
|
|
62
|
-
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]);
|
|
63
61
|
watch(open, (v) => {
|
|
64
62
|
if (!v) return;
|
|
65
63
|
mode.value = "blank";
|
|
@@ -75,13 +73,13 @@ watch(open, (v) => {
|
|
|
75
73
|
});
|
|
76
74
|
async function submit() {
|
|
77
75
|
if (!name.value.trim()) {
|
|
78
|
-
error.value = "
|
|
76
|
+
error.value = t("integration.workflows.create.error.nameRequired");
|
|
79
77
|
return;
|
|
80
78
|
}
|
|
81
79
|
submitting.value = true;
|
|
82
80
|
error.value = null;
|
|
83
81
|
try {
|
|
84
|
-
const tpl = TRIGGER_TEMPLATES.find((
|
|
82
|
+
const tpl = TRIGGER_TEMPLATES.find((tt) => tt.type === triggerType.value);
|
|
85
83
|
const handle = globalThis.crypto?.randomUUID?.() ?? `trg-${Date.now()}`;
|
|
86
84
|
const blob = {
|
|
87
85
|
nodeManifestVersion: "v0-draft",
|
|
@@ -103,7 +101,7 @@ async function submit() {
|
|
|
103
101
|
open.value = false;
|
|
104
102
|
await router.push(`/integrations/workflows/${created.id}`);
|
|
105
103
|
} catch (e) {
|
|
106
|
-
error.value = e.message || "
|
|
104
|
+
error.value = e.message || t("integration.workflows.create.error.generic");
|
|
107
105
|
} finally {
|
|
108
106
|
submitting.value = false;
|
|
109
107
|
}
|
|
@@ -111,12 +109,12 @@ async function submit() {
|
|
|
111
109
|
const instantiatingSlug = ref(null);
|
|
112
110
|
const templateSearch = ref("");
|
|
113
111
|
watch(() => templates.value.length, (n) => {
|
|
114
|
-
if (n) void ensureDetails(templates.value.map((
|
|
112
|
+
if (n) void ensureDetails(templates.value.map((tpl) => tpl.slug));
|
|
115
113
|
}, { immediate: true });
|
|
116
114
|
const filteredTemplates = computed(() => {
|
|
117
115
|
const q = templateSearch.value.trim().toLowerCase();
|
|
118
116
|
if (!q) return templates.value;
|
|
119
|
-
return templates.value.filter((
|
|
117
|
+
return templates.value.filter((tpl) => `${tpl.name} ${tpl.description} ${tpl.category}`.toLowerCase().includes(q));
|
|
120
118
|
});
|
|
121
119
|
async function useTemplate(slug) {
|
|
122
120
|
if (instantiatingSlug.value) return;
|
|
@@ -127,7 +125,7 @@ async function useTemplate(slug) {
|
|
|
127
125
|
open.value = false;
|
|
128
126
|
await router.push(`/integrations/workflows/${id}`);
|
|
129
127
|
} catch (e) {
|
|
130
|
-
toast.error(e?.message ?? "
|
|
128
|
+
toast.error(e?.message ?? t("integration.workflows.create.toast.templateError"));
|
|
131
129
|
} finally {
|
|
132
130
|
instantiatingSlug.value = null;
|
|
133
131
|
}
|
|
@@ -138,7 +136,7 @@ async function useTemplate(slug) {
|
|
|
138
136
|
<Dialog v-model:open="open">
|
|
139
137
|
<DialogContent class="sm:max-w-4xl">
|
|
140
138
|
<DialogHeader>
|
|
141
|
-
<DialogTitle>
|
|
139
|
+
<DialogTitle>{{ t("integration.workflows.create.dialog.title") }}</DialogTitle>
|
|
142
140
|
</DialogHeader>
|
|
143
141
|
|
|
144
142
|
<!-- Switcher: the same TabBar the studio surfaces use. -->
|
|
@@ -149,42 +147,42 @@ async function useTemplate(slug) {
|
|
|
149
147
|
<form v-if="mode === 'blank'" class="-m-1 max-h-[65vh] space-y-3 overflow-y-auto p-1" @submit.prevent="submit">
|
|
150
148
|
<!-- Two-column rows: label rail left, field right. -->
|
|
151
149
|
<div class="grid grid-cols-[130px_1fr] items-center gap-x-4">
|
|
152
|
-
<Label for="wf-name">
|
|
153
|
-
<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" />
|
|
154
152
|
</div>
|
|
155
153
|
|
|
156
154
|
<div class="grid grid-cols-[130px_1fr] items-start gap-x-4">
|
|
157
|
-
<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>
|
|
158
156
|
<Textarea
|
|
159
157
|
id="wf-desc"
|
|
160
158
|
v-model="description"
|
|
161
159
|
rows="2"
|
|
162
|
-
placeholder="
|
|
160
|
+
:placeholder="t('integration.workflows.create.field.description.placeholder')"
|
|
163
161
|
class="bg-white"
|
|
164
162
|
/>
|
|
165
163
|
</div>
|
|
166
164
|
|
|
167
165
|
<div class="grid grid-cols-[130px_1fr] items-center gap-x-4">
|
|
168
|
-
<Label>
|
|
169
|
-
<WorkflowTagsInput v-model="tags" :suggestions="tagSuggestions" />
|
|
166
|
+
<Label for="wf-tags">{{ t("integration.workflows.create.field.tags.label") }}</Label>
|
|
167
|
+
<WorkflowTagsInput v-model="tags" input-id="wf-tags" :suggestions="tagSuggestions" />
|
|
170
168
|
</div>
|
|
171
169
|
|
|
172
170
|
<div class="grid grid-cols-[130px_1fr] items-center gap-x-4">
|
|
173
|
-
<Label>
|
|
174
|
-
<WorkflowIconPicker v-model="icon" />
|
|
171
|
+
<Label id="wf-icon-label">{{ t("integration.workflows.create.field.icon.label") }}</Label>
|
|
172
|
+
<WorkflowIconPicker v-model="icon" trigger-labelledby="wf-icon-label" />
|
|
175
173
|
</div>
|
|
176
174
|
|
|
177
175
|
<div class="grid grid-cols-[130px_1fr] items-center gap-x-4">
|
|
178
|
-
<Label>
|
|
176
|
+
<Label for="wf-trigger">{{ t("integration.workflows.create.field.trigger.label") }}</Label>
|
|
179
177
|
<Select v-model="triggerType">
|
|
180
|
-
<SelectTrigger class="w-full bg-white">
|
|
178
|
+
<SelectTrigger id="wf-trigger" class="w-full bg-white">
|
|
181
179
|
<SelectValue>
|
|
182
180
|
<span class="flex min-w-0 items-center gap-2">
|
|
183
181
|
<span class="flex size-6 shrink-0 items-center justify-center rounded-md bg-emerald-100 text-emerald-600">
|
|
184
182
|
<component :is="iconFor(selectedTrigger?.icon)" :size="13" />
|
|
185
183
|
</span>
|
|
186
|
-
<span class="shrink-0 text-sm font-medium">{{
|
|
187
|
-
<span class="truncate text-sm text-muted-foreground">{{
|
|
184
|
+
<span class="shrink-0 text-sm font-medium">{{ resolveLocalized(selectedTrigger?.name) }}</span>
|
|
185
|
+
<span class="truncate text-sm text-muted-foreground">{{ resolveLocalized(selectedTrigger?.description) }}</span>
|
|
188
186
|
</span>
|
|
189
187
|
</SelectValue>
|
|
190
188
|
</SelectTrigger>
|
|
@@ -195,8 +193,8 @@ async function useTemplate(slug) {
|
|
|
195
193
|
<component :is="iconFor(t.icon)" :size="14" />
|
|
196
194
|
</span>
|
|
197
195
|
<span class="min-w-0">
|
|
198
|
-
<span class="block text-sm font-medium">{{
|
|
199
|
-
<span class="block text-sm text-muted-foreground">{{
|
|
196
|
+
<span class="block text-sm font-medium">{{ resolveLocalized(t.name) }}</span>
|
|
197
|
+
<span class="block text-sm text-muted-foreground">{{ resolveLocalized(t.description) }}</span>
|
|
200
198
|
</span>
|
|
201
199
|
</span>
|
|
202
200
|
</SelectItem>
|
|
@@ -205,9 +203,9 @@ async function useTemplate(slug) {
|
|
|
205
203
|
</div>
|
|
206
204
|
|
|
207
205
|
<div class="grid grid-cols-[130px_1fr] items-center gap-x-4">
|
|
208
|
-
<Label>
|
|
206
|
+
<Label for="wf-exec-mode">{{ t("integration.workflows.create.field.executionMode.label") }}</Label>
|
|
209
207
|
<Select v-model="executionMode">
|
|
210
|
-
<SelectTrigger class="w-full bg-white">
|
|
208
|
+
<SelectTrigger id="wf-exec-mode" class="w-full bg-white">
|
|
211
209
|
<SelectValue>
|
|
212
210
|
<span class="flex min-w-0 items-center gap-2">
|
|
213
211
|
<span class="flex size-6 shrink-0 items-center justify-center rounded-md bg-slate-100 text-slate-500">
|
|
@@ -241,10 +239,10 @@ async function useTemplate(slug) {
|
|
|
241
239
|
<div class="flex gap-2 pt-1">
|
|
242
240
|
<Button type="submit" :disabled="submitting" class="h-8 text-sm">
|
|
243
241
|
<Loader2 v-if="submitting" :size="14" class="animate-spin" />
|
|
244
|
-
{{ submitting ? "
|
|
242
|
+
{{ submitting ? t("integration.workflows.create.action.submitting") : t("integration.workflows.create.action.submit") }}
|
|
245
243
|
</Button>
|
|
246
244
|
<Button type="button" variant="outline" class="h-8 text-sm" @click="open = false">
|
|
247
|
-
|
|
245
|
+
{{ t("integration.common.action.cancel") }}
|
|
248
246
|
</Button>
|
|
249
247
|
</div>
|
|
250
248
|
</form>
|
|
@@ -256,7 +254,8 @@ async function useTemplate(slug) {
|
|
|
256
254
|
<input
|
|
257
255
|
v-model="templateSearch"
|
|
258
256
|
type="text"
|
|
259
|
-
|
|
257
|
+
:aria-label="t('integration.workflows.create.template.search.aria')"
|
|
258
|
+
:placeholder="t('integration.workflows.create.template.search.placeholder')"
|
|
260
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"
|
|
261
260
|
>
|
|
262
261
|
</div>
|
|
@@ -275,45 +274,31 @@ async function useTemplate(slug) {
|
|
|
275
274
|
{{ templatesError }}
|
|
276
275
|
</div>
|
|
277
276
|
<p v-else-if="!templates.length" class="p-6 text-center text-sm text-muted-foreground">
|
|
278
|
-
|
|
277
|
+
{{ t("integration.workflows.create.template.empty") }}
|
|
279
278
|
</p>
|
|
280
279
|
<p v-else-if="!filteredTemplates.length" class="p-6 text-center text-sm text-muted-foreground">
|
|
281
|
-
|
|
280
|
+
{{ t("integration.workflows.create.template.noMatch", { query: templateSearch }) }}
|
|
282
281
|
</p>
|
|
283
282
|
|
|
284
283
|
<div v-else class="grid gap-2.5 sm:grid-cols-2 lg:grid-cols-3">
|
|
285
|
-
<
|
|
284
|
+
<TemplateCard
|
|
286
285
|
v-for="tpl in filteredTemplates"
|
|
287
286
|
:key="tpl.slug"
|
|
288
|
-
|
|
289
|
-
|
|
287
|
+
:template="tpl"
|
|
288
|
+
density="compact"
|
|
289
|
+
:busy="instantiatingSlug === tpl.slug"
|
|
290
290
|
:disabled="!!instantiatingSlug"
|
|
291
|
-
@
|
|
292
|
-
|
|
293
|
-
<div class="relative h-24 shrink-0 border-b border-black/[0.06] bg-slate-50/70 [background-image:radial-gradient(circle,rgba(15,23,42,0.07)_1px,transparent_1px)] [background-size:12px_12px]">
|
|
294
|
-
<TemplateFlowPreview v-if="tpl.detailLoaded" :blob="tpl.blob" :height="96" class="px-1" />
|
|
295
|
-
<div v-else class="flex h-full items-center justify-center">
|
|
296
|
-
<Loader2 :size="14" class="animate-spin text-muted-foreground/40" />
|
|
297
|
-
</div>
|
|
298
|
-
<span class="absolute left-1.5 top-1.5 inline-flex items-center rounded-full bg-slate-100 px-1.5 py-px uppercase text-[11px] font-medium text-slate-700">
|
|
299
|
-
{{ tpl.category.charAt(0).toUpperCase() + tpl.category.slice(1) }}
|
|
300
|
-
</span>
|
|
301
|
-
<Loader2 v-if="instantiatingSlug === tpl.slug" :size="13" class="absolute right-1.5 top-1.5 animate-spin text-muted-foreground" />
|
|
302
|
-
</div>
|
|
303
|
-
<div class="flex min-h-0 flex-1 flex-col p-3">
|
|
304
|
-
<div class="truncate text-sm font-medium">{{ tpl.name }}</div>
|
|
305
|
-
<p class="mt-0.5 line-clamp-2 text-sm text-muted-foreground">{{ tpl.description }}</p>
|
|
306
|
-
</div>
|
|
307
|
-
</button>
|
|
291
|
+
@select="useTemplate(tpl.slug)"
|
|
292
|
+
/>
|
|
308
293
|
</div>
|
|
309
294
|
|
|
310
295
|
<div class="flex items-center justify-between pt-1">
|
|
311
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">
|
|
312
297
|
<Sparkles :size="13" />
|
|
313
|
-
|
|
298
|
+
{{ t("integration.workflows.create.template.browseAll") }}
|
|
314
299
|
</NuxtLink>
|
|
315
300
|
<Button type="button" variant="outline" class="h-8 text-sm" @click="open = false">
|
|
316
|
-
|
|
301
|
+
{{ t("integration.common.action.cancel") }}
|
|
317
302
|
</Button>
|
|
318
303
|
</div>
|
|
319
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>
|
|
@@ -4,10 +4,14 @@ import { type EdgeProps } from '@vue-flow/core';
|
|
|
4
4
|
*
|
|
5
5
|
* - hard right-angle routing (`getSmoothStepPath`, radius 0)
|
|
6
6
|
* - arrow-head pointing into the target
|
|
7
|
-
* -
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
7
|
+
* - colour encodes IMPORTANCE, not meaning (PO-197): the main flow leads in
|
|
8
|
+
* slate, the error/exception path recedes in a paler slate. The semantic
|
|
9
|
+
* colour (green = normal/active output incl. branch & switch cases, rose =
|
|
10
|
+
* error) lives on the source PORT DOT — a small, localised legend — so the
|
|
11
|
+
* connective edges stay calm.
|
|
12
|
+
* - optional center label (used by the canvas to print branch names) — branch
|
|
13
|
+
* routes only, never the error route (promised: `specs/editor-output-paths.md`,
|
|
14
|
+
* reasoning: `docs/editor-path-colors.md`)
|
|
11
15
|
* - n8n-style midpoint tools on hover/selection: [+] splices a node into
|
|
12
16
|
* the connection (editor-provided action), [trash] deletes the edge
|
|
13
17
|
*
|
|
@@ -19,6 +23,16 @@ interface Data {
|
|
|
19
23
|
kind: EdgeKind;
|
|
20
24
|
/** Optional human label shown mid-edge (e.g. "true", "cases:paid"). */
|
|
21
25
|
label?: string;
|
|
26
|
+
/**
|
|
27
|
+
* Fan-out lane: this edge's rank among its source node's ACTUAL outgoing
|
|
28
|
+
* edges (ordered left-to-right by output handle) and how many there are.
|
|
29
|
+
* Siblings leaving a multi-output node (Switch, out/error) would otherwise
|
|
30
|
+
* bend on the same horizontal line and overlap — the renderer staggers the
|
|
31
|
+
* bend height by lane so the horizontal runs sit on distinct levels. A lone
|
|
32
|
+
* edge has `laneCount` 1 and gets no stagger.
|
|
33
|
+
*/
|
|
34
|
+
laneIndex?: number;
|
|
35
|
+
laneCount?: number;
|
|
22
36
|
/**
|
|
23
37
|
* Run-replay overlay (WorkflowRunReplayPanel): the edge is on the path the
|
|
24
38
|
* run actually took — drawn blue and thicker. `ahead` marks the part of the
|
|
@@ -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,8 +40,22 @@ 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);
|
|
46
|
+
const centerY = computed(() => {
|
|
47
|
+
const n = props.data?.laneCount ?? 1;
|
|
48
|
+
const i = props.data?.laneIndex ?? 0;
|
|
49
|
+
if (n <= 1) return void 0;
|
|
50
|
+
const mid = (props.sourceY + props.targetY) / 2;
|
|
51
|
+
const STEP = 11;
|
|
52
|
+
const span = Math.abs(props.targetY - props.sourceY);
|
|
53
|
+
const dist = Math.abs(i - (n - 1) / 2);
|
|
54
|
+
const tie = i / Math.max(1, n - 1) * (STEP / 3);
|
|
55
|
+
const lift = Math.min(dist * STEP + tie, Math.max(0, span / 2 - span / 6));
|
|
56
|
+
const towardSource = Math.sign(props.sourceY - props.targetY) || -1;
|
|
57
|
+
return mid + towardSource * lift;
|
|
58
|
+
});
|
|
44
59
|
const path = computed(() => getSmoothStepPath({
|
|
45
60
|
sourceX: props.sourceX,
|
|
46
61
|
sourceY: props.sourceY,
|
|
@@ -48,15 +63,18 @@ const path = computed(() => getSmoothStepPath({
|
|
|
48
63
|
targetX: props.targetX,
|
|
49
64
|
targetY: props.targetY,
|
|
50
65
|
targetPosition: props.targetPosition,
|
|
51
|
-
borderRadius: 0
|
|
66
|
+
borderRadius: 0,
|
|
67
|
+
centerY: centerY.value
|
|
52
68
|
}));
|
|
69
|
+
const NEUTRAL = { stroke: "#94a3b8", label: "#475569", bg: "#f1f5f9" };
|
|
70
|
+
const MUTED = { stroke: "#cbd5e1", label: "#94a3b8", bg: "#f8fafc" };
|
|
53
71
|
const palette = {
|
|
54
|
-
default:
|
|
55
|
-
success:
|
|
56
|
-
error:
|
|
57
|
-
"branch-true":
|
|
58
|
-
"branch-false":
|
|
59
|
-
branch:
|
|
72
|
+
default: NEUTRAL,
|
|
73
|
+
success: NEUTRAL,
|
|
74
|
+
error: MUTED,
|
|
75
|
+
"branch-true": NEUTRAL,
|
|
76
|
+
"branch-false": NEUTRAL,
|
|
77
|
+
branch: NEUTRAL
|
|
60
78
|
};
|
|
61
79
|
const color = computed(() => palette[props.data?.kind ?? "default"] ?? palette.default);
|
|
62
80
|
const isSelected = computed(() => props.selected);
|
|
@@ -129,7 +147,7 @@ function spliceNode() {
|
|
|
129
147
|
<button
|
|
130
148
|
type="button"
|
|
131
149
|
class="flex size-5 items-center justify-center rounded text-muted-foreground transition hover:bg-muted/60 hover:text-foreground"
|
|
132
|
-
title="
|
|
150
|
+
:title="t('integration.canvas.edge.action.insertNode')"
|
|
133
151
|
@click.stop="spliceNode"
|
|
134
152
|
>
|
|
135
153
|
<Plus :size="12" />
|
|
@@ -138,10 +156,10 @@ function spliceNode() {
|
|
|
138
156
|
<button
|
|
139
157
|
type="button"
|
|
140
158
|
class="flex size-5 items-center justify-center rounded text-muted-foreground transition hover:bg-rose-50 hover:text-rose-600"
|
|
141
|
-
title="
|
|
159
|
+
:title="t('integration.canvas.edge.action.delete')"
|
|
142
160
|
@click.stop="removeEdge"
|
|
143
161
|
>
|
|
144
|
-
<
|
|
162
|
+
<TrashBin :size="12" />
|
|
145
163
|
</button>
|
|
146
164
|
</div>
|
|
147
165
|
</EdgeLabelRenderer>
|
|
@@ -4,10 +4,14 @@ import { type EdgeProps } from '@vue-flow/core';
|
|
|
4
4
|
*
|
|
5
5
|
* - hard right-angle routing (`getSmoothStepPath`, radius 0)
|
|
6
6
|
* - arrow-head pointing into the target
|
|
7
|
-
* -
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
7
|
+
* - colour encodes IMPORTANCE, not meaning (PO-197): the main flow leads in
|
|
8
|
+
* slate, the error/exception path recedes in a paler slate. The semantic
|
|
9
|
+
* colour (green = normal/active output incl. branch & switch cases, rose =
|
|
10
|
+
* error) lives on the source PORT DOT — a small, localised legend — so the
|
|
11
|
+
* connective edges stay calm.
|
|
12
|
+
* - optional center label (used by the canvas to print branch names) — branch
|
|
13
|
+
* routes only, never the error route (promised: `specs/editor-output-paths.md`,
|
|
14
|
+
* reasoning: `docs/editor-path-colors.md`)
|
|
11
15
|
* - n8n-style midpoint tools on hover/selection: [+] splices a node into
|
|
12
16
|
* the connection (editor-provided action), [trash] deletes the edge
|
|
13
17
|
*
|
|
@@ -19,6 +23,16 @@ interface Data {
|
|
|
19
23
|
kind: EdgeKind;
|
|
20
24
|
/** Optional human label shown mid-edge (e.g. "true", "cases:paid"). */
|
|
21
25
|
label?: string;
|
|
26
|
+
/**
|
|
27
|
+
* Fan-out lane: this edge's rank among its source node's ACTUAL outgoing
|
|
28
|
+
* edges (ordered left-to-right by output handle) and how many there are.
|
|
29
|
+
* Siblings leaving a multi-output node (Switch, out/error) would otherwise
|
|
30
|
+
* bend on the same horizontal line and overlap — the renderer staggers the
|
|
31
|
+
* bend height by lane so the horizontal runs sit on distinct levels. A lone
|
|
32
|
+
* edge has `laneCount` 1 and gets no stagger.
|
|
33
|
+
*/
|
|
34
|
+
laneIndex?: number;
|
|
35
|
+
laneCount?: number;
|
|
22
36
|
/**
|
|
23
37
|
* Run-replay overlay (WorkflowRunReplayPanel): the edge is on the path the
|
|
24
38
|
* run actually took — drawn blue and thicker. `ahead` marks the part of the
|
|
@@ -8,8 +8,27 @@ type __VLS_Props = {
|
|
|
8
8
|
runBusy?: boolean;
|
|
9
9
|
/** The run is queued behind a bundle build — busy, but worth saying why. */
|
|
10
10
|
runWaiting?: boolean;
|
|
11
|
+
/**
|
|
12
|
+
* One of the page's own lightboxes is open above the editor (settings,
|
|
13
|
+
* duplicate, run payload, the sync response, the deactivation prompt). The
|
|
14
|
+
* canvas stands its keyboard shortcuts down while one is up — see
|
|
15
|
+
* `canvasHasKeyboard` below. A new page-level dialog belongs in here too.
|
|
16
|
+
*/
|
|
17
|
+
overlayOpen?: boolean;
|
|
11
18
|
};
|
|
12
|
-
|
|
19
|
+
/**
|
|
20
|
+
* Show a node the user was sent to from outside the canvas — the validation
|
|
21
|
+
* banner's per-node line (PO-209). Centres the card and opens its settings,
|
|
22
|
+
* which is where the fields at fault are.
|
|
23
|
+
*
|
|
24
|
+
* A node inside a collapsed group has no card on the canvas: centring would pan
|
|
25
|
+
* to empty space, so it only opens the settings. The reverse can't happen — an
|
|
26
|
+
* id that is on the canvas is in the blob by construction.
|
|
27
|
+
*/
|
|
28
|
+
declare function revealNode(id: string): void;
|
|
29
|
+
declare const _default: import("vue").DefineComponent<__VLS_Props, {
|
|
30
|
+
revealNode: typeof revealNode;
|
|
31
|
+
}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
13
32
|
change: (blob: WorkflowBlob) => any;
|
|
14
33
|
run: () => any;
|
|
15
34
|
toggleActive: () => any;
|