Implement AND/OR filter logic in useCategoryFilter and update MarketPage UI

- Added support for AND/OR filtering modes in the `useCategoryFilter` composable, allowing users to filter products based on all or any selected categories.
- Introduced reactive state management for filter mode and updated the filtering logic to accommodate the new functionality.
- Enhanced the MarketPage UI with a toggle for selecting filter modes, improving user experience and accessibility.
- Updated ARIA attributes for better screen reader support in the filter mode selection.

These changes significantly enhance the category filtering capabilities, providing users with more control over product visibility.

Refactor CreateProductDialog and MarketPage for improved category handling

- Updated CreateProductDialog to utilize `model-value` and `@update:model-value` for the CategoryInput component, enhancing reactivity in category selection.
- Enhanced MarketPage filtering logic to support AND/OR modes, allowing for more flexible product filtering based on selected categories.
- Improved category normalization and matching logic to ensure accurate filtering results.

These changes streamline the category management and filtering processes, providing users with a more intuitive experience when creating and finding products.
This commit is contained in:
padreug 2025-09-26 00:03:23 +02:00
parent bb761abe75
commit 39ecba581f
3 changed files with 106 additions and 24 deletions

View file

@ -11,6 +11,7 @@ export interface CategoryFilterOptions {
caseSensitive?: boolean
includeEmpty?: boolean
minCount?: number
mode?: 'any' | 'all' // OR vs AND logic
}
/**
@ -24,6 +25,9 @@ export function useCategoryFilter(
// Use Set for O(1) lookups instead of array
const selectedCategories = ref<Set<string>>(new Set())
// Filter mode state (reactive)
const filterMode = ref<'any' | 'all'>(options.mode || 'any')
// Computed property for all available categories with counts
const allCategories = computed<CategoryItem[]>(() => {
const categoryMap = new Map<string, number>()
@ -49,7 +53,7 @@ export function useCategoryFilter(
.sort((a, b) => b.count - a.count) // Sort by popularity
})
// Optimized product filtering with early returns and efficient lookups
// Optimized product filtering with AND/OR logic
const filteredProducts = computed<Product[]>(() => {
const selectedSet = selectedCategories.value
@ -58,26 +62,34 @@ export function useCategoryFilter(
return products.value
}
// Convert selected categories to array for faster iteration in some cases
const selectedArray = Array.from(selectedSet)
return products.value.filter(product => {
// Handle empty categories
if (!product.categories?.length) {
return options.includeEmpty || false
}
// Check if product has any selected category (optimized)
for (const cat of product.categories) {
if (cat && cat.trim()) {
const normalizedCategory = options.caseSensitive ? cat : cat.toLowerCase()
if (selectedSet.has(normalizedCategory)) {
return true // Early return on first match
}
}
// Normalize product categories
const productCategories = product.categories
.filter(cat => cat && cat.trim())
.map(cat => options.caseSensitive ? cat : cat.toLowerCase())
if (productCategories.length === 0) {
return options.includeEmpty || false
}
return false
// Count matches between product categories and selected categories
const matchingCategories = productCategories.filter(cat =>
selectedSet.has(cat)
)
// Apply AND/OR logic
if (filterMode.value === 'all') {
// AND logic: Product must have ALL selected categories
return matchingCategories.length === selectedSet.size
} else {
// OR logic: Product must have ANY selected category
return matchingCategories.length > 0
}
})
})
@ -139,6 +151,14 @@ export function useCategoryFilter(
return selectedCategories.value.has(normalizedCategory)
}
const setFilterMode = (mode: 'any' | 'all') => {
filterMode.value = mode
}
const toggleFilterMode = () => {
filterMode.value = filterMode.value === 'any' ? 'all' : 'any'
}
// Category statistics
const categoryStats = computed(() => ({
totalCategories: allCategories.value.length,
@ -150,6 +170,7 @@ export function useCategoryFilter(
return {
// State (readonly to prevent external mutation)
selectedCategories: readonly(selectedCategories),
filterMode: readonly(filterMode),
allCategories,
filteredProducts,
selectedCount,
@ -163,7 +184,9 @@ export function useCategoryFilter(
removeCategory,
clearAllCategories,
selectMultipleCategories,
isSelected
isSelected,
setFilterMode,
toggleFilterMode
}
}