From efb7b461693370b1b2ff99c260e9074cf65a4d8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9C=A4=ED=9D=AC=EC=A4=80?= Date: Sun, 22 Dec 2024 19:42:14 +0900 Subject: [PATCH] =?UTF-8?q?=20Feat=20:=20=EC=9E=AC=EB=A3=8C=EA=B8=B0?= =?UTF-8?q?=EB=B0=98=20=EB=A0=88=EC=8B=9C=ED=94=BC=20=EC=B6=94=EC=B2=9C=20?= =?UTF-8?q?1.=20=EB=A0=88=EC=8B=9C=ED=94=BC=20=EC=B6=94=EC=B2=9C=20?= =?UTF-8?q?=ED=8E=98=EC=9D=B4=EC=A7=80=EB=A5=BC=20=EA=B5=AC=EC=84=B1?= =?UTF-8?q?=ED=95=98=EC=98=80=EC=8A=B5=EB=8B=88=EB=8B=A4.=202.=20Header=20?= =?UTF-8?q?=EC=97=90=20=EB=A0=88=EC=8B=9C=ED=94=BC=EB=A1=9C=20=EA=B0=80?= =?UTF-8?q?=EB=8A=94=20=EB=A7=81=ED=81=AC=20=EB=A7=8C=EB=93=A4=EC=96=B4?= =?UTF-8?q?=EB=86=A8=EB=8A=94=EB=8D=B0=20=EC=B6=94=ED=9B=84=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=20=ED=95=B4=EB=8F=84=EB=90=A9=EB=8B=88=EB=8B=A4.=203.?= =?UTF-8?q?=20userPk=20=EA=B8=B0=EB=B0=98=EC=9C=BC=EB=A1=9C=20=EB=A0=88?= =?UTF-8?q?=EC=8B=9C=ED=94=BC=20=EC=B6=94=EC=B2=9C=EC=9D=84=20=ED=95=98?= =?UTF-8?q?=EB=8A=94=EB=8D=B0=20=ED=94=84=EB=A1=A0=ED=8A=B8=EC=97=90?= =?UTF-8?q?=EC=84=9C=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=EC=8B=9C=20pk=20?= =?UTF-8?q?=EB=A5=BC=20=EC=96=B4=EB=94=94=EC=97=90=20=EB=8B=B4=EC=9D=84?= =?UTF-8?q?=EC=A7=80=20=EC=A0=95=ED=95=B4=EC=A7=80=EB=A9=B4=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=20=ED=95=98=EA=B2=A0=EC=8A=B5=EB=8B=88=EB=8B=A4.=20Re?= =?UTF-8?q?lated=20to=20:=20#09?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/App.jsx | 2 + src/components/main/Header.jsx | 1 + src/pages/recipe/Recipe.jsx | 20 +++-- src/pages/recipe/RecipeRecommend.css | 112 +++++++++++++++++++++++++++ src/pages/recipe/RecipeRecommend.jsx | 94 ++++++++++++++++++++++ src/sources/api/recipeAPI.jsx | 6 ++ 6 files changed, 227 insertions(+), 8 deletions(-) create mode 100644 src/pages/recipe/RecipeRecommend.css create mode 100644 src/pages/recipe/RecipeRecommend.jsx diff --git a/src/App.jsx b/src/App.jsx index 7007531..2402726 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -4,6 +4,7 @@ import Ingredient from "./pages/ingredient/Ingregdient" import Recipe from "./pages/recipe/Recipe" import {AddRecipe} from "./pages/recipe/AddRecipe" import Join from "./pages/user/Join" +import RecipeRecommend from "./pages/recipe/RecipeRecommend" function App() { return ( @@ -17,6 +18,7 @@ function App() { } /> } /> + } /> } /> diff --git a/src/components/main/Header.jsx b/src/components/main/Header.jsx index 3099bd4..57931e6 100644 --- a/src/components/main/Header.jsx +++ b/src/components/main/Header.jsx @@ -13,6 +13,7 @@ function Header() { diff --git a/src/pages/recipe/Recipe.jsx b/src/pages/recipe/Recipe.jsx index b965de2..81cc27c 100644 --- a/src/pages/recipe/Recipe.jsx +++ b/src/pages/recipe/Recipe.jsx @@ -1,18 +1,20 @@ import { useEffect, useState,useCallback } from "react" import { getRecipeList } from "../../sources/api/recipeAPI.jsx"; -import {Route, useNavigate} from "react-router-dom"; +import {Link, Route, useNavigate} from "react-router-dom"; import { AddRecipe} from "./addRecipe.jsx"; function Recipe() { const [recipeList, setRecipeList] = useState([]) const navigate = useNavigate(); + const userPk = localStorage.getItem('userPk'); + //userPk 를 어떻게 처리할지에 따라 바꿀듯합니다. useEffect(() => { const fetchRecipes = async () => { try { const data = await getRecipeList(); - setRecipeList(data); // 상태 업데이트 - console.log(recipeList); // 가져온 데이터 확인 + setRecipeList(data); + console.log(recipeList); } catch (err) { console.error(err); } @@ -28,11 +30,13 @@ function Recipe() { return ( <>

레시피 페이지

-
    -
  • - -
  • -
+
+ + + 맞춤 레시피 보기 + +
) } diff --git a/src/pages/recipe/RecipeRecommend.css b/src/pages/recipe/RecipeRecommend.css new file mode 100644 index 0000000..b52c1ca --- /dev/null +++ b/src/pages/recipe/RecipeRecommend.css @@ -0,0 +1,112 @@ +.recipe-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); + gap: 2rem; + padding: 2rem; +} + +.recipe-card { + background: white; + border-radius: 12px; + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); + overflow: hidden; + transition: transform 0.2s ease; +} + +.recipe-card:hover { + transform: translateY(-5px); +} + +.recipe-image { + width: 100%; + height: 200px; + object-fit: cover; +} + +.recipe-content { + padding: 1.5rem; +} + +.recipe-title { + font-size: 1.25rem; + font-weight: 600; + color: #333; + margin-bottom: 0.5rem; +} + +.recipe-info { + display: flex; + flex-direction: column; + gap: 0.5rem; +} + +.match-rate { + color: #3FA2F6; + font-weight: 600; +} + +.expiry-warning { + background: #ffebeb; + color: #ff4d4d; + padding: 0.5rem; + border-radius: 6px; + font-size: 0.9rem; +} + +.view-recipe-btn { + width: 100%; + background: #96C9F4; + color: white; + padding: 0.75rem; + border: none; + border-radius: 6px; + margin-top: 1rem; + cursor: pointer; + /* transition: background 0.2s ease; */ +} + +.view-recipe-btn:hover { + background: #3FA2F6; +} + +/* RecipeRecommend.css에 추가 */ +.ingredients-section { + background: #f8f9fa; + padding: 1rem; + border-radius: 8px; +} + +.ingredients-grid { + display: flex; + flex-wrap: wrap; + gap: 0.5rem; +} + +.ingredient-tag { + display: inline-flex; + align-items: center; + padding: 0.25rem 0.75rem; + border-radius: 20px; + font-size: 0.875rem; + background: white; + border: 1px solid #e0e0e0; +} + +.ingredient-tag.necessary { + border-color: #3FA2F6; + color: #3FA2F6; +} + +.ingredient-tag.optional { + border-color: #96C9F4; + color: #96C9F4; +} + +.necessary-badge { + background: #3FA2F6; + color: white; + font-size: 0.75rem; + padding: 0.125rem 0.375rem; + border-radius: 10px; + margin-left: 0.5rem; +} \ No newline at end of file diff --git a/src/pages/recipe/RecipeRecommend.jsx b/src/pages/recipe/RecipeRecommend.jsx new file mode 100644 index 0000000..95aa74b --- /dev/null +++ b/src/pages/recipe/RecipeRecommend.jsx @@ -0,0 +1,94 @@ +// RecipeRecommend.jsx +import { useEffect, useState } from "react"; +import { getRecommendedRecipes } from "../../sources/api/recipeAPI"; +import { API_URL_HOST } from "../../sources/api/recipeAPI"; +import defaultImage from "../../assets/image/default.gif"; +import './RecipeRecommend.css'; +import { useParams } from "react-router-dom"; + +function RecipeRecommend() { + const [recommendedRecipes, setRecommendedRecipes] = useState([]); + const {userPk} = useParams(); + + useEffect(() => { + const fetchRecommendedRecipes = async () => { + try { + const data = await getRecommendedRecipes(userPk); + setRecommendedRecipes(data); + } catch (err) { + console.error("레시피 추천 로드 실패:", err); + } + }; + + fetchRecommendedRecipes(); + }, [userPk]); + + return ( +
+

+ 맞춤 레시피 추천 +

+ +
+ {recommendedRecipes.map(recipe => ( +
+ 0 + ? `${API_URL_HOST}/${recipe.mainImages[0].filePath}` + : defaultImage} + alt={recipe.recipeName} + className="recipe-image" + /> + +
+

{recipe.recipeName}

+

{recipe.recipeContent}

+ +
+

필요한 재료

+
+ {recipe.ingredients.map((ingredient, index) => ( + + {ingredient.ingredientName} + {ingredient.necessary && + 필수 + } + + ))} +
+
+ +
+
+ 조리 시간 + {recipe.recipeCookingTime}분 +
+ +
+ 재료 매칭률 + {recipe.matchRate.toFixed(1)}% +
+ + {recipe.remainExpirationDays <= 3 && ( +
+ ⚠️ {recipe.urgentIngredientName} + {recipe.remainExpirationDays}일 남음 +
+ )} +
+ + +
+
+ ))} +
+
+ ); +} + +export default RecipeRecommend; \ No newline at end of file diff --git a/src/sources/api/recipeAPI.jsx b/src/sources/api/recipeAPI.jsx index 21404ce..a0dbd93 100644 --- a/src/sources/api/recipeAPI.jsx +++ b/src/sources/api/recipeAPI.jsx @@ -15,4 +15,10 @@ export const createRecipe = async (recipe) => { const url = `${prefix}`; const res = await axios.post(url, recipe); return res.data; +} + +export const getRecommendedRecipes = async (userPk) => { + const url = `${prefix}/recommend?userPk=${userPk}`; + const res = await axios.get(url); + return res.data; } \ No newline at end of file