Skip to content
Snippets Groups Projects
Commit a1ec1d63 authored by 이권민's avatar 이권민
Browse files

[ADD] drag for monthly calendar

parents 84bd8075 e5cbb7b6
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";
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 toggleHour = (day, hour) => {
const daySchedule = schedule[day] || [];
if (daySchedule.includes(hour)) {
setSchedule({
...schedule,
[day]: daySchedule.filter((h) => h !== hour),
});
} else {
setSchedule({
...schedule,
[day]: [...daySchedule, hour],
const handlePrevWeek = () => {
setCurrentWeekIndex(Math.max(0, currentWeekIndex - 1));
};
const handleNextWeek = () => {
setCurrentWeekIndex(Math.min(weeks.length - 1, currentWeekIndex + 1));
};
useEffect(() => {
const start = new Date(startDate);
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 (
<table className="calendar-container">
<thead>
<tr>
{["", ...days].map((day) => (
<th key={day}>{day}</th>
))}
</tr>
</thead>
<tbody>
{hours.map((hour) => (
<tr key={hour}>
<td>{hour}</td>
{days.map((day) => (
<td
key={day}
className={schedule[day]?.includes(hour) ? "selected" : ""}
onClick={() => toggleHour(day, hour)}
/>
<div>
<button onClick={handlePrevWeek} disabled={currentWeekIndex === 0}>
Prev Week
</button>
<button
onClick={handleNextWeek}
disabled={currentWeekIndex === weeks.length - 1}
>
Next Week
</button>
<table className="calendar-container">
<thead>
<tr>
<th>Time</th>
{weekDates.map((date) => (
<th key={date.toISOString()}>
{date.getMonth() + 1}/{date.getDate()} ({days[date.getDay()]})
</th>
))}
</tr>
))}
</tbody>
</table>
</thead>
<tbody>
{[...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";
import "../styles/ResultEnd.css";
import "../styles/CalendarWeek.css";
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 [completedPeopleNum, setcompletedPeopleNum] = useState(3);
const [completedPeopleNum, setcompletedPeopleNum] = useState(
meetingData.currentParticipants
);
const [selectedDate, setSelectedDate] = useState("");
//const [possibleDates, setpossibleDate] = useState("");
const possibleDates = ["23.07.01 ~~~", "23.07.02 ~~~", "23.07.03 ~~~"];
const [completedTasks, setCompletedTasks] = useState(3);
const [totalTasks, setTotalTasks] = useState(7);
const [timeLeft, setTimeLeft] = useState("00:37:30");
const [hoveredInfo, setHoveredInfo] = useState(null);
const handleDateChange = (event) => {
setSelectedDate(event.target.value);
};
return (
<div style={{ textAlign: "center", width: "50%" }}>
<h1 className="title-box">{title}</h1>
<p>투표가 종료되었습니다.</p>
<p style={{ color: "blue" }}>약속 시간은 {resultTime}입니다.</p>
<div>
<h2>총 참여한 인원수</h2>
<p>{completedPeopleNum}</p>
<form className="form-container">
{possibleDates.map((date, index) => (
<label key={index}>
<input
type="radio"
name="date"
value={date}
checked={selectedDate === date}
onChange={handleDateChange}
/>
{date}
</label>
))}
</form>
<button
style={{ marginTop: "20px", padding: "10px 20px" }}
onClick={() => console.log(selectedDate)}
>
이 시간으로 정했어요
</button>
<button style={{ marginTop: "10px", padding: "10px 20px" }}>
랜덤으로 약속 시간 확정하기
</button>
</div>
<CalendarWeek className="calander" />
<span
className="possibleMan"
style={{
position: "absolute",
marginTop: "100px",
marginLeft: "100px",
}}
>
<div style={{ textAlign: "center" }}>가능한 사람</div>
<div
style={{ textAlign: "center", display: "flex", flexDirection: "column" }}
>
<span className="row1">
<h1 className="title-box">{title}</h1>
<p>투표가 종료되었습니다.</p>
<p style={{ color: "blue" }}>약속 시간은 {resultTime}입니다.</p>
<div>
<h2>총 참여한 인원수</h2>
<p>{completedPeopleNum}</p>
<form className="form-container">
{possibleDates.map((date, index) => (
<label key={index}>
<input
type="radio"
name="date"
value={date}
checked={selectedDate === date}
onChange={handleDateChange}
/>
{date}
</label>
))}
</form>
<button
style={{ marginTop: "20px", padding: "10px 20px" }}
onClick={() => console.log(selectedDate)}
>
이 시간으로 정했어요
</button>
<button style={{ marginTop: "10px", padding: "10px 20px" }}>
랜덤으로 약속 시간 확정하기
</button>
</div>
<span style={{ display: "flex", flexDirection: "row" }}>
<div className="calander-container">
<CalendarWeek
participants={meetingData.participants}
startDate={meetingData.startDate}
endDate={meetingData.endDate}
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>
</div>
);
......
//결과 확인 페이지
import React, { useState } from "react";
import React, { useState, useEffect } from "react";
import CalendarWeek from "./CalendarWeek";
import { useNavigate } from "react-router-dom";
import "../styles/ResultMake.css";
function ResultMakeForm() {
const [title, setTitle] = useState("Title 예시");
const [completedTasks, setCompletedTasks] = useState(3);
const [totalTasks, setTotalTasks] = useState(7);
const [timeLeft, setTimeLeft] = useState("00:37:30");
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 [currentParticipants, setcurrentParticipands] = useState(
meetingData.currentParticipants
);
const [maxParticipants, setmaxParticipants] = useState(
meetingData.maxParticipants
);
const [timeLeft, setTimeLeft] = useState("");
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 = () => {
navigate("/meetinginfo/linkpage");
};
return (
<div>
<h1 className="title-box">{title}</h1>
<div>
현재 완료한 일업수 {completedTasks} / {totalTasks}
<>
<div className="column-container">
<h1 className="title-box">{title}</h1>
<div>
현재 완료한 인원수 {currentParticipants} / {maxParticipants}
</div>
<div>종료까지 남은 시간 {timeLeft}</div>
<button onClick={handleEdit}>수정하기</button>
<button
onClick={() => {
navigate("/resultend");
}}
>
투표 종료하기
</button>
</div>
<div>종료까지 남은 시간 {timeLeft}</div>
<button onClick={handleEdit}>수정하기</button>
<button
onClick={() => {
navigate("/resultend");
}}
>
투표 종료하기
</button>
<CalendarWeek />
<span className="mostTime">
<div style={{ textAlign: "center" }}>
가장 많은 사람들이 가능한 일정
<span className="flex-row">
<div className="calander-container">
<CalendarWeek
participants={meetingData.participants}
startDate={meetingData.startDate}
endDate={meetingData.endDate}
maxParticipants={meetingData.maxParticipants}
hoveredInfo={hoveredInfo}
setHoveredInfo={setHoveredInfo}
/>
</div>
<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>
<ol>//일정 5개 나열</ol>
</span>
<span className="possibleMan">
<div style={{ textAlign: "center" }}>가능한 사람</div>
</span>
</div>
</>
);
}
export default ResultMakeForm;
......@@ -3,7 +3,9 @@ import "../styles/HomeMake.css";
function ResultEnd() {
return (
<div className="center-container">
<div
style={{ display: "flex", alignItems: "center", flexDirection: "column" }}
>
<ResultEndForm />
</div>
);
......
import ResultMakeForm from "../components/ResultMakeForm";
import "../styles/HomeMake.css";
function ResultMake() {
return (
<div className="center-container">
<div
style={{ display: "flex", alignItems: "center", flexDirection: "column" }}
>
<ResultMakeForm />
</div>
);
......
......@@ -35,7 +35,7 @@ h2 {
margin: 0;
}
.timeStartEnd{
.timeStartEnd {
display: flex;
align-items: center;
justify-content: space-between;
......@@ -67,13 +67,13 @@ button:hover {
background-color: #2980b9;
}
.header{
.header {
display: flex;
gap: 100px;
}
.purpose-selector{
margin : 20px;
.purpose-selector {
margin: 20px;
}
.calendarTable {
margin: 0 auto; /* 가운데 정렬을 위해 margin을 auto로 설정합니다. */
......@@ -111,7 +111,6 @@ button:hover {
border-style: solid;
padding: 20px;
}
.title-box {
text-align: center;
border-width: 1px;
......
......@@ -6,5 +6,18 @@
.calendar {
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