diff --git a/webapp/frontend/package-lock.json b/webapp/frontend/package-lock.json index 70b846031fcfe76d89de09b187c6d51907c1f17b..fb6ddb458226517a1211c64fc5bc084d008de524 100644 --- a/webapp/frontend/package-lock.json +++ b/webapp/frontend/package-lock.json @@ -11,7 +11,9 @@ "@testing-library/jest-dom": "^5.17.0", "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", + "buffer": "^6.0.3", "http-proxy-middleware": "^3.0.3", + "jwt-decode": "^4.0.0", "react": "^18.3.1", "react-dom": "^18.3.1", "react-router-dom": "^6.28.0", @@ -5423,6 +5425,26 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "license": "MIT" }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, "node_modules/batch": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", @@ -5617,6 +5639,30 @@ "node-int64": "^0.4.0" } }, + "node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, "node_modules/buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", @@ -9378,6 +9424,26 @@ "node": ">=4" } }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -11255,6 +11321,15 @@ "node": ">=4.0" } }, + "node_modules/jwt-decode": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-4.0.0.tgz", + "integrity": "sha512-+KJGIyHgkGuIq3IEBNftfhW/LfWhXUIY6OmyVWjliu5KH1y0fw7VQ8YndE2O4qZdMSd9SqbnC8GOcZEy0Om7sA==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", diff --git a/webapp/frontend/package.json b/webapp/frontend/package.json index 3edbe53ba488a266602ab782127bd10fa77d64f8..441a25a8dffa03c2d44a0e3439407a17dea76360 100644 --- a/webapp/frontend/package.json +++ b/webapp/frontend/package.json @@ -6,7 +6,9 @@ "@testing-library/jest-dom": "^5.17.0", "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", + "buffer": "^6.0.3", "http-proxy-middleware": "^3.0.3", + "jwt-decode": "^4.0.0", "react": "^18.3.1", "react-dom": "^18.3.1", "react-router-dom": "^6.28.0", diff --git a/webapp/frontend/src/assets/listitem.css b/webapp/frontend/src/assets/listitem.css index fadf239b9dc8059be38bf14cd6cfbda0953a4e2f..b4a86e407ae7429d45531bee013d5f3b90464d7b 100644 --- a/webapp/frontend/src/assets/listitem.css +++ b/webapp/frontend/src/assets/listitem.css @@ -9,6 +9,7 @@ height: 100%; margin: 0px; padding: 0px; + color: black; } .modalContents { @@ -22,6 +23,7 @@ height: 500px; background-color: var(--color-white); border-radius: 25px; + color: black; } .crewName, .eventName { diff --git a/webapp/frontend/src/components/List.js b/webapp/frontend/src/components/List.js index 5c6293f014c5255679e988e353350c31a5f5af8e..9dc054e2b37560670b16992c39fa0424dedd04a5 100644 --- a/webapp/frontend/src/components/List.js +++ b/webapp/frontend/src/components/List.js @@ -45,6 +45,7 @@ function List() { }; const fetchEvents = async (regionID, sportTypeID, eventDate, page) => { + console.log(regionID, sportTypeID, eventDate, page); try { const response = await fetch(`${apiUrl}/api/events?page=${page}®ionID=${regionID}&sportTypeID=${sportTypeID}&eventDate=${eventDate}`, { method: 'GET', diff --git a/webapp/frontend/src/components/ListItem.js b/webapp/frontend/src/components/ListItem.js index d465a1a579ae343016996c21406620d82f170d3e..b6f1132defbdd77f16e6c67cfb6ff3395474287a 100644 --- a/webapp/frontend/src/components/ListItem.js +++ b/webapp/frontend/src/components/ListItem.js @@ -18,7 +18,7 @@ function ListItem( {crew, event, updateApplicants} ){ const [crewRegionNames, setCrewRegionNames] = useState({}); //상위지역, 하위지역 이름 저장 const [eventRegionNames, setEventRegionNames] = useState({}); - const sportTypes = [ + /*const sportTypes = [ { sportTypeId: 1, sportName: "러닝", @@ -41,7 +41,9 @@ function ListItem( {crew, event, updateApplicants} ){ difficulty: ["상관 없음", "V1", "V2", "V3", "V4", "V5", "V6", "V7", "V8+"] } } - ]; + ];*/ + + const sportTypes = ["러닝", "클라이밍", "헬스"]; const handleCrewRegisterModal = () => { @@ -82,26 +84,30 @@ function ListItem( {crew, event, updateApplicants} ){ if (!response.ok) { const errorData = await response.json(); console.log(errorData); + console.log(crewID); alert(errorData); + } else{ + const crewData = await response.json(); + setCrewCapacity(crewData.currentMemberCount); } - const crewData = await response.json(); - setCrewCapacity(crewData.currentMemberCount); - } catch (error) { - console.error(error); - alert(error); - } - try { - const response = await fetch(`${apiUrl}/api/crews/${crewID}/members`, { + const postResponse = await fetch(`${apiUrl}/api/crews/${crewID}/members`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'x-auth-token': token, }, }); - console.log('크루 가입 성공:', response); - updateApplicants(crew.id, crewCapacity); - alert('크루 가입이 완료되었습니다.'); + + if (postResponse.ok) { + console.log('크루 가입 성공:', postResponse); + updateApplicants(crewID, crewCapacity); + console.log(crew.id, crew.capacity); + alert('크루 가입이 완료되었습니다.'); + } else { + const postErrorData = await postResponse.json(); + alert('가입 실패:', postErrorData); + } } catch (error) { console.error(error); alert(error); @@ -122,25 +128,27 @@ function ListItem( {crew, event, updateApplicants} ){ const errorData = await response.json(); console.log(errorData); alert(errorData); + } else { + const eventData = await response.json(); + setEventCapacity(eventData.currentMemberCount); } - const eventData = await response.json(); - setEventCapacity(eventData.currentMemberCount); - } catch (error) { - console.error(error); - alert(error); - } - try { - const response = await fetch(`${apiUrl}/events/${eventID}/members`, { + const postResponse = await fetch(`${apiUrl}/events/${eventID}/members`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'x-auth-token': token, }, }); - console.log('모임 참여 성공:', response); - updateApplicants(event.id, eventCapacity); - alert('모임 참여 신청이 완료되었습니다.'); + + if (postResponse.ok) { + console.log('모임 가입 성공:', postResponse); + updateApplicants(eventID, crewCapacity); + alert('모임 가입이 완료되었습니다.'); + } else { + const postErrorData = await postResponse.json(); + alert('가입 실패:', postErrorData); + } } catch (error) { console.error(error); alert(error); @@ -238,7 +246,7 @@ function ListItem( {crew, event, updateApplicants} ){ </div> </div> - <button className="createRegisterButton" onClick={()=>handleCrewRegister(crew.id)}>가입하기</button> + <button className="createRegisterButton" onClick={()=>handleCrewRegister(crew.crewID)}>가입하기</button> </div> </div> diff --git a/webapp/frontend/src/components/Listing.js b/webapp/frontend/src/components/Listing.js index d89bbb902b174e8433d68e18dfbe8dfce1f32284..90088d7e3b6f09290094cb320b8d328c62002260 100644 --- a/webapp/frontend/src/components/Listing.js +++ b/webapp/frontend/src/components/Listing.js @@ -108,6 +108,7 @@ function Listing(){ const handleSubmit = (e) => { e.preventDefault(); const formId = e.target.id; + console.log(crewFormData); if (formId === 'crewForm') { @@ -134,7 +135,6 @@ function Listing(){ } onCreateEvent(eventFormData); - alert("모임 생성이 완료되었습니다."); setEventFormData({ name: "", region: "", type: "", date: "", fee: 0, capacity: 0, condition: "" }); } } // 크루 or 모임 생성 @@ -267,7 +267,7 @@ function Listing(){ <div className="eventTypeInput"> <label htmlFor="eventType">종목</label> - <select name="type" id="crewType" value={crewFormData.typeId} onChange={handleCrewChange}> + <select name="type" id="eventType" value={eventFormData.typeId} onChange={handleEventChange}> <option className='crewTypeOption' value="">선택 ▼</option> <option value="1">러닝</option> <option value="3">클라이밍</option> diff --git a/webapp/frontend/src/components/Login.js b/webapp/frontend/src/components/Login.js index c774115d69f7fd4f81123961280ee62453acd076..7e1a6bca84438356d4a6bd172c371e3fa7b429b7 100644 --- a/webapp/frontend/src/components/Login.js +++ b/webapp/frontend/src/components/Login.js @@ -4,13 +4,14 @@ import '../assets/login.css'; import { useState, useContext } from 'react'; import { AuthContext } from '../contexts/AuthContext.js'; import { useNavigate, Link } from 'react-router-dom'; +import { jwtDecode } from 'jwt-decode'; const apiUrl = process.env.REACT_APP_API_URL; function Login() { - const { login } = useContext(AuthContext); + const { login, isLogIn } = useContext(AuthContext); const navigate = useNavigate(); const [loginData, setLoginData] = useState({ email: '', password: '' }); @@ -37,11 +38,17 @@ function Login() { const result = await response.json(); if (response.ok) { - localStorage.setItem('authToken', result.auth_token); - login(); - alert("로그인에 성공하였습니다."); - navigate('/'); - } else { + if (result.auth_token) { + localStorage.setItem('authToken', result.auth_token); + const decoded = jwtDecode(result.auth_token); + + if (decoded?.userID) { + localStorage.setItem('userID', decoded.userID); + login(); + console.log(result.auth_token); + alert("로그인에 성공하였습니다."); + navigate('/'); + }}} else { setError(result.error); alert(error); } diff --git a/webapp/frontend/src/components/Mypage.js b/webapp/frontend/src/components/Mypage.js index c3a9f3c1336ddac295f1d3b9b870e7e58ac571f0..c8ae6f0c19ba0d0a0f5ed646ab5ab594a5a32153 100644 --- a/webapp/frontend/src/components/Mypage.js +++ b/webapp/frontend/src/components/Mypage.js @@ -57,7 +57,7 @@ function Mypage() { birthDate: formState.birthDate, experience: formState.experience, introduction: formState.introduction, - sportTypeId: formState.sportTypeId, + sportTypeID: formState.sportTypeID, }, }; @@ -117,7 +117,7 @@ function Mypage() { regionID: 0, birthDate: '', job: '', - sportTypeId: 0, + sportTypeID: 0, experience: '', introduction: '', }); @@ -155,6 +155,8 @@ function Mypage() { .then(response => response.json()) .then(data => setUser(data)) .catch(error => console.error('Error fetching courses:', error)); + + console.log(userID); }, []); useEffect(() => { @@ -164,11 +166,12 @@ function Mypage() { regionID: user.profile?.regionID || '', birthDate: user.profile?.birthDate || '', job: user.profile?.job || '', - sportTypeId: user.profile?.sportTypeId || '', + sportTypeID: user.profile?.sportTypeID || '', experience: user.profile?.experience || '', introduction: user.profile?.introduction || '', }); } + console.log(user); }, [user]); //유저가 가입한 크루 데이터 @@ -208,6 +211,7 @@ function Mypage() { }) .then((response) => response.json()) .then(async (data) => { + if (data && Array.isArray(data.events)) { // data.events가 배열인지 확인 const eventsWithRegion = await Promise.all(data.events.map(async (event) => { const region = await fetchRegion(event.regionID); // 지역 데이터 가져오기 return { @@ -215,9 +219,10 @@ function Mypage() { regionName: region.regionName, // 지역 이름을 추가 }; })); - + setCrews(eventsWithRegion); setTotalEventPages(data.totalPages); // 총 페이지 수 설정 + } }) .catch((error) => console.error("모임 데이터를 가져오는 데 실패했습니다:", error)); } @@ -307,7 +312,7 @@ function Mypage() { <span>{user.name}</span> <input type="date" name="birthDate" value={formState.birthDate} onChange={handleInputChange} placeholder="Birth Date"/> <input type="text" name="job" value={formState.job} onChange={handleInputChange} placeholder="Job"/> - <select name="sportTypeId" value={formState.sportTypeId} onChange={handleInputChange}> + <select name="sportTypeID" value={formState.sportTypeID} onChange={handleInputChange}> <option value="">Select Sport Type</option> {sportType.map((type, index) => ( <option key={index} value={index + 1}> @@ -325,9 +330,9 @@ function Mypage() { <> <div className='userProfileDetails'> <span>{user.name}</span> {/* 사용자 이름 */} - <span>{user.profile?.birthDate ? user.profile.birthDate : "Birth date not available"}</span> {/* 사용자 생년월일 */} + <span>{user.profile?.birthDate ? new Date(user.profile.birthDate).toLocaleDateString('ko-KR') : ''}</span> {/* 사용자 생년월일 */} <span>{user.profile?.job ? user.profile.job : "Job date not available"}</span> {/* 사용자 직업 */} - <span>{user.profile?.sportTypeId ? sportType[user.profile.sportTypeId - 1] : "sportTypeId date not available"}</span> {/* 운동 종목 */} + <span>{user.profile?.sportTypeID ? sportType[user.profile.sportTypeID - 1] : "sportTypeId date not available"}</span> {/* 운동 종목 */} <span>{user.profile?.experience ? user.profile.experience : "experience date not available"}</span> {/* 사용자 설정 내용 */} <span>{user.profile?.introduction ? user.profile.introduction : "introduction date not available"}</span> {/* 사용자 이름 */} <span> </span> @@ -352,7 +357,7 @@ function Mypage() { <div className="crew" key={crew.crewID}> <h4>{crew.name}</h4> <p>{crew.regionName}</p> - <p>{sportType[crew.sportTypeId - 1]}</p> + <p>{sportType[crew.sportTypeID - 1]}</p> <button className="enter" onClick={() => handleNavClick(crew)}>입장하기</button> </div> ))} @@ -379,7 +384,7 @@ function Mypage() { <div className="meet" key={event.eventID}> <h4>{event.name}</h4> <p>{event.regionName}</p> - <p>{sportType[event.sportTypeId - 1]}</p> + <p>{sportType[event.sportTypeID - 1]}</p> <button className="viewDetail" onClick={() => openModal(event)}>자세히보기</button> </div> ))} @@ -411,7 +416,7 @@ function Mypage() { </div> <div className='meetsDetail'> <p className='item'>{selectedEvent.regionID}</p> - <p className='item'>{sportType[selectedEvent.sportTypeId - 1]}</p> + <p className='item'>{sportType[selectedEvent.sportTypeID - 1]}</p> <p className='item'>{selectedEvent.eventDate}</p> <p className='item'>{selectedEvent.currentMemberCount}/{selectedEvent.capacity}</p> <p className='item'>{selectedEvent.feeCondition ? selectedEvent.feeCondition : "설정된 비용/조건이 없습니다."}</p> @@ -425,7 +430,7 @@ function Mypage() { </div> <div className='meetsDetail'> <p className='item'>{selectedEvent.regionID}</p> - <p className='item'>{sportType[selectedEvent.sportTypeId - 1]}</p> + <p className='item'>{sportType[selectedEvent.sportTypeID - 1]}</p> <input className="item" type="date" value={selectedEvent.eventDate} onChange={(e) => setSelectedEvent({ ...selectedEvent, eventDate: e.target.value })}/> <input className="item" type="number" value={selectedEvent.capacity} onChange={(e) => setSelectedEvent({ ...selectedEvent, capacity: parseInt(e.target.value) || 0})}/> <input className="item" type="text" value={selectedEvent.feeCondition} onChange={(e) => setSelectedEvent({ ...selectedEvent, feeCondition: e.target.value })}/> diff --git a/webapp/frontend/src/components/NavBar.js b/webapp/frontend/src/components/NavBar.js index a1955c851c27dcc9d19da95eb826a3b719bfa3ee..0ecc712786e11d627b0fbb24cbb8afdb85bc756c 100644 --- a/webapp/frontend/src/components/NavBar.js +++ b/webapp/frontend/src/components/NavBar.js @@ -13,6 +13,9 @@ function NavBar() { const navigate = useNavigate(); const { isLogIn, logout } = useContext(AuthContext); + console.log(isLogIn); + + const handleLogOut = () => { logout(); localStorage.removeItem('authToken'); @@ -22,7 +25,12 @@ function NavBar() { } const handlenotUserAccess = () => { - alert('로그인 후 이용가능합니다.'); + const token = localStorage.getItem('authToken'); + if (!token) { + alert('로그인 후 이용가능합니다.'); + } else { + navigate('/Mypage'); + } } diff --git a/webapp/frontend/src/components/Search.js b/webapp/frontend/src/components/Search.js index 431f9237851e35d7705c774b2e7b97020ee560e2..227a6cace8bc56a2effd7b6ade9d3ebf805b5952 100644 --- a/webapp/frontend/src/components/Search.js +++ b/webapp/frontend/src/components/Search.js @@ -30,7 +30,7 @@ function Search() { if (sportType) queryParams.set('sportTypeID', sportType); if (region) queryParams.set('regionID', region); - if (date) queryParams.set('eventDate', date); + if (date && date.trim() !== "") queryParams.set('eventDate', date); queryParams.set('type', 'event'); // 이벤트 검색을 위한 type 설정 navigate(`/list?${queryParams.toString()}`); diff --git a/webapp/frontend/src/contexts/AuthContext.js b/webapp/frontend/src/contexts/AuthContext.js index 3aeea7e7c16a1b4e709975e3e6b7c51ed526fece..d19dbf5e44e80fab3ab7cc770a497ce4e6b46f99 100644 --- a/webapp/frontend/src/contexts/AuthContext.js +++ b/webapp/frontend/src/contexts/AuthContext.js @@ -1,10 +1,17 @@ -import React, { createContext, useState } from 'react'; +import React, { createContext, useState, useEffect } from 'react'; export const AuthContext = createContext(); export const AuthProvider = ({ children }) => { const [isLogIn, setIsLogIn] = useState(false); + /*useEffect(() => { + const token = localStorage.getItem('authToken'); + if (token) { + setIsLogIn(true); + } + }, []);*/ + const login = () => setIsLogIn(true); const logout = () => setIsLogIn(false); diff --git a/webapp/frontend/src/contexts/CrewContext.js b/webapp/frontend/src/contexts/CrewContext.js index 0f0fd3dbaf440970c6c70dd049ce2b6b9e432476..5f64f27b4360583eb7541058fbf4328adf4152c9 100644 --- a/webapp/frontend/src/contexts/CrewContext.js +++ b/webapp/frontend/src/contexts/CrewContext.js @@ -64,7 +64,8 @@ export const CrewProvider = ({ children }) => { }; const onCreateEvent = (newEvent) => { - fetch('/api/events', { + console.log("인증토큰", token); + fetch(`${apiUrl}/api/events`, { method: 'POST', headers: { 'Content-Type': 'application/json', @@ -83,6 +84,7 @@ export const CrewProvider = ({ children }) => { .then(response => response.json()) .then(data => { setEvents(prevEvent => [...prevEvent, data]); + alert("모임 생성이 완료되었습니다."); }) .catch(error => console.error(error)); };