mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-08-19 00:02:03 +00:00
291 lines
7.8 KiB
Svelte
291 lines
7.8 KiB
Svelte
<script lang="ts">
|
|
import { Alert, Button, Drawer, DrawerContent } from './common'
|
|
import { Loader2 } from 'lucide-svelte'
|
|
import { scriptLangToEditorLang } from '$lib/scripts'
|
|
import Tabs from './common/tabs/Tabs.svelte'
|
|
import Tab from './common/tabs/Tab.svelte'
|
|
import {
|
|
cleanValueProperties,
|
|
orderedJsonStringify,
|
|
orderedYamlStringify,
|
|
replaceFalseWithUndefined,
|
|
type Value
|
|
} from '$lib/utils'
|
|
import type { Script } from '$lib/gen'
|
|
|
|
type DiffData = {
|
|
lang?: string
|
|
content?: string
|
|
metadata: string
|
|
}
|
|
|
|
let diffType: 'draft' | 'deployed' | 'custom' | undefined = $state(undefined)
|
|
|
|
let contentType = $derived.by(() => {
|
|
if (!data || !diffType) return undefined
|
|
const dataType = diffType === 'custom' ? 'original' : diffType
|
|
return data[dataType]?.content !== data.current.content
|
|
? 'content'
|
|
: data[dataType]?.metadata !== data.current.metadata
|
|
? 'metadata'
|
|
: undefined
|
|
})
|
|
|
|
let diffViewer: Drawer | undefined = $state(undefined)
|
|
|
|
interface Props {
|
|
restoreDeployed?: () => Promise<void>
|
|
restoreDraft?: () => Promise<void>
|
|
isFlow?: boolean
|
|
}
|
|
|
|
let { restoreDeployed = undefined, restoreDraft = undefined, isFlow = false }: Props = $props()
|
|
|
|
let data:
|
|
| {
|
|
mode: 'normal'
|
|
deployed: DiffData | undefined
|
|
draft: DiffData | undefined
|
|
current: DiffData
|
|
path?: string
|
|
button?: { text: string; onClick: () => void }
|
|
}
|
|
| {
|
|
mode: 'simple'
|
|
title: string
|
|
original: DiffData | undefined
|
|
current: DiffData
|
|
button?: { text: string; onClick: () => void }
|
|
}
|
|
| undefined = $state(undefined)
|
|
|
|
export function openDrawer() {
|
|
data = undefined
|
|
diffType = undefined
|
|
diffViewer?.openDrawer()
|
|
}
|
|
|
|
export function closeDrawer() {
|
|
diffViewer?.closeDrawer()
|
|
}
|
|
|
|
function prepareDiff(data: Value) {
|
|
const metadata = structuredClone(cleanValueProperties(replaceFalseWithUndefined(data)))
|
|
const content = metadata['content']
|
|
if (metadata['content'] !== undefined) {
|
|
metadata['content'] = 'check content diff'
|
|
}
|
|
return {
|
|
lang: data.language ? scriptLangToEditorLang(data.language as Script['language']) : undefined,
|
|
content,
|
|
metadata: orderedYamlStringify(metadata)
|
|
}
|
|
}
|
|
|
|
export function setDiff(
|
|
diff:
|
|
| {
|
|
mode: 'normal'
|
|
deployed: Value
|
|
draft: Value | undefined
|
|
current: Value
|
|
defaultDiffType?: 'deployed' | 'draft'
|
|
button?: { text: string; onClick: () => void }
|
|
}
|
|
| {
|
|
mode: 'simple'
|
|
original: Value
|
|
current: Value
|
|
title: string
|
|
button?: { text: string; onClick: () => void }
|
|
}
|
|
) {
|
|
if (diff.mode === 'normal') {
|
|
const { deployed, draft, current, defaultDiffType, button } = diff
|
|
data = {
|
|
mode: 'normal',
|
|
deployed: !deployed.draft_only ? prepareDiff(deployed) : undefined,
|
|
draft: draft ? prepareDiff(draft) : undefined,
|
|
current: prepareDiff(current),
|
|
path: draft?.path || deployed?.path,
|
|
button
|
|
}
|
|
|
|
if (defaultDiffType && data[defaultDiffType]) {
|
|
diffType = defaultDiffType
|
|
} else if (data.deployed) {
|
|
diffType = 'deployed'
|
|
} else if (data.draft) {
|
|
diffType = 'draft'
|
|
}
|
|
} else {
|
|
const { original, current, title, button } = diff
|
|
data = {
|
|
title,
|
|
mode: 'simple',
|
|
original: prepareDiff(original),
|
|
current: prepareDiff(current),
|
|
button
|
|
}
|
|
diffType = 'custom'
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<Drawer bind:this={diffViewer} size="1200px" on:close>
|
|
<DrawerContent title="Diff" on:close={diffViewer.closeDrawer}>
|
|
<div class="flex flex-col gap-4 h-full">
|
|
{#if diffType && data}
|
|
<Tabs bind:selected={diffType} wrapperClass="shrink-0">
|
|
{#if data.mode === 'simple'}
|
|
<Tab value="custom" label={data.title} />
|
|
{:else}
|
|
<Tab
|
|
value="deployed"
|
|
disabled={!data.deployed}
|
|
label="{'Deployed <> Current'}{!data.deployed ? ' (no deployed version)' : ''}"
|
|
/>
|
|
|
|
<Tab
|
|
value="draft"
|
|
disabled={!data.draft}
|
|
label="{'Latest saved draft <> Current'}{!data.draft ? ' (no draft)' : ''}"
|
|
/>
|
|
{/if}
|
|
</Tabs>
|
|
{/if}
|
|
{#if data?.mode === 'normal'}
|
|
{#if diffType === 'draft'}
|
|
<Button
|
|
unifiedSize="md"
|
|
variant="default"
|
|
wrapperClasses="self-start"
|
|
onClick={restoreDraft}
|
|
disabled={orderedJsonStringify(data.draft) === orderedJsonStringify(data.current)}
|
|
>Restore to latest saved draft</Button
|
|
>
|
|
{:else if diffType === 'deployed'}
|
|
<Button
|
|
unifiedSize="md"
|
|
variant="default"
|
|
wrapperClasses="self-start"
|
|
onClick={restoreDeployed}
|
|
disabled={!data.draft &&
|
|
orderedJsonStringify(data.deployed) === orderedJsonStringify(data.current)}
|
|
>
|
|
Restore to deployed{data.draft ? ' and discard draft' : ''}
|
|
</Button>
|
|
{/if}
|
|
{/if}
|
|
{#if data}
|
|
{#if contentType}
|
|
{@const content =
|
|
data.mode === 'normal'
|
|
? diffType === 'draft'
|
|
? data.draft?.content
|
|
: data.deployed?.content
|
|
: data.original?.content}
|
|
{@const metadata =
|
|
data.mode === 'normal'
|
|
? diffType === 'draft'
|
|
? data.draft?.metadata
|
|
: data.deployed?.metadata
|
|
: data.original?.metadata}
|
|
{@const lang =
|
|
data.mode === 'normal'
|
|
? diffType === 'draft'
|
|
? data.draft?.lang
|
|
: data.deployed?.lang
|
|
: data.original?.lang}
|
|
<div class="flex flex-col h-full gap-4">
|
|
{#if data.current.content !== undefined}
|
|
<Tabs bind:selected={contentType}>
|
|
<Tab
|
|
value="content"
|
|
disabled={content === data.current.content}
|
|
label={`Content${content === data.current.content ? ' (no changes)' : ''}`}
|
|
/>
|
|
<Tab
|
|
value="metadata"
|
|
disabled={metadata === data.current.metadata}
|
|
label={`Metadata${metadata === data.current.metadata ? ' (no changes)' : ''}`}
|
|
/>
|
|
</Tabs>
|
|
{/if}
|
|
<div class="flex-1">
|
|
{#key diffType}
|
|
{#if contentType === 'content'}
|
|
{#await import('$lib/components/DiffEditor.svelte')}
|
|
<Loader2 class="animate-spin" />
|
|
{:then Module}
|
|
<Module.default
|
|
open={true}
|
|
automaticLayout
|
|
className="h-full"
|
|
defaultLang={lang}
|
|
defaultModifiedLang={data.current.lang}
|
|
defaultOriginal={content}
|
|
defaultModified={data.current.content}
|
|
readOnly
|
|
/>
|
|
{/await}
|
|
{:else if contentType === 'metadata'}
|
|
{#if isFlow}
|
|
{#await import('$lib/components/FlowDiffViewer.svelte')}
|
|
<Loader2 class="animate-spin" />
|
|
{:then Module}
|
|
<Module.default
|
|
beforeYaml={metadata ?? ''}
|
|
afterYaml={data.current.metadata}
|
|
/>
|
|
{/await}
|
|
{:else}
|
|
{#await import('$lib/components/DiffEditor.svelte')}
|
|
<Loader2 class="animate-spin" />
|
|
{:then Module}
|
|
<Module.default
|
|
open={true}
|
|
automaticLayout
|
|
className="h-full"
|
|
defaultLang="yaml"
|
|
defaultOriginal={metadata}
|
|
defaultModified={data.current.metadata}
|
|
readOnly
|
|
/>
|
|
{/await}
|
|
{/if}
|
|
{/if}
|
|
{/key}
|
|
</div>
|
|
</div>
|
|
{:else}
|
|
<Alert title="No changes detected">
|
|
{#if diffType === 'draft'}
|
|
There are no differences between latest saved draft and current
|
|
{:else if diffType === 'deployed'}
|
|
There are no differences between deployed and current
|
|
{:else if diffType === 'custom'}
|
|
There are no differences
|
|
{/if}
|
|
</Alert>
|
|
{/if}
|
|
{:else}
|
|
<Loader2 class="animate-spin" />
|
|
{/if}
|
|
</div>
|
|
{#snippet actions()}
|
|
{#if data?.button}
|
|
<Button
|
|
variant="subtle"
|
|
onClick={() => {
|
|
if (data?.button) {
|
|
data.button.onClick()
|
|
diffViewer?.closeDrawer()
|
|
}
|
|
}}>{data.button.text}</Button
|
|
>
|
|
{/if}
|
|
{/snippet}
|
|
</DrawerContent>
|
|
</Drawer>
|