mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-10-16 08:03:02 +00:00
feat: open an asset's upstream trigger from its pane header
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
6fb492fabf
commit
1b7bb90c5a
@@ -21,10 +21,16 @@
|
||||
Trash2,
|
||||
X,
|
||||
Pencil,
|
||||
ChevronDown
|
||||
ChevronDown,
|
||||
Zap
|
||||
} from 'lucide-svelte'
|
||||
import DropdownV2 from '$lib/components/DropdownV2.svelte'
|
||||
import { upstreamScriptItems, type UpstreamScript } from './upstreamScriptItems'
|
||||
import {
|
||||
upstreamScriptItems,
|
||||
upstreamTriggerItems,
|
||||
type UpstreamScript,
|
||||
type UpstreamTriggerAction
|
||||
} from './upstreamScriptItems'
|
||||
import { inferArgs } from '$lib/infer'
|
||||
import { emptySchema, sendUserToast } from '$lib/utils'
|
||||
import type { Schema } from '$lib/common'
|
||||
@@ -164,6 +170,8 @@
|
||||
* which calls `onEditScript` with the one picked. */
|
||||
producerScripts?: UpstreamScript[]
|
||||
onEditScript?: (path: string) => void
|
||||
/** The selected asset's upstream triggers that can be opened from here. */
|
||||
producerTriggers?: UpstreamTriggerAction[]
|
||||
// Pipeline-wide column-lineage graph (built by the parent page from the
|
||||
// resolved graph). Drives the transitive column-lineage trace shown for a
|
||||
// selected materialized asset.
|
||||
@@ -314,6 +322,7 @@
|
||||
selectionProducers = [],
|
||||
producerScripts = [],
|
||||
onEditScript,
|
||||
producerTriggers = [],
|
||||
selectionColumnGraph,
|
||||
selectionColumnLoading = false,
|
||||
selectionColumnTruncated = false,
|
||||
@@ -1059,6 +1068,36 @@
|
||||
{/snippet}
|
||||
</DropdownV2>
|
||||
{/if}
|
||||
{#if selection?.kind === 'asset' && producerTriggers.length === 1}
|
||||
<Button
|
||||
variant="default"
|
||||
unifiedSize="sm"
|
||||
startIcon={{ icon: Zap }}
|
||||
onclick={() => producerTriggers[0].onOpen()}
|
||||
title={producerTriggers[0].detail}
|
||||
>
|
||||
{producerTriggers[0].label}
|
||||
</Button>
|
||||
{:else if selection?.kind === 'asset' && producerTriggers.length > 1}
|
||||
<DropdownV2
|
||||
items={upstreamTriggerItems(producerTriggers)}
|
||||
placement="bottom-end"
|
||||
enableFlyTransition
|
||||
>
|
||||
{#snippet buttonReplacement()}
|
||||
<Button
|
||||
nonCaptureEvent
|
||||
variant="default"
|
||||
unifiedSize="sm"
|
||||
startIcon={{ icon: Zap }}
|
||||
endIcon={{ icon: ChevronDown }}
|
||||
title={`${producerTriggers.length} triggers start the scripts that build this asset`}
|
||||
>
|
||||
Triggers
|
||||
</Button>
|
||||
{/snippet}
|
||||
</DropdownV2>
|
||||
{/if}
|
||||
{#if readOnly && onRequestEdit}
|
||||
<Button
|
||||
variant="accent"
|
||||
|
||||
@@ -11,6 +11,9 @@
|
||||
import type { ColumnLineageGraph } from './columnLineageGraph'
|
||||
import HideButton from '$lib/components/apps/editor/settingsPanel/HideButton.svelte'
|
||||
import AssetGraphCanvas from './AssetGraphCanvas.svelte'
|
||||
import { TRIGGER_NODE_STYLE } from './TriggerNode.svelte'
|
||||
import { describeSchedule } from '$lib/utils/describeCron'
|
||||
import type { UpstreamTriggerAction } from './upstreamScriptItems'
|
||||
import type { AssetUpstreamDelete } from './assetsOnlyView'
|
||||
import AssetGraphDetailsPane from './AssetGraphDetailsPane.svelte'
|
||||
import PipelineEventLog from './PipelineEventLog.svelte'
|
||||
@@ -289,6 +292,52 @@
|
||||
return { path: p.path, summary: r?.summary, language: r?.language }
|
||||
})
|
||||
)
|
||||
// The selected asset's upstream triggers that can be opened from here, each
|
||||
// with the same action its trigger node has: edit (edit mode), create a
|
||||
// missing one (edit mode), or the webhook / data-upload drawers.
|
||||
let producerTriggers = $derived.by((): UpstreamTriggerAction[] => {
|
||||
const scripts = new Set(producerScripts.map((p) => p.path))
|
||||
const seen = new Set<string>()
|
||||
const out: UpstreamTriggerAction[] = []
|
||||
for (const t of displayGraph.triggers ?? []) {
|
||||
if (t.trigger_kind === 'asset' || !scripts.has(t.runnable_path)) continue
|
||||
const kind = t.trigger_kind
|
||||
const rowless = kind === 'webhook' || kind === 'data_upload'
|
||||
const key = `${kind}:${rowless || t.missing ? t.runnable_path : t.path}`
|
||||
if (seen.has(key)) continue
|
||||
seen.add(key)
|
||||
const label = TRIGGER_NODE_STYLE[kind]?.label ?? kind
|
||||
const script = t.runnable_path
|
||||
let action: UpstreamTriggerAction | undefined
|
||||
if (kind === 'webhook') {
|
||||
if (onOpenWebhook)
|
||||
action = { label: 'Open webhook', detail: script, onOpen: () => onOpenWebhook(script) }
|
||||
} else if (kind === 'data_upload') {
|
||||
if (onOpenDataUpload)
|
||||
action = { label: 'Upload data', detail: script, onOpen: () => onOpenDataUpload(script) }
|
||||
} else if (t.missing && !rowless) {
|
||||
if (onCreateMissingTrigger)
|
||||
action = {
|
||||
label: `Create ${label} trigger`,
|
||||
detail: script,
|
||||
onOpen: () => onCreateMissingTrigger(kind, script)
|
||||
}
|
||||
} else if (t.path && onEditTrigger) {
|
||||
const path = t.path
|
||||
action = {
|
||||
label: kind === 'schedule' ? 'Edit schedule' : `Edit ${label} trigger`,
|
||||
detail:
|
||||
kind === 'schedule' && t.schedule
|
||||
? (t.summary ?? describeSchedule(t.schedule, t.timezone) ?? path)
|
||||
: path,
|
||||
onOpen: () => onEditTrigger(kind, path, script)
|
||||
}
|
||||
}
|
||||
if (action) out.push(action)
|
||||
}
|
||||
return out
|
||||
})
|
||||
|
||||
function editProducerScript(path: string) {
|
||||
if (mode !== 'edit') onRequestEdit?.()
|
||||
onSelect({ kind: 'runnable', runnable_kind: 'script', path })
|
||||
@@ -491,8 +540,7 @@
|
||||
try {
|
||||
if (typeof localStorage !== 'undefined') {
|
||||
if (isEmpty) localStorage.removeItem(key)
|
||||
else
|
||||
localStorage.setItem(key, encodeState(bundle))
|
||||
else localStorage.setItem(key, encodeState(bundle))
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('failed to mirror pipeline state', e)
|
||||
@@ -593,6 +641,7 @@
|
||||
selectionProducers={activeDraft ? [] : selectionProducers}
|
||||
{producerScripts}
|
||||
onEditScript={editProducerScript}
|
||||
{producerTriggers}
|
||||
{selectionColumnGraph}
|
||||
{selectionColumnLoading}
|
||||
{selectionColumnTruncated}
|
||||
|
||||
@@ -19,3 +19,14 @@ export function upstreamScriptItems(
|
||||
action: () => onPick(s.path)
|
||||
}))
|
||||
}
|
||||
|
||||
/** An upstream trigger the asset pane can open, and how. */
|
||||
export type UpstreamTriggerAction = { label: string; detail?: string; onOpen: () => void }
|
||||
|
||||
export function upstreamTriggerItems(triggers: UpstreamTriggerAction[]): Item[] {
|
||||
return triggers.map((t) => ({
|
||||
displayName: t.label,
|
||||
description: t.detail,
|
||||
action: () => t.onOpen()
|
||||
}))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user