@revenexx/studio-integrations 0.6.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +643 -0
- package/README.md +31 -0
- package/dist/module.d.mts +17 -1
- package/dist/module.json +1 -1
- package/dist/module.mjs +12 -3
- package/dist/runtime/components/Integrations/CredentialCreateDialog.vue +32 -28
- package/dist/runtime/components/Integrations/CredentialDetailDialog.vue +45 -38
- 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 +21 -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.d.vue.ts +3 -0
- package/dist/runtime/components/Integrations/NodeConfigDynamicField.vue +30 -16
- package/dist/runtime/components/Integrations/NodeConfigDynamicField.vue.d.ts +3 -0
- package/dist/runtime/components/Integrations/NodeConfigDynamicSchema.vue +10 -8
- package/dist/runtime/components/Integrations/NodeConfigField.d.vue.ts +3 -0
- package/dist/runtime/components/Integrations/NodeConfigField.vue +45 -42
- package/dist/runtime/components/Integrations/NodeConfigField.vue.d.ts +3 -0
- package/dist/runtime/components/Integrations/NodeInspector.d.vue.ts +2 -2
- package/dist/runtime/components/Integrations/NodeInspector.vue +315 -221
- package/dist/runtime/components/Integrations/NodeInspector.vue.d.ts +2 -2
- package/dist/runtime/components/Integrations/NodeOutputSchema.d.vue.ts +2 -1
- package/dist/runtime/components/Integrations/NodeOutputSchema.vue +37 -10
- package/dist/runtime/components/Integrations/NodeOutputSchema.vue.d.ts +2 -1
- package/dist/runtime/components/Integrations/NodePalette.vue +40 -35
- 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 +82 -34
- package/dist/runtime/components/Integrations/StickyNoteView.vue.d.ts +14 -4
- package/dist/runtime/components/Integrations/SurfaceCard.d.vue.ts +50 -0
- package/dist/runtime/components/Integrations/SurfaceCard.vue +55 -0
- package/dist/runtime/components/Integrations/SurfaceCard.vue.d.ts +50 -0
- package/dist/runtime/components/Integrations/TemplateCard.vue +18 -8
- package/dist/runtime/components/Integrations/TemplateFlowPreview.vue +22 -12
- package/dist/runtime/components/Integrations/WorkflowContextMenu.vue +34 -33
- package/dist/runtime/components/Integrations/WorkflowCreateDialog.vue +41 -39
- package/dist/runtime/components/Integrations/WorkflowDuplicateDialog.vue +8 -7
- package/dist/runtime/components/Integrations/WorkflowEdgeView.vue +6 -4
- package/dist/runtime/components/Integrations/WorkflowEditor.d.vue.ts +16 -0
- package/dist/runtime/components/Integrations/WorkflowEditor.vue +131 -87
- package/dist/runtime/components/Integrations/WorkflowEditor.vue.d.ts +16 -0
- package/dist/runtime/components/Integrations/WorkflowHistoryPanel.d.vue.ts +6 -0
- package/dist/runtime/components/Integrations/WorkflowHistoryPanel.vue +27 -24
- package/dist/runtime/components/Integrations/WorkflowHistoryPanel.vue.d.ts +6 -0
- package/dist/runtime/components/Integrations/WorkflowIconPicker.vue +7 -6
- package/dist/runtime/components/Integrations/WorkflowNodeView.vue +76 -46
- package/dist/runtime/components/Integrations/WorkflowRunReplayPanel.vue +55 -43
- package/dist/runtime/components/Integrations/WorkflowSettingsDialog.d.vue.ts +16 -17
- package/dist/runtime/components/Integrations/WorkflowSettingsDialog.vue +55 -43
- package/dist/runtime/components/Integrations/WorkflowSettingsDialog.vue.d.ts +16 -17
- package/dist/runtime/components/Integrations/WorkflowTagsInput.vue +9 -8
- package/dist/runtime/components/Integrations/WorkflowTriggerView.vue +15 -13
- package/dist/runtime/components/Integrations/WorkflowVariablesDialog.vue +79 -39
- 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.d.ts +11 -0
- package/dist/runtime/composables/useIntegrationsCredentials.js +8 -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 +14 -2
- 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/useIntegrationsSchedules.d.ts +3 -2
- 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.d.ts +23 -3
- package/dist/runtime/composables/useIntegrationsTriggers.js +21 -8
- package/dist/runtime/composables/useIntegrationsWorkflows.d.ts +10 -1
- package/dist/runtime/composables/useIntegrationsWorkflows.js +30 -7
- package/dist/runtime/composables/useNodeConfigResolve.js +4 -2
- package/dist/runtime/data/integrationNodeCatalog.d.ts +63 -1
- package/dist/runtime/data/integrationNodeCatalog.js +22 -4
- package/dist/runtime/data/integrationTemplates.d.ts +9 -0
- package/dist/runtime/data/integrationTemplates.js +7 -1
- package/dist/runtime/data/nodeInputData.d.ts +58 -1
- package/dist/runtime/data/nodeInputData.js +30 -5
- package/dist/runtime/data/payloadForm.js +2 -1
- package/dist/runtime/data/triggerGlyphs.d.ts +47 -0
- package/dist/runtime/data/triggerGlyphs.js +11 -0
- package/dist/runtime/data/workflowIconCatalog.js +2 -1
- package/dist/runtime/i18n/messages/de.json +1224 -0
- package/dist/runtime/i18n/messages/en.json +1224 -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 +73 -52
- package/dist/runtime/pages/integrations/index.vue +66 -73
- 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 +89 -72
- package/dist/runtime/pages/integrations/templates/index.vue +35 -27
- package/dist/runtime/pages/integrations/workflows/[id].vue +351 -197
- 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/types/integrations.d.ts +25 -1
- package/dist/runtime/utils/cockpitSources.d.ts +22 -3
- package/dist/runtime/utils/credentialState.d.ts +30 -2
- package/dist/runtime/utils/credentialState.js +26 -11
- package/dist/runtime/utils/localize.d.ts +52 -5
- package/dist/runtime/utils/localize.js +47 -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/servedPaths.d.ts +22 -0
- package/dist/runtime/utils/servedPaths.js +3 -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 +2 -2
- package/dist/runtime/utils/validationFindings.js +4 -3
- package/dist/types.d.mts +2 -6
- package/package.json +3 -2
|
@@ -2,8 +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 {
|
|
5
|
+
import { TrashBinMinimalistic as TrashBin } from "#solar-icons";
|
|
6
|
+
import { triggerGlyph as glyphForTrigger } from "../../data/triggerGlyphs";
|
|
6
7
|
import { resolveDisplayName } from "../../utils/localize";
|
|
8
|
+
import { triggerTypeLabel } from "../../data/integrationNodeCatalog";
|
|
7
9
|
import { describeCron } from "../../utils/schedule";
|
|
8
10
|
const props = defineProps({
|
|
9
11
|
id: { type: String, required: true },
|
|
@@ -12,19 +14,18 @@ const props = defineProps({
|
|
|
12
14
|
});
|
|
13
15
|
defineOptions({ inheritAttrs: false });
|
|
14
16
|
defineEmits(["updateNodeInternals"]);
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
});
|
|
17
|
+
const t = useIntegrationsT();
|
|
18
|
+
const icon = computed(() => glyphForTrigger(props.data.type));
|
|
18
19
|
const subtitle = computed(() => {
|
|
19
20
|
const c = props.data.config;
|
|
20
|
-
if (props.data.type === "schedule") return describeCron(c.cron) ?? c.cron ?? "
|
|
21
|
+
if (props.data.type === "schedule") return describeCron(c.cron) ?? c.cron ?? t("integration.canvas.trigger.subtitle.noSchedule");
|
|
21
22
|
if (props.data.type === "webhook") return `${c.method ?? "POST"} ${c.path ?? "/"}`;
|
|
22
|
-
if (props.data.type === "event") return c.event_type ?
|
|
23
|
-
return "
|
|
23
|
+
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");
|
|
24
|
+
return t("integration.canvas.trigger.subtitle.onDemand");
|
|
24
25
|
});
|
|
25
26
|
const title = computed(() => {
|
|
26
27
|
const n = props.data.name;
|
|
27
|
-
if (!n) return
|
|
28
|
+
if (!n) return triggerTypeLabel(props.data.type);
|
|
28
29
|
return resolveDisplayName(n);
|
|
29
30
|
});
|
|
30
31
|
const hovered = ref(false);
|
|
@@ -54,10 +55,11 @@ function sideToPosition(s) {
|
|
|
54
55
|
<button
|
|
55
56
|
type="button"
|
|
56
57
|
class="flex h-6 items-center gap-1 rounded px-1.5 text-sm text-rose-600 hover:bg-rose-50"
|
|
57
|
-
title="
|
|
58
|
+
:title="t('integration.common.action.delete')"
|
|
59
|
+
:aria-label="t('integration.canvas.trigger.action.deleteAria')"
|
|
58
60
|
@click="remove"
|
|
59
61
|
>
|
|
60
|
-
<
|
|
62
|
+
<TrashBin :size="11" />
|
|
61
63
|
</button>
|
|
62
64
|
</NodeToolbar>
|
|
63
65
|
|
|
@@ -74,7 +76,7 @@ function sideToPosition(s) {
|
|
|
74
76
|
identically (icon chip size-8/15, subtitle plain text-sm, mt-0.5). -->
|
|
75
77
|
<div class="flex items-center gap-2.5 px-3 py-2.5">
|
|
76
78
|
<span class="flex size-8 shrink-0 items-center justify-center rounded-md bg-emerald-500 text-white shadow-sm">
|
|
77
|
-
<component :is="icon" :size="15"
|
|
79
|
+
<component :is="icon" :size="15" />
|
|
78
80
|
</span>
|
|
79
81
|
<div class="min-w-0 flex-1">
|
|
80
82
|
<div class="text-sm font-semibold text-foreground leading-tight truncate">
|
|
@@ -86,8 +88,8 @@ function sideToPosition(s) {
|
|
|
86
88
|
</div>
|
|
87
89
|
</div>
|
|
88
90
|
<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">
|
|
89
|
-
<span>
|
|
90
|
-
<span v-if="data.active === false" class="text-amber-600">
|
|
91
|
+
<span>{{ t("integration.canvas.badge.trigger") }} · {{ data.type }}</span>
|
|
92
|
+
<span v-if="data.active === false" class="text-amber-600">{{ t("integration.canvas.trigger.badge.inactive") }}</span>
|
|
91
93
|
</div>
|
|
92
94
|
</div>
|
|
93
95
|
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
<script setup>
|
|
2
2
|
import { computed, reactive, ref, watch } from "vue";
|
|
3
|
-
import { AddCircle as Plus, Copy, TrashBinMinimalistic as
|
|
3
|
+
import { AddCircle as Plus, Copy, TrashBinMinimalistic as TrashBin } from "#solar-icons";
|
|
4
4
|
import {
|
|
5
5
|
Button,
|
|
6
6
|
Dialog,
|
|
7
7
|
DialogContent,
|
|
8
8
|
DialogDescription,
|
|
9
|
+
DialogFooter,
|
|
9
10
|
DialogHeader,
|
|
10
11
|
DialogTitle,
|
|
11
12
|
Input,
|
|
@@ -18,40 +19,59 @@ import {
|
|
|
18
19
|
SelectValue
|
|
19
20
|
} from "@revenexx/studio";
|
|
20
21
|
import { toast } from "vue-sonner";
|
|
21
|
-
import { resolveDisplayName } from "../../utils/localize";
|
|
22
|
+
import { languageKeyRead, languageKeyToWrite, resolveDisplayName } from "../../utils/localize";
|
|
22
23
|
const props = defineProps({
|
|
23
24
|
open: { type: Boolean, required: true },
|
|
24
25
|
variables: { type: Object, required: true }
|
|
25
26
|
});
|
|
26
27
|
const emit = defineEmits(["update:open", "update:variables"]);
|
|
28
|
+
const t = useIntegrationsT();
|
|
27
29
|
const KEY_PATTERN = /^[a-z_][a-z0-9_]*$/i;
|
|
28
30
|
const TYPES = ["string", "number", "boolean", "object", "array"];
|
|
31
|
+
const working = ref({});
|
|
29
32
|
const jsonDrafts = reactive({});
|
|
30
33
|
const jsonErrors = reactive({});
|
|
34
|
+
const descDrafts = reactive({});
|
|
31
35
|
watch(() => props.open, (o) => {
|
|
32
36
|
if (!o) return;
|
|
37
|
+
working.value = JSON.parse(JSON.stringify(props.variables));
|
|
33
38
|
for (const k of Object.keys(jsonDrafts)) delete jsonDrafts[k];
|
|
34
39
|
for (const k of Object.keys(jsonErrors)) delete jsonErrors[k];
|
|
35
|
-
for (const
|
|
40
|
+
for (const k of Object.keys(descDrafts)) delete descDrafts[k];
|
|
41
|
+
for (const [key, v] of Object.entries(working.value)) {
|
|
42
|
+
descDrafts[key] = descriptionText(v);
|
|
36
43
|
if (v.type === "object" || v.type === "array") {
|
|
37
44
|
jsonDrafts[key] = v.default === void 0 ? "" : JSON.stringify(v.default, null, 2);
|
|
38
45
|
jsonErrors[key] = null;
|
|
39
46
|
}
|
|
40
47
|
}
|
|
41
48
|
});
|
|
42
|
-
const entries = computed(() => Object.entries(
|
|
49
|
+
const entries = computed(() => Object.entries(working.value));
|
|
50
|
+
const changed = computed(() => JSON.stringify(working.value) !== JSON.stringify(props.variables));
|
|
51
|
+
const hasJsonError = computed(() => Object.values(jsonErrors).some(Boolean));
|
|
43
52
|
function commit(next) {
|
|
44
|
-
|
|
53
|
+
working.value = next;
|
|
54
|
+
}
|
|
55
|
+
function applyAndClose() {
|
|
56
|
+
for (const [key, v] of Object.entries(working.value)) {
|
|
57
|
+
if ((v.type === "object" || v.type === "array") && jsonDrafts[key] !== void 0) {
|
|
58
|
+
applyJsonDraft(key, v.type);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
if (hasJsonError.value) return;
|
|
62
|
+
if (changed.value) emit("update:variables", working.value);
|
|
63
|
+
emit("update:open", false);
|
|
45
64
|
}
|
|
46
65
|
function patchVar(key, patch) {
|
|
47
|
-
const cur =
|
|
66
|
+
const cur = working.value[key];
|
|
48
67
|
if (!cur) return;
|
|
49
|
-
commit({ ...
|
|
68
|
+
commit({ ...working.value, [key]: { ...cur, ...patch } });
|
|
50
69
|
}
|
|
51
70
|
function removeVar(key) {
|
|
52
|
-
const next = { ...
|
|
71
|
+
const next = { ...working.value };
|
|
53
72
|
delete next[key];
|
|
54
73
|
delete jsonDrafts[key];
|
|
74
|
+
delete descDrafts[key];
|
|
55
75
|
delete jsonErrors[key];
|
|
56
76
|
commit(next);
|
|
57
77
|
}
|
|
@@ -64,6 +84,8 @@ function changeType(key, type) {
|
|
|
64
84
|
array: void 0
|
|
65
85
|
};
|
|
66
86
|
patchVar(key, { type, default: blank[type] });
|
|
87
|
+
delete jsonDrafts[key];
|
|
88
|
+
delete jsonErrors[key];
|
|
67
89
|
if (type === "object" || type === "array") {
|
|
68
90
|
jsonDrafts[key] = "";
|
|
69
91
|
jsonErrors[key] = null;
|
|
@@ -80,49 +102,55 @@ function applyJsonDraft(key, type) {
|
|
|
80
102
|
const parsed = JSON.parse(raw);
|
|
81
103
|
const ok = type === "array" ? Array.isArray(parsed) : parsed !== null && typeof parsed === "object" && !Array.isArray(parsed);
|
|
82
104
|
if (!ok) {
|
|
83
|
-
jsonErrors[key] = type === "array" ? "
|
|
105
|
+
jsonErrors[key] = type === "array" ? t("integration.workflows.variables.error.mustBeArray") : t("integration.workflows.variables.error.mustBeObject");
|
|
84
106
|
return;
|
|
85
107
|
}
|
|
86
108
|
jsonErrors[key] = null;
|
|
87
109
|
patchVar(key, { default: parsed });
|
|
88
110
|
} catch {
|
|
89
|
-
jsonErrors[key] = "
|
|
111
|
+
jsonErrors[key] = t("integration.workflows.variables.error.invalidJson");
|
|
90
112
|
}
|
|
91
113
|
}
|
|
92
114
|
function descriptionText(v) {
|
|
93
115
|
return resolveDisplayName(v.description);
|
|
94
116
|
}
|
|
95
117
|
function writeDescription(key, text) {
|
|
96
|
-
const cur =
|
|
118
|
+
const cur = working.value[key];
|
|
97
119
|
if (!cur) return;
|
|
120
|
+
descDrafts[key] = text;
|
|
121
|
+
const existing = typeof cur.description === "object" && cur.description !== null ? { ...cur.description } : {};
|
|
98
122
|
if (!text.trim()) {
|
|
123
|
+
delete existing[languageKeyRead(cur.description)];
|
|
124
|
+
if (Object.keys(existing).length) {
|
|
125
|
+
patchVar(key, { description: existing });
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
99
128
|
const { description: _drop, ...rest } = cur;
|
|
100
|
-
commit({ ...
|
|
129
|
+
commit({ ...working.value, [key]: rest });
|
|
101
130
|
return;
|
|
102
131
|
}
|
|
103
|
-
|
|
104
|
-
patchVar(key, { description: { ...existing, en: text } });
|
|
132
|
+
patchVar(key, { description: { ...existing, [languageKeyToWrite(cur.description)]: text } });
|
|
105
133
|
}
|
|
106
134
|
const newKey = ref("");
|
|
107
135
|
const newKeyError = computed(() => {
|
|
108
136
|
const k = newKey.value.trim();
|
|
109
137
|
if (!k) return null;
|
|
110
|
-
if (!KEY_PATTERN.test(k)) return "
|
|
111
|
-
if (
|
|
138
|
+
if (!KEY_PATTERN.test(k)) return t("integration.workflows.variables.error.invalidKey");
|
|
139
|
+
if (working.value[k]) return t("integration.workflows.variables.error.keyExists");
|
|
112
140
|
return null;
|
|
113
141
|
});
|
|
114
142
|
function addVariable() {
|
|
115
143
|
const k = newKey.value.trim();
|
|
116
144
|
if (!k || newKeyError.value) return;
|
|
117
|
-
commit({ ...
|
|
145
|
+
commit({ ...working.value, [k]: { type: "string", default: "" } });
|
|
118
146
|
newKey.value = "";
|
|
119
147
|
}
|
|
120
148
|
async function copyUsage(key) {
|
|
121
149
|
try {
|
|
122
150
|
await navigator.clipboard.writeText(`\${{ vars.${key} }}`);
|
|
123
|
-
toast.success("
|
|
151
|
+
toast.success(t("integration.workflows.variables.toast.copied"));
|
|
124
152
|
} catch {
|
|
125
|
-
toast.error("
|
|
153
|
+
toast.error(t("integration.workflows.variables.toast.copyFailed"));
|
|
126
154
|
}
|
|
127
155
|
}
|
|
128
156
|
</script>
|
|
@@ -131,24 +159,24 @@ async function copyUsage(key) {
|
|
|
131
159
|
<Dialog :open="open" @update:open="(v2) => emit('update:open', v2)">
|
|
132
160
|
<DialogContent class="max-w-[94vw] sm:max-w-3xl">
|
|
133
161
|
<DialogHeader>
|
|
134
|
-
<DialogTitle>
|
|
162
|
+
<DialogTitle>{{ t("integration.workflows.variables.dialog.title") }}</DialogTitle>
|
|
135
163
|
<DialogDescription class="text-sm">
|
|
136
|
-
|
|
164
|
+
{{ t("integration.workflows.variables.dialog.description.before") }}
|
|
137
165
|
<code v-pre class="rounded bg-black/[0.05] px-1 py-0.5 font-mono text-xs">${{ vars.key }}</code>.
|
|
138
|
-
|
|
166
|
+
{{ t("integration.workflows.variables.dialog.description.after") }}
|
|
139
167
|
</DialogDescription>
|
|
140
168
|
</DialogHeader>
|
|
141
169
|
|
|
142
170
|
<div class="max-h-[62vh] space-y-3 overflow-y-auto pr-1">
|
|
143
171
|
<p v-if="!entries.length" class="rounded-lg border border-dashed border-black/[0.12] px-4 py-6 text-center text-sm text-muted-foreground">
|
|
144
|
-
|
|
172
|
+
{{ t("integration.workflows.variables.state.empty") }}
|
|
145
173
|
</p>
|
|
146
174
|
|
|
147
175
|
<div
|
|
148
176
|
v-for="[key, v] in entries"
|
|
149
177
|
:key="key"
|
|
150
178
|
role="group"
|
|
151
|
-
:aria-label="
|
|
179
|
+
:aria-label="t('integration.workflows.variables.row.aria', { key })"
|
|
152
180
|
class="rounded-lg border border-black/[0.06] bg-white p-3"
|
|
153
181
|
>
|
|
154
182
|
<div class="flex items-center gap-2">
|
|
@@ -156,7 +184,7 @@ async function copyUsage(key) {
|
|
|
156
184
|
<button
|
|
157
185
|
type="button"
|
|
158
186
|
class="flex size-6 shrink-0 items-center justify-center rounded-md text-muted-foreground transition hover:bg-muted/50 hover:text-foreground"
|
|
159
|
-
:title="
|
|
187
|
+
:title="t('integration.workflows.variables.action.copyUsage.title', { key })"
|
|
160
188
|
@click="copyUsage(key)"
|
|
161
189
|
>
|
|
162
190
|
<Copy :size="12" />
|
|
@@ -165,10 +193,10 @@ async function copyUsage(key) {
|
|
|
165
193
|
<button
|
|
166
194
|
type="button"
|
|
167
195
|
class="flex size-6 shrink-0 items-center justify-center rounded-md text-muted-foreground transition hover:bg-rose-50 hover:text-rose-600"
|
|
168
|
-
title="
|
|
196
|
+
:title="t('integration.workflows.variables.action.delete')"
|
|
169
197
|
@click="removeVar(key)"
|
|
170
198
|
>
|
|
171
|
-
<
|
|
199
|
+
<TrashBin :size="13" />
|
|
172
200
|
</button>
|
|
173
201
|
</div>
|
|
174
202
|
|
|
@@ -176,12 +204,12 @@ async function copyUsage(key) {
|
|
|
176
204
|
<!-- One row per variable, so every id carries its key — otherwise
|
|
177
205
|
the labels of the second row point at the first row's fields. -->
|
|
178
206
|
<div class="space-y-1">
|
|
179
|
-
<Label :for="`var-${key}-type`" class="text-mono text-muted-foreground">
|
|
180
|
-
<Select :model-value="v.type" @update:model-value="(
|
|
207
|
+
<Label :for="`var-${key}-type`" class="text-mono text-muted-foreground">{{ t("integration.workflows.variables.field.type.label") }}</Label>
|
|
208
|
+
<Select :model-value="v.type" @update:model-value="(vt) => changeType(key, vt)">
|
|
181
209
|
<!-- Both: the `id` lets the visible Label's `for` resolve, and the
|
|
182
210
|
`aria-label` names the trigger without relying on an engine
|
|
183
211
|
naming a button from a <label> — see the sweep's docblock. -->
|
|
184
|
-
<SelectTrigger :id="`var-${key}-type`" aria-label="
|
|
212
|
+
<SelectTrigger :id="`var-${key}-type`" :aria-label="t('integration.workflows.variables.field.type.label')" class="h-8 bg-white text-sm">
|
|
185
213
|
<SelectValue />
|
|
186
214
|
</SelectTrigger>
|
|
187
215
|
<SelectContent>
|
|
@@ -193,7 +221,7 @@ async function copyUsage(key) {
|
|
|
193
221
|
<div class="space-y-1">
|
|
194
222
|
<!-- "Value", not "Default": PO-206. The `for`/`id` still name the
|
|
195
223
|
blob field, which is called `default` and stays that way. -->
|
|
196
|
-
<Label :for="`var-${key}-default`" class="text-mono text-muted-foreground">
|
|
224
|
+
<Label :for="`var-${key}-default`" class="text-mono text-muted-foreground">{{ t("integration.workflows.variables.field.value.label") }}</Label>
|
|
197
225
|
|
|
198
226
|
<!-- Only one of these renders, so they share the one `id` the Label
|
|
199
227
|
points at. The labelable ones take their name from that Label —
|
|
@@ -221,7 +249,7 @@ async function copyUsage(key) {
|
|
|
221
249
|
<Switch
|
|
222
250
|
:id="`var-${key}-default`"
|
|
223
251
|
:model-value="v.default === true"
|
|
224
|
-
aria-label="
|
|
252
|
+
:aria-label="t('integration.workflows.variables.field.value.label')"
|
|
225
253
|
@update:model-value="(val) => patchVar(key, { default: val })"
|
|
226
254
|
/>
|
|
227
255
|
</div>
|
|
@@ -233,7 +261,7 @@ async function copyUsage(key) {
|
|
|
233
261
|
spellcheck="false"
|
|
234
262
|
class="w-full rounded-md border bg-white p-2 font-mono text-xs focus:outline-none focus:ring-2 focus:ring-primary/40"
|
|
235
263
|
:class="jsonErrors[key] ? 'border-rose-400' : 'border-black/[0.08]'"
|
|
236
|
-
:placeholder="v.type === 'array' ? '
|
|
264
|
+
:placeholder="v.type === 'array' ? t('integration.workflows.variables.field.value.placeholder.array') : t('integration.workflows.variables.field.value.placeholder.object')"
|
|
237
265
|
@input="(e) => jsonDrafts[key] = e.target.value"
|
|
238
266
|
@blur="applyJsonDraft(key, v.type)"
|
|
239
267
|
/>
|
|
@@ -243,12 +271,12 @@ async function copyUsage(key) {
|
|
|
243
271
|
</div>
|
|
244
272
|
|
|
245
273
|
<div class="mt-2 space-y-1">
|
|
246
|
-
<Label :for="`var-${key}-desc`" class="text-mono text-muted-foreground">
|
|
274
|
+
<Label :for="`var-${key}-desc`" class="text-mono text-muted-foreground">{{ t("integration.workflows.variables.field.description.label") }}</Label>
|
|
247
275
|
<Input
|
|
248
276
|
:id="`var-${key}-desc`"
|
|
249
|
-
:model-value="descriptionText(v)"
|
|
277
|
+
:model-value="descDrafts[key] ?? descriptionText(v)"
|
|
250
278
|
class="h-8 bg-white text-sm"
|
|
251
|
-
placeholder="
|
|
279
|
+
:placeholder="t('integration.workflows.variables.field.description.placeholder')"
|
|
252
280
|
@update:model-value="(val) => writeDescription(key, String(val))"
|
|
253
281
|
/>
|
|
254
282
|
</div>
|
|
@@ -256,23 +284,35 @@ async function copyUsage(key) {
|
|
|
256
284
|
|
|
257
285
|
<!-- Add row -->
|
|
258
286
|
<div class="rounded-lg border border-dashed border-black/[0.12] p-3">
|
|
259
|
-
<Label for="var-new-key" class="text-mono text-muted-foreground">
|
|
287
|
+
<Label for="var-new-key" class="text-mono text-muted-foreground">{{ t("integration.workflows.variables.field.newKey.label") }}</Label>
|
|
260
288
|
<div class="mt-1 flex items-center gap-2">
|
|
261
289
|
<Input
|
|
262
290
|
id="var-new-key"
|
|
263
291
|
v-model="newKey"
|
|
264
292
|
class="h-8 flex-1 bg-white font-mono text-sm"
|
|
265
|
-
placeholder="
|
|
293
|
+
:placeholder="t('integration.workflows.variables.field.newKey.placeholder')"
|
|
266
294
|
@keydown.enter.prevent="addVariable"
|
|
267
295
|
/>
|
|
268
296
|
<Button size="sm" class="h-8" :disabled="!newKey.trim() || !!newKeyError" @click="addVariable">
|
|
269
297
|
<Plus :size="14" class="mr-1" />
|
|
270
|
-
|
|
298
|
+
{{ t("integration.workflows.variables.action.add") }}
|
|
271
299
|
</Button>
|
|
272
300
|
</div>
|
|
273
301
|
<p v-if="newKeyError" class="mt-1 text-xs text-rose-600">{{ newKeyError }}</p>
|
|
274
302
|
</div>
|
|
275
303
|
</div>
|
|
304
|
+
|
|
305
|
+
<!-- Cancel / Done, the same pair every dialog in the editor closes on. Done
|
|
306
|
+
reaches the workflow's unsaved state; only the header's Save reaches the
|
|
307
|
+
platform. -->
|
|
308
|
+
<DialogFooter>
|
|
309
|
+
<Button variant="outline" size="sm" class="h-8" @click="emit('update:open', false)">
|
|
310
|
+
{{ t("integration.common.action.cancel") }}
|
|
311
|
+
</Button>
|
|
312
|
+
<Button size="sm" class="h-8" :disabled="hasJsonError" @click="applyAndClose">
|
|
313
|
+
{{ t("integration.workflows.variables.action.done") }}
|
|
314
|
+
</Button>
|
|
315
|
+
</DialogFooter>
|
|
276
316
|
</DialogContent>
|
|
277
317
|
</Dialog>
|
|
278
318
|
</template>
|