Files
FictionArchive/fictionarchive-web-astro/src/lib/components/JobRow.svelte

169 lines
4.9 KiB
Svelte

<script lang="ts">
import { formatDistanceToNow, format } from 'date-fns';
import ChevronRight from '@lucide/svelte/icons/chevron-right';
import JobStatusBadge from './JobStatusBadge.svelte';
import * as Tooltip from '$lib/components/ui/tooltip';
import { cn } from '$lib/utils';
interface MetadataEntry {
key: string;
value: string;
}
interface ChildJob {
id: string;
jobType: string;
displayName: string;
status: string;
errorMessage: string | null;
metadata: MetadataEntry[] | null;
createdTime: string;
lastUpdatedTime: string;
}
interface JobNode {
id: string;
parentJobId: string | null;
jobType: string;
displayName: string;
status: string;
errorMessage: string | null;
metadata: MetadataEntry[] | null;
createdTime: string;
lastUpdatedTime: string;
childJobs: ChildJob[] | null;
}
interface Props {
job: JobNode;
expanded: boolean;
onToggle: () => void;
columnCount: number;
}
let { job, expanded, onToggle, columnCount }: Props = $props();
const children = $derived(job.childJobs ?? []);
const hasChildren = $derived(children.length > 0);
const metadata = $derived(job.metadata ?? []);
function formatTime(iso: string): string {
try {
return formatDistanceToNow(new Date(iso), { addSuffix: true });
} catch {
return iso;
}
}
function formatTimeFull(iso: string): string {
try {
return format(new Date(iso), 'yyyy-MM-dd HH:mm:ss');
} catch {
return iso;
}
}
</script>
<!-- Main row -->
<tr class={cn("border-b hover:bg-muted/50 transition-colors", expanded && "bg-muted/30")}>
<td class="w-10 px-3 py-3">
{#if hasChildren}
<button onclick={onToggle} class={cn("hover:bg-accent rounded p-1 transition-transform", expanded && "rotate-90")}>
<ChevronRight class="h-4 w-4" />
</button>
{/if}
</td>
<td class="px-3 py-3 font-medium">{job.displayName}</td>
<td class="px-3 py-3 text-muted-foreground text-sm">{job.jobType}</td>
<td class="px-3 py-3"><JobStatusBadge status={job.status} /></td>
<td class="px-3 py-3 text-sm text-muted-foreground">
<Tooltip.Provider>
<Tooltip.Root>
<Tooltip.Trigger class="cursor-default">{formatTime(job.createdTime)}</Tooltip.Trigger>
<Tooltip.Content>{formatTimeFull(job.createdTime)}</Tooltip.Content>
</Tooltip.Root>
</Tooltip.Provider>
</td>
<td class="px-3 py-3 text-sm text-muted-foreground">
{#if hasChildren}
{children.length} sub-job{children.length !== 1 ? 's' : ''}
{/if}
</td>
</tr>
<!-- Expanded detail -->
{#if expanded}
<tr class="border-b bg-muted/20">
<td colspan={columnCount} class="px-6 py-4">
<div class="space-y-4">
<!-- Job details -->
<div class="grid grid-cols-2 gap-x-8 gap-y-2 text-sm max-w-lg">
<span class="text-muted-foreground">Last Updated</span>
<span>{formatTimeFull(job.lastUpdatedTime)}</span>
</div>
{#if job.errorMessage}
<div class="text-sm">
<span class="text-muted-foreground font-medium">Error: </span>
<span class="text-destructive">{job.errorMessage}</span>
</div>
{/if}
<!-- Metadata -->
{#if metadata.length > 0}
<div>
<h4 class="text-sm font-medium text-muted-foreground mb-2">Metadata</h4>
<div class="grid grid-cols-2 gap-x-8 gap-y-1 text-sm max-w-lg">
{#each metadata as entry (entry.key)}
<span class="text-muted-foreground">{entry.key}</span>
<span class="break-all">{entry.value}</span>
{/each}
</div>
</div>
{/if}
<!-- Sub-jobs table -->
{#if hasChildren}
<div>
<h4 class="text-sm font-medium text-muted-foreground mb-2">Sub-jobs</h4>
<table class="w-full text-sm">
<thead>
<tr class="border-b text-left text-muted-foreground">
<th class="px-3 py-2 font-medium">Name</th>
<th class="px-3 py-2 font-medium">Type</th>
<th class="px-3 py-2 font-medium">Status</th>
<th class="px-3 py-2 font-medium">Created</th>
</tr>
</thead>
<tbody>
{#each children as child (child.id)}
<tr class="border-b last:border-0">
<td class="px-3 py-2">{child.displayName}</td>
<td class="px-3 py-2 text-muted-foreground">{child.jobType}</td>
<td class="px-3 py-2"><JobStatusBadge status={child.status} /></td>
<td class="px-3 py-2 text-muted-foreground">
<Tooltip.Provider>
<Tooltip.Root>
<Tooltip.Trigger class="cursor-default">{formatTime(child.createdTime)}</Tooltip.Trigger>
<Tooltip.Content>{formatTimeFull(child.createdTime)}</Tooltip.Content>
</Tooltip.Root>
</Tooltip.Provider>
</td>
</tr>
{#if child.errorMessage}
<tr class="border-b last:border-0">
<td colspan="4" class="px-3 pb-2">
<span class="text-destructive text-xs">{child.errorMessage}</span>
</td>
</tr>
{/if}
{/each}
</tbody>
</table>
</div>
{/if}
</div>
</td>
</tr>
{/if}