feat(frontend): Fix search focus issue and add transaction statistics.

Co-authored-by: elisiariocouto <818914+elisiariocouto@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot]
2025-12-07 01:32:03 +00:00
committed by Elisiário Couto
parent 88037f328d
commit 2c85722fd0
2 changed files with 114 additions and 0 deletions

View File

@@ -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 (
<div className="space-y-6 max-w-full">
{/* New FilterBar */}
@@ -366,6 +385,78 @@ export default function TransactionsTable() {
isSearchLoading={isSearchLoading}
/>
{/* Transaction Statistics */}
{transactions.length > 0 && (
<div className="grid grid-cols-1 md:grid-cols-4 gap-4">
<Card className="p-4">
<div className="flex items-center justify-between">
<div>
<p className="text-xs text-muted-foreground uppercase tracking-wider">
Showing
</p>
<p className="text-2xl font-bold text-foreground mt-1">
{stats.pageCount}
</p>
<p className="text-xs text-muted-foreground mt-1">
of {stats.totalCount} total
</p>
</div>
</div>
</Card>
<Card className="p-4">
<div className="flex items-center justify-between">
<div>
<p className="text-xs text-muted-foreground uppercase tracking-wider">
Income
</p>
<p className="text-2xl font-bold text-green-600 mt-1">
+{formatCurrency(stats.totalIncome, transactions[0]?.transaction_currency || "EUR")}
</p>
</div>
<TrendingUp className="h-8 w-8 text-green-600 opacity-50" />
</div>
</Card>
<Card className="p-4">
<div className="flex items-center justify-between">
<div>
<p className="text-xs text-muted-foreground uppercase tracking-wider">
Expenses
</p>
<p className="text-2xl font-bold text-red-600 mt-1">
-{formatCurrency(stats.totalExpenses, transactions[0]?.transaction_currency || "EUR")}
</p>
</div>
<TrendingDown className="h-8 w-8 text-red-600 opacity-50" />
</div>
</Card>
<Card className="p-4">
<div className="flex items-center justify-between">
<div>
<p className="text-xs text-muted-foreground uppercase tracking-wider">
Net Change
</p>
<p
className={`text-2xl font-bold mt-1 ${
stats.netChange >= 0 ? "text-green-600" : "text-red-600"
}`}
>
{stats.netChange >= 0 ? "+" : ""}
{formatCurrency(stats.netChange, transactions[0]?.transaction_currency || "EUR")}
</p>
</div>
{stats.netChange >= 0 ? (
<TrendingUp className="h-8 w-8 text-green-600 opacity-50" />
) : (
<TrendingDown className="h-8 w-8 text-red-600 opacity-50" />
)}
</div>
</Card>
</div>
)}
{/* Responsive Table/Cards */}
<Card>
{/* Desktop Table View (hidden on mobile) */}

View File

@@ -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<HTMLInputElement>(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({
<div className="relative w-[200px]">
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 h-4 w-4 text-muted-foreground" />
<Input
ref={searchInputRef}
placeholder="Search transactions..."
value={filterState.searchTerm}
onChange={(e) => onFilterChange("searchTerm", e.target.value)}
@@ -99,6 +121,7 @@ export function FilterBar({
<div className="relative">
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 h-4 w-4 text-muted-foreground" />
<Input
ref={searchInputRef}
placeholder="Search..."
value={filterState.searchTerm}
onChange={(e) => onFilterChange("searchTerm", e.target.value)}