@revenexx/studio-integrations 0.1.3 → 0.2.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/dist/module.json +1 -1
- package/dist/runtime/components/Integrations/CredentialCreateDialog.d.vue.ts +13 -0
- package/dist/runtime/components/Integrations/CredentialCreateDialog.vue +279 -0
- package/dist/runtime/components/Integrations/CredentialCreateDialog.vue.d.ts +13 -0
- package/dist/runtime/components/Integrations/CredentialDetailDialog.d.vue.ts +20 -0
- package/dist/runtime/components/Integrations/CredentialDetailDialog.vue +342 -0
- package/dist/runtime/components/Integrations/CredentialDetailDialog.vue.d.ts +20 -0
- package/dist/runtime/components/Integrations/CredentialLogo.d.vue.ts +14 -0
- package/dist/runtime/components/Integrations/CredentialLogo.vue +37 -0
- package/dist/runtime/components/Integrations/CredentialLogo.vue.d.ts +14 -0
- package/dist/runtime/components/Integrations/CredentialRevenexxSource.d.vue.ts +12 -0
- package/dist/runtime/components/Integrations/CredentialRevenexxSource.vue +364 -0
- package/dist/runtime/components/Integrations/CredentialRevenexxSource.vue.d.ts +12 -0
- package/dist/runtime/components/Integrations/CredentialTypePicker.d.vue.ts +15 -0
- package/dist/runtime/components/Integrations/CredentialTypePicker.vue +71 -0
- package/dist/runtime/components/Integrations/CredentialTypePicker.vue.d.ts +15 -0
- package/dist/runtime/components/Integrations/DeadLettersPanel.d.vue.ts +5 -0
- package/dist/runtime/components/Integrations/DeadLettersPanel.vue +151 -0
- package/dist/runtime/components/Integrations/DeadLettersPanel.vue.d.ts +5 -0
- package/dist/runtime/components/Integrations/EditorTip.d.vue.ts +17 -0
- package/dist/runtime/components/Integrations/EditorTip.vue +46 -0
- package/dist/runtime/components/Integrations/EditorTip.vue.d.ts +17 -0
- package/dist/runtime/components/Integrations/EventTypeCombobox.d.vue.ts +9 -0
- package/dist/runtime/components/Integrations/EventTypeCombobox.vue +64 -0
- package/dist/runtime/components/Integrations/EventTypeCombobox.vue.d.ts +9 -0
- package/dist/runtime/components/Integrations/ExpressionInput.d.vue.ts +19 -0
- package/dist/runtime/components/Integrations/ExpressionInput.vue +236 -0
- package/dist/runtime/components/Integrations/ExpressionInput.vue.d.ts +19 -0
- package/dist/runtime/components/Integrations/GroupFrameView.d.vue.ts +28 -0
- package/dist/runtime/components/Integrations/GroupFrameView.vue +222 -0
- package/dist/runtime/components/Integrations/GroupFrameView.vue.d.ts +28 -0
- package/dist/runtime/components/Integrations/NodeConfigDynamicField.d.vue.ts +14 -0
- package/dist/runtime/components/Integrations/NodeConfigDynamicField.vue +184 -0
- package/dist/runtime/components/Integrations/NodeConfigDynamicField.vue.d.ts +14 -0
- package/dist/runtime/components/Integrations/NodeConfigDynamicSchema.d.vue.ts +16 -0
- package/dist/runtime/components/Integrations/NodeConfigDynamicSchema.vue +102 -0
- package/dist/runtime/components/Integrations/NodeConfigDynamicSchema.vue.d.ts +16 -0
- package/dist/runtime/components/Integrations/NodeConfigField.d.vue.ts +23 -0
- package/dist/runtime/components/Integrations/NodeConfigField.vue +587 -0
- package/dist/runtime/components/Integrations/NodeConfigField.vue.d.ts +23 -0
- package/dist/runtime/components/Integrations/NodeDynamicOutputsResolver.d.vue.ts +14 -0
- package/dist/runtime/components/Integrations/NodeDynamicOutputsResolver.vue +80 -0
- package/dist/runtime/components/Integrations/NodeDynamicOutputsResolver.vue.d.ts +14 -0
- package/dist/runtime/components/Integrations/NodeInspector.d.vue.ts +22 -3
- package/dist/runtime/components/Integrations/NodeInspector.vue +1074 -294
- package/dist/runtime/components/Integrations/NodeInspector.vue.d.ts +22 -3
- package/dist/runtime/components/Integrations/NodeLogo.d.vue.ts +9 -0
- package/dist/runtime/components/Integrations/NodeLogo.vue +38 -0
- package/dist/runtime/components/Integrations/NodeLogo.vue.d.ts +9 -0
- package/dist/runtime/components/Integrations/NodePalette.d.vue.ts +22 -1
- package/dist/runtime/components/Integrations/NodePalette.vue +411 -90
- package/dist/runtime/components/Integrations/NodePalette.vue.d.ts +22 -1
- package/dist/runtime/components/Integrations/QueryBuilder.d.vue.ts +9 -0
- package/dist/runtime/components/Integrations/QueryBuilder.vue +239 -0
- package/dist/runtime/components/Integrations/QueryBuilder.vue.d.ts +9 -0
- package/dist/runtime/components/Integrations/{RunDetailDialog.d.vue.ts → RunActionsMenu.d.vue.ts} +3 -4
- package/dist/runtime/components/Integrations/RunActionsMenu.vue +86 -0
- package/dist/runtime/components/Integrations/{RunDetailDialog.vue.d.ts → RunActionsMenu.vue.d.ts} +3 -4
- package/dist/runtime/components/Integrations/RunDetailPanel.d.vue.ts +14 -0
- package/dist/runtime/components/Integrations/RunDetailPanel.vue +777 -0
- package/dist/runtime/components/Integrations/RunDetailPanel.vue.d.ts +14 -0
- package/dist/runtime/components/Integrations/RunPayloadInput.d.vue.ts +13 -0
- package/dist/runtime/components/Integrations/RunPayloadInput.vue +175 -0
- package/dist/runtime/components/Integrations/RunPayloadInput.vue.d.ts +13 -0
- package/dist/runtime/components/Integrations/RunResponseModal.d.vue.ts +27 -0
- package/dist/runtime/components/Integrations/RunResponseModal.vue +84 -0
- package/dist/runtime/components/Integrations/RunResponseModal.vue.d.ts +27 -0
- package/dist/runtime/components/Integrations/RunsTable.d.vue.ts +21 -0
- package/dist/runtime/components/Integrations/RunsTable.vue +114 -0
- package/dist/runtime/components/Integrations/RunsTable.vue.d.ts +21 -0
- package/dist/runtime/components/Integrations/SftpAccountImportDialog.d.vue.ts +11 -0
- package/dist/runtime/components/Integrations/SftpAccountImportDialog.vue +91 -0
- package/dist/runtime/components/Integrations/SftpAccountImportDialog.vue.d.ts +11 -0
- package/dist/runtime/components/Integrations/Sidebar.vue +1 -0
- package/dist/runtime/components/Integrations/StickyNoteView.d.vue.ts +28 -0
- package/dist/runtime/components/Integrations/StickyNoteView.vue +208 -0
- package/dist/runtime/components/Integrations/StickyNoteView.vue.d.ts +28 -0
- package/dist/runtime/components/Integrations/TemplateFlowPreview.d.vue.ts +29 -0
- package/dist/runtime/components/Integrations/TemplateFlowPreview.vue +201 -0
- package/dist/runtime/components/Integrations/TemplateFlowPreview.vue.d.ts +29 -0
- package/dist/runtime/components/Integrations/WorkflowContextMenu.d.vue.ts +32 -0
- package/dist/runtime/components/Integrations/WorkflowContextMenu.vue +138 -0
- package/dist/runtime/components/Integrations/WorkflowContextMenu.vue.d.ts +32 -0
- package/dist/runtime/components/Integrations/WorkflowCreateDialog.d.vue.ts +9 -0
- package/dist/runtime/components/Integrations/WorkflowCreateDialog.vue +322 -0
- package/dist/runtime/components/Integrations/WorkflowCreateDialog.vue.d.ts +9 -0
- package/dist/runtime/components/Integrations/WorkflowDuplicateDialog.d.vue.ts +18 -0
- package/dist/runtime/components/Integrations/WorkflowDuplicateDialog.vue +63 -0
- package/dist/runtime/components/Integrations/WorkflowDuplicateDialog.vue.d.ts +18 -0
- package/dist/runtime/components/Integrations/WorkflowEdgeView.d.vue.ts +12 -1
- package/dist/runtime/components/Integrations/WorkflowEdgeView.vue +68 -6
- package/dist/runtime/components/Integrations/WorkflowEdgeView.vue.d.ts +12 -1
- package/dist/runtime/components/Integrations/WorkflowEditor.d.vue.ts +8 -1
- package/dist/runtime/components/Integrations/WorkflowEditor.vue +1424 -144
- package/dist/runtime/components/Integrations/WorkflowEditor.vue.d.ts +8 -1
- package/dist/runtime/components/Integrations/WorkflowHistoryPanel.d.vue.ts +12 -0
- package/dist/runtime/components/Integrations/WorkflowHistoryPanel.vue +417 -0
- package/dist/runtime/components/Integrations/WorkflowHistoryPanel.vue.d.ts +12 -0
- package/dist/runtime/components/Integrations/WorkflowIconPicker.d.vue.ts +10 -0
- package/dist/runtime/components/Integrations/WorkflowIconPicker.vue +89 -0
- package/dist/runtime/components/Integrations/WorkflowIconPicker.vue.d.ts +10 -0
- package/dist/runtime/components/Integrations/WorkflowIconView.d.vue.ts +10 -0
- package/dist/runtime/components/Integrations/WorkflowIconView.vue +35 -0
- package/dist/runtime/components/Integrations/WorkflowIconView.vue.d.ts +10 -0
- package/dist/runtime/components/Integrations/WorkflowNodeView.d.vue.ts +5 -1
- package/dist/runtime/components/Integrations/WorkflowNodeView.vue +114 -84
- package/dist/runtime/components/Integrations/WorkflowNodeView.vue.d.ts +5 -1
- package/dist/runtime/components/Integrations/WorkflowRunReplayPanel.d.vue.ts +12 -0
- package/dist/runtime/components/Integrations/WorkflowRunReplayPanel.vue +654 -0
- package/dist/runtime/components/Integrations/WorkflowRunReplayPanel.vue.d.ts +12 -0
- package/dist/runtime/components/Integrations/WorkflowSettingsDialog.d.vue.ts +36 -0
- package/dist/runtime/components/Integrations/WorkflowSettingsDialog.vue +136 -0
- package/dist/runtime/components/Integrations/WorkflowSettingsDialog.vue.d.ts +36 -0
- package/dist/runtime/components/Integrations/WorkflowTagsInput.d.vue.ts +14 -0
- package/dist/runtime/components/Integrations/WorkflowTagsInput.vue +63 -0
- package/dist/runtime/components/Integrations/WorkflowTagsInput.vue.d.ts +14 -0
- package/dist/runtime/components/Integrations/WorkflowTriggerView.vue +35 -15
- package/dist/runtime/components/Integrations/WorkflowVariablesDialog.d.vue.ts +13 -0
- package/dist/runtime/components/Integrations/WorkflowVariablesDialog.vue +265 -0
- package/dist/runtime/components/Integrations/WorkflowVariablesDialog.vue.d.ts +13 -0
- package/dist/runtime/components/Integrations/WorkflowsTable.d.vue.ts +4 -1
- package/dist/runtime/components/Integrations/WorkflowsTable.vue +122 -39
- package/dist/runtime/components/Integrations/WorkflowsTable.vue.d.ts +4 -1
- package/dist/runtime/components/Integrations/workflowLayout.d.ts +5 -0
- package/dist/runtime/components/Integrations/workflowLayout.js +5 -3
- package/dist/runtime/composables/integrationsApi.d.ts +6 -2
- package/dist/runtime/composables/integrationsApi.js +5 -4
- package/dist/runtime/composables/useCanvasShortcuts.d.ts +37 -0
- package/dist/runtime/composables/useCanvasShortcuts.js +62 -0
- package/dist/runtime/composables/useIntegrationsDeadLetters.d.ts +22 -0
- package/dist/runtime/composables/useIntegrationsDeadLetters.js +50 -0
- package/dist/runtime/composables/useIntegrationsNodes.d.ts +4 -0
- package/dist/runtime/composables/useIntegrationsNodes.js +6 -3
- package/dist/runtime/composables/useIntegrationsRevisions.d.ts +40 -0
- package/dist/runtime/composables/useIntegrationsRevisions.js +51 -0
- package/dist/runtime/composables/useIntegrationsRuns.d.ts +21 -1
- package/dist/runtime/composables/useIntegrationsRuns.js +92 -4
- package/dist/runtime/composables/useIntegrationsTemplates.d.ts +1 -0
- package/dist/runtime/composables/useIntegrationsTemplates.js +32 -4
- package/dist/runtime/composables/useIntegrationsWorkflows.d.ts +1 -0
- package/dist/runtime/composables/useIntegrationsWorkflows.js +57 -1
- package/dist/runtime/composables/useMiniMapLayout.d.ts +34 -0
- package/dist/runtime/composables/useMiniMapLayout.js +119 -0
- package/dist/runtime/composables/useNodeConfigResolve.d.ts +43 -0
- package/dist/runtime/composables/useNodeConfigResolve.js +123 -0
- package/dist/runtime/composables/useNodeDrag.d.ts +22 -0
- package/dist/runtime/composables/useNodeDrag.js +10 -0
- package/dist/runtime/composables/usePaletteLayout.d.ts +41 -0
- package/dist/runtime/composables/usePaletteLayout.js +113 -0
- package/dist/runtime/data/expressionEditor.d.ts +22 -0
- package/dist/runtime/data/expressionEditor.js +40 -0
- package/dist/runtime/data/integrationNodeCatalog.js +8 -8
- package/dist/runtime/data/nodeInputData.d.ts +37 -0
- package/dist/runtime/data/nodeInputData.js +62 -0
- package/dist/runtime/data/nodeRunData.d.ts +56 -0
- package/dist/runtime/data/nodeRunData.js +91 -0
- package/dist/runtime/data/payloadForm.d.ts +44 -0
- package/dist/runtime/data/payloadForm.js +126 -0
- package/dist/runtime/data/workflowIconCatalog.d.ts +34 -0
- package/dist/runtime/data/workflowIconCatalog.js +116 -0
- package/dist/runtime/pages/integrations/audit.vue +25 -35
- package/dist/runtime/pages/integrations/credentials.vue +117 -122
- package/dist/runtime/pages/integrations/index.vue +133 -36
- package/dist/runtime/pages/integrations/runs.vue +125 -75
- package/dist/runtime/pages/integrations/schedules.vue +5 -16
- package/dist/runtime/pages/integrations/secrets.vue +1 -3
- package/dist/runtime/pages/integrations/workflows/[id].vue +599 -194
- package/dist/runtime/pages/integrations/workflows/index.vue +10 -8
- package/dist/runtime/types/integrations.d.ts +190 -4
- package/dist/runtime/utils/credentialState.d.ts +28 -0
- package/dist/runtime/utils/credentialState.js +29 -0
- package/dist/runtime/utils/localize.d.ts +3 -1
- package/dist/runtime/utils/localize.js +1 -2
- package/dist/runtime/utils/statusStyle.d.ts +6 -0
- package/dist/runtime/utils/statusStyle.js +15 -0
- package/package.json +12 -2
- package/dist/runtime/components/Integrations/RunDetailDialog.vue +0 -169
- package/dist/runtime/pages/integrations/workflows/new.d.vue.ts +0 -2
- package/dist/runtime/pages/integrations/workflows/new.vue +0 -153
- package/dist/runtime/pages/integrations/workflows/new.vue.d.ts +0 -2
|
@@ -1,19 +1,40 @@
|
|
|
1
1
|
<script setup>
|
|
2
|
-
import { computed } from "vue";
|
|
3
|
-
import
|
|
2
|
+
import { computed, ref, watch } from "vue";
|
|
3
|
+
import TemplateFlowPreview from "../../components/Integrations/TemplateFlowPreview.vue";
|
|
4
|
+
import WorkflowCreateDialog from "../../components/Integrations/WorkflowCreateDialog.vue";
|
|
5
|
+
import { Pulse as Activity, DangerTriangle as AlertTriangle, ArrowRight, Box as Boxes, ClockCircle as Clock, Database, Layers, AddCircle as Plus, Radio, MagicStick as Sparkles, LinkRound as Webhook, Programming as Workflow } from "#solar-icons";
|
|
4
6
|
import { Badge, Button } from "@revenexx/studio";
|
|
5
7
|
useHead({ title: "Dashboard \xB7 Integration Studio" });
|
|
6
|
-
const { workflows, activeCount: activeWorkflows, load: loadWorkflows } = useIntegrationsWorkflows();
|
|
7
|
-
const { runs, runningCount, failedCount, load: loadRuns } = useIntegrationsRuns();
|
|
8
|
-
const { popular, load: loadTemplates } = useIntegrationsTemplates();
|
|
8
|
+
const { workflows, activeCount: activeWorkflows, loading: workflowsLoading, load: loadWorkflows } = useIntegrationsWorkflows();
|
|
9
|
+
const { runs, runningCount, failedCount, loading: runsLoading, load: loadRuns } = useIntegrationsRuns();
|
|
10
|
+
const { popular, loading: templatesLoading, load: loadTemplates, ensureDetails } = useIntegrationsTemplates();
|
|
11
|
+
watch(() => popular.value.map((t) => t.slug).join(","), () => {
|
|
12
|
+
if (popular.value.length) void ensureDetails(popular.value.map((t) => t.slug));
|
|
13
|
+
}, { immediate: true });
|
|
9
14
|
const { activeCount: schedulesActive, load: loadSchedules } = useIntegrationsSchedules();
|
|
15
|
+
const { pendingCount: missedRuns, globalLoading: missedLoading, loadGlobal: loadMissedRuns } = useIntegrationsDeadLetters();
|
|
16
|
+
const kpisLoading = computed(() => workflowsLoading.value && !workflows.value.length || runsLoading.value && !runs.value.length);
|
|
17
|
+
const latestLoading = computed(() => runsLoading.value && !runs.value.length);
|
|
18
|
+
const createOpen = ref(false);
|
|
19
|
+
const NuxtLinkComp = resolveComponent("NuxtLink");
|
|
10
20
|
onMounted(() => {
|
|
11
21
|
void loadWorkflows();
|
|
12
22
|
void loadRuns();
|
|
13
23
|
void loadTemplates();
|
|
14
24
|
void loadSchedules();
|
|
25
|
+
void loadMissedRuns();
|
|
26
|
+
});
|
|
27
|
+
const latestPerWorkflow = computed(() => {
|
|
28
|
+
const seen = /* @__PURE__ */ new Set();
|
|
29
|
+
const out = [];
|
|
30
|
+
for (const r of runs.value) {
|
|
31
|
+
if (seen.has(r.workflowId)) continue;
|
|
32
|
+
seen.add(r.workflowId);
|
|
33
|
+
out.push(r);
|
|
34
|
+
if (out.length >= 10) break;
|
|
35
|
+
}
|
|
36
|
+
return out;
|
|
15
37
|
});
|
|
16
|
-
const recentRuns = computed(() => runs.value.slice(0, 6));
|
|
17
38
|
const failedRecent = computed(() => runs.value.filter((r) => r.status === "failed").slice(0, 3));
|
|
18
39
|
function formatRelative(iso) {
|
|
19
40
|
if (!iso) return "\u2014";
|
|
@@ -58,8 +79,22 @@ const kpis = computed(() => [
|
|
|
58
79
|
sub: "cron triggers active",
|
|
59
80
|
icon: Clock,
|
|
60
81
|
tile: "bg-violet-500 text-white"
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
label: "Missed runs",
|
|
85
|
+
value: missedRuns.value,
|
|
86
|
+
sub: "trigger firings without a run",
|
|
87
|
+
icon: AlertTriangle,
|
|
88
|
+
tile: missedRuns.value ? "bg-amber-500 text-white" : "bg-slate-300 text-white",
|
|
89
|
+
to: "/integrations/runs?tab=missed"
|
|
61
90
|
}
|
|
62
91
|
]);
|
|
92
|
+
const surfaces = [
|
|
93
|
+
{ label: "Import & Export", to: "/integrations/io", icon: Database, hint: "Profiles that move catalogue and entity data in and out." },
|
|
94
|
+
{ label: "SFTP Server", to: "/integrations/sftp", icon: Boxes, hint: "Managed SFTP endpoints for file-based exchange." },
|
|
95
|
+
{ label: "Event Stream", to: "/integrations/events", icon: Radio, hint: "Live platform events that workflows subscribe to." },
|
|
96
|
+
{ label: "Webhooks", to: "/integrations/webhooks", icon: Webhook, hint: "Inbound HTTP endpoints that trigger workflows." }
|
|
97
|
+
];
|
|
63
98
|
</script>
|
|
64
99
|
|
|
65
100
|
<template>
|
|
@@ -75,25 +110,31 @@ const kpis = computed(() => [
|
|
|
75
110
|
Browse templates
|
|
76
111
|
</Button>
|
|
77
112
|
</NuxtLink>
|
|
78
|
-
<
|
|
79
|
-
<
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
</Button>
|
|
83
|
-
</NuxtLink>
|
|
113
|
+
<Button class="h-7 text-sm" @click="createOpen = true">
|
|
114
|
+
<Plus :size="14" />
|
|
115
|
+
New workflow
|
|
116
|
+
</Button>
|
|
84
117
|
</template>
|
|
85
118
|
</PageHeader>
|
|
86
119
|
|
|
87
120
|
<div class="p-4 space-y-4">
|
|
88
121
|
<!-- KPI strip -->
|
|
89
122
|
<div class="grid gap-2 sm:grid-cols-2 lg:grid-cols-4">
|
|
90
|
-
<
|
|
123
|
+
<component
|
|
124
|
+
:is="k.to ? NuxtLinkComp : 'div'"
|
|
125
|
+
v-for="k in kpis"
|
|
126
|
+
:key="k.label"
|
|
127
|
+
:to="k.to"
|
|
128
|
+
class="block rounded-lg border border-black/[0.06] bg-white p-3"
|
|
129
|
+
:class="k.to ? 'transition hover:border-black/20' : ''"
|
|
130
|
+
>
|
|
91
131
|
<div class="flex items-start justify-between gap-2">
|
|
92
132
|
<div class="min-w-0">
|
|
93
133
|
<div class="text-mono text-muted-foreground truncate">
|
|
94
134
|
{{ k.label }}
|
|
95
135
|
</div>
|
|
96
|
-
<div class="
|
|
136
|
+
<div v-if="kpisLoading" class="my-1.5 h-6 w-12 rounded bg-black/[0.07] animate-pulse" />
|
|
137
|
+
<div v-else class="text-2xl font-semibold tabular-nums text-[#0B0522]">
|
|
97
138
|
{{ k.value }}
|
|
98
139
|
</div>
|
|
99
140
|
<div class="text-sm text-muted-foreground">
|
|
@@ -104,7 +145,7 @@ const kpis = computed(() => [
|
|
|
104
145
|
<component :is="k.icon" :size="16" />
|
|
105
146
|
</span>
|
|
106
147
|
</div>
|
|
107
|
-
</
|
|
148
|
+
</component>
|
|
108
149
|
</div>
|
|
109
150
|
|
|
110
151
|
<!-- Two-column: recent runs + needs attention + popular templates -->
|
|
@@ -116,7 +157,7 @@ const kpis = computed(() => [
|
|
|
116
157
|
<span class="flex size-6 items-center justify-center rounded-md bg-slate-100 text-slate-500">
|
|
117
158
|
<Activity :size="12" />
|
|
118
159
|
</span>
|
|
119
|
-
<h3 class="text-sm font-semibold">
|
|
160
|
+
<h3 class="text-sm font-semibold">Latest executions</h3>
|
|
120
161
|
</div>
|
|
121
162
|
<NuxtLink to="/integrations/runs" class="text-sm text-muted-foreground hover:text-foreground inline-flex items-center gap-1">
|
|
122
163
|
View all <ArrowRight :size="11" />
|
|
@@ -124,17 +165,17 @@ const kpis = computed(() => [
|
|
|
124
165
|
</div>
|
|
125
166
|
<div>
|
|
126
167
|
<div
|
|
127
|
-
v-for="r in
|
|
168
|
+
v-for="r in latestPerWorkflow"
|
|
128
169
|
:key="r.id"
|
|
129
|
-
class="grid grid-cols-[
|
|
130
|
-
@click="$router.push(`/integrations/workflows/${r.workflowId}
|
|
170
|
+
class="grid grid-cols-[90px_minmax(0,1fr)_100px_70px] items-center gap-2 border-b border-black/[0.04] px-3 py-2 text-sm last:border-b-0 cursor-pointer hover:bg-muted/30"
|
|
171
|
+
@click="$router.push({ path: `/integrations/workflows/${r.workflowId}`, query: { run: r.id } })"
|
|
131
172
|
>
|
|
132
|
-
<Badge variant="outline" :class="['text-
|
|
173
|
+
<Badge variant="outline" :class="['border text-xs font-medium uppercase', statusClass(r.status)]">
|
|
133
174
|
{{ r.status }}
|
|
134
175
|
</Badge>
|
|
135
176
|
<div class="truncate">
|
|
136
177
|
<div class="font-medium text-foreground truncate">{{ r.workflowName }}</div>
|
|
137
|
-
<div class="font-mono text-
|
|
178
|
+
<div class="font-mono text-xs text-muted-foreground truncate">{{ r.temporalRunId ?? "\u2014" }}</div>
|
|
138
179
|
</div>
|
|
139
180
|
<div class="text-sm text-muted-foreground tabular-nums truncate">
|
|
140
181
|
{{ formatRelative(r.startedAt) }}
|
|
@@ -143,7 +184,22 @@ const kpis = computed(() => [
|
|
|
143
184
|
{{ r.durationMs == null ? "\u2026" : `${(r.durationMs / 1e3).toFixed(2)}s` }}
|
|
144
185
|
</div>
|
|
145
186
|
</div>
|
|
146
|
-
<
|
|
187
|
+
<template v-if="latestLoading && !latestPerWorkflow.length">
|
|
188
|
+
<div
|
|
189
|
+
v-for="n in 6"
|
|
190
|
+
:key="`sk-run-${n}`"
|
|
191
|
+
class="grid grid-cols-[90px_minmax(0,1fr)_100px_70px] items-center gap-2 border-b border-black/[0.04] px-3 py-2.5 last:border-b-0"
|
|
192
|
+
>
|
|
193
|
+
<div class="h-5 w-16 rounded bg-black/[0.07] animate-pulse" />
|
|
194
|
+
<div class="space-y-1.5">
|
|
195
|
+
<div class="h-3 w-1/2 rounded bg-black/[0.07] animate-pulse" />
|
|
196
|
+
<div class="h-3 w-2/3 rounded bg-black/[0.05] animate-pulse" />
|
|
197
|
+
</div>
|
|
198
|
+
<div class="h-3 w-14 rounded bg-black/[0.05] animate-pulse" />
|
|
199
|
+
<div class="h-3 w-10 justify-self-end rounded bg-black/[0.05] animate-pulse" />
|
|
200
|
+
</div>
|
|
201
|
+
</template>
|
|
202
|
+
<div v-else-if="!latestPerWorkflow.length" class="px-3 py-8 text-center text-sm text-muted-foreground">
|
|
147
203
|
No runs yet.
|
|
148
204
|
</div>
|
|
149
205
|
</div>
|
|
@@ -159,7 +215,21 @@ const kpis = computed(() => [
|
|
|
159
215
|
<h3 class="text-sm font-semibold">Needs attention</h3>
|
|
160
216
|
</div>
|
|
161
217
|
</div>
|
|
162
|
-
<div v-if="failedRecent.length">
|
|
218
|
+
<div v-if="(missedLoading || latestLoading) && !missedRuns && !failedRecent.length" class="space-y-2 p-3">
|
|
219
|
+
<div v-for="n in 3" :key="`sk-att-${n}`" class="h-3 rounded bg-black/[0.07] animate-pulse" :class="n === 1 ? 'w-2/3' : n === 2 ? 'w-1/2' : 'w-3/5'" />
|
|
220
|
+
</div>
|
|
221
|
+
<div v-else-if="missedRuns || failedRecent.length">
|
|
222
|
+
<!-- Missed runs first — trigger firings that never became a run. -->
|
|
223
|
+
<NuxtLink
|
|
224
|
+
v-if="missedRuns"
|
|
225
|
+
to="/integrations/runs?tab=missed"
|
|
226
|
+
class="block space-y-1 border-b border-black/[0.04] px-3 py-2 text-sm hover:bg-muted/30"
|
|
227
|
+
>
|
|
228
|
+
<div class="font-medium">{{ missedRuns }} missed run{{ missedRuns === 1 ? "" : "s" }}</div>
|
|
229
|
+
<div class="text-sm text-amber-700">
|
|
230
|
+
Triggers fired while their workflow couldn't run — review & replay.
|
|
231
|
+
</div>
|
|
232
|
+
</NuxtLink>
|
|
163
233
|
<div
|
|
164
234
|
v-for="r in failedRecent"
|
|
165
235
|
:key="r.id"
|
|
@@ -181,6 +251,27 @@ const kpis = computed(() => [
|
|
|
181
251
|
</div>
|
|
182
252
|
</div>
|
|
183
253
|
|
|
254
|
+
<!-- Data exchange & events — the studio surfaces beyond workflows. -->
|
|
255
|
+
<div class="grid gap-2 sm:grid-cols-2 lg:grid-cols-4">
|
|
256
|
+
<NuxtLink
|
|
257
|
+
v-for="sfc in surfaces"
|
|
258
|
+
:key="sfc.to"
|
|
259
|
+
:to="sfc.to"
|
|
260
|
+
class="group rounded-lg border border-black/[0.06] bg-white p-3 transition hover:border-black/20"
|
|
261
|
+
>
|
|
262
|
+
<div class="flex items-center gap-2">
|
|
263
|
+
<span class="flex size-6 items-center justify-center rounded-md bg-slate-100 text-slate-500">
|
|
264
|
+
<component :is="sfc.icon" :size="12" />
|
|
265
|
+
</span>
|
|
266
|
+
<span class="min-w-0 flex-1 truncate text-sm font-semibold">{{ sfc.label }}</span>
|
|
267
|
+
<ArrowRight :size="11" class="text-muted-foreground transition-transform group-hover:translate-x-0.5" />
|
|
268
|
+
</div>
|
|
269
|
+
<p class="mt-2 text-sm text-muted-foreground">
|
|
270
|
+
{{ sfc.hint }}
|
|
271
|
+
</p>
|
|
272
|
+
</NuxtLink>
|
|
273
|
+
</div>
|
|
274
|
+
|
|
184
275
|
<!-- Popular templates -->
|
|
185
276
|
<div class="rounded-lg border border-black/[0.06] bg-white">
|
|
186
277
|
<div class="flex items-center justify-between border-b border-black/[0.06] px-3 py-2">
|
|
@@ -194,7 +285,14 @@ const kpis = computed(() => [
|
|
|
194
285
|
See all <ArrowRight :size="11" />
|
|
195
286
|
</NuxtLink>
|
|
196
287
|
</div>
|
|
197
|
-
<div v-if="!popular.length" class="
|
|
288
|
+
<div v-if="templatesLoading && !popular.length" class="grid gap-2 p-3 sm:grid-cols-2 lg:grid-cols-4">
|
|
289
|
+
<div v-for="n in 4" :key="`sk-tpl-${n}`" class="rounded-md border border-black/[0.06] bg-white p-3 space-y-2">
|
|
290
|
+
<div class="h-5 w-20 rounded bg-black/[0.07] animate-pulse" />
|
|
291
|
+
<div class="h-3 w-3/4 rounded bg-black/[0.07] animate-pulse" />
|
|
292
|
+
<div class="h-3 w-1/2 rounded bg-black/[0.05] animate-pulse" />
|
|
293
|
+
</div>
|
|
294
|
+
</div>
|
|
295
|
+
<div v-else-if="!popular.length" class="px-3 py-6 text-center text-sm text-muted-foreground">
|
|
198
296
|
No templates registered yet.
|
|
199
297
|
</div>
|
|
200
298
|
<div v-else class="grid gap-2 p-3 sm:grid-cols-2 lg:grid-cols-4">
|
|
@@ -202,25 +300,24 @@ const kpis = computed(() => [
|
|
|
202
300
|
v-for="tpl in popular"
|
|
203
301
|
:key="tpl.slug"
|
|
204
302
|
:to="`/integrations/templates/${tpl.slug}`"
|
|
205
|
-
class="
|
|
303
|
+
class="flex flex-col overflow-hidden rounded-md border border-black/[0.06] bg-white transition hover:border-black/20 hover:shadow-sm"
|
|
206
304
|
>
|
|
207
|
-
<div class="
|
|
208
|
-
<
|
|
305
|
+
<div class="relative h-20 shrink-0 border-b border-black/[0.06] bg-slate-50/70 [background-image:radial-gradient(circle,rgba(15,23,42,0.07)_1px,transparent_1px)] [background-size:12px_12px]">
|
|
306
|
+
<TemplateFlowPreview v-if="tpl.detailLoaded" :blob="tpl.blob" :height="80" class="px-1" />
|
|
307
|
+
<span class="absolute left-1.5 top-1.5 inline-flex items-center whitespace-nowrap rounded-full bg-slate-100 px-1.5 py-px text-[11px] font-medium uppercase text-slate-700">
|
|
209
308
|
{{ tpl.category }}
|
|
210
|
-
</Badge>
|
|
211
|
-
<span class="flex-1" />
|
|
212
|
-
<span class="text-sm text-muted-foreground tabular-nums inline-flex items-center gap-0.5">
|
|
213
|
-
<TrendingUp :size="10" />
|
|
214
|
-
{{ tpl.installs }}
|
|
215
309
|
</span>
|
|
216
310
|
</div>
|
|
217
|
-
<div class="
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
311
|
+
<div class="flex min-h-0 flex-1 flex-col p-3">
|
|
312
|
+
<div class="text-sm font-medium truncate">{{ tpl.name }}</div>
|
|
313
|
+
<p class="mt-1 text-sm text-muted-foreground line-clamp-2">
|
|
314
|
+
{{ tpl.description }}
|
|
315
|
+
</p>
|
|
316
|
+
</div>
|
|
221
317
|
</NuxtLink>
|
|
222
318
|
</div>
|
|
223
319
|
</div>
|
|
224
320
|
</div>
|
|
321
|
+
<WorkflowCreateDialog v-model:open="createOpen" />
|
|
225
322
|
</IntegrationsLayout>
|
|
226
323
|
</template>
|
|
@@ -1,44 +1,76 @@
|
|
|
1
1
|
<script setup>
|
|
2
|
-
import {
|
|
3
|
-
import { Badge } from "@revenexx/studio";
|
|
4
|
-
import
|
|
2
|
+
import { RefreshCircle as Loader2, Refresh as ReplayIcon, TrashBinMinimalistic as Trash2 } from "#solar-icons";
|
|
3
|
+
import { Badge, Button } from "@revenexx/studio";
|
|
4
|
+
import { toast } from "vue-sonner";
|
|
5
|
+
import RunsTable from "../../components/Integrations/RunsTable.vue";
|
|
5
6
|
import { statusClass } from "../../utils/statusStyle";
|
|
6
7
|
useHead({ title: "Runs \xB7 Integration Studio" });
|
|
7
|
-
const
|
|
8
|
+
const route = useRoute();
|
|
9
|
+
const { runs, loading, load } = useIntegrationsRuns();
|
|
10
|
+
const {
|
|
11
|
+
globalItems: missed,
|
|
12
|
+
globalLoading: missedLoading,
|
|
13
|
+
pendingCount,
|
|
14
|
+
loadGlobal,
|
|
15
|
+
replay,
|
|
16
|
+
discard
|
|
17
|
+
} = useIntegrationsDeadLetters();
|
|
18
|
+
const tab = ref(route.query.tab === "missed" ? "missed" : "runs");
|
|
19
|
+
const tabs = computed(() => [
|
|
20
|
+
{ key: "runs", label: "Runs" },
|
|
21
|
+
// "Missed runs": trigger firings that never became a run (dead letters).
|
|
22
|
+
{ key: "missed", label: "Missed runs", badge: pendingCount.value || null }
|
|
23
|
+
]);
|
|
24
|
+
const hasActiveRuns = computed(() => runs.value.some((r) => r.status === "running" || r.status === "queued"));
|
|
25
|
+
let pollTimer = null;
|
|
8
26
|
onMounted(() => {
|
|
9
|
-
void load();
|
|
27
|
+
void load(true);
|
|
28
|
+
void loadGlobal(true);
|
|
29
|
+
pollTimer = setInterval(() => {
|
|
30
|
+
if (hasActiveRuns.value) void load(true);
|
|
31
|
+
}, 3e3);
|
|
10
32
|
});
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
33
|
+
onUnmounted(() => {
|
|
34
|
+
if (pollTimer) {
|
|
35
|
+
clearInterval(pollTimer);
|
|
36
|
+
pollTimer = null;
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
const busyId = ref(null);
|
|
40
|
+
async function onReplay(dl) {
|
|
41
|
+
busyId.value = dl.id;
|
|
42
|
+
try {
|
|
43
|
+
const res = await replay(dl.id);
|
|
44
|
+
const newId = res && typeof res === "object" && "id" in res ? String(res.id) : null;
|
|
45
|
+
toast.success(newId ? `Replayed \u2014 started run #${newId}` : "Replayed");
|
|
46
|
+
await loadGlobal(true);
|
|
47
|
+
void load(true);
|
|
48
|
+
} catch (e) {
|
|
49
|
+
toast.error(e?.message ?? "Could not replay.");
|
|
50
|
+
} finally {
|
|
51
|
+
busyId.value = null;
|
|
52
|
+
}
|
|
16
53
|
}
|
|
17
|
-
function
|
|
18
|
-
|
|
54
|
+
async function onDiscard(dl) {
|
|
55
|
+
busyId.value = dl.id;
|
|
56
|
+
try {
|
|
57
|
+
await discard(dl.id);
|
|
58
|
+
toast.success("Discarded");
|
|
59
|
+
await loadGlobal(true);
|
|
60
|
+
} catch (e) {
|
|
61
|
+
toast.error(e?.message ?? "Could not discard.");
|
|
62
|
+
} finally {
|
|
63
|
+
busyId.value = null;
|
|
64
|
+
}
|
|
19
65
|
}
|
|
20
|
-
function
|
|
66
|
+
function fmt(iso) {
|
|
21
67
|
if (!iso) return "\u2014";
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
}
|
|
28
|
-
function formatDuration(ms) {
|
|
29
|
-
if (ms == null) return "\u2014";
|
|
30
|
-
if (ms < 1e3) return `${ms}ms`;
|
|
31
|
-
if (ms < 6e4) return `${(ms / 1e3).toFixed(2)}s`;
|
|
32
|
-
return `${(ms / 6e4).toFixed(1)}m`;
|
|
68
|
+
try {
|
|
69
|
+
return new Date(iso).toLocaleString();
|
|
70
|
+
} catch {
|
|
71
|
+
return iso;
|
|
72
|
+
}
|
|
33
73
|
}
|
|
34
|
-
const columns = [
|
|
35
|
-
{ key: "status", label: "Status", width: "100px", sortable: true, sortGetter: (r) => r.status },
|
|
36
|
-
{ key: "workflow", label: "Workflow", sortable: true, sortGetter: (r) => r.workflowName.toLowerCase(), searchGetter: (r) => `${r.workflowName} ${r.id}` },
|
|
37
|
-
{ key: "trigger", label: "Trigger", width: "120px", sortable: true, sortGetter: (r) => r.triggerType },
|
|
38
|
-
{ key: "started", label: "Started", width: "120px", sortable: true, sortGetter: (r) => r.startedAt },
|
|
39
|
-
{ key: "duration", label: "Duration", width: "100px", sortable: true, sortGetter: (r) => r.durationMs ?? -1 },
|
|
40
|
-
{ key: "steps", label: "Steps", width: "80px", sortable: true, sortGetter: (r) => r.steps.length }
|
|
41
|
-
];
|
|
42
74
|
</script>
|
|
43
75
|
|
|
44
76
|
<template>
|
|
@@ -48,53 +80,71 @@ const columns = [
|
|
|
48
80
|
description="Execution history across every workflow — sourced from Temporal."
|
|
49
81
|
/>
|
|
50
82
|
|
|
51
|
-
<
|
|
52
|
-
<DataTable
|
|
53
|
-
:data="runs"
|
|
54
|
-
:columns="columns"
|
|
55
|
-
:row-key="(r) => r.id"
|
|
56
|
-
:default-sort="{ column: 'started', direction: 'desc' }"
|
|
57
|
-
:search="{ placeholder: 'Search runs\u2026' }"
|
|
58
|
-
:filter="true"
|
|
59
|
-
empty-text="No runs yet — kick off a workflow to populate this view."
|
|
60
|
-
@row-click="(r) => openRun(r)"
|
|
61
|
-
>
|
|
62
|
-
<template #cell-status="{ row: run }">
|
|
63
|
-
<Badge variant="outline" :class="['text-sm border', statusClass(run.status)]">
|
|
64
|
-
{{ run.status }}
|
|
65
|
-
</Badge>
|
|
66
|
-
</template>
|
|
67
|
-
|
|
68
|
-
<template #cell-workflow="{ row: run }">
|
|
69
|
-
<div class="min-w-0">
|
|
70
|
-
<div class="font-medium text-sm truncate">{{ run.workflowName }}</div>
|
|
71
|
-
<div class="font-mono text-sm text-muted-foreground truncate">{{ run.id }}</div>
|
|
72
|
-
</div>
|
|
73
|
-
</template>
|
|
83
|
+
<TabBar v-model="tab" :tabs="tabs" class="px-4" />
|
|
74
84
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
<component :is="triggerIcon(run.triggerType)" :size="11" />
|
|
78
|
-
{{ run.triggerType }}
|
|
79
|
-
</div>
|
|
80
|
-
</template>
|
|
85
|
+
<div class="p-4 space-y-4">
|
|
86
|
+
<RunsTable v-if="tab === 'runs'" :runs="runs" :loading="loading" @changed="() => load(true)" />
|
|
81
87
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
88
|
+
<!-- Missed runs — trigger firings that never produced a run (the
|
|
89
|
+
workflow wasn't runnable when its schedule/event fired). -->
|
|
90
|
+
<template v-else>
|
|
91
|
+
<div class="flex items-center justify-between">
|
|
92
|
+
<p class="text-sm text-muted-foreground">
|
|
93
|
+
Triggers that fired while their workflow couldn't run (bundle building or failed). Replay starts a fresh run from the captured payload.
|
|
94
|
+
</p>
|
|
95
|
+
<Button variant="outline" size="sm" class="h-7 text-sm" :disabled="missedLoading" @click="() => loadGlobal(true)">
|
|
96
|
+
<Loader2 v-if="missedLoading" :size="12" class="animate-spin" />
|
|
97
|
+
Refresh
|
|
98
|
+
</Button>
|
|
99
|
+
</div>
|
|
85
100
|
|
|
86
|
-
<
|
|
87
|
-
<
|
|
88
|
-
|
|
101
|
+
<div v-if="missedLoading && !missed.length"><div class="rounded-lg border border-black/[0.06] bg-white divide-y divide-black/[0.04]">
|
|
102
|
+
<div v-for="n in 4" :key="`sk-${n}`" class="flex items-start gap-3 px-3 py-3">
|
|
103
|
+
<div class="h-5 w-16 shrink-0 rounded bg-black/[0.07] animate-pulse" />
|
|
104
|
+
<div class="min-w-0 flex-1 space-y-2">
|
|
105
|
+
<div class="h-3 w-1/3 rounded bg-black/[0.07] animate-pulse" />
|
|
106
|
+
<div class="h-3 w-2/3 rounded bg-black/[0.05] animate-pulse" />
|
|
107
|
+
</div>
|
|
108
|
+
</div>
|
|
109
|
+
</div>
|
|
110
|
+
</div>
|
|
111
|
+
<div v-else-if="!missed.length" class="rounded-lg border border-black/[0.06] bg-white p-6 text-center text-sm text-muted-foreground">
|
|
112
|
+
No missed runs. Every trigger firing made it into a run.
|
|
113
|
+
</div>
|
|
89
114
|
|
|
90
|
-
<
|
|
91
|
-
<
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
115
|
+
<div v-else class="rounded-lg border border-black/[0.06] bg-white divide-y divide-black/[0.04]">
|
|
116
|
+
<div
|
|
117
|
+
v-for="dl in missed"
|
|
118
|
+
:key="dl.id"
|
|
119
|
+
class="flex items-start gap-3 px-3 py-2.5"
|
|
120
|
+
>
|
|
121
|
+
<Badge variant="outline" :class="['border text-xs font-medium uppercase shrink-0 mt-0.5', statusClass(dl.status)]">{{ dl.status }}</Badge>
|
|
122
|
+
<div class="min-w-0 flex-1">
|
|
123
|
+
<NuxtLink :to="`/integrations/workflows/${dl.workflow_id}`" class="text-sm font-medium truncate transition-colors hover:text-primary">
|
|
124
|
+
{{ dl.workflowName }}
|
|
125
|
+
</NuxtLink>
|
|
126
|
+
<div class="text-sm break-words">{{ dl.reason || "No reason recorded." }}</div>
|
|
127
|
+
<div class="mt-0.5 flex flex-wrap items-center gap-x-3 gap-y-0.5 text-xs text-muted-foreground font-mono">
|
|
128
|
+
<span>#{{ dl.id }}</span>
|
|
129
|
+
<span v-if="dl.source">{{ dl.source }}</span>
|
|
130
|
+
<span v-if="dl.trigger_handle">↳ {{ dl.trigger_handle }}</span>
|
|
131
|
+
<span>{{ fmt(dl.created_at) }}</span>
|
|
132
|
+
</div>
|
|
133
|
+
</div>
|
|
134
|
+
<div v-if="dl.status === 'pending'" class="flex shrink-0 items-center gap-1">
|
|
135
|
+
<Button variant="outline" size="sm" class="h-7 text-sm" :disabled="busyId === dl.id" @click="onReplay(dl)">
|
|
136
|
+
<Loader2 v-if="busyId === dl.id" :size="12" class="animate-spin" />
|
|
137
|
+
<ReplayIcon v-else :size="12" />
|
|
138
|
+
Replay
|
|
139
|
+
</Button>
|
|
140
|
+
<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)">
|
|
141
|
+
<Trash2 :size="12" />
|
|
142
|
+
Discard
|
|
143
|
+
</Button>
|
|
144
|
+
</div>
|
|
145
|
+
</div>
|
|
146
|
+
</div>
|
|
147
|
+
</template>
|
|
96
148
|
</div>
|
|
97
|
-
|
|
98
|
-
<RunDetailDialog v-model:open="detailOpen" :run="selectedRun" />
|
|
99
149
|
</IntegrationsLayout>
|
|
100
150
|
</template>
|
|
@@ -3,21 +3,10 @@ import { ClockCircle as Clock, Globus as Globe } from "#solar-icons";
|
|
|
3
3
|
import { Badge } from "@revenexx/studio";
|
|
4
4
|
useHead({ title: "Schedules \xB7 Integration Studio" });
|
|
5
5
|
const router = useRouter();
|
|
6
|
-
const { entries, activeCount, load } = useIntegrationsSchedules();
|
|
6
|
+
const { entries, activeCount, loading, load } = useIntegrationsSchedules();
|
|
7
7
|
onMounted(() => {
|
|
8
|
-
void load();
|
|
8
|
+
void load(true);
|
|
9
9
|
});
|
|
10
|
-
function describeCron(cron) {
|
|
11
|
-
const parts = cron.split(/\s+/);
|
|
12
|
-
if (parts.length !== 5) return cron;
|
|
13
|
-
const [m, h, dom, mon, dow] = parts;
|
|
14
|
-
if (cron === "0 * * * *") return "every hour";
|
|
15
|
-
if (m === "0" && /^\d+$/.test(h ?? "") && dom === "*" && mon === "*" && dow === "*") return `daily at ${h?.padStart(2, "0")}:00`;
|
|
16
|
-
if (m === "0" && h === "0" && dom === "*" && mon === "*" && dow === "*") return "daily at midnight";
|
|
17
|
-
if (dom !== "*" && mon === "*") return `on day ${dom} of each month`;
|
|
18
|
-
if (dow !== "*" && dow !== "?") return `weekly on ${dow}`;
|
|
19
|
-
return cron;
|
|
20
|
-
}
|
|
21
10
|
function formatRelative(iso) {
|
|
22
11
|
if (!iso) return "\u2014";
|
|
23
12
|
const ms = new Date(iso).getTime() - Date.now();
|
|
@@ -57,6 +46,7 @@ const columns = [
|
|
|
57
46
|
|
|
58
47
|
<div class="p-4">
|
|
59
48
|
<DataTable
|
|
49
|
+
:loading="loading"
|
|
60
50
|
:data="entries"
|
|
61
51
|
:columns="columns"
|
|
62
52
|
:row-key="(e2) => e2.id"
|
|
@@ -73,7 +63,6 @@ const columns = [
|
|
|
73
63
|
</div>
|
|
74
64
|
<div class="min-w-0">
|
|
75
65
|
<div class="font-medium text-sm truncate">{{ e.workflowName }}</div>
|
|
76
|
-
<div class="text-sm text-muted-foreground truncate">{{ describeCron(e.cron) }}</div>
|
|
77
66
|
</div>
|
|
78
67
|
</div>
|
|
79
68
|
</template>
|
|
@@ -98,10 +87,10 @@ const columns = [
|
|
|
98
87
|
</template>
|
|
99
88
|
|
|
100
89
|
<template #cell-status="{ row: e }">
|
|
101
|
-
<Badge v-if="e.active" variant="outline" class="bg-emerald-500/10 border-emerald-500/20 text-emerald-700 text-
|
|
90
|
+
<Badge v-if="e.active" variant="outline" class="bg-emerald-500/10 border-emerald-500/20 text-emerald-700 text-xs font-medium uppercase">
|
|
102
91
|
live
|
|
103
92
|
</Badge>
|
|
104
|
-
<Badge v-else variant="outline" class="text-
|
|
93
|
+
<Badge v-else variant="outline" class="text-xs font-medium uppercase">
|
|
105
94
|
paused
|
|
106
95
|
</Badge>
|
|
107
96
|
</template>
|
|
@@ -166,10 +166,8 @@ const columns = [
|
|
|
166
166
|
<div v-if="error" class="mb-3 rounded-lg border border-rose-500/20 bg-rose-500/5 px-3 py-2 text-sm text-rose-700">
|
|
167
167
|
{{ error }}
|
|
168
168
|
</div>
|
|
169
|
-
<div v-if="loading && !secrets.length" class="flex items-center gap-2 px-1 py-3 text-sm text-muted-foreground">
|
|
170
|
-
<Loader2 :size="14" class="animate-spin" /> Loading secrets…
|
|
171
|
-
</div>
|
|
172
169
|
<DataTable
|
|
170
|
+
:loading="loading"
|
|
173
171
|
:data="secrets"
|
|
174
172
|
:columns="columns"
|
|
175
173
|
:row-key="(s2) => s2.key"
|