diff --git a/src/APP/components/Footer/Footer.footer.jsx b/src/APP/components/Footer/Footer.footer.jsx index c3208a71..8f2d0698 100644 --- a/src/APP/components/Footer/Footer.footer.jsx +++ b/src/APP/components/Footer/Footer.footer.jsx @@ -1,23 +1,62 @@ -import React from 'react' -import * as itemS from "./Styled/Footer.footer.styles" +import React from 'react'; +import * as itemS from './Styled/Footer.footer.styles'; export default function footer() { + const currentYear = new Date().getFullYear(); return ( -
- KOALA(한국항공대학교 알고리즘 학회) +
+ KOALA + (한국항공대학교 알고리즘 학회)
- 문의처 : kau-koala@naver.com |업데이트 노트 링크 - Copyrightⓒ2024.KOALA. All rights reserved. + + 문의처 : kau-koala@naver.com | + + 업데이트 노트{' '} + 링크 + + + + Copyrightⓒ{currentYear}.KOALA. All rights reserved. + - - + + + + + + - ) + ); } diff --git a/src/APP/components/Header/Header.header.jsx b/src/APP/components/Header/Header.header.jsx index 3e584077..c778d0fb 100644 --- a/src/APP/components/Header/Header.header.jsx +++ b/src/APP/components/Header/Header.header.jsx @@ -1,25 +1,25 @@ -import React, { useEffect, useRef, useState } from 'react'; +import React, { useEffect, useRef, useState } from "react"; import * as itemS from "./Styled/Header.header.styles"; -import request from '../../Api/request'; -import ProfileModal from './Header.profile.modal'; +import request from "../../Api/request"; +import ProfileModal from "./Header.profile.modal"; export default function Header({ dark }) { const [isLoggedIn, setIsLoggedIn] = useState(false); - const [userName, setUserName] = useState(''); - const [profileUrl, setProfileUrl] = useState(''); + const [userName, setUserName] = useState(""); + const [profileUrl, setProfileUrl] = useState(""); const [showProfileModal, setShowProfileModal] = useState(false); - const [activeMenu, setActiveMenu] = useState(''); + const [activeMenu, setActiveMenu] = useState(""); // Refs for detecting clicks outside - const modalRef = useRef(null); + const modalRef = useRef(null); const studyMenuRef = useRef(null); const codingMenuRef = useRef(null); // Fetch login status on component mount const checkLoginStatus = async () => { try { - const response = await request.get('/member/info'); - console.log("로그인 멤버 정보 조회", response); + const response = await request.get("/member/info"); + // console.log("로그인 멤버 정보 조회", response); if (response["isSuccess"]) { setUserName(response.result.name); setProfileUrl(response.result.profileUrl); @@ -30,8 +30,7 @@ export default function Header({ dark }) { } }; useEffect(() => { - - const accessToken = localStorage.getItem('accessToken'); + const accessToken = localStorage.getItem("accessToken"); if (accessToken) { checkLoginStatus(); } @@ -39,47 +38,50 @@ export default function Header({ dark }) { // Toggle Profile Modal const toggleProfileModal = () => { - setShowProfileModal(prev => !prev); - setActiveMenu(''); + setShowProfileModal((prev) => !prev); + setActiveMenu(""); }; // Handle Menu Clicks const handleMenuClick = (menu) => { - setActiveMenu((prev) => (prev === menu ? '' : menu)); + setActiveMenu((prev) => (prev === menu ? "" : menu)); }; // Handle Navigation and Close Menus const handleNav = () => { - setActiveMenu(''); + setActiveMenu(""); }; // Click Outside Handler useEffect(() => { const handleClickOutside = (event) => { // Check if click is inside Profile Modal - const clickedInsideProfile = modalRef.current && modalRef.current.contains(event.target); + const clickedInsideProfile = + modalRef.current && modalRef.current.contains(event.target); // Check if click is inside any open submenu let clickedInsideMenu = false; - if (activeMenu === 'study') { - clickedInsideMenu = studyMenuRef.current && studyMenuRef.current.contains(event.target); - } else if (activeMenu === 'coding') { - clickedInsideMenu = codingMenuRef.current && codingMenuRef.current.contains(event.target); + if (activeMenu === "study") { + clickedInsideMenu = + studyMenuRef.current && studyMenuRef.current.contains(event.target); + } else if (activeMenu === "coding") { + clickedInsideMenu = + codingMenuRef.current && codingMenuRef.current.contains(event.target); } // If click is outside both Profile Modal and any open submenu, close them if (!clickedInsideProfile && !clickedInsideMenu) { setShowProfileModal(false); - setActiveMenu(''); + setActiveMenu(""); } }; // Attach the event listener - document.addEventListener('mousedown', handleClickOutside); + document.addEventListener("mousedown", handleClickOutside); // Cleanup the event listener on component unmount return () => { - document.removeEventListener('mousedown', handleClickOutside); + document.removeEventListener("mousedown", handleClickOutside); }; }, [showProfileModal, activeMenu]); @@ -88,23 +90,38 @@ export default function Header({ dark }) { - - + + 나의 스터디 - handleMenuClick('study')}> + handleMenuClick("study")}> 스터디 - {activeMenu === 'study' && ( + {activeMenu === "study" && ( - {e.stopPropagation(); handleNav();}}> + { + e.stopPropagation(); + handleNav(); + }} + > 정규 스터디 - {e.stopPropagation(); handleNav();}}> + { + e.stopPropagation(); + handleNav(); + }} + > 자율 스터디 @@ -114,29 +131,37 @@ export default function Header({ dark }) { 커뮤니티 - handleMenuClick('coding')}> + handleMenuClick("coding")}> 코딩테스트 분석 - {activeMenu === 'coding' && ( + {activeMenu === "coding" && ( - {e.stopPropagation(); handleNav();}}> - 기업/부트캠프 + { + e.stopPropagation(); + handleNav(); + }} + > + + 기업/부트캠프 + )} {isLoggedIn ? ( -
+
안녕하세요, {userName} 님 - + {showProfileModal && ( - )} diff --git a/src/APP/user-pages/DailyChallenge/DailyChallenge.dailychallenge.main.jsx b/src/APP/user-pages/DailyChallenge/DailyChallenge.dailychallenge.main.jsx index d32c4635..518e67b5 100644 --- a/src/APP/user-pages/DailyChallenge/DailyChallenge.dailychallenge.main.jsx +++ b/src/APP/user-pages/DailyChallenge/DailyChallenge.dailychallenge.main.jsx @@ -7,7 +7,6 @@ import request from '../../Api/request'; import axios from 'axios'; export default function DailyChallenge() { - const location = useLocation(); const navigate = useNavigate(); const accessToken = localStorage.getItem("accessToken"); @@ -19,9 +18,7 @@ export default function DailyChallenge() { const [showLevel, setShowLevel] = useState(false); const [tierSrc, setTierSrc] = useState('https://static.solved.ac/tier_small/0.svg'); const [challengeData, setChallengeData] = useState(null); - const [hasLoadedChallengeData, setHasLoadedChallengeData] = useState(false); const [participantsCount, setParticipantsCount] = useState(0); - const [hasJoinedToday, setHasJoinedToday] = useState(false); const [challengeHistory, setChallengeHistory] = useState([]); const msToHHMMSS = (ms) => { @@ -62,24 +59,6 @@ export default function DailyChallenge() { const isOneHourLeft = timeLeftMs <= 3600 * 1000; - /* - // 금일 챌린지 참여 여부 확인 - const checkTodayJoinStatus = async () => { - if (!accessToken) return; - - try { - const response = await request.get('/challenge/check-join'); - if (response.isSuccess && response.result) { - setHasJoinedToday(true); - // 참여했다면 챌린지 이력도 조회 - await loadChallengeHistory(); - } - } catch (error) { - console.error('챌린지 참여 여부 확인 실패:', error); - } - }; - */ - // 챌린지 이력 조회 const loadChallengeHistory = async () => { try { @@ -99,17 +78,9 @@ export default function DailyChallenge() { console.log("챌린지 이력 조회 성공:", result); - // 참여 인원 수 세팅 setParticipantsCount(totalCount); - - // totalCount가 0보다 크면 참여 이력 있다고 판단 - if (totalCount > 0 && joinLogList.length > 0) { - setHasJoinedToday(true); - setChallengeHistory(joinLogList); - } else { - setHasJoinedToday(false); - setChallengeHistory([]); - } + setChallengeHistory(joinLogList); + } catch (error) { console.error("챌린지 이력 조회 실패:", error); } @@ -128,7 +99,6 @@ export default function DailyChallenge() { useEffect(() => { const msToMidnight = getMsToMidnight(); - // 자정에 새로고침 const midnightTimer = setTimeout(() => { window.location.reload(); }, msToMidnight); @@ -140,6 +110,7 @@ export default function DailyChallenge() { // 컴포넌트 마운트 시 참여 여부 확인 useEffect(() => { + loadChallengeData(); loadChallengeHistory(); }, []); @@ -160,12 +131,8 @@ export default function DailyChallenge() { // 챌린지 데이터 로드 (한 번만 호출) const loadChallengeData = async () => { - - // 비로그인이라면 로드하지 않음 if (!accessToken) return null; - - // 이미 로드되어 있다면 캐시 반환 - if (hasLoadedChallengeData && challengeData) { + if (challengeData) { return challengeData; } @@ -179,14 +146,9 @@ export default function DailyChallenge() { levelImageUrl: response.result.levelImageUrl, algorithmList: response.result.algorithmList || [], } - // 챌린지 데이터 저장 setChallengeData(data); - setHasLoadedChallengeData(true); - return data; } - - } catch (error) { console.error('금일 챌린지 문제 조회 실패:', error); } finally { @@ -196,7 +158,6 @@ export default function DailyChallenge() { // 문제풀기 버튼 클릭 const handleProblemSolve = async () => { - // 로그인 상태 확인 if (!accessToken) { navigate('/login'); return; @@ -204,7 +165,6 @@ export default function DailyChallenge() { const data = await loadChallengeData(); - // 데이터가 있으면 백준 링크 열기 if (data) { const bojLink = `https://www.acmicpc.net/problem/${data.problemNumber}`; window.open(bojLink, '_blank'); @@ -212,7 +172,6 @@ export default function DailyChallenge() { }; const handleTagToggle = async () => { - // 로그인 상태 확인 if (!accessToken) { navigate('/login'); return; @@ -221,11 +180,10 @@ export default function DailyChallenge() { await loadChallengeData(); setShowTags((v) => !v); - setShowLevel(false); // 다른 펼쳐진 요소 접기 + setShowLevel(false); }; const handleTierToggle = async () => { - // 로그인 상태 확인 if (!accessToken) { navigate('/login'); return; @@ -234,7 +192,7 @@ export default function DailyChallenge() { await loadChallengeData(); setShowLevel((v) => !v); - setShowTags(false); // 다른 펼쳐진 요소 접기 + setShowTags(false); }; @@ -293,7 +251,6 @@ export default function DailyChallenge() { diff --git a/src/APP/user-pages/DailyChallenge/DailyChallenge.dailychallenge.table.jsx b/src/APP/user-pages/DailyChallenge/DailyChallenge.dailychallenge.table.jsx index 8b54f4b5..cddb6c30 100644 --- a/src/APP/user-pages/DailyChallenge/DailyChallenge.dailychallenge.table.jsx +++ b/src/APP/user-pages/DailyChallenge/DailyChallenge.dailychallenge.table.jsx @@ -3,14 +3,17 @@ import RankingTuple from './DailyChallenge.dailychallenge.tuple'; import rankingData from "./dummyRanking.js"; const DEFAULT_ROWS = [ - { rank: "1", name: "홍길동", speed: "0ms", memory: "0KB", codeLength: "1000B" }, - { rank: "2", name: "홍길동", speed: "0ms", memory: "0KB", codeLength: "2000B" }, - { rank: "3", name: "홍길동", speed: "0ms", memory: "0KB", codeLength: "3000B" }, + { rank: "1", name: "이유경", speed: "0ms", memory: "0KB", codeLength: "1000B" }, + { rank: "2", name: "이육영", speed: "0ms", memory: "0KB", codeLength: "2000B" }, + { rank: "3", name: "이규영", speed: "0ms", memory: "0KB", codeLength: "3000B" }, ]; -export default function RankingTable({ disable, language, date, challengeHistory = [] }) { +export default function RankingTable({ language, date, challengeHistory = [] }) { + + const hasRecordForDate = challengeHistory.some(item => item.date === date); + const isDisabled = !hasRecordForDate; - if (disable) { + if (isDisabled) { return ( @@ -92,7 +95,7 @@ export default function RankingTable({ disable, language, date, challengeHistory key={`${item.name}-${item.rank}-${index}`} item={item} language={language} - disable={disable} + disable={false} /> ))} diff --git a/src/APP/user-pages/Inquiry/Inquiry.inquiry.main.jsx b/src/APP/user-pages/Inquiry/Inquiry.inquiry.main.jsx index 9427d19a..e8f4555a 100644 --- a/src/APP/user-pages/Inquiry/Inquiry.inquiry.main.jsx +++ b/src/APP/user-pages/Inquiry/Inquiry.inquiry.main.jsx @@ -1,233 +1,256 @@ -import React, {useState, useEffect} from 'react'; +import React, { useState, useEffect } from 'react'; import request from '../../Api/request'; import * as itemS from './Styled/Inquiry.inquiry.main.styles'; import InquiryTable from './Inquiry.inquiry.table'; -import {useNavigate} from 'react-router-dom'; +import { useNavigate } from 'react-router-dom'; import useDebounce from '../../Common/useDebounce'; export default function Inquiry() { - const [isRegularMember, setIsRegularMember] = useState(false); - const navigate = useNavigate(); + const [isRegularMember, setIsRegularMember] = useState(false); + const navigate = useNavigate(); - const [posts, setPosts] = useState([]); - const [role, setRole] = useState(''); - const [categories, setCategories] = useState([{code: '', name: '전체'}]); // Default '전체' tab + const [posts, setPosts] = useState([]); + const [role, setRole] = useState(''); + const [categories, setCategories] = useState([{ code: '', name: '전체' }]); // Default '전체' tab - // api 요청 파라미터 - const [searchKeyword, setSearchKeyword] = useState(''); - const debouncedQuery = useDebounce(searchKeyword, 500); - const [sortType, setSortType] = useState('LATEST'); - const [selectedTab, setSelectedTab] = useState(''); + // api 요청 파라미터 + const [searchKeyword, setSearchKeyword] = useState(''); + const debouncedQuery = useDebounce(searchKeyword, 500); + const [sortType, setSortType] = useState('LATEST'); + const [selectedTab, setSelectedTab] = useState(''); - const [sortText, setSortText] = useState('최신순'); - const [isSortDropVisible, setIsSortDropVisible] = useState(false); // 정렬 드롭박스 열기/닫기 + const [sortText, setSortText] = useState('최신순'); + const [isSortDropVisible, setIsSortDropVisible] = useState(false); // 정렬 드롭박스 열기/닫기 - // 페이지 - const [currentPage, setCurrentPage] = useState(0); - const [totalPages, setTotalPages] = useState(0); //TODO - 임시 ) 전체 페이지 수 -> response 값으로 전체 개수 받아와야함 - const [currentPageGroup, setCurrentPageGroup] = useState(0); - const itemsPerPage = 10; // 페이지당 항목 수 + // 페이지 + const [currentPage, setCurrentPage] = useState(0); + const [totalPages, setTotalPages] = useState(0); //TODO - 임시 ) 전체 페이지 수 -> response 값으로 전체 개수 받아와야함 + const [currentPageGroup, setCurrentPageGroup] = useState(0); + const itemsPerPage = 10; // 페이지당 항목 수 - const [isTabClick, setIsTabClick] = useState(false); + const [isTabClick, setIsTabClick] = useState(false); - const pageNumbers = Array.from( - {length: Math.min(5, totalPages - currentPageGroup * 5)}, - (_, i) => currentPageGroup * 5 + i - ); + const pageNumbers = Array.from( + { length: Math.min(5, totalPages - currentPageGroup * 5) }, + (_, i) => currentPageGroup * 5 + i + ); + + useEffect(() => { + // Load `regularStudyMemberYn` from localStorage and set state + setIsRegularMember(localStorage.getItem('regularStudyMemberYn') === 'true'); + }, []); + + const fetchCategories = async () => { + try { + const response = await request.get('/inquiry/category'); + if (response.isSuccess) { + const apiCategories = response.result.categoryList; + setCategories([{ code: '', name: '전체' }, ...apiCategories]); // Add '전체' as the first tab + } else { + console.error('카테고리 목록 조회 실패:', response); + } + } catch (error) { + console.error('카테고리 목록 조회 오류', error); + } + }; + + const fetchInquiry = async () => { + try { + const response = await request.get( + `/inquiry?searchKeyword=${searchKeyword}&category=${selectedTab}&sort=${sortType}&page=${ + currentPage + 1 + }&size=${itemsPerPage}` + ); + if (response.isSuccess) { + // console.log('문의하기 목록 : ', response); + setPosts(response.result.inquiryList); + setTotalPages(Math.ceil(response.result.totalCount / itemsPerPage)); + } else { + console.error('문의하기 목록 조회 실패:', response); + } + } catch (error) { + console.error('문의하기 목록 조회 오류', error); + } + }; + + // "ROLE_ADMIN"인지 아닌지 확인하기 위해서 한번 더 api호출 + const fetchMyInfo = async () => { + try { + const response = await request.get(`/member/my-info`); + + if (response.isSuccess) { + setRole(response.result.role); + } else { + console.error('내 개인정보 조회 실패:', response); + } + } catch (error) { + console.error('내 개인정보 조회 오류', error); + } + }; + + useEffect(() => { + fetchMyInfo(); + }, []); + + useEffect(() => { + fetchCategories(); + }, []); + + useEffect(() => { + fetchInquiry(); + }, [selectedTab, sortType, currentPage, debouncedQuery]); + + const handleTabClick = (tab) => { + setSelectedTab(tab.code); + setIsTabClick(tab.code !== ''); + setCurrentPage(0); + setCurrentPageGroup(0); + }; - useEffect(() => { - // Load `regularStudyMemberYn` from localStorage and set state - setIsRegularMember(localStorage.getItem('regularStudyMemberYn') === 'true'); - }, []); - - const fetchCategories = async () => { - try { - const response = await request.get('/inquiry/category'); - if (response.isSuccess) { - const apiCategories = response.result.categoryList; - setCategories([{code: '', name: '전체'}, ...apiCategories]); // Add '전체' as the first tab - } else { - console.error('카테고리 목록 조회 실패:', response); - } - } catch (error) { - console.error('카테고리 목록 조회 오류', error); - } - }; - - const fetchInquiry = async () => { - try { - const response = await request.get( - `/inquiry?searchKeyword=${searchKeyword}&category=${selectedTab}&sort=${sortType}&page=${ - currentPage + 1 - }&size=${itemsPerPage}` - ); - if (response.isSuccess) { - // console.log('문의하기 목록 : ', response); - setPosts(response.result.inquiryList); - setTotalPages(Math.ceil(response.result.totalCount / itemsPerPage)); - } else { - console.error('문의하기 목록 조회 실패:', response); - } - } catch (error) { - console.error('문의하기 목록 조회 오류', error); - } - }; - - // "ROLE_ADMIN"인지 아닌지 확인하기 위해서 한번 더 api호출 - const fetchMyInfo = async () => { - try { - const response = await request.get(`/member/my-info`); - - if (response.isSuccess) { - setRole(response.result.role); - } else { - console.error('내 개인정보 조회 실패:', response); - } - } catch (error) { - console.error('내 개인정보 조회 오류', error); - } - }; - - useEffect(() => { - fetchMyInfo(); - }, []); - - useEffect(() => { - fetchCategories(); - }, []); - - useEffect(() => { - fetchInquiry(); - }, [selectedTab, sortType, currentPage, debouncedQuery]); - - const handleTabClick = (tab) => { - setSelectedTab(tab.code); - setIsTabClick(tab.code !== ''); - setCurrentPage(0); - setCurrentPageGroup(0); - }; - - const handleSearch = () => { - setCurrentPage(0); - setCurrentPageGroup(0); - }; - - const handlePageChange = (newPage) => { - if (newPage >= 0 && newPage < totalPages) { - setCurrentPage(newPage); - setCurrentPageGroup(Math.floor(newPage / 5)); // 페이지 그룹을 업데이트 - } - }; - - const handlePageGroupChange = (direction) => { - if (direction === 'next' && (currentPageGroup + 1) * 5 < totalPages) { - setCurrentPageGroup(currentPageGroup + 1); - setCurrentPage((currentPageGroup + 1) * 5); // 새로운 그룹의 첫 번째 페이지로 이동 - } else if (direction === 'prev' && currentPageGroup > 0) { - setCurrentPageGroup(currentPageGroup - 1); - setCurrentPage((currentPageGroup - 1) * 5); // 새로운 그룹의 첫 번째 페이지로 이동 - } - }; - - const toggleSortDrop = () => { - setIsSortDropVisible((prevState) => !prevState); - }; - - const onSortType = (type) => { - setIsSortDropVisible(false); - setSortType(type); - setSortText(type === 'LATEST' ? '최신순' : type === 'VIEW_COUNT' ? '조회수' : '좋아요'); - }; - - const handleWriteClick = () => { - navigate('/writeinquiry'); - }; - - return ( - - - - - - - 문의하기 >{' '} - {selectedTab ? categories.find((tab) => tab.code === selectedTab)?.name : '전체'} - - - - setSearchKeyword(e.target.value)} - placeholder="제목, 작성자 검색" - /> - handleSearch()} src="/img/search.svg" alt="돋보기" /> - - - - - {categories.map((tab) => - tab.code === selectedTab ? ( - handleTabClick(tab)}> - {tab.name} - - ) : ( - handleTabClick(tab)}> - {tab.name} - - ) - )} - - - - {sortText} - - {isSortDropVisible && ( - - onSortType('LATEST')}>최신순 - onSortType('VIEW_COUNT')}>조회수 - - )} - - - - - - - - handlePageGroupChange('prev')} - disabled={currentPageGroup === 0} - /> - {pageNumbers.map((pageNumber) => ( - handlePageChange(pageNumber)} - active={pageNumber === currentPage} - > - {pageNumber + 1} - - ))} - handlePageGroupChange('next')} - disabled={(currentPageGroup + 1) * 5 >= totalPages} - /> - - - {isRegularMember && role !== 'ROLE_ADMIN' ? ( - + 글쓰기 - ) : ( - - )} - - - - + const handleSearch = () => { + setCurrentPage(0); + setCurrentPageGroup(0); + }; + + const handlePageChange = (newPage) => { + if (newPage >= 0 && newPage < totalPages) { + setCurrentPage(newPage); + setCurrentPageGroup(Math.floor(newPage / 5)); // 페이지 그룹을 업데이트 + } + }; + + const handlePageGroupChange = (direction) => { + if (direction === 'next' && (currentPageGroup + 1) * 5 < totalPages) { + setCurrentPageGroup(currentPageGroup + 1); + setCurrentPage((currentPageGroup + 1) * 5); // 새로운 그룹의 첫 번째 페이지로 이동 + } else if (direction === 'prev' && currentPageGroup > 0) { + setCurrentPageGroup(currentPageGroup - 1); + setCurrentPage((currentPageGroup - 1) * 5); // 새로운 그룹의 첫 번째 페이지로 이동 + } + }; + + const toggleSortDrop = () => { + setIsSortDropVisible((prevState) => !prevState); + }; + + const onSortType = (type) => { + setIsSortDropVisible(false); + setSortType(type); + setSortText( + type === 'LATEST' ? '최신순' : type === 'VIEW_COUNT' ? '조회수' : '좋아요' ); + }; + + const handleWriteClick = () => { + navigate('/writeinquiry'); + }; + + return ( + + + + + + + 문의하기 >{' '} + {selectedTab + ? categories.find((tab) => tab.code === selectedTab)?.name + : '전체'} + + + + setSearchKeyword(e.target.value)} + placeholder="제목, 작성자 검색" + /> + handleSearch()} + src="/img/search.svg" + alt="돋보기" + /> + + + + + {categories.map((tab) => + tab.code === selectedTab ? ( + handleTabClick(tab)} + > + {tab.name} + + ) : ( + handleTabClick(tab)}> + {tab.name} + + ) + )} + + + + + {sortText} + + + {isSortDropVisible && ( + + onSortType('LATEST')}> + 최신순 + + onSortType('VIEW_COUNT')}> + 조회수 + + + )} + + + + + + + + handlePageGroupChange('prev')} + disabled={currentPageGroup === 0} + /> + {pageNumbers.map((pageNumber) => ( + handlePageChange(pageNumber)} + active={pageNumber === currentPage} + > + {pageNumber + 1} + + ))} + handlePageGroupChange('next')} + disabled={(currentPageGroup + 1) * 5 >= totalPages} + /> + + + {role !== 'ROLE_ADMIN' ? ( + + + 글쓰기 + + ) : ( + + )} + + + + + ); } diff --git a/src/APP/user-pages/Langding/Langding.landing.jsx b/src/APP/user-pages/Langding/Langding.landing.jsx index 252e1b90..cc7ce471 100644 --- a/src/APP/user-pages/Langding/Langding.landing.jsx +++ b/src/APP/user-pages/Langding/Langding.landing.jsx @@ -20,7 +20,7 @@ export default function Langding() { // console.log("스터디 최신 기수 api", response); setDetailRecentGeneration(response.data.result); if (response.data["isSuccess"]) { - console.log("api 연동 성공"); + // console.log("api 연동 성공"); } else { console.error("api 연동 실패:", response); } @@ -37,7 +37,7 @@ export default function Langding() { console.log("최신 기수 스터디 개수 api", response); setDetailStudyCount(response.data.result); if (response.data["isSuccess"]) { - console.log("api 연동 성공"); + // console.log("api 연동 성공"); } else { console.error("api 연동 실패:", response); } @@ -48,7 +48,7 @@ export default function Langding() { const checkLoginStatus = async () => { try { const response = await request.get("/member/info"); - console.log("로그인 멤버 정보 조회", response); + // console.log("로그인 멤버 정보 조회", response); if (response["isSuccess"]) { setIsLoggedIn(true); localStorage.setItem("memberId", response.result.memberId); diff --git a/src/APP/user-pages/Mypage/Mypage.mypage.challenge.rewardprogress.jsx b/src/APP/user-pages/Mypage/Mypage.mypage.challenge.rewardprogress.jsx index acd64dea..b658bfc7 100644 --- a/src/APP/user-pages/Mypage/Mypage.mypage.challenge.rewardprogress.jsx +++ b/src/APP/user-pages/Mypage/Mypage.mypage.challenge.rewardprogress.jsx @@ -1,10 +1,31 @@ import React from "react"; +import { useNavigate } from "react-router-dom"; import * as ItemS from "./Styled/Mypage.mypage.challenge.rewardprogress.styles"; export default function RewardProgress({ challengeRewardCount, challengeWinCount, + regularStudyId, }) { + const navigate = useNavigate(); + + const handleUseReward = () => { + // id 값이 없는 경우를 대비한 방어 코드 + if (!regularStudyId) { + console.error("스터디 ID가 전달되지 않았습니다."); + return; + } + + // 로컬 스토리지 키 생성 + const storageKey = `activeComponent_${regularStudyId}`; + + // 'attendance' 컴포넌트가 보이도록 로컬 스토리지에 값 설정 + localStorage.setItem(storageKey, "attendance"); + + // 페이지 이동 + navigate(`/regularstudy/${regularStudyId}`); + }; + return ( @@ -57,7 +78,7 @@ export default function RewardProgress({ /> */} - 보상 사용하기 + 보상 사용하기 ); } diff --git a/src/APP/user-pages/Mypage/Mypage.mypage.challenge.table.jsx b/src/APP/user-pages/Mypage/Mypage.mypage.challenge.table.jsx index 4270a391..922bbbee 100644 --- a/src/APP/user-pages/Mypage/Mypage.mypage.challenge.table.jsx +++ b/src/APP/user-pages/Mypage/Mypage.mypage.challenge.table.jsx @@ -11,11 +11,12 @@ export default function ChallengeTable({ logCount, rewardCount, winCount, + regularStudyId, isMemberMatch, fetchRewardLog, onChangeLogType, }) { - // const [count, setCount] = useState(inquiryCount); //TODO - - 임시로 10 넣음 + // const [count, setCount] = useState(inquiryCount); const [challengeRewardCount, setChallengeRewardCount] = useState(rewardCount); const [challengeWinCount, setChallengeWinCount] = useState(winCount); @@ -96,6 +97,7 @@ export default function ChallengeTable({ diff --git a/src/APP/user-pages/Mypage/Mypage.mypage.main.jsx b/src/APP/user-pages/Mypage/Mypage.mypage.main.jsx index eb2e322b..5a392fae 100644 --- a/src/APP/user-pages/Mypage/Mypage.mypage.main.jsx +++ b/src/APP/user-pages/Mypage/Mypage.mypage.main.jsx @@ -28,6 +28,7 @@ export default function MyPage() { const [logType, setLogType] = useState(""); // "" | "ACQUIRED" | "USED" const [rewardCount, setRewardCount] = useState(0); // 누적 교환권 수 const [winCount, setWinCount] = useState(0); // 챌린지 win 수 + const [regularStudyId, setRegularStudyId] = useState(null); // 참여중인 정규 스터디 id // 내 스터디, 내가 쓴 글 탭 변경 const [selectedTab, setSelectedTab] = useState("study"); @@ -40,12 +41,24 @@ export default function MyPage() { const totalPagesPassStudy = Math.ceil(passStudyList.length / itemsPerPage); // 참여 스터디 총 페이지 수 const totalPagesApplyStudy = Math.ceil(applyStudyList.length / itemsPerPage); // 지원 스터디 총 페이지 수 + const fetchInfo = async () => { + try { + const response = await request.get("/member/info"); + // console.log("로그인 멤버 정규스터 정보 조회", response); + if (response.isSuccess && response.result.regularStudyId !== null) { + setRegularStudyId(response.result.regularStudyId); + } + } catch (error) { + console.error("로그인 멤버 정보 조회 실패", error); + } + }; + const fetchMyInfo = async () => { try { const response = await request.get(`/member/${handle}/info`); if (response.isSuccess) { - console.log("나의 정보 조회 성공", response); + // console.log("나의 정보 조회 성공", response); setMyInfoData(response.result); } else { console.error("나의 정보 조회 실패:", response); @@ -60,7 +73,7 @@ export default function MyPage() { const response = await request.get(`/member/${handle}/study`); if (response.isSuccess) { - console.log("나의 스터디 조회 성공", response); + // console.log("나의 스터디 조회 성공", response); setPassStudyList(response.result.passStudyList); setApplyStudyList(response.result.applyStudyList); } else { @@ -74,7 +87,7 @@ export default function MyPage() { const fetchBoard = async () => { try { const response = await request.get(`/member/${handle}/board`); - console.log("내 게시글 목록 조회 성공", response); + // console.log("내 게시글 목록 조회 성공", response); if (response.isSuccess) { setBoards(response.result.boardList); @@ -92,7 +105,7 @@ export default function MyPage() { const fetchinquiry = async () => { try { const response = await request.get(`/member/${handle}/inquiry`); - console.log("내 문의하기 목록 조회 성공", response); + // console.log("내 문의하기 목록 조회 성공", response); if (response.isSuccess) { setInquiries(response.result.inquiryList); @@ -111,7 +124,7 @@ export default function MyPage() { ? `/challenge/reward/log?logType=${type}` : `/challenge/reward/log`; const response = await request.get(url); - console.log("보상 로그 조회 성공", response); + // console.log("보상 로그 조회 성공", response); if (response.isSuccess) { setRewardLogs(response.result.rewardLogList); setLogCount(response.result.totalCount); @@ -126,7 +139,7 @@ export default function MyPage() { const fetchRewardStatus = async () => { try { const response = await request.get(`/challenge/reward/status`); - console.log("내 챌린지 보상 현황 조회 성공", response); + // console.log("내 챌린지 보상 현황 조회 성공", response); if (response.isSuccess) { setRewardCount(response.result.rewardCount); @@ -148,6 +161,7 @@ export default function MyPage() { }, [handle, logType]); // logType이 바뀌면 자동 호출 useEffect(() => { + fetchInfo(); fetchMyInfo(); fetchMyStudy(); fetchBoard(); @@ -252,6 +266,7 @@ export default function MyPage() { logCount={logCount} rewardCount={rewardCount} winCount={winCount} + regularStudyId={regularStudyId} isMemberMatch={isMemberMatch} fetchRewardLog={fetchRewardLog} onChangeLogType={setLogType} diff --git a/src/APP/user-pages/RegularStudy/RegularStudy.regularstudy.attendance.jsx b/src/APP/user-pages/RegularStudy/RegularStudy.regularstudy.attendance.jsx index a5560808..a7cac79f 100644 --- a/src/APP/user-pages/RegularStudy/RegularStudy.regularstudy.attendance.jsx +++ b/src/APP/user-pages/RegularStudy/RegularStudy.regularstudy.attendance.jsx @@ -7,7 +7,7 @@ import AttendanceModal from './RegularStudy.regularstudy.modal'; import { AlertContext } from '../../Common/Alert/AlertContext'; import { ConfirmContext } from '../../Common/Confirm/ConfirmContext'; -export default function RegularStudyAttendance() { +export default function RegularStudyAttendance({ memberRole, endYn }) { const { id } = useParams(); const [currentTab, setCurrentTab] = useState('문제 인증'); const [data, setData] = useState({}); @@ -34,6 +34,7 @@ export default function RegularStudyAttendance() { try { const response = await request.get(`study/${id}/attendance`); if (response['isSuccess']) { + // console.log('출석 데이터 조회: ', response.result); setOriginalAttendanceData(response.result.attendanceList); const transformedData = transformData(response.result.attendanceList); setData(transformedData); @@ -560,20 +561,23 @@ export default function RegularStudyAttendance() { flexDirection: 'row', justifyContent: 'space-between', marginBottom: '0.167rem', + height: '1.75rem', }} > *이름을 클릭하면 주차별 출석 인증 내역을 확인할 수 있습니다. - - 챌린지 보상 사용하기 - + {!endYn && memberRole === 'MEMBER' && ( + + 챌린지 보상 사용하기 + + )}
)} diff --git a/src/APP/user-pages/RegularStudy/RegularStudy.regularstudy.main.jsx b/src/APP/user-pages/RegularStudy/RegularStudy.regularstudy.main.jsx index 4986f86f..6aea0c05 100644 --- a/src/APP/user-pages/RegularStudy/RegularStudy.regularstudy.main.jsx +++ b/src/APP/user-pages/RegularStudy/RegularStudy.regularstudy.main.jsx @@ -1,14 +1,30 @@ -import React, { useEffect, useState } from 'react' -import * as itemS from "./Styled/RegularStudy.regularstudy.main.styles"; -import RegularStudySideBar from "./RegularStudy.regularstudy.sidebar"; -import RegularStudyHome from "./RegularStudy.regularstudy.home" -import RegularStudyAttendance from "./RegularStudy.regularstudy.attendance" -import RegularStudyCurriculum from "./RegularStudy.regularstudy.curriculum" +import React, { useEffect, useState } from 'react'; +import * as itemS from './Styled/RegularStudy.regularstudy.main.styles'; +import RegularStudySideBar from './RegularStudy.regularstudy.sidebar'; +import RegularStudyHome from './RegularStudy.regularstudy.home'; +import RegularStudyAttendance from './RegularStudy.regularstudy.attendance'; +import RegularStudyCurriculum from './RegularStudy.regularstudy.curriculum'; import RegularStudyMocktest from './RegularStudy.regularstudy.mocktest'; import { useParams } from 'react-router-dom'; +import request from '../../Api/request'; export default function RegularStudyMain() { - const { id } = useParams(); // 정규스터디 ID 가져오기 + const { id } = useParams(); // 정규스터디 ID 가져오기 + const [regularStudyInfo, setRegularStudyInfo] = useState(null); + + const fetchRegularStudyInfo = async () => { + try { + const response = await request.get(`study/${id}/info`); + // console.log('정규 스터디 조회 정보: ', response); + if (response['isSuccess']) { + setRegularStudyInfo(response.result); + } else { + console.error('정규 스터디 조회 실패:', response); + } + } catch (err) { + console.error('정규스터디 정보 조회 오류', err); + } + }; const getInitialComponent = () => { const savedComponent = localStorage.getItem(`activeComponent_${id}`); @@ -18,15 +34,21 @@ export default function RegularStudyMain() { const [activeComponent, setActiveComponent] = useState(getInitialComponent); useEffect(() => { + fetchRegularStudyInfo(); localStorage.setItem(`activeComponent_${id}`, activeComponent); }, [activeComponent, id]); const renderComponent = () => { switch (activeComponent) { - case 'home': + case 'home': return ; case 'attendance': - return ; + return ( + + ); case 'curriculum': return ; case 'mocktest': @@ -34,13 +56,22 @@ export default function RegularStudyMain() { default: return ; } + }; + + if (!regularStudyInfo) { + return
Loading regular study info...
; } + return ( - + {renderComponent()} - ) + ); } diff --git a/src/APP/user-pages/RegularStudy/RegularStudy.regularstudy.sidebar.jsx b/src/APP/user-pages/RegularStudy/RegularStudy.regularstudy.sidebar.jsx index 835a1859..8b6dda82 100644 --- a/src/APP/user-pages/RegularStudy/RegularStudy.regularstudy.sidebar.jsx +++ b/src/APP/user-pages/RegularStudy/RegularStudy.regularstudy.sidebar.jsx @@ -6,28 +6,9 @@ import { useNavigate, useParams } from 'react-router-dom'; export default function RegularStudySideBar({ setActiveComponent, activeComponent, + regularStudyInfo, }) { - const { id } = useParams(); // 파라미터로 받는 해당 정규스터디의 studyId const navigate = useNavigate(); - const [regularStudyInfo, setRegularStudyInfo] = useState(null); - - useEffect(() => { - const fetchRegularStudyInfo = async () => { - try { - const response = await request.get(`study/${id}/info`); - // console.log("정규 스터디 사이드 바 조회 정보: ", response); - setRegularStudyInfo(response.result); - if (response['isSuccess']) { - console.log('정규 스터디 조회 성공'); - } else { - console.error('정규 스터디 조회 실패:', response); - } - } catch (err) { - console.error('정규스터디 정보 조회 오류', err); - } - }; - fetchRegularStudyInfo(); - }, [id]); const handleApplicationClick = () => { if (regularStudyInfo.answerYN) {