[d7b7f00] | 1 | import React, { useState, useEffect } from 'react';
|
---|
| 2 | import { useLocation, useNavigate } from 'react-router-dom';
|
---|
| 3 | import styles from '../../css/RecipesCss/recipe-style.module.css';
|
---|
| 4 | import Header from '../HomeComponents/Header';
|
---|
| 5 | import Footer from '../HomeComponents/Footer';
|
---|
| 6 |
|
---|
| 7 | const Recipes = () => {
|
---|
| 8 | const [recipes, setRecipes] = useState([]);
|
---|
| 9 | const [page, setPage] = useState(0);
|
---|
| 10 | const [totalPages, setTotalPages] = useState(0);
|
---|
| 11 | const [loading, setLoading] = useState(false);
|
---|
| 12 |
|
---|
| 13 | const location = useLocation();
|
---|
| 14 | const navigate = useNavigate();
|
---|
| 15 |
|
---|
| 16 | const getSearchParams = (location) => {
|
---|
| 17 | const searchParams = new URLSearchParams(location.search);
|
---|
| 18 | const nationality = searchParams.get('nationality');
|
---|
| 19 | const category = searchParams.get('category');
|
---|
| 20 | const page = parseInt(searchParams.get('page'), 10);
|
---|
| 21 | const productIds = searchParams.getAll('productId').map(id => parseInt(id));
|
---|
| 22 | const searchTerm = searchParams.get('searchTerm');
|
---|
| 23 |
|
---|
| 24 | return {
|
---|
| 25 | nationality,
|
---|
| 26 | category,
|
---|
| 27 | page: isNaN(page) ? 0 : page,
|
---|
| 28 | productIds,
|
---|
| 29 | searchTerm,
|
---|
| 30 | };
|
---|
| 31 | };
|
---|
| 32 | useEffect(() => {
|
---|
| 33 | const { nationality, category, page, productIds, searchTerm } = getSearchParams(location);
|
---|
| 34 |
|
---|
| 35 | setPage(page);
|
---|
| 36 | loadRecipes(page, nationality, category, productIds, searchTerm);
|
---|
| 37 | }, [location]);
|
---|
| 38 |
|
---|
| 39 | const loadRecipes = (page, nationality, category, productIds, searchTerm) => {
|
---|
| 40 | setLoading(true);
|
---|
| 41 |
|
---|
| 42 | let url = `http://localhost:8080/api/recipes?page=${page}&size=9`;
|
---|
| 43 | if (searchTerm) {
|
---|
| 44 | url += `&searchTerm=${encodeURIComponent(searchTerm)}`;
|
---|
| 45 | }
|
---|
| 46 | if (nationality) {
|
---|
| 47 | url += `&nationality=${nationality}`;
|
---|
| 48 | }
|
---|
| 49 | if (category) {
|
---|
| 50 | url += `&category=${category}`;
|
---|
| 51 | }
|
---|
| 52 | if (productIds && productIds.length > 0) {
|
---|
| 53 | productIds.forEach(id => {
|
---|
| 54 | url += `&productId=${id}`;
|
---|
| 55 | });
|
---|
| 56 | }
|
---|
| 57 |
|
---|
| 58 | fetch(url)
|
---|
| 59 | .then(response => response.json())
|
---|
| 60 | .then(data => {
|
---|
| 61 | setRecipes(data.content);
|
---|
| 62 | setTotalPages(data.totalPages);
|
---|
| 63 |
|
---|
| 64 | setTimeout(() => {
|
---|
| 65 | setLoading(false);
|
---|
| 66 | }, 300);
|
---|
| 67 |
|
---|
| 68 | window.scrollTo({ top: 0, behavior: 'smooth' });
|
---|
| 69 | })
|
---|
| 70 | .catch(error => {
|
---|
| 71 | console.error('Error fetching recipes:', error);
|
---|
| 72 | setLoading(false);
|
---|
| 73 | });
|
---|
| 74 | };
|
---|
| 75 |
|
---|
| 76 | const handlePageChange = (newPage) => {
|
---|
| 77 | if (newPage !== page && newPage >= 0 && newPage < totalPages) {
|
---|
| 78 | setPage(newPage);
|
---|
| 79 |
|
---|
| 80 | const searchParams = new URLSearchParams(location.search);
|
---|
| 81 | searchParams.set('page', newPage);
|
---|
| 82 |
|
---|
| 83 | navigate(`/recipes?${searchParams.toString()}`);
|
---|
| 84 | }
|
---|
| 85 | };
|
---|
| 86 |
|
---|
| 87 | const navigateToRecipe = (id) => {
|
---|
| 88 | const { nationality, category, productIds, page, searchTerm } = getSearchParams(location);
|
---|
| 89 |
|
---|
| 90 | navigate(`/recipes/${id}`, {
|
---|
| 91 | state: {
|
---|
| 92 | category,
|
---|
| 93 | nationality,
|
---|
| 94 | productIds,
|
---|
| 95 | page,
|
---|
| 96 | searchTerm,
|
---|
| 97 | previousSearch: location.search,
|
---|
| 98 | },
|
---|
| 99 | });
|
---|
| 100 | };
|
---|
| 101 |
|
---|
| 102 | return (
|
---|
| 103 | <>
|
---|
| 104 | <Header />
|
---|
| 105 | <div className={styles.recipesPage}>
|
---|
| 106 | {loading && <div className={styles.loaderOverlay}><div className={styles.spinner}></div></div>}
|
---|
| 107 | <div className={styles.container}>
|
---|
| 108 | <h1 className={styles.sectionTitle}>Discover Your Next Favorite Dish</h1>
|
---|
| 109 | <button className={styles.addRecipeButton} onClick={() => navigate("/recipes/add")}>Add your recipe</button>
|
---|
| 110 | <div className={styles.recipeCards}>
|
---|
| 111 | {recipes.map(recipe => (
|
---|
| 112 | <div key={recipe.id} className={styles.recipeCard}>
|
---|
| 113 | <div
|
---|
| 114 | className={styles.recipeLink}
|
---|
| 115 | onClick={() => navigateToRecipe(recipe.id)}
|
---|
| 116 | >
|
---|
| 117 | <img src={recipe.strMealThumb} alt={recipe.strMeal} className={styles.recipeImage} />
|
---|
| 118 | <h2 className={styles.recipeTitle}>{recipe.strMeal}</h2>
|
---|
| 119 | <div className={styles.recipeDetails}>
|
---|
| 120 | <span className={styles.category}>{recipe.strCategory}</span>
|
---|
| 121 | <span className={styles.origin}>{recipe.strArea}</span>
|
---|
| 122 | </div>
|
---|
| 123 | </div>
|
---|
| 124 | </div>
|
---|
| 125 | ))}
|
---|
| 126 | </div>
|
---|
| 127 | <div className={styles.pagination}>
|
---|
| 128 | {page > 0 && (
|
---|
| 129 | <button
|
---|
| 130 | onClick={() => handlePageChange(page - 1)}
|
---|
| 131 | className={styles.pageButton}
|
---|
| 132 | >
|
---|
| 133 | ←
|
---|
| 134 | </button>
|
---|
| 135 | )}
|
---|
| 136 | <span className={styles.currentPage}>
|
---|
| 137 | Page {page + 1} of {totalPages}
|
---|
| 138 | </span>
|
---|
| 139 | {page < totalPages - 1 && (
|
---|
| 140 | <button
|
---|
| 141 | onClick={() => handlePageChange(page + 1)}
|
---|
| 142 | className={styles.pageButton}
|
---|
| 143 | >
|
---|
| 144 | →
|
---|
| 145 | </button>
|
---|
| 146 | )}
|
---|
| 147 | </div>
|
---|
| 148 | </div>
|
---|
| 149 | </div>
|
---|
| 150 | <Footer />
|
---|
| 151 | </>
|
---|
| 152 | );
|
---|
| 153 | };
|
---|
| 154 |
|
---|
| 155 | export default Recipes;
|
---|