"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) : "-"}
},
},
]
}