feat: enhance category filter functionality to include child categories in selection and deselection; update transaction page to expand selected categories based on parent-child relationships

This commit is contained in:
Julien Froidefond
2025-12-21 07:38:57 +01:00
parent b4dace0673
commit 55f0e5c625
2 changed files with 67 additions and 9 deletions

View File

@@ -87,21 +87,58 @@ export function CategoryFilterCombobox({
return; return;
} }
// Check if this is a parent category
const isParentCategory = parentCategories.some((p) => p.id === newValue);
const childCategories = isParentCategory
? childrenByParent[newValue] || []
: [];
// Category selection - toggle // Category selection - toggle
let newSelection: string[]; let newSelection: string[];
if (isAll || isUncategorized) { if (isAll || isUncategorized) {
// Start fresh with just this category // Start fresh with this category and its children (if parent)
if (isParentCategory && childCategories.length > 0) {
newSelection = [
newValue,
...childCategories.map((child) => child.id),
];
} else {
newSelection = [newValue]; newSelection = [newValue];
}
} else if (value.includes(newValue)) { } else if (value.includes(newValue)) {
// Remove category // Remove category and its children (if parent)
if (isParentCategory && childCategories.length > 0) {
const childIds = childCategories.map((child) => child.id);
newSelection = value.filter(
(v) => v !== newValue && !childIds.includes(v)
);
} else {
newSelection = value.filter((v) => v !== newValue); newSelection = value.filter((v) => v !== newValue);
}
if (newSelection.length === 0) { if (newSelection.length === 0) {
newSelection = ["all"]; newSelection = ["all"];
} }
} else { } else {
// Add category // Add category and its children (if parent)
if (isParentCategory && childCategories.length > 0) {
const childIds = childCategories.map((child) => child.id);
newSelection = [
...value.filter((v) => !childIds.includes(v)), // Remove any existing children
newValue,
...childIds,
];
} else {
// Check if this child's parent is already selected
const category = categories.find((c) => c.id === newValue);
if (category?.parentId && value.includes(category.parentId)) {
// Parent is selected, so we're adding a child - keep parent
newSelection = [...value, newValue]; newSelection = [...value, newValue];
} else {
// Regular add
newSelection = [...value, newValue];
}
}
} }
onChange(newSelection); onChange(newSelection);

View File

@@ -8,6 +8,7 @@ import {
useDuplicateIds, useDuplicateIds,
} from "@/lib/hooks"; } from "@/lib/hooks";
import type { TransactionsPaginatedParams } from "@/services/banking.service"; import type { TransactionsPaginatedParams } from "@/services/banking.service";
import type { Category } from "@/lib/types";
type SortField = "date" | "amount" | "description"; type SortField = "date" | "amount" | "description";
type SortOrder = "asc" | "desc"; type SortOrder = "asc" | "desc";
@@ -74,15 +75,35 @@ export function useTransactionsPage() {
const categoryIdsParam = searchParams.get("categoryIds"); const categoryIdsParam = searchParams.get("categoryIds");
const includeUncategorizedParam = searchParams.get("includeUncategorized"); const includeUncategorizedParam = searchParams.get("includeUncategorized");
if (categoryIdsParam) { if (categoryIdsParam && metadata) {
const categoryIds = categoryIdsParam.split(","); const categoryIds = categoryIdsParam.split(",");
setSelectedCategories(categoryIds);
// Expand parent categories to include their children
const expandedCategoryIds = new Set<string>(categoryIds);
categoryIds.forEach((categoryId) => {
// Check if this is a parent category
const category = metadata.categories.find(
(c: Category) => c.id === categoryId
);
if (category && category.parentId === null) {
// Find all children of this parent
const children = metadata.categories.filter(
(c: Category) => c.parentId === categoryId
);
children.forEach((child: Category) => {
expandedCategoryIds.add(child.id);
});
}
});
setSelectedCategories(Array.from(expandedCategoryIds));
setPage(0); setPage(0);
} else if (includeUncategorizedParam === "true") { } else if (includeUncategorizedParam === "true") {
setSelectedCategories(["uncategorized"]); setSelectedCategories(["uncategorized"]);
setPage(0); setPage(0);
} }
}, [searchParams]); }, [searchParams, metadata]);
// Calculate start date based on period // Calculate start date based on period
const startDate = useMemo(() => { const startDate = useMemo(() => {