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;
|
---|