refactor: standardize code formatting and improve readability across multiple components, including transaction handling and sidebar layout adjustments
This commit is contained in:
@@ -125,9 +125,6 @@ export async function DELETE(request: Request) {
|
|||||||
console.error("Error deleting transaction:", error);
|
console.error("Error deleting transaction:", error);
|
||||||
const errorMessage =
|
const errorMessage =
|
||||||
error instanceof Error ? error.message : "Failed to delete transaction";
|
error instanceof Error ? error.message : "Failed to delete transaction";
|
||||||
return NextResponse.json(
|
return NextResponse.json({ error: errorMessage }, { status: 500 });
|
||||||
{ error: errorMessage },
|
|
||||||
{ status: 500 },
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ export default function RulesPage() {
|
|||||||
offset: 0,
|
offset: 0,
|
||||||
includeUncategorized: true,
|
includeUncategorized: true,
|
||||||
},
|
},
|
||||||
!!metadata
|
!!metadata,
|
||||||
);
|
);
|
||||||
|
|
||||||
const refresh = useCallback(() => {
|
const refresh = useCallback(() => {
|
||||||
@@ -56,7 +56,7 @@ export default function RulesPage() {
|
|||||||
const [filterMinCount, setFilterMinCount] = useState(2);
|
const [filterMinCount, setFilterMinCount] = useState(2);
|
||||||
const [expandedGroups, setExpandedGroups] = useState<Set<string>>(new Set());
|
const [expandedGroups, setExpandedGroups] = useState<Set<string>>(new Set());
|
||||||
const [selectedGroup, setSelectedGroup] = useState<TransactionGroup | null>(
|
const [selectedGroup, setSelectedGroup] = useState<TransactionGroup | null>(
|
||||||
null
|
null,
|
||||||
);
|
);
|
||||||
const [isDialogOpen, setIsDialogOpen] = useState(false);
|
const [isDialogOpen, setIsDialogOpen] = useState(false);
|
||||||
const [isAutoCategorizing, setIsAutoCategorizing] = useState(false);
|
const [isAutoCategorizing, setIsAutoCategorizing] = useState(false);
|
||||||
@@ -87,7 +87,7 @@ export default function RulesPage() {
|
|||||||
totalAmount: transactions.reduce((sum, t) => sum + t.amount, 0),
|
totalAmount: transactions.reduce((sum, t) => sum + t.amount, 0),
|
||||||
suggestedKeyword: suggestKeyword(descriptions),
|
suggestedKeyword: suggestKeyword(descriptions),
|
||||||
};
|
};
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
// Filter by search query
|
// Filter by search query
|
||||||
@@ -98,7 +98,7 @@ export default function RulesPage() {
|
|||||||
(g) =>
|
(g) =>
|
||||||
g.displayName.toLowerCase().includes(query) ||
|
g.displayName.toLowerCase().includes(query) ||
|
||||||
g.key.includes(query) ||
|
g.key.includes(query) ||
|
||||||
g.suggestedKeyword.toLowerCase().includes(query)
|
g.suggestedKeyword.toLowerCase().includes(query),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -167,7 +167,7 @@ export default function RulesPage() {
|
|||||||
|
|
||||||
// 1. Add keyword to category
|
// 1. Add keyword to category
|
||||||
const category = metadata.categories.find(
|
const category = metadata.categories.find(
|
||||||
(c: { id: string }) => c.id === ruleData.categoryId
|
(c: { id: string }) => c.id === ruleData.categoryId,
|
||||||
);
|
);
|
||||||
if (!category) {
|
if (!category) {
|
||||||
throw new Error("Category not found");
|
throw new Error("Category not found");
|
||||||
@@ -175,7 +175,7 @@ export default function RulesPage() {
|
|||||||
|
|
||||||
// Check if keyword already exists
|
// Check if keyword already exists
|
||||||
const keywordExists = category.keywords.some(
|
const keywordExists = category.keywords.some(
|
||||||
(k: string) => k.toLowerCase() === ruleData.keyword.toLowerCase()
|
(k: string) => k.toLowerCase() === ruleData.keyword.toLowerCase(),
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!keywordExists) {
|
if (!keywordExists) {
|
||||||
@@ -193,14 +193,14 @@ export default function RulesPage() {
|
|||||||
method: "PUT",
|
method: "PUT",
|
||||||
headers: { "Content-Type": "application/json" },
|
headers: { "Content-Type": "application/json" },
|
||||||
body: JSON.stringify({ id, categoryId: ruleData.categoryId }),
|
body: JSON.stringify({ id, categoryId: ruleData.categoryId }),
|
||||||
})
|
}),
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
refresh();
|
refresh();
|
||||||
},
|
},
|
||||||
[metadata, refresh]
|
[metadata, refresh],
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleAutoCategorize = useCallback(async () => {
|
const handleAutoCategorize = useCallback(async () => {
|
||||||
@@ -214,7 +214,7 @@ export default function RulesPage() {
|
|||||||
for (const transaction of uncategorized) {
|
for (const transaction of uncategorized) {
|
||||||
const categoryId = autoCategorize(
|
const categoryId = autoCategorize(
|
||||||
transaction.description + " " + (transaction.memo || ""),
|
transaction.description + " " + (transaction.memo || ""),
|
||||||
metadata.categories
|
metadata.categories,
|
||||||
);
|
);
|
||||||
if (categoryId) {
|
if (categoryId) {
|
||||||
await fetch("/api/banking/transactions", {
|
await fetch("/api/banking/transactions", {
|
||||||
@@ -228,7 +228,7 @@ export default function RulesPage() {
|
|||||||
|
|
||||||
refresh();
|
refresh();
|
||||||
alert(
|
alert(
|
||||||
`${categorizedCount} transaction(s) catégorisée(s) automatiquement`
|
`${categorizedCount} transaction(s) catégorisée(s) automatiquement`,
|
||||||
);
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error auto-categorizing:", error);
|
console.error("Error auto-categorizing:", error);
|
||||||
@@ -247,8 +247,8 @@ export default function RulesPage() {
|
|||||||
method: "PUT",
|
method: "PUT",
|
||||||
headers: { "Content-Type": "application/json" },
|
headers: { "Content-Type": "application/json" },
|
||||||
body: JSON.stringify({ ...t, categoryId }),
|
body: JSON.stringify({ ...t, categoryId }),
|
||||||
})
|
}),
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
refresh();
|
refresh();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -256,7 +256,7 @@ export default function RulesPage() {
|
|||||||
alert("Erreur lors de la catégorisation");
|
alert("Erreur lors de la catégorisation");
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[refresh]
|
[refresh],
|
||||||
);
|
);
|
||||||
|
|
||||||
if (
|
if (
|
||||||
|
|||||||
@@ -65,20 +65,20 @@ export default function TransactionsPage() {
|
|||||||
const [showReconciled, setShowReconciled] = useState<string>("all");
|
const [showReconciled, setShowReconciled] = useState<string>("all");
|
||||||
const [period, setPeriod] = useState<Period>("all");
|
const [period, setPeriod] = useState<Period>("all");
|
||||||
const [customStartDate, setCustomStartDate] = useState<Date | undefined>(
|
const [customStartDate, setCustomStartDate] = useState<Date | undefined>(
|
||||||
undefined
|
undefined,
|
||||||
);
|
);
|
||||||
const [customEndDate, setCustomEndDate] = useState<Date | undefined>(
|
const [customEndDate, setCustomEndDate] = useState<Date | undefined>(
|
||||||
undefined
|
undefined,
|
||||||
);
|
);
|
||||||
const [isCustomDatePickerOpen, setIsCustomDatePickerOpen] = useState(false);
|
const [isCustomDatePickerOpen, setIsCustomDatePickerOpen] = useState(false);
|
||||||
const [sortField, setSortField] = useState<SortField>("date");
|
const [sortField, setSortField] = useState<SortField>("date");
|
||||||
const [sortOrder, setSortOrder] = useState<SortOrder>("desc");
|
const [sortOrder, setSortOrder] = useState<SortOrder>("desc");
|
||||||
const [selectedTransactions, setSelectedTransactions] = useState<Set<string>>(
|
const [selectedTransactions, setSelectedTransactions] = useState<Set<string>>(
|
||||||
new Set()
|
new Set(),
|
||||||
);
|
);
|
||||||
const [ruleDialogOpen, setRuleDialogOpen] = useState(false);
|
const [ruleDialogOpen, setRuleDialogOpen] = useState(false);
|
||||||
const [ruleTransaction, setRuleTransaction] = useState<Transaction | null>(
|
const [ruleTransaction, setRuleTransaction] = useState<Transaction | null>(
|
||||||
null
|
null,
|
||||||
);
|
);
|
||||||
const [updatingTransactionIds, setUpdatingTransactionIds] = useState<
|
const [updatingTransactionIds, setUpdatingTransactionIds] = useState<
|
||||||
Set<string>
|
Set<string>
|
||||||
@@ -182,7 +182,7 @@ export default function TransactionsPage() {
|
|||||||
// Use transactions from current page to find similar ones
|
// Use transactions from current page to find similar ones
|
||||||
const normalizedDesc = normalizeDescription(ruleTransaction.description);
|
const normalizedDesc = normalizeDescription(ruleTransaction.description);
|
||||||
const similarTransactions = transactionsData.transactions.filter(
|
const similarTransactions = transactionsData.transactions.filter(
|
||||||
(t) => normalizeDescription(t.description) === normalizedDesc
|
(t) => normalizeDescription(t.description) === normalizedDesc,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (similarTransactions.length === 0) return null;
|
if (similarTransactions.length === 0) return null;
|
||||||
@@ -193,7 +193,7 @@ export default function TransactionsPage() {
|
|||||||
transactions: similarTransactions,
|
transactions: similarTransactions,
|
||||||
totalAmount: similarTransactions.reduce((sum, t) => sum + t.amount, 0),
|
totalAmount: similarTransactions.reduce((sum, t) => sum + t.amount, 0),
|
||||||
suggestedKeyword: suggestKeyword(
|
suggestedKeyword: suggestKeyword(
|
||||||
similarTransactions.map((t) => t.description)
|
similarTransactions.map((t) => t.description),
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
}, [ruleTransaction, transactionsData]);
|
}, [ruleTransaction, transactionsData]);
|
||||||
@@ -209,7 +209,7 @@ export default function TransactionsPage() {
|
|||||||
|
|
||||||
// 1. Add keyword to category
|
// 1. Add keyword to category
|
||||||
const category = metadata.categories.find(
|
const category = metadata.categories.find(
|
||||||
(c: { id: string }) => c.id === ruleData.categoryId
|
(c: { id: string }) => c.id === ruleData.categoryId,
|
||||||
);
|
);
|
||||||
if (!category) {
|
if (!category) {
|
||||||
throw new Error("Category not found");
|
throw new Error("Category not found");
|
||||||
@@ -217,7 +217,7 @@ export default function TransactionsPage() {
|
|||||||
|
|
||||||
// Check if keyword already exists
|
// Check if keyword already exists
|
||||||
const keywordExists = category.keywords.some(
|
const keywordExists = category.keywords.some(
|
||||||
(k: string) => k.toLowerCase() === ruleData.keyword.toLowerCase()
|
(k: string) => k.toLowerCase() === ruleData.keyword.toLowerCase(),
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!keywordExists) {
|
if (!keywordExists) {
|
||||||
@@ -235,8 +235,8 @@ export default function TransactionsPage() {
|
|||||||
method: "PUT",
|
method: "PUT",
|
||||||
headers: { "Content-Type": "application/json" },
|
headers: { "Content-Type": "application/json" },
|
||||||
body: JSON.stringify({ id, categoryId: ruleData.categoryId }),
|
body: JSON.stringify({ id, categoryId: ruleData.categoryId }),
|
||||||
})
|
}),
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -245,7 +245,7 @@ export default function TransactionsPage() {
|
|||||||
queryClient.invalidateQueries({ queryKey: ["banking-metadata"] });
|
queryClient.invalidateQueries({ queryKey: ["banking-metadata"] });
|
||||||
setRuleDialogOpen(false);
|
setRuleDialogOpen(false);
|
||||||
},
|
},
|
||||||
[metadata, queryClient]
|
[metadata, queryClient],
|
||||||
);
|
);
|
||||||
|
|
||||||
const invalidateAll = useCallback(() => {
|
const invalidateAll = useCallback(() => {
|
||||||
@@ -272,7 +272,7 @@ export default function TransactionsPage() {
|
|||||||
if (!transactionsData) return;
|
if (!transactionsData) return;
|
||||||
|
|
||||||
const transaction = transactionsData.transactions.find(
|
const transaction = transactionsData.transactions.find(
|
||||||
(t) => t.id === transactionId
|
(t) => t.id === transactionId,
|
||||||
);
|
);
|
||||||
if (!transaction) return;
|
if (!transaction) return;
|
||||||
|
|
||||||
@@ -297,7 +297,7 @@ export default function TransactionsPage() {
|
|||||||
if (!transactionsData) return;
|
if (!transactionsData) return;
|
||||||
|
|
||||||
const transaction = transactionsData.transactions.find(
|
const transaction = transactionsData.transactions.find(
|
||||||
(t) => t.id === transactionId
|
(t) => t.id === transactionId,
|
||||||
);
|
);
|
||||||
if (!transaction || transaction.isReconciled) return;
|
if (!transaction || transaction.isReconciled) return;
|
||||||
|
|
||||||
@@ -320,12 +320,12 @@ export default function TransactionsPage() {
|
|||||||
|
|
||||||
const setCategory = async (
|
const setCategory = async (
|
||||||
transactionId: string,
|
transactionId: string,
|
||||||
categoryId: string | null
|
categoryId: string | null,
|
||||||
) => {
|
) => {
|
||||||
if (!transactionsData) return;
|
if (!transactionsData) return;
|
||||||
|
|
||||||
const transaction = transactionsData.transactions.find(
|
const transaction = transactionsData.transactions.find(
|
||||||
(t) => t.id === transactionId
|
(t) => t.id === transactionId,
|
||||||
);
|
);
|
||||||
if (!transaction) return;
|
if (!transaction) return;
|
||||||
|
|
||||||
@@ -350,7 +350,7 @@ export default function TransactionsPage() {
|
|||||||
return {
|
return {
|
||||||
...oldData,
|
...oldData,
|
||||||
transactions: oldData.transactions.map((t) =>
|
transactions: oldData.transactions.map((t) =>
|
||||||
t.id === transactionId ? { ...t, categoryId } : t
|
t.id === transactionId ? { ...t, categoryId } : t,
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
@@ -370,7 +370,7 @@ export default function TransactionsPage() {
|
|||||||
if (!transactionsData) return;
|
if (!transactionsData) return;
|
||||||
|
|
||||||
const transactionsToUpdate = transactionsData.transactions.filter((t) =>
|
const transactionsToUpdate = transactionsData.transactions.filter((t) =>
|
||||||
selectedTransactions.has(t.id)
|
selectedTransactions.has(t.id),
|
||||||
);
|
);
|
||||||
|
|
||||||
setSelectedTransactions(new Set());
|
setSelectedTransactions(new Set());
|
||||||
@@ -382,8 +382,8 @@ export default function TransactionsPage() {
|
|||||||
method: "PUT",
|
method: "PUT",
|
||||||
headers: { "Content-Type": "application/json" },
|
headers: { "Content-Type": "application/json" },
|
||||||
body: JSON.stringify({ ...t, isReconciled: reconciled }),
|
body: JSON.stringify({ ...t, isReconciled: reconciled }),
|
||||||
})
|
}),
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
invalidateTransactions();
|
invalidateTransactions();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -395,7 +395,7 @@ export default function TransactionsPage() {
|
|||||||
if (!transactionsData) return;
|
if (!transactionsData) return;
|
||||||
|
|
||||||
const transactionsToUpdate = transactionsData.transactions.filter((t) =>
|
const transactionsToUpdate = transactionsData.transactions.filter((t) =>
|
||||||
selectedTransactions.has(t.id)
|
selectedTransactions.has(t.id),
|
||||||
);
|
);
|
||||||
|
|
||||||
const transactionIds = transactionsToUpdate.map((t) => t.id);
|
const transactionIds = transactionsToUpdate.map((t) => t.id);
|
||||||
@@ -413,8 +413,8 @@ export default function TransactionsPage() {
|
|||||||
method: "PUT",
|
method: "PUT",
|
||||||
headers: { "Content-Type": "application/json" },
|
headers: { "Content-Type": "application/json" },
|
||||||
body: JSON.stringify({ ...t, categoryId }),
|
body: JSON.stringify({ ...t, categoryId }),
|
||||||
})
|
}),
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Mise à jour directe du cache après succès
|
// Mise à jour directe du cache après succès
|
||||||
@@ -424,7 +424,7 @@ export default function TransactionsPage() {
|
|||||||
return {
|
return {
|
||||||
...oldData,
|
...oldData,
|
||||||
transactions: oldData.transactions.map((t) =>
|
transactions: oldData.transactions.map((t) =>
|
||||||
transactionIds.includes(t.id) ? { ...t, categoryId } : t
|
transactionIds.includes(t.id) ? { ...t, categoryId } : t,
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
@@ -446,7 +446,7 @@ export default function TransactionsPage() {
|
|||||||
setSelectedTransactions(new Set());
|
setSelectedTransactions(new Set());
|
||||||
} else {
|
} else {
|
||||||
setSelectedTransactions(
|
setSelectedTransactions(
|
||||||
new Set(transactionsData.transactions.map((t) => t.id))
|
new Set(transactionsData.transactions.map((t) => t.id)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -488,7 +488,7 @@ export default function TransactionsPage() {
|
|||||||
return {
|
return {
|
||||||
...oldData,
|
...oldData,
|
||||||
transactions: oldData.transactions.filter(
|
transactions: oldData.transactions.filter(
|
||||||
(t) => t.id !== transactionId
|
(t) => t.id !== transactionId,
|
||||||
),
|
),
|
||||||
total: oldData.total - 1,
|
total: oldData.total - 1,
|
||||||
};
|
};
|
||||||
@@ -499,13 +499,13 @@ export default function TransactionsPage() {
|
|||||||
`/api/banking/transactions?id=${transactionId}`,
|
`/api/banking/transactions?id=${transactionId}`,
|
||||||
{
|
{
|
||||||
method: "DELETE",
|
method: "DELETE",
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
const errorData = await response.json().catch(() => ({}));
|
const errorData = await response.json().catch(() => ({}));
|
||||||
throw new Error(
|
throw new Error(
|
||||||
errorData.error || `Failed to delete transaction: ${response.status}`
|
errorData.error || `Failed to delete transaction: ${response.status}`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -77,10 +77,7 @@ function SidebarContent({
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<nav className={cn(
|
<nav className={cn("flex-1 space-y-2", collapsed ? "p-2" : "p-4")}>
|
||||||
"flex-1 space-y-2",
|
|
||||||
collapsed ? "p-2" : "p-4"
|
|
||||||
)}>
|
|
||||||
{navItems.map((item) => {
|
{navItems.map((item) => {
|
||||||
const isActive = pathname === item.href;
|
const isActive = pathname === item.href;
|
||||||
return (
|
return (
|
||||||
@@ -117,10 +114,12 @@ function SidebarContent({
|
|||||||
})}
|
})}
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<div className={cn(
|
<div
|
||||||
"border-t border-border/30 space-y-2",
|
className={cn(
|
||||||
collapsed ? "p-2" : "p-4"
|
"border-t border-border/30 space-y-2",
|
||||||
)}>
|
collapsed ? "p-2" : "p-4",
|
||||||
|
)}
|
||||||
|
>
|
||||||
<Link href="/settings" onClick={handleLinkClick}>
|
<Link href="/settings" onClick={handleLinkClick}>
|
||||||
<Button
|
<Button
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
@@ -186,10 +185,12 @@ export function Sidebar({ open, onOpenChange }: SidebarProps) {
|
|||||||
collapsed ? "w-16" : "w-64",
|
collapsed ? "w-16" : "w-64",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<div className={cn(
|
<div
|
||||||
"flex items-center border-b border-border/30 transition-all duration-300",
|
className={cn(
|
||||||
collapsed ? "justify-center p-4" : "justify-between p-6"
|
"flex items-center border-b border-border/30 transition-all duration-300",
|
||||||
)}>
|
collapsed ? "justify-center p-4" : "justify-between p-6",
|
||||||
|
)}
|
||||||
|
>
|
||||||
{!collapsed && (
|
{!collapsed && (
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
<div className="w-12 h-12 rounded-2xl bg-gradient-to-br from-primary via-primary/90 to-primary/80 flex items-center justify-center shadow-xl shadow-primary/30 backdrop-blur-sm">
|
<div className="w-12 h-12 rounded-2xl bg-gradient-to-br from-primary via-primary/90 to-primary/80 flex items-center justify-center shadow-xl shadow-primary/30 backdrop-blur-sm">
|
||||||
@@ -206,7 +207,7 @@ export function Sidebar({ open, onOpenChange }: SidebarProps) {
|
|||||||
onClick={() => setCollapsed(!collapsed)}
|
onClick={() => setCollapsed(!collapsed)}
|
||||||
className={cn(
|
className={cn(
|
||||||
"hover:bg-muted/60 rounded-xl transition-all duration-300 hover:scale-110",
|
"hover:bg-muted/60 rounded-xl transition-all duration-300 hover:scale-110",
|
||||||
collapsed ? "" : "ml-auto"
|
collapsed ? "" : "ml-auto",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{collapsed ? (
|
{collapsed ? (
|
||||||
|
|||||||
@@ -40,9 +40,7 @@ export function PageHeader({
|
|||||||
<h1 className="text-2xl md:text-4xl lg:text-5xl font-black text-foreground tracking-tight leading-tight flex-1 min-w-0">
|
<h1 className="text-2xl md:text-4xl lg:text-5xl font-black text-foreground tracking-tight leading-tight flex-1 min-w-0">
|
||||||
{title}
|
{title}
|
||||||
</h1>
|
</h1>
|
||||||
{rightContent && (
|
{rightContent && <div className="shrink-0">{rightContent}</div>}
|
||||||
<div className="shrink-0">{rightContent}</div>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
{description && (
|
{description && (
|
||||||
<div className="text-base md:text-lg text-muted-foreground/70 font-semibold">
|
<div className="text-base md:text-lg text-muted-foreground/70 font-semibold">
|
||||||
|
|||||||
@@ -164,7 +164,7 @@ export function TransactionTable({
|
|||||||
setFocusedIndex(index);
|
setFocusedIndex(index);
|
||||||
onMarkReconciled(transactionId);
|
onMarkReconciled(transactionId);
|
||||||
},
|
},
|
||||||
[onMarkReconciled]
|
[onMarkReconciled],
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleKeyDown = useCallback(
|
const handleKeyDown = useCallback(
|
||||||
@@ -193,7 +193,7 @@ export function TransactionTable({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[focusedIndex, transactions, onMarkReconciled, virtualizer]
|
[focusedIndex, transactions, onMarkReconciled, virtualizer],
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -210,7 +210,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(
|
||||||
@@ -218,7 +218,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 (
|
||||||
@@ -269,7 +269,7 @@ export function TransactionTable({
|
|||||||
className={cn(
|
className={cn(
|
||||||
"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",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<div className="flex items-start justify-between gap-2">
|
<div className="flex items-start justify-between gap-2">
|
||||||
@@ -297,7 +297,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 ? "+" : ""}
|
||||||
@@ -332,7 +332,7 @@ export function TransactionTable({
|
|||||||
showBadge
|
showBadge
|
||||||
align="start"
|
align="start"
|
||||||
disabled={updatingTransactionIds.has(
|
disabled={updatingTransactionIds.has(
|
||||||
transaction.id
|
transaction.id,
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -365,7 +365,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);
|
||||||
@@ -474,7 +474,7 @@ export function TransactionTable({
|
|||||||
className={cn(
|
className={cn(
|
||||||
"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",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<div className="p-3">
|
<div className="p-3">
|
||||||
@@ -529,7 +529,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 ? "+" : ""}
|
||||||
@@ -596,7 +596,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);
|
||||||
|
|||||||
@@ -182,9 +182,7 @@ export function AccountFilterCombobox({
|
|||||||
{isFolderPartiallySelected(folder.id) && (
|
{isFolderPartiallySelected(folder.id) && (
|
||||||
<div className="h-3 w-3 rounded-sm bg-primary/50 mr-1" />
|
<div className="h-3 w-3 rounded-sm bg-primary/50 mr-1" />
|
||||||
)}
|
)}
|
||||||
{isFolderSelected(folder.id) && (
|
{isFolderSelected(folder.id) && <Check className="h-4 w-4" />}
|
||||||
<Check className="h-4 w-4" />
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</CommandItem>
|
</CommandItem>
|
||||||
|
|
||||||
@@ -306,9 +304,7 @@ export function AccountFilterCombobox({
|
|||||||
)
|
)
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
{isAll && (
|
{isAll && <Check className="ml-auto h-4 w-4" />}
|
||||||
<Check className="ml-auto h-4 w-4" />
|
|
||||||
)}
|
|
||||||
</CommandItem>
|
</CommandItem>
|
||||||
</CommandGroup>
|
</CommandGroup>
|
||||||
|
|
||||||
|
|||||||
@@ -193,7 +193,11 @@ export function CategoryFilterCombobox({
|
|||||||
align="start"
|
align="start"
|
||||||
onOpenAutoFocus={(e) => e.preventDefault()}
|
onOpenAutoFocus={(e) => e.preventDefault()}
|
||||||
>
|
>
|
||||||
<Command value={isAll ? "all" : isUncategorized ? "uncategorized" : value.join(",")}>
|
<Command
|
||||||
|
value={
|
||||||
|
isAll ? "all" : isUncategorized ? "uncategorized" : value.join(",")
|
||||||
|
}
|
||||||
|
>
|
||||||
<CommandInput placeholder="Rechercher..." />
|
<CommandInput placeholder="Rechercher..." />
|
||||||
<CommandList className="max-h-[300px]">
|
<CommandList className="max-h-[300px]">
|
||||||
<CommandEmpty>Aucune catégorie trouvée.</CommandEmpty>
|
<CommandEmpty>Aucune catégorie trouvée.</CommandEmpty>
|
||||||
@@ -212,9 +216,7 @@ export function CategoryFilterCombobox({
|
|||||||
({filteredTransactions.length})
|
({filteredTransactions.length})
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
{isAll && (
|
{isAll && <Check className="ml-auto h-4 w-4 shrink-0" />}
|
||||||
<Check className="ml-auto h-4 w-4 shrink-0" />
|
|
||||||
)}
|
|
||||||
</CommandItem>
|
</CommandItem>
|
||||||
<CommandItem
|
<CommandItem
|
||||||
value="uncategorized"
|
value="uncategorized"
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ const MOBILE_BREAKPOINT = 768;
|
|||||||
|
|
||||||
export function useIsMobile() {
|
export function useIsMobile() {
|
||||||
const [isMobile, setIsMobile] = React.useState<boolean | undefined>(
|
const [isMobile, setIsMobile] = React.useState<boolean | undefined>(
|
||||||
undefined
|
undefined,
|
||||||
);
|
);
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ export function useLocalStorage<T>(key: string, initialValue: T) {
|
|||||||
|
|
||||||
// Helper function to serialize transaction params into a query key
|
// Helper function to serialize transaction params into a query key
|
||||||
export function getTransactionsQueryKey(
|
export function getTransactionsQueryKey(
|
||||||
params: TransactionsPaginatedParams = {}
|
params: TransactionsPaginatedParams = {},
|
||||||
): (string | number)[] {
|
): (string | number)[] {
|
||||||
const key: (string | number)[] = ["transactions"];
|
const key: (string | number)[] = ["transactions"];
|
||||||
if (params.limit) key.push(`limit:${params.limit}`);
|
if (params.limit) key.push(`limit:${params.limit}`);
|
||||||
@@ -106,7 +106,7 @@ export function getTransactionsQueryKey(
|
|||||||
|
|
||||||
export function useTransactions(
|
export function useTransactions(
|
||||||
params: TransactionsPaginatedParams = {},
|
params: TransactionsPaginatedParams = {},
|
||||||
enabled = true
|
enabled = true,
|
||||||
) {
|
) {
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
@@ -134,7 +134,7 @@ export function useTransactions(
|
|||||||
if (params.isReconciled !== undefined && params.isReconciled !== "all") {
|
if (params.isReconciled !== undefined && params.isReconciled !== "all") {
|
||||||
searchParams.set(
|
searchParams.set(
|
||||||
"isReconciled",
|
"isReconciled",
|
||||||
params.isReconciled === true ? "true" : "false"
|
params.isReconciled === true ? "true" : "false",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (params.sortField) searchParams.set("sortField", params.sortField);
|
if (params.sortField) searchParams.set("sortField", params.sortField);
|
||||||
|
|||||||
Reference in New Issue
Block a user