Filtering & Search
The Pizza Chef Frontend implements a comprehensive filtering and search system that allows users to quickly find pizzas based on multiple criteria. All filter preferences are persisted to localStorage for a seamless user experience across sessions.
State Management
Filters are managed globally through Redux Toolkit and defined in the pizzaSlice.ts:
export interface PizzaFilters {
search: string;
category: string;
maxPrice: number;
sortBy: SortOption;
}
export type SortOption = 'name-asc' | 'name-desc' | 'price-asc' | 'price-desc';
Default Filter Values
The application initializes with sensible defaults that are loaded from localStorage or fallback to:
const defaults: PizzaFilters = {
search: '',
category: 'all',
maxPrice: 50,
sortBy: 'name-asc',
};
The maxPrice filter has special handling: if a saved value is lower than 50, it resets to 50 to ensure new pizzas aren’t hidden by default.
Filter Actions
The Redux slice provides dedicated actions for updating each filter criterion:
setSearch: (state, action: PayloadAction<string>) => {
state.filters.search = action.payload;
localStorage.setItem('pizza_filters', JSON.stringify(state.filters));
}
setCategory: (state, action: PayloadAction<string>) => {
state.filters.category = action.payload;
localStorage.setItem('pizza_filters', JSON.stringify(state.filters));
}
setMaxPrice: (state, action: PayloadAction<number>) => {
state.filters.maxPrice = action.payload;
localStorage.setItem('pizza_filters', JSON.stringify(state.filters));
}
setSortBy: (state, action: PayloadAction<SortOption>) => {
state.filters.sortBy = action.payload;
localStorage.setItem('pizza_filters', JSON.stringify(state.filters));
}
Each action immediately persists the updated filters to localStorage, ensuring user preferences survive page refreshes.
Filter Logic Implementation
The actual filtering happens in the Dashboard.tsx component:
Search Filtering
The search filter matches against both pizza names and ingredients:
const searchTerm = filters.search.toLowerCase().trim();
const matchesSearch = !searchTerm ||
pizza.name.toLowerCase().includes(searchTerm) ||
pizza.ingredients.some((i: string) => i.toLowerCase().includes(searchTerm));
The search is case-insensitive and searches both the pizza name and all ingredients, making it easy to find “margherita” or any pizza with “basil”.
Category Filtering
Available categories include:
all - Shows all pizzas
Vegetarian
Meat
Seafood
Spicy
const matchesCategory = filters.category === 'all' || pizza.category === filters.category;
Price Range Filtering
The price filter uses a slider (0-50) with explicit number conversion for reliability:
const pizzaPrice = Number(pizza.price);
const maxPrice = Number(filters.maxPrice);
const matchesPrice = pizzaPrice <= maxPrice;
Combined Filter Logic
const filteredPizzas = pizzas
.filter((pizza: Pizza) => {
return matchesSearch && matchesCategory && matchesPrice;
})
All three criteria must be satisfied for a pizza to appear in the filtered results.
Sorting Implementation
After filtering, pizzas are sorted based on the selected option:
.sort((a: Pizza, b: Pizza) => {
if (filters.sortBy === 'name-asc') return a.name.localeCompare(b.name);
if (filters.sortBy === 'name-desc') return b.name.localeCompare(a.name);
const priceA = Number(a.price);
const priceB = Number(b.price);
if (filters.sortBy === 'price-asc') return priceA - priceB;
if (filters.sortBy === 'price-desc') return priceB - priceA;
return 0;
});
Sort Options
- A - Z (
name-asc): Alphabetical order using localeCompare
- Z - A (
name-desc): Reverse alphabetical order
- Price: Low to High (
price-asc): Ascending price sort
- Price: High to Low (
price-desc): Descending price sort
The sorting uses localeCompare() for name sorting to properly handle international characters and special characters.
UI Components
The PizzaFilters.tsx component provides the user interface:
<input
type="text"
placeholder="Search for your favorite pizza..."
value={filters.search}
onChange={(e) => dispatch(setSearch(e.target.value))}
/>
Category Dropdown
<select
value={filters.category}
onChange={(e) => dispatch(setCategory(e.target.value))}
>
{['all', 'Vegetarian', 'Meat', 'Seafood', 'Spicy'].map((cat) => (
<option key={cat} value={cat}>
{cat === 'all' ? 'All Categories' : cat}
</option>
))}
</select>
Price Range Slider
<input
type="range"
min="0"
max="50"
step="1"
value={filters.maxPrice}
onChange={(e) => dispatch(setMaxPrice(Number(e.target.value)))}
/>
The current max price is displayed in real-time as the user adjusts the slider.
Sort Dropdown
<select
value={filters.sortBy}
onChange={(e) => dispatch(setSortBy(e.target.value as SortOption))}
>
<option value="name-asc">A - Z</option>
<option value="name-desc">Z - A</option>
<option value="price-asc">Price: Low to High</option>
<option value="price-desc">Price: High to Low</option>
</select>
The filter and sort operations run on every render when the filters or pizza catalog changes. For optimal performance:
- Filtering happens before sorting to reduce the sort set size
- String comparisons use
.toLowerCase() once per filter cycle
- Number conversions are explicit to avoid type coercion issues
For very large catalogs (1000+ items), consider memoizing the filtered results using useMemo to avoid unnecessary recalculations.
Empty State Handling
When no pizzas match the current filters:
{filteredPizzas.length > 0 ? (
// Render pizza cards
) : (
<div className="col-span-full py-24 text-center">
<p>No pizzas found with these filters...</p>
<p>Try adjusting your search criteria!</p>
</div>
)}
LocalStorage Keys
pizza_filters: Stores the complete filter state as JSON
{
"search": "margherita",
"category": "Vegetarian",
"maxPrice": 30,
"sortBy": "price-asc"
}
Integration with Custom Pizzas
Custom pizzas created via the Add Pizza form are automatically included in the filterable catalog:
pizzas: [...pizzasData, ...loadCustomPizzas()] as Pizza[]
The filter system treats custom and default pizzas identically, providing a unified search experience.
src/store/pizzaSlice.ts:18-41 - Filter loading and defaults
src/store/pizzaSlice.ts:54-77 - Filter action reducers
src/components/pizza/PizzaFilters.tsx - Filter UI component
src/pages/Dashboard.tsx:11-34 - Filter and sort logic
src/types/pizza.ts:11-18 - Filter type definitions