refactor: optimize TransactionsPage component by removing fullscreen functionality and stabilizing transactions reference with useMemo; clean up unused imports

This commit is contained in:
Julien Froidefond
2025-12-21 08:17:33 +01:00
parent dbcf8e7abd
commit b3e99a15d2
2 changed files with 19 additions and 110 deletions

View File

@@ -1,15 +1,8 @@
"use client"; "use client";
import { useCallback, useState } from "react"; import { useCallback, useMemo } from "react";
import { PageLayout, PageHeader } from "@/components/layout"; import { PageLayout, PageHeader } from "@/components/layout";
import { import { RefreshCw, Receipt, Euro, ChevronDown } from "lucide-react";
RefreshCw,
Maximize2,
Minimize2,
Receipt,
Euro,
ChevronDown,
} from "lucide-react";
import { import {
TransactionFilters, TransactionFilters,
TransactionBulkActions, TransactionBulkActions,
@@ -30,12 +23,6 @@ import {
CollapsibleTrigger, CollapsibleTrigger,
} from "@/components/ui/collapsible"; } from "@/components/ui/collapsible";
import { Upload } from "lucide-react"; import { Upload } from "lucide-react";
import {
Dialog,
DialogContent,
DialogHeader,
DialogTitle,
} from "@/components/ui/dialog";
import { useTransactionsPage } from "@/hooks/use-transactions-page"; import { useTransactionsPage } from "@/hooks/use-transactions-page";
import { useTransactionMutations } from "@/hooks/use-transaction-mutations"; import { useTransactionMutations } from "@/hooks/use-transaction-mutations";
import { useTransactionRules } from "@/hooks/use-transaction-rules"; import { useTransactionRules } from "@/hooks/use-transaction-rules";
@@ -43,7 +30,6 @@ import { useTransactionsChartData } from "@/hooks/use-transactions-chart-data";
export default function TransactionsPage() { export default function TransactionsPage() {
const queryClient = useQueryClient(); const queryClient = useQueryClient();
const [isFullscreen, setIsFullscreen] = useState(false);
// Main page state and logic // Main page state and logic
const { const {
@@ -147,7 +133,11 @@ export default function TransactionsPage() {
[handleBulkSetCategory, selectedTransactions, clearSelection] [handleBulkSetCategory, selectedTransactions, clearSelection]
); );
const filteredTransactions = transactionsData?.transactions || []; // Stabilize transactions reference to prevent unnecessary re-renders
const filteredTransactions = useMemo(
() => transactionsData?.transactions || [],
[transactionsData?.transactions]
);
const totalTransactions = transactionsData?.total || 0; const totalTransactions = transactionsData?.total || 0;
const hasMore = transactionsData?.hasMore || false; const hasMore = transactionsData?.hasMore || false;
const uncategorizedCount = transactionsData?.uncategorizedCount || 0; const uncategorizedCount = transactionsData?.uncategorizedCount || 0;
@@ -310,19 +300,6 @@ export default function TransactionsPage() {
</div> </div>
) : ( ) : (
<> <>
<div className="flex items-center justify-between mb-4">
<div className="flex-1" />
<Button
variant="outline"
size="sm"
onClick={() => setIsFullscreen(true)}
className="gap-2"
>
<Maximize2 className="w-4 h-4" />
Plein écran
</Button>
</div>
<TransactionTable <TransactionTable
transactions={filteredTransactions} transactions={filteredTransactions}
accounts={metadata.accounts} accounts={metadata.accounts}
@@ -355,74 +332,6 @@ export default function TransactionsPage() {
</> </>
)} )}
<Dialog open={isFullscreen} onOpenChange={setIsFullscreen}>
<DialogContent
className="!max-w-none !max-h-none !w-full !h-full !p-0 flex flex-col !rounded-none !translate-x-0 !translate-y-0 !top-0 !left-0 !right-0 !bottom-0 !m-0"
showCloseButton={false}
style={{
width: "100vw",
height: "100vh",
maxWidth: "100vw",
maxHeight: "100vh",
}}
>
<DialogHeader className="px-6 pt-6 pb-4 border-b shrink-0">
<div className="flex items-center justify-between">
<DialogTitle>Transactions</DialogTitle>
<Button
variant="ghost"
size="sm"
onClick={() => setIsFullscreen(false)}
className="gap-2"
>
<Minimize2 className="w-4 h-4" />
Réduire
</Button>
</div>
</DialogHeader>
<div className="flex-1 overflow-auto px-6 pb-6 min-h-0">
<div className="mb-4">
<TransactionBulkActions
selectedCount={selectedTransactions.size}
categories={metadata.categories}
onReconcile={handleBulkReconcileWithClear}
onSetCategory={handleBulkSetCategoryWithClear}
/>
</div>
<TransactionTable
transactions={filteredTransactions}
accounts={metadata.accounts}
categories={metadata.categories}
selectedTransactions={selectedTransactions}
sortField={sortField}
sortOrder={sortOrder}
onSortChange={onSortChange}
onToggleSelectAll={onToggleSelectAll}
onToggleSelectTransaction={onToggleSelectTransaction}
onToggleReconciled={toggleReconciled}
onMarkReconciled={markReconciled}
onSetCategory={setCategory}
onCreateRule={handleCreateRule}
onDelete={deleteTransaction}
formatCurrency={formatCurrency}
formatDate={formatDate}
updatingTransactionIds={updatingTransactionIds}
duplicateIds={duplicateIds}
highlightDuplicates={showDuplicates}
/>
<div className="mt-4">
<TransactionPagination
page={page}
pageSize={pageSize}
total={totalTransactions}
hasMore={hasMore}
onPageChange={onPageChange}
/>
</div>
</div>
</DialogContent>
</Dialog>
<RuleCreateDialog <RuleCreateDialog
open={ruleDialogOpen} open={ruleDialogOpen}
onOpenChange={setRuleDialogOpen} onOpenChange={setRuleDialogOpen}

View File

@@ -169,7 +169,7 @@ export function TransactionTable({
setFocusedIndex(index); setFocusedIndex(index);
onMarkReconciled(transactionId); onMarkReconciled(transactionId);
}, },
[onMarkReconciled], [onMarkReconciled]
); );
const handleKeyDown = useCallback( const handleKeyDown = useCallback(
@@ -198,7 +198,7 @@ export function TransactionTable({
} }
} }
}, },
[focusedIndex, transactions, onMarkReconciled, virtualizer], [focusedIndex, transactions, onMarkReconciled, virtualizer]
); );
useEffect(() => { useEffect(() => {
@@ -215,7 +215,7 @@ export function TransactionTable({
(accountId: string) => { (accountId: string) => {
return accounts.find((a) => a.id === accountId); return accounts.find((a) => a.id === accountId);
}, },
[accounts], [accounts]
); );
const getCategory = useCallback( const getCategory = useCallback(
@@ -223,7 +223,7 @@ export function TransactionTable({
if (!categoryId) return null; if (!categoryId) return null;
return categories.find((c) => c.id === categoryId); return categories.find((c) => c.id === categoryId);
}, },
[categories], [categories]
); );
return ( return (
@@ -281,7 +281,7 @@ export function TransactionTable({
"p-4 space-y-3 hover:bg-muted/50 cursor-pointer border-b border-border", "p-4 space-y-3 hover:bg-muted/50 cursor-pointer border-b border-border",
transaction.isReconciled && "bg-emerald-500/5", transaction.isReconciled && "bg-emerald-500/5",
isFocused && "bg-primary/10 ring-1 ring-primary/30", isFocused && "bg-primary/10 ring-1 ring-primary/30",
isDuplicate && "shadow-sm", isDuplicate && "shadow-sm"
)} )}
> >
<div className="flex items-start justify-between gap-2"> <div className="flex items-start justify-between gap-2">
@@ -323,7 +323,7 @@ export function TransactionTable({
"font-semibold tabular-nums text-sm md:text-base shrink-0", "font-semibold tabular-nums text-sm md:text-base shrink-0",
transaction.amount >= 0 transaction.amount >= 0
? "text-emerald-600" ? "text-emerald-600"
: "text-red-600", : "text-red-600"
)} )}
> >
{transaction.amount >= 0 ? "+" : ""} {transaction.amount >= 0 ? "+" : ""}
@@ -358,7 +358,7 @@ export function TransactionTable({
showBadge showBadge
align="start" align="start"
disabled={updatingTransactionIds.has( disabled={updatingTransactionIds.has(
transaction.id, transaction.id
)} )}
/> />
</div> </div>
@@ -391,7 +391,7 @@ export function TransactionTable({
e.stopPropagation(); e.stopPropagation();
if ( if (
confirm( confirm(
`Êtes-vous sûr de vouloir supprimer cette transaction ?\n\n${transaction.description}\n${formatCurrency(transaction.amount)}`, `Êtes-vous sûr de vouloir supprimer cette transaction ?\n\n${transaction.description}\n${formatCurrency(transaction.amount)}`
) )
) { ) {
onDelete(transaction.id); onDelete(transaction.id);
@@ -464,7 +464,7 @@ export function TransactionTable({
<div className="p-3"></div> <div className="p-3"></div>
</div> </div>
</div> </div>
{/* Body virtualisé */} {/* Body virtualisé ou non selon le mode */}
<div <div
ref={parentRef} ref={parentRef}
className="overflow-auto" className="overflow-auto"
@@ -507,7 +507,7 @@ export function TransactionTable({
"grid grid-cols-[auto_120px_2fr_150px_180px_140px_auto_auto] gap-0 border-b border-border hover:bg-muted/50 cursor-pointer", "grid grid-cols-[auto_120px_2fr_150px_180px_140px_auto_auto] gap-0 border-b border-border hover:bg-muted/50 cursor-pointer",
transaction.isReconciled && "bg-emerald-500/5", transaction.isReconciled && "bg-emerald-500/5",
isFocused && "bg-primary/10 ring-1 ring-primary/30", isFocused && "bg-primary/10 ring-1 ring-primary/30",
isDuplicate && "shadow-sm", isDuplicate && "shadow-sm"
)} )}
> >
<div className="p-3" onClick={(e) => e.stopPropagation()}> <div className="p-3" onClick={(e) => e.stopPropagation()}>
@@ -576,7 +576,7 @@ export function TransactionTable({
"p-3 text-right font-semibold tabular-nums", "p-3 text-right font-semibold tabular-nums",
transaction.amount >= 0 transaction.amount >= 0
? "text-emerald-600" ? "text-emerald-600"
: "text-red-600", : "text-red-600"
)} )}
> >
{transaction.amount >= 0 ? "+" : ""} {transaction.amount >= 0 ? "+" : ""}
@@ -643,7 +643,7 @@ export function TransactionTable({
e.stopPropagation(); e.stopPropagation();
if ( if (
confirm( confirm(
`Êtes-vous sûr de vouloir supprimer cette transaction ?\n\n${transaction.description}\n${formatCurrency(transaction.amount)}`, `Êtes-vous sûr de vouloir supprimer cette transaction ?\n\n${transaction.description}\n${formatCurrency(transaction.amount)}`
) )
) { ) {
onDelete(transaction.id); onDelete(transaction.id);