From 300b4e7db7a317a857f477c6a8bbb0d29bcb5fd8 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 7 Dec 2025 01:32:03 +0000 Subject: [PATCH] feat(frontend): Fix search focus issue and add transaction statistics. Co-authored-by: elisiariocouto <818914+elisiariocouto@users.noreply.github.com> --- frontend/src/components/TransactionsTable.tsx | 91 +++++++++++++++++++ frontend/src/components/filters/FilterBar.tsx | 23 +++++ 2 files changed, 114 insertions(+) diff --git a/frontend/src/components/TransactionsTable.tsx b/frontend/src/components/TransactionsTable.tsx index 13fb601..bf624b2 100644 --- a/frontend/src/components/TransactionsTable.tsx +++ b/frontend/src/components/TransactionsTable.tsx @@ -355,6 +355,25 @@ export default function TransactionsTable() { ); } + // Calculate stats from current page transactions + const totalIncome = transactions + .filter((t: Transaction) => t.transaction_value > 0) + .reduce((sum: number, t: Transaction) => sum + t.transaction_value, 0); + + const totalExpenses = Math.abs( + transactions + .filter((t: Transaction) => t.transaction_value < 0) + .reduce((sum: number, t: Transaction) => sum + t.transaction_value, 0) + ); + + const stats = { + totalCount: pagination?.total || 0, + pageCount: transactions.length, + totalIncome, + totalExpenses, + netChange: totalIncome - totalExpenses, + }; + return (
{/* New FilterBar */} @@ -366,6 +385,78 @@ export default function TransactionsTable() { isSearchLoading={isSearchLoading} /> + {/* Transaction Statistics */} + {transactions.length > 0 && ( +
+ +
+
+

+ Showing +

+

+ {stats.pageCount} +

+

+ of {stats.totalCount} total +

+
+
+
+ + +
+
+

+ Income +

+

+ +{formatCurrency(stats.totalIncome, transactions[0]?.transaction_currency || "EUR")} +

+
+ +
+
+ + +
+
+

+ Expenses +

+

+ -{formatCurrency(stats.totalExpenses, transactions[0]?.transaction_currency || "EUR")} +

+
+ +
+
+ + +
+
+

+ Net Change +

+

= 0 ? "text-green-600" : "text-red-600" + }`} + > + {stats.netChange >= 0 ? "+" : ""} + {formatCurrency(stats.netChange, transactions[0]?.transaction_currency || "EUR")} +

+
+ {stats.netChange >= 0 ? ( + + ) : ( + + )} +
+
+
+ )} + {/* Responsive Table/Cards */} {/* Desktop Table View (hidden on mobile) */} diff --git a/frontend/src/components/filters/FilterBar.tsx b/frontend/src/components/filters/FilterBar.tsx index 8400680..2b60ab4 100644 --- a/frontend/src/components/filters/FilterBar.tsx +++ b/frontend/src/components/filters/FilterBar.tsx @@ -1,3 +1,4 @@ +import { useRef, useEffect } from "react"; import { Search } from "lucide-react"; import { Input } from "@/components/ui/input"; import { cn } from "@/lib/utils"; @@ -30,6 +31,26 @@ export function FilterBar({ isSearchLoading = false, className, }: FilterBarProps) { + const searchInputRef = useRef(null); + const wasFocused = useRef(false); + + // Track if search input was focused before re-render + useEffect(() => { + const currentInput = searchInputRef.current; + if (!currentInput) return; + + // Check if the input was focused before the effect runs + if (document.activeElement === currentInput) { + wasFocused.current = true; + } + + // If it was focused, restore focus after render + if (wasFocused.current && document.activeElement !== currentInput) { + currentInput.focus(); + wasFocused.current = false; + } + }); + const hasActiveFilters = filterState.searchTerm || filterState.selectedAccount || @@ -61,6 +82,7 @@ export function FilterBar({
onFilterChange("searchTerm", e.target.value)} @@ -99,6 +121,7 @@ export function FilterBar({
onFilterChange("searchTerm", e.target.value)}