'use client' import { useState, useTransition } from 'react' import { Badge } from '@/components/ui/badge' import { Button } from '@/components/ui/button' import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from '@/components/ui/table' import { cancelJobAction, deleteJobAction } from '@/actions/admin/jobs' import { debugProps } from '@/lib/debug' type Job = { id: string kind: string status: string created_at: Date user: { username: string } product: { name: string } branch: string | null pr_url: string | null error: string | null model_id: string | null actual_thinking_tokens: number | null requested_model: string | null requested_thinking_budget: number | null requested_permission_mode: string | null cost_usd: number | null } const STATUS_CLASS: Record = { QUEUED: 'bg-secondary text-secondary-foreground', CLAIMED: 'bg-status-in-progress text-white border-transparent', RUNNING: 'bg-warning text-warning-foreground border-transparent', DONE: 'bg-status-done text-white border-transparent', FAILED: 'bg-priority-high text-white border-transparent', CANCELLED: 'bg-muted text-muted-foreground', SKIPPED: 'bg-muted/60 text-muted-foreground italic border-transparent', } const KIND_LABEL: Record = { TASK_IMPLEMENTATION: 'Taak', IDEA_GRILL: 'Idee Grill', IDEA_MAKE_PLAN: 'Idee Plan', } const ACTIVE_STATUSES = new Set(['QUEUED', 'CLAIMED', 'RUNNING']) function JobRow({ job }: { job: Job }) { const [pending, startTransition] = useTransition() function handleCancel() { startTransition(() => cancelJobAction(job.id)) } function handleDelete() { startTransition(() => deleteJobAction(job.id)) } return ( {job.id.slice(0, 8)} {job.user.username} {job.product.name} {KIND_LABEL[job.kind] ?? job.kind} {job.status} {job.branch ?? '—'} {new Date(job.created_at).toLocaleString('nl-NL', { dateStyle: 'short', timeStyle: 'short' })} {job.error && ( {job.error} )}
{ACTIVE_STATUSES.has(job.status) && ( )}
) } function StatusTable({ jobs }: { jobs: Job[] }) { return ( ID Gebruiker Product Type Status Branch Aangemaakt Fout Acties {jobs.length === 0 && ( Geen jobs gevonden )} {jobs.map(job => ( ))}
) } function CostRow({ job }: { job: Job }) { const [pending, startTransition] = useTransition() function handleCancel() { startTransition(() => cancelJobAction(job.id)) } function handleDelete() { startTransition(() => deleteJobAction(job.id)) } const costLabel = job.cost_usd != null ? `$${job.cost_usd.toFixed(4)}` : '—' const thinkingLabel = job.actual_thinking_tokens != null ? job.actual_thinking_tokens.toLocaleString('nl-NL') : '—' const modelMismatch = job.requested_model != null && job.model_id != null && job.requested_model !== job.model_id const modelTitle = job.requested_model ? `Aangevraagd: ${job.requested_model}${modelMismatch ? ' (mismatch met actueel)' : ''}` : undefined return ( {job.id.slice(0, 8)} {job.user.username} {job.product.name} {KIND_LABEL[job.kind] ?? job.kind} {job.model_id ?? '—'} {thinkingLabel} {costLabel} {new Date(job.created_at).toLocaleString('nl-NL', { dateStyle: 'short', timeStyle: 'short' })}
{ACTIVE_STATUSES.has(job.status) && ( )}
) } function CostsTable({ jobs }: { jobs: Job[] }) { return ( ID Gebruiker Product Type Model Thinking Kosten (USD) Aangemaakt Acties {jobs.length === 0 && ( Geen jobs gevonden )} {jobs.map((job) => )}
) } export function JobsTable({ jobs }: { jobs: Job[] }) { const [view, setView] = useState<'status' | 'costs'>('status') return (
{view === 'status' ? : }
) }