Select Git revision
-
HyunjinNoh authoredHyunjinNoh authored
ResultMakeForm.js 8.87 KiB
import React, { useState, useEffect } from "react";
import CalendarWeek from "./CalendarWeek";
import PasswordModal from "./PasswordModal";
import { useNavigate, useParams } from "react-router-dom";
import axios from "axios";
import "../styles/ResultMake.css";
function ResultMakeForm() {
const [meetingData, setMeetingData] = useState(null);
const { meeting_id } = useParams();
const navigate = useNavigate();
const [timeLeft, setTimeLeft] = useState("");
const [hoveredInfo, setHoveredInfo] = useState(null);
const [isLoading, setIsLoading] = useState(true);
const [isModalOpen, setIsModalOpen] = useState(false);
const [mostAvailableDates, setMostAvailableDates] = useState([]);
const [topAvailableDates, setTopAvailableDates] = useState([]);
useEffect(() => {
if (meetingData && meetingData.participants) {
calculateTopAvailableDates();
}
}, [meetingData]);
const calculateTopAvailableDates = () => {
let dateAvailability = {};
meetingData.participants.forEach((participant) => {
participant.availableSchedules.forEach((schedule) => {
if (!dateAvailability[schedule.availableDate]) {
dateAvailability[schedule.availableDate] = {};
}
schedule.availableTimes.forEach((time) => {
if (!dateAvailability[schedule.availableDate][time]) {
dateAvailability[schedule.availableDate][time] = 0;
}
dateAvailability[schedule.availableDate][time]++;
});
});
});
let dateCounts = Object.entries(dateAvailability).map(([date, times]) => {
let maxCount = Math.max(...Object.values(times));
let count = Object.values(times).filter((val) => val === maxCount).length;
return { date, count, maxCount };
});
dateCounts.sort((a, b) => b.maxCount - a.maxCount || b.count - a.count);
setTopAvailableDates(dateCounts.slice(0, 5));
};
useEffect(() => {
if (meetingData && meetingData.participants) {
calculateMostAvailableDates();
}
}, [meetingData]);
const calculateMostAvailableDates = () => {
let dateAvailabilityCount = {};
meetingData.participants.forEach((participant) => {
participant.availableSchedules.forEach((schedule) => {
if (!dateAvailabilityCount[schedule.availableDate]) {
dateAvailabilityCount[schedule.availableDate] = new Set();
}
schedule.availableTimes.forEach((time) => {
dateAvailabilityCount[schedule.availableDate].add(time);
});
});
});
const sortedDates = Object.entries(dateAvailabilityCount)
.map(([date, times]) => ({ date, count: times.size }))
.sort((a, b) => b.count - a.count)
.slice(0, 5);
setMostAvailableDates(sortedDates);
};
useEffect(() => {
const fetchMeetingData = async () => {
setIsLoading(true);
try {
const response = await axios.get(`/meetings/${meeting_id}/details`, {
withCredentials: true,
});
setMeetingData(response.data);
setIsLoading(false);
} catch (error) {
console.error("데이터 로딩 에러:", error);
setIsLoading(false);
}
};
fetchMeetingData();
}, [meeting_id]);
useEffect(() => {
const calculateTimeLeft = () => {
if (!meetingData || !meetingData.voteExpiresAt) {
return { days: 0, hours: 0, minutes: 0, seconds: 0 };
}
// 서버의 voteExpiresAt 값을 한국 시간대로 가정하여 파싱
const voteExpiresKST = new Date(meetingData.voteExpiresAt);
// 현재 로컬 시간 (브라우저 시간대를 한국 시간대로 조정)
const now = new Date();
const nowKST = new Date(now.getTime() + 9 * 60 * 60 * 1000); // UTC 시간에 9시간을 더해 KST로 조정
// 남은 시간 계산
const difference = voteExpiresKST - nowKST;
if (difference > 0) {
return {
days: Math.floor(difference / (1000 * 60 * 60 * 24)),
hours: Math.floor((difference / (1000 * 60 * 60)) % 24),
minutes: Math.floor((difference / 1000 / 60) % 60),
seconds: Math.floor((difference / 1000) % 60),
};
} else {
return { days: 0, hours: 0, minutes: 0, seconds: 0 };
}
};
const updateTimer = () => {
const newTimeLeft = calculateTimeLeft();
const formattedTime = `${
newTimeLeft.days ? newTimeLeft.days + "일 " : ""
}${newTimeLeft.hours ? newTimeLeft.hours + "시간 " : ""}${
newTimeLeft.minutes ? newTimeLeft.minutes + "분 " : ""
}${newTimeLeft.seconds ? newTimeLeft.seconds + "초" : ""}`;
setTimeLeft(formattedTime);
};
const timerId = setInterval(updateTimer, 1000);
return () => clearInterval(timerId);
}, [meetingData]);
const handleEdit = async () => {
try {
const response = await axios.get(`/meetings/${meeting_id}/`);
const {
startDate,
endDate,
availableVotingStartTime,
availableVotingEndTime,
} = response.data;
try {
const scheduleResponse = await axios.get(
`/meetings/${meeting_id}/my/schedules`
);
navigate(`/homeparticipate/${meeting_id}/usertimeinfo/`, {
state: {
id: meeting_id,
startTime: availableVotingStartTime,
endTime: availableVotingEndTime,
startDate,
endDate,
schedules: scheduleResponse.data.schedules,
},
});
} catch (error) {
console.error(error);
}
} catch (error) {
console.error(error);
}
};
const closeMeeting = () => {
setIsModalOpen(true);
};
const handleModalClose = () => {
setIsModalOpen(false);
};
const handlePasswordSubmit = async (password) => {
setIsModalOpen(false);
try {
await axios.patch(`/meetings/${meeting_id}/close`, {
adminPassword: password,
});
navigate(`/resultend/${meeting_id}`);
} catch (error) {
if (error.response && error.response.status === 401) {
alert("비밀번호가 틀렸습니다.");
} else if (error.response && error.response.status === 409) {
alert("이미 종료된 투표입니다.");
} else {
console.error("오류 발생:", error);
}
}
};
return (
<>
{isLoading ? (
<div>Loading...</div>
) : (
<div className="column-container">
<h1 className="title-box">{meetingData?.title}</h1>
{meetingData.maxParticipants && (
<div>
현재 완료한 인원수 : {meetingData?.currentParticipants} /{" "}
{meetingData?.maxParticipants}
</div>
)}
{!meetingData.maxParticipants && (
<div>현재 완료한 인원수 : {meetingData?.currentParticipants}</div>
)}
<div>종료까지 남은 시간 : {timeLeft}</div>
<button onClick={handleEdit}>수정하기</button>
<button onClick={closeMeeting}>투표 종료하기</button>
</div>
)}
{meetingData && (
<div className="flex-row">
<CalendarWeek
participants={meetingData.participants}
startDate={meetingData.startDate}
endDate={meetingData.endDate}
currentParticipants={meetingData.currentParticipants}
maxParticipants={meetingData.maxParticipants}
hoveredInfo={hoveredInfo}
setHoveredInfo={setHoveredInfo}
availableVotingStartTime={meetingData.availableVotingStartTime}
availableVotingEndTime={meetingData.availableVotingEndTime}
/>
</div>
)}
<PasswordModal
isOpen={isModalOpen}
onRequestClose={handleModalClose}
onSubmit={handlePasswordSubmit}
/>
<div className="flex-bottom-container">
<span className="mostTime">
<strong style={{ textAlign: "center" }}>
가장 많은 사람들이 가능한 일정
</strong>
<ol>
{topAvailableDates.map((dateInfo, index) => (
<li key={index}>
{dateInfo.date} ({dateInfo.maxCount}명이 가능한 시간대:{" "}
{dateInfo.count}개 )
</li>
))}
</ol>
</span>
<span className="possible">
{!hoveredInfo && (
<div>
<strong>가능한 사람들이 표시됩니다.</strong>
<p>마우스를 달력 위에 올려보세요!</p>
</div>
)}
{hoveredInfo && (
<div style={{ textAlign: "center" }}>
<strong>
{hoveredInfo.date} {hoveredInfo.time}에 가능한 사람:
</strong>
<ul>
{hoveredInfo.participants.map((name) => (
<li key={name}>{name}</li>
))}
</ul>
</div>
)}
</span>
</div>
</>
);
}
export default ResultMakeForm;