- app/(app)/admin/layout.tsx: sidebar-nav (Gebruikers/Claude Jobs/Producten) - app/(app)/admin/page.tsx: redirect naar /admin/jobs - app/(app)/admin/jobs/page.tsx: server component, query jobs+user+product, status-filter via searchParams - components/admin/jobs-table.tsx: StatusFilter (router.push op select), JobsTable met status-badges (MD3 tokens), CancelButton (disabled in eindstatus), DeleteDialog
42 lines
1.2 KiB
TypeScript
42 lines
1.2 KiB
TypeScript
import { requireAdmin } from '@/lib/auth-guard'
|
|
import { prisma } from '@/lib/prisma'
|
|
import type { ClaudeJobStatus } from '@prisma/client'
|
|
import { JobsTable, StatusFilter } from '@/components/admin/jobs-table'
|
|
|
|
const VALID_STATUSES = new Set<ClaudeJobStatus>([
|
|
'QUEUED', 'CLAIMED', 'RUNNING', 'DONE', 'FAILED', 'CANCELLED',
|
|
])
|
|
|
|
export default async function AdminJobsPage({
|
|
searchParams,
|
|
}: {
|
|
searchParams: Promise<{ status?: string }>
|
|
}) {
|
|
await requireAdmin()
|
|
|
|
const params = await searchParams
|
|
const rawStatus = params.status ?? ''
|
|
const statusFilter = VALID_STATUSES.has(rawStatus as ClaudeJobStatus)
|
|
? (rawStatus as ClaudeJobStatus)
|
|
: undefined
|
|
|
|
const jobs = await prisma.claudeJob.findMany({
|
|
where: statusFilter ? { status: statusFilter } : undefined,
|
|
include: {
|
|
user: { select: { username: true } },
|
|
product: { select: { name: true } },
|
|
},
|
|
orderBy: { created_at: 'desc' },
|
|
take: 200,
|
|
})
|
|
|
|
return (
|
|
<div className="space-y-4">
|
|
<div className="flex items-center justify-between">
|
|
<h1 className="text-xl font-semibold text-foreground">Claude Jobs</h1>
|
|
<StatusFilter current={rawStatus} />
|
|
</div>
|
|
<JobsTable jobs={jobs} />
|
|
</div>
|
|
)
|
|
}
|