TerraTechImportsTable.tsx 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  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 { BarChart3, 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 TerraTechImportsTableProps {
  35. data: Import[];
  36. onViewSummary: (importRecord: Import) => void;
  37. }
  38. export function TerraTechImportsTable({ data, onViewSummary }: TerraTechImportsTableProps) {
  39. const [sorting, setSorting] = useState<SortingState>([
  40. { id: "importDate", desc: true },
  41. ]);
  42. const columns: ColumnDef<Import>[] = [
  43. {
  44. accessorKey: "name",
  45. header: "Name",
  46. cell: ({ row }) => (
  47. <div className="font-medium">{row.getValue("name")}</div>
  48. ),
  49. },
  50. {
  51. accessorKey: "layout.name",
  52. header: "Layout",
  53. cell: ({ row }) => (
  54. <Badge variant="outline" className="dark:border-gray-600 dark:text-gray-300">
  55. {row.original.layout.name}
  56. </Badge>
  57. ),
  58. },
  59. {
  60. accessorKey: "importDate",
  61. header: "Import Date",
  62. cell: ({ row }) => {
  63. const date = new Date(row.getValue("importDate"));
  64. return (
  65. <div className="flex items-center text-sm text-muted-foreground">
  66. <Calendar className="mr-2 h-3 w-3" />
  67. {format(date, "MMM d, yyyy HH:mm")}
  68. </div>
  69. );
  70. },
  71. },
  72. {
  73. id: "actions",
  74. header: "Actions",
  75. cell: ({ row }) => (
  76. <div className="flex gap-2">
  77. <Button
  78. variant="default"
  79. size="sm"
  80. onClick={() => onViewSummary(row.original)}
  81. title="View Summary"
  82. className="bg-amber-500 hover:bg-amber-600 text-white"
  83. >
  84. <BarChart3 className="h-4 w-4 mr-1" />
  85. View Summary
  86. </Button>
  87. </div>
  88. ),
  89. enableSorting: false,
  90. },
  91. ];
  92. const table = useReactTable({
  93. data,
  94. columns,
  95. state: {
  96. sorting,
  97. },
  98. onSortingChange: setSorting,
  99. getCoreRowModel: getCoreRowModel(),
  100. getPaginationRowModel: getPaginationRowModel(),
  101. getSortedRowModel: getSortedRowModel(),
  102. initialState: {
  103. pagination: {
  104. pageSize: 10,
  105. },
  106. },
  107. });
  108. return (
  109. <div className="space-y-4">
  110. <div className="rounded-md border bg-white dark:bg-gray-800 dark:border-gray-700">
  111. <Table>
  112. <TableHeader>
  113. {table.getHeaderGroups().map((headerGroup) => (
  114. <TableRow key={headerGroup.id} className="dark:border-gray-700">
  115. {headerGroup.headers.map((header) => (
  116. <TableHead
  117. key={header.id}
  118. onClick={header.column.getToggleSortingHandler()}
  119. className={header.column.getCanSort() ? "cursor-pointer select-none" : ""}
  120. >
  121. <div className="flex items-center gap-1">
  122. {flexRender(
  123. header.column.columnDef.header,
  124. header.getContext()
  125. )}
  126. {header.column.getIsSorted() && (
  127. <span>
  128. {header.column.getIsSorted() === "asc" ? "↑" : "↓"}
  129. </span>
  130. )}
  131. </div>
  132. </TableHead>
  133. ))}
  134. </TableRow>
  135. ))}
  136. </TableHeader>
  137. <TableBody>
  138. {table.getRowModel().rows?.length ? (
  139. table.getRowModel().rows.map((row) => (
  140. <TableRow
  141. key={row.id}
  142. data-state={row.getIsSelected() && "selected"}
  143. className="dark:border-gray-700"
  144. >
  145. {row.getVisibleCells().map((cell) => (
  146. <TableCell key={cell.id}>
  147. {flexRender(cell.column.columnDef.cell, cell.getContext())}
  148. </TableCell>
  149. ))}
  150. </TableRow>
  151. ))
  152. ) : (
  153. <TableRow>
  154. <TableCell colSpan={columns.length} className="h-24 text-center">
  155. No results.
  156. </TableCell>
  157. </TableRow>
  158. )}
  159. </TableBody>
  160. </Table>
  161. </div>
  162. <div className="flex items-center justify-between">
  163. <div className="text-sm text-muted-foreground">
  164. Showing {table.getRowModel().rows.length} of {data.length} imports
  165. </div>
  166. <div className="flex items-center space-x-2">
  167. <Button
  168. variant="outline"
  169. size="sm"
  170. onClick={() => table.previousPage()}
  171. disabled={!table.getCanPreviousPage()}
  172. >
  173. <ChevronLeft className="h-4 w-4 mr-1" />
  174. Previous
  175. </Button>
  176. <div className="text-sm font-medium">
  177. Page {table.getState().pagination.pageIndex + 1} of{" "}
  178. {table.getPageCount()}
  179. </div>
  180. <Button
  181. variant="outline"
  182. size="sm"
  183. onClick={() => table.nextPage()}
  184. disabled={!table.getCanNextPage()}
  185. >
  186. Next
  187. <ChevronRight className="h-4 w-4 ml-1" />
  188. </Button>
  189. </div>
  190. </div>
  191. </div>
  192. );
  193. }