feat: run log dialog

This commit is contained in:
bennykok 2024-01-31 15:17:47 +08:00
parent 009589630d
commit 3e5ff7702e
5 changed files with 84 additions and 11 deletions

View File

@ -7,10 +7,12 @@ import { toast } from "sonner";
export function CopyButton({ export function CopyButton({
className, className,
children,
...props ...props
}: { }: {
text: string; text: string;
className?: string; className?: string;
children?: React.ReactNode;
}) { }) {
return ( return (
<Button <Button
@ -21,7 +23,7 @@ export function CopyButton({
}} }}
className={cn(" p-2 min-h-0 aspect-square", className)} className={cn(" p-2 min-h-0 aspect-square", className)}
> >
<Copy size={14} /> {children} <Copy size={14} />
</Button> </Button>
); );
} }

View File

@ -1,6 +1,7 @@
"use client"; "use client";
import React, { useEffect, useRef } from "react"; import React, { useEffect, useRef } from "react";
import { toast } from "sonner";
export type LogsType = { export type LogsType = {
machine_id?: string; machine_id?: string;
@ -36,10 +37,24 @@ export function LogsViewer({ logs }: { logs: LogsType }) {
} }
container.current = ref; container.current = ref;
}} }}
className="flex flex-col text-xs p-2 overflow-y-scroll max-h-[400px] whitespace-break-spaces" className="flex flex-col text-xs p-2 overflow-y-scroll whitespace-break-spaces"
> >
{logs.map((x, i) => ( {logs.map((x, i) => (
<div key={i}>{x.logs}</div> <div
key={i}
className="hover:bg-gray-100 flex flex-row items-center gap-2"
onClick={() => {
toast.success("Copied to clipboard");
navigator.clipboard.writeText(x.logs);
}}
>
<span className="min-w-fit">
{new Date(x.timestamp).toLocaleString()}
</span>
<div className="h-full w-[1px] bg-stone-400 flex-shrink-0"></div>
{/* Display timestamp */}
<div>{x.logs}</div>
</div>
))} ))}
</div> </div>
); );

View File

@ -3,8 +3,10 @@ import { RunOutputs } from "@/components/RunOutputs";
import { Badge } from "@/components/ui/badge"; import { Badge } from "@/components/ui/badge";
import { import {
Dialog, Dialog,
DialogClose,
DialogContent, DialogContent,
DialogDescription, DialogDescription,
DialogFooter,
DialogHeader, DialogHeader,
DialogTitle, DialogTitle,
DialogTrigger, DialogTrigger,
@ -20,6 +22,8 @@ import { type findAllRuns } from "@/server/findAllRuns";
import { Suspense } from "react"; import { Suspense } from "react";
import { LiveStatus } from "./LiveStatus"; import { LiveStatus } from "./LiveStatus";
import { LogsType, LogsViewer } from "@/components/LogsViewer"; import { LogsType, LogsViewer } from "@/components/LogsViewer";
import { Button } from "@/components/ui/button";
import { Edit, ExternalLink } from "lucide-react";
export async function RunDisplay({ export async function RunDisplay({
run, run,
@ -75,8 +79,7 @@ export async function RunDisplay({
<div className="max-h-96 overflow-y-scroll"> <div className="max-h-96 overflow-y-scroll">
<RunInputs run={run} /> <RunInputs run={run} />
<Suspense> <Suspense>
<RunOutputs run_id={run.id} /> <RunOutputs run={run} />
{run.run_log && <LogsViewer logs={run.run_log} />}
</Suspense> </Suspense>
</div> </div>
{/* <div className="max-h-96 overflow-y-scroll">{view}</div> */} {/* <div className="max-h-96 overflow-y-scroll">{view}</div> */}

View File

@ -1,3 +1,13 @@
import {
Dialog,
DialogClose,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog";
import { OutputRender } from "./OutputRender"; import { OutputRender } from "./OutputRender";
import { CodeBlock } from "@/components/CodeBlock"; import { CodeBlock } from "@/components/CodeBlock";
import { import {
@ -8,10 +18,17 @@ import {
TableHeader, TableHeader,
TableRow, TableRow,
} from "@/components/ui/table"; } from "@/components/ui/table";
import type { findAllRuns } from "@/server/findAllRuns";
import { getRunsOutput } from "@/server/getRunsOutput"; import { getRunsOutput } from "@/server/getRunsOutput";
import { Button } from "@/components/ui/button";
import { ExternalLink } from "lucide-react";
import { LogsViewer } from "@/components/LogsViewer";
import { CopyButton } from "@/components/CopyButton";
export async function RunOutputs({ run_id }: { run_id: string }) { export async function RunOutputs({
const outputs = await getRunsOutput(run_id); run,
}: { run: Awaited<ReturnType<typeof findAllRuns>>[0] }) {
const outputs = await getRunsOutput(run.id);
return ( return (
<Table className="table-fixed"> <Table className="table-fixed">
<TableHeader className="bg-background top-0 sticky"> <TableHeader className="bg-background top-0 sticky">
@ -21,6 +38,42 @@ export async function RunOutputs({ run_id }: { run_id: string }) {
</TableRow> </TableRow>
</TableHeader> </TableHeader>
<TableBody> <TableBody>
<TableRow key={run.id}>
<TableCell className="break-words">Run log</TableCell>
<TableCell>
{run.run_log ? (
<Dialog>
<DialogTrigger asChild>
<Button variant="secondary" className="w-fit">
View Log <ExternalLink size={14} />
</Button>
</DialogTrigger>
<DialogContent className="sm:max-w-[1000px] h-full max-h-[600px] grid grid-rows-[auto,1fr,auto]">
<DialogHeader>
<DialogTitle>Run Log</DialogTitle>
</DialogHeader>
<LogsViewer logs={run.run_log} />
<DialogFooter>
<CopyButton
className="w-fit aspect-auto p-4"
text={JSON.stringify(run.run_log)}
>
Copy
</CopyButton>
<DialogClose>
<Button type="button" variant="secondary">
Close
</Button>
</DialogClose>
</DialogFooter>
</DialogContent>
</Dialog>
) : (
"No log available"
)}
</TableCell>
</TableRow>
{outputs?.map((run) => { {outputs?.map((run) => {
const fileName = const fileName =
run.data.images?.[0].filename || run.data.images?.[0].filename ||
@ -45,7 +98,7 @@ export async function RunOutputs({ run_id }: { run_id: string }) {
<TableRow key={run.id}> <TableRow key={run.id}>
<TableCell className="break-words">{fileName}</TableCell> <TableCell className="break-words">{fileName}</TableCell>
<TableCell> <TableCell>
<OutputRender run_id={run_id} filename={fileName} /> <OutputRender run_id={run.run_id} filename={fileName} />
</TableCell> </TableCell>
</TableRow> </TableRow>
); );

View File

@ -5,9 +5,9 @@ import { db } from "@/db/db";
import { workflowRunOutputs } from "@/db/schema"; import { workflowRunOutputs } from "@/db/schema";
import { eq } from "drizzle-orm"; import { eq } from "drizzle-orm";
export async function getRunsOutputDisplay(run_id: string) { // export async function getRunsOutputDisplay(run_id: string) {
return <RunOutputs run_id={run_id} />; // return <RunOutputs run_id={run_id} />;
} // }
export async function getRunsOutput(run_id: string) { export async function getRunsOutput(run_id: string) {
// throw new Error("Not implemented"); // throw new Error("Not implemented");