@revenexx/studio-integrations 0.5.1 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +428 -0
- package/README.md +48 -4
- package/dist/module.d.mts +17 -1
- package/dist/module.json +1 -1
- package/dist/module.mjs +10 -2
- package/dist/runtime/components/Integrations/CredentialCreateDialog.vue +43 -41
- package/dist/runtime/components/Integrations/CredentialDetailDialog.vue +123 -50
- package/dist/runtime/components/Integrations/CredentialRevenexxSource.vue +108 -71
- package/dist/runtime/components/Integrations/CredentialTypePicker.vue +12 -15
- package/dist/runtime/components/Integrations/DeadLettersPanel.vue +18 -17
- package/dist/runtime/components/Integrations/EventTypeCombobox.d.vue.ts +1 -0
- package/dist/runtime/components/Integrations/EventTypeCombobox.vue +7 -4
- package/dist/runtime/components/Integrations/EventTypeCombobox.vue.d.ts +1 -0
- package/dist/runtime/components/Integrations/ExpressionInput.d.vue.ts +4 -0
- package/dist/runtime/components/Integrations/ExpressionInput.vue +67 -37
- package/dist/runtime/components/Integrations/ExpressionInput.vue.d.ts +4 -0
- package/dist/runtime/components/Integrations/GroupFrameView.vue +30 -20
- package/dist/runtime/components/Integrations/Layout.vue +48 -4
- package/dist/runtime/components/Integrations/NodeConfigDynamicField.vue +23 -23
- package/dist/runtime/components/Integrations/NodeConfigDynamicSchema.vue +11 -13
- package/dist/runtime/components/Integrations/NodeConfigField.d.vue.ts +2 -0
- package/dist/runtime/components/Integrations/NodeConfigField.vue +187 -93
- package/dist/runtime/components/Integrations/NodeConfigField.vue.d.ts +2 -0
- package/dist/runtime/components/Integrations/NodeInspector.d.vue.ts +6 -0
- package/dist/runtime/components/Integrations/NodeInspector.vue +1157 -376
- package/dist/runtime/components/Integrations/NodeInspector.vue.d.ts +6 -0
- package/dist/runtime/components/Integrations/NodeLogo.d.vue.ts +12 -0
- package/dist/runtime/components/Integrations/NodeLogo.vue +2 -30
- package/dist/runtime/components/Integrations/NodeLogo.vue.d.ts +12 -0
- package/dist/runtime/components/Integrations/NodeMark.d.vue.ts +15 -0
- package/dist/runtime/components/Integrations/NodeMark.vue +79 -0
- package/dist/runtime/components/Integrations/NodeMark.vue.d.ts +15 -0
- package/dist/runtime/components/Integrations/NodeOutputSchema.d.vue.ts +21 -0
- package/dist/runtime/components/Integrations/NodeOutputSchema.vue +43 -0
- package/dist/runtime/components/Integrations/NodeOutputSchema.vue.d.ts +21 -0
- package/dist/runtime/components/Integrations/NodePalette.vue +122 -55
- package/dist/runtime/components/Integrations/QueryBuilder.vue +46 -38
- package/dist/runtime/components/Integrations/RunActionsMenu.vue +38 -12
- package/dist/runtime/components/Integrations/RunDetailPanel.vue +114 -62
- package/dist/runtime/components/Integrations/RunPayloadInput.vue +27 -13
- package/dist/runtime/components/Integrations/RunResponseModal.vue +11 -10
- package/dist/runtime/components/Integrations/RunsTable.d.vue.ts +0 -1
- package/dist/runtime/components/Integrations/RunsTable.vue +38 -16
- package/dist/runtime/components/Integrations/RunsTable.vue.d.ts +0 -1
- package/dist/runtime/components/Integrations/SftpAccountImportDialog.vue +8 -7
- package/dist/runtime/components/Integrations/Sidebar.vue +14 -10
- package/dist/runtime/components/Integrations/StickyNoteView.d.vue.ts +14 -4
- package/dist/runtime/components/Integrations/StickyNoteView.vue +38 -23
- package/dist/runtime/components/Integrations/StickyNoteView.vue.d.ts +14 -4
- package/dist/runtime/components/Integrations/TemplateCard.d.vue.ts +18 -0
- package/dist/runtime/components/Integrations/TemplateCard.vue +119 -0
- package/dist/runtime/components/Integrations/TemplateCard.vue.d.ts +18 -0
- package/dist/runtime/components/Integrations/TemplateFlowPreview.d.vue.ts +2 -2
- package/dist/runtime/components/Integrations/TemplateFlowPreview.vue +16 -18
- package/dist/runtime/components/Integrations/TemplateFlowPreview.vue.d.ts +2 -2
- package/dist/runtime/components/Integrations/WorkflowContextMenu.vue +34 -33
- package/dist/runtime/components/Integrations/WorkflowCreateDialog.vue +52 -67
- package/dist/runtime/components/Integrations/WorkflowDuplicateDialog.vue +8 -7
- package/dist/runtime/components/Integrations/WorkflowEdgeView.d.vue.ts +18 -4
- package/dist/runtime/components/Integrations/WorkflowEdgeView.vue +29 -11
- package/dist/runtime/components/Integrations/WorkflowEdgeView.vue.d.ts +18 -4
- package/dist/runtime/components/Integrations/WorkflowEditor.d.vue.ts +20 -1
- package/dist/runtime/components/Integrations/WorkflowEditor.vue +240 -116
- package/dist/runtime/components/Integrations/WorkflowEditor.vue.d.ts +20 -1
- package/dist/runtime/components/Integrations/WorkflowHistoryPanel.vue +28 -43
- package/dist/runtime/components/Integrations/WorkflowIconPicker.d.vue.ts +19 -1
- package/dist/runtime/components/Integrations/WorkflowIconPicker.vue +17 -7
- package/dist/runtime/components/Integrations/WorkflowIconPicker.vue.d.ts +19 -1
- package/dist/runtime/components/Integrations/WorkflowNodeView.d.vue.ts +4 -0
- package/dist/runtime/components/Integrations/WorkflowNodeView.vue +198 -56
- package/dist/runtime/components/Integrations/WorkflowNodeView.vue.d.ts +4 -0
- package/dist/runtime/components/Integrations/WorkflowRunReplayPanel.vue +56 -59
- package/dist/runtime/components/Integrations/WorkflowSettingsDialog.vue +28 -21
- package/dist/runtime/components/Integrations/WorkflowTagsInput.d.vue.ts +1 -0
- package/dist/runtime/components/Integrations/WorkflowTagsInput.vue +15 -9
- package/dist/runtime/components/Integrations/WorkflowTagsInput.vue.d.ts +1 -0
- package/dist/runtime/components/Integrations/WorkflowTriggerView.vue +15 -10
- package/dist/runtime/components/Integrations/WorkflowVariablesDialog.vue +50 -36
- package/dist/runtime/components/Integrations/WorkflowsTable.vue +126 -48
- package/dist/runtime/composables/integrationsApi.js +4 -2
- package/dist/runtime/composables/useCredentialTypes.js +8 -6
- package/dist/runtime/composables/useIntegrationsAudit.js +4 -3
- package/dist/runtime/composables/useIntegrationsCredentials.js +10 -8
- package/dist/runtime/composables/useIntegrationsDeadLetters.js +7 -6
- package/dist/runtime/composables/useIntegrationsFocus.d.ts +12 -0
- package/dist/runtime/composables/useIntegrationsLocale.d.ts +26 -0
- package/dist/runtime/composables/useIntegrationsLocale.js +8 -0
- package/dist/runtime/composables/useIntegrationsNodes.js +7 -5
- package/dist/runtime/composables/useIntegrationsRealtime.d.ts +149 -0
- package/dist/runtime/composables/useIntegrationsRealtime.js +80 -0
- package/dist/runtime/composables/useIntegrationsRevisions.js +9 -7
- package/dist/runtime/composables/useIntegrationsRuns.d.ts +1 -1
- package/dist/runtime/composables/useIntegrationsRuns.js +14 -7
- package/dist/runtime/composables/useIntegrationsSchedules.d.ts +4 -0
- package/dist/runtime/composables/useIntegrationsSchedules.js +10 -3
- package/dist/runtime/composables/useIntegrationsSecrets.js +14 -9
- package/dist/runtime/composables/useIntegrationsT.d.ts +35 -0
- package/dist/runtime/composables/useIntegrationsT.js +9 -0
- package/dist/runtime/composables/useIntegrationsTemplates.d.ts +3 -3
- package/dist/runtime/composables/useIntegrationsTemplates.js +28 -16
- package/dist/runtime/composables/useIntegrationsTriggers.js +10 -8
- package/dist/runtime/composables/useIntegrationsWorkflows.js +13 -7
- package/dist/runtime/composables/useLoadGate.d.ts +17 -0
- package/dist/runtime/composables/useLoadGate.js +8 -0
- package/dist/runtime/composables/useNodeConfigResolve.js +2 -1
- package/dist/runtime/data/branchReorder.d.ts +37 -0
- package/dist/runtime/data/branchReorder.js +71 -0
- package/dist/runtime/data/handleId.d.ts +23 -0
- package/dist/runtime/data/handleId.js +6 -0
- package/dist/runtime/data/integrationNodeCatalog.d.ts +41 -0
- package/dist/runtime/data/integrationNodeCatalog.js +14 -0
- package/dist/runtime/data/integrationTemplates.d.ts +48 -0
- package/dist/runtime/data/integrationTemplates.js +21 -0
- package/dist/runtime/data/integrationWorkflowSamples.js +1 -1
- package/dist/runtime/data/nodeIcons.d.ts +2 -0
- package/dist/runtime/data/nodeIcons.js +73 -0
- package/dist/runtime/data/nodeInputData.d.ts +16 -4
- package/dist/runtime/data/nodeInputData.js +4 -0
- package/dist/runtime/data/payloadForm.d.ts +9 -2
- package/dist/runtime/data/payloadForm.js +29 -4
- package/dist/runtime/data/portTone.d.ts +17 -0
- package/dist/runtime/data/portTone.js +7 -0
- package/dist/runtime/data/webhookAuth.d.ts +18 -0
- package/dist/runtime/data/webhookAuth.js +19 -0
- package/dist/runtime/data/workflowIconCatalog.js +4 -6
- package/dist/runtime/i18n/messages/de.json +1207 -0
- package/dist/runtime/i18n/messages/en.json +1207 -0
- package/dist/runtime/i18n/translate.d.ts +83 -0
- package/dist/runtime/i18n/translate.js +41 -0
- package/dist/runtime/pages/integrations/audit.vue +140 -37
- package/dist/runtime/pages/integrations/credentials/oauth-complete.vue +9 -6
- package/dist/runtime/pages/integrations/credentials.vue +180 -64
- package/dist/runtime/pages/integrations/index.vue +91 -92
- package/dist/runtime/pages/integrations/runs.vue +33 -21
- package/dist/runtime/pages/integrations/schedules.vue +88 -23
- package/dist/runtime/pages/integrations/secrets.vue +57 -35
- package/dist/runtime/pages/integrations/templates/[slug].vue +119 -88
- package/dist/runtime/pages/integrations/templates/index.vue +82 -103
- package/dist/runtime/pages/integrations/workflows/[id].vue +344 -91
- package/dist/runtime/pages/integrations/workflows/index.vue +23 -5
- package/dist/runtime/plugins/locale.d.ts +10 -0
- package/dist/runtime/plugins/locale.js +6 -0
- package/dist/runtime/plugins/realtime.client.d.ts +32 -0
- package/dist/runtime/plugins/realtime.client.js +4 -0
- package/dist/runtime/types/integrations.d.ts +65 -6
- package/dist/runtime/utils/apiError.d.ts +47 -0
- package/dist/runtime/utils/apiError.js +32 -0
- package/dist/runtime/utils/cockpitSources.d.ts +153 -0
- package/dist/runtime/utils/cockpitSources.js +14 -0
- package/dist/runtime/utils/credentialState.d.ts +24 -1
- package/dist/runtime/utils/credentialState.js +26 -11
- package/dist/runtime/utils/loadGate.d.ts +61 -0
- package/dist/runtime/utils/loadGate.js +22 -0
- package/dist/runtime/utils/localize.d.ts +46 -4
- package/dist/runtime/utils/localize.js +34 -7
- package/dist/runtime/utils/notify.d.ts +59 -0
- package/dist/runtime/utils/notify.js +21 -0
- package/dist/runtime/utils/pollBudget.d.ts +72 -0
- package/dist/runtime/utils/pollBudget.js +17 -0
- package/dist/runtime/utils/revenexxCredentialSource.d.ts +26 -0
- package/dist/runtime/utils/revenexxCredentialSource.js +8 -0
- package/dist/runtime/utils/schedule.d.ts +116 -0
- package/dist/runtime/utils/schedule.js +219 -0
- package/dist/runtime/utils/statusStyle.d.ts +26 -2
- package/dist/runtime/utils/statusStyle.js +9 -1
- package/dist/runtime/utils/validationFindings.d.ts +101 -0
- package/dist/runtime/utils/validationFindings.js +54 -0
- package/dist/runtime/utils/whenIdle.d.ts +42 -0
- package/dist/runtime/utils/whenIdle.js +12 -0
- package/dist/types.d.mts +2 -6
- package/package.json +10 -3
|
@@ -2,23 +2,18 @@
|
|
|
2
2
|
import { computed, ref, watch } from "vue";
|
|
3
3
|
import * as Lucide from "lucide-vue-next";
|
|
4
4
|
import { INTEGRATION_NODE_CATALOG, NODE_CATEGORIES, TRIGGER_TEMPLATES } from "../../data/integrationNodeCatalog";
|
|
5
|
+
import { nodeIcon } from "../../data/nodeIcons";
|
|
6
|
+
import { resolveLocalized } from "../../utils/localize";
|
|
5
7
|
const { load: loadNodes } = useIntegrationsNodes();
|
|
6
8
|
onMounted(() => {
|
|
7
9
|
void loadNodes();
|
|
8
10
|
});
|
|
11
|
+
const t = useIntegrationsT();
|
|
9
12
|
const emit = defineEmits(["insert"]);
|
|
10
13
|
const search = ref("");
|
|
11
14
|
const searchInput = ref(null);
|
|
12
15
|
defineExpose({ focusSearch: () => searchInput.value?.focus() });
|
|
13
|
-
|
|
14
|
-
if (!value) return "";
|
|
15
|
-
return typeof value === "string" ? value : value.en ?? Object.values(value)[0] ?? "";
|
|
16
|
-
}
|
|
17
|
-
function iconFor(name) {
|
|
18
|
-
if (!name) return Lucide.Box;
|
|
19
|
-
const cleaned = (name.includes(":") ? name.split(":").pop() : name).split("-").map((s) => s.charAt(0).toUpperCase() + s.slice(1)).join("");
|
|
20
|
-
return Lucide[cleaned] || Lucide.Box;
|
|
21
|
-
}
|
|
16
|
+
const iconFor = nodeIcon;
|
|
22
17
|
const CORE_GROUP_BY_CATEGORY = {
|
|
23
18
|
control: "Flow",
|
|
24
19
|
transform: "Data",
|
|
@@ -61,7 +56,7 @@ function hasLogo(m) {
|
|
|
61
56
|
}
|
|
62
57
|
function groupPath(m) {
|
|
63
58
|
if (m.groups?.length) {
|
|
64
|
-
const path = m.groups.map((g) =>
|
|
59
|
+
const path = m.groups.map((g) => resolveLocalized(g)).filter(Boolean);
|
|
65
60
|
if (isIntegration(m) || !path.length) return path;
|
|
66
61
|
return [CORE_LABEL_ALIAS[path[0]] ?? path[0], ...path.slice(1)];
|
|
67
62
|
}
|
|
@@ -91,6 +86,31 @@ const latestManifests = computed(() => {
|
|
|
91
86
|
}
|
|
92
87
|
return out;
|
|
93
88
|
});
|
|
89
|
+
const versionsBySlug = computed(() => {
|
|
90
|
+
const map = /* @__PURE__ */ new Map();
|
|
91
|
+
for (const m of INTEGRATION_NODE_CATALOG) {
|
|
92
|
+
const list = map.get(m.slug);
|
|
93
|
+
if (list) list.push(m);
|
|
94
|
+
else map.set(m.slug, [m]);
|
|
95
|
+
}
|
|
96
|
+
return map;
|
|
97
|
+
});
|
|
98
|
+
function versionsOf(slug) {
|
|
99
|
+
return versionsBySlug.value.get(slug) ?? [];
|
|
100
|
+
}
|
|
101
|
+
const selectedVersion = ref({});
|
|
102
|
+
function setSelectedVersion(slug, version) {
|
|
103
|
+
selectedVersion.value = { ...selectedVersion.value, [slug]: version };
|
|
104
|
+
}
|
|
105
|
+
function effVersion(m) {
|
|
106
|
+
const sel = selectedVersion.value[m.slug];
|
|
107
|
+
return sel && versionsOf(m.slug).some((v) => v.version === sel) ? sel : m.version;
|
|
108
|
+
}
|
|
109
|
+
function effManifest(m) {
|
|
110
|
+
const v = effVersion(m);
|
|
111
|
+
if (v === m.version) return m;
|
|
112
|
+
return versionsOf(m.slug).find((x) => x.version === v) ?? m;
|
|
113
|
+
}
|
|
94
114
|
const tree = computed(() => {
|
|
95
115
|
const apps = makeGroup("Integrations");
|
|
96
116
|
const core = makeGroup("Core");
|
|
@@ -141,8 +161,8 @@ const currentGroup = computed(() => {
|
|
|
141
161
|
return cursor ?? null;
|
|
142
162
|
});
|
|
143
163
|
const levelTitle = computed(() => {
|
|
144
|
-
if (triggersLevel.value) return "
|
|
145
|
-
return currentGroup.value?.label ?? "
|
|
164
|
+
if (triggersLevel.value) return t("integration.canvas.palette.group.triggers");
|
|
165
|
+
return currentGroup.value?.label ?? t("integration.canvas.palette.title.addNode");
|
|
146
166
|
});
|
|
147
167
|
function pushPath(path) {
|
|
148
168
|
direction.value = "push";
|
|
@@ -166,7 +186,7 @@ const searchHits = computed(() => {
|
|
|
166
186
|
const scope = currentGroup.value ? subtreeManifests(currentGroup.value) : latestManifests.value.filter((m) => m.category !== "trigger");
|
|
167
187
|
const hits = [];
|
|
168
188
|
for (const m of scope) {
|
|
169
|
-
const hay = `${
|
|
189
|
+
const hay = `${resolveLocalized(m.name)} ${m.slug} ${resolveLocalized(m.description)}`.toLowerCase();
|
|
170
190
|
if (hay.includes(q)) hits.push({ manifest: m, path: groupPath(m) });
|
|
171
191
|
if (hits.length >= 50) break;
|
|
172
192
|
}
|
|
@@ -175,7 +195,7 @@ const searchHits = computed(() => {
|
|
|
175
195
|
const matchedTriggers = computed(() => {
|
|
176
196
|
const q = search.value.trim().toLowerCase();
|
|
177
197
|
if (!q || currentGroup.value) return [];
|
|
178
|
-
const matches = (
|
|
198
|
+
const matches = (tpl) => resolveLocalized(tpl.name).toLowerCase().includes(q) || tpl.type.includes(q) || resolveLocalized(tpl.description).toLowerCase().includes(q);
|
|
179
199
|
return TRIGGER_TEMPLATES.filter(matches);
|
|
180
200
|
});
|
|
181
201
|
const nodeDrag = useNodeDrag();
|
|
@@ -205,24 +225,31 @@ function logoSrcOf(m) {
|
|
|
205
225
|
const imgs = m.images ?? [];
|
|
206
226
|
return (imgs.find((i) => i.category === "logo") ?? imgs[0])?.src ?? null;
|
|
207
227
|
}
|
|
208
|
-
|
|
228
|
+
const paletteDragEnabled = ref(true);
|
|
229
|
+
function onVersionSelectLeave(ev) {
|
|
230
|
+
const el = ev.currentTarget;
|
|
231
|
+
if (el && el !== el.ownerDocument.activeElement) paletteDragEnabled.value = true;
|
|
232
|
+
}
|
|
233
|
+
function onManifestDragStart(leaf, ev) {
|
|
209
234
|
if (!ev.dataTransfer) return;
|
|
235
|
+
const m = effManifest(leaf);
|
|
210
236
|
ev.dataTransfer.setData("application/integration-node", JSON.stringify({ kind: "manifest", slug: m.slug, version: m.version }));
|
|
211
237
|
ev.dataTransfer.effectAllowed = "move";
|
|
212
|
-
setCustomDragImage(ev,
|
|
238
|
+
setCustomDragImage(ev, resolveLocalized(m.name), logoSrcOf(m), "#8b5cf6");
|
|
213
239
|
nodeDrag.start({ kind: "manifest", manifest: m });
|
|
214
240
|
}
|
|
215
241
|
function onTriggerDragStart(tpl, ev) {
|
|
216
242
|
if (!ev.dataTransfer) return;
|
|
217
243
|
ev.dataTransfer.setData("application/integration-node", JSON.stringify({ kind: "trigger", type: tpl.type }));
|
|
218
244
|
ev.dataTransfer.effectAllowed = "move";
|
|
219
|
-
setCustomDragImage(ev,
|
|
245
|
+
setCustomDragImage(ev, resolveLocalized(tpl.name), null, "#10b981");
|
|
220
246
|
nodeDrag.start({ kind: "trigger", tpl });
|
|
221
247
|
}
|
|
222
248
|
function onDragEnd() {
|
|
223
249
|
nodeDrag.end();
|
|
224
250
|
}
|
|
225
|
-
function insertManifest(
|
|
251
|
+
function insertManifest(leaf) {
|
|
252
|
+
const m = effManifest(leaf);
|
|
226
253
|
emit("insert", { kind: "manifest", slug: m.slug, version: m.version });
|
|
227
254
|
}
|
|
228
255
|
function insertTrigger(tpl) {
|
|
@@ -234,6 +261,9 @@ function categoryColor(label) {
|
|
|
234
261
|
const cat = NODE_CATEGORIES.find((c) => c.label === label || CORE_GROUP_BY_CATEGORY[String(c.key)] === label);
|
|
235
262
|
return cat?.color ?? "bg-slate-100 text-slate-500";
|
|
236
263
|
}
|
|
264
|
+
function nodeCategoryColor(m) {
|
|
265
|
+
return NODE_CATEGORIES.find((c) => c.key === m.category)?.color ?? "bg-slate-100 text-slate-500";
|
|
266
|
+
}
|
|
237
267
|
watch(tree, () => {
|
|
238
268
|
if (stack.value.length && !triggersLevel.value && !currentGroup.value) stack.value = [];
|
|
239
269
|
});
|
|
@@ -249,7 +279,7 @@ const searching = computed(() => search.value.trim().length > 0);
|
|
|
249
279
|
<button
|
|
250
280
|
type="button"
|
|
251
281
|
class="flex size-6 shrink-0 items-center justify-center rounded-md text-muted-foreground transition hover:bg-muted/50 hover:text-foreground"
|
|
252
|
-
title="
|
|
282
|
+
:title="t('integration.canvas.palette.action.back')"
|
|
253
283
|
@click="popLevel"
|
|
254
284
|
>
|
|
255
285
|
<Lucide.ArrowLeft :size="14" />
|
|
@@ -264,7 +294,8 @@ const searching = computed(() => search.value.trim().length > 0);
|
|
|
264
294
|
ref="searchInput"
|
|
265
295
|
v-model="search"
|
|
266
296
|
type="text"
|
|
267
|
-
:
|
|
297
|
+
:aria-label="stack.length ? t('integration.canvas.palette.search.ariaGroup', { group: levelTitle }) : t('integration.canvas.palette.search.ariaAll')"
|
|
298
|
+
:placeholder="stack.length ? t('integration.canvas.palette.search.placeholderGroup', { group: levelTitle }) : t('integration.canvas.palette.search.placeholderAll')"
|
|
268
299
|
class="h-7 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"
|
|
269
300
|
@keydown.esc="search ? search = '' : stack.length && popLevel()"
|
|
270
301
|
>
|
|
@@ -291,45 +322,63 @@ const searching = computed(() => search.value.trim().length > 0);
|
|
|
291
322
|
<!-- ── Search results (flat, path as subtitle) ─────────────────── -->
|
|
292
323
|
<template v-if="searching">
|
|
293
324
|
<div v-if="matchedTriggers.length" class="mb-1">
|
|
294
|
-
<div class="px-2 py-1 text-mono text-muted-foreground">
|
|
325
|
+
<div class="px-2 py-1 text-mono text-muted-foreground">{{ t("integration.canvas.palette.group.triggers") }}</div>
|
|
295
326
|
<div
|
|
296
|
-
v-for="
|
|
297
|
-
:key="
|
|
327
|
+
v-for="tpl in matchedTriggers"
|
|
328
|
+
:key="tpl.id"
|
|
298
329
|
class="group flex cursor-grab items-center gap-2 rounded-md border border-transparent px-2 py-1.5 transition hover:border-black/[0.08] hover:bg-muted/40 active:cursor-grabbing"
|
|
299
330
|
draggable="true"
|
|
300
|
-
@dragstart="onTriggerDragStart(
|
|
331
|
+
@dragstart="onTriggerDragStart(tpl, $event)"
|
|
301
332
|
@dragend="onDragEnd"
|
|
302
|
-
@click="insertTrigger(
|
|
333
|
+
@click="insertTrigger(tpl)"
|
|
303
334
|
>
|
|
304
335
|
<span class="flex size-6 shrink-0 items-center justify-center rounded-md bg-emerald-100 text-emerald-600">
|
|
305
|
-
<component :is="iconFor(
|
|
336
|
+
<component :is="iconFor(tpl.icon)" :size="13" />
|
|
306
337
|
</span>
|
|
307
338
|
<div class="min-w-0">
|
|
308
|
-
<div class="truncate text-sm font-medium">{{
|
|
309
|
-
<div class="text-xs leading-snug text-muted-foreground">
|
|
339
|
+
<div class="truncate text-sm font-medium">{{ resolveLocalized(tpl.name) }}</div>
|
|
340
|
+
<div class="text-xs leading-snug text-muted-foreground">{{ t("integration.canvas.badge.trigger") }}</div>
|
|
310
341
|
</div>
|
|
311
342
|
</div>
|
|
312
343
|
</div>
|
|
313
344
|
|
|
314
345
|
<p v-if="!searchHits.length && !matchedTriggers.length" class="px-2 py-6 text-center text-sm text-muted-foreground">
|
|
315
|
-
|
|
346
|
+
{{ t("integration.canvas.palette.search.empty", { query: search }) }}
|
|
316
347
|
</p>
|
|
317
348
|
<div
|
|
318
349
|
v-for="hit in searchHits"
|
|
319
350
|
:key="`${hit.manifest.slug}@${hit.manifest.version}`"
|
|
320
351
|
class="group flex cursor-grab items-start gap-2 rounded-md border border-transparent px-2 py-1.5 transition hover:border-black/[0.08] hover:bg-muted/40 active:cursor-grabbing"
|
|
321
|
-
draggable="
|
|
322
|
-
:title="
|
|
352
|
+
:draggable="paletteDragEnabled"
|
|
353
|
+
:title="resolveLocalized(hit.manifest.name)"
|
|
323
354
|
@dragstart="onManifestDragStart(hit.manifest, $event)"
|
|
324
355
|
@dragend="onDragEnd"
|
|
325
356
|
@click="insertManifest(hit.manifest)"
|
|
326
357
|
>
|
|
327
|
-
<span class="flex size-
|
|
328
|
-
<
|
|
358
|
+
<span class="flex size-8 shrink-0 items-center justify-center rounded-md" :class="nodeCategoryColor(hit.manifest)">
|
|
359
|
+
<IntegrationsNodeMark :manifest="hit.manifest" :size="18" :badge="false" />
|
|
329
360
|
</span>
|
|
330
361
|
<div class="min-w-0 flex-1">
|
|
331
|
-
<div class="
|
|
332
|
-
|
|
362
|
+
<div class="flex items-center gap-1.5">
|
|
363
|
+
<span class="min-w-0 flex-1 truncate text-sm font-medium">{{ resolveLocalized(hit.manifest.name) }}</span>
|
|
364
|
+
<select
|
|
365
|
+
v-if="versionsOf(hit.manifest.slug).length > 1"
|
|
366
|
+
:value="effVersion(hit.manifest)"
|
|
367
|
+
class="shrink-0 rounded border border-black/10 bg-white px-1 py-px text-xs tabular-nums text-muted-foreground"
|
|
368
|
+
:aria-label="t('integration.canvas.palette.version.selectAria', { name: resolveLocalized(hit.manifest.name) })"
|
|
369
|
+
@click.stop
|
|
370
|
+
@mousedown.stop
|
|
371
|
+
@change.stop="setSelectedVersion(hit.manifest.slug, $event.target.value)"
|
|
372
|
+
@mouseenter="paletteDragEnabled = false"
|
|
373
|
+
@mouseleave="onVersionSelectLeave"
|
|
374
|
+
@focus="paletteDragEnabled = false"
|
|
375
|
+
@blur="paletteDragEnabled = true"
|
|
376
|
+
>
|
|
377
|
+
<option v-for="v in versionsOf(hit.manifest.slug)" :key="v.version" :value="v.version">v{{ v.version }}</option>
|
|
378
|
+
</select>
|
|
379
|
+
<span v-else class="shrink-0 text-xs tabular-nums text-muted-foreground/60">v{{ hit.manifest.version }}</span>
|
|
380
|
+
</div>
|
|
381
|
+
<div class="text-xs leading-snug text-muted-foreground">{{ resolveLocalized(hit.manifest.description) }}</div>
|
|
333
382
|
<div class="truncate text-xs text-muted-foreground/70">{{ hit.path.join(" \u203A ") }}</div>
|
|
334
383
|
</div>
|
|
335
384
|
</div>
|
|
@@ -338,21 +387,21 @@ const searching = computed(() => search.value.trim().length > 0);
|
|
|
338
387
|
<!-- ── Triggers level ──────────────────────────────────────────── -->
|
|
339
388
|
<template v-else-if="triggersLevel">
|
|
340
389
|
<div
|
|
341
|
-
v-for="
|
|
342
|
-
:key="
|
|
390
|
+
v-for="tpl in TRIGGER_TEMPLATES"
|
|
391
|
+
:key="tpl.id"
|
|
343
392
|
class="group flex cursor-grab items-center gap-2 rounded-md border border-transparent px-2 py-1.5 transition hover:border-black/[0.08] hover:bg-muted/40 active:cursor-grabbing"
|
|
344
393
|
draggable="true"
|
|
345
|
-
:title="
|
|
346
|
-
@dragstart="onTriggerDragStart(
|
|
394
|
+
:title="resolveLocalized(tpl.name)"
|
|
395
|
+
@dragstart="onTriggerDragStart(tpl, $event)"
|
|
347
396
|
@dragend="onDragEnd"
|
|
348
|
-
@click="insertTrigger(
|
|
397
|
+
@click="insertTrigger(tpl)"
|
|
349
398
|
>
|
|
350
399
|
<span class="flex size-6 shrink-0 items-center justify-center rounded-md bg-emerald-100 text-emerald-600">
|
|
351
|
-
<component :is="iconFor(
|
|
400
|
+
<component :is="iconFor(tpl.icon)" :size="13" />
|
|
352
401
|
</span>
|
|
353
402
|
<div class="min-w-0">
|
|
354
|
-
<div class="truncate text-sm font-medium">{{
|
|
355
|
-
<div class="text-xs leading-snug text-muted-foreground">{{
|
|
403
|
+
<div class="truncate text-sm font-medium">{{ resolveLocalized(tpl.name) }}</div>
|
|
404
|
+
<div class="text-xs leading-snug text-muted-foreground">{{ resolveLocalized(tpl.description) }}</div>
|
|
356
405
|
</div>
|
|
357
406
|
</div>
|
|
358
407
|
</template>
|
|
@@ -361,7 +410,7 @@ const searching = computed(() => search.value.trim().length > 0);
|
|
|
361
410
|
<template v-else-if="!currentGroup">
|
|
362
411
|
<!-- Triggers group on top -->
|
|
363
412
|
<div v-if="tree.core.children.size">
|
|
364
|
-
<div class="px-2 py-1 text-mono text-muted-foreground">
|
|
413
|
+
<div class="px-2 py-1 text-mono text-muted-foreground">{{ t("integration.canvas.palette.group.core") }}</div>
|
|
365
414
|
<button
|
|
366
415
|
type="button"
|
|
367
416
|
class="flex w-full items-center gap-2 rounded-md border border-transparent px-2 py-1.5 text-left transition hover:border-black/[0.08] hover:bg-muted/40"
|
|
@@ -370,7 +419,7 @@ const searching = computed(() => search.value.trim().length > 0);
|
|
|
370
419
|
<span class="flex size-6 shrink-0 items-center justify-center rounded-md bg-slate-100 text-emerald-600/80">
|
|
371
420
|
<Lucide.Zap :size="13" />
|
|
372
421
|
</span>
|
|
373
|
-
<span class="min-w-0 flex-1 truncate text-sm font-medium">
|
|
422
|
+
<span class="min-w-0 flex-1 truncate text-sm font-medium">{{ t("integration.canvas.palette.group.triggers") }}</span>
|
|
374
423
|
<span class="text-sm tabular-nums text-muted-foreground">{{ TRIGGER_TEMPLATES.length }}</span>
|
|
375
424
|
<Lucide.ChevronRight :size="13" class="text-muted-foreground" />
|
|
376
425
|
</button>
|
|
@@ -391,7 +440,7 @@ const searching = computed(() => search.value.trim().length > 0);
|
|
|
391
440
|
</div>
|
|
392
441
|
|
|
393
442
|
<div v-if="tree.apps.children.size" class="mt-2">
|
|
394
|
-
<div class="px-2 py-1 text-mono text-muted-foreground">
|
|
443
|
+
<div class="px-2 py-1 text-mono text-muted-foreground">{{ t("integration.canvas.palette.group.integrations") }}</div>
|
|
395
444
|
<button
|
|
396
445
|
v-for="[label, group] in appRows"
|
|
397
446
|
:key="`app-${label}`"
|
|
@@ -400,7 +449,7 @@ const searching = computed(() => search.value.trim().length > 0);
|
|
|
400
449
|
@click="pushPath(['__apps', label])"
|
|
401
450
|
>
|
|
402
451
|
<span class="flex size-6 shrink-0 items-center justify-center rounded-md bg-white ring-1 ring-black/[0.06]">
|
|
403
|
-
<
|
|
452
|
+
<IntegrationsNodeMark v-if="groupLogoManifest(group)" :manifest="groupLogoManifest(group)" :size="14" logo-only />
|
|
404
453
|
<Lucide.Package v-else :size="13" class="text-violet-600" />
|
|
405
454
|
</span>
|
|
406
455
|
<span class="min-w-0 flex-1 truncate text-sm font-medium">{{ label }}</span>
|
|
@@ -423,7 +472,7 @@ const searching = computed(() => search.value.trim().length > 0);
|
|
|
423
472
|
class="flex size-6 shrink-0 items-center justify-center rounded-md"
|
|
424
473
|
:class="groupLogoManifest(group) ? 'bg-white ring-1 ring-black/[0.06]' : 'bg-slate-100 text-slate-500'"
|
|
425
474
|
>
|
|
426
|
-
<
|
|
475
|
+
<IntegrationsNodeMark v-if="groupLogoManifest(group)" :manifest="groupLogoManifest(group)" :size="14" logo-only />
|
|
427
476
|
<Lucide.FolderOpen v-else :size="13" />
|
|
428
477
|
</span>
|
|
429
478
|
<span class="min-w-0 flex-1 truncate text-sm font-medium">{{ label }}</span>
|
|
@@ -435,18 +484,36 @@ const searching = computed(() => search.value.trim().length > 0);
|
|
|
435
484
|
v-for="m in currentGroup.leaves"
|
|
436
485
|
:key="`${m.slug}@${m.version}`"
|
|
437
486
|
class="group flex cursor-grab items-start gap-2 rounded-md border border-transparent px-2 py-1.5 transition hover:border-black/[0.08] hover:bg-muted/40 active:cursor-grabbing"
|
|
438
|
-
draggable="
|
|
439
|
-
:title="
|
|
487
|
+
:draggable="paletteDragEnabled"
|
|
488
|
+
:title="resolveLocalized(m.name)"
|
|
440
489
|
@dragstart="onManifestDragStart(m, $event)"
|
|
441
490
|
@dragend="onDragEnd"
|
|
442
491
|
@click="insertManifest(m)"
|
|
443
492
|
>
|
|
444
|
-
<span class="flex size-6 shrink-0 items-center justify-center rounded-md" :class="
|
|
445
|
-
<
|
|
493
|
+
<span class="flex size-6 shrink-0 items-center justify-center rounded-md" :class="nodeCategoryColor(m)">
|
|
494
|
+
<IntegrationsNodeMark :manifest="m" :size="14" :badge="false" />
|
|
446
495
|
</span>
|
|
447
496
|
<div class="min-w-0 flex-1">
|
|
448
|
-
<div class="
|
|
449
|
-
|
|
497
|
+
<div class="flex items-center gap-1.5">
|
|
498
|
+
<span class="min-w-0 flex-1 truncate text-sm font-medium">{{ resolveLocalized(m.name) }}</span>
|
|
499
|
+
<select
|
|
500
|
+
v-if="versionsOf(m.slug).length > 1"
|
|
501
|
+
:value="effVersion(m)"
|
|
502
|
+
class="shrink-0 rounded border border-black/10 bg-white px-1 py-px text-xs tabular-nums text-muted-foreground"
|
|
503
|
+
:aria-label="t('integration.canvas.palette.version.selectAria', { name: resolveLocalized(m.name) })"
|
|
504
|
+
@click.stop
|
|
505
|
+
@mousedown.stop
|
|
506
|
+
@change.stop="setSelectedVersion(m.slug, $event.target.value)"
|
|
507
|
+
@mouseenter="paletteDragEnabled = false"
|
|
508
|
+
@mouseleave="onVersionSelectLeave"
|
|
509
|
+
@focus="paletteDragEnabled = false"
|
|
510
|
+
@blur="paletteDragEnabled = true"
|
|
511
|
+
>
|
|
512
|
+
<option v-for="v in versionsOf(m.slug)" :key="v.version" :value="v.version">v{{ v.version }}</option>
|
|
513
|
+
</select>
|
|
514
|
+
<span v-else class="shrink-0 text-xs tabular-nums text-muted-foreground/60">v{{ m.version }}</span>
|
|
515
|
+
</div>
|
|
516
|
+
<div class="text-xs leading-snug text-muted-foreground">{{ resolveLocalized(m.description) }}</div>
|
|
450
517
|
</div>
|
|
451
518
|
</div>
|
|
452
519
|
</template>
|
|
@@ -455,7 +522,7 @@ const searching = computed(() => search.value.trim().length > 0);
|
|
|
455
522
|
</div>
|
|
456
523
|
|
|
457
524
|
<div class="border-t border-black/[0.06] px-3 py-2 text-sm text-muted-foreground">
|
|
458
|
-
|
|
525
|
+
{{ t("integration.canvas.palette.footer.hint") }}
|
|
459
526
|
</div>
|
|
460
527
|
</aside>
|
|
461
528
|
</template>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<script setup>
|
|
2
2
|
import { computed, ref, watch } from "vue";
|
|
3
|
-
import { AddCircle as Plus, TrashBinMinimalistic as
|
|
3
|
+
import { AddCircle as Plus, TrashBinMinimalistic as TrashBin } from "#solar-icons";
|
|
4
4
|
import {
|
|
5
5
|
Button,
|
|
6
6
|
Input,
|
|
@@ -15,27 +15,28 @@ const props = defineProps({
|
|
|
15
15
|
modelValue: { type: null, required: true }
|
|
16
16
|
});
|
|
17
17
|
const emit = defineEmits(["update:modelValue"]);
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
{ value: "
|
|
21
|
-
{ value: "
|
|
22
|
-
{ value: "
|
|
23
|
-
{ value: "
|
|
24
|
-
{ value: "
|
|
25
|
-
{ value: "
|
|
26
|
-
{ value: "
|
|
27
|
-
{ value: "
|
|
28
|
-
{ value: "
|
|
29
|
-
{ value: "
|
|
30
|
-
{ value: "
|
|
31
|
-
{ value: "
|
|
32
|
-
{ value: "
|
|
33
|
-
{ value: "
|
|
34
|
-
{ value: "
|
|
35
|
-
{ value: "
|
|
36
|
-
|
|
18
|
+
const t = useIntegrationsT();
|
|
19
|
+
const METHODS = computed(() => [
|
|
20
|
+
{ value: "equal", label: t("integration.nodeConfig.queryBuilder.method.equal"), attribute: true, values: "list" },
|
|
21
|
+
{ value: "notEqual", label: t("integration.nodeConfig.queryBuilder.method.notEqual"), attribute: true, values: "list" },
|
|
22
|
+
{ value: "contains", label: t("integration.nodeConfig.queryBuilder.method.contains"), attribute: true, values: "list" },
|
|
23
|
+
{ value: "search", label: t("integration.nodeConfig.queryBuilder.method.search"), attribute: true, values: "single" },
|
|
24
|
+
{ value: "startsWith", label: t("integration.nodeConfig.queryBuilder.method.startsWith"), attribute: true, values: "single" },
|
|
25
|
+
{ value: "endsWith", label: t("integration.nodeConfig.queryBuilder.method.endsWith"), attribute: true, values: "single" },
|
|
26
|
+
{ value: "lessThan", label: t("integration.nodeConfig.queryBuilder.method.lessThan"), attribute: true, values: "single" },
|
|
27
|
+
{ value: "lessThanEqual", label: t("integration.nodeConfig.queryBuilder.method.lessThanEqual"), attribute: true, values: "single" },
|
|
28
|
+
{ value: "greaterThan", label: t("integration.nodeConfig.queryBuilder.method.greaterThan"), attribute: true, values: "single" },
|
|
29
|
+
{ value: "greaterThanEqual", label: t("integration.nodeConfig.queryBuilder.method.greaterThanEqual"), attribute: true, values: "single" },
|
|
30
|
+
{ value: "between", label: t("integration.nodeConfig.queryBuilder.method.between"), attribute: true, values: "list" },
|
|
31
|
+
{ value: "isNull", label: t("integration.nodeConfig.queryBuilder.method.isNull"), attribute: true, values: "none" },
|
|
32
|
+
{ value: "isNotNull", label: t("integration.nodeConfig.queryBuilder.method.isNotNull"), attribute: true, values: "none" },
|
|
33
|
+
{ value: "orderAsc", label: t("integration.nodeConfig.queryBuilder.method.orderAsc"), attribute: true, values: "none" },
|
|
34
|
+
{ value: "orderDesc", label: t("integration.nodeConfig.queryBuilder.method.orderDesc"), attribute: true, values: "none" },
|
|
35
|
+
{ value: "limit", label: t("integration.nodeConfig.queryBuilder.method.limit"), attribute: false, values: "single" },
|
|
36
|
+
{ value: "offset", label: t("integration.nodeConfig.queryBuilder.method.offset"), attribute: false, values: "single" }
|
|
37
|
+
]);
|
|
37
38
|
function methodMeta(method) {
|
|
38
|
-
return METHODS.find((m) => m.value === method) ?? null;
|
|
39
|
+
return METHODS.value.find((m) => m.value === method) ?? null;
|
|
39
40
|
}
|
|
40
41
|
function formatValue(v) {
|
|
41
42
|
return typeof v === "string" ? v : JSON.stringify(v);
|
|
@@ -57,16 +58,16 @@ function parseRow(str) {
|
|
|
57
58
|
return { kind: "raw", text: str };
|
|
58
59
|
}
|
|
59
60
|
function coerceValue(part) {
|
|
60
|
-
const
|
|
61
|
-
if (/^-?\d+(\.\d+)?$/.test(
|
|
62
|
-
if (
|
|
63
|
-
if (
|
|
64
|
-
return
|
|
61
|
+
const t2 = part.trim();
|
|
62
|
+
if (/^-?\d+(\.\d+)?$/.test(t2)) return Number(t2);
|
|
63
|
+
if (t2 === "true") return true;
|
|
64
|
+
if (t2 === "false") return false;
|
|
65
|
+
return t2;
|
|
65
66
|
}
|
|
66
67
|
function rowToString(row) {
|
|
67
68
|
if (row.kind === "raw") {
|
|
68
|
-
const
|
|
69
|
-
return
|
|
69
|
+
const t2 = row.text.trim();
|
|
70
|
+
return t2 === "" ? null : t2;
|
|
70
71
|
}
|
|
71
72
|
const meta = methodMeta(row.method);
|
|
72
73
|
if (!meta) return null;
|
|
@@ -131,13 +132,13 @@ function writeJson(text) {
|
|
|
131
132
|
try {
|
|
132
133
|
const parsed = JSON.parse(text);
|
|
133
134
|
if (!Array.isArray(parsed)) {
|
|
134
|
-
jsonError.value = "
|
|
135
|
+
jsonError.value = t("integration.nodeConfig.queryBuilder.json.notArray");
|
|
135
136
|
return;
|
|
136
137
|
}
|
|
137
138
|
jsonError.value = null;
|
|
138
139
|
emit("update:modelValue", parsed.map(String));
|
|
139
140
|
} catch {
|
|
140
|
-
jsonError.value = "
|
|
141
|
+
jsonError.value = t("integration.nodeConfig.json.invalid");
|
|
141
142
|
}
|
|
142
143
|
}
|
|
143
144
|
const rowMeta = computed(() => rows.value.map((r) => r.kind === "query" ? methodMeta(r.method) : null));
|
|
@@ -153,7 +154,7 @@ const rowMeta = computed(() => rows.value.map((r) => r.kind === "query" ? method
|
|
|
153
154
|
:class="mode === 'builder' ? 'bg-slate-100 text-foreground' : 'text-muted-foreground hover:text-foreground'"
|
|
154
155
|
@click="mode = 'builder'"
|
|
155
156
|
>
|
|
156
|
-
|
|
157
|
+
{{ t("integration.nodeConfig.queryBuilder.mode.builder") }}
|
|
157
158
|
</button>
|
|
158
159
|
<button
|
|
159
160
|
type="button"
|
|
@@ -161,7 +162,7 @@ const rowMeta = computed(() => rows.value.map((r) => r.kind === "query" ? method
|
|
|
161
162
|
:class="mode === 'json' ? 'bg-slate-100 text-foreground' : 'text-muted-foreground hover:text-foreground'"
|
|
162
163
|
@click="mode = 'json'"
|
|
163
164
|
>
|
|
164
|
-
|
|
165
|
+
{{ t("integration.nodeConfig.object.toggle.json") }}
|
|
165
166
|
</button>
|
|
166
167
|
</div>
|
|
167
168
|
</div>
|
|
@@ -173,7 +174,9 @@ const rowMeta = computed(() => rows.value.map((r) => r.kind === "query" ? method
|
|
|
173
174
|
:model-value="row.method"
|
|
174
175
|
@update:model-value="(v) => setRow(idx, { method: String(v) })"
|
|
175
176
|
>
|
|
176
|
-
|
|
177
|
+
<!-- A repeating row has no room for visible labels, so each
|
|
178
|
+
control names itself and says which row it is. -->
|
|
179
|
+
<SelectTrigger :aria-label="t('integration.nodeConfig.queryBuilder.row.comparisonAria', { n: idx + 1 })" class="h-7 w-40 shrink-0 bg-white text-sm">
|
|
177
180
|
<SelectValue />
|
|
178
181
|
</SelectTrigger>
|
|
179
182
|
<SelectContent>
|
|
@@ -184,15 +187,17 @@ const rowMeta = computed(() => rows.value.map((r) => r.kind === "query" ? method
|
|
|
184
187
|
</Select>
|
|
185
188
|
<Input
|
|
186
189
|
v-if="rowMeta[idx]?.attribute"
|
|
190
|
+
:aria-label="t('integration.nodeConfig.queryBuilder.row.fieldAria', { n: idx + 1 })"
|
|
187
191
|
:model-value="row.attribute"
|
|
188
|
-
placeholder="
|
|
192
|
+
:placeholder="t('integration.nodeConfig.queryBuilder.row.fieldPlaceholder')"
|
|
189
193
|
class="h-7 w-36 shrink-0 bg-white font-mono text-sm"
|
|
190
194
|
@update:model-value="(v) => setRow(idx, { attribute: String(v) })"
|
|
191
195
|
/>
|
|
192
196
|
<Input
|
|
193
197
|
v-if="rowMeta[idx]?.values !== 'none'"
|
|
198
|
+
:aria-label="t('integration.nodeConfig.queryBuilder.row.valueAria', { n: idx + 1 })"
|
|
194
199
|
:model-value="row.values"
|
|
195
|
-
:placeholder="rowMeta[idx]?.values === 'list' ? '
|
|
200
|
+
:placeholder="rowMeta[idx]?.values === 'list' ? t('integration.nodeConfig.queryBuilder.row.valueListPlaceholder') : t('integration.nodeConfig.queryBuilder.row.valuePlaceholder')"
|
|
196
201
|
class="h-7 min-w-0 flex-1 bg-white text-sm"
|
|
197
202
|
@update:model-value="(v) => setRow(idx, { values: String(v) })"
|
|
198
203
|
/>
|
|
@@ -200,17 +205,19 @@ const rowMeta = computed(() => rows.value.map((r) => r.kind === "query" ? method
|
|
|
200
205
|
</template>
|
|
201
206
|
<Input
|
|
202
207
|
v-else
|
|
208
|
+
:aria-label="t('integration.nodeConfig.queryBuilder.row.rawTextAria', { n: idx + 1 })"
|
|
203
209
|
:model-value="row.text"
|
|
204
210
|
class="h-7 min-w-0 flex-1 bg-white font-mono text-sm"
|
|
205
|
-
title="
|
|
211
|
+
:title="t('integration.nodeConfig.queryBuilder.row.rawTextTitle')"
|
|
206
212
|
@update:model-value="(v) => setRow(idx, { text: String(v) })"
|
|
207
213
|
/>
|
|
208
214
|
<button
|
|
209
215
|
type="button"
|
|
216
|
+
:aria-label="t('integration.nodeConfig.queryBuilder.row.removeAria', { n: idx + 1 })"
|
|
210
217
|
class="flex size-6 shrink-0 items-center justify-center rounded text-muted-foreground transition hover:text-rose-600"
|
|
211
218
|
@click="removeRow(idx)"
|
|
212
219
|
>
|
|
213
|
-
<
|
|
220
|
+
<TrashBin :size="11" />
|
|
214
221
|
</button>
|
|
215
222
|
</div>
|
|
216
223
|
<Button
|
|
@@ -221,12 +228,13 @@ const rowMeta = computed(() => rows.value.map((r) => r.kind === "query" ? method
|
|
|
221
228
|
@click="addRow"
|
|
222
229
|
>
|
|
223
230
|
<Plus :size="11" />
|
|
224
|
-
|
|
231
|
+
{{ t("integration.nodeConfig.queryBuilder.action.addFilter") }}
|
|
225
232
|
</Button>
|
|
226
233
|
</template>
|
|
227
234
|
|
|
228
235
|
<template v-else>
|
|
229
236
|
<Textarea
|
|
237
|
+
:aria-label="t('integration.nodeConfig.queryBuilder.jsonAria')"
|
|
230
238
|
:model-value="jsonDraft"
|
|
231
239
|
:rows="6"
|
|
232
240
|
spellcheck="false"
|