diff --git a/apps/dokploy/components/dashboard/docker/events/show-docker-events.tsx b/apps/dokploy/components/dashboard/docker/events/show-docker-events.tsx index 7e5ef7dc2..96d0f6556 100644 --- a/apps/dokploy/components/dashboard/docker/events/show-docker-events.tsx +++ b/apps/dokploy/components/dashboard/docker/events/show-docker-events.tsx @@ -1,4 +1,14 @@ -import { Activity, Loader2, RefreshCw } from "lucide-react"; +import { + type ColumnDef, + flexRender, + getCoreRowModel, + getPaginationRowModel, + getSortedRowModel, + type PaginationState, + type SortingState, + useReactTable, +} from "@tanstack/react-table"; +import { Activity, ArrowUpDown, Loader2, RefreshCw } from "lucide-react"; import { useMemo, useState } from "react"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; @@ -32,6 +42,7 @@ interface Props { } type DockerEvent = RouterOutputs["docker"]["getEvents"]["events"][number]; +type EventRow = DockerEvent & { key: string }; const RANGE_OPTIONS = [ { label: "Last 5 minutes", value: 5 }, @@ -73,9 +84,46 @@ const getTypeVariant = (type?: string): BadgeVariant => const getActionVariant = (action?: string): BadgeVariant => (action && ACTION_VARIANTS[action]) || "blank"; +const getResource = (event: DockerEvent) => + event.Actor?.Attributes?.name ?? event.Actor?.ID ?? "-"; + +const getAttributesText = (event: DockerEvent) => { + const attributes = Object.entries(event.Actor?.Attributes ?? {}).filter( + ([key]) => key !== "name", + ); + return attributes.map(([key, value]) => `${key}=${value}`).join(" ") || "-"; +}; + +const SortableHeader = ({ + column, + title, +}: { + column: { + getIsSorted: () => false | "asc" | "desc"; + toggleSorting: (asc: boolean) => void; + }; + title: string; +}) => ( + +); + export const ShowDockerEvents = ({ serverId }: Props) => { const [minutes, setMinutes] = useState(15); const [search, setSearch] = useState(""); + const [sorting, setSorting] = useState([ + { id: "time", desc: true }, + ]); + const [pagination, setPagination] = useState({ + pageIndex: 0, + pageSize: 15, + }); const { data, isLoading, isRefetching, refetch, error } = api.docker.getEvents.useQuery({ @@ -83,7 +131,14 @@ export const ShowDockerEvents = ({ serverId }: Props) => { minutes, }); - const events = data?.events ?? []; + const events = useMemo( + () => + (data?.events ?? []).map((event, index) => ({ + ...event, + key: `${event.time}-${event.Action}-${index}`, + })), + [data], + ); const filteredEvents = useMemo(() => { if (!search.trim()) return events; @@ -98,6 +153,96 @@ export const ShowDockerEvents = ({ serverId }: Props) => { }); }, [events, search]); + const columns = useMemo[]>( + () => [ + { + id: "time", + accessorFn: (event) => event.time ?? 0, + header: ({ column }) => , + cell: ({ row }) => ( + + {row.original.time + ? new Date(row.original.time * 1000).toLocaleTimeString() + : "-"} + + ), + }, + { + id: "type", + accessorFn: (event) => event.Type ?? "", + header: ({ column }) => , + cell: ({ row }) => ( + + {row.original.Type ?? "unknown"} + + ), + }, + { + id: "action", + accessorFn: (event) => event.Action ?? "", + header: ({ column }) => ( + + ), + cell: ({ row }) => ( + + {row.original.Action ?? "-"} + + ), + }, + { + id: "resource", + accessorFn: (event) => getResource(event), + header: ({ column }) => ( + + ), + cell: ({ row }) => { + const resource = getResource(row.original); + return ( +
+ {resource} +
+ ); + }, + }, + { + id: "attributes", + accessorFn: (event) => getAttributesText(event), + header: "Attributes", + enableSorting: false, + cell: ({ row }) => { + const attributesText = getAttributesText(row.original); + return ( +
+ {attributesText} +
+ ); + }, + }, + ], + [], + ); + + const table = useReactTable({ + data: filteredEvents, + columns, + getRowId: (row) => row.key, + state: { + sorting, + pagination, + }, + onSortingChange: setSorting, + onPaginationChange: setPagination, + getCoreRowModel: getCoreRowModel(), + getSortedRowModel: getSortedRowModel(), + getPaginationRowModel: getPaginationRowModel(), + }); + return (
@@ -135,7 +280,10 @@ export const ShowDockerEvents = ({ serverId }: Props) => { setSearch(e.target.value)} + onChange={(e) => { + setSearch(e.target.value); + setPagination((prev) => ({ ...prev, pageIndex: 0 })); + }} className="max-w-xs" />