ImportsTable.tsx 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. "use client";
  2. import React, { useState } from "react";
  3. import {
  4. ColumnDef,
  5. flexRender,
  6. getCoreRowModel,
  7. getPaginationRowModel,
  8. getSortedRowModel,
  9. useReactTable,
  10. SortingState,
  11. } from "@tanstack/react-table";
  12. import { Button } from "@/components/ui/button";
  13. import {
  14. Table,
  15. TableBody,
  16. TableCell,
  17. TableHead,
  18. TableHeader,
  19. TableRow,
  20. } from "@/components/ui/table";
  21. import { Badge } from "@/components/ui/badge";
  22. import { format } from "date-fns";
  23. import { FileText, Edit, Trash2, Calendar, ChevronLeft, ChevronRight } from "lucide-react";
  24. interface Import {
  25. id: number;
  26. name: string;
  27. importDate: string;
  28. layoutId: number;
  29. layout: {
  30. id: number;
  31. name: string;
  32. };
  33. }
  34. interface ImportsTableProps {
  35. data: Import[];
  36. onView: (importRecord: Import) => void;
  37. onEdit: (importRecord: Import) => void;
  38. onDelete: (id: number) => void;
  39. }
  40. export function ImportsTable({ data, onView, onEdit, onDelete }: ImportsTableProps) {
  41. const [sorting, setSorting] = useState<SortingState>([
  42. { id: "importDate", desc: true },
  43. ]);
  44. const columns: ColumnDef<Import>[] = [
  45. {
  46. accessorKey: "name",
  47. header: "Name",
  48. cell: ({ row }) => (
  49. <div className="font-medium">{row.getValue("name")}</div>
  50. ),
  51. },
  52. {
  53. accessorKey: "layout.name",
  54. header: "Layout",
  55. cell: ({ row }) => (
  56. <Badge variant="outline" className="dark:border-gray-600 dark:text-gray-300">
  57. {row.original.layout.name}
  58. </Badge>
  59. ),
  60. },
  61. {
  62. accessorKey: "importDate",
  63. header: "Import Date",
  64. cell: ({ row }) => {
  65. const date = new Date(row.getValue("importDate"));
  66. return (
  67. <div className="flex items-center text-sm text-muted-foreground">
  68. <Calendar className="mr-2 h-3 w-3" />
  69. {format(date, "MMM d, yyyy HH:mm")}
  70. </div>
  71. );
  72. },
  73. },
  74. {
  75. id: "actions",
  76. header: "Actions",
  77. cell: ({ row }) => (
  78. <div className="flex gap-2">
  79. <Button
  80. variant="ghost"
  81. size="sm"
  82. onClick={() => onView(row.original)}
  83. title="View details"
  84. >
  85. <FileText className="h-4 w-4" />
  86. </Button>
  87. <Button
  88. variant="ghost"
  89. size="sm"
  90. onClick={() => onEdit(row.original)}
  91. title="Edit import"
  92. >
  93. <Edit className="h-4 w-4" />
  94. </Button>
  95. <Button
  96. variant="ghost"
  97. size="sm"
  98. onClick={() => onDelete(row.original.id)}
  99. title="Delete import"
  100. className="text-destructive hover:text-destructive"
  101. >
  102. <Trash2 className="h-4 w-4" />
  103. </Button>
  104. </div>
  105. ),
  106. enableSorting: false,
  107. },
  108. ];
  109. const table = useReactTable({
  110. data,
  111. columns,
  112. state: {
  113. sorting,
  114. },
  115. onSortingChange: setSorting,
  116. getCoreRowModel: getCoreRowModel(),
  117. getPaginationRowModel: getPaginationRowModel(),
  118. getSortedRowModel: getSortedRowModel(),
  119. initialState: {
  120. pagination: {
  121. pageSize: 10,
  122. },
  123. },
  124. });
  125. return (
  126. <div className="space-y-4">
  127. <div className="rounded-md border bg-white dark:bg-gray-800 dark:border-gray-700">
  128. <Table>
  129. <TableHeader>
  130. {table.getHeaderGroups().map((headerGroup) => (
  131. <TableRow key={headerGroup.id} className="dark:border-gray-700">
  132. {headerGroup.headers.map((header) => (
  133. <TableHead
  134. key={header.id}
  135. onClick={header.column.getToggleSortingHandler()}
  136. className={header.column.getCanSort() ? "cursor-pointer select-none" : ""}
  137. >
  138. <div className="flex items-center gap-1">
  139. {flexRender(
  140. header.column.columnDef.header,
  141. header.getContext()
  142. )}
  143. {header.column.getIsSorted() && (
  144. <span>
  145. {header.column.getIsSorted() === "asc" ? "↑" : "↓"}
  146. </span>
  147. )}
  148. </div>
  149. </TableHead>
  150. ))}
  151. </TableRow>
  152. ))}
  153. </TableHeader>
  154. <TableBody>
  155. {table.getRowModel().rows?.length ? (
  156. table.getRowModel().rows.map((row) => (
  157. <TableRow
  158. key={row.id}
  159. data-state={row.getIsSelected() && "selected"}
  160. className="dark:border-gray-700"
  161. >
  162. {row.getVisibleCells().map((cell) => (
  163. <TableCell key={cell.id}>
  164. {flexRender(cell.column.columnDef.cell, cell.getContext())}
  165. </TableCell>
  166. ))}
  167. </TableRow>
  168. ))
  169. ) : (
  170. <TableRow>
  171. <TableCell colSpan={columns.length} className="h-24 text-center">
  172. No results.
  173. </TableCell>
  174. </TableRow>
  175. )}
  176. </TableBody>
  177. </Table>
  178. </div>
  179. <div className="flex items-center justify-between">
  180. <div className="text-sm text-muted-foreground">
  181. Showing {table.getRowModel().rows.length} of {data.length} imports
  182. </div>
  183. <div className="flex items-center space-x-2">
  184. <Button
  185. variant="outline"
  186. size="sm"
  187. onClick={() => table.previousPage()}
  188. disabled={!table.getCanPreviousPage()}
  189. >
  190. <ChevronLeft className="h-4 w-4 mr-1" />
  191. Previous
  192. </Button>
  193. <div className="text-sm font-medium">
  194. Page {table.getState().pagination.pageIndex + 1} of{" "}
  195. {table.getPageCount()}
  196. </div>
  197. <Button
  198. variant="outline"
  199. size="sm"
  200. onClick={() => table.nextPage()}
  201. disabled={!table.getCanNextPage()}
  202. >
  203. Next
  204. <ChevronRight className="h-4 w-4 ml-1" />
  205. </Button>
  206. </div>
  207. </div>
  208. </div>
  209. );
  210. }