diff --git a/src/components/infiniteScroll.js b/src/components/infiniteScroll.js index d994fd9351f235009a6efc5eea59ce79e89ef357..09fb7491dafc93086614ad53e333d74048d4c7a8 100644 --- a/src/components/infiniteScroll.js +++ b/src/components/infiniteScroll.js @@ -1,23 +1,33 @@ import React, { useState, useEffect } from 'react'; import {PageItem} from "./recruit/recruitList"; -//import {PageItem} from "./subscribe/subscribeList" import InfiniteScrollStyles from './infiniteScroll.module.css' import {ChannelItem} from "./channel/channelList"; + function InfiniteScroll(props){ const [items, setItems] = useState([]); const [minId, setMinId] = useState(0); const [isLoading, setIsLoading] = useState(false); const page = props.pagename; + const queryString1 = props.queryString1; + const queryString2 = props.queryString2; const initialFetchData = async ()=>{ try{ - const response = await fetch(`/api/${page}`); + const response = await fetch(`/api/${page}${queryString1}`); + console.log(`/api/${page}${queryString1}`); const jsonData = await response.json(); - setMinId(jsonData['minId']); const newData = jsonData[page]; - if (page==='recruits'){ - + setMinId(jsonData['minId']); + + if(page==='channels'){ + if(newData.length>0){ + const components=newData.map((item)=>( + <ChannelItem data={item}/> + )); + setItems(prevItem => [...prevItem, ...components]); + } + }else { if(newData.length > 0) { const components = newData.map((item) => ( <PageItem data={item}/> @@ -25,16 +35,7 @@ function InfiniteScroll(props){ setItems(prevItem => [...prevItem, ...components]); } - } - else if(page==='channels'){ - if(newData.length>0){ - const components=newData.map((item)=>( - <ChannelItem data={item}/> - )); - setItems(prevItem => [...prevItem, ...components]); - } - - } + } } catch(error){ console.log('Error during fetch:', error); @@ -43,7 +44,7 @@ function InfiniteScroll(props){ useEffect(() => { initialFetchData(); // 최초 렌더링 시에 데이터 가져오기 - }, []); + }, [page, queryString1, queryString2]); const fetchData = async ()=>{ if (isLoading || minId === 0) return; @@ -51,22 +52,14 @@ function InfiniteScroll(props){ setIsLoading(true); try{ - const response = await fetch(`/api/${page}&minId=${minId}`); + const response = await fetch(`/api/${page}?minId=${minId}${queryString2}`); + console.log(`/api/${page}?minId=${minId}${queryString2}`); const jsonData = await response.json(); const newData = jsonData[page]; setMinId(jsonData['minId']); - - if (page==='recruits'){ - if(newData.length > 0) { - const components = newData.map((item) => ( - <PageItem data={item}/> - )); - - setItems(prevItem => [...prevItem, ...components]); - } - } - else if(page==='channels'){ + + if(page==='channels'){ if(newData.length>0){ for (let i = 0; i < newData.length; i += 2) { const component = ( @@ -78,7 +71,16 @@ function InfiniteScroll(props){ setItems((prevItem) => [...prevItem, component]); } } - } + }else { + if(newData.length > 0) { + const components = newData.map((item) => ( + <PageItem data={item}/> + )); + + setItems(prevItem => [...prevItem, ...components]); + } + } + } catch(error){ console.log('Error during fetch:', error); } finally{ @@ -86,15 +88,11 @@ function InfiniteScroll(props){ } }; - useEffect(() => { - fetchData(); // 최초 렌더링 시에 데이터 가져오기 - }, [page]); - // 스크롤 이벤트 감지 const handleScroll = () => { if ( - window.innerHeight + document.documentElement.scrollTop + 100 >= - document.documentElement.offsetHeight + window.innerHeight + window.scrollY + 100 >= + document.body.offsetHeight && !isLoading ) { // 스크롤이 페이지 하단에 도달하면 데이터를 가져옴 diff --git a/src/components/infiniteScroll.module.css b/src/components/infiniteScroll.module.css index 2f6da344ee0727aa0a87247f537c9cc916e5882b..1f59f35e65c7c7b2dcf1882743673f848622a179 100644 --- a/src/components/infiniteScroll.module.css +++ b/src/components/infiniteScroll.module.css @@ -1,5 +1,5 @@ .loading{ - font-size: 30px; + font-size: 20px; } /* InfiniteScroll.module.css */ diff --git a/src/components/comment.js b/src/components/recruit/comment.js similarity index 98% rename from src/components/comment.js rename to src/components/recruit/comment.js index 6374f4dbf86cf3920129406f07fcd49d10931a8e..ab9efea9244de46a4073d72b5e3119145533e2ea 100644 --- a/src/components/comment.js +++ b/src/components/recruit/comment.js @@ -1,7 +1,7 @@ import React, { useState, useEffect, useContext } from "react"; import commentStyles from './comment.module.css' -import { AuthContext } from "../App"; -import DeleteComment from "./recruit/deleteComment"; +import { AuthContext } from "../../App"; +import DeleteComment from "./deleteComment"; function CommentItem({ data, setCount }){ const [isDeleteOpen, setIsDeleteOpen] = useState(false); diff --git a/src/components/comment.module.css b/src/components/recruit/comment.module.css similarity index 100% rename from src/components/comment.module.css rename to src/components/recruit/comment.module.css diff --git a/src/components/recruit/createVote.js b/src/components/recruit/createVote.js index 6bf3785bd5dd0b995049d7f653905fc0dd2e7055..b0f55067c55eb83d8ae0d2b10b9fa8e48406bdc7 100644 --- a/src/components/recruit/createVote.js +++ b/src/components/recruit/createVote.js @@ -1,7 +1,7 @@ import React from 'react'; import createVoteStyles from './createVote.module.css'; -function CreateVote({ id, data, create, onClose }){ +function CreateVote({ id, data }){ const createVote = async ()=>{ try{ const response = await fetch(`/api/recruits/${id}/times/save`,{ @@ -14,7 +14,6 @@ function CreateVote({ id, data, create, onClose }){ if (response.ok){ alert('투표가 등록되었습니다.'); - create(); window.location.reload(); }else{ throw new Error('Data loading error'); diff --git a/src/components/recruit/recruitDetail.js b/src/components/recruit/recruitDetail.js index 45e8e083d41f865a5f5fb514d5e1ce7150cb5bb4..3cbc64d472d7a3180ad5afa9e42cd8df3b769242 100644 --- a/src/components/recruit/recruitDetail.js +++ b/src/components/recruit/recruitDetail.js @@ -1,6 +1,6 @@ import React, { useState, useContext } from "react"; import recruitDetailStyles from './recruitDetail.module.css'; -import Comment from '../comment'; +import Comment from './comment'; import MyScheduleApp from '../calendar/calendar'; import { AuthContext } from '../../App'; import ModifyRecruit from "./modifyRecruit"; diff --git a/src/components/recruit/recruitDetail.module.css b/src/components/recruit/recruitDetail.module.css index 2fd953315d3abc5fea2e8f138f48ea262a5d93ad..be8def3e55f61cba74557ae93617eaefe2c73d0e 100644 --- a/src/components/recruit/recruitDetail.module.css +++ b/src/components/recruit/recruitDetail.module.css @@ -79,7 +79,7 @@ height: 35px; font-weight: 600; - font-size: 30px; + font-size: 27px; line-height: 30x; color: #000000; @@ -165,7 +165,7 @@ width: 500px; height: 31px; - font-size: 20px; + font-size: 18px; line-height: 38px; color: #000000; @@ -196,7 +196,7 @@ width: 500px; height: 200px; - font-size: 24px; + font-size: 22px; line-height: 33px; color: #000000; diff --git a/src/components/recruit/recruitList.js b/src/components/recruit/recruitList.js index d2efaa4f1ceb0a1a1523cb2e598f6c7343c03fe8..be230474c259d154207a3c735c7fbf5020ae045b 100644 --- a/src/components/recruit/recruitList.js +++ b/src/components/recruit/recruitList.js @@ -122,7 +122,11 @@ function PageItem(props){ {isParticipants ?( <div className={recruitListStyles.voteContainer}> - <button type="button" className={recruitListStyles.vote} onClick={openVote}>투표하기</button> + {props.data.vote === 'Before' || props.data.vote === 'During' ? ( + <button type="button" className={recruitListStyles.vote} onClick={openVote}>투표하기</button> + ):( + <button type="button" className={recruitListStyles.vote} onClick={openVote}>결과확인</button> + )} <div className={recruitListStyles.voteInquiry}> <Vote isOpen={isVoteOpen} onClose={closeVote} data={props.data}/> </div> @@ -139,20 +143,40 @@ function PageItem(props){ } function RecruitList(){ - const [generatedUrl, setGeneratedUrl] = useState(''); + const [generatedUrl, setGeneratedUrl] = useState('recruits'); + const [firstQueryString, setFirstQueryString] = useState(''); + const [secondQueryString, setSecondQueryString] = useState(''); + const [forceRender, setForceRender] = useState(false); const handleUrlGeneration = (url) => { setGeneratedUrl(url); }; + const handleFirstQueryString = (url) => { + setFirstQueryString(url); + }; + + const handleSecondQueryString = (url) => { + setSecondQueryString(url); + }; + + useEffect(() => { + setForceRender((prev) => !prev); + }, [generatedUrl, firstQueryString, secondQueryString]) + return( <div className={recruitListStyles.RecruitList}> - <Search currentPage='recruits' onUrlGenerated={handleUrlGeneration}/> - {generatedUrl === '' ?( - <InfiniteScroll pagename='recruits' /> - ):( - <InfiniteScroll pagename={generatedUrl} /> - )} + <Search + currentPage='recruits' + onUrlGenerated={handleUrlGeneration} + onFirstQueryString={handleFirstQueryString} + onSecondQueryString={handleSecondQueryString} + /> + <InfiniteScroll + pagename={generatedUrl} + queryString1={firstQueryString} + queryString2={secondQueryString} + /> </div> ) } diff --git a/src/components/recruit/timeInquiry.js b/src/components/recruit/timeInquiry.js index bd575e943531e44b538e07ab06d60102780143de..5a4fcd8acfa9b5e7c51629e8bdad5c144d865073 100644 --- a/src/components/recruit/timeInquiry.js +++ b/src/components/recruit/timeInquiry.js @@ -38,8 +38,6 @@ function TimeItem({ item, setSelectedTimes }){ } function TimeInquiry({ isOpen, onClose, data }){ - const savedIsVote = localStorage.getItem('isVote'); - const [isVote, setIsVote] = useState(savedIsVote ? JSON.parse(savedIsVote) : false); const [inquirydata, setInquirydata] = useState([]); const [isLoading, setIsLoading] = useState(true); const [selectedTimes, setSelectedTimes] = useState([]); @@ -85,12 +83,6 @@ function TimeInquiry({ isOpen, onClose, data }){ }); }; - const handleButtonClick = () => { - if(!isVote){ - setIsVote(true); - } - }; - const timeInquiry = async (body)=>{ setIsLoading(true); @@ -138,12 +130,6 @@ function TimeInquiry({ isOpen, onClose, data }){ setIsLoading(false); } } - - useEffect(() => { - if(isVote){ - localStorage.setItem('isVote', JSON.stringify(isVote)); - } - }, [isVote]); return( <div className={timeInquiryStyles.timeInquiry}> @@ -245,7 +231,7 @@ function TimeInquiry({ isOpen, onClose, data }){ <div className={timeInquiryStyles.voteButton}> <div className={timeInquiryStyles.createVote}> - <CreateVote id={data.id} data={selectedTimes} create={handleButtonClick} onClose={onClose}/> + <CreateVote id={data.id} data={selectedTimes}/> </div> </div> diff --git a/src/components/recruit/vote.js b/src/components/recruit/vote.js index 3bbcb8315f2ab9db8ea9f3c384ab5c383eb40a68..ea9ddd83780f4062e668e0aff8ef20305b81d37a 100644 --- a/src/components/recruit/vote.js +++ b/src/components/recruit/vote.js @@ -69,7 +69,7 @@ function Vote({ isOpen, onClose, data }) { } } - const vote = async()=>{ + const vote = async ()=>{ try{ const response = await fetch(`/api/recruits/${data.id}/times/vote`,{ method: 'POST', @@ -90,8 +90,24 @@ function Vote({ isOpen, onClose, data }) { } } - const closedVote = ()=>{ + const endVote = async ()=>{ + try{ + const response = await fetch(`/api/recruits/${data.id}/times/vote`,{ + method: 'PUT', + headers: { + 'Content-Type': 'application/json', + }, + }); + if (response.ok){ + alert("투표가 마감되었습니다."); + window.location.reload(); + }else{ + throw new Error('Data loading error'); + } + } catch(error){ + console.log('Error during fetch:', error); + } } useEffect(() => { @@ -116,10 +132,21 @@ function Vote({ isOpen, onClose, data }) { </div> <div className={voteStyles.voteContainer}> - <button className={voteStyles.goVote} onClick={vote}>투표하기</button> - {userData.nickname === data.Writer.nickname && - <button className={voteStyles.closedVote} onClick={closedVote}>투표 마감</button> - } + {data.vote === 'During' ? ( + <div> + <button className={voteStyles.goVote} onClick={vote}>투표하기</button> + {userData.nickname === data.Writer.nickname && + <button className={voteStyles.goVote} onClick={endVote}>투표 마감</button> + } + </div> + ):( + <div> + <button className={voteStyles.endVote} onClick={vote} disabled>투표하기</button> + {userData.nickname === data.Writer.nickname && + <button className={voteStyles.endVote} onClick={endVote} disabled>투표 마감</button> + } + </div> + )} </div> </div> </div> diff --git a/src/components/recruit/vote.module.css b/src/components/recruit/vote.module.css index d3adb3195216797aa6e1f05e84d700c7246f8e60..beb10d35fe19d0d5946cd10f0c7a392ede174ada 100644 --- a/src/components/recruit/vote.module.css +++ b/src/components/recruit/vote.module.css @@ -98,7 +98,7 @@ gap: 10px; } -.goVote, .closedVote{ +.goVote{ width: 90px; height: 40px; border:none; @@ -110,3 +110,16 @@ box-shadow: 2px 2px 5px rgba(0, 0, 0, 0.2); /* 그림자 추가 */ transition: background-color 0.3s, color 0.3s, transform 0.3s; } + +.endVote{ + width: 90px; + height: 40px; + border:none; + border-radius: 5px; + cursor: pointer; + + color: white; + background-color:#b5b9c5; + box-shadow: 2px 2px 5px rgba(0, 0, 0, 0.2); /* 그림자 추가 */ + transition: background-color 0.3s, color 0.3s, transform 0.3s; +} \ No newline at end of file diff --git a/src/components/search.js b/src/components/search.js index f7d8d11440c2d53970b0d710b5b74364cd10a71a..93edb7864d454835f24f6cb2fd782105625985b5 100644 --- a/src/components/search.js +++ b/src/components/search.js @@ -7,7 +7,7 @@ import ExistingChannel from './channel/existingChannel'; -function Search({ currentPage, onUrlGenerated }){ +function Search({ currentPage, onUrlGenerated, onFirstQueryString, onSecondQueryString }){ const [isModalOpen, setIsModalOpen] = useState(false); const [searchTerm, setSearchTerm] = useState(''); const [searchType, setSearchType] = useState('title'); @@ -16,6 +16,7 @@ function Search({ currentPage, onUrlGenerated }){ const [myChannelData, setmyChannelData]=useState(''); const navigate = useNavigate(); + const openModal = () => { setIsModalOpen(true); }; @@ -59,23 +60,32 @@ function Search({ currentPage, onUrlGenerated }){ const handleSearch = () => { - let searchURL = `${currentPage}/search?`; + let searchURL = `${currentPage}/search`; + let firstQueryString = '?'; + let secondQueryString = '&'; if (searchType === 'title') { - searchURL += `title=${searchTerm}`; + firstQueryString += `title=${searchTerm}`; + secondQueryString += `title=${searchTerm}`; } else if (searchType === 'content') { - searchURL += `content=${searchTerm}`; + firstQueryString += `content=${searchTerm}`; + secondQueryString += `content=${searchTerm}`; } else if (searchType === 'writer') { - searchURL += `writer=${searchTerm}`; + firstQueryString += `writer=${searchTerm}`; + secondQueryString += `writer=${searchTerm}`; } else if (searchType === 'title&content') { - searchURL += `title=${searchTerm}&content=${searchTerm}`; + firstQueryString += `title=${searchTerm}&content=${searchTerm}`; + secondQueryString += `title=${searchTerm}&content=${searchTerm}`; } if (sortType !== ''){ - searchURL += `&sort=${sortType}`; + firstQueryString += `&sort=${sortType}`; + secondQueryString += `&sort=${sortType}`; } onUrlGenerated(searchURL); + onFirstQueryString(firstQueryString); + onSecondQueryString(secondQueryString); }; return( @@ -90,16 +100,16 @@ function Search({ currentPage, onUrlGenerated }){ onChange={(e) => setSearchTerm(e.target.value)} /> <div onClick={handleSearch}> - <svg className={searchStyles.icon} width="23" height="24" viewBox="0 0 23 24" fill="none" xmlns="http://www.w3.org/2000/svg"> - <g clipPath="url(#clip0_40_6242)"> - <path d="M14.8542 13.9808H14.0971L13.8287 13.722C14.7679 12.6295 15.3333 11.2112 15.3333 9.66825C15.3333 6.22784 12.5446 3.43909 9.10417 3.43909C5.66375 3.43909 2.875 6.22784 2.875 9.66825C2.875 13.1087 5.66375 15.8974 9.10417 15.8974C10.6471 15.8974 12.0654 15.332 13.1579 14.3928L13.4167 14.6612V15.4183L18.2083 20.2003L19.6362 18.7724L14.8542 13.9808ZM9.10417 13.9808C6.71792 13.9808 4.79167 12.0545 4.79167 9.66825C4.79167 7.282 6.71792 5.35575 9.10417 5.35575C11.4904 5.35575 13.4167 7.282 13.4167 9.66825C13.4167 12.0545 11.4904 13.9808 9.10417 13.9808Z" fill="#323232"/> - </g> - <defs> - <clipPath id="clip0_40_6242"> - <rect width="23" height="23" fill="white" transform="translate(0 0.564087)"/> - </clipPath> - </defs> - </svg> + <svg className={searchStyles.icon} width="23" height="24" viewBox="0 0 23 24" fill="none" xmlns="http://www.w3.org/2000/svg"> + <g clipPath="url(#clip0_40_6242)"> + <path d="M14.8542 13.9808H14.0971L13.8287 13.722C14.7679 12.6295 15.3333 11.2112 15.3333 9.66825C15.3333 6.22784 12.5446 3.43909 9.10417 3.43909C5.66375 3.43909 2.875 6.22784 2.875 9.66825C2.875 13.1087 5.66375 15.8974 9.10417 15.8974C10.6471 15.8974 12.0654 15.332 13.1579 14.3928L13.4167 14.6612V15.4183L18.2083 20.2003L19.6362 18.7724L14.8542 13.9808ZM9.10417 13.9808C6.71792 13.9808 4.79167 12.0545 4.79167 9.66825C4.79167 7.282 6.71792 5.35575 9.10417 5.35575C11.4904 5.35575 13.4167 7.282 13.4167 9.66825C13.4167 12.0545 11.4904 13.9808 9.10417 13.9808Z" fill="#323232"/> + </g> + <defs> + <clipPath id="clip0_40_6242"> + <rect width="23" height="23" fill="white" transform="translate(0 0.564087)"/> + </clipPath> + </defs> + </svg> </div> </div> <select