2024-04-01 21:34:52 +03:00
|
|
|
import { client } from "@/api/AxiosClient";
|
|
|
|
|
import { Status, TaskApiResponse } from "@/api/types";
|
|
|
|
|
import { Label } from "@/components/ui/label";
|
|
|
|
|
import { Textarea } from "@/components/ui/textarea";
|
2024-04-24 17:11:12 +03:00
|
|
|
import { keepPreviousData, useQuery } from "@tanstack/react-query";
|
2024-04-01 21:34:52 +03:00
|
|
|
import { useParams } from "react-router-dom";
|
2024-04-07 21:52:59 +03:00
|
|
|
import { StatusBadge } from "@/components/StatusBadge";
|
2024-04-01 21:34:52 +03:00
|
|
|
import { basicTimeFormat } from "@/util/timeFormat";
|
2024-04-07 21:52:59 +03:00
|
|
|
import { StepArtifactsLayout } from "./StepArtifactsLayout";
|
2024-04-16 20:40:59 +03:00
|
|
|
import { getRecordingURL, getScreenshotURL } from "./artifactUtils";
|
2024-04-23 13:54:04 -07:00
|
|
|
import { Skeleton } from "@/components/ui/skeleton";
|
2024-04-24 17:11:12 +03:00
|
|
|
import { Input } from "@/components/ui/input";
|
|
|
|
|
import { ZoomableImage } from "@/components/ZoomableImage";
|
|
|
|
|
import {
|
|
|
|
|
Card,
|
|
|
|
|
CardContent,
|
|
|
|
|
CardDescription,
|
|
|
|
|
CardHeader,
|
|
|
|
|
CardTitle,
|
|
|
|
|
} from "@/components/ui/card";
|
|
|
|
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
|
|
|
|
import { Separator } from "@/components/ui/separator";
|
2024-04-01 21:34:52 +03:00
|
|
|
|
|
|
|
|
function TaskDetails() {
|
|
|
|
|
const { taskId } = useParams();
|
|
|
|
|
|
|
|
|
|
const {
|
|
|
|
|
data: task,
|
|
|
|
|
isFetching: isTaskFetching,
|
|
|
|
|
isError: isTaskError,
|
|
|
|
|
error: taskError,
|
|
|
|
|
} = useQuery<TaskApiResponse>({
|
2024-04-07 21:52:59 +03:00
|
|
|
queryKey: ["task", taskId, "details"],
|
2024-04-01 21:34:52 +03:00
|
|
|
queryFn: async () => {
|
|
|
|
|
return client.get(`/tasks/${taskId}`).then((response) => response.data);
|
|
|
|
|
},
|
2024-04-24 17:11:12 +03:00
|
|
|
refetchInterval: (query) => {
|
|
|
|
|
if (
|
|
|
|
|
query.state.data?.status === Status.Running ||
|
|
|
|
|
query.state.data?.status === Status.Queued
|
|
|
|
|
) {
|
2024-04-24 21:14:05 +03:00
|
|
|
return 30000;
|
2024-04-24 17:11:12 +03:00
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
},
|
|
|
|
|
placeholderData: keepPreviousData,
|
2024-04-01 21:34:52 +03:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (isTaskError) {
|
|
|
|
|
return <div>Error: {taskError?.message}</div>;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return (
|
2024-04-29 23:41:58 +03:00
|
|
|
<div className="flex flex-col gap-8 max-w-6xl mx-auto p-8 pt-0">
|
2024-04-24 17:11:12 +03:00
|
|
|
<div className="flex items-center">
|
|
|
|
|
<Label className="w-32 shrink-0 text-lg">Task ID</Label>
|
|
|
|
|
<Input value={taskId} readOnly />
|
|
|
|
|
</div>
|
|
|
|
|
<div className="flex items-center">
|
|
|
|
|
<Label className="w-32 text-lg">Status</Label>
|
|
|
|
|
{isTaskFetching ? (
|
|
|
|
|
<Skeleton className="w-32 h-8" />
|
|
|
|
|
) : task ? (
|
|
|
|
|
<StatusBadge status={task?.status} />
|
2024-04-01 21:34:52 +03:00
|
|
|
) : null}
|
2024-04-24 17:11:12 +03:00
|
|
|
</div>
|
|
|
|
|
{task?.status === Status.Completed ? (
|
2024-04-01 21:34:52 +03:00
|
|
|
<div className="flex items-center">
|
2024-04-24 17:11:12 +03:00
|
|
|
<Label className="w-32 shrink-0 text-lg">Extracted Information</Label>
|
|
|
|
|
<Textarea
|
|
|
|
|
rows={5}
|
|
|
|
|
value={JSON.stringify(task.extracted_information, null, 2)}
|
|
|
|
|
readOnly
|
|
|
|
|
/>
|
2024-04-01 21:34:52 +03:00
|
|
|
</div>
|
2024-04-24 17:11:12 +03:00
|
|
|
) : null}
|
|
|
|
|
{task?.status === Status.Failed || task?.status === Status.Terminated ? (
|
|
|
|
|
<div className="flex items-center">
|
|
|
|
|
<Label className="w-32 shrink-0 text-lg">Failure Reason</Label>
|
|
|
|
|
<Textarea
|
|
|
|
|
rows={5}
|
|
|
|
|
value={JSON.stringify(task.failure_reason)}
|
|
|
|
|
readOnly
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
) : null}
|
|
|
|
|
{task ? (
|
|
|
|
|
<Card>
|
|
|
|
|
<CardHeader className="border-b-2">
|
|
|
|
|
<CardTitle className="text-xl">Task Artifacts</CardTitle>
|
|
|
|
|
<CardDescription>
|
|
|
|
|
Recording and final screenshot of the task
|
|
|
|
|
</CardDescription>
|
|
|
|
|
</CardHeader>
|
|
|
|
|
<CardContent>
|
|
|
|
|
<Tabs defaultValue="recording">
|
|
|
|
|
<TabsList>
|
|
|
|
|
<TabsTrigger value="recording">Recording</TabsTrigger>
|
|
|
|
|
<TabsTrigger value="final-screenshot">
|
|
|
|
|
Final Screenshot
|
|
|
|
|
</TabsTrigger>
|
|
|
|
|
</TabsList>
|
|
|
|
|
<TabsContent value="recording">
|
|
|
|
|
{task.recording_url ? (
|
|
|
|
|
<video
|
|
|
|
|
width={800}
|
|
|
|
|
height={450}
|
|
|
|
|
src={getRecordingURL(task)}
|
|
|
|
|
controls
|
2024-04-23 13:54:04 -07:00
|
|
|
/>
|
|
|
|
|
) : (
|
2024-04-24 17:11:12 +03:00
|
|
|
<div>No recording available</div>
|
2024-04-23 13:54:04 -07:00
|
|
|
)}
|
2024-04-24 17:11:12 +03:00
|
|
|
</TabsContent>
|
|
|
|
|
<TabsContent value="final-screenshot">
|
|
|
|
|
{task ? (
|
|
|
|
|
<div className="h-[450px] w-[800px]">
|
|
|
|
|
{task.screenshot_url ? (
|
|
|
|
|
<ZoomableImage
|
|
|
|
|
src={getScreenshotURL(task)}
|
|
|
|
|
alt="screenshot"
|
|
|
|
|
className="object-cover w-full h-full"
|
|
|
|
|
/>
|
|
|
|
|
) : (
|
|
|
|
|
<p>No screenshot available</p>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
) : null}
|
|
|
|
|
</TabsContent>
|
|
|
|
|
</Tabs>
|
|
|
|
|
</CardContent>
|
|
|
|
|
</Card>
|
|
|
|
|
) : null}
|
2024-04-24 18:14:20 +03:00
|
|
|
<Card>
|
|
|
|
|
<CardHeader className="border-b-2">
|
|
|
|
|
<CardTitle className="text-lg">Steps</CardTitle>
|
|
|
|
|
<CardDescription>Task Steps and Step Artifacts</CardDescription>
|
|
|
|
|
</CardHeader>
|
|
|
|
|
<CardContent className="min-h-96">
|
|
|
|
|
<StepArtifactsLayout />
|
|
|
|
|
</CardContent>
|
|
|
|
|
</Card>
|
2024-04-24 17:11:12 +03:00
|
|
|
<Card>
|
|
|
|
|
<CardHeader className="border-b-2">
|
|
|
|
|
<CardTitle className="text-xl">Parameters</CardTitle>
|
|
|
|
|
<CardDescription>Task URL and Input Parameters</CardDescription>
|
|
|
|
|
</CardHeader>
|
|
|
|
|
<CardContent className="py-8">
|
|
|
|
|
{task ? (
|
|
|
|
|
<div className="flex flex-col gap-8">
|
|
|
|
|
<div className="flex items-center">
|
|
|
|
|
<Label className="w-40 shrink-0">URL</Label>
|
|
|
|
|
<Input value={task.request.url} readOnly />
|
|
|
|
|
</div>
|
|
|
|
|
<Separator />
|
|
|
|
|
<div className="flex items-center">
|
|
|
|
|
<Label className="w-40 shrink-0">Created at</Label>
|
|
|
|
|
<Input value={basicTimeFormat(task.created_at)} readOnly />
|
2024-04-23 13:54:04 -07:00
|
|
|
</div>
|
2024-04-24 17:11:12 +03:00
|
|
|
<Separator />
|
|
|
|
|
<div className="flex items-center">
|
|
|
|
|
<Label className="w-40 shrink-0">Navigation Goal</Label>
|
|
|
|
|
<Textarea
|
|
|
|
|
rows={5}
|
|
|
|
|
value={task.request.navigation_goal}
|
|
|
|
|
readOnly
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
<Separator />
|
|
|
|
|
<div className="flex items-center">
|
|
|
|
|
<Label className="w-40 shrink-0">Navigation Payload</Label>
|
|
|
|
|
<Textarea
|
|
|
|
|
rows={5}
|
2024-04-24 22:30:13 +03:00
|
|
|
value={
|
|
|
|
|
typeof task.request.navigation_payload === "object"
|
|
|
|
|
? JSON.stringify(task.request.navigation_payload, null, 2)
|
|
|
|
|
: task.request.navigation_payload
|
|
|
|
|
}
|
2024-04-24 17:11:12 +03:00
|
|
|
readOnly
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
<Separator />
|
|
|
|
|
<div className="flex items-center">
|
|
|
|
|
<Label className="w-40 shrink-0">Data Extraction Goal</Label>
|
|
|
|
|
<Textarea
|
|
|
|
|
rows={5}
|
|
|
|
|
value={task.request.data_extraction_goal}
|
|
|
|
|
readOnly
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
) : null}
|
|
|
|
|
</CardContent>
|
|
|
|
|
</Card>
|
2024-04-01 21:34:52 +03:00
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export { TaskDetails };
|