refactor: optimize TransactionsPage component by removing fullscreen functionality and stabilizing transactions reference with useMemo; clean up unused imports
This commit is contained in:
@@ -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}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
Reference in New Issue
Block a user