@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
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<script setup>
|
|
2
|
-
import { computed, reactive, ref, watch } from "vue";
|
|
2
|
+
import { computed, reactive, ref, useId, watch } from "vue";
|
|
3
3
|
import {
|
|
4
4
|
AddCircle as Plus,
|
|
5
5
|
CheckCircle as Check,
|
|
@@ -23,31 +23,37 @@ import {
|
|
|
23
23
|
SelectValue
|
|
24
24
|
} from "@revenexx/studio";
|
|
25
25
|
import { toast } from "vue-sonner";
|
|
26
|
+
import { REVENEXX_SOURCE_KIND_BY_SLUG } from "../../utils/revenexxCredentialSource";
|
|
27
|
+
import { cockpitSources } from "../../utils/cockpitSources";
|
|
26
28
|
const props = defineProps({
|
|
27
29
|
type: { type: [Object, null], required: true }
|
|
28
30
|
});
|
|
29
31
|
const emit = defineEmits(["update:active", "apply"]);
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
const
|
|
47
|
-
const
|
|
48
|
-
const
|
|
49
|
-
const
|
|
50
|
-
const
|
|
32
|
+
const t = useIntegrationsT();
|
|
33
|
+
const kind = computed(() => props.type ? REVENEXX_SOURCE_KIND_BY_SLUG[props.type.slug] ?? null : null);
|
|
34
|
+
const uid = useId();
|
|
35
|
+
const SWITCH_LABEL = computed(() => ({
|
|
36
|
+
sftp: t("integration.credentials.revenexxSource.switch.sftp"),
|
|
37
|
+
smtp: t("integration.credentials.revenexxSource.switch.smtp"),
|
|
38
|
+
api: t("integration.credentials.revenexxSource.switch.api")
|
|
39
|
+
}));
|
|
40
|
+
const NEW_LABEL = computed(() => ({
|
|
41
|
+
sftp: t("integration.credentials.revenexxSource.new.sftp"),
|
|
42
|
+
smtp: t("integration.credentials.revenexxSource.new.smtp"),
|
|
43
|
+
api: t("integration.credentials.revenexxSource.new.api")
|
|
44
|
+
}));
|
|
45
|
+
const sources = cockpitSources();
|
|
46
|
+
const available = sources !== null;
|
|
47
|
+
const tenants = computed(() => sources?.tenants.tenants.value ?? []);
|
|
48
|
+
const onlyTenantSlug = computed(() => sources?.tenants.onlyTenantSlug.value ?? null);
|
|
49
|
+
const loadTenants = () => sources?.tenants.load() ?? Promise.resolve();
|
|
50
|
+
const markets = computed(() => sources?.market.markets.value ?? []);
|
|
51
|
+
const activeCode = computed(() => sources?.market.activeCode.value ?? "");
|
|
52
|
+
const fetchMarkets = () => sources?.market.fetchMarkets() ?? Promise.resolve();
|
|
53
|
+
const GLOBAL_MARKET_CODE = sources?.market.GLOBAL_MARKET_CODE ?? "__global__";
|
|
54
|
+
const sftp = sources?.sftp ?? null;
|
|
55
|
+
const mail = sources?.mail ?? null;
|
|
56
|
+
const consoleKeys = sources?.keys ?? null;
|
|
51
57
|
const config = useRuntimeConfig();
|
|
52
58
|
const sftpHost = config.public.sftpHost ?? "share.revenexx.com";
|
|
53
59
|
const sftpPort = Number(config.public.sftpPort ?? 42);
|
|
@@ -67,12 +73,13 @@ watch(kind, () => {
|
|
|
67
73
|
selectedId.value = "";
|
|
68
74
|
applied.value = null;
|
|
69
75
|
});
|
|
70
|
-
const sftpOptions = computed(() => sftp
|
|
76
|
+
const sftpOptions = computed(() => (sftp?.accounts.value ?? []).filter((a) => (a.is_active || a.status === "active") && a.login_username).map((a) => ({ value: String(a.id), label: a.login_username })));
|
|
71
77
|
const marketOptions = computed(() => [
|
|
72
|
-
{ code: GLOBAL_MARKET_CODE, label: "
|
|
78
|
+
{ code: GLOBAL_MARKET_CODE, label: t("integration.credentials.revenexxSource.market.global") },
|
|
73
79
|
...markets.value.map((m) => ({ code: m.code, label: `${m.name} (${m.code})` }))
|
|
74
80
|
]);
|
|
75
81
|
async function ensureLists() {
|
|
82
|
+
if (!sftp || !mail) return;
|
|
76
83
|
loadingList.value = true;
|
|
77
84
|
try {
|
|
78
85
|
await loadTenants();
|
|
@@ -86,7 +93,7 @@ async function ensureLists() {
|
|
|
86
93
|
await fetchMarkets();
|
|
87
94
|
}
|
|
88
95
|
} catch (e) {
|
|
89
|
-
toast.error(e?.message ?? "
|
|
96
|
+
toast.error(e?.message ?? t("integration.credentials.revenexxSource.error.loadResources"));
|
|
90
97
|
} finally {
|
|
91
98
|
loadingList.value = false;
|
|
92
99
|
}
|
|
@@ -98,14 +105,15 @@ function marketSlug() {
|
|
|
98
105
|
return marketCode.value === GLOBAL_MARKET_CODE ? "" : marketCode.value;
|
|
99
106
|
}
|
|
100
107
|
async function applySftp() {
|
|
101
|
-
|
|
108
|
+
if (!sftp) return;
|
|
109
|
+
const tenantSlug = tenant.value;
|
|
102
110
|
const acc = sftp.accounts.value.find((a) => String(a.id) === selectedId.value);
|
|
103
|
-
if (!
|
|
111
|
+
if (!tenantSlug || !acc) return;
|
|
104
112
|
applying.value = true;
|
|
105
113
|
try {
|
|
106
|
-
const creds = await sftp.revealPassword(
|
|
114
|
+
const creds = await sftp.revealPassword(tenantSlug, acc.id);
|
|
107
115
|
if (!creds) {
|
|
108
|
-
toast.error("
|
|
116
|
+
toast.error(t("integration.credentials.revenexxSource.error.accountProvisioning"));
|
|
109
117
|
return;
|
|
110
118
|
}
|
|
111
119
|
emit("apply", {
|
|
@@ -116,15 +124,20 @@ async function applySftp() {
|
|
|
116
124
|
});
|
|
117
125
|
applied.value = `${acc.login_username}@${sftpHost}:${sftpPort}`;
|
|
118
126
|
} catch (e) {
|
|
119
|
-
toast.error(e?.message ?? "
|
|
127
|
+
toast.error(e?.message ?? t("integration.credentials.revenexxSource.error.readSftpPassword"));
|
|
120
128
|
} finally {
|
|
121
129
|
applying.value = false;
|
|
122
130
|
}
|
|
123
131
|
}
|
|
124
132
|
async function applySmtp() {
|
|
133
|
+
if (!mail) return;
|
|
125
134
|
applying.value = true;
|
|
126
135
|
try {
|
|
127
136
|
const cred = await mail.getCredential(selectedId.value);
|
|
137
|
+
if (!cred) {
|
|
138
|
+
toast.error(t("integration.credentials.revenexxSource.error.mailCredentialGone"));
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
128
141
|
emit("apply", {
|
|
129
142
|
host: mailSmtpHost,
|
|
130
143
|
port: mailSmtpPort,
|
|
@@ -134,7 +147,7 @@ async function applySmtp() {
|
|
|
134
147
|
});
|
|
135
148
|
applied.value = `${cred.smtp_username}@${mailSmtpHost}:${mailSmtpPort}`;
|
|
136
149
|
} catch (e) {
|
|
137
|
-
toast.error(e?.message ?? "
|
|
150
|
+
toast.error(e?.message ?? t("integration.credentials.revenexxSource.error.readMailCredential"));
|
|
138
151
|
} finally {
|
|
139
152
|
applying.value = false;
|
|
140
153
|
}
|
|
@@ -165,39 +178,45 @@ function openCreate() {
|
|
|
165
178
|
}
|
|
166
179
|
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
167
180
|
async function createSftp() {
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
181
|
+
if (!sftp) return;
|
|
182
|
+
const tenantSlug = tenant.value;
|
|
183
|
+
if (!tenantSlug) {
|
|
184
|
+
toast.error(t("integration.credentials.revenexxSource.error.noTenant"));
|
|
171
185
|
return;
|
|
172
186
|
}
|
|
173
187
|
creating.value = true;
|
|
174
188
|
try {
|
|
175
|
-
let acc = await sftp.create(
|
|
189
|
+
let acc = await sftp.create(tenantSlug, createName.value.trim() || void 0);
|
|
176
190
|
for (let i = 0; i < 24 && !(acc.is_active || acc.status === "active"); i++) {
|
|
177
|
-
provisioningNote.value = "
|
|
191
|
+
provisioningNote.value = t("integration.credentials.revenexxSource.status.provisioning");
|
|
178
192
|
await sleep(2500);
|
|
179
|
-
acc = await sftp.refresh(
|
|
180
|
-
if (acc.status === "failed") throw new Error(acc.error_message || "
|
|
193
|
+
acc = await sftp.refresh(tenantSlug, acc.id);
|
|
194
|
+
if (acc.status === "failed") throw new Error(acc.error_message || t("integration.credentials.revenexxSource.error.provisioningFailed"));
|
|
181
195
|
}
|
|
182
196
|
if (!(acc.is_active || acc.status === "active")) {
|
|
183
|
-
throw new Error("
|
|
197
|
+
throw new Error(t("integration.credentials.revenexxSource.error.stillProvisioning"));
|
|
184
198
|
}
|
|
185
|
-
await sftp.load(
|
|
199
|
+
await sftp.load(tenantSlug);
|
|
186
200
|
selectedId.value = String(acc.id);
|
|
187
201
|
await applySftp();
|
|
188
202
|
createOpen.value = false;
|
|
189
|
-
toast.success("
|
|
203
|
+
toast.success(t("integration.credentials.revenexxSource.toast.sftpCreated"));
|
|
190
204
|
} catch (e) {
|
|
191
|
-
toast.error(e?.message ?? "
|
|
205
|
+
toast.error(e?.message ?? t("integration.credentials.revenexxSource.error.createSftp"));
|
|
192
206
|
} finally {
|
|
193
207
|
creating.value = false;
|
|
194
208
|
provisioningNote.value = "";
|
|
195
209
|
}
|
|
196
210
|
}
|
|
197
211
|
async function createSmtp() {
|
|
212
|
+
if (!mail) return;
|
|
198
213
|
creating.value = true;
|
|
199
214
|
try {
|
|
200
215
|
const cred = await mail.createCredential({ name: createName.value.trim() || "SMTP", type: "SMTP" });
|
|
216
|
+
if (!cred) {
|
|
217
|
+
toast.error(t("integration.credentials.revenexxSource.error.mailCredentialNotReturned"));
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
201
220
|
emit("apply", {
|
|
202
221
|
host: mailSmtpHost,
|
|
203
222
|
port: mailSmtpPort,
|
|
@@ -209,24 +228,25 @@ async function createSmtp() {
|
|
|
209
228
|
smtpCreds.value = [{ uuid: cred.uuid, name: cred.name }, ...smtpCreds.value];
|
|
210
229
|
selectedId.value = cred.uuid;
|
|
211
230
|
createOpen.value = false;
|
|
212
|
-
toast.success("
|
|
231
|
+
toast.success(t("integration.credentials.revenexxSource.toast.smtpCreated"));
|
|
213
232
|
} catch (e) {
|
|
214
|
-
toast.error(e?.message ?? "
|
|
233
|
+
toast.error(e?.message ?? t("integration.credentials.revenexxSource.error.createSmtp"));
|
|
215
234
|
} finally {
|
|
216
235
|
creating.value = false;
|
|
217
236
|
}
|
|
218
237
|
}
|
|
219
238
|
async function createApiKey() {
|
|
239
|
+
if (!consoleKeys) return;
|
|
220
240
|
creating.value = true;
|
|
221
241
|
try {
|
|
222
242
|
const { secret } = await consoleKeys.create(createName.value.trim() || "Integrations key", ["*"]);
|
|
223
243
|
emit("apply", { apiKey: secret, tenant: tenant.value ?? "", market: marketSlug() });
|
|
224
244
|
apiKeyReady.value = true;
|
|
225
|
-
applied.value =
|
|
245
|
+
applied.value = marketSlug() ? t("integration.credentials.revenexxSource.status.keyForMarket", { tenant: tenant.value, market: marketSlug() }) : t("integration.credentials.revenexxSource.status.keyForGlobal", { tenant: tenant.value });
|
|
226
246
|
createOpen.value = false;
|
|
227
|
-
toast.success("
|
|
247
|
+
toast.success(t("integration.credentials.revenexxSource.toast.apiKeyCreated"));
|
|
228
248
|
} catch (e) {
|
|
229
|
-
toast.error(e?.message ?? "
|
|
249
|
+
toast.error(e?.message ?? t("integration.credentials.revenexxSource.error.createApiKey"));
|
|
230
250
|
} finally {
|
|
231
251
|
creating.value = false;
|
|
232
252
|
}
|
|
@@ -244,24 +264,41 @@ function submitCreate() {
|
|
|
244
264
|
<div class="flex items-center justify-between gap-3">
|
|
245
265
|
<div class="min-w-0">
|
|
246
266
|
<div class="text-sm font-medium">{{ SWITCH_LABEL[kind] }}</div>
|
|
267
|
+
<!-- Two sentences, one per state. Saying WHERE the sources are missing is
|
|
268
|
+
the point: without it an operator in the dev studio or another host
|
|
269
|
+
reads a dead switch and cannot tell a broken feature from one that
|
|
270
|
+
belongs somewhere else. The manual fields stay on screen either way. -->
|
|
247
271
|
<p class="text-xs text-muted-foreground">
|
|
248
|
-
|
|
272
|
+
<template v-if="available">
|
|
273
|
+
{{ t("integration.credentials.revenexxSource.hint.available") }}
|
|
274
|
+
</template>
|
|
275
|
+
<template v-else>
|
|
276
|
+
{{ t("integration.credentials.revenexxSource.hint.unavailable") }}
|
|
277
|
+
</template>
|
|
249
278
|
</p>
|
|
250
279
|
</div>
|
|
251
|
-
|
|
280
|
+
<!-- Disabled rather than hidden: a control that vanishes leaves nothing to
|
|
281
|
+
read the explanation from, and the operator cannot tell whether the
|
|
282
|
+
feature exists at all. -->
|
|
283
|
+
<Switch
|
|
284
|
+
:aria-label="SWITCH_LABEL[kind]"
|
|
285
|
+
:disabled="!available"
|
|
286
|
+
:model-value="active"
|
|
287
|
+
@update:model-value="(v) => active = v"
|
|
288
|
+
/>
|
|
252
289
|
</div>
|
|
253
290
|
|
|
254
291
|
<div v-if="active" class="mt-3 space-y-3">
|
|
255
292
|
<!-- Tenant (preselected) -->
|
|
256
293
|
<div class="space-y-1.5">
|
|
257
|
-
<Label>
|
|
294
|
+
<Label :for="`${uid}-tenant`">{{ t("integration.credentials.revenexxSource.field.tenant.label") }}</Label>
|
|
258
295
|
<Select :model-value="tenant ?? ''" @update:model-value="(v) => tenant = v">
|
|
259
|
-
<SelectTrigger class="bg-white">
|
|
260
|
-
<SelectValue placeholder="
|
|
296
|
+
<SelectTrigger :id="`${uid}-tenant`" class="bg-white">
|
|
297
|
+
<SelectValue :placeholder="t('integration.credentials.revenexxSource.field.tenant.placeholder')" />
|
|
261
298
|
</SelectTrigger>
|
|
262
299
|
<SelectContent>
|
|
263
|
-
<SelectItem v-for="
|
|
264
|
-
{{
|
|
300
|
+
<SelectItem v-for="tn in tenants" :key="tn.slug" :value="tn.slug">
|
|
301
|
+
{{ tn.name }} ({{ tn.slug }})
|
|
265
302
|
</SelectItem>
|
|
266
303
|
</SelectContent>
|
|
267
304
|
</Select>
|
|
@@ -269,10 +306,10 @@ function submitCreate() {
|
|
|
269
306
|
|
|
270
307
|
<!-- Market scope — only the Revenexx API credential is market-scoped -->
|
|
271
308
|
<div v-if="kind === 'api'" class="space-y-1.5">
|
|
272
|
-
<Label>
|
|
309
|
+
<Label :for="`${uid}-market`">{{ t("integration.credentials.revenexxSource.field.market.label") }}</Label>
|
|
273
310
|
<Select :model-value="marketCode" @update:model-value="(v) => marketCode = v">
|
|
274
|
-
<SelectTrigger class="bg-white">
|
|
275
|
-
<SelectValue placeholder="
|
|
311
|
+
<SelectTrigger :id="`${uid}-market`" class="bg-white">
|
|
312
|
+
<SelectValue :placeholder="t('integration.credentials.revenexxSource.field.market.placeholder')" />
|
|
276
313
|
</SelectTrigger>
|
|
277
314
|
<SelectContent>
|
|
278
315
|
<SelectItem v-for="m in marketOptions" :key="m.code" :value="m.code">
|
|
@@ -284,11 +321,11 @@ function submitCreate() {
|
|
|
284
321
|
|
|
285
322
|
<!-- Resource select (SFTP / Mail) -->
|
|
286
323
|
<div v-if="kind === 'sftp' || kind === 'smtp'" class="space-y-1.5">
|
|
287
|
-
<Label>{{ kind === "sftp" ? "
|
|
324
|
+
<Label :for="`${uid}-resource`">{{ kind === "sftp" ? t("integration.credentials.revenexxSource.field.resource.sftpLabel") : t("integration.credentials.revenexxSource.field.resource.mailLabel") }}</Label>
|
|
288
325
|
<div class="flex items-center gap-2">
|
|
289
326
|
<Select :model-value="selectedId" class="flex-1" @update:model-value="(v) => onSelect(v)">
|
|
290
|
-
<SelectTrigger class="bg-white">
|
|
291
|
-
<SelectValue :placeholder="loadingList ? '
|
|
327
|
+
<SelectTrigger :id="`${uid}-resource`" class="bg-white">
|
|
328
|
+
<SelectValue :placeholder="loadingList ? t('integration.common.state.loading') : t('integration.credentials.revenexxSource.field.resource.placeholder')" />
|
|
292
329
|
</SelectTrigger>
|
|
293
330
|
<SelectContent>
|
|
294
331
|
<template v-if="kind === 'sftp'">
|
|
@@ -301,7 +338,7 @@ function submitCreate() {
|
|
|
301
338
|
</Select>
|
|
302
339
|
<Button variant="outline" size="sm" class="shrink-0 bg-white" :disabled="!tenant" @click="openCreate">
|
|
303
340
|
<Plus :size="14" class="mr-1" />
|
|
304
|
-
|
|
341
|
+
{{ t("integration.credentials.revenexxSource.action.new") }}
|
|
305
342
|
</Button>
|
|
306
343
|
</div>
|
|
307
344
|
</div>
|
|
@@ -310,7 +347,7 @@ function submitCreate() {
|
|
|
310
347
|
<div v-else-if="kind === 'api'">
|
|
311
348
|
<Button variant="outline" size="sm" class="bg-white" :disabled="!tenant" @click="openCreate">
|
|
312
349
|
<Plus :size="14" class="mr-1" />
|
|
313
|
-
|
|
350
|
+
{{ t("integration.credentials.revenexxSource.action.createApiKey") }}
|
|
314
351
|
</Button>
|
|
315
352
|
</div>
|
|
316
353
|
|
|
@@ -318,7 +355,7 @@ function submitCreate() {
|
|
|
318
355
|
<div v-if="applied || applying" class="flex items-center gap-2 rounded-md bg-emerald-500/10 px-3 py-2 text-sm text-emerald-700">
|
|
319
356
|
<Loader2 v-if="applying" :size="14" class="animate-spin" />
|
|
320
357
|
<Check v-else :size="14" />
|
|
321
|
-
<span class="truncate">{{ applying ? "
|
|
358
|
+
<span class="truncate">{{ applying ? t("integration.credentials.revenexxSource.status.copying") : t("integration.credentials.revenexxSource.status.using", { value: applied }) }}</span>
|
|
322
359
|
</div>
|
|
323
360
|
</div>
|
|
324
361
|
|
|
@@ -326,36 +363,36 @@ function submitCreate() {
|
|
|
326
363
|
<Dialog v-model:open="createOpen">
|
|
327
364
|
<DialogContent class="sm:max-w-md">
|
|
328
365
|
<DialogHeader>
|
|
329
|
-
<DialogTitle>{{ kind ? NEW_LABEL[kind] : "
|
|
366
|
+
<DialogTitle>{{ kind ? NEW_LABEL[kind] : t("integration.credentials.revenexxSource.new.fallback") }}</DialogTitle>
|
|
330
367
|
<DialogDescription class="text-sm">
|
|
331
368
|
<template v-if="kind === 'api'">
|
|
332
|
-
|
|
369
|
+
{{ t("integration.credentials.revenexxSource.createDialog.description.api", { tenant }) }}
|
|
333
370
|
</template>
|
|
334
371
|
<template v-else-if="kind === 'sftp'">
|
|
335
|
-
|
|
372
|
+
{{ t("integration.credentials.revenexxSource.createDialog.description.sftp", { tenant }) }}
|
|
336
373
|
</template>
|
|
337
374
|
<template v-else>
|
|
338
|
-
|
|
375
|
+
{{ t("integration.credentials.revenexxSource.createDialog.description.smtp") }}
|
|
339
376
|
</template>
|
|
340
377
|
</DialogDescription>
|
|
341
378
|
</DialogHeader>
|
|
342
379
|
|
|
343
380
|
<div class="space-y-1.5 py-1">
|
|
344
|
-
<Label for="rvnxx-new-name">{{ kind === "sftp" ? "
|
|
381
|
+
<Label for="rvnxx-new-name">{{ kind === "sftp" ? t("integration.credentials.revenexxSource.createDialog.field.label") : t("integration.credentials.revenexxSource.createDialog.field.name") }}</Label>
|
|
345
382
|
<Input
|
|
346
383
|
id="rvnxx-new-name"
|
|
347
384
|
v-model="createName"
|
|
348
385
|
class="bg-white"
|
|
349
|
-
:placeholder="kind === 'sftp' ? '
|
|
386
|
+
:placeholder="kind === 'sftp' ? t('integration.credentials.revenexxSource.createDialog.field.placeholder.sftp') : t('integration.credentials.revenexxSource.createDialog.field.placeholder.default')"
|
|
350
387
|
/>
|
|
351
388
|
<p v-if="provisioningNote" class="text-xs text-muted-foreground">{{ provisioningNote }}</p>
|
|
352
389
|
</div>
|
|
353
390
|
|
|
354
391
|
<DialogFooter>
|
|
355
|
-
<Button variant="ghost" :disabled="creating" @click="createOpen = false">
|
|
392
|
+
<Button variant="ghost" :disabled="creating" @click="createOpen = false">{{ t("integration.common.action.cancel") }}</Button>
|
|
356
393
|
<Button :disabled="creating" @click="submitCreate">
|
|
357
394
|
<Loader2 v-if="creating" :size="14" class="mr-1 animate-spin" />
|
|
358
|
-
|
|
395
|
+
{{ t("integration.credentials.revenexxSource.action.create") }}
|
|
359
396
|
</Button>
|
|
360
397
|
</DialogFooter>
|
|
361
398
|
</DialogContent>
|
|
@@ -2,30 +2,26 @@
|
|
|
2
2
|
import { computed, ref } from "vue";
|
|
3
3
|
import { Magnifier as Search } from "#solar-icons";
|
|
4
4
|
import { Input } from "@revenexx/studio";
|
|
5
|
+
import { resolveLocalized } from "../../utils/localize";
|
|
5
6
|
const props = defineProps({
|
|
6
7
|
types: { type: Array, required: true }
|
|
7
8
|
});
|
|
8
9
|
const emit = defineEmits(["pick"]);
|
|
10
|
+
const t = useIntegrationsT();
|
|
9
11
|
const query = ref("");
|
|
10
|
-
function loc(v) {
|
|
11
|
-
if (!v) return "";
|
|
12
|
-
if (typeof v === "string") return v;
|
|
13
|
-
const o = v;
|
|
14
|
-
return o.en ?? o.de ?? Object.values(o)[0] ?? "";
|
|
15
|
-
}
|
|
16
12
|
function authKindLabel(k) {
|
|
17
13
|
switch (k) {
|
|
18
14
|
case "api-key":
|
|
19
|
-
return "
|
|
15
|
+
return t("integration.credentials.shared.authKind.apiKey");
|
|
20
16
|
case "oauth2-authcode":
|
|
21
17
|
case "oauth2-client-credentials":
|
|
22
|
-
return "
|
|
18
|
+
return t("integration.credentials.shared.authKind.oauth2");
|
|
23
19
|
case "basic-auth":
|
|
24
|
-
return "
|
|
20
|
+
return t("integration.credentials.shared.authKind.basicAuth");
|
|
25
21
|
case "static":
|
|
26
|
-
return "
|
|
22
|
+
return t("integration.credentials.shared.authKind.static");
|
|
27
23
|
case "secret":
|
|
28
|
-
return "
|
|
24
|
+
return t("integration.credentials.shared.authKind.secret");
|
|
29
25
|
default:
|
|
30
26
|
return k ?? "";
|
|
31
27
|
}
|
|
@@ -33,7 +29,7 @@ function authKindLabel(k) {
|
|
|
33
29
|
const filtered = computed(() => {
|
|
34
30
|
const q = query.value.trim().toLowerCase();
|
|
35
31
|
if (!q) return props.types;
|
|
36
|
-
return props.types.filter((
|
|
32
|
+
return props.types.filter((t2) => resolveLocalized(t2.name).toLowerCase().includes(q));
|
|
37
33
|
});
|
|
38
34
|
</script>
|
|
39
35
|
|
|
@@ -43,15 +39,16 @@ const filtered = computed(() => {
|
|
|
43
39
|
<Search :size="15" class="absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground" />
|
|
44
40
|
<Input
|
|
45
41
|
v-model="query"
|
|
42
|
+
:aria-label="t('integration.credentials.typePicker.search.aria')"
|
|
46
43
|
class="bg-white pl-9"
|
|
47
|
-
placeholder="
|
|
44
|
+
:placeholder="t('integration.credentials.typePicker.search.placeholder')"
|
|
48
45
|
autofocus
|
|
49
46
|
/>
|
|
50
47
|
</div>
|
|
51
48
|
|
|
52
49
|
<div class="mt-2 max-h-80 overflow-y-auto -mx-1 px-1">
|
|
53
50
|
<p v-if="!filtered.length" class="px-2 py-6 text-center text-sm text-muted-foreground">
|
|
54
|
-
|
|
51
|
+
{{ t("integration.credentials.typePicker.empty", { query }) }}
|
|
55
52
|
</p>
|
|
56
53
|
<button
|
|
57
54
|
v-for="t in filtered"
|
|
@@ -62,7 +59,7 @@ const filtered = computed(() => {
|
|
|
62
59
|
>
|
|
63
60
|
<IntegrationsCredentialLogo :type="t" :size="16" />
|
|
64
61
|
<div class="min-w-0 flex-1 truncate text-sm">
|
|
65
|
-
<span class="font-medium">{{
|
|
62
|
+
<span class="font-medium">{{ resolveLocalized(t.name) || t.slug }}</span>
|
|
66
63
|
<span class="ml-1.5 text-muted-foreground">({{ authKindLabel(t.auth_kind) }})</span>
|
|
67
64
|
</div>
|
|
68
65
|
</button>
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
<script setup>
|
|
2
2
|
import { onMounted, ref, watch } from "vue";
|
|
3
|
-
import { RefreshCircle as Loader2, Refresh as ReplayIcon, TrashBinMinimalistic as
|
|
3
|
+
import { RefreshCircle as Loader2, Refresh as ReplayIcon, TrashBinMinimalistic as TrashBin } from "#solar-icons";
|
|
4
4
|
import { Badge, Button } from "@revenexx/studio";
|
|
5
5
|
import { toast } from "vue-sonner";
|
|
6
6
|
const props = defineProps({
|
|
7
7
|
workflowId: { type: Number, required: true }
|
|
8
8
|
});
|
|
9
|
+
const t = useIntegrationsT();
|
|
9
10
|
const { list, replay, discard } = useIntegrationsDeadLetters();
|
|
10
11
|
const PER_PAGE = 25;
|
|
11
12
|
const items = ref([]);
|
|
@@ -25,7 +26,7 @@ async function load(p = page.value) {
|
|
|
25
26
|
lastPage.value = res.lastPage;
|
|
26
27
|
page.value = res.page;
|
|
27
28
|
} catch (e) {
|
|
28
|
-
error.value = e?.message ?? "
|
|
29
|
+
error.value = e?.message ?? t("integration.deadLetters.error.load");
|
|
29
30
|
} finally {
|
|
30
31
|
loading.value = false;
|
|
31
32
|
}
|
|
@@ -41,10 +42,10 @@ async function onReplay(dl) {
|
|
|
41
42
|
try {
|
|
42
43
|
const res = await replay(dl.id);
|
|
43
44
|
const newId = res && typeof res === "object" && "id" in res ? String(res.id) : null;
|
|
44
|
-
toast.success(newId ?
|
|
45
|
+
toast.success(newId ? t("integration.deadLetters.toast.replay.started", { id: newId }) : t("integration.deadLetters.toast.replay.done"));
|
|
45
46
|
await load();
|
|
46
47
|
} catch (e) {
|
|
47
|
-
toast.error(e?.message ?? "
|
|
48
|
+
toast.error(e?.message ?? t("integration.deadLetters.toast.replay.error"));
|
|
48
49
|
} finally {
|
|
49
50
|
busyId.value = null;
|
|
50
51
|
}
|
|
@@ -53,10 +54,10 @@ async function onDiscard(dl) {
|
|
|
53
54
|
busyId.value = dl.id;
|
|
54
55
|
try {
|
|
55
56
|
await discard(dl.id);
|
|
56
|
-
toast.success("
|
|
57
|
+
toast.success(t("integration.deadLetters.toast.discard.done"));
|
|
57
58
|
await load();
|
|
58
59
|
} catch (e) {
|
|
59
|
-
toast.error(e?.message ?? "
|
|
60
|
+
toast.error(e?.message ?? t("integration.deadLetters.toast.discard.error"));
|
|
60
61
|
} finally {
|
|
61
62
|
busyId.value = null;
|
|
62
63
|
}
|
|
@@ -80,14 +81,14 @@ function fmt(iso) {
|
|
|
80
81
|
<div class="space-y-3">
|
|
81
82
|
<div class="flex items-center justify-between">
|
|
82
83
|
<div>
|
|
83
|
-
<h3 class="text-sm font-semibold">
|
|
84
|
+
<h3 class="text-sm font-semibold">{{ t("integration.deadLetters.panel.title") }}</h3>
|
|
84
85
|
<p class="mt-0.5 text-sm text-muted-foreground">
|
|
85
|
-
|
|
86
|
+
{{ t("integration.deadLetters.panel.description") }}
|
|
86
87
|
</p>
|
|
87
88
|
</div>
|
|
88
89
|
<Button variant="outline" size="sm" class="h-7 text-sm" :disabled="loading" @click="() => load()">
|
|
89
90
|
<Loader2 v-if="loading" :size="12" class="animate-spin" />
|
|
90
|
-
|
|
91
|
+
{{ t("integration.common.action.refresh") }}
|
|
91
92
|
</Button>
|
|
92
93
|
</div>
|
|
93
94
|
|
|
@@ -107,7 +108,7 @@ function fmt(iso) {
|
|
|
107
108
|
</div>
|
|
108
109
|
</div>
|
|
109
110
|
<div v-else-if="!items.length" class="rounded-lg border border-black/[0.06] bg-white p-6 text-center text-sm text-muted-foreground">
|
|
110
|
-
|
|
111
|
+
{{ t("integration.deadLetters.state.empty") }}
|
|
111
112
|
</div>
|
|
112
113
|
|
|
113
114
|
<div v-else class="rounded-lg border border-black/[0.06] bg-white divide-y divide-black/[0.04]">
|
|
@@ -118,7 +119,7 @@ function fmt(iso) {
|
|
|
118
119
|
>
|
|
119
120
|
<Badge variant="outline" :class="['border text-xs font-medium uppercase shrink-0 mt-0.5', statusClass(dl.status)]">{{ dl.status }}</Badge>
|
|
120
121
|
<div class="min-w-0 flex-1">
|
|
121
|
-
<div class="text-sm break-words">{{ dl.reason || "
|
|
122
|
+
<div class="text-sm break-words">{{ dl.reason || t("integration.deadLetters.reason.fallback") }}</div>
|
|
122
123
|
<div class="mt-0.5 flex flex-wrap items-center gap-x-3 gap-y-0.5 text-xs text-muted-foreground font-mono">
|
|
123
124
|
<span>#{{ dl.id }}</span>
|
|
124
125
|
<span v-if="dl.source">{{ dl.source }}</span>
|
|
@@ -130,21 +131,21 @@ function fmt(iso) {
|
|
|
130
131
|
<Button variant="outline" size="sm" class="h-7 text-sm" :disabled="busyId === dl.id" @click="onReplay(dl)">
|
|
131
132
|
<Loader2 v-if="busyId === dl.id" :size="12" class="animate-spin" />
|
|
132
133
|
<ReplayIcon v-else :size="12" />
|
|
133
|
-
|
|
134
|
+
{{ t("integration.deadLetters.action.replay") }}
|
|
134
135
|
</Button>
|
|
135
136
|
<Button variant="ghost" size="sm" class="h-7 text-sm text-rose-600 hover:bg-rose-500/10" :disabled="busyId === dl.id" @click="onDiscard(dl)">
|
|
136
|
-
<
|
|
137
|
-
|
|
137
|
+
<TrashBin :size="12" />
|
|
138
|
+
{{ t("integration.deadLetters.action.discard") }}
|
|
138
139
|
</Button>
|
|
139
140
|
</div>
|
|
140
141
|
</div>
|
|
141
142
|
</div>
|
|
142
143
|
|
|
143
144
|
<div v-if="lastPage > 1" class="flex items-center justify-between text-sm text-muted-foreground">
|
|
144
|
-
<span class="tabular-nums">
|
|
145
|
+
<span class="tabular-nums">{{ t("integration.deadLetters.pagination.summary", { page, lastPage, total }) }}</span>
|
|
145
146
|
<div class="flex gap-2">
|
|
146
|
-
<Button variant="outline" size="sm" class="h-7 text-sm" :disabled="page <= 1 || loading" @click="goPage(page - 1)"
|
|
147
|
-
<Button variant="outline" size="sm" class="h-7 text-sm" :disabled="page >= lastPage || loading" @click="goPage(page + 1)">
|
|
147
|
+
<Button variant="outline" size="sm" class="h-7 text-sm" :disabled="page <= 1 || loading" @click="goPage(page - 1)">{{ t("integration.deadLetters.pagination.newer") }}</Button>
|
|
148
|
+
<Button variant="outline" size="sm" class="h-7 text-sm" :disabled="page >= lastPage || loading" @click="goPage(page + 1)">{{ t("integration.deadLetters.pagination.older") }}</Button>
|
|
148
149
|
</div>
|
|
149
150
|
</div>
|
|
150
151
|
</div>
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
type __VLS_Props = {
|
|
2
2
|
modelValue: string;
|
|
3
|
+
inputId?: string;
|
|
3
4
|
};
|
|
4
5
|
declare const _default: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
5
6
|
"update:modelValue": (value: string) => any;
|
|
@@ -2,9 +2,11 @@
|
|
|
2
2
|
import { computed, onMounted, ref } from "vue";
|
|
3
3
|
import { Input } from "@revenexx/studio";
|
|
4
4
|
const props = defineProps({
|
|
5
|
-
modelValue: { type: String, required: true }
|
|
5
|
+
modelValue: { type: String, required: true },
|
|
6
|
+
inputId: { type: String, required: false }
|
|
6
7
|
});
|
|
7
8
|
const emit = defineEmits(["update:modelValue"]);
|
|
9
|
+
const t = useIntegrationsT();
|
|
8
10
|
const catalog = typeof useDefinedEvents === "function" ? useDefinedEvents() : { emits: ref([]), listens: ref([]), loading: ref(false), load: async () => {
|
|
9
11
|
} };
|
|
10
12
|
onMounted(() => {
|
|
@@ -14,8 +16,8 @@ const focused = ref(false);
|
|
|
14
16
|
const options = computed(() => {
|
|
15
17
|
const out = /* @__PURE__ */ new Map();
|
|
16
18
|
for (const e of catalog.emits.value) out.set(e.topic, { topic: e.topic, source: e.app });
|
|
17
|
-
for (const
|
|
18
|
-
if (!out.has(
|
|
19
|
+
for (const t2 of catalog.listens.value) {
|
|
20
|
+
if (!out.has(t2)) out.set(t2, { topic: t2, source: null });
|
|
19
21
|
}
|
|
20
22
|
return [...out.values()].sort((a, b) => a.topic.localeCompare(b.topic));
|
|
21
23
|
});
|
|
@@ -34,6 +36,7 @@ function pick(topic) {
|
|
|
34
36
|
<template>
|
|
35
37
|
<div class="relative">
|
|
36
38
|
<Input
|
|
39
|
+
:id="props.inputId"
|
|
37
40
|
:model-value="modelValue"
|
|
38
41
|
placeholder="customer.created"
|
|
39
42
|
class="h-8 text-sm bg-white font-mono"
|
|
@@ -46,7 +49,7 @@ function pick(topic) {
|
|
|
46
49
|
class="absolute left-0 right-0 top-full z-30 mt-1 max-h-56 overflow-y-auto rounded-md border border-black/[0.08] bg-white py-1 shadow-md"
|
|
47
50
|
>
|
|
48
51
|
<div v-if="catalog.loading.value && !filtered.length" class="px-2.5 py-1.5 text-sm text-muted-foreground">
|
|
49
|
-
|
|
52
|
+
{{ t("integration.canvas.eventCombobox.loading") }}
|
|
50
53
|
</div>
|
|
51
54
|
<button
|
|
52
55
|
v-for="o in filtered"
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
type __VLS_Props = {
|
|
2
2
|
modelValue: string;
|
|
3
|
+
inputId?: string;
|
|
3
4
|
};
|
|
4
5
|
declare const _default: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
5
6
|
"update:modelValue": (value: string) => any;
|
|
@@ -10,6 +10,10 @@ type __VLS_Props = {
|
|
|
10
10
|
placeholder?: string;
|
|
11
11
|
variables?: Record<string, WorkflowVariable>;
|
|
12
12
|
upstreamOutputs?: UpstreamOutput[];
|
|
13
|
+
/** Draw the editor as at fault — the field above owns the reason. */
|
|
14
|
+
invalid?: boolean;
|
|
15
|
+
/** The setting's label, so the editable region has an accessible name. */
|
|
16
|
+
label?: string;
|
|
13
17
|
};
|
|
14
18
|
declare const _default: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
15
19
|
"update:modelValue": (value: string) => any;
|