"use client" import { Badge } from "@/components/ui/badge" import { Button } from "@/components/ui/button" import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select" import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table" import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip" import { type ColumnDef, type ColumnFiltersState, type SortingState, type VisibilityState, flexRender, getCoreRowModel, getFilteredRowModel, getPaginationRowModel, getSortedRowModel, useReactTable, } from "@tanstack/react-table" import { cva } from "class-variance-authority" import { AlertCircle, ArrowUpDown, RefreshCwIcon } from "lucide-react" import * as React from "react" import { CopyIconButton } from "../../components/copyIconButton" import { useMemo } from "react" import { LightweightCodeHighlighter } from "../../components/lightweightCodeHighlighter" import { useRouter } from "next/navigation" import { useToast } from "@/components/hooks/use-toast" import { DisplayDate } from "../../components/DisplayDate" // @see: https://v0.app/chat/repo-indexing-status-uhjdDim8OUS export type RepoIndexingJob = { id: string type: "INDEX" | "CLEANUP" status: "PENDING" | "IN_PROGRESS" | "COMPLETED" | "FAILED" createdAt: Date updatedAt: Date completedAt: Date | null errorMessage: string | null } const statusBadgeVariants = cva("", { variants: { status: { PENDING: "bg-secondary text-secondary-foreground hover:bg-secondary/80", IN_PROGRESS: "bg-primary text-primary-foreground hover:bg-primary/90", COMPLETED: "bg-green-600 text-white hover:bg-green-700", FAILED: "bg-destructive text-destructive-foreground hover:bg-destructive/90", }, }, }) const getStatusBadge = (status: RepoIndexingJob["status"]) => { const labels = { PENDING: "Pending", IN_PROGRESS: "In Progress", COMPLETED: "Completed", FAILED: "Failed", } return {labels[status]} } const getTypeBadge = (type: RepoIndexingJob["type"]) => { return ( {type} ) } const getDuration = (start: Date, end: Date | null) => { if (!end) return "-" const diff = end.getTime() - start.getTime() const minutes = Math.floor(diff / 60000) const seconds = Math.floor((diff % 60000) / 1000) return `${minutes}m ${seconds}s` } export const columns: ColumnDef[] = [ { accessorKey: "type", header: "Type", cell: ({ row }) => getTypeBadge(row.getValue("type")), filterFn: (row, id, value) => { return value.includes(row.getValue(id)) }, }, { accessorKey: "status", header: "Status", cell: ({ row }) => { const job = row.original return (
{getStatusBadge(row.getValue("status"))} {job.errorMessage && ( {job.errorMessage} )}
) }, filterFn: (row, id, value) => { return value.includes(row.getValue(id)) }, }, { accessorKey: "createdAt", header: ({ column }) => { return ( ) }, cell: ({ row }) => , }, { accessorKey: "completedAt", header: ({ column }) => { return ( ) }, cell: ({ row }) => { const completedAt = row.getValue("completedAt") as Date | null; if (!completedAt) { return "-"; } return }, }, { id: "duration", header: "Duration", cell: ({ row }) => { const job = row.original return getDuration(job.createdAt, job.completedAt) }, }, { accessorKey: "id", header: "Job ID", cell: ({ row }) => { const id = row.getValue("id") as string return (
{id} { navigator.clipboard.writeText(id); return true; }} />
) }, }, ] export const RepoJobsTable = ({ data }: { data: RepoIndexingJob[] }) => { const [sorting, setSorting] = React.useState([{ id: "createdAt", desc: true }]) const [columnFilters, setColumnFilters] = React.useState([]) const [columnVisibility, setColumnVisibility] = React.useState({}) const router = useRouter(); const { toast } = useToast(); const table = useReactTable({ data, columns, onSortingChange: setSorting, onColumnFiltersChange: setColumnFilters, getCoreRowModel: getCoreRowModel(), getPaginationRowModel: getPaginationRowModel(), getSortedRowModel: getSortedRowModel(), getFilteredRowModel: getFilteredRowModel(), onColumnVisibilityChange: setColumnVisibility, state: { sorting, columnFilters, columnVisibility, }, }) const { numCompleted, numInProgress, numPending, numFailed, } = useMemo(() => { return { numCompleted: data.filter((job) => job.status === "COMPLETED").length, numInProgress: data.filter((job) => job.status === "IN_PROGRESS").length, numPending: data.filter((job) => job.status === "PENDING").length, numFailed: data.filter((job) => job.status === "FAILED").length, }; }, [data]); return (
{table.getHeaderGroups().map((headerGroup) => ( {headerGroup.headers.map((header) => { return ( {header.isPlaceholder ? null : flexRender(header.column.columnDef.header, header.getContext())} ) })} ))} {table.getRowModel().rows?.length ? ( table.getRowModel().rows.map((row) => ( {row.getVisibleCells().map((cell) => ( {flexRender(cell.column.columnDef.cell, cell.getContext())} ))} )) ) : ( No indexing jobs found. )}
{table.getFilteredRowModel().rows.length} job(s) total
) }