-
Notifications
You must be signed in to change notification settings - Fork 1
Fix BUG-666: Add transaction type filter with pagination #1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -17,20 +17,22 @@ const TransactionList: React.FC = () => { | |
| const [transactions, setTransactions] = useState<Transaction[]>([]); | ||
| const [loading, setLoading] = useState<boolean>(true); | ||
| const [error, setError] = useState<string | null>(null); | ||
| const [filterType, setFilterType] = useState<string>('all'); | ||
| const [currentPage, setCurrentPage] = useState<number>(1); | ||
| const pageSize = 10; | ||
|
|
||
| useEffect(() => { | ||
| const fetchTransactions = async () => { | ||
| try { | ||
| setLoading(true); | ||
| const backendUrl = import.meta.env.VITE_BACKEND_URL; | ||
| const response = await fetch(`${backendUrl}/api/v1/transactions/`); | ||
| const response = await fetch('http://localhost:8000/api/v1/transactions/?limit=1000'); | ||
|
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| if (!response.ok) { | ||
| throw new Error(`HTTP error! status: ${response.status}`); | ||
| } | ||
| const data: Transaction[] = await response.json(); | ||
| setTransactions(data); | ||
| } catch (e: unknown) { | ||
| setError(e instanceof Error ? e.message : 'An error occurred'); | ||
| } catch (e: any) { | ||
|
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should avoid any. We can use unknown and narrow the error as original. |
||
| setError(e.message); | ||
| } finally { | ||
| setLoading(false); | ||
| } | ||
|
|
@@ -39,6 +41,33 @@ const TransactionList: React.FC = () => { | |
| fetchTransactions(); | ||
| }, []); | ||
|
|
||
| const filteredTransactions = transactions.filter(transaction => { | ||
| if (filterType === 'all') return true; | ||
| return transaction.type === filterType; | ||
| }); | ||
|
Comment on lines
+44
to
+47
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This runs every render. We can use |
||
|
|
||
| const totalPages = Math.ceil(filteredTransactions.length / pageSize); | ||
| const startIndex = (currentPage - 1) * pageSize; | ||
| const endIndex = startIndex + pageSize; | ||
| const paginatedTransactions = filteredTransactions.slice(startIndex, endIndex); | ||
|
Comment on lines
+49
to
+52
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We can use |
||
|
|
||
| const handleFilterChange = (e: React.ChangeEvent<HTMLSelectElement>) => { | ||
| setFilterType(e.target.value); | ||
| setCurrentPage(1); // Reset to first page when filter changes | ||
| }; | ||
|
|
||
| const handleNextPage = () => { | ||
| if (currentPage < totalPages) { | ||
| setCurrentPage(currentPage + 1); | ||
| } | ||
| }; | ||
|
|
||
| const handlePreviousPage = () => { | ||
| if (currentPage > 1) { | ||
| setCurrentPage(currentPage - 1); | ||
| } | ||
| }; | ||
|
|
||
| if (loading) { | ||
| return <div className="text-gray-700">Loading transactions...</div>; | ||
| } | ||
|
|
@@ -49,8 +78,23 @@ const TransactionList: React.FC = () => { | |
|
|
||
| return ( | ||
| <div className="bg-white shadow overflow-hidden sm:rounded-lg mt-8"> | ||
| <div className="px-4 py-5 sm:px-6"> | ||
| <div className="px-4 py-5 sm:px-6 flex justify-between items-center"> | ||
| <h3 className="text-lg leading-6 font-medium text-gray-900">Recent Transactions</h3> | ||
| <div className="flex items-center gap-2"> | ||
| <label htmlFor="type-filter" className="text-sm font-medium text-gray-700"> | ||
| Filter by type: | ||
| </label> | ||
| <select | ||
| id="type-filter" | ||
| value={filterType} | ||
| onChange={handleFilterChange} | ||
| className="border border-gray-300 rounded-md px-3 py-1 text-sm focus:outline-none focus:ring-2 focus:ring-blue-500" | ||
| > | ||
|
Comment on lines
+87
to
+92
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We can add |
||
| <option value="all">All</option> | ||
| <option value="credit">Credit</option> | ||
| <option value="debit">Debit</option> | ||
| </select> | ||
| </div> | ||
| </div> | ||
| <div className="border-t border-gray-200"> | ||
| <table className="min-w-full divide-y divide-gray-200"> | ||
|
|
@@ -64,7 +108,7 @@ const TransactionList: React.FC = () => { | |
| </tr> | ||
| </thead> | ||
| <tbody className="bg-white divide-y divide-gray-200"> | ||
| {transactions.map((transaction, index) => ( | ||
| {paginatedTransactions.map((transaction, index) => ( | ||
| <tr key={transaction.id} className={`${index % 2 === 0 ? 'bg-gray-50' : 'bg-white'}`}> | ||
| <td className="px-6 py-4 whitespace-nowrap text-center text-sm text-gray-500"> | ||
| {new Date(transaction.date).toLocaleDateString()} | ||
|
|
@@ -87,16 +131,48 @@ const TransactionList: React.FC = () => { | |
| </td> | ||
| </tr> | ||
| ))} | ||
| {transactions.length === 0 && ( | ||
| {paginatedTransactions.length === 0 && ( | ||
| <tr> | ||
| <td colSpan={5} className="px-6 py-4 text-center text-gray-500">No transactions found.</td> | ||
| </tr> | ||
| )} | ||
| </tbody> | ||
| </table> | ||
| </div> | ||
|
|
||
| {totalPages > 1 && ( | ||
| <div className="px-4 py-3 bg-gray-50 border-t border-gray-200 sm:px-6 flex items-center justify-between"> | ||
| <div className="text-sm text-gray-700"> | ||
| Page {currentPage} of {totalPages} ({filteredTransactions.length} transactions) | ||
| </div> | ||
| <div className="flex gap-2"> | ||
| <button | ||
| onClick={handlePreviousPage} | ||
| disabled={currentPage === 1} | ||
| className={`px-3 py-1 rounded text-sm font-medium ${ | ||
| currentPage === 1 | ||
| ? 'bg-gray-200 text-gray-400 cursor-not-allowed' | ||
| : 'bg-blue-500 text-white hover:bg-blue-600' | ||
| }`} | ||
| > | ||
| Previous | ||
| </button> | ||
| <button | ||
| onClick={handleNextPage} | ||
| disabled={currentPage === totalPages} | ||
| className={`px-3 py-1 rounded text-sm font-medium ${ | ||
| currentPage === totalPages | ||
| ? 'bg-gray-200 text-gray-400 cursor-not-allowed' | ||
| : 'bg-blue-500 text-white hover:bg-blue-600' | ||
| }`} | ||
| > | ||
| Next | ||
| </button> | ||
| </div> | ||
| </div> | ||
| )} | ||
| </div> | ||
| ); | ||
| }; | ||
|
|
||
| export default TransactionList; | ||
| export default TransactionList; | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What about making this configurable?