'use client'; import { useState, useMemo, useEffect, useCallback, useRef } from 'react'; import { useSearchParams, useRouter, usePathname } from 'next/navigation'; import RecipeLayout from './RecipeLayout'; import RecipeCard from './RecipeCard'; import type { Recipe } from '@/lib/recipes'; import type { FilterState } from '@/lib/types'; interface RecipesClientProps { recipes: Recipe[]; categories: string[]; tags: string[]; } function parseFiltersFromParams(searchParams: URLSearchParams): FilterState { return { search: searchParams.get('search') || '', category: searchParams.get('category') || '', selectedTags: searchParams.get('tags') ? [...new Set(searchParams.get('tags')!.split(',').filter(Boolean))] : [], }; } function buildQueryString(filters: FilterState): string { const params = new URLSearchParams(); if (filters.search) params.set('search', filters.search); if (filters.category) params.set('category', filters.category); if (filters.selectedTags.length > 0) params.set('tags', filters.selectedTags.join(',')); const qs = params.toString(); return qs ? `?${qs}` : ''; } export default function RecipesClient({ recipes, categories, tags }: RecipesClientProps) { const searchParams = useSearchParams(); const router = useRouter(); const pathname = usePathname(); const [filters, setFilters] = useState(() => parseFiltersFromParams(searchParams) ); // Track internal updates to avoid reacting to our own URL changes const isInternalUpdate = useRef(false); // Sync URL → state on browser back/forward useEffect(() => { if (isInternalUpdate.current) { isInternalUpdate.current = false; return; } setFilters(parseFiltersFromParams(searchParams)); }, [searchParams]); // Update filters and sync to URL const updateFilters = useCallback((newFilters: FilterState) => { isInternalUpdate.current = true; setFilters(newFilters); router.replace(`${pathname}${buildQueryString(newFilters)}`, { scroll: false }); }, [router, pathname]); const filteredRecipes = useMemo(() => { return recipes.filter((recipe) => { if (filters.search) { const searchLower = filters.search.toLowerCase(); const matchesSearch = recipe.title.toLowerCase().includes(searchLower) || recipe.description.toLowerCase().includes(searchLower) || recipe.tags.some((tag) => tag.toLowerCase().includes(searchLower)); if (!matchesSearch) return false; } if (filters.category && recipe.category !== filters.category) { return false; } if (filters.selectedTags.length > 0) { const hasAllTags = filters.selectedTags.every((tag) => recipe.tags.includes(tag)); if (!hasAllTags) return false; } return true; }); }, [recipes, filters]); return (
{filteredRecipes.length === recipes.length ? `Showing all ${recipes.length} recipes` : `Showing ${filteredRecipes.length} of ${recipes.length} recipes`}
{filteredRecipes.length > 0 ? (
{filteredRecipes.map((recipe) => ( ))}
) : (
🔍

No recipes found

Try adjusting your filters or search terms

)}
); }