diff --git a/src/assets/css/main/header.module.css b/src/assets/css/main/header.module.css index 7115f26..1e2a86f 100644 --- a/src/assets/css/main/header.module.css +++ b/src/assets/css/main/header.module.css @@ -18,6 +18,7 @@ font-weight: bolder; margin: 0; } + .logoReci { color: black; /* Reci의 기본 색상 */ } @@ -42,3 +43,45 @@ .nav a:hover { color: rgb(61, 60, 60); } + +.alertContainer { + position: relative; /* 알림 버튼 컨테이너 기준으로 위치 설정 */ + display: inline-block; + cursor: pointer; +} + +.alerts { + position: absolute; /* 부모 컨테이너를 기준으로 절대 위치 */ + top: 100%; /* 컨테이너 바로 아래에 표시 */ + left: -100%; /* 부모 컨테이너 기준으로 가장 왼쪽에 위치 */ + background-color: white; + border: 1px solid #ccc; + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); + padding: 10px; + width: 250px; + z-index: 1000; /* 다른 요소 위에 표시 */ + max-height: 200px; /* 최대 높이 설정 */ + overflow-y: auto; /* 내용이 넘칠 경우 스크롤 */ + transform: translateX(-150px); /* 알림 위치를 더 왼쪽으로 이동 */ +} + +.alerts ul { + list-style: none; + padding: 0; + margin: 0; +} + +.alerts li { + padding: 5px 0; + border-bottom: 1px solid #eee; +} + +.alerts li:last-child { + border-bottom: none; +} + +.alerts p { + margin: 0; + text-align: center; + color: #666; +} diff --git a/src/components/main/Header.jsx b/src/components/main/Header.jsx index ec91f97..09ebbd8 100644 --- a/src/components/main/Header.jsx +++ b/src/components/main/Header.jsx @@ -1,7 +1,50 @@ -import React from "react"; -import style from '../../assets/css/main/header.module.css'; +import React, { useEffect, useState } from "react"; +import axios from "axios"; +import style from "../../assets/css/main/header.module.css"; function Header() { + const [userPk, setUserPk] = useState(null); // JWT에서 추출한 userPk + const [alerts, setAlerts] = useState([]); // 알림 데이터 + const [showAlerts, setShowAlerts] = useState(false); // 알림 표시 여부 + + // JWT에서 userPk 추출하는 함수 + function getUserPkFromToken(token) { + try { + const payload = token.split(".")[1]; // 토큰의 payload 부분 + const decoded = JSON.parse(atob(payload)); // Base64 디코딩 후 JSON 파싱 + return decoded.username; // username에 담긴 userPk 반환 + } catch (error) { + console.error("Failed to decode token:", error); + return null; + } + } + + // API 요청 함수 + async function fetchAlerts(userPk) { + try { + const response = await axios({ + method: "post", + url: "http://localhost:8080/ingredient/alert", + data: { userPk }, + }); + setAlerts(response.data); + } catch (error) { + console.error("Failed to fetch alerts:", error); + } + } + + // 컴포넌트가 마운트될 때 실행 + useEffect(() => { + const token = localStorage.getItem("token"); // 로컬스토리지에서 JWT 가져오기 + if (token) { + const extractedUserPk = getUserPkFromToken(token); // userPk 추출 + setUserPk(extractedUserPk); // 상태 업데이트 + if (extractedUserPk) { + fetchAlerts(extractedUserPk); // API 호출 + } + } + }, []); + return (
@@ -14,8 +57,37 @@ function Header() { 내 냉장고 레시피 - 즐겨찾기 - 로그인 + {userPk ? ( + <> + 로그아웃 +
setShowAlerts(true)} // 마우스를 올렸을 때 + onMouseLeave={() => setShowAlerts(false)} // 마우스가 벗어났을 때 + > + 알림 ({alerts.length}) + {showAlerts && ( +
+ {alerts.length > 0 ? ( +
    + {alerts.map((alert) => ( +
  • + {alert.ingredientManagement.ingredientName} + ({alert.ingredientManagement.ingredientStorage.ingredientStorage}) + - {alert.remainDate}일 남음 +
  • + ))} +
+ ) : ( +

알림이 없습니다.

+ )} +
+ )} +
+ + ) : ( + 로그인 + )}
); diff --git a/src/pages/recipe/Recipe.jsx b/src/pages/recipe/Recipe.jsx index b3298b6..96b9f3a 100644 --- a/src/pages/recipe/Recipe.jsx +++ b/src/pages/recipe/Recipe.jsx @@ -2,7 +2,6 @@ import { useEffect, useState,useCallback } from "react" import { getRecipeList } from "../../sources/api/recipeAPI.jsx"; import {Link, Route, useNavigate} from "react-router-dom"; import { AddRecipe} from "./addRecipe.jsx"; -import {Route, useNavigate} from "react-router-dom"; import RecipeItem from "../../components/recipe/RecipeItem.jsx" import "../../assets/css/recipe/recipe.css" diff --git a/src/sources/api/IngredientAPI.js b/src/sources/api/IngredientAPI.js index af7adb8..5fd72af 100644 --- a/src/sources/api/IngredientAPI.js +++ b/src/sources/api/IngredientAPI.js @@ -2,6 +2,18 @@ import axios from "axios"; const BASE_URL = "http://localhost:8080/ingredient"; +function getUserPkFromToken(token) { + try { + const payload = token.split(".")[1]; // 토큰의 payload 부분 + const decoded = JSON.parse(atob(payload)); // Base64 디코딩 후 JSON 파싱 + return decoded.username; // username에 담긴 userPk 반환 + } catch (error) { + console.error("Failed to decode token:", error); + return null; + } +} + + export async function getAllIngredients() { try { const response = await axios.get(`${BASE_URL}`); // 백엔드에 전체 재료 목록을 가져오는 API 필요 @@ -13,12 +25,15 @@ export async function getAllIngredients() { } export async function addIngredientToMyRefrigerator(ingredientData) { + const token = localStorage.getItem("token") + const extractedUserPk = getUserPkFromToken(token); + try { const response = await axios({ url: `${BASE_URL}`, method: "post", params: { - userPk: 1, // 실제 로그인된 사용자 PK로 교체 필요 + userPk: extractedUserPk, // 실제 로그인된 사용자 PK로 교체 필요 ingredientManagementPk: ingredientData.ingredientManagementPk, }, data: { @@ -35,12 +50,15 @@ export async function addIngredientToMyRefrigerator(ingredientData) { } export async function createIngredient(ingredientData) { + const token = localStorage.getItem("token") + const extractedUserPk = getUserPkFromToken(token); + try { const response = await axios({ url: `${BASE_URL}`, method: "post", params: { - userPk: 1, + userPk: extractedUserPk, ingredientManagementPk: ingredientData.ingredientManagementPk, }, data: { @@ -83,12 +101,14 @@ export async function deleteIngredient( } export async function getUsersIngredient() { + const token = localStorage.getItem("token") + const extractedUserPk = getUserPkFromToken(token); try { const response = await axios({ url: `${BASE_URL}`, method: "get", params: { - userPk: 1, + userPk: extractedUserPk, }, }); return response.data; // 데이터를 반환 @@ -99,12 +119,14 @@ export async function getUsersIngredient() { } export function updateIngredientBookmark(userPk, isBookmarked, ingredient) { + const token = localStorage.getItem("token") + const extractedUserPk = getUserPkFromToken(token); if (!isBookmarked) { axios({ url: `${BASE_URL}/bookmark/regist`, method: "post", data: { - userPk: userPk, + userPk: extractedUserPk, ingredientMyRefrigeratorPk: ingredient.ingredientMyRefrigeratorPk, }, }) @@ -120,7 +142,7 @@ export function updateIngredientBookmark(userPk, isBookmarked, ingredient) { url: `${BASE_URL}/bookmark/delete`, method: "delete", data: { - userPk: userPk, + userPk: extractedUserPk, ingredientBookmarkPk: ingredient.ingredientBookmarkPk, }, })