Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat: create real time saving in votememo #160

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
"react-mobile-datepicker": "^4.0.2",
"react-router-dom": "^6.21.1",
"recoil": "^0.7.7",
"recoil-persist": "^5.1.0",
"swiper": "^11.0.5"
},
"devDependencies": {
Expand Down
4 changes: 2 additions & 2 deletions src/api/vote.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export const getVoteInfo = async (voteId: number): Promise<VoteInfo> => {

//보트 리스트
export const getVoteListInfo = async (spaceId: number): Promise<VoteListInfo[]> => {
const response = await axios.get(`/api/votes/${spaceId}`);
const response = await axios.get(`/api/votes`, {params: {spaceId, voteStatusOption: 'ALL'}});
return response.data;
};

Expand All @@ -28,7 +28,7 @@ export const getVoteListInfo = async (spaceId: number): Promise<VoteListInfo[]>
/* ----------------------------------- P O S T ---------------------------------- */

//vote 추가
export const PostNewVote = async ({spaceId, title}: PostVoteTitleProps) => {
export const postNewVote = async ({spaceId, title}: PostVoteTitleProps) => {
try {
const response = await axios.post('/api/votes', {spaceId, title});
console.log('axios 포스트 성공', response);
Expand Down
32 changes: 16 additions & 16 deletions src/components/BottomSlide/BottomSlide.tsx
Original file line number Diff line number Diff line change
@@ -1,33 +1,33 @@
import { Slide } from "@chakra-ui/react";
import { useRef } from "react";
import {Slide} from '@chakra-ui/react';
import {useRef} from 'react';

import styles from "./BottomSlide.module.scss";
import styles from './BottomSlide.module.scss';

import useOnClickOutside from "@/hooks/useOnClickOutside";
import useOnClickOutside from '@/hooks/useOnClickOutside';

import CloseIcon from "@/assets/close.svg?react";
import CloseIcon from '@/assets/close.svg?react';

import { BottomSlideProps } from "../../types/bottomSlide";
import {BottomSlideProps} from '../../types/bottomSlide';

function BottomSlide({ isOpen, onClose, children }: BottomSlideProps) {
function BottomSlide({isOpen, onClose, children}: BottomSlideProps) {
const containerStyle = {
display: isOpen ? "block" : "none",
display: isOpen ? 'block' : 'none',
};

const closeModal = () => {
onClose();
document.body.style.overflow = 'visible';
};

const slideRef = useRef(null);
useOnClickOutside(slideRef, onClose);
useOnClickOutside(slideRef, closeModal);

return (
<div className={styles.slideContainer} style={containerStyle}>
<Slide
ref={slideRef}
className={styles.slide}
direction="bottom"
in={isOpen}
>
<Slide ref={slideRef} className={styles.slide} direction='bottom' in={isOpen}>
<div className={styles.slide__content}>
<div className={styles.closeButtonContainer}>
<button onClick={onClose}>
<button onClick={closeModal}>
<CloseIcon />
</button>
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,34 @@
import {useState} from 'react';
import {useRecoilValue} from 'recoil';

import styles from './SelectButton.module.scss';

import useGetSelectedCandidates from '@/hooks/useGetSelectedCandidates';
import useGetSelectedArray from '@/hooks/useGetSelectedArray';

import {selectedPlaceState} from '@/recoil/vote/selectPlace';

// //"선택된 장소 객체"
const placeInfo = {
placeId: 23,
placeName: '안녕호텔',
category: '호텔',
location: '서울',
placeImageURL: 'https://img-cf.kurly.com/shop/data/goodsview/20210218/gv30000159355_1.jpg',
latlng: {lat: 33.450936, lng: 126.569477},
};

const SelectButton = () => {
const [isClicked, setIsClicked] = useState(false);
const {addCandidateInSelectedList} = useGetSelectedCandidates();
const selectedPlaces = useRecoilValue(selectedPlaceState);

//"선택된 장소의 ID"
const placeId = 22;
const {toggleItemInNewArray} = useGetSelectedArray(selectedPlaceState);

const handleClick = () => {
setIsClicked((prev) => !prev);
addCandidateInSelectedList(placeId);
toggleItemInNewArray(placeInfo);
};

console.log('선택한 배열', selectedPlaces);
return (
<button className={`${styles.container} ${isClicked ? styles.clicked : ''}`} onClick={handleClick}>
{isClicked ? '선택됨' : '선택'}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@ import MapPinNumber from '../MapPins/MapPinNumber';
import {CandidatesInfo, Latlng} from '@/types/vote';

const CandidatesMapBody = ({candidates}: {candidates: CandidatesInfo[]}) => {
const [centerMarker, setCenterMarker] = useState(candidates[0].latlng);
const [centerMarker, setCenterMarker] = useState(candidates[0].placeInfo.latlng);
const [selectedPinIndex, setSelectedPinIndex] = useState(0);
const swiperRef = useRef<SwiperRef>(null);

useEffect(() => {
setCenterMarker(candidates[0].latlng);
setCenterMarker(candidates[0].placeInfo.latlng);
}, []);

const handleMapMarkerClick = (latlng: Latlng, i: number) => {
Expand All @@ -29,10 +29,13 @@ const CandidatesMapBody = ({candidates}: {candidates: CandidatesInfo[]}) => {
<div className={styles.container}>
<Map className={styles.map} center={centerMarker} level={2}>
{candidates.map((candidate, i) => (
<CustomOverlayMap key={`${candidate.placeName}-${candidate.latlng}-${i}`} position={candidate.latlng}>
<CustomOverlayMap
key={`${candidate.placeInfo.placeName}-${candidate.placeInfo.latlng}-${i}`}
position={candidate.placeInfo.latlng}
>
<div
className={`pin ${selectedPinIndex === i ? 'active' : ''}`}
onClick={() => handleMapMarkerClick(candidate.latlng, i)}
onClick={() => handleMapMarkerClick(candidate.placeInfo.latlng, i)}
>
{selectedPinIndex === i ? <MapPinActive number={i + 1} /> : <MapPinNumber number={i + 1} />}
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {CandidatesSlideProps} from '@/types/vote';
const CandidatesSlide = ({candidates, setSelectedPinIndex, setCenterMarker, swiperRef}: CandidatesSlideProps) => {
const handleSlideChange = (swiper: SwiperClass) => {
const activeCandidate = candidates[swiper.activeIndex];
setCenterMarker(activeCandidate.latlng);
setCenterMarker(activeCandidate.placeInfo.latlng);
setSelectedPinIndex(swiper.activeIndex);
};

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
@use '@/sass' as *;

.container {
display: flex;
flex-direction: column;

width: 100%;
// margin: 15px 0;
padding: 15px 8px 50px;

// background-color: aqua;
// z-index: 102;
}

.dayBox {
@include typography(bodyLarge);
padding: 12px 0;

display: flex;
justify-content: space-between;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import {Button, Checkbox} from '@chakra-ui/react';

import styles from './AddToJourney.module.scss';

const AddToJourney = () => {
const days = [1, 2, 3];

return (
<div className={styles.container}>
{days.map((day, i) => (
<label htmlFor={`${i + 1}`} className={styles.dayBox}>
<span>DAY {day}</span>
<Checkbox id={`${i + 1}`} variant='candidateCheckbox' boxSize='1.7rem' />
</label>
))}
<Button variant='CTAButton'>일정에 추가하기</Button>
</div>
);
};

export default AddToJourney;
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import CreateVoteModal from '../../CreateVoteModal/CreateVoteModal';

import {AlertModalProps, VoteMeatballProps} from '@/types/vote';

const VoteMeatball = ({state, title, isZeroCandidates}: VoteMeatballProps) => {
const VoteMeatball = ({state, title, isZeroCandidates, allCandidatesNotVoted}: VoteMeatballProps) => {
const {id: voteId} = useParams();
const setIsCreateModalOpen = useSetRecoilState(isCreateModalOpenState);
const setIsBTOpen = useSetRecoilState(isBottomSlideOpenState);
Expand Down Expand Up @@ -65,7 +65,7 @@ const VoteMeatball = ({state, title, isZeroCandidates}: VoteMeatballProps) => {
</button>
) : (
<button
disabled={isZeroCandidates}
disabled={isZeroCandidates || allCandidatesNotVoted}
onClick={() =>
showAlertModal({
onClickAction: modalConsole,
Expand All @@ -83,7 +83,7 @@ const VoteMeatball = ({state, title, isZeroCandidates}: VoteMeatballProps) => {
<p>투표 제목 수정</p>
</button>

<button disabled={isZeroCandidates} onClick={changeToCandidateSelecting}>
<button disabled={isZeroCandidates || state === '결정완료'} onClick={changeToCandidateSelecting}>
<TrashIcon />
<p>후보 삭제</p>
</button>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@
// gap: 8px;

&__name {
text-align: start;
@include typography(titleSmall);
color: $neutral900;
}
Expand Down Expand Up @@ -110,3 +111,9 @@
}
}
}

.isCandidateSelecting {
* {
color: $neutral300;
}
}
35 changes: 22 additions & 13 deletions src/components/Vote/VoteContent/CandidateCard/CandidateCard.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import {useState} from 'react';
import {FaRegStar, FaStar} from 'react-icons/fa';
import {Link} from 'react-router-dom';
import {useRecoilValue} from 'recoil';

import styles from './CandidateCard.module.scss';
Expand All @@ -11,14 +10,16 @@ import ThirdIcon from '@/assets/voteIcons/rank_3.svg?react';
import AddDayIcon from '@/assets/voteIcons/vote_addDay.svg?react';
import {isCandidateSelectingState} from '@/recoil/vote/alertModal';

import AddToJourney from '../../VoteBottomSlideContent/AddToJourney/AddToJourney';
import VotedUserList from '../../VoteBottomSlideContent/VotedUserList/VotedUserList';

import {CandidateCardProps} from '@/types/vote';

const CandidateCard = ({onBottomSlideOpen, candidate, showResults, index, isMapStyle}: CandidateCardProps) => {
const [isVoted, setIsVoted] = useState(false);
const isCandidateSelecting = useRecoilValue(isCandidateSelectingState);
const voteCounts = candidate.voteUserId.length;

const placeInfo = candidate.placeInfo;

const getRankClassName = (index: number) => {
switch (index) {
Expand Down Expand Up @@ -50,7 +51,7 @@ const CandidateCard = ({onBottomSlideOpen, candidate, showResults, index, isMapS
const RankIcon = showResults && getRankIcon(index);

const voteStarIcon = () => {
if (isVoted) return <FaStar style={{color: '#fee500'}} />;
if (isVoted || candidate.amIVoted) return <FaStar style={{color: '#fee500'}} />;
else if (isMapStyle) return <FaStar style={{color: '#e3e5e5'}} />;
else return <FaRegStar />;
};
Expand All @@ -65,7 +66,7 @@ const CandidateCard = ({onBottomSlideOpen, candidate, showResults, index, isMapS

return (
<div className={`${styles.container} ${rankClassName} candidateCard ${isMapStyle ? styles.isMapStyle : ''}`}>
<img src={candidate.imageURL} alt={candidate.placeName} />
<img src={placeInfo.placeImageURL} alt={placeInfo.placeName} />
{RankIcon && (
<div className={styles.rankTag}>
<RankIcon />
Expand All @@ -74,14 +75,18 @@ const CandidateCard = ({onBottomSlideOpen, candidate, showResults, index, isMapS

<div className={styles.main}>
<div className={styles.main__contextBox}>
<Link to='' className={styles.main__contextBox__name}>
{candidate.placeName} {'>'}
</Link>
<button
className={styles.main__contextBox__name}
// onClick={navigate로 넣기}
disabled={isCandidateSelecting}
>
{placeInfo.placeName.length >= 10 ? placeInfo.placeName.slice(0, 10) + ' ⋯' : placeInfo.placeName}
</button>

<div className={styles.main__contextBox__category}>
{candidate.category}
{placeInfo.category}
{'ꞏ'}
{candidate.location}
{placeInfo.location}
</div>

{/* 일정 담기
Expand All @@ -90,15 +95,19 @@ const CandidateCard = ({onBottomSlideOpen, candidate, showResults, index, isMapS
있 : 바텀시트 -> 일정 추가api -> 시트close, 완료 토스트
*/}

{showResults && (
<button className={styles.main__contextBox__addDay}>
{showResults && onBottomSlideOpen && (
<button onClick={() => onBottomSlideOpen(<AddToJourney />)} className={styles.main__contextBox__addDay}>
<AddDayIcon /> 일정에 담기
</button>
)}
</div>
<button className={styles.main__voteBox} onClick={onVoteBoxClick} disabled={isCandidateSelecting || isMapStyle}>
<button
className={`${styles.main__voteBox} ${isCandidateSelecting && styles.isCandidateSelecting}`}
onClick={onVoteBoxClick}
disabled={isCandidateSelecting || isMapStyle || isCandidateSelecting}
>
<div className={styles.main__voteBox__star}>{voteStarIcon()}</div>
<div className={styles.main__voteBox__vote}>{showResults ? voteCounts : '투표'}</div>
<div className={styles.main__voteBox__vote}>{showResults ? candidate.voteCount : '투표'}</div>
</button>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
@use "@/sass" as *;
@use '@/sass' as *;

.container {
margin-bottom: 50px;
Expand All @@ -18,7 +18,7 @@
&__memo {
display: flex;
align-items: center;
gap: 6px;
gap: 8px;

margin-top: 16px;
&__text {
Expand Down
Loading