|
1 | 1 | import { observer } from "mobx-react"; |
| 2 | +import { useState, useEffect } from "react"; |
2 | 3 | import { state } from "../App"; |
3 | 4 | import { EvaluationRow } from "./EvaluationRow"; |
| 5 | +import Button from "./Button"; |
4 | 6 |
|
5 | | -const TableBody = observer(() => { |
6 | | - return ( |
7 | | - <tbody className="divide-y divide-gray-200"> |
8 | | - {state.sortedDataset.map((row, index) => ( |
9 | | - <EvaluationRow key={row.rollout_id} row={row} index={index} /> |
10 | | - ))} |
11 | | - </tbody> |
12 | | - ); |
13 | | -}); |
| 7 | +const TableBody = observer( |
| 8 | + ({ currentPage, pageSize }: { currentPage: number; pageSize: number }) => { |
| 9 | + const startIndex = (currentPage - 1) * pageSize; |
| 10 | + const endIndex = startIndex + pageSize; |
| 11 | + const paginatedData = state.sortedDataset.slice(startIndex, endIndex); |
| 12 | + |
| 13 | + return ( |
| 14 | + <tbody className="divide-y divide-gray-200"> |
| 15 | + {paginatedData.map((row, index) => ( |
| 16 | + <EvaluationRow |
| 17 | + key={row.rollout_id} |
| 18 | + row={row} |
| 19 | + index={startIndex + index} |
| 20 | + /> |
| 21 | + ))} |
| 22 | + </tbody> |
| 23 | + ); |
| 24 | + } |
| 25 | +); |
14 | 26 |
|
15 | 27 | // Dedicated component for rendering the list - following MobX best practices |
16 | 28 | export const EvaluationTable = observer(() => { |
| 29 | + const [currentPage, setCurrentPage] = useState(1); |
| 30 | + const [pageSize, setPageSize] = useState(50); |
| 31 | + |
| 32 | + const totalRows = state.sortedDataset.length; |
| 33 | + const totalPages = Math.ceil(totalRows / pageSize); |
| 34 | + const startRow = (currentPage - 1) * pageSize + 1; |
| 35 | + const endRow = Math.min(currentPage * pageSize, totalRows); |
| 36 | + |
| 37 | + const handlePageChange = (page: number) => { |
| 38 | + setCurrentPage(Math.max(1, Math.min(page, totalPages))); |
| 39 | + }; |
| 40 | + |
| 41 | + const handlePageSizeChange = (newPageSize: number) => { |
| 42 | + setPageSize(newPageSize); |
| 43 | + setCurrentPage(1); // Reset to first page when changing page size |
| 44 | + }; |
| 45 | + |
| 46 | + // Reset to first page when dataset changes |
| 47 | + useEffect(() => { |
| 48 | + setCurrentPage(1); |
| 49 | + }, [totalRows]); |
| 50 | + |
17 | 51 | return ( |
18 | 52 | <div className="bg-white border border-gray-200 overflow-x-auto"> |
| 53 | + {/* Pagination Controls */} |
| 54 | + <div className="px-3 py-2 border-b border-gray-200 bg-gray-50 flex items-center justify-between"> |
| 55 | + <div className="flex items-center gap-4"> |
| 56 | + <div className="text-xs text-gray-600"> |
| 57 | + Showing {startRow}-{endRow} of {totalRows} rows |
| 58 | + </div> |
| 59 | + <div className="flex items-center gap-2"> |
| 60 | + <label className="text-xs text-gray-600">Page size:</label> |
| 61 | + <select |
| 62 | + value={pageSize} |
| 63 | + onChange={(e) => handlePageSizeChange(Number(e.target.value))} |
| 64 | + className="text-xs border border-gray-300 rounded px-2 py-1 bg-white" |
| 65 | + > |
| 66 | + <option value={25}>25</option> |
| 67 | + <option value={50}>50</option> |
| 68 | + <option value={100}>100</option> |
| 69 | + <option value={200}>200</option> |
| 70 | + </select> |
| 71 | + </div> |
| 72 | + </div> |
| 73 | + <div className="flex items-center gap-2"> |
| 74 | + <Button |
| 75 | + onClick={() => handlePageChange(1)} |
| 76 | + disabled={currentPage === 1} |
| 77 | + size="sm" |
| 78 | + variant="secondary" |
| 79 | + > |
| 80 | + First |
| 81 | + </Button> |
| 82 | + <Button |
| 83 | + onClick={() => handlePageChange(currentPage - 1)} |
| 84 | + disabled={currentPage === 1} |
| 85 | + size="sm" |
| 86 | + variant="secondary" |
| 87 | + > |
| 88 | + Previous |
| 89 | + </Button> |
| 90 | + <span className="text-xs text-gray-600 px-2"> |
| 91 | + Page {currentPage} of {totalPages} |
| 92 | + </span> |
| 93 | + <Button |
| 94 | + onClick={() => handlePageChange(currentPage + 1)} |
| 95 | + disabled={currentPage === totalPages} |
| 96 | + size="sm" |
| 97 | + variant="secondary" |
| 98 | + > |
| 99 | + Next |
| 100 | + </Button> |
| 101 | + <Button |
| 102 | + onClick={() => handlePageChange(totalPages)} |
| 103 | + disabled={currentPage === totalPages} |
| 104 | + size="sm" |
| 105 | + variant="secondary" |
| 106 | + > |
| 107 | + Last |
| 108 | + </Button> |
| 109 | + </div> |
| 110 | + </div> |
| 111 | + |
19 | 112 | <table className="w-full min-w-max"> |
20 | 113 | {/* Table Header */} |
21 | 114 | <thead className="bg-gray-50 border-b border-gray-200"> |
@@ -45,7 +138,7 @@ export const EvaluationTable = observer(() => { |
45 | 138 | </thead> |
46 | 139 |
|
47 | 140 | {/* Table Body */} |
48 | | - <TableBody /> |
| 141 | + <TableBody currentPage={currentPage} pageSize={pageSize} /> |
49 | 142 | </table> |
50 | 143 | </div> |
51 | 144 | ); |
|
0 commit comments