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;
}
// Check if this is a parent category
const isParentCategory = parentCategories.some((p) => p.id === newValue);
const childCategories = isParentCategory
? childrenByParent[newValue] || []
: [];
// Category selection - toggle
let newSelection: string[];
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];
}
} 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);
}
if (newSelection.length === 0) {
newSelection = ["all"];
}
} 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];
} else {
// Regular add
newSelection = [...value, newValue];
}
}
}
onChange(newSelection);

View File

@@ -8,6 +8,7 @@ import {
useDuplicateIds,
} from "@/lib/hooks";
import type { TransactionsPaginatedParams } from "@/services/banking.service";
import type { Category } from "@/lib/types";
type SortField = "date" | "amount" | "description";
type SortOrder = "asc" | "desc";
@@ -74,15 +75,35 @@ export function useTransactionsPage() {
const categoryIdsParam = searchParams.get("categoryIds");
const includeUncategorizedParam = searchParams.get("includeUncategorized");
if (categoryIdsParam) {
if (categoryIdsParam && metadata) {
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);
} else if (includeUncategorizedParam === "true") {
setSelectedCategories(["uncategorized"]);
setPage(0);
}
}, [searchParams]);
}, [searchParams, metadata]);
// Calculate start date based on period
const startDate = useMemo(() => {