Files
xingrin/frontend/components/websites/websites-data-table.tsx
yyhuni bbef6af000 fix(frontend): update filter examples to use correct wildcard syntax
- Replace wildcard patterns with asterisks (*) with trailing slash notation
- Update directories filter example from "/api/*" to "/api/"
- Update endpoints filter example from "/api/*" to "/api/"
- Update IP addresses filter example from "192.168.1.*" to "192.168.1."
- Update subdomains filter example from "*.test.com" to ".test.com"
- Update vulnerabilities filter example from "/api/*" to "/api/"
- Update websites filter example from "/api/*" to "/api/"
- Standardize filter syntax across all data table components for consistency
2026-01-18 21:41:30 +08:00

126 lines
3.7 KiB
TypeScript

"use client"
import * as React from "react"
import type { ColumnDef } from "@tanstack/react-table"
import { useTranslations } from "next-intl"
import { UnifiedDataTable } from "@/components/ui/data-table"
import type { FilterField } from "@/components/common/smart-filter-input"
import type { WebSite } from "@/types/website.types"
import type { PaginationInfo } from "@/types/common.types"
import type { DownloadOption } from "@/types/data-table.types"
// Website page filter field configuration
const WEBSITE_FILTER_FIELDS: FilterField[] = [
{ key: "url", label: "URL", description: "Full URL" },
{ key: "host", label: "Host", description: "Hostname" },
{ key: "title", label: "Title", description: "Page title" },
{ key: "status", label: "Status", description: "HTTP status code" },
{ key: "tech", label: "Tech", description: "Technologies" },
{ key: "responseHeaders", label: "Headers", description: "Response headers" },
]
// Website page filter examples
const WEBSITE_FILTER_EXAMPLES = [
'host="api.example.com" && status="200"',
'title="Login" || title="Admin"',
'url="/api/" && status!="404"',
'tech="nginx" || tech="apache"',
]
interface WebSitesDataTableProps {
data: WebSite[]
columns: ColumnDef<WebSite>[]
// Smart filter
filterValue?: string
onFilterChange?: (value: string) => void
isSearching?: boolean
pagination?: { pageIndex: number; pageSize: number }
setPagination?: React.Dispatch<React.SetStateAction<{ pageIndex: number; pageSize: number }>>
paginationInfo?: PaginationInfo
onPaginationChange?: (pagination: { pageIndex: number; pageSize: number }) => void
onBulkDelete?: () => void
onSelectionChange?: (selectedRows: WebSite[]) => void
onDownloadAll?: () => void
onDownloadSelected?: () => void
onBulkAdd?: () => void
}
export function WebSitesDataTable({
data = [],
columns,
filterValue,
onFilterChange,
isSearching = false,
pagination,
setPagination,
paginationInfo,
onPaginationChange,
onBulkDelete,
onSelectionChange,
onDownloadAll,
onDownloadSelected,
onBulkAdd,
}: WebSitesDataTableProps) {
const t = useTranslations("common.status")
const tActions = useTranslations("common.actions")
const tDownload = useTranslations("common.download")
// Handle smart filter search
const handleSmartSearch = (rawQuery: string) => {
if (onFilterChange) {
onFilterChange(rawQuery)
}
}
// Download options
const downloadOptions: DownloadOption[] = []
if (onDownloadAll) {
downloadOptions.push({
key: "all",
label: tDownload("all"),
onClick: onDownloadAll,
})
}
if (onDownloadSelected) {
downloadOptions.push({
key: "selected",
label: tDownload("selected"),
onClick: onDownloadSelected,
disabled: (count) => count === 0,
})
}
return (
<UnifiedDataTable
data={data}
columns={columns}
getRowId={(row) => String(row.id)}
// Pagination
pagination={pagination}
setPagination={setPagination}
paginationInfo={paginationInfo}
onPaginationChange={onPaginationChange}
// Smart filter
searchMode="smart"
searchValue={filterValue}
onSearch={handleSmartSearch}
isSearching={isSearching}
filterFields={WEBSITE_FILTER_FIELDS}
filterExamples={WEBSITE_FILTER_EXAMPLES}
// Selection
onSelectionChange={onSelectionChange}
// Bulk operations
onBulkDelete={onBulkDelete}
bulkDeleteLabel={tActions("delete")}
showAddButton={false}
// Bulk add button
onBulkAdd={onBulkAdd}
bulkAddLabel={tActions("add")}
// Download
downloadOptions={downloadOptions.length > 0 ? downloadOptions : undefined}
// Empty state
emptyMessage={t("noData")}
/>
)
}