mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-10-28 08:02:43 +00:00
ui(deploy-to-hub): open-in-tab affordance on each dependency and trigger
Adds a small ExternalLink icon at the far right of every row in the Resource dependencies drawer (script / flow / app / raw_app) and the Triggers drawer (per trigger kind, opens the matching list page — /routes, /schedules, /websocket_triggers, /kafka_triggers, …). Both buttons open in a new tab scoped to the current $workspaceStore. Sized to sit after the role badge so the dominant signal (input vs hardcoded path, script vs flow) stays read first. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.7
parent
b1db720d1f
commit
2789bdd0bf
@@ -1,4 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { base } from '$lib/base'
|
||||
import { Badge, Button, Drawer, DrawerContent } from '$lib/components/common'
|
||||
import WorkspaceDeployLayout from '$lib/components/WorkspaceDeployLayout.svelte'
|
||||
import SchemaForm from '$lib/components/SchemaForm.svelte'
|
||||
@@ -506,6 +507,40 @@
|
||||
bundleReadme = hubReadme
|
||||
bundleDrawer?.openDrawer()
|
||||
}
|
||||
const TRIGGER_KIND_ROUTE: Record<TriggerKindLabel, string> = {
|
||||
http: 'routes',
|
||||
websocket: 'websocket_triggers',
|
||||
schedule: 'schedules',
|
||||
kafka: 'kafka_triggers',
|
||||
nats: 'nats_triggers',
|
||||
sqs: 'sqs_triggers',
|
||||
mqtt: 'mqtt_triggers',
|
||||
gcp: 'gcp_triggers',
|
||||
azure: 'azure_triggers',
|
||||
postgres: 'postgres_triggers',
|
||||
email: 'email_triggers'
|
||||
}
|
||||
function openTriggerUrl(kind: TriggerKindLabel): string | undefined {
|
||||
const ws = $workspaceStore
|
||||
if (!ws) return undefined
|
||||
return `${base}/${TRIGGER_KIND_ROUTE[kind]}?workspace=${ws}`
|
||||
}
|
||||
|
||||
function openItemUrl(kind: ItemKind, path: string): string | undefined {
|
||||
const ws = $workspaceStore
|
||||
if (!ws || !path) return undefined
|
||||
const segment =
|
||||
kind === 'script'
|
||||
? 'scripts/get'
|
||||
: kind === 'flow'
|
||||
? 'flows/get'
|
||||
: kind === 'app' || kind === 'raw_app'
|
||||
? 'apps/get'
|
||||
: undefined
|
||||
if (!segment) return undefined
|
||||
return `${base}/${segment}/${path}?workspace=${ws}`
|
||||
}
|
||||
|
||||
function sanitizeSlug(s: string): string {
|
||||
return s
|
||||
.toLowerCase()
|
||||
@@ -772,8 +807,8 @@
|
||||
|
||||
// `hasHardcoded` = pinned via $res: path (relocated as a stub); else input-only.
|
||||
type DependencyUsage =
|
||||
| { role: 'input'; label: string; kind: ItemKind }
|
||||
| { role: 'hardcoded'; label: string; kind: ItemKind; path: string }
|
||||
| { role: 'input'; label: string; kind: ItemKind; itemPath: string }
|
||||
| { role: 'hardcoded'; label: string; kind: ItemKind; path: string; itemPath: string }
|
||||
interface DependencyType {
|
||||
resource_type: string
|
||||
hasHardcoded: boolean
|
||||
@@ -805,11 +840,17 @@
|
||||
if (!stub || HIDDEN_RESOURCE_TYPES.has(stub.resource_type)) continue
|
||||
const e = ensure(stub.resource_type)
|
||||
e.hasHardcoded = true
|
||||
e.usages.push({ role: 'hardcoded', label, kind: it.kind, path: stub.originalPath })
|
||||
e.usages.push({
|
||||
role: 'hardcoded',
|
||||
label,
|
||||
kind: it.kind,
|
||||
path: stub.originalPath,
|
||||
itemPath: it.path
|
||||
})
|
||||
}
|
||||
for (const t of typesFromSchema(it.schema)) {
|
||||
if (HIDDEN_RESOURCE_TYPES.has(t)) continue
|
||||
ensure(t).usages.push({ role: 'input', label, kind: it.kind })
|
||||
ensure(t).usages.push({ role: 'input', label, kind: it.kind, itemPath: it.path })
|
||||
}
|
||||
}
|
||||
return [...byType.values()].sort((a, b) => a.resource_type.localeCompare(b.resource_type))
|
||||
@@ -1815,6 +1856,7 @@
|
||||
</div>
|
||||
<div class="flex flex-col gap-3">
|
||||
{#each r.usages as u (u.role + ':' + u.label + ':' + (u.role === 'hardcoded' ? u.path : ''))}
|
||||
{@const itemUrl = openItemUrl(u.kind, u.itemPath)}
|
||||
<div class="flex flex-col gap-1 text-xs">
|
||||
<div class="flex items-center gap-2">
|
||||
{#if u.kind === 'script'}
|
||||
@@ -1853,6 +1895,17 @@
|
||||
</Popover>
|
||||
{/if}
|
||||
</span>
|
||||
{#if itemUrl}
|
||||
<a
|
||||
href={itemUrl}
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
title="Open {u.kind} in new tab"
|
||||
class="shrink-0 text-hint hover:text-primary"
|
||||
>
|
||||
<ExternalLink size={12} />
|
||||
</a>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
@@ -1909,6 +1962,7 @@
|
||||
{@const cfg = t.config as any}
|
||||
{@const previewKey = t.kind === 'schedule' ? `${cfg.schedule}|${cfg.timezone}` : ''}
|
||||
{@const preview = t.kind === 'schedule' ? schedulePreviews[previewKey] : undefined}
|
||||
{@const triggerUrl = openTriggerUrl(t.kind)}
|
||||
<div class="flex flex-col gap-1 text-xs">
|
||||
<div class="flex items-center gap-2">
|
||||
{#if t.is_flow}
|
||||
@@ -1924,6 +1978,17 @@
|
||||
>
|
||||
{t.is_flow ? 'flow' : 'script'}
|
||||
</span>
|
||||
{#if triggerUrl}
|
||||
<a
|
||||
href={triggerUrl}
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
title="Open {TRIGGER_KIND_BADGE[t.kind]} trigger list in new tab"
|
||||
class="shrink-0 text-hint hover:text-primary"
|
||||
>
|
||||
<ExternalLink size={12} />
|
||||
</a>
|
||||
{/if}
|
||||
</div>
|
||||
{#if details.length > 0}
|
||||
<dl class="ml-5 grid grid-cols-[auto_1fr] gap-x-2 gap-y-0.5 text-[11px]">
|
||||
|
||||
Reference in New Issue
Block a user