@revenexx/studio-integrations 0.1.4 → 0.3.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.
Files changed (188) hide show
  1. package/README.md +31 -0
  2. package/dist/module.d.mts +2 -2
  3. package/dist/module.json +1 -1
  4. package/dist/runtime/components/Integrations/CredentialCreateDialog.d.vue.ts +13 -0
  5. package/dist/runtime/components/Integrations/CredentialCreateDialog.vue +279 -0
  6. package/dist/runtime/components/Integrations/CredentialCreateDialog.vue.d.ts +13 -0
  7. package/dist/runtime/components/Integrations/CredentialDetailDialog.d.vue.ts +20 -0
  8. package/dist/runtime/components/Integrations/CredentialDetailDialog.vue +342 -0
  9. package/dist/runtime/components/Integrations/CredentialDetailDialog.vue.d.ts +20 -0
  10. package/dist/runtime/components/Integrations/CredentialLogo.d.vue.ts +14 -0
  11. package/dist/runtime/components/Integrations/CredentialLogo.vue +37 -0
  12. package/dist/runtime/components/Integrations/CredentialLogo.vue.d.ts +14 -0
  13. package/dist/runtime/components/Integrations/CredentialRevenexxSource.d.vue.ts +12 -0
  14. package/dist/runtime/components/Integrations/CredentialRevenexxSource.vue +364 -0
  15. package/dist/runtime/components/Integrations/CredentialRevenexxSource.vue.d.ts +12 -0
  16. package/dist/runtime/components/Integrations/CredentialTypePicker.d.vue.ts +15 -0
  17. package/dist/runtime/components/Integrations/CredentialTypePicker.vue +71 -0
  18. package/dist/runtime/components/Integrations/CredentialTypePicker.vue.d.ts +15 -0
  19. package/dist/runtime/components/Integrations/DeadLettersPanel.d.vue.ts +5 -0
  20. package/dist/runtime/components/Integrations/DeadLettersPanel.vue +151 -0
  21. package/dist/runtime/components/Integrations/DeadLettersPanel.vue.d.ts +5 -0
  22. package/dist/runtime/components/Integrations/EditorTip.d.vue.ts +17 -0
  23. package/dist/runtime/components/Integrations/EditorTip.vue +46 -0
  24. package/dist/runtime/components/Integrations/EditorTip.vue.d.ts +17 -0
  25. package/dist/runtime/components/Integrations/EventTypeCombobox.d.vue.ts +9 -0
  26. package/dist/runtime/components/Integrations/EventTypeCombobox.vue +64 -0
  27. package/dist/runtime/components/Integrations/EventTypeCombobox.vue.d.ts +9 -0
  28. package/dist/runtime/components/Integrations/ExpressionInput.d.vue.ts +19 -0
  29. package/dist/runtime/components/Integrations/ExpressionInput.vue +236 -0
  30. package/dist/runtime/components/Integrations/ExpressionInput.vue.d.ts +19 -0
  31. package/dist/runtime/components/Integrations/GroupFrameView.d.vue.ts +28 -0
  32. package/dist/runtime/components/Integrations/GroupFrameView.vue +222 -0
  33. package/dist/runtime/components/Integrations/GroupFrameView.vue.d.ts +28 -0
  34. package/dist/runtime/components/Integrations/NodeConfigDynamicField.d.vue.ts +14 -0
  35. package/dist/runtime/components/Integrations/NodeConfigDynamicField.vue +184 -0
  36. package/dist/runtime/components/Integrations/NodeConfigDynamicField.vue.d.ts +14 -0
  37. package/dist/runtime/components/Integrations/NodeConfigDynamicSchema.d.vue.ts +16 -0
  38. package/dist/runtime/components/Integrations/NodeConfigDynamicSchema.vue +102 -0
  39. package/dist/runtime/components/Integrations/NodeConfigDynamicSchema.vue.d.ts +16 -0
  40. package/dist/runtime/components/Integrations/NodeConfigField.d.vue.ts +23 -0
  41. package/dist/runtime/components/Integrations/NodeConfigField.vue +587 -0
  42. package/dist/runtime/components/Integrations/NodeConfigField.vue.d.ts +23 -0
  43. package/dist/runtime/components/Integrations/NodeDynamicOutputsResolver.d.vue.ts +14 -0
  44. package/dist/runtime/components/Integrations/NodeDynamicOutputsResolver.vue +80 -0
  45. package/dist/runtime/components/Integrations/NodeDynamicOutputsResolver.vue.d.ts +14 -0
  46. package/dist/runtime/components/Integrations/NodeInspector.d.vue.ts +22 -3
  47. package/dist/runtime/components/Integrations/NodeInspector.vue +1074 -294
  48. package/dist/runtime/components/Integrations/NodeInspector.vue.d.ts +22 -3
  49. package/dist/runtime/components/Integrations/NodeLogo.d.vue.ts +9 -0
  50. package/dist/runtime/components/Integrations/NodeLogo.vue +38 -0
  51. package/dist/runtime/components/Integrations/NodeLogo.vue.d.ts +9 -0
  52. package/dist/runtime/components/Integrations/NodePalette.d.vue.ts +22 -1
  53. package/dist/runtime/components/Integrations/NodePalette.vue +411 -90
  54. package/dist/runtime/components/Integrations/NodePalette.vue.d.ts +22 -1
  55. package/dist/runtime/components/Integrations/QueryBuilder.d.vue.ts +9 -0
  56. package/dist/runtime/components/Integrations/QueryBuilder.vue +239 -0
  57. package/dist/runtime/components/Integrations/QueryBuilder.vue.d.ts +9 -0
  58. package/dist/runtime/components/Integrations/{RunDetailDialog.d.vue.ts → RunActionsMenu.d.vue.ts} +3 -4
  59. package/dist/runtime/components/Integrations/RunActionsMenu.vue +86 -0
  60. package/dist/runtime/components/Integrations/{RunDetailDialog.vue.d.ts → RunActionsMenu.vue.d.ts} +3 -4
  61. package/dist/runtime/components/Integrations/RunDetailPanel.d.vue.ts +14 -0
  62. package/dist/runtime/components/Integrations/RunDetailPanel.vue +777 -0
  63. package/dist/runtime/components/Integrations/RunDetailPanel.vue.d.ts +14 -0
  64. package/dist/runtime/components/Integrations/RunPayloadInput.d.vue.ts +13 -0
  65. package/dist/runtime/components/Integrations/RunPayloadInput.vue +175 -0
  66. package/dist/runtime/components/Integrations/RunPayloadInput.vue.d.ts +13 -0
  67. package/dist/runtime/components/Integrations/RunResponseModal.d.vue.ts +27 -0
  68. package/dist/runtime/components/Integrations/RunResponseModal.vue +84 -0
  69. package/dist/runtime/components/Integrations/RunResponseModal.vue.d.ts +27 -0
  70. package/dist/runtime/components/Integrations/RunsTable.d.vue.ts +21 -0
  71. package/dist/runtime/components/Integrations/RunsTable.vue +114 -0
  72. package/dist/runtime/components/Integrations/RunsTable.vue.d.ts +21 -0
  73. package/dist/runtime/components/Integrations/SftpAccountImportDialog.d.vue.ts +11 -0
  74. package/dist/runtime/components/Integrations/SftpAccountImportDialog.vue +91 -0
  75. package/dist/runtime/components/Integrations/SftpAccountImportDialog.vue.d.ts +11 -0
  76. package/dist/runtime/components/Integrations/StickyNoteView.d.vue.ts +28 -0
  77. package/dist/runtime/components/Integrations/StickyNoteView.vue +208 -0
  78. package/dist/runtime/components/Integrations/StickyNoteView.vue.d.ts +28 -0
  79. package/dist/runtime/components/Integrations/TemplateFlowPreview.d.vue.ts +29 -0
  80. package/dist/runtime/components/Integrations/TemplateFlowPreview.vue +201 -0
  81. package/dist/runtime/components/Integrations/TemplateFlowPreview.vue.d.ts +29 -0
  82. package/dist/runtime/components/Integrations/WorkflowContextMenu.d.vue.ts +32 -0
  83. package/dist/runtime/components/Integrations/WorkflowContextMenu.vue +138 -0
  84. package/dist/runtime/components/Integrations/WorkflowContextMenu.vue.d.ts +32 -0
  85. package/dist/runtime/components/Integrations/WorkflowCreateDialog.d.vue.ts +9 -0
  86. package/dist/runtime/components/Integrations/WorkflowCreateDialog.vue +322 -0
  87. package/dist/runtime/components/Integrations/WorkflowCreateDialog.vue.d.ts +9 -0
  88. package/dist/runtime/components/Integrations/WorkflowDuplicateDialog.d.vue.ts +18 -0
  89. package/dist/runtime/components/Integrations/WorkflowDuplicateDialog.vue +63 -0
  90. package/dist/runtime/components/Integrations/WorkflowDuplicateDialog.vue.d.ts +18 -0
  91. package/dist/runtime/components/Integrations/WorkflowEdgeView.d.vue.ts +12 -1
  92. package/dist/runtime/components/Integrations/WorkflowEdgeView.vue +68 -6
  93. package/dist/runtime/components/Integrations/WorkflowEdgeView.vue.d.ts +12 -1
  94. package/dist/runtime/components/Integrations/WorkflowEditor.d.vue.ts +8 -1
  95. package/dist/runtime/components/Integrations/WorkflowEditor.vue +1424 -144
  96. package/dist/runtime/components/Integrations/WorkflowEditor.vue.d.ts +8 -1
  97. package/dist/runtime/components/Integrations/WorkflowHistoryPanel.d.vue.ts +12 -0
  98. package/dist/runtime/components/Integrations/WorkflowHistoryPanel.vue +417 -0
  99. package/dist/runtime/components/Integrations/WorkflowHistoryPanel.vue.d.ts +12 -0
  100. package/dist/runtime/components/Integrations/WorkflowIconPicker.d.vue.ts +10 -0
  101. package/dist/runtime/components/Integrations/WorkflowIconPicker.vue +89 -0
  102. package/dist/runtime/components/Integrations/WorkflowIconPicker.vue.d.ts +10 -0
  103. package/dist/runtime/components/Integrations/WorkflowIconView.d.vue.ts +10 -0
  104. package/dist/runtime/components/Integrations/WorkflowIconView.vue +35 -0
  105. package/dist/runtime/components/Integrations/WorkflowIconView.vue.d.ts +10 -0
  106. package/dist/runtime/components/Integrations/WorkflowNodeView.d.vue.ts +5 -1
  107. package/dist/runtime/components/Integrations/WorkflowNodeView.vue +114 -84
  108. package/dist/runtime/components/Integrations/WorkflowNodeView.vue.d.ts +5 -1
  109. package/dist/runtime/components/Integrations/WorkflowRunReplayPanel.d.vue.ts +12 -0
  110. package/dist/runtime/components/Integrations/WorkflowRunReplayPanel.vue +654 -0
  111. package/dist/runtime/components/Integrations/WorkflowRunReplayPanel.vue.d.ts +12 -0
  112. package/dist/runtime/components/Integrations/WorkflowSettingsDialog.d.vue.ts +36 -0
  113. package/dist/runtime/components/Integrations/WorkflowSettingsDialog.vue +136 -0
  114. package/dist/runtime/components/Integrations/WorkflowSettingsDialog.vue.d.ts +36 -0
  115. package/dist/runtime/components/Integrations/WorkflowTagsInput.d.vue.ts +14 -0
  116. package/dist/runtime/components/Integrations/WorkflowTagsInput.vue +63 -0
  117. package/dist/runtime/components/Integrations/WorkflowTagsInput.vue.d.ts +14 -0
  118. package/dist/runtime/components/Integrations/WorkflowTriggerView.vue +35 -15
  119. package/dist/runtime/components/Integrations/WorkflowVariablesDialog.d.vue.ts +13 -0
  120. package/dist/runtime/components/Integrations/WorkflowVariablesDialog.vue +265 -0
  121. package/dist/runtime/components/Integrations/WorkflowVariablesDialog.vue.d.ts +13 -0
  122. package/dist/runtime/components/Integrations/WorkflowsTable.d.vue.ts +4 -1
  123. package/dist/runtime/components/Integrations/WorkflowsTable.vue +122 -39
  124. package/dist/runtime/components/Integrations/WorkflowsTable.vue.d.ts +4 -1
  125. package/dist/runtime/components/Integrations/workflowLayout.d.ts +5 -0
  126. package/dist/runtime/components/Integrations/workflowLayout.js +5 -3
  127. package/dist/runtime/composables/integrationsApi.d.ts +6 -7
  128. package/dist/runtime/composables/integrationsApi.js +6 -12
  129. package/dist/runtime/composables/useCanvasShortcuts.d.ts +37 -0
  130. package/dist/runtime/composables/useCanvasShortcuts.js +62 -0
  131. package/dist/runtime/composables/useCredentialTypes.js +2 -1
  132. package/dist/runtime/composables/useIntegrationsAudit.js +2 -1
  133. package/dist/runtime/composables/useIntegrationsCredentials.js +2 -1
  134. package/dist/runtime/composables/useIntegrationsDeadLetters.d.ts +22 -0
  135. package/dist/runtime/composables/useIntegrationsDeadLetters.js +50 -0
  136. package/dist/runtime/composables/useIntegrationsNodes.d.ts +4 -0
  137. package/dist/runtime/composables/useIntegrationsNodes.js +8 -4
  138. package/dist/runtime/composables/useIntegrationsRevisions.d.ts +40 -0
  139. package/dist/runtime/composables/useIntegrationsRevisions.js +52 -0
  140. package/dist/runtime/composables/useIntegrationsRuns.d.ts +21 -1
  141. package/dist/runtime/composables/useIntegrationsRuns.js +94 -5
  142. package/dist/runtime/composables/useIntegrationsSecrets.js +2 -1
  143. package/dist/runtime/composables/useIntegrationsTemplates.d.ts +1 -0
  144. package/dist/runtime/composables/useIntegrationsTemplates.js +34 -5
  145. package/dist/runtime/composables/useIntegrationsTriggers.js +2 -1
  146. package/dist/runtime/composables/useIntegrationsWorkflows.d.ts +1 -0
  147. package/dist/runtime/composables/useIntegrationsWorkflows.js +59 -2
  148. package/dist/runtime/composables/useMiniMapLayout.d.ts +34 -0
  149. package/dist/runtime/composables/useMiniMapLayout.js +119 -0
  150. package/dist/runtime/composables/useNodeConfigResolve.d.ts +43 -0
  151. package/dist/runtime/composables/useNodeConfigResolve.js +124 -0
  152. package/dist/runtime/composables/useNodeDrag.d.ts +22 -0
  153. package/dist/runtime/composables/useNodeDrag.js +10 -0
  154. package/dist/runtime/composables/usePaletteLayout.d.ts +41 -0
  155. package/dist/runtime/composables/usePaletteLayout.js +113 -0
  156. package/dist/runtime/data/expressionEditor.d.ts +22 -0
  157. package/dist/runtime/data/expressionEditor.js +40 -0
  158. package/dist/runtime/data/integrationNodeCatalog.js +8 -8
  159. package/dist/runtime/data/nodeInputData.d.ts +37 -0
  160. package/dist/runtime/data/nodeInputData.js +62 -0
  161. package/dist/runtime/data/nodeRunData.d.ts +56 -0
  162. package/dist/runtime/data/nodeRunData.js +91 -0
  163. package/dist/runtime/data/payloadForm.d.ts +44 -0
  164. package/dist/runtime/data/payloadForm.js +126 -0
  165. package/dist/runtime/data/workflowIconCatalog.d.ts +34 -0
  166. package/dist/runtime/data/workflowIconCatalog.js +116 -0
  167. package/dist/runtime/pages/integrations/audit.vue +25 -35
  168. package/dist/runtime/pages/integrations/credentials.vue +117 -122
  169. package/dist/runtime/pages/integrations/index.vue +133 -36
  170. package/dist/runtime/pages/integrations/runs.vue +125 -75
  171. package/dist/runtime/pages/integrations/schedules.vue +5 -16
  172. package/dist/runtime/pages/integrations/secrets.vue +1 -3
  173. package/dist/runtime/pages/integrations/workflows/[id].vue +599 -194
  174. package/dist/runtime/pages/integrations/workflows/index.vue +10 -8
  175. package/dist/runtime/types/integrations.d.ts +190 -4
  176. package/dist/runtime/utils/apiError.d.ts +17 -0
  177. package/dist/runtime/utils/apiError.js +8 -0
  178. package/dist/runtime/utils/credentialState.d.ts +28 -0
  179. package/dist/runtime/utils/credentialState.js +29 -0
  180. package/dist/runtime/utils/localize.d.ts +3 -1
  181. package/dist/runtime/utils/localize.js +1 -2
  182. package/dist/runtime/utils/statusStyle.d.ts +6 -0
  183. package/dist/runtime/utils/statusStyle.js +15 -0
  184. package/package.json +20 -3
  185. package/dist/runtime/components/Integrations/RunDetailDialog.vue +0 -169
  186. package/dist/runtime/pages/integrations/workflows/new.d.vue.ts +0 -2
  187. package/dist/runtime/pages/integrations/workflows/new.vue +0 -153
  188. package/dist/runtime/pages/integrations/workflows/new.vue.d.ts +0 -2
@@ -0,0 +1,342 @@
1
+ <script setup>
2
+ import { computed, reactive, ref, watch } from "vue";
3
+ import {
4
+ CheckCircle as CheckCircle2,
5
+ CloseCircle as XCircle,
6
+ RefreshCircle as Loader2,
7
+ TestTube as TestIcon,
8
+ TrashBinTrash as Trash2
9
+ } from "#solar-icons";
10
+ import {
11
+ Button,
12
+ Dialog,
13
+ DialogContent,
14
+ DialogDescription,
15
+ DialogHeader,
16
+ DialogTitle,
17
+ Input,
18
+ Label,
19
+ Switch,
20
+ Select,
21
+ SelectContent,
22
+ SelectItem,
23
+ SelectTrigger,
24
+ SelectValue
25
+ } from "@revenexx/studio";
26
+ import { toast } from "vue-sonner";
27
+ import { credentialState, relativeTime } from "../../utils/credentialState";
28
+ const props = defineProps({
29
+ open: { type: Boolean, required: true },
30
+ credential: { type: [Object, null], required: true },
31
+ type: { type: [Object, null], required: true }
32
+ });
33
+ const emit = defineEmits(["update:open", "changed"]);
34
+ const { update, remove, test, oauthAuthorizeUrl, load } = useIntegrationsCredentials();
35
+ const tab = ref("credentials");
36
+ const tabs = [
37
+ { key: "credentials", label: "Credentials" },
38
+ { key: "details", label: "Details" }
39
+ ];
40
+ const form = reactive({ name: "", config: {} });
41
+ const sourceActive = ref(false);
42
+ const saving = ref(false);
43
+ const deleting = ref(false);
44
+ const testing = ref(false);
45
+ const liveTest = ref(null);
46
+ const connecting = ref(false);
47
+ function loc(v) {
48
+ if (!v) return "";
49
+ if (typeof v === "string") return v;
50
+ const o = v;
51
+ return o.en ?? o.de ?? Object.values(o)[0] ?? "";
52
+ }
53
+ const typeName = computed(() => loc(props.type?.name) || props.credential?.credential_type_slug || "");
54
+ const isOAuthAuthcode = computed(() => props.type?.auth_kind === "oauth2-authcode");
55
+ const state = computed(() => props.credential ? credentialState(props.credential) : null);
56
+ function fieldsOf(t) {
57
+ return t?.fields ?? [];
58
+ }
59
+ function formatDateTime(iso) {
60
+ if (!iso) return "\u2014";
61
+ try {
62
+ return new Date(iso).toLocaleString();
63
+ } catch {
64
+ return String(iso);
65
+ }
66
+ }
67
+ watch(
68
+ () => [props.open, props.credential?.id],
69
+ () => {
70
+ if (!props.open || !props.credential) return;
71
+ tab.value = "credentials";
72
+ form.name = props.credential.name;
73
+ form.config = { ...props.credential.public_config ?? {} };
74
+ sourceActive.value = false;
75
+ liveTest.value = null;
76
+ },
77
+ { immediate: true }
78
+ );
79
+ function buildConfigPayload() {
80
+ const out = {};
81
+ for (const f of fieldsOf(props.type)) {
82
+ const v = form.config[f.key];
83
+ if (f.type === "secret" && (v === void 0 || v === "")) continue;
84
+ if (v !== void 0) out[f.key] = v;
85
+ }
86
+ return out;
87
+ }
88
+ async function runTest() {
89
+ if (!props.credential) return;
90
+ testing.value = true;
91
+ liveTest.value = null;
92
+ try {
93
+ liveTest.value = await test(props.credential.id);
94
+ await load(true);
95
+ emit("changed");
96
+ } finally {
97
+ testing.value = false;
98
+ }
99
+ }
100
+ async function save() {
101
+ if (!props.credential || !form.name.trim()) return;
102
+ saving.value = true;
103
+ try {
104
+ await update(props.credential.id, { name: form.name.trim(), config: buildConfigPayload() });
105
+ toast.success("Credential updated");
106
+ emit("changed");
107
+ emit("update:open", false);
108
+ } catch (e) {
109
+ toast.error(e?.message ?? "Could not save credential.");
110
+ } finally {
111
+ saving.value = false;
112
+ }
113
+ }
114
+ async function destroy() {
115
+ if (!props.credential) return;
116
+ if (!confirm(`Delete credential "${props.credential.name}"? Workflows referencing it will fail until re-bound.`)) return;
117
+ deleting.value = true;
118
+ try {
119
+ await remove(props.credential.id);
120
+ toast.success("Credential deleted");
121
+ emit("changed");
122
+ emit("update:open", false);
123
+ } catch (e) {
124
+ toast.error(e?.message ?? "Could not delete credential.");
125
+ } finally {
126
+ deleting.value = false;
127
+ }
128
+ }
129
+ async function connect() {
130
+ if (!props.credential) return;
131
+ connecting.value = true;
132
+ try {
133
+ const authorizeUrl = await oauthAuthorizeUrl(props.credential.id);
134
+ const popup = window.open(authorizeUrl, "oauth-connect", "width=600,height=720");
135
+ const onMessage = async (ev) => {
136
+ if (ev.data?.type === "integrations-oauth-complete") {
137
+ window.removeEventListener("message", onMessage);
138
+ await load(true);
139
+ emit("changed");
140
+ toast.success("Connected");
141
+ }
142
+ };
143
+ window.addEventListener("message", onMessage);
144
+ const poll = window.setInterval(async () => {
145
+ if (popup?.closed) {
146
+ window.clearInterval(poll);
147
+ window.removeEventListener("message", onMessage);
148
+ await load(true);
149
+ emit("changed");
150
+ }
151
+ }, 1e3);
152
+ } catch (e) {
153
+ toast.error(e?.message ?? "Could not start OAuth connect.");
154
+ } finally {
155
+ connecting.value = false;
156
+ }
157
+ }
158
+ </script>
159
+
160
+ <template>
161
+ <Dialog :open="open" @update:open="(v) => emit('update:open', v)">
162
+ <DialogContent class="sm:max-w-3xl">
163
+ <DialogHeader>
164
+ <div class="flex items-start gap-3">
165
+ <IntegrationsCredentialLogo :type="type" :size="22" />
166
+ <div class="min-w-0 flex-1">
167
+ <DialogTitle class="truncate">{{ credential?.name }}</DialogTitle>
168
+ <DialogDescription class="text-sm truncate">{{ typeName }}</DialogDescription>
169
+ </div>
170
+ </div>
171
+ </DialogHeader>
172
+
173
+ <div v-if="credential && state">
174
+ <!-- Standard cockpit tab bar (violet underline) -->
175
+ <TabBar :model-value="tab" :tabs="tabs" @update:model-value="(k) => tab = k" />
176
+
177
+ <!-- Tab 1: Credentials -->
178
+ <div v-if="tab === 'credentials'" class="space-y-5 max-h-[62vh] overflow-y-auto pr-1 pt-4">
179
+ <!-- Connection state — the headline answer: are these credentials correct? -->
180
+ <div class="rounded-lg border p-3" :class="state.toneClass">
181
+ <div class="flex items-center gap-2">
182
+ <CheckCircle2 v-if="state.key === 'connected'" :size="16" class="shrink-0" />
183
+ <XCircle v-else-if="state.key === 'failed'" :size="16" class="shrink-0" />
184
+ <span class="font-medium">{{ state.label }}</span>
185
+ <span v-if="state.at" class="text-sm opacity-70">· verified {{ relativeTime(state.at) }}</span>
186
+ <div class="flex-1" />
187
+ <Button
188
+ v-if="isOAuthAuthcode"
189
+ size="sm"
190
+ variant="outline"
191
+ class="bg-white/60"
192
+ :disabled="connecting"
193
+ @click="connect"
194
+ >
195
+ <Loader2 v-if="connecting" :size="13" class="mr-1 animate-spin" />
196
+ Reconnect
197
+ </Button>
198
+ </div>
199
+ <p v-if="state.key === 'failed' && state.message" class="mt-1 text-sm break-words opacity-90">
200
+ {{ state.message }}
201
+ </p>
202
+ <p v-else-if="state.key === 'untested'" class="mt-1 text-sm opacity-80">
203
+ Run a test to verify these credentials actually connect.
204
+ </p>
205
+ </div>
206
+
207
+ <!-- Editable fields -->
208
+ <div class="space-y-3">
209
+ <div class="space-y-1.5">
210
+ <Label for="detail-cred-name">Name</Label>
211
+ <Input id="detail-cred-name" v-model="form.name" class="bg-white" />
212
+ </div>
213
+
214
+ <!-- "Use revenexx …" source picker (SFTP / Mail / API key) -->
215
+ <IntegrationsCredentialRevenexxSource
216
+ :type="type"
217
+ @update:active="(v) => sourceActive = v"
218
+ @apply="(c) => Object.assign(form.config, c)"
219
+ />
220
+
221
+ <div v-for="f in fieldsOf(type)" v-show="!sourceActive" :key="f.key" class="space-y-1.5">
222
+ <Label>{{ loc(f.label) }}{{ f.required ? " *" : "" }}</Label>
223
+
224
+ <Input
225
+ v-if="f.type === 'secret'"
226
+ :model-value="form.config[f.key] ?? ''"
227
+ type="password"
228
+ autocomplete="new-password"
229
+ placeholder="Leave blank to keep current"
230
+ class="bg-white"
231
+ @update:model-value="(v) => form.config[f.key] = v"
232
+ />
233
+ <Input
234
+ v-else-if="f.type === 'number'"
235
+ type="number"
236
+ :model-value="form.config[f.key] ?? ''"
237
+ class="bg-white"
238
+ @update:model-value="(v) => form.config[f.key] = v === '' ? void 0 : Number(v)"
239
+ />
240
+ <div v-else-if="f.type === 'boolean'" class="flex items-center gap-2 pt-1">
241
+ <Switch :model-value="Boolean(form.config[f.key])" @update:model-value="(v) => form.config[f.key] = v" />
242
+ </div>
243
+ <Select
244
+ v-else-if="f.type === 'select'"
245
+ :model-value="form.config[f.key] ?? ''"
246
+ @update:model-value="(v) => form.config[f.key] = v"
247
+ >
248
+ <SelectTrigger class="bg-white">
249
+ <SelectValue placeholder="Pick one" />
250
+ </SelectTrigger>
251
+ <SelectContent>
252
+ <SelectItem v-for="opt in f.options ?? []" :key="String(opt.value)" :value="String(opt.value)">
253
+ {{ loc(opt.label) }}
254
+ </SelectItem>
255
+ </SelectContent>
256
+ </Select>
257
+ <Input
258
+ v-else
259
+ :model-value="form.config[f.key] ?? ''"
260
+ class="bg-white"
261
+ @update:model-value="(v) => form.config[f.key] = v"
262
+ />
263
+ </div>
264
+
265
+ <!-- Live test feedback (from the test the operator just ran) -->
266
+ <div
267
+ v-if="liveTest"
268
+ class="rounded-md px-3 py-2 text-sm"
269
+ :class="liveTest.ok ? 'bg-emerald-500/10 text-emerald-700' : 'bg-rose-500/10 text-rose-700'"
270
+ >
271
+ {{ liveTest.ok ? "Connection ok." : liveTest.error ?? "Connection failed." }}
272
+ </div>
273
+
274
+ <div class="flex items-center gap-2 pt-1">
275
+ <div class="flex-1" />
276
+ <Button variant="outline" :disabled="testing || saving" @click="runTest">
277
+ <Loader2 v-if="testing" :size="14" class="mr-1 animate-spin" />
278
+ <TestIcon v-else :size="14" class="mr-1" />
279
+ Connect
280
+ </Button>
281
+ <Button :disabled="saving || !form.name.trim()" @click="save">
282
+ <Loader2 v-if="saving" :size="14" class="mr-1 animate-spin" />
283
+ Save
284
+ </Button>
285
+ </div>
286
+ </div>
287
+
288
+ </div>
289
+
290
+ <!-- Tab 2: Details -->
291
+ <div v-else class="space-y-5 max-h-[62vh] overflow-y-auto pr-1 pt-4">
292
+ <div class="grid grid-cols-[9rem_1fr] gap-x-4 gap-y-2 text-sm">
293
+ <span class="text-muted-foreground">Type</span>
294
+ <span>{{ typeName }}</span>
295
+
296
+ <span class="text-muted-foreground">Type slug</span>
297
+ <span class="font-mono text-xs">{{ credential.credential_type_slug }}</span>
298
+
299
+ <span class="text-muted-foreground">Version</span>
300
+ <span class="font-mono text-xs">{{ type?.version ?? "\u2014" }}</span>
301
+
302
+ <span class="text-muted-foreground">Auth kind</span>
303
+ <span class="font-mono text-xs">{{ type?.auth_kind ?? "\u2014" }}</span>
304
+
305
+ <span class="text-muted-foreground">Last tested</span>
306
+ <span class="tabular-nums">{{ formatDateTime(credential.last_test_at) }}</span>
307
+
308
+ <span class="text-muted-foreground">Credential ID</span>
309
+ <span class="font-mono text-xs break-all">{{ credential.id }}</span>
310
+
311
+ <span class="text-muted-foreground">Created</span>
312
+ <span class="tabular-nums">{{ formatDateTime(credential.created_at) }}</span>
313
+
314
+ <span class="text-muted-foreground">Updated</span>
315
+ <span class="tabular-nums">{{ formatDateTime(credential.updated_at) }}</span>
316
+ </div>
317
+
318
+ <!-- Danger zone -->
319
+ <div class="rounded-lg border border-rose-500/30 bg-rose-500/[0.03] p-3">
320
+ <div class="text-sm font-semibold text-rose-700">Danger zone</div>
321
+ <div class="mt-1.5 flex items-center justify-between gap-3">
322
+ <p class="text-sm text-muted-foreground">
323
+ Deleting this credential breaks any workflow that references it until it's re-bound.
324
+ </p>
325
+ <Button
326
+ variant="outline"
327
+ size="sm"
328
+ class="shrink-0 border-rose-500/40 text-rose-700 hover:bg-rose-500/10"
329
+ :disabled="deleting"
330
+ @click="destroy"
331
+ >
332
+ <Loader2 v-if="deleting" :size="14" class="mr-1 animate-spin" />
333
+ <Trash2 v-else :size="14" class="mr-1" />
334
+ Delete credential
335
+ </Button>
336
+ </div>
337
+ </div>
338
+ </div>
339
+ </div>
340
+ </DialogContent>
341
+ </Dialog>
342
+ </template>
@@ -0,0 +1,20 @@
1
+ import type { Credential, CredentialType } from '../../types/integrations.js';
2
+ /**
3
+ * Credential detail / edit view — a single scrolling page (no tabs): the
4
+ * connection state up top, the editable fields, then read-only metadata.
5
+ * Self-contained — owns update / delete / test / OAuth reconnect against the
6
+ * Integrations service via the shared composable.
7
+ */
8
+ type __VLS_Props = {
9
+ open: boolean;
10
+ credential: Credential | null;
11
+ type: CredentialType | null;
12
+ };
13
+ declare const _default: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
14
+ "update:open": (args_0: boolean) => any;
15
+ changed: () => any;
16
+ }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
17
+ "onUpdate:open"?: ((args_0: boolean) => any) | undefined;
18
+ onChanged?: (() => any) | undefined;
19
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
20
+ export default _default;
@@ -0,0 +1,14 @@
1
+ import type { CredentialType } from '../../types/integrations.js';
2
+ /**
3
+ * A credential type's brand logo. Renders the `logo` image from the type's
4
+ * `images[]` (a short-lived signed URL from the credential-types API), falling
5
+ * back to a generic key glyph when no image is present or the image fails to
6
+ * load (expired signature / 404). This is the single place that knows how a
7
+ * credential type renders its icon.
8
+ */
9
+ type __VLS_Props = {
10
+ type?: CredentialType | null;
11
+ size?: number;
12
+ };
13
+ declare const _default: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
14
+ export default _default;
@@ -0,0 +1,37 @@
1
+ <script setup>
2
+ import { computed, ref, watch } from "vue";
3
+ import { ShieldKeyhole as KeyIcon } from "#solar-icons";
4
+ const props = defineProps({
5
+ type: { type: [Object, null], required: false },
6
+ size: { type: Number, required: false }
7
+ });
8
+ const box = computed(() => (props.size ?? 16) + 16);
9
+ const glyph = computed(() => props.size ?? 16);
10
+ const logoSrc = computed(() => {
11
+ const imgs = props.type?.images;
12
+ if (!imgs?.length) return null;
13
+ const pick = imgs.find((i) => i.category === "logo") ?? imgs[0];
14
+ return typeof pick?.src === "string" && pick.src ? pick.src : null;
15
+ });
16
+ const failed = ref(false);
17
+ watch(logoSrc, () => {
18
+ failed.value = false;
19
+ });
20
+ const showImg = computed(() => !!logoSrc.value && !failed.value);
21
+ </script>
22
+
23
+ <template>
24
+ <span
25
+ class="flex items-center justify-center overflow-hidden rounded-md bg-slate-100 text-slate-500 shrink-0"
26
+ :style="{ width: `${box}px`, height: `${box}px` }"
27
+ >
28
+ <img
29
+ v-if="showImg"
30
+ :src="logoSrc"
31
+ alt=""
32
+ class="h-full w-full object-contain p-0.5"
33
+ @error="failed = true"
34
+ >
35
+ <KeyIcon v-else :size="glyph" />
36
+ </span>
37
+ </template>
@@ -0,0 +1,14 @@
1
+ import type { CredentialType } from '../../types/integrations.js';
2
+ /**
3
+ * A credential type's brand logo. Renders the `logo` image from the type's
4
+ * `images[]` (a short-lived signed URL from the credential-types API), falling
5
+ * back to a generic key glyph when no image is present or the image fails to
6
+ * load (expired signature / 404). This is the single place that knows how a
7
+ * credential type renders its icon.
8
+ */
9
+ type __VLS_Props = {
10
+ type?: CredentialType | null;
11
+ size?: number;
12
+ };
13
+ declare const _default: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
14
+ export default _default;
@@ -0,0 +1,12 @@
1
+ import type { CredentialType } from '../../types/integrations.js';
2
+ type __VLS_Props = {
3
+ type: CredentialType | null;
4
+ };
5
+ declare const _default: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
6
+ "update:active": (args_0: boolean) => any;
7
+ apply: (args_0: Record<string, unknown>) => any;
8
+ }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
9
+ "onUpdate:active"?: ((args_0: boolean) => any) | undefined;
10
+ onApply?: ((args_0: Record<string, unknown>) => any) | undefined;
11
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
12
+ export default _default;