chore: standardize quotes in pnpm-lock.yaml and clean up formatting in various files for improved readability
This commit is contained in:
@@ -16,3 +16,4 @@ README.md
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -62,10 +62,8 @@ function FolderDropZone({
|
||||
export default function AccountsPage() {
|
||||
const queryClient = useQueryClient();
|
||||
const { data: metadata, isLoading: isLoadingMetadata } = useBankingMetadata();
|
||||
const {
|
||||
data: accountsWithStats,
|
||||
isLoading: isLoadingAccounts,
|
||||
} = useAccountsWithStats();
|
||||
const { data: accountsWithStats, isLoading: isLoadingAccounts } =
|
||||
useAccountsWithStats();
|
||||
|
||||
// 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 />;
|
||||
}
|
||||
|
||||
// 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) => {
|
||||
return new Intl.NumberFormat("fr-FR", {
|
||||
|
||||
@@ -21,10 +21,7 @@ export async function GET(request: NextRequest) {
|
||||
});
|
||||
}
|
||||
|
||||
return NextResponse.json(
|
||||
{ error: "Invalid request" },
|
||||
{ status: 400 },
|
||||
);
|
||||
return NextResponse.json({ error: "Invalid request" }, { status: 400 });
|
||||
} catch (error) {
|
||||
console.error("Error fetching accounts:", error);
|
||||
return NextResponse.json(
|
||||
|
||||
@@ -21,10 +21,7 @@ export async function GET(request: NextRequest) {
|
||||
});
|
||||
}
|
||||
|
||||
return NextResponse.json(
|
||||
{ error: "Invalid request" },
|
||||
{ status: 400 },
|
||||
);
|
||||
return NextResponse.json({ error: "Invalid request" }, { status: 400 });
|
||||
} catch (error) {
|
||||
console.error("Error fetching category stats:", error);
|
||||
return NextResponse.json(
|
||||
|
||||
@@ -41,7 +41,7 @@ export default function CategoriesPage() {
|
||||
const [isDialogOpen, setIsDialogOpen] = useState(false);
|
||||
const [editingCategory, setEditingCategory] = useState<Category | null>(null);
|
||||
const [expandedParents, setExpandedParents] = useState<Set<string>>(
|
||||
new Set()
|
||||
new Set(),
|
||||
);
|
||||
const [formData, setFormData] = useState({
|
||||
name: "",
|
||||
@@ -52,7 +52,7 @@ export default function CategoriesPage() {
|
||||
});
|
||||
const [searchQuery, setSearchQuery] = useState("");
|
||||
const [recatResults, setRecatResults] = useState<RecategorizationResult[]>(
|
||||
[]
|
||||
[],
|
||||
);
|
||||
const [isRecatDialogOpen, setIsRecatDialogOpen] = useState(false);
|
||||
const [isRecategorizing, setIsRecategorizing] = useState(false);
|
||||
@@ -68,7 +68,7 @@ export default function CategoriesPage() {
|
||||
};
|
||||
|
||||
const parents = metadata.categories.filter(
|
||||
(c: Category) => c.parentId === null
|
||||
(c: Category) => c.parentId === null,
|
||||
);
|
||||
const children: Record<string, Category[]> = {};
|
||||
const orphans: Category[] = [];
|
||||
@@ -77,7 +77,7 @@ export default function CategoriesPage() {
|
||||
.filter((c: Category) => c.parentId !== null)
|
||||
.forEach((child: Category) => {
|
||||
const parentExists = parents.some(
|
||||
(p: Category) => p.id === child.parentId
|
||||
(p: Category) => p.id === child.parentId,
|
||||
);
|
||||
if (parentExists) {
|
||||
if (!children[child.parentId!]) {
|
||||
@@ -136,7 +136,7 @@ export default function CategoriesPage() {
|
||||
|
||||
return { total, count };
|
||||
},
|
||||
[categoryStats, childrenByParent]
|
||||
[categoryStats, childrenByParent],
|
||||
);
|
||||
|
||||
if (isLoadingMetadata || !metadata || isLoadingStats || !categoryStats) {
|
||||
@@ -248,7 +248,7 @@ export default function CategoriesPage() {
|
||||
try {
|
||||
// Fetch uncategorized transactions
|
||||
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) {
|
||||
throw new Error("Failed to fetch uncategorized transactions");
|
||||
@@ -261,11 +261,11 @@ export default function CategoriesPage() {
|
||||
for (const transaction of uncategorized) {
|
||||
const categoryId = autoCategorize(
|
||||
transaction.description + " " + (transaction.memo || ""),
|
||||
metadata.categories
|
||||
metadata.categories,
|
||||
);
|
||||
if (categoryId) {
|
||||
const category = metadata.categories.find(
|
||||
(c: Category) => c.id === categoryId
|
||||
(c: Category) => c.id === categoryId,
|
||||
);
|
||||
if (category) {
|
||||
results.push({ transaction, category });
|
||||
@@ -299,9 +299,9 @@ export default function CategoriesPage() {
|
||||
return children.some(
|
||||
(c) =>
|
||||
c.name.toLowerCase().includes(query) ||
|
||||
c.keywords.some((k) => k.toLowerCase().includes(query))
|
||||
c.keywords.some((k) => k.toLowerCase().includes(query)),
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
return (
|
||||
@@ -346,9 +346,9 @@ export default function CategoriesPage() {
|
||||
(c) =>
|
||||
c.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
||||
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;
|
||||
const stats = getCategoryStats(parent.id, true);
|
||||
@@ -435,7 +435,7 @@ export default function CategoriesPage() {
|
||||
</p>
|
||||
<p className="text-xs text-muted-foreground truncate">
|
||||
{new Date(result.transaction.date).toLocaleDateString(
|
||||
"fr-FR"
|
||||
"fr-FR",
|
||||
)}
|
||||
{" • "}
|
||||
{new Intl.NumberFormat("fr-FR", {
|
||||
|
||||
@@ -12,10 +12,7 @@ import { useQueryClient } from "@tanstack/react-query";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { Sparkles, RefreshCw } from "lucide-react";
|
||||
import {
|
||||
updateCategory,
|
||||
autoCategorize,
|
||||
} from "@/lib/store-db";
|
||||
import { updateCategory, autoCategorize } from "@/lib/store-db";
|
||||
import {
|
||||
normalizeDescription,
|
||||
suggestKeyword,
|
||||
@@ -33,7 +30,7 @@ interface TransactionGroup {
|
||||
export default function RulesPage() {
|
||||
const queryClient = useQueryClient();
|
||||
const { data: metadata, isLoading: isLoadingMetadata } = useBankingMetadata();
|
||||
|
||||
|
||||
// Fetch uncategorized transactions only
|
||||
const {
|
||||
data: transactionsData,
|
||||
@@ -260,7 +257,12 @@ export default function RulesPage() {
|
||||
[refresh],
|
||||
);
|
||||
|
||||
if (isLoadingMetadata || !metadata || isLoadingTransactions || !transactionsData) {
|
||||
if (
|
||||
isLoadingMetadata ||
|
||||
!metadata ||
|
||||
isLoadingTransactions ||
|
||||
!transactionsData
|
||||
) {
|
||||
return <LoadingState />;
|
||||
}
|
||||
|
||||
|
||||
@@ -60,20 +60,20 @@ export default function TransactionsPage() {
|
||||
const [showReconciled, setShowReconciled] = useState<string>("all");
|
||||
const [period, setPeriod] = useState<Period>("all");
|
||||
const [customStartDate, setCustomStartDate] = useState<Date | undefined>(
|
||||
undefined
|
||||
undefined,
|
||||
);
|
||||
const [customEndDate, setCustomEndDate] = useState<Date | undefined>(
|
||||
undefined
|
||||
undefined,
|
||||
);
|
||||
const [isCustomDatePickerOpen, setIsCustomDatePickerOpen] = useState(false);
|
||||
const [sortField, setSortField] = useState<SortField>("date");
|
||||
const [sortOrder, setSortOrder] = useState<SortOrder>("desc");
|
||||
const [selectedTransactions, setSelectedTransactions] = useState<Set<string>>(
|
||||
new Set()
|
||||
new Set(),
|
||||
);
|
||||
const [ruleDialogOpen, setRuleDialogOpen] = useState(false);
|
||||
const [ruleTransaction, setRuleTransaction] = useState<Transaction | null>(
|
||||
null
|
||||
null,
|
||||
);
|
||||
|
||||
// Get start date based on period
|
||||
@@ -188,7 +188,7 @@ export default function TransactionsPage() {
|
||||
// Use transactions from current page to find similar ones
|
||||
const normalizedDesc = normalizeDescription(ruleTransaction.description);
|
||||
const similarTransactions = transactionsData.transactions.filter(
|
||||
(t) => normalizeDescription(t.description) === normalizedDesc
|
||||
(t) => normalizeDescription(t.description) === normalizedDesc,
|
||||
);
|
||||
|
||||
if (similarTransactions.length === 0) return null;
|
||||
@@ -199,7 +199,7 @@ export default function TransactionsPage() {
|
||||
transactions: similarTransactions,
|
||||
totalAmount: similarTransactions.reduce((sum, t) => sum + t.amount, 0),
|
||||
suggestedKeyword: suggestKeyword(
|
||||
similarTransactions.map((t) => t.description)
|
||||
similarTransactions.map((t) => t.description),
|
||||
),
|
||||
};
|
||||
}, [ruleTransaction, transactionsData]);
|
||||
@@ -215,7 +215,7 @@ export default function TransactionsPage() {
|
||||
|
||||
// 1. Add keyword to category
|
||||
const category = metadata.categories.find(
|
||||
(c: { id: string }) => c.id === ruleData.categoryId
|
||||
(c: { id: string }) => c.id === ruleData.categoryId,
|
||||
);
|
||||
if (!category) {
|
||||
throw new Error("Category not found");
|
||||
@@ -223,7 +223,7 @@ export default function TransactionsPage() {
|
||||
|
||||
// Check if keyword already exists
|
||||
const keywordExists = category.keywords.some(
|
||||
(k: string) => k.toLowerCase() === ruleData.keyword.toLowerCase()
|
||||
(k: string) => k.toLowerCase() === ruleData.keyword.toLowerCase(),
|
||||
);
|
||||
|
||||
if (!keywordExists) {
|
||||
@@ -241,8 +241,8 @@ export default function TransactionsPage() {
|
||||
method: "PUT",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ id, categoryId: ruleData.categoryId }),
|
||||
})
|
||||
)
|
||||
}),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -251,7 +251,7 @@ export default function TransactionsPage() {
|
||||
queryClient.invalidateQueries({ queryKey: ["banking-metadata"] });
|
||||
setRuleDialogOpen(false);
|
||||
},
|
||||
[metadata, queryClient]
|
||||
[metadata, queryClient],
|
||||
);
|
||||
|
||||
const invalidateAll = useCallback(() => {
|
||||
@@ -282,7 +282,7 @@ export default function TransactionsPage() {
|
||||
if (!transactionsData) return;
|
||||
|
||||
const transaction = transactionsData.transactions.find(
|
||||
(t) => t.id === transactionId
|
||||
(t) => t.id === transactionId,
|
||||
);
|
||||
if (!transaction) return;
|
||||
|
||||
@@ -307,7 +307,7 @@ export default function TransactionsPage() {
|
||||
if (!transactionsData) return;
|
||||
|
||||
const transaction = transactionsData.transactions.find(
|
||||
(t) => t.id === transactionId
|
||||
(t) => t.id === transactionId,
|
||||
);
|
||||
if (!transaction || transaction.isReconciled) return;
|
||||
|
||||
@@ -330,12 +330,12 @@ export default function TransactionsPage() {
|
||||
|
||||
const setCategory = async (
|
||||
transactionId: string,
|
||||
categoryId: string | null
|
||||
categoryId: string | null,
|
||||
) => {
|
||||
if (!transactionsData) return;
|
||||
|
||||
const transaction = transactionsData.transactions.find(
|
||||
(t) => t.id === transactionId
|
||||
(t) => t.id === transactionId,
|
||||
);
|
||||
if (!transaction) return;
|
||||
|
||||
@@ -357,7 +357,7 @@ export default function TransactionsPage() {
|
||||
if (!transactionsData) return;
|
||||
|
||||
const transactionsToUpdate = transactionsData.transactions.filter((t) =>
|
||||
selectedTransactions.has(t.id)
|
||||
selectedTransactions.has(t.id),
|
||||
);
|
||||
|
||||
setSelectedTransactions(new Set());
|
||||
@@ -369,8 +369,8 @@ export default function TransactionsPage() {
|
||||
method: "PUT",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ ...t, isReconciled: reconciled }),
|
||||
})
|
||||
)
|
||||
}),
|
||||
),
|
||||
);
|
||||
invalidateTransactions();
|
||||
} catch (error) {
|
||||
@@ -382,7 +382,7 @@ export default function TransactionsPage() {
|
||||
if (!transactionsData) return;
|
||||
|
||||
const transactionsToUpdate = transactionsData.transactions.filter((t) =>
|
||||
selectedTransactions.has(t.id)
|
||||
selectedTransactions.has(t.id),
|
||||
);
|
||||
|
||||
setSelectedTransactions(new Set());
|
||||
@@ -394,8 +394,8 @@ export default function TransactionsPage() {
|
||||
method: "PUT",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ ...t, categoryId }),
|
||||
})
|
||||
)
|
||||
}),
|
||||
),
|
||||
);
|
||||
invalidateTransactions();
|
||||
} catch (error) {
|
||||
@@ -409,7 +409,7 @@ export default function TransactionsPage() {
|
||||
setSelectedTransactions(new Set());
|
||||
} else {
|
||||
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}`,
|
||||
{
|
||||
method: "DELETE",
|
||||
}
|
||||
},
|
||||
);
|
||||
if (!response.ok) throw new Error("Failed to delete transaction");
|
||||
invalidateTransactions();
|
||||
|
||||
@@ -22,4 +22,3 @@ export function QueryProvider({ children }: { children: React.ReactNode }) {
|
||||
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -137,7 +137,9 @@ export function useTransactions(
|
||||
export function useCategoryStats() {
|
||||
return useQuery({
|
||||
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");
|
||||
if (!response.ok) {
|
||||
throw new Error("Failed to fetch category stats");
|
||||
|
||||
6735
pnpm-lock.yaml
generated
6735
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -154,10 +154,7 @@ export const bankingService = {
|
||||
if (categoryIds && categoryIds.length > 0) {
|
||||
if (includeUncategorized) {
|
||||
categoryFilter.push({
|
||||
OR: [
|
||||
{ categoryId: { in: categoryIds } },
|
||||
{ categoryId: null },
|
||||
],
|
||||
OR: [{ categoryId: { in: categoryIds } }, { categoryId: null }],
|
||||
});
|
||||
} else {
|
||||
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
|
||||
// We need to sum absolute values, so we'll do it in two steps
|
||||
const stats = await prisma.transaction.groupBy({
|
||||
@@ -325,7 +324,7 @@ export const bankingService = {
|
||||
});
|
||||
|
||||
const statsMap: Record<string, { count: number; total: number }> = {};
|
||||
|
||||
|
||||
// Get uncategorized count
|
||||
const uncategorizedCount = await prisma.transaction.count({
|
||||
where: { categoryId: null },
|
||||
@@ -342,7 +341,7 @@ export const bankingService = {
|
||||
where: { categoryId: stat.categoryId },
|
||||
select: { amount: true },
|
||||
});
|
||||
|
||||
|
||||
const total = categoryTransactions.reduce(
|
||||
(sum, t) => sum + Math.abs(t.amount),
|
||||
0,
|
||||
@@ -380,21 +379,19 @@ export const bankingService = {
|
||||
countMap.set(tc.accountId, tc._count.id);
|
||||
});
|
||||
|
||||
return accounts.map(
|
||||
(a): Account & { transactionCount: number } => ({
|
||||
id: a.id,
|
||||
name: a.name,
|
||||
bankId: a.bankId,
|
||||
accountNumber: a.accountNumber,
|
||||
type: a.type as Account["type"],
|
||||
folderId: a.folderId,
|
||||
balance: a.balance,
|
||||
initialBalance: a.initialBalance,
|
||||
currency: a.currency,
|
||||
lastImport: a.lastImport,
|
||||
externalUrl: a.externalUrl,
|
||||
transactionCount: countMap.get(a.id) || 0,
|
||||
}),
|
||||
);
|
||||
return accounts.map((a): Account & { transactionCount: number } => ({
|
||||
id: a.id,
|
||||
name: a.name,
|
||||
bankId: a.bankId,
|
||||
accountNumber: a.accountNumber,
|
||||
type: a.type as Account["type"],
|
||||
folderId: a.folderId,
|
||||
balance: a.balance,
|
||||
initialBalance: a.initialBalance,
|
||||
currency: a.currency,
|
||||
lastImport: a.lastImport,
|
||||
externalUrl: a.externalUrl,
|
||||
transactionCount: countMap.get(a.id) || 0,
|
||||
}));
|
||||
},
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user