diff --git a/app/(app)/admin/jobs/page.tsx b/app/(app)/admin/jobs/page.tsx
index b1c9920..76d0825 100644
--- a/app/(app)/admin/jobs/page.tsx
+++ b/app/(app)/admin/jobs/page.tsx
@@ -16,15 +16,34 @@ export default async function AdminJobsPage() {
branch: true,
pr_url: true,
error: true,
+ model_id: true,
+ input_tokens: true,
+ output_tokens: true,
+ cache_read_tokens: true,
+ cache_write_tokens: true,
user: { select: { username: true } },
product: { select: { name: true } },
},
})
+ const prices = await prisma.modelPrice.findMany()
+ const priceMap = new Map(prices.map((p) => [p.model_id, p]))
+
+ const jobsWithCost = jobs.map((job) => {
+ const p = job.model_id ? priceMap.get(job.model_id) : undefined
+ if (!p || job.input_tokens == null) return { ...job, cost_usd: null }
+ const cost =
+ (job.input_tokens ?? 0) * Number(p.input_price_per_1m) / 1_000_000 +
+ (job.output_tokens ?? 0) * Number(p.output_price_per_1m) / 1_000_000 +
+ (job.cache_read_tokens ?? 0) * Number(p.cache_read_price_per_1m) / 1_000_000 +
+ (job.cache_write_tokens ?? 0) * Number(p.cache_write_price_per_1m) / 1_000_000
+ return { ...job, cost_usd: cost }
+ })
+
return (
Claude Jobs
-
+
)
}
diff --git a/components/admin/jobs-table.tsx b/components/admin/jobs-table.tsx
index a241549..3b1c312 100644
--- a/components/admin/jobs-table.tsx
+++ b/components/admin/jobs-table.tsx
@@ -1,6 +1,6 @@
'use client'
-import { useTransition } from 'react'
+import { useState, useTransition } from 'react'
import { Badge } from '@/components/ui/badge'
import { Button } from '@/components/ui/button'
import {
@@ -23,6 +23,8 @@ type Job = {
branch: string | null
pr_url: string | null
error: string | null
+ model_id: string | null
+ cost_usd: number | null
}
const STATUS_CLASS: Record = {
@@ -92,7 +94,7 @@ function JobRow({ job }: { job: Job }) {
)
}
-export function JobsTable({ jobs }: { jobs: Job[] }) {
+function StatusTable({ jobs }: { jobs: Job[] }) {
return (
@@ -123,3 +125,86 @@ export function JobsTable({ jobs }: { jobs: 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)}` : '—'
+ return (
+
+ {job.id.slice(0, 8)}
+ {job.user.username}
+ {job.product.name}
+ {KIND_LABEL[job.kind] ?? job.kind}
+ {job.model_id ?? '—'}
+ {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
+ 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' ?
:
}
+
+ )
+}