chore: standardize quotes in pnpm-lock.yaml and clean up formatting in various files for improved readability

This commit is contained in:
Julien Froidefond
2025-12-06 12:38:45 +01:00
parent ad8b936c7a
commit a7f3433f5f
11 changed files with 4385 additions and 2512 deletions

View File

@@ -16,3 +16,4 @@ README.md

View File

@@ -62,10 +62,8 @@ function FolderDropZone({
export default function AccountsPage() { export default function AccountsPage() {
const queryClient = useQueryClient(); const queryClient = useQueryClient();
const { data: metadata, isLoading: isLoadingMetadata } = useBankingMetadata(); const { data: metadata, isLoading: isLoadingMetadata } = useBankingMetadata();
const { const { data: accountsWithStats, isLoading: isLoadingAccounts } =
data: accountsWithStats, useAccountsWithStats();
isLoading: isLoadingAccounts,
} = useAccountsWithStats();
// refresh function is not used directly, invalidations are done inline // refresh function is not used directly, invalidations are done inline
@@ -105,12 +103,19 @@ export default function AccountsPage() {
}), }),
); );
if (isLoadingMetadata || !metadata || isLoadingAccounts || !accountsWithStats) { if (
isLoadingMetadata ||
!metadata ||
isLoadingAccounts ||
!accountsWithStats
) {
return <LoadingState />; return <LoadingState />;
} }
// Convert accountsWithStats to regular accounts for compatibility // Convert accountsWithStats to regular accounts for compatibility
const accounts = accountsWithStats.map(({ transactionCount: _transactionCount, ...account }) => account); const accounts = accountsWithStats.map(
({ transactionCount: _transactionCount, ...account }) => account,
);
const formatCurrency = (amount: number) => { const formatCurrency = (amount: number) => {
return new Intl.NumberFormat("fr-FR", { return new Intl.NumberFormat("fr-FR", {

View File

@@ -21,10 +21,7 @@ export async function GET(request: NextRequest) {
}); });
} }
return NextResponse.json( return NextResponse.json({ error: "Invalid request" }, { status: 400 });
{ error: "Invalid request" },
{ status: 400 },
);
} catch (error) { } catch (error) {
console.error("Error fetching accounts:", error); console.error("Error fetching accounts:", error);
return NextResponse.json( return NextResponse.json(

View File

@@ -21,10 +21,7 @@ export async function GET(request: NextRequest) {
}); });
} }
return NextResponse.json( return NextResponse.json({ error: "Invalid request" }, { status: 400 });
{ error: "Invalid request" },
{ status: 400 },
);
} catch (error) { } catch (error) {
console.error("Error fetching category stats:", error); console.error("Error fetching category stats:", error);
return NextResponse.json( return NextResponse.json(

View File

@@ -41,7 +41,7 @@ export default function CategoriesPage() {
const [isDialogOpen, setIsDialogOpen] = useState(false); const [isDialogOpen, setIsDialogOpen] = useState(false);
const [editingCategory, setEditingCategory] = useState<Category | null>(null); const [editingCategory, setEditingCategory] = useState<Category | null>(null);
const [expandedParents, setExpandedParents] = useState<Set<string>>( const [expandedParents, setExpandedParents] = useState<Set<string>>(
new Set() new Set(),
); );
const [formData, setFormData] = useState({ const [formData, setFormData] = useState({
name: "", name: "",
@@ -52,7 +52,7 @@ export default function CategoriesPage() {
}); });
const [searchQuery, setSearchQuery] = useState(""); const [searchQuery, setSearchQuery] = useState("");
const [recatResults, setRecatResults] = useState<RecategorizationResult[]>( const [recatResults, setRecatResults] = useState<RecategorizationResult[]>(
[] [],
); );
const [isRecatDialogOpen, setIsRecatDialogOpen] = useState(false); const [isRecatDialogOpen, setIsRecatDialogOpen] = useState(false);
const [isRecategorizing, setIsRecategorizing] = useState(false); const [isRecategorizing, setIsRecategorizing] = useState(false);
@@ -68,7 +68,7 @@ export default function CategoriesPage() {
}; };
const parents = metadata.categories.filter( const parents = metadata.categories.filter(
(c: Category) => c.parentId === null (c: Category) => c.parentId === null,
); );
const children: Record<string, Category[]> = {}; const children: Record<string, Category[]> = {};
const orphans: Category[] = []; const orphans: Category[] = [];
@@ -77,7 +77,7 @@ export default function CategoriesPage() {
.filter((c: Category) => c.parentId !== null) .filter((c: Category) => c.parentId !== null)
.forEach((child: Category) => { .forEach((child: Category) => {
const parentExists = parents.some( const parentExists = parents.some(
(p: Category) => p.id === child.parentId (p: Category) => p.id === child.parentId,
); );
if (parentExists) { if (parentExists) {
if (!children[child.parentId!]) { if (!children[child.parentId!]) {
@@ -136,7 +136,7 @@ export default function CategoriesPage() {
return { total, count }; return { total, count };
}, },
[categoryStats, childrenByParent] [categoryStats, childrenByParent],
); );
if (isLoadingMetadata || !metadata || isLoadingStats || !categoryStats) { if (isLoadingMetadata || !metadata || isLoadingStats || !categoryStats) {
@@ -248,7 +248,7 @@ export default function CategoriesPage() {
try { try {
// Fetch uncategorized transactions // Fetch uncategorized transactions
const uncategorizedResponse = await fetch( const uncategorizedResponse = await fetch(
"/api/banking/transactions?limit=1000&offset=0&includeUncategorized=true" "/api/banking/transactions?limit=1000&offset=0&includeUncategorized=true",
); );
if (!uncategorizedResponse.ok) { if (!uncategorizedResponse.ok) {
throw new Error("Failed to fetch uncategorized transactions"); throw new Error("Failed to fetch uncategorized transactions");
@@ -261,11 +261,11 @@ export default function CategoriesPage() {
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) {
const category = metadata.categories.find( const category = metadata.categories.find(
(c: Category) => c.id === categoryId (c: Category) => c.id === categoryId,
); );
if (category) { if (category) {
results.push({ transaction, category }); results.push({ transaction, category });
@@ -299,9 +299,9 @@ export default function CategoriesPage() {
return children.some( return children.some(
(c) => (c) =>
c.name.toLowerCase().includes(query) || c.name.toLowerCase().includes(query) ||
c.keywords.some((k) => k.toLowerCase().includes(query)) c.keywords.some((k) => k.toLowerCase().includes(query)),
); );
} },
); );
return ( return (
@@ -346,9 +346,9 @@ export default function CategoriesPage() {
(c) => (c) =>
c.name.toLowerCase().includes(searchQuery.toLowerCase()) || c.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
c.keywords.some((k) => c.keywords.some((k) =>
k.toLowerCase().includes(searchQuery.toLowerCase()) k.toLowerCase().includes(searchQuery.toLowerCase()),
) || ) ||
parent.name.toLowerCase().includes(searchQuery.toLowerCase()) parent.name.toLowerCase().includes(searchQuery.toLowerCase()),
) )
: allChildren; : allChildren;
const stats = getCategoryStats(parent.id, true); const stats = getCategoryStats(parent.id, true);
@@ -435,7 +435,7 @@ export default function CategoriesPage() {
</p> </p>
<p className="text-xs text-muted-foreground truncate"> <p className="text-xs text-muted-foreground truncate">
{new Date(result.transaction.date).toLocaleDateString( {new Date(result.transaction.date).toLocaleDateString(
"fr-FR" "fr-FR",
)} )}
{" • "} {" • "}
{new Intl.NumberFormat("fr-FR", { {new Intl.NumberFormat("fr-FR", {

View File

@@ -12,10 +12,7 @@ import { useQueryClient } from "@tanstack/react-query";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Badge } from "@/components/ui/badge"; import { Badge } from "@/components/ui/badge";
import { Sparkles, RefreshCw } from "lucide-react"; import { Sparkles, RefreshCw } from "lucide-react";
import { import { updateCategory, autoCategorize } from "@/lib/store-db";
updateCategory,
autoCategorize,
} from "@/lib/store-db";
import { import {
normalizeDescription, normalizeDescription,
suggestKeyword, suggestKeyword,
@@ -260,7 +257,12 @@ export default function RulesPage() {
[refresh], [refresh],
); );
if (isLoadingMetadata || !metadata || isLoadingTransactions || !transactionsData) { if (
isLoadingMetadata ||
!metadata ||
isLoadingTransactions ||
!transactionsData
) {
return <LoadingState />; return <LoadingState />;
} }

View File

@@ -60,20 +60,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,
); );
// Get start date based on period // Get start date based on period
@@ -188,7 +188,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;
@@ -199,7 +199,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]);
@@ -215,7 +215,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");
@@ -223,7 +223,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) {
@@ -241,8 +241,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 }),
}) }),
) ),
); );
} }
@@ -251,7 +251,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(() => {
@@ -282,7 +282,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;
@@ -307,7 +307,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;
@@ -330,12 +330,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;
@@ -357,7 +357,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());
@@ -369,8 +369,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) {
@@ -382,7 +382,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());
@@ -394,8 +394,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 }),
}) }),
) ),
); );
invalidateTransactions(); invalidateTransactions();
} catch (error) { } catch (error) {
@@ -409,7 +409,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)),
); );
} }
}; };
@@ -445,7 +445,7 @@ export default function TransactionsPage() {
`/api/banking/transactions?id=${transactionId}`, `/api/banking/transactions?id=${transactionId}`,
{ {
method: "DELETE", method: "DELETE",
} },
); );
if (!response.ok) throw new Error("Failed to delete transaction"); if (!response.ok) throw new Error("Failed to delete transaction");
invalidateTransactions(); invalidateTransactions();

View File

@@ -22,4 +22,3 @@ export function QueryProvider({ children }: { children: React.ReactNode }) {
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider> <QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
); );
} }

View File

@@ -137,7 +137,9 @@ export function useTransactions(
export function useCategoryStats() { export function useCategoryStats() {
return useQuery({ return useQuery({
queryKey: ["category-stats"], queryKey: ["category-stats"],
queryFn: async (): Promise<Record<string, { count: number; total: number }>> => { queryFn: async (): Promise<
Record<string, { count: number; total: number }>
> => {
const response = await fetch("/api/banking/categories?statsOnly=true"); const response = await fetch("/api/banking/categories?statsOnly=true");
if (!response.ok) { if (!response.ok) {
throw new Error("Failed to fetch category stats"); throw new Error("Failed to fetch category stats");

6735
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -154,10 +154,7 @@ export const bankingService = {
if (categoryIds && categoryIds.length > 0) { if (categoryIds && categoryIds.length > 0) {
if (includeUncategorized) { if (includeUncategorized) {
categoryFilter.push({ categoryFilter.push({
OR: [ OR: [{ categoryId: { in: categoryIds } }, { categoryId: null }],
{ categoryId: { in: categoryIds } },
{ categoryId: null },
],
}); });
} else { } else {
categoryFilter.push({ categoryId: { in: categoryIds } }); categoryFilter.push({ categoryId: { in: categoryIds } });
@@ -311,7 +308,9 @@ export const bankingService = {
}; };
}, },
async getCategoryStats(): Promise<Record<string, { count: number; total: number }>> { async getCategoryStats(): Promise<
Record<string, { count: number; total: number }>
> {
// Get stats for all categories in one query using aggregation // Get stats for all categories in one query using aggregation
// We need to sum absolute values, so we'll do it in two steps // We need to sum absolute values, so we'll do it in two steps
const stats = await prisma.transaction.groupBy({ const stats = await prisma.transaction.groupBy({
@@ -380,21 +379,19 @@ export const bankingService = {
countMap.set(tc.accountId, tc._count.id); countMap.set(tc.accountId, tc._count.id);
}); });
return accounts.map( return accounts.map((a): Account & { transactionCount: number } => ({
(a): Account & { transactionCount: number } => ({ id: a.id,
id: a.id, name: a.name,
name: a.name, bankId: a.bankId,
bankId: a.bankId, accountNumber: a.accountNumber,
accountNumber: a.accountNumber, type: a.type as Account["type"],
type: a.type as Account["type"], folderId: a.folderId,
folderId: a.folderId, balance: a.balance,
balance: a.balance, initialBalance: a.initialBalance,
initialBalance: a.initialBalance, currency: a.currency,
currency: a.currency, lastImport: a.lastImport,
lastImport: a.lastImport, externalUrl: a.externalUrl,
externalUrl: a.externalUrl, transactionCount: countMap.get(a.id) || 0,
transactionCount: countMap.get(a.id) || 0, }));
}),
);
}, },
}; };