@revenexx/studio-integrations 0.1.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 (106) hide show
  1. package/README.md +51 -0
  2. package/dist/module.d.mts +6 -0
  3. package/dist/module.json +9 -0
  4. package/dist/module.mjs +45 -0
  5. package/dist/runtime/components/Integrations/Layout.d.vue.ts +12 -0
  6. package/dist/runtime/components/Integrations/Layout.vue +20 -0
  7. package/dist/runtime/components/Integrations/Layout.vue.d.ts +12 -0
  8. package/dist/runtime/components/Integrations/NodeInspector.d.vue.ts +16 -0
  9. package/dist/runtime/components/Integrations/NodeInspector.vue +450 -0
  10. package/dist/runtime/components/Integrations/NodeInspector.vue.d.ts +16 -0
  11. package/dist/runtime/components/Integrations/NodePalette.d.vue.ts +2 -0
  12. package/dist/runtime/components/Integrations/NodePalette.vue +140 -0
  13. package/dist/runtime/components/Integrations/NodePalette.vue.d.ts +2 -0
  14. package/dist/runtime/components/Integrations/RunDetailDialog.d.vue.ts +11 -0
  15. package/dist/runtime/components/Integrations/RunDetailDialog.vue +169 -0
  16. package/dist/runtime/components/Integrations/RunDetailDialog.vue.d.ts +11 -0
  17. package/dist/runtime/components/Integrations/Sidebar.d.vue.ts +2 -0
  18. package/dist/runtime/components/Integrations/Sidebar.vue +32 -0
  19. package/dist/runtime/components/Integrations/Sidebar.vue.d.ts +2 -0
  20. package/dist/runtime/components/Integrations/WorkflowEdgeView.d.vue.ts +23 -0
  21. package/dist/runtime/components/Integrations/WorkflowEdgeView.vue +86 -0
  22. package/dist/runtime/components/Integrations/WorkflowEdgeView.vue.d.ts +23 -0
  23. package/dist/runtime/components/Integrations/WorkflowEditor.d.vue.ts +14 -0
  24. package/dist/runtime/components/Integrations/WorkflowEditor.vue +419 -0
  25. package/dist/runtime/components/Integrations/WorkflowEditor.vue.d.ts +14 -0
  26. package/dist/runtime/components/Integrations/WorkflowNodeView.d.vue.ts +11 -0
  27. package/dist/runtime/components/Integrations/WorkflowNodeView.vue +200 -0
  28. package/dist/runtime/components/Integrations/WorkflowNodeView.vue.d.ts +11 -0
  29. package/dist/runtime/components/Integrations/WorkflowTriggerView.d.vue.ts +8 -0
  30. package/dist/runtime/components/Integrations/WorkflowTriggerView.vue +82 -0
  31. package/dist/runtime/components/Integrations/WorkflowTriggerView.vue.d.ts +8 -0
  32. package/dist/runtime/components/Integrations/WorkflowsTable.d.vue.ts +6 -0
  33. package/dist/runtime/components/Integrations/WorkflowsTable.vue +268 -0
  34. package/dist/runtime/components/Integrations/WorkflowsTable.vue.d.ts +6 -0
  35. package/dist/runtime/components/Integrations/workflowLayout.d.ts +17 -0
  36. package/dist/runtime/components/Integrations/workflowLayout.js +29 -0
  37. package/dist/runtime/composables/integrationsApi.d.ts +13 -0
  38. package/dist/runtime/composables/integrationsApi.js +55 -0
  39. package/dist/runtime/composables/useCredentialTypes.d.ts +19 -0
  40. package/dist/runtime/composables/useCredentialTypes.js +47 -0
  41. package/dist/runtime/composables/useIntegrationsAudit.d.ts +26 -0
  42. package/dist/runtime/composables/useIntegrationsAudit.js +39 -0
  43. package/dist/runtime/composables/useIntegrationsCredentials.d.ts +43 -0
  44. package/dist/runtime/composables/useIntegrationsCredentials.js +98 -0
  45. package/dist/runtime/composables/useIntegrationsFocus.d.ts +16 -0
  46. package/dist/runtime/composables/useIntegrationsFocus.js +17 -0
  47. package/dist/runtime/composables/useIntegrationsNodes.d.ts +6 -0
  48. package/dist/runtime/composables/useIntegrationsNodes.js +32 -0
  49. package/dist/runtime/composables/useIntegrationsRuns.d.ts +31 -0
  50. package/dist/runtime/composables/useIntegrationsRuns.js +109 -0
  51. package/dist/runtime/composables/useIntegrationsSchedules.d.ts +14 -0
  52. package/dist/runtime/composables/useIntegrationsSchedules.js +32 -0
  53. package/dist/runtime/composables/useIntegrationsSecrets.d.ts +27 -0
  54. package/dist/runtime/composables/useIntegrationsSecrets.js +76 -0
  55. package/dist/runtime/composables/useIntegrationsTemplates.d.ts +19 -0
  56. package/dist/runtime/composables/useIntegrationsTemplates.js +141 -0
  57. package/dist/runtime/composables/useIntegrationsTriggers.d.ts +39 -0
  58. package/dist/runtime/composables/useIntegrationsTriggers.js +39 -0
  59. package/dist/runtime/composables/useIntegrationsWorkflows.d.ts +28 -0
  60. package/dist/runtime/composables/useIntegrationsWorkflows.js +152 -0
  61. package/dist/runtime/data/integrationNodeCatalog.d.ts +41 -0
  62. package/dist/runtime/data/integrationNodeCatalog.js +67 -0
  63. package/dist/runtime/data/integrationRuns.d.ts +7 -0
  64. package/dist/runtime/data/integrationRuns.js +109 -0
  65. package/dist/runtime/data/integrationTemplates.d.ts +11 -0
  66. package/dist/runtime/data/integrationTemplates.js +9 -0
  67. package/dist/runtime/data/integrationWorkflowSamples.d.ts +18 -0
  68. package/dist/runtime/data/integrationWorkflowSamples.js +318 -0
  69. package/dist/runtime/pages/integrations/audit.d.vue.ts +2 -0
  70. package/dist/runtime/pages/integrations/audit.vue +156 -0
  71. package/dist/runtime/pages/integrations/audit.vue.d.ts +2 -0
  72. package/dist/runtime/pages/integrations/credentials/oauth-complete.d.vue.ts +2 -0
  73. package/dist/runtime/pages/integrations/credentials/oauth-complete.vue +28 -0
  74. package/dist/runtime/pages/integrations/credentials/oauth-complete.vue.d.ts +2 -0
  75. package/dist/runtime/pages/integrations/credentials.d.vue.ts +2 -0
  76. package/dist/runtime/pages/integrations/credentials.vue +403 -0
  77. package/dist/runtime/pages/integrations/credentials.vue.d.ts +2 -0
  78. package/dist/runtime/pages/integrations/index.d.vue.ts +2 -0
  79. package/dist/runtime/pages/integrations/index.vue +226 -0
  80. package/dist/runtime/pages/integrations/index.vue.d.ts +2 -0
  81. package/dist/runtime/pages/integrations/runs.d.vue.ts +2 -0
  82. package/dist/runtime/pages/integrations/runs.vue +100 -0
  83. package/dist/runtime/pages/integrations/runs.vue.d.ts +2 -0
  84. package/dist/runtime/pages/integrations/schedules.d.vue.ts +2 -0
  85. package/dist/runtime/pages/integrations/schedules.vue +111 -0
  86. package/dist/runtime/pages/integrations/schedules.vue.d.ts +2 -0
  87. package/dist/runtime/pages/integrations/secrets.d.vue.ts +2 -0
  88. package/dist/runtime/pages/integrations/secrets.vue +248 -0
  89. package/dist/runtime/pages/integrations/secrets.vue.d.ts +2 -0
  90. package/dist/runtime/pages/integrations/workflows/[id].d.vue.ts +2 -0
  91. package/dist/runtime/pages/integrations/workflows/[id].vue +326 -0
  92. package/dist/runtime/pages/integrations/workflows/[id].vue.d.ts +2 -0
  93. package/dist/runtime/pages/integrations/workflows/index.d.vue.ts +2 -0
  94. package/dist/runtime/pages/integrations/workflows/index.vue +31 -0
  95. package/dist/runtime/pages/integrations/workflows/index.vue.d.ts +2 -0
  96. package/dist/runtime/pages/integrations/workflows/new.d.vue.ts +2 -0
  97. package/dist/runtime/pages/integrations/workflows/new.vue +153 -0
  98. package/dist/runtime/pages/integrations/workflows/new.vue.d.ts +2 -0
  99. package/dist/runtime/types/integrations.d.ts +427 -0
  100. package/dist/runtime/types/integrations.js +0 -0
  101. package/dist/runtime/utils/localize.d.ts +9 -0
  102. package/dist/runtime/utils/localize.js +21 -0
  103. package/dist/runtime/utils/statusStyle.d.ts +15 -0
  104. package/dist/runtime/utils/statusStyle.js +78 -0
  105. package/dist/types.d.mts +9 -0
  106. package/package.json +46 -0
@@ -0,0 +1,318 @@
1
+ export const SAMPLE_WORKFLOWS = [
2
+ // -----------------------------------------------------------------------
3
+ // 1) Manual REST call — mirrors manual-rest-call.json
4
+ // -----------------------------------------------------------------------
5
+ {
6
+ id: 1,
7
+ name: "Manual REST GET",
8
+ description: "User-triggered call to a REST endpoint configured via workflow variables.",
9
+ blob_definition_version: "v0-draft",
10
+ active: true,
11
+ revision: 4,
12
+ created_at: "2026-04-12T08:00:00Z",
13
+ updated_at: "2026-05-08T02:14:11Z",
14
+ blob: {
15
+ nodeManifestVersion: "v0-draft",
16
+ name: { en: "Manual REST GET", de: "Manueller REST-Abruf" },
17
+ description: {
18
+ en: "User-triggered call to a REST endpoint configured via workflow variables.",
19
+ de: "Benutzerausgel\xF6ster Aufruf eines REST-Endpunkts, konfiguriert \xFCber Workflow-Variablen."
20
+ },
21
+ variables: {
22
+ endpoint: {
23
+ type: "string",
24
+ default: "https://api.example.com/health",
25
+ description: { en: "Target URL", de: "Ziel-URL" }
26
+ },
27
+ timeoutSeconds: { type: "number", default: 30 }
28
+ },
29
+ triggers: [
30
+ {
31
+ id: "manual-1",
32
+ type: "manual",
33
+ name: { en: "Run now", de: "Jetzt ausf\xFChren" },
34
+ config: {}
35
+ }
36
+ ],
37
+ nodes: [
38
+ {
39
+ id: "fetch",
40
+ nodeSlug: "rest-get",
41
+ nodeVersion: "1.0.0",
42
+ config: {
43
+ url: "${{ vars.endpoint }}",
44
+ timeout: "${{ vars.timeoutSeconds }}"
45
+ },
46
+ position: { x: 220, y: 100 },
47
+ retry: { maxAttempts: 3, backoffSeconds: 2, strategy: "exponential" }
48
+ }
49
+ ],
50
+ edges: [
51
+ { id: "e1", from: { nodeId: "manual-1", output: "default" }, to: { nodeId: "fetch" } }
52
+ ]
53
+ }
54
+ },
55
+ // -----------------------------------------------------------------------
56
+ // 2) Webhook receiver with error routing — mirrors webhook-with-error-routing.json
57
+ // -----------------------------------------------------------------------
58
+ {
59
+ id: 2,
60
+ name: "Webhook order receiver",
61
+ description: "Receives Stripe order webhooks, validates the payload, and forwards to the fulfillment API. Errors flow into a notification node via the reserved error output.",
62
+ blob_definition_version: "v0-draft",
63
+ active: true,
64
+ revision: 3,
65
+ created_at: "2026-04-22T11:30:00Z",
66
+ updated_at: "2026-05-09T15:02:33Z",
67
+ blob: {
68
+ nodeManifestVersion: "v0-draft",
69
+ name: "Webhook order receiver",
70
+ description: "Receives Stripe order webhooks, validates the payload, and forwards to the fulfillment API. Errors flow into a notification node via the reserved error output.",
71
+ triggers: [
72
+ {
73
+ id: "stripe-hook",
74
+ type: "webhook",
75
+ name: "Stripe order created",
76
+ config: {
77
+ path: "/webhooks/stripe/orders",
78
+ method: "POST",
79
+ secretRef: "STRIPE_WEBHOOK_SECRET"
80
+ }
81
+ }
82
+ ],
83
+ nodes: [
84
+ {
85
+ id: "validate",
86
+ nodeSlug: "control-if",
87
+ nodeVersion: "1.0.0",
88
+ config: { condition: "${{ trigger.payload.type }}" },
89
+ position: { x: 260, y: 80 }
90
+ },
91
+ {
92
+ id: "forward",
93
+ nodeSlug: "rest-get",
94
+ nodeVersion: "1.0.0",
95
+ config: {
96
+ url: "https://fulfillment.internal/orders",
97
+ "bearer-token": "FULFILLMENT_API_TOKEN"
98
+ },
99
+ position: { x: 520, y: 0 },
100
+ retry: { maxAttempts: 4, backoffSeconds: 5, strategy: "linear" }
101
+ },
102
+ {
103
+ id: "notify-failure",
104
+ nodeSlug: "rest-get",
105
+ nodeVersion: "1.0.0",
106
+ config: { url: "https://alerts.internal/notify" },
107
+ position: { x: 760, y: 200 }
108
+ }
109
+ ],
110
+ edges: [
111
+ { id: "e1", from: { nodeId: "stripe-hook", output: "default" }, to: { nodeId: "validate" } },
112
+ { id: "e2", from: { nodeId: "validate", output: "true" }, to: { nodeId: "forward" } },
113
+ { id: "e3", from: { nodeId: "forward", output: "error" }, to: { nodeId: "notify-failure" } }
114
+ ]
115
+ }
116
+ },
117
+ // -----------------------------------------------------------------------
118
+ // 3) Scheduled ETL with branching — mirrors scheduled-with-branching.json
119
+ // -----------------------------------------------------------------------
120
+ {
121
+ id: 3,
122
+ name: "Daily ETL with branching",
123
+ description: "Pulls orders from upstream, branches per status with control-switch.",
124
+ blob_definition_version: "v0-draft",
125
+ active: true,
126
+ revision: 7,
127
+ created_at: "2026-04-12T08:00:00Z",
128
+ updated_at: "2026-05-08T02:14:11Z",
129
+ blob: {
130
+ nodeManifestVersion: "v0-draft",
131
+ name: "Daily ETL with branching",
132
+ triggers: [
133
+ {
134
+ id: "nightly",
135
+ type: "schedule",
136
+ config: { cron: "0 3 * * *", timezone: "Europe/Berlin" }
137
+ }
138
+ ],
139
+ variables: {
140
+ source: { type: "string", default: "primary", required: false }
141
+ },
142
+ nodes: [
143
+ {
144
+ id: "fetch",
145
+ nodeSlug: "rest-get",
146
+ nodeVersion: "1.0.0",
147
+ config: { url: "https://api.example.com/orders?source=${{ vars.source }}" },
148
+ position: { x: 220, y: 80 },
149
+ retry: { maxAttempts: 5, backoffSeconds: 1, strategy: "exponential" }
150
+ },
151
+ {
152
+ id: "route",
153
+ nodeSlug: "control-switch",
154
+ nodeVersion: "1.0.0",
155
+ config: {
156
+ expression: "${{ nodes.fetch.outputs.success }}",
157
+ cases: ["paid", "pending", "failed"]
158
+ },
159
+ position: { x: 480, y: 80 }
160
+ },
161
+ {
162
+ id: "ship",
163
+ nodeSlug: "rest-get",
164
+ nodeVersion: "1.0.0",
165
+ config: { url: "https://api.example.com/ship" },
166
+ position: { x: 760, y: 0 }
167
+ },
168
+ {
169
+ id: "alert",
170
+ nodeSlug: "rest-get",
171
+ nodeVersion: "1.0.0",
172
+ config: { url: "https://api.example.com/alert" },
173
+ position: { x: 760, y: 200 }
174
+ }
175
+ ],
176
+ edges: [
177
+ { id: "e1", from: { nodeId: "nightly", output: "default" }, to: { nodeId: "fetch" } },
178
+ { id: "e2", from: { nodeId: "fetch", output: "success" }, to: { nodeId: "route" } },
179
+ { id: "e3", from: { nodeId: "route", output: "paid" }, to: { nodeId: "ship" } },
180
+ { id: "e4", from: { nodeId: "fetch", output: "error" }, to: { nodeId: "alert" } }
181
+ ]
182
+ }
183
+ },
184
+ // -----------------------------------------------------------------------
185
+ // 4) Empty draft — exists to demonstrate the "Draft" status group
186
+ // -----------------------------------------------------------------------
187
+ {
188
+ id: 4,
189
+ name: "Empty draft",
190
+ description: "No nodes yet. Drag from the palette to start.",
191
+ blob_definition_version: "v0-draft",
192
+ active: false,
193
+ revision: 0,
194
+ created_at: "2026-05-10T16:00:00Z",
195
+ updated_at: "2026-05-10T16:00:00Z",
196
+ blob: {
197
+ nodeManifestVersion: "v0-draft",
198
+ name: "Empty draft",
199
+ triggers: [
200
+ { id: "trg-1", type: "manual", config: {} }
201
+ ],
202
+ nodes: [],
203
+ edges: []
204
+ }
205
+ }
206
+ ];
207
+ export const SAMPLE_SECRETS = [
208
+ { key: "STRIPE_WEBHOOK_SECRET", created_at: "2026-04-01T08:00:00Z", updated_at: "2026-04-01T08:00:00Z" },
209
+ { key: "FULFILLMENT_API_TOKEN", created_at: "2026-04-15T11:30:00Z", updated_at: "2026-04-15T11:30:00Z" },
210
+ { key: "ERP_API_KEY", created_at: "2026-03-12T09:14:00Z", updated_at: "2026-05-01T14:00:00Z" },
211
+ { key: "SLACK_WEBHOOK_URL", created_at: "2026-02-04T16:00:00Z", updated_at: "2026-02-04T16:00:00Z" },
212
+ { key: "HUBSPOT_ACCESS_TOKEN", created_at: "2026-04-22T12:00:00Z", updated_at: "2026-04-22T12:00:00Z" }
213
+ ];
214
+ export const CONNECTION_TEMPLATES = [
215
+ {
216
+ vendor: "shopify",
217
+ name: "Shopify",
218
+ category: "shop",
219
+ icon: "ShoppingBag",
220
+ description: "Ingest orders, customers and products from a Shopify store.",
221
+ requiredSecrets: [
222
+ { key: "SHOPIFY_ACCESS_TOKEN", label: "Admin API token", sensitive: true },
223
+ { key: "SHOPIFY_WEBHOOK_SECRET", label: "Webhook signing secret", sensitive: true }
224
+ ]
225
+ },
226
+ {
227
+ vendor: "sap-business-one",
228
+ name: "SAP Business One",
229
+ category: "erp",
230
+ icon: "Server",
231
+ description: "Sync products, prices and orders with SAP Business One.",
232
+ requiredSecrets: [
233
+ { key: "SAP_B1_USER", label: "Service account", sensitive: false },
234
+ { key: "SAP_B1_PASSWORD", label: "Service password", sensitive: true }
235
+ ]
236
+ },
237
+ {
238
+ vendor: "hubspot",
239
+ name: "HubSpot",
240
+ category: "crm",
241
+ icon: "Contact",
242
+ description: "Two-way sync for contacts and deals.",
243
+ requiredSecrets: [{ key: "HUBSPOT_ACCESS_TOKEN", label: "Private app token", sensitive: true }]
244
+ },
245
+ {
246
+ vendor: "akeneo",
247
+ name: "Akeneo PIM",
248
+ category: "pim",
249
+ icon: "Layers",
250
+ description: "Pull product data and enrichment from Akeneo.",
251
+ requiredSecrets: [
252
+ { key: "AKENEO_CLIENT_ID", label: "Client ID", sensitive: false },
253
+ { key: "AKENEO_CLIENT_SECRET", label: "Client secret", sensitive: true }
254
+ ]
255
+ },
256
+ {
257
+ vendor: "slack",
258
+ name: "Slack",
259
+ category: "other",
260
+ icon: "MessageSquare",
261
+ description: "Post notifications and alerts to Slack channels.",
262
+ requiredSecrets: [{ key: "SLACK_WEBHOOK_URL", label: "Incoming webhook URL", sensitive: true }]
263
+ },
264
+ {
265
+ vendor: "stripe",
266
+ name: "Stripe",
267
+ category: "other",
268
+ icon: "CreditCard",
269
+ description: "Receive payment events from Stripe.",
270
+ requiredSecrets: [{ key: "STRIPE_WEBHOOK_SECRET", label: "Webhook signing secret", sensitive: true }]
271
+ }
272
+ ];
273
+ export const SAMPLE_CONNECTIONS = [
274
+ {
275
+ id: "conn-stripe-1",
276
+ vendor: "stripe",
277
+ name: "Stripe \xB7 live",
278
+ status: "connected",
279
+ lastVerifiedAt: "2026-05-10T07:14:11Z",
280
+ secretRefs: ["STRIPE_WEBHOOK_SECRET"],
281
+ config: {},
282
+ createdAt: "2026-04-15T11:30:00Z",
283
+ updatedAt: "2026-05-09T15:02:33Z"
284
+ },
285
+ {
286
+ id: "conn-sap-1",
287
+ vendor: "sap-business-one",
288
+ name: "SAP Business One \xB7 production",
289
+ status: "misconfigured",
290
+ lastVerifiedAt: "2026-05-04T10:00:00Z",
291
+ secretRefs: [],
292
+ config: { host: "sap.acme.test" },
293
+ createdAt: "2026-04-22T11:00:00Z",
294
+ updatedAt: "2026-05-04T10:00:00Z"
295
+ },
296
+ {
297
+ id: "conn-hubspot-1",
298
+ vendor: "hubspot",
299
+ name: "HubSpot \xB7 Sales",
300
+ status: "connected",
301
+ lastVerifiedAt: "2026-05-10T08:00:00Z",
302
+ secretRefs: ["HUBSPOT_ACCESS_TOKEN"],
303
+ config: { portalId: "23456789" },
304
+ createdAt: "2026-04-22T12:00:00Z",
305
+ updatedAt: "2026-04-22T12:00:00Z"
306
+ },
307
+ {
308
+ id: "conn-slack-1",
309
+ vendor: "slack",
310
+ name: "Slack \xB7 #ops",
311
+ status: "connected",
312
+ lastVerifiedAt: "2026-05-10T09:00:00Z",
313
+ secretRefs: ["SLACK_WEBHOOK_URL"],
314
+ config: { channel: "#ops" },
315
+ createdAt: "2026-02-04T16:00:00Z",
316
+ updatedAt: "2026-02-04T16:00:00Z"
317
+ }
318
+ ];
@@ -0,0 +1,2 @@
1
+ declare const _default: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
2
+ export default _default;
@@ -0,0 +1,156 @@
1
+ <script setup>
2
+ import {
3
+ ShieldCheck,
4
+ Magnifier as Search,
5
+ CloseCircle as Close,
6
+ Copy as CopyIcon,
7
+ ShieldKeyhole as Credential,
8
+ Key as Secret,
9
+ Programming as Workflow,
10
+ Bolt as Trigger,
11
+ Play as Run
12
+ } from "#solar-icons";
13
+ import { Badge, Button, Input } from "@revenexx/studio";
14
+ import { toast } from "vue-sonner";
15
+ useHead({ title: "Audit Log \xB7 Integration Studio" });
16
+ const { entries, loading, error, hasMore, load, loadMore } = useIntegrationsAudit();
17
+ const resourceTypes = ["credential", "secret", "workflow", "trigger", "workflow_run"];
18
+ const actions = ["create", "update", "delete", "run.started", "run.finished", "run.failed"];
19
+ const resourceType = ref(null);
20
+ const action = ref(null);
21
+ const actor = ref("");
22
+ function filters() {
23
+ return { resource_type: resourceType.value ?? void 0, action: action.value ?? void 0, sub: actor.value.trim() || void 0 };
24
+ }
25
+ function reload() {
26
+ void load(filters());
27
+ }
28
+ onMounted(reload);
29
+ watch([resourceType, action], reload);
30
+ const resourceIcon = { credential: Credential, secret: Secret, workflow: Workflow, trigger: Trigger, workflow_run: Run };
31
+ function actionTone(a) {
32
+ if (a === "create" || a === "run.finished") return "bg-emerald-500/10 text-emerald-700 border-emerald-500/20";
33
+ if (a === "update") return "bg-amber-500/10 text-amber-700 border-amber-500/20";
34
+ if (a === "delete" || a === "run.failed") return "bg-rose-500/10 text-rose-700 border-rose-500/20";
35
+ return "bg-violet-500/10 text-violet-700 border-violet-500/20";
36
+ }
37
+ function prettyAction(a) {
38
+ return a.replace(".", " ");
39
+ }
40
+ function prettyType(t) {
41
+ return t.replace("_", " ");
42
+ }
43
+ function resourceName(e) {
44
+ return e.metadata?.name || e.resource_id || "\u2014";
45
+ }
46
+ function fmtDate(iso) {
47
+ return iso ? new Date(iso).toLocaleString() : "\u2014";
48
+ }
49
+ const columns = [
50
+ { key: "action", label: "Action", width: "130px", sortable: true, sortGetter: (e) => e.action },
51
+ { key: "resource", label: "Resource", sortable: true, sortGetter: (e) => `${e.resource_type} ${resourceName(e)}`, searchGetter: (e) => `${e.resource_type} ${resourceName(e)} ${e.action} ${e.actor_sub ?? ""}` },
52
+ { key: "actor", label: "Actor", width: "170px", sortable: true, sortGetter: (e) => e.actor_sub ?? "" },
53
+ { key: "when", label: "When", width: "180px", sortable: true, sortGetter: (e) => e.created_at }
54
+ ];
55
+ const selected = ref(null);
56
+ function copy(t) {
57
+ if (t && navigator.clipboard) {
58
+ navigator.clipboard.writeText(t);
59
+ toast.success("Copied");
60
+ }
61
+ }
62
+ </script>
63
+
64
+ <template>
65
+ <IntegrationsLayout>
66
+ <PageHeader title="Audit Log" description="Who changed what, and when. Append-only, per-tenant — credentials, secrets, workflows, triggers and runs." />
67
+
68
+ <div class="p-4 space-y-3">
69
+ <!-- Filters -->
70
+ <div class="flex flex-wrap items-center gap-2">
71
+ <div class="flex flex-wrap gap-1.5">
72
+ <button class="rounded-full border px-3 py-1 text-xs transition-colors" :class="!resourceType ? 'border-primary/40 bg-primary/10 text-primary' : 'border-black/[0.06] text-muted-foreground hover:bg-black/[0.03]'" @click="resourceType = null">All</button>
73
+ <button v-for="t in resourceTypes" :key="t" class="rounded-full border px-3 py-1 text-xs capitalize transition-colors" :class="resourceType === t ? 'border-primary/40 bg-primary/10 text-primary' : 'border-black/[0.06] text-muted-foreground hover:bg-black/[0.03]'" @click="resourceType = t">{{ prettyType(t) }}</button>
74
+ </div>
75
+ <div class="flex-1" />
76
+ <select v-model="action" class="h-9 rounded-md border border-black/[0.1] bg-white px-2 text-sm">
77
+ <option :value="null">All actions</option>
78
+ <option v-for="a in actions" :key="a" :value="a">{{ prettyAction(a) }}</option>
79
+ </select>
80
+ <div class="relative">
81
+ <Search :size="14" class="absolute left-2.5 top-2.5 text-muted-foreground" />
82
+ <Input v-model="actor" class="h-9 w-44 pl-8 text-sm" placeholder="Actor (sub)…" @keydown.enter="reload" />
83
+ </div>
84
+ </div>
85
+
86
+ <p v-if="error" class="text-sm text-rose-700">{{ error }}</p>
87
+
88
+ <DataTable
89
+ :data="entries"
90
+ :columns="columns"
91
+ :row-key="(e) => e.id"
92
+ :default-sort="{ column: 'when', direction: 'desc' }"
93
+ :search="{ placeholder: 'Search this page\u2026' }"
94
+ :empty-text="loading ? 'Loading\u2026' : 'No audit entries match.'"
95
+ @row-click="(e) => selected = e"
96
+ >
97
+ <template #cell-action="{ row }">
98
+ <Badge variant="outline" :class="['text-xs border', actionTone(row.action)]">{{ prettyAction(row.action) }}</Badge>
99
+ </template>
100
+ <template #cell-resource="{ row }">
101
+ <div class="flex items-center gap-2 min-w-0">
102
+ <component :is="resourceIcon[row.resource_type] ?? ShieldCheck" :size="14" class="text-muted-foreground shrink-0" />
103
+ <div class="min-w-0">
104
+ <div class="text-sm text-[#0B0522] truncate">{{ resourceName(row) }}</div>
105
+ <div class="text-xs text-muted-foreground capitalize">{{ prettyType(row.resource_type) }}<span v-if="row.resource_id"> · #{{ row.resource_id }}</span></div>
106
+ </div>
107
+ </div>
108
+ </template>
109
+ <template #cell-actor="{ row }">
110
+ <span class="text-sm font-mono text-muted-foreground truncate">{{ row.actor_sub ?? "system" }}</span>
111
+ </template>
112
+ <template #cell-when="{ row }">
113
+ <span class="text-sm text-muted-foreground">{{ fmtDate(row.created_at) }}</span>
114
+ </template>
115
+ </DataTable>
116
+
117
+ <div v-if="hasMore" class="flex justify-center pt-1">
118
+ <Button variant="outline" size="sm" :disabled="loading" @click="loadMore(filters())">{{ loading ? "Loading\u2026" : "Load more" }}</Button>
119
+ </div>
120
+ </div>
121
+
122
+ <!-- Detail flyout -->
123
+ <Transition name="drawer">
124
+ <div v-if="selected" class="fixed inset-0 z-50 flex" @keydown.esc="selected = null">
125
+ <div class="flex-1 bg-black/20 backdrop-blur-[1px]" @click="selected = null" />
126
+ <aside class="w-full max-w-md overflow-y-auto border-l border-black/[0.06] bg-white shadow-xl">
127
+ <div class="sticky top-0 z-10 flex items-center gap-2 border-b border-black/[0.06] bg-white/90 backdrop-blur px-4 py-3">
128
+ <Badge variant="outline" :class="['text-xs border', actionTone(selected.action)]">{{ prettyAction(selected.action) }}</Badge>
129
+ <span class="text-sm font-semibold text-[#0B0522] capitalize">{{ prettyType(selected.resource_type) }}</span>
130
+ <Button variant="ghost" size="sm" class="ml-auto h-7 w-7 p-0" @click="selected = null"><Close :size="15" /></Button>
131
+ </div>
132
+ <div class="p-4 space-y-4 text-sm">
133
+ <div class="space-y-1.5">
134
+ <div class="flex justify-between gap-2"><span class="text-muted-foreground">Resource</span><span class="font-medium text-right">{{ resourceName(selected) }}</span></div>
135
+ <div v-if="selected.resource_id" class="flex justify-between gap-2"><span class="text-muted-foreground">ID</span><code class="font-mono text-xs">{{ selected.resource_id }}</code></div>
136
+ <div class="flex justify-between gap-2"><span class="text-muted-foreground">Actor</span><code class="font-mono text-xs">{{ selected.actor_sub ?? "system" }}</code></div>
137
+ <div class="flex justify-between gap-2"><span class="text-muted-foreground">When</span><span>{{ fmtDate(selected.created_at) }}</span></div>
138
+ <div v-if="selected.tenant_id" class="flex justify-between gap-2"><span class="text-muted-foreground">Tenant</span><span>{{ selected.tenant_id }}</span></div>
139
+ </div>
140
+ <div v-if="selected.metadata && Object.keys(selected.metadata).length">
141
+ <div class="mb-1 flex items-center justify-between">
142
+ <span class="text-[11px] font-medium uppercase tracking-wide text-muted-foreground">Details</span>
143
+ <button class="inline-flex items-center gap-1 text-xs text-muted-foreground hover:text-foreground" @click="copy(JSON.stringify(selected.metadata, null, 2))"><CopyIcon :size="12" /> copy</button>
144
+ </div>
145
+ <pre class="overflow-x-auto rounded-md bg-[#0B0522] p-3 text-xs leading-relaxed text-[#E6E1F5]"><code>{{ JSON.stringify(selected.metadata, null, 2) }}</code></pre>
146
+ </div>
147
+ </div>
148
+ </aside>
149
+ </div>
150
+ </Transition>
151
+ </IntegrationsLayout>
152
+ </template>
153
+
154
+ <style scoped>
155
+ .drawer-enter-active,.drawer-leave-active{transition:opacity .2s ease}.drawer-enter-active aside,.drawer-leave-active aside{transition:transform .25s cubic-bezier(.22,1,.36,1)}.drawer-enter-from,.drawer-leave-to{opacity:0}.drawer-enter-from aside,.drawer-leave-to aside{transform:translateX(100%)}
156
+ </style>
@@ -0,0 +1,2 @@
1
+ declare const _default: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
2
+ export default _default;
@@ -0,0 +1,2 @@
1
+ declare const _default: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
2
+ export default _default;
@@ -0,0 +1,28 @@
1
+ <script setup>
2
+ import { onMounted, ref } from "vue";
3
+ definePageMeta({ layout: false });
4
+ const closing = ref(true);
5
+ onMounted(() => {
6
+ try {
7
+ window.opener?.postMessage({ type: "integrations-oauth-complete" }, window.location.origin);
8
+ } catch {
9
+ }
10
+ window.setTimeout(() => {
11
+ window.close();
12
+ closing.value = false;
13
+ }, 400);
14
+ });
15
+ </script>
16
+
17
+ <template>
18
+ <div class="flex min-h-screen items-center justify-center bg-white text-center">
19
+ <div class="space-y-2">
20
+ <p class="text-sm font-medium text-foreground">
21
+ Connection complete
22
+ </p>
23
+ <p class="text-sm text-muted-foreground">
24
+ {{ closing ? "You can close this window." : "Done \u2014 closing\u2026" }}
25
+ </p>
26
+ </div>
27
+ </div>
28
+ </template>
@@ -0,0 +1,2 @@
1
+ declare const _default: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
2
+ export default _default;
@@ -0,0 +1,2 @@
1
+ declare const _default: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
2
+ export default _default;