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() {
|
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", {
|
||||||
|
|||||||
@@ -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(
|
||||||
|
|||||||
@@ -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(
|
||||||
|
|||||||
@@ -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", {
|
||||||
|
|||||||
@@ -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 />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -22,4 +22,3 @@ export function QueryProvider({ children }: { children: React.ReactNode }) {
|
|||||||
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
|
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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");
|
||||||
|
|||||||
6637
pnpm-lock.yaml
generated
6637
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 (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,8 +379,7 @@ 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,
|
||||||
@@ -394,7 +392,6 @@ export const bankingService = {
|
|||||||
lastImport: a.lastImport,
|
lastImport: a.lastImport,
|
||||||
externalUrl: a.externalUrl,
|
externalUrl: a.externalUrl,
|
||||||
transactionCount: countMap.get(a.id) || 0,
|
transactionCount: countMap.get(a.id) || 0,
|
||||||
}),
|
}));
|
||||||
);
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user