|
|
@@ -0,0 +1,218 @@
|
|
|
+"use client";
|
|
|
+
|
|
|
+import React, { useState } from "react";
|
|
|
+import {
|
|
|
+ ColumnDef,
|
|
|
+ flexRender,
|
|
|
+ getCoreRowModel,
|
|
|
+ getPaginationRowModel,
|
|
|
+ getSortedRowModel,
|
|
|
+ useReactTable,
|
|
|
+ SortingState,
|
|
|
+} from "@tanstack/react-table";
|
|
|
+import { Button } from "@/components/ui/button";
|
|
|
+import {
|
|
|
+ Table,
|
|
|
+ TableBody,
|
|
|
+ TableCell,
|
|
|
+ TableHead,
|
|
|
+ TableHeader,
|
|
|
+ TableRow,
|
|
|
+} from "@/components/ui/table";
|
|
|
+import { Badge } from "@/components/ui/badge";
|
|
|
+import { format } from "date-fns";
|
|
|
+import { FileText, Edit, Trash2, Calendar, ChevronLeft, ChevronRight } from "lucide-react";
|
|
|
+
|
|
|
+interface Import {
|
|
|
+ id: number;
|
|
|
+ name: string;
|
|
|
+ importDate: string;
|
|
|
+ layoutId: number;
|
|
|
+ layout: {
|
|
|
+ id: number;
|
|
|
+ name: string;
|
|
|
+ };
|
|
|
+}
|
|
|
+
|
|
|
+interface ImportsTableProps {
|
|
|
+ data: Import[];
|
|
|
+ onView: (importRecord: Import) => void;
|
|
|
+ onEdit: (importRecord: Import) => void;
|
|
|
+ onDelete: (id: number) => void;
|
|
|
+}
|
|
|
+
|
|
|
+export function ImportsTable({ data, onView, onEdit, onDelete }: ImportsTableProps) {
|
|
|
+ const [sorting, setSorting] = useState<SortingState>([
|
|
|
+ { id: "importDate", desc: true },
|
|
|
+ ]);
|
|
|
+
|
|
|
+ const columns: ColumnDef<Import>[] = [
|
|
|
+ {
|
|
|
+ accessorKey: "name",
|
|
|
+ header: "Name",
|
|
|
+ cell: ({ row }) => (
|
|
|
+ <div className="font-medium">{row.getValue("name")}</div>
|
|
|
+ ),
|
|
|
+ },
|
|
|
+ {
|
|
|
+ accessorKey: "layout.name",
|
|
|
+ header: "Layout",
|
|
|
+ cell: ({ row }) => (
|
|
|
+ <Badge variant="outline" className="dark:border-gray-600 dark:text-gray-300">
|
|
|
+ {row.original.layout.name}
|
|
|
+ </Badge>
|
|
|
+ ),
|
|
|
+ },
|
|
|
+ {
|
|
|
+ accessorKey: "importDate",
|
|
|
+ header: "Import Date",
|
|
|
+ cell: ({ row }) => {
|
|
|
+ const date = new Date(row.getValue("importDate"));
|
|
|
+ return (
|
|
|
+ <div className="flex items-center text-sm text-muted-foreground">
|
|
|
+ <Calendar className="mr-2 h-3 w-3" />
|
|
|
+ {format(date, "MMM d, yyyy HH:mm")}
|
|
|
+ </div>
|
|
|
+ );
|
|
|
+ },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ id: "actions",
|
|
|
+ header: "Actions",
|
|
|
+ cell: ({ row }) => (
|
|
|
+ <div className="flex gap-2">
|
|
|
+ <Button
|
|
|
+ variant="ghost"
|
|
|
+ size="sm"
|
|
|
+ onClick={() => onView(row.original)}
|
|
|
+ title="View details"
|
|
|
+ >
|
|
|
+ <FileText className="h-4 w-4" />
|
|
|
+ </Button>
|
|
|
+ <Button
|
|
|
+ variant="ghost"
|
|
|
+ size="sm"
|
|
|
+ onClick={() => onEdit(row.original)}
|
|
|
+ title="Edit import"
|
|
|
+ >
|
|
|
+ <Edit className="h-4 w-4" />
|
|
|
+ </Button>
|
|
|
+ <Button
|
|
|
+ variant="ghost"
|
|
|
+ size="sm"
|
|
|
+ onClick={() => onDelete(row.original.id)}
|
|
|
+ title="Delete import"
|
|
|
+ className="text-destructive hover:text-destructive"
|
|
|
+ >
|
|
|
+ <Trash2 className="h-4 w-4" />
|
|
|
+ </Button>
|
|
|
+ </div>
|
|
|
+ ),
|
|
|
+ enableSorting: false,
|
|
|
+ },
|
|
|
+ ];
|
|
|
+
|
|
|
+ const table = useReactTable({
|
|
|
+ data,
|
|
|
+ columns,
|
|
|
+ state: {
|
|
|
+ sorting,
|
|
|
+ },
|
|
|
+ onSortingChange: setSorting,
|
|
|
+ getCoreRowModel: getCoreRowModel(),
|
|
|
+ getPaginationRowModel: getPaginationRowModel(),
|
|
|
+ getSortedRowModel: getSortedRowModel(),
|
|
|
+ initialState: {
|
|
|
+ pagination: {
|
|
|
+ pageSize: 10,
|
|
|
+ },
|
|
|
+ },
|
|
|
+ });
|
|
|
+
|
|
|
+ return (
|
|
|
+ <div className="space-y-4">
|
|
|
+ <div className="rounded-md border bg-white dark:bg-gray-800 dark:border-gray-700">
|
|
|
+ <Table>
|
|
|
+ <TableHeader>
|
|
|
+ {table.getHeaderGroups().map((headerGroup) => (
|
|
|
+ <TableRow key={headerGroup.id} className="dark:border-gray-700">
|
|
|
+ {headerGroup.headers.map((header) => (
|
|
|
+ <TableHead
|
|
|
+ key={header.id}
|
|
|
+ onClick={header.column.getToggleSortingHandler()}
|
|
|
+ className={header.column.getCanSort() ? "cursor-pointer select-none" : ""}
|
|
|
+ >
|
|
|
+ <div className="flex items-center gap-1">
|
|
|
+ {flexRender(
|
|
|
+ header.column.columnDef.header,
|
|
|
+ header.getContext()
|
|
|
+ )}
|
|
|
+ {header.column.getIsSorted() && (
|
|
|
+ <span>
|
|
|
+ {header.column.getIsSorted() === "asc" ? "↑" : "↓"}
|
|
|
+ </span>
|
|
|
+ )}
|
|
|
+ </div>
|
|
|
+ </TableHead>
|
|
|
+ ))}
|
|
|
+ </TableRow>
|
|
|
+ ))}
|
|
|
+ </TableHeader>
|
|
|
+ <TableBody>
|
|
|
+ {table.getRowModel().rows?.length ? (
|
|
|
+ table.getRowModel().rows.map((row) => (
|
|
|
+ <TableRow
|
|
|
+ key={row.id}
|
|
|
+ data-state={row.getIsSelected() && "selected"}
|
|
|
+ className="dark:border-gray-700"
|
|
|
+ >
|
|
|
+ {row.getVisibleCells().map((cell) => (
|
|
|
+ <TableCell key={cell.id}>
|
|
|
+ {flexRender(cell.column.columnDef.cell, cell.getContext())}
|
|
|
+ </TableCell>
|
|
|
+ ))}
|
|
|
+ </TableRow>
|
|
|
+ ))
|
|
|
+ ) : (
|
|
|
+ <TableRow>
|
|
|
+ <TableCell colSpan={columns.length} className="h-24 text-center">
|
|
|
+ No results.
|
|
|
+ </TableCell>
|
|
|
+ </TableRow>
|
|
|
+ )}
|
|
|
+ </TableBody>
|
|
|
+ </Table>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div className="flex items-center justify-between">
|
|
|
+ <div className="text-sm text-muted-foreground">
|
|
|
+ Showing {table.getRowModel().rows.length} of {data.length} imports
|
|
|
+ </div>
|
|
|
+ <div className="flex items-center space-x-2">
|
|
|
+ <Button
|
|
|
+ variant="outline"
|
|
|
+ size="sm"
|
|
|
+ onClick={() => table.previousPage()}
|
|
|
+ disabled={!table.getCanPreviousPage()}
|
|
|
+ >
|
|
|
+ <ChevronLeft className="h-4 w-4 mr-1" />
|
|
|
+ Previous
|
|
|
+ </Button>
|
|
|
+ <div className="text-sm font-medium">
|
|
|
+ Page {table.getState().pagination.pageIndex + 1} of{" "}
|
|
|
+ {table.getPageCount()}
|
|
|
+ </div>
|
|
|
+ <Button
|
|
|
+ variant="outline"
|
|
|
+ size="sm"
|
|
|
+ onClick={() => table.nextPage()}
|
|
|
+ disabled={!table.getCanNextPage()}
|
|
|
+ >
|
|
|
+ Next
|
|
|
+ <ChevronRight className="h-4 w-4 ml-1" />
|
|
|
+ </Button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ );
|
|
|
+}
|