Files
windmill/frontend/src/lib/components/scriptEditor/LogPanel.svelte
T

221 lines
6.6 KiB
Svelte

<script lang="ts">
import { CompletedJob, Job, JobService, Preview } from '$lib/gen'
import { workspaceStore } from '$lib/stores'
import { displayDate } from '$lib/utils'
import { faTimes } from '@fortawesome/free-solid-svg-icons'
import Icon from 'svelte-awesome'
import { check } from 'svelte-awesome/icons'
import Tabs from '../common/tabs/Tabs.svelte'
import Tab from '../common/tabs/Tab.svelte'
import TabContent from '../common/tabs/TabContent.svelte'
import DisplayResult from '../DisplayResult.svelte'
import TableCustom from '../TableCustom.svelte'
import Drawer from '../common/drawer/Drawer.svelte'
import DrawerContent from '../common/drawer/DrawerContent.svelte'
import HighlightCode from '../HighlightCode.svelte'
import LogViewer from '../LogViewer.svelte'
import { Pane, Splitpanes } from 'svelte-splitpanes'
import SplitPanesWrapper from '../splitPanes/SplitPanesWrapper.svelte'
import { Loader2 } from 'lucide-svelte'
import type Editor from '../Editor.svelte'
import type DiffEditor from '../DiffEditor.svelte'
import ScriptFix from '../copilot/ScriptFix.svelte'
export let lang: Preview.language | undefined
export let previewIsLoading = false
export let previewJob: Job | undefined
export let pastPreviews: CompletedJob[] = []
export let editor: Editor | undefined = undefined
export let diffEditor: DiffEditor | undefined = undefined
export let args: Record<string, any> | undefined = undefined
type DrawerContent = {
mode: 'json' | Preview.language | 'plain'
title: string
content: any
}
let selectedTab = 'logs'
let drawerOpen: boolean = false
let drawerContent: DrawerContent | undefined = undefined
export function setFocusToLogs() {
selectedTab = 'logs'
}
function openDrawer(newContent: DrawerContent) {
drawerContent = newContent
drawerOpen = true
}
function closeDrawer() {
drawerOpen = false
}
</script>
<Drawer bind:open={drawerOpen} size="800px">
<DrawerContent title={drawerContent?.title} on:close={() => closeDrawer()}>
{#if drawerContent?.content == undefined}
<div class="p-2"> <Loader2 class="animate-spin" /> </div>
{:else if drawerContent?.mode === 'json'}
<DisplayResult
workspaceId={previewJob?.workspace_id}
jobId={previewJob?.id}
result={drawerContent.content}
/>
{:else if drawerContent?.mode === 'plain'}
<pre
class="overflow-x-auto break-words relative h-full m-2 text-xs bg-surface shadow-inner p-2"
>{drawerContent?.content}
</pre>
{:else if drawerContent?.mode === 'deno' || drawerContent?.mode === 'python3' || drawerContent?.mode === 'go' || drawerContent?.mode === 'bash' || drawerContent?.mode === 'nativets'}
<HighlightCode language={drawerContent?.mode} code={drawerContent?.content} />
{/if}
</DrawerContent>
</Drawer>
<Tabs bind:selected={selectedTab} class="mt-1">
<Tab value="logs" size="xs">Logs & Result</Tab>
<Tab value="history" size="xs">History</Tab>
<svelte:fragment slot="content">
<!--
TabContent would wrap it in an extra div which is undesirable in this situation
because SplitPanesWrapper uses the parent element as a reference point.
-->
{#if selectedTab === 'logs'}
<SplitPanesWrapper>
<Splitpanes horizontal>
<Pane class="relative">
<LogViewer
jobId={previewJob?.id}
duration={previewJob?.['duration_ms']}
mem={previewJob?.['mem_peak']}
content={previewJob?.logs}
isLoading={previewIsLoading}
/>
</Pane>
<Pane>
{#if previewJob != undefined && 'result' in previewJob}
<div class="relative w-full h-full p-2">
<DisplayResult
workspaceId={previewJob?.workspace_id}
jobId={previewJob?.id}
result={previewJob.result}
>
<svelte:fragment slot="copilot-fix">
{#if lang && editor && diffEditor && args && previewJob?.result?.error}
<ScriptFix
error={JSON.stringify(previewJob.result.error)}
{lang}
{editor}
{diffEditor}
{args}
/>
{/if}
</svelte:fragment>
</DisplayResult>
</div>
{:else}
<div class="text-sm text-tertiary p-2">
{#if previewIsLoading}
<Loader2 class="animate-spin" />
{:else}
Test to see the result here
{/if}
</div>
{/if}
</Pane>
</Splitpanes>
</SplitPanesWrapper>
{/if}
<TabContent value="history" class="p-2">
<TableCustom>
<tr slot="header-row">
<th class="text-xs">Id</th>
<th class="text-xs">Created at</th>
<th class="text-xs">Success</th>
<th class="text-xs">Result</th>
<th class="text-xs">Code</th>
<th class="text-xs">Logs</th>
</tr>
<tbody slot="body">
{#each pastPreviews as { id, created_at, success }}
<tr class="">
<td class="text-xs">
<a class="pr-3" href="/run/{id}?workspace={$workspaceStore}" target="_blank"
>{id.substring(30)}</a
>
</td>
<td class="text-xs">{displayDate(created_at)}</td>
<td class="text-xs">
{#if success}
<Icon class="text-green-600" data={check} scale={0.6} />
{:else}
<Icon class="text-red-700" data={faTimes} scale={0.6} />
{/if}
</td>
<td class="text-xs">
<a
href="#result"
class="text-xs"
on:click={() => {
openDrawer({ mode: 'json', content: undefined, title: 'Result' })
JobService.getCompletedJobResult({
workspace: $workspaceStore ?? 'NO_W',
id
}).then((res) => {
drawerContent && (drawerContent.content = res)
})
}}
>
See Result
</a>
</td>
<td class="text-xs">
<a
href="#code"
class="text-xs"
on:click={async () => {
const code = (
await JobService.getCompletedJob({
workspace: $workspaceStore ?? 'NO_W',
id
})
).raw_code
openDrawer({
mode: lang ?? 'plain',
content: String(code),
title: `Code ${lang}`
})
}}
>
View code
</a>
</td>
<td>
<a
href="#logs"
class="text-xs"
on:click={async () => {
const logs = (
await JobService.getCompletedJob({
workspace: $workspaceStore ?? 'NO_W',
id
})
).logs
openDrawer({ mode: 'plain', content: String(logs), title: `Logs for ${id}` })
}}
>
View logs
</a>
</td>
</tr>
{/each}
</tbody>
</TableCustom>
</TabContent>
</svelte:fragment>
</Tabs>