Skip to content
Snippets Groups Projects
Commit e5cbb7b6 authored by 준현 강's avatar 준현 강
Browse files

[ADD] weekly calender + hover updated

parent 05c70d15
No related branches found
No related tags found
No related merge requests found
import React, { useState } from "react"; import React, { useState, useEffect } from "react";
import "../styles/CalendarWeek.css"; import "../styles/CalendarWeek.css";
const hours = [...Array(24).keys()].map((i) => `${i}:00`); // 0:00 to 23:00
const days = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"];
const CalendarWeek = () => { const days = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
const CalendarWeek = ({
participants,
startDate,
endDate,
maxParticipants,
hoveredInfo,
setHoveredInfo,
}) => {
const [currentWeekIndex, setCurrentWeekIndex] = useState(0);
const [weeks, setWeeks] = useState([]);
const [schedule, setSchedule] = useState({}); const [schedule, setSchedule] = useState({});
const toggleHour = (day, hour) => { const handlePrevWeek = () => {
const daySchedule = schedule[day] || []; setCurrentWeekIndex(Math.max(0, currentWeekIndex - 1));
if (daySchedule.includes(hour)) { };
setSchedule({
...schedule, const handleNextWeek = () => {
[day]: daySchedule.filter((h) => h !== hour), setCurrentWeekIndex(Math.min(weeks.length - 1, currentWeekIndex + 1));
}); };
} else {
setSchedule({ useEffect(() => {
...schedule, const start = new Date(startDate);
[day]: [...daySchedule, hour], start.setDate(
start.getDate() - (start.getDay() === 0 ? 6 : start.getDay() - 1)
);
const end = new Date(endDate);
let weeksTemp = [];
while (start <= end) {
const weekStart = new Date(start);
const weekDates = Array.from({ length: 7 }).map((_, index) => {
const date = new Date(weekStart);
date.setDate(date.getDate() + index);
return date;
}); });
weeksTemp.push(weekDates);
start.setDate(start.getDate() + 7);
} }
setWeeks(weeksTemp);
let newSchedule = {};
participants.forEach((participant) => {
participant.availableSchedules.forEach(
({ availableDate, availableTimes }) => {
const date = new Date(availableDate);
availableTimes.forEach((time) => {
const hour = Math.floor(time / 2);
const minute = (time % 2) * 30;
const timeString = `${hour.toString().padStart(2, "0")}:${minute
.toString()
.padStart(2, "0")}`;
const dateString = `${date.getFullYear()}-${(date.getMonth() + 1)
.toString()
.padStart(2, "0")}-${date.getDate().toString().padStart(2, "0")}`;
if (!newSchedule[dateString]) {
newSchedule[dateString] = [];
}
newSchedule[dateString].push({
time: timeString,
name: participant.name,
});
});
}
);
});
setSchedule(newSchedule);
}, [participants, startDate, endDate]);
const calculateOpacity = (dateString, timeString) => {
const availableCount =
schedule[dateString]?.filter((s) => s.time === timeString).length || 0;
return 100 - (availableCount / maxParticipants) * 100;
};
const handleMouseEnter = (dateString, timeString) => {
const availableParticipants =
schedule[dateString]
?.filter((s) => s.time === timeString)
.map((s) => s.name) || [];
setHoveredInfo({
date: dateString,
time: timeString,
participants: availableParticipants,
});
}; };
const handleMouseLeave = () => {
setHoveredInfo(null);
};
const weekDates = weeks[currentWeekIndex] || [];
return ( return (
<table className="calendar-container"> <div>
<thead> <button onClick={handlePrevWeek} disabled={currentWeekIndex === 0}>
<tr> Prev Week
{["", ...days].map((day) => ( </button>
<th key={day}>{day}</th> <button
))} onClick={handleNextWeek}
</tr> disabled={currentWeekIndex === weeks.length - 1}
</thead> >
<tbody> Next Week
{hours.map((hour) => ( </button>
<tr key={hour}> <table className="calendar-container">
<td>{hour}</td> <thead>
{days.map((day) => ( <tr>
<td <th>Time</th>
key={day} {weekDates.map((date) => (
className={schedule[day]?.includes(hour) ? "selected" : ""} <th key={date.toISOString()}>
onClick={() => toggleHour(day, hour)} {date.getMonth() + 1}/{date.getDate()} ({days[date.getDay()]})
/> </th>
))} ))}
</tr> </tr>
))} </thead>
</tbody> <tbody>
</table> {[...Array(48).keys()].map((timeSlot) => {
const hour = Math.floor(timeSlot / 2);
const minute = (timeSlot % 2) * 30;
return (
<tr key={timeSlot}>
<td>
{hour.toString().padStart(2, "0") +
":" +
minute.toString().padStart(2, "0")}
</td>
{weekDates.map((date) => {
const dateString = `${date.getFullYear()}-${(
date.getMonth() + 1
)
.toString()
.padStart(2, "0")}-${date
.getDate()
.toString()
.padStart(2, "0")}`;
const timeString = `${hour
.toString()
.padStart(2, "0")}:${minute.toString().padStart(2, "0")}`;
const opacity = calculateOpacity(dateString, timeString);
const cellStyle = { opacity: `${opacity}%` };
return (
<td
key={`${date.toISOString()}-${timeString}`}
style={cellStyle}
onMouseEnter={() =>
handleMouseEnter(dateString, timeString)
}
onMouseLeave={handleMouseLeave}
/>
);
})}
</tr>
);
})}
</tbody>
</table>
{hoveredInfo && (
<div className="possibleMan" style={{ textAlign: "center" }}>
<strong>가능한 사람:</strong>
<ul>
{hoveredInfo.participants.map((name) => (
<li key={name}>{name}</li>
))}
</ul>
</div>
)}
</div>
); );
}; };
......
...@@ -3,61 +3,148 @@ import React, { useState } from "react"; ...@@ -3,61 +3,148 @@ import React, { useState } from "react";
import "../styles/ResultEnd.css"; import "../styles/ResultEnd.css";
import "../styles/CalendarWeek.css"; import "../styles/CalendarWeek.css";
export default function ResultEndForm() { export default function ResultEndForm() {
const [title, setTitle] = useState("Title 예시"); const meetingData = {
id: "1ag123jkF1",
title: "제목",
purpose: "STUDY",
startDate: "2023-12-20",
endDate: "2024-1-07",
currentParticipants: 2,
maxParticipants: 4,
voteExpiresAt: "2023-12-25T03:24:00",
isClosed: false,
participants: [
{
name: "test1",
availableSchedules: [
{
availableDate: "2023-12-20",
availableTimes: [6, 7, 8, 9, 14, 15, 16, 17],
},
{
availableDate: "2023-12-21",
availableTimes: [16, 17],
},
{
availableDate: "2023-12-22",
availableTimes: [24, 25, 26, 27, 28, 40, 41, 42],
},
],
},
{
name: "test2",
availableSchedules: [
{
availableDate: "2023-12-22",
availableTimes: [38, 40],
},
],
},
{
name: "test3",
availableSchedules: [
{
availableDate: "2023-12-22",
availableTimes: [38, 40, 41, 42],
},
],
},
{
name: "test4",
availableSchedules: [
{
availableDate: "2023-12-22",
availableTimes: [38],
},
],
},
{
name: "test5",
availableSchedules: [
{
availableDate: "2023-12-22",
availableTimes: [38],
},
],
},
],
};
const [title, setTitle] = useState(meetingData.title);
const [resultTime, setresultTIme] = useState("00:37:30"); const [resultTime, setresultTIme] = useState("00:37:30");
const [completedPeopleNum, setcompletedPeopleNum] = useState(3); const [completedPeopleNum, setcompletedPeopleNum] = useState(
meetingData.currentParticipants
);
const [selectedDate, setSelectedDate] = useState(""); const [selectedDate, setSelectedDate] = useState("");
//const [possibleDates, setpossibleDate] = useState("");
const possibleDates = ["23.07.01 ~~~", "23.07.02 ~~~", "23.07.03 ~~~"]; const possibleDates = ["23.07.01 ~~~", "23.07.02 ~~~", "23.07.03 ~~~"];
const [completedTasks, setCompletedTasks] = useState(3); const [hoveredInfo, setHoveredInfo] = useState(null);
const [totalTasks, setTotalTasks] = useState(7);
const [timeLeft, setTimeLeft] = useState("00:37:30");
const handleDateChange = (event) => { const handleDateChange = (event) => {
setSelectedDate(event.target.value); setSelectedDate(event.target.value);
}; };
return ( return (
<div style={{ textAlign: "center", width: "50%" }}> <div
<h1 className="title-box">{title}</h1> style={{ textAlign: "center", display: "flex", flexDirection: "column" }}
<p>투표가 종료되었습니다.</p> >
<p style={{ color: "blue" }}>약속 시간은 {resultTime}입니다.</p> <span className="row1">
<div> <h1 className="title-box">{title}</h1>
<h2>총 참여한 인원수</h2> <p>투표가 종료되었습니다.</p>
<p>{completedPeopleNum}</p> <p style={{ color: "blue" }}>약속 시간은 {resultTime}입니다.</p>
<form className="form-container"> <div>
{possibleDates.map((date, index) => ( <h2>총 참여한 인원수</h2>
<label key={index}> <p>{completedPeopleNum}</p>
<input <form className="form-container">
type="radio" {possibleDates.map((date, index) => (
name="date" <label key={index}>
value={date} <input
checked={selectedDate === date} type="radio"
onChange={handleDateChange} name="date"
/> value={date}
{date} checked={selectedDate === date}
</label> onChange={handleDateChange}
))} />
</form> {date}
<button </label>
style={{ marginTop: "20px", padding: "10px 20px" }} ))}
onClick={() => console.log(selectedDate)} </form>
> <button
이 시간으로 정했어요 style={{ marginTop: "20px", padding: "10px 20px" }}
</button> onClick={() => console.log(selectedDate)}
<button style={{ marginTop: "10px", padding: "10px 20px" }}> >
랜덤으로 약속 시간 확정하기 이 시간으로 정했어요
</button> </button>
</div> <button style={{ marginTop: "10px", padding: "10px 20px" }}>
<CalendarWeek className="calander" /> 랜덤으로 약속 시간 확정하기
<span </button>
className="possibleMan" </div>
style={{ <span style={{ display: "flex", flexDirection: "row" }}>
position: "absolute", <div className="calander-container">
marginTop: "100px", <CalendarWeek
marginLeft: "100px", participants={meetingData.participants}
}} startDate={meetingData.startDate}
> endDate={meetingData.endDate}
<div style={{ textAlign: "center" }}>가능한 사람</div> maxParticipants={meetingData.maxParticipants}
hoveredInfo={hoveredInfo}
setHoveredInfo={setHoveredInfo}
/>
</div>
<div className="row2">
<span className="possible">
{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>
</span>
</span> </span>
</div> </div>
); );
......
//결과 확인 페이지 //결과 확인 페이지
import React, { useState } from "react"; import React, { useState, useEffect } from "react";
import CalendarWeek from "./CalendarWeek"; import CalendarWeek from "./CalendarWeek";
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";
import "../styles/ResultMake.css";
function ResultMakeForm() { function ResultMakeForm() {
const [title, setTitle] = useState("Title 예시"); const meetingData = {
const [completedTasks, setCompletedTasks] = useState(3); id: "1ag123jkF1",
const [totalTasks, setTotalTasks] = useState(7); title: "제목",
const [timeLeft, setTimeLeft] = useState("00:37:30"); purpose: "STUDY",
startDate: "2023-12-20",
endDate: "2024-1-07",
currentParticipants: 2,
maxParticipants: 4,
voteExpiresAt: "2023-12-25T03:24:00",
isClosed: false,
participants: [
{
name: "test1",
availableSchedules: [
{
availableDate: "2023-12-20",
availableTimes: [6, 7, 8, 9, 14, 15, 16, 17],
},
{
availableDate: "2023-12-21",
availableTimes: [16, 17],
},
{
availableDate: "2023-12-22",
availableTimes: [24, 25, 26, 27, 28, 40, 41, 42],
},
],
},
{
name: "test2",
availableSchedules: [
{
availableDate: "2023-12-22",
availableTimes: [38, 40],
},
],
},
{
name: "test3",
availableSchedules: [
{
availableDate: "2023-12-22",
availableTimes: [38, 40, 41, 42],
},
],
},
{
name: "test4",
availableSchedules: [
{
availableDate: "2023-12-22",
availableTimes: [38],
},
],
},
{
name: "test5",
availableSchedules: [
{
availableDate: "2023-12-22",
availableTimes: [38],
},
],
},
],
};
const [title, setTitle] = useState(meetingData.title);
const [currentParticipants, setcurrentParticipands] = useState(
meetingData.currentParticipants
);
const [maxParticipants, setmaxParticipants] = useState(
meetingData.maxParticipants
);
const [timeLeft, setTimeLeft] = useState("");
const navigate = useNavigate(); const navigate = useNavigate();
const [hoveredInfo, setHoveredInfo] = useState(null);
// 타이머를 시작하고 관리하는 로직 // 타이머를 시작하고 관리하는 로직
useEffect(() => {
const calculateTimeLeft = () => {
const now = new Date();
const voteExpires = new Date(meetingData.voteExpiresAt);
const difference = voteExpires - now;
let timeLeft = {};
if (difference > 0) {
timeLeft = {
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),
};
}
return timeLeft;
};
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);
};
updateTimer();
const timerId = setInterval(updateTimer, 1000);
return () => clearInterval(timerId); // 컴포넌트가 언마운트될 때 인터벌을 정리합니다.
}, [meetingData.voteExpiresAt]);
const handleEdit = () => { const handleEdit = () => {
navigate("/meetinginfo/linkpage"); navigate("/meetinginfo/linkpage");
}; };
return ( return (
<div> <>
<h1 className="title-box">{title}</h1> <div className="column-container">
<div> <h1 className="title-box">{title}</h1>
현재 완료한 일업수 {completedTasks} / {totalTasks} <div>
현재 완료한 인원수 {currentParticipants} / {maxParticipants}
</div>
<div>종료까지 남은 시간 {timeLeft}</div>
<button onClick={handleEdit}>수정하기</button>
<button
onClick={() => {
navigate("/resultend");
}}
>
투표 종료하기
</button>
</div> </div>
<div>종료까지 남은 시간 {timeLeft}</div> <span className="flex-row">
<button onClick={handleEdit}>수정하기</button> <div className="calander-container">
<button <CalendarWeek
onClick={() => { participants={meetingData.participants}
navigate("/resultend"); startDate={meetingData.startDate}
}} endDate={meetingData.endDate}
> maxParticipants={meetingData.maxParticipants}
투표 종료하기 hoveredInfo={hoveredInfo}
</button> setHoveredInfo={setHoveredInfo}
<CalendarWeek /> />
<span className="mostTime"> </div>
<div style={{ textAlign: "center" }}>
가장 많은 사람들이 가능한 일정 <div className="row-container">
<span className="mostTime">
<div style={{ textAlign: "center" }}>
가장 많은 사람들이 가능한 일정
</div>
<ol>//일정 5개 나열</ol>
</span>
<span className="possibleMan">
{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> </div>
<ol>//일정 5개 나열</ol>
</span>
<span className="possibleMan">
<div style={{ textAlign: "center" }}>가능한 사람</div>
</span> </span>
</div> </>
); );
} }
export default ResultMakeForm; export default ResultMakeForm;
...@@ -3,7 +3,9 @@ import "../styles/HomeMake.css"; ...@@ -3,7 +3,9 @@ import "../styles/HomeMake.css";
function ResultEnd() { function ResultEnd() {
return ( return (
<div className="center-container"> <div
style={{ display: "flex", alignItems: "center", flexDirection: "column" }}
>
<ResultEndForm /> <ResultEndForm />
</div> </div>
); );
......
import ResultMakeForm from "../components/ResultMakeForm"; import ResultMakeForm from "../components/ResultMakeForm";
import "../styles/HomeMake.css";
function ResultMake() { function ResultMake() {
return ( return (
<div className="center-container"> <div
style={{ display: "flex", alignItems: "center", flexDirection: "column" }}
>
<ResultMakeForm /> <ResultMakeForm />
</div> </div>
); );
......
...@@ -35,7 +35,7 @@ h2 { ...@@ -35,7 +35,7 @@ h2 {
margin: 0; margin: 0;
} }
.timeStartEnd{ .timeStartEnd {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
...@@ -67,13 +67,13 @@ button:hover { ...@@ -67,13 +67,13 @@ button:hover {
background-color: #2980b9; background-color: #2980b9;
} }
.header{ .header {
display: flex; display: flex;
gap: 100px; gap: 100px;
} }
.purpose-selector{ .purpose-selector {
margin : 20px; margin: 20px;
} }
.calendarTable { .calendarTable {
margin: 0 auto; /* 가운데 정렬을 위해 margin을 auto로 설정합니다. */ margin: 0 auto; /* 가운데 정렬을 위해 margin을 auto로 설정합니다. */
...@@ -88,27 +88,6 @@ button:hover { ...@@ -88,27 +88,6 @@ button:hover {
color: black; /* 검정색으로 글자색 지정 */ color: black; /* 검정색으로 글자색 지정 */
} }
.mostTime {
position: absolute;
margin-top: 200px;
margin-left: 20px;
width: 20em;
border-width: 1px;
border-color: black;
border-style: solid;
padding: 20px;
}
.possibleMan {
position: absolute;
margin-left: 20px;
margin-top: 800px;
width: 20em;
border-width: 1px;
border-color: black;
border-style: solid;
padding: 20px;
}
.title-box { .title-box {
text-align: center; text-align: center;
border-width: 1px; border-width: 1px;
......
...@@ -6,5 +6,18 @@ ...@@ -6,5 +6,18 @@
.calendar { .calendar {
border: 1px solid; border: 1px solid;
padding : 10px; padding: 10px;
}
.possible {
margin-left: 0px;
position: fixed;
width: 20em;
border-width: 1px;
border-color: black;
border-style: solid;
padding: 20px;
}
.row {
display: flex;
justify-content: center;
} }
.column-container {
display: flex;
flex-direction: column;
}
.row-container {
display: flex;
}
.flex-row {
display: flex;
flex-direction: row;
}
.calendar-container {
margin-right: 30px;
}
.mostTime {
background-color: white;
position: fixed;
margin-left: 20px;
width: 20em;
border-width: 1px;
border-color: black;
border-style: solid;
padding: 20px;
}
.possibleMan {
background-color: white;
position: fixed;
margin-left: 20px;
margin-top: 200px;
width: 20em;
border-width: 1px;
border-color: black;
border-style: solid;
padding: 20px;
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment