"use client" import React from "react" import { ColumnDef } from "@tanstack/react-table" import { Button } from "@/components/ui/button" import { Badge } from "@/components/ui/badge" import { Checkbox } from "@/components/ui/checkbox" import { ChevronsUpDown, ChevronUp, ChevronDown } from "lucide-react" import type { Endpoint } from "@/types/endpoint.types" import { TruncatedCell, TruncatedUrlCell } from "@/components/ui/truncated-cell" interface CreateColumnsProps { formatDate: (dateString: string) => string } function DataTableColumnHeader({ column, title, }: { column: { getCanSort: () => boolean; getIsSorted: () => false | "asc" | "desc"; toggleSorting: (desc?: boolean) => void } title: string }) { if (!column.getCanSort()) { return
{title}
} const isSorted = column.getIsSorted() return ( ) } function HttpStatusBadge({ statusCode }: { statusCode: number | null | undefined }) { if (statusCode === null || statusCode === undefined) { return ( - ) } const getStatusVariant = (code: number): "default" | "secondary" | "destructive" | "outline" => { if (code >= 200 && code < 300) { return "outline" } else if (code >= 300 && code < 400) { return "secondary" } else if (code >= 400 && code < 500) { return "default" } else if (code >= 500) { return "destructive" } else { return "secondary" } } const variant = getStatusVariant(statusCode) return ( {statusCode} ) } /** * Body Preview 单元格组件 - 最多显示3行,超出折叠,点击展开查看完整内容 */ function BodyPreviewCell({ value }: { value: string | null | undefined }) { const [expanded, setExpanded] = React.useState(false) if (!value) { return - } return (
setExpanded(!expanded)} title={expanded ? "点击收起" : "点击展开"} > {value}
{value.length > 100 && ( )}
) } export function createEndpointColumns({ formatDate, }: CreateColumnsProps): ColumnDef[] { return [ { id: "select", size: 40, minSize: 40, maxSize: 40, enableResizing: false, header: ({ table }) => ( table.toggleAllPageRowsSelected(!!value)} aria-label="Select all" /> ), cell: ({ row }) => ( row.toggleSelected(!!value)} aria-label="Select row" /> ), enableSorting: false, enableHiding: false, }, { accessorKey: "url", header: ({ column }) => ( ), size: 400, minSize: 200, maxSize: 700, cell: ({ row }) => { const url = row.getValue("url") as string return }, }, { accessorKey: "title", header: ({ column }) => ( ), size: 150, minSize: 100, maxSize: 300, cell: ({ row }) => ( ), }, { accessorKey: "statusCode", header: ({ column }) => ( ), size: 80, minSize: 60, maxSize: 100, cell: ({ row }) => { const status = row.getValue("statusCode") as number | null | undefined return }, }, { accessorKey: "contentLength", header: ({ column }) => ( ), size: 100, minSize: 80, maxSize: 150, cell: ({ row }) => { const len = row.getValue("contentLength") as number | null | undefined if (len === null || len === undefined) { return - } return {new Intl.NumberFormat().format(len)} }, }, { accessorKey: "location", header: ({ column }) => ( ), size: 150, minSize: 100, maxSize: 300, cell: ({ row }) => ( ), }, { accessorKey: "webserver", header: ({ column }) => ( ), size: 120, minSize: 80, maxSize: 200, cell: ({ row }) => ( ), }, { accessorKey: "contentType", header: ({ column }) => ( ), size: 120, minSize: 80, maxSize: 200, cell: ({ row }) => ( ), }, { accessorKey: "tech", header: ({ column }) => ( ), size: 200, minSize: 150, cell: ({ row }) => { const tech = (row.getValue("tech") as string[] | null | undefined) || [] if (!tech.length) return - return (
{tech.map((t, index) => ( {t} ))}
) }, }, { accessorKey: "bodyPreview", header: ({ column }) => ( ), size: 350, minSize: 250, cell: ({ row }) => ( ), }, { accessorKey: "vhost", header: ({ column }) => ( ), size: 80, minSize: 60, maxSize: 100, cell: ({ row }) => { const vhost = row.getValue("vhost") as boolean | null | undefined if (vhost === null || vhost === undefined) return - return {vhost ? "true" : "false"} }, }, { accessorKey: "tags", header: ({ column }) => ( ), size: 150, minSize: 100, maxSize: 250, cell: ({ row }) => { const tags = (row.getValue("tags") as string[] | null | undefined) || [] if (!tags.length) { return - } return (
{tags.map((tag, idx) => ( {tag} ))}
) }, enableSorting: false, }, { accessorKey: "responseTime", header: ({ column }) => ( ), size: 100, minSize: 80, maxSize: 150, cell: ({ row }) => { const rt = row.getValue("responseTime") as number | null | undefined if (rt === null || rt === undefined) { return - } const formatted = `${rt.toFixed(4)}s` return {formatted} }, }, { accessorKey: "discoveredAt", header: ({ column }) => ( ), size: 150, minSize: 120, maxSize: 200, cell: ({ row }) => { const discoveredAt = row.getValue("discoveredAt") as string | undefined return
{discoveredAt ? formatDate(discoveredAt) : "-"}
}, }, ] }