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

[EDIT] CSS

parents 7481ae31 3f1f32be
Branches
No related tags found
No related merge requests found
Showing
with 363 additions and 109 deletions
......@@ -2,3 +2,4 @@
# Ignore node_modules directory
node_modules/
package.json
......@@ -3,5 +3,5 @@ WORKDIR /app
COPY package.json ./
RUN npm install
COPY . .
EXPOSE 80
EXPOSE 3001
CMD ["npm", "start"]
\ No newline at end of file
......@@ -19,7 +19,7 @@
"web-vitals": "^2.1.4"
},
"scripts": {
"start": "react-scripts start",
"start": "export PORT=3001 && react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
......
......@@ -115,7 +115,7 @@ const CalendarWeek = ({
const weekDates = weeks[currentWeekIndex] || [];
return (
<div className="wrap">
<div className="calendarWeekContainer wrap">
<div className="button-container">
<button onClick={handlePrevWeek} disabled={currentWeekIndex === 0}>
Prev Week
......
......@@ -3,6 +3,7 @@ import Input from "./Input";
import Button from "./Button";
import { useNavigate, useParams } from "react-router-dom";
import axios from "axios";
import "../styles/HomeParticipateForm.css"
function HomeParticipateForm() {
const [name, setName] = useState("");
......@@ -186,6 +187,7 @@ function HomeParticipateForm() {
<form>
<div>
<h1>투표에 참여하기</h1>
<h2> 아이디가 없다면 아래 양식에 맞춰 작성 참여하기를 누르세요</h2>
<Input
type="text"
value={name}
......@@ -204,7 +206,7 @@ function HomeParticipateForm() {
onChange={handleEmail}
placeholder="이메일(선택)"
/>
<Button type="submit" text="참여" onClick={handleSubmit} />
<Button type="submit" text="참여하기" onClick={handleSubmit} />
</div>
</form>
);
......
import PropTypes from "prop-types";
function Input({ type, value, onChange, placeholder }) {
function Input({ type, value, onChange, placeholder, classname }) {
return (
<input
className={classname}
type={type}
placeholder={placeholder}
onChange={onChange}
......
......@@ -28,8 +28,9 @@ function LinkPageForm() {
return (
<form onSubmit={handleSubmit}>
<h1>투표 링크가 생성되었습니다</h1>
<hr/>
<div>
<p>hey</p>
<Input
value={`https://when-meet.link/HomeParticipate/${id}`}
/>
......
......@@ -6,7 +6,7 @@ export default function MakeHeader({
}) {
return (
<div className="header">
<h2>
<h2 className="calendarh2">
<button type="button" onClick={prevMonth}>
prev
</button>
......
......@@ -90,38 +90,49 @@ function MeetingInfoForm() {
};
return (
<form onSubmit={handleSubmit}>
<div>
<div className="purpose-selector">
<h1>약속 일정 만들기</h1>
<label>
목적:
<select value={meetingPurpose} onChange={handleOnChange}>
<option value="선택">선택</option>
<option value="스터디">스터디</option>
<option value="회의">회의</option>
<option value="놀기">놀기</option>
<option value="식사">식사</option>
<option value="기타">기타</option>
</select>
</label>
<div className="center-container">
<h1>약속 일정 만들기</h1>
<div className="purpose">
<h2 className="not-enter">약속 목적 </h2>
<select className="purpose-selector" value={meetingPurpose} onChange={handleOnChange}>
<option value="선택">선택</option>
<option value="스터디">스터디</option>
<option value="회의">회의</option>
<option value="놀기">놀기</option>
<option value="식사">식사</option>
<option value="기타">기타</option>
</select>
</div>
<div className="calendar-month">
<Calendar usingDate={usingDate} setUsingDate={setUsingDate} />
</div>
<div className="timeStartEnd">
투표 가능 시간
<TimeInput onTimeChange={handleStartTimeChange} />
~
<TimeInput onTimeChange={handleEndTimeChange} />
<div className="form-input">
<h2>추가 설정</h2>
<div className="num-of-people">
<h2>투표 인원수</h2>
<Input
classname="voteNumber"
type="number"
value={number}
onChange={handleNumber}
placeholder="인원수"
/>
</div>
<div className="timeStartEnd">
<h2>투표 가능 시간</h2>
<TimeInput onTimeChange={handleStartTimeChange} />
~
<TimeInput onTimeChange={handleEndTimeChange} />
</div>
<div className="end-time">
<h2 className="not-enter">투표 종료 시간</h2>
<Input
classname= "vote-end"
type="datetime-local"
value={endVote}
onChange={handleVoteEnd} />
</div>
</div>
<Input
type="number"
value={number}
onChange={handleNumber}
placeholder="예상 투표 인원(선택)"
/>
<a>예상 투표 종료 시간(선택)</a>
<Input type="datetime-local" value={endVote} onChange={handleVoteEnd} />
<Button type="submit" text="시작하기" />
</div>
</form>
......
......@@ -13,7 +13,70 @@ function ResultMakeForm() {
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);
......@@ -141,15 +204,15 @@ function ResultMakeForm() {
<h1 className="title-box">{meetingData?.title}</h1>
{meetingData.maxParticipants && (
<div>
현재 완료한 인원수 {meetingData?.currentParticipants} /{" "}
현재 완료한 인원수 : {meetingData?.currentParticipants} /{" "}
{meetingData?.maxParticipants}
</div>
)}
{!meetingData.maxParticipants && (
<div>현재 완료한 인원수 {meetingData?.currentParticipants}</div>
<div>현재 완료한 인원수 : {meetingData?.currentParticipants}</div>
)}
<div>종료까지 남은 시간 {timeLeft}</div>
<div>종료까지 남은 시간 : {timeLeft}</div>
<button onClick={handleEdit}>수정하기</button>
<button onClick={closeMeeting}>투표 종료하기</button>
</div>
......@@ -174,33 +237,42 @@ function ResultMakeForm() {
onRequestClose={handleModalClose}
onSubmit={handlePasswordSubmit}
/>
<span className="mostTime">
<div style={{ textAlign: "center" }}>
가장 많은 사람들이 가능한 일정
</div>
<ol>//일정 5개 나열</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 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>
</>
);
}
......
......@@ -11,7 +11,7 @@ function TimeInput({ onTimeChange }) {
return (
<div>
<label>
<select value={time} onChange={handleTimeChange}>
<select className="time-selector" value={time} onChange={handleTimeChange}>
{Array.from({ length: 48 }, (_, i) => {
const paddedHour = Math.floor(i / 2).toString().padStart(2, "0");
const paddedMinute = (i % 2 === 0 ? "00" : "30");
......
.calendar-month .calendar {
display: flex;
flex-direction: column;
align-items: center;
border: 1px solid #ccc;
}
table {
table-layout: fixed;
}
.calendarTable {
width: 100%;
}
.header {
display: flex;
flex-direction: column;
......@@ -18,8 +32,3 @@
margin: 0; /* 기본 마진을 제거합니다. */
font-size: 16px; /* 원하는 크기로 조절합니다. */
}
table {
/* width: 100%; */
border: 1px solid #444444;
}
.calendar-container {
.calendarWeekContainer .calendar-container {
margin-top: 20px;
width: 100%;
background-color: #5fbdff;
}
.button-container {
.calendarWeekContainer .button-container {
display: flex;
justify-content: space-between;
}
.button-container button:first-child {
margin-right: 20px;
.calendarWeekContainer .button-container button:first-child {
margin-right: 10%;
margin-left: 10%;
}
.button-container button:last-child {
margin-left: 20px;
.calendarWeekContainer .button-container button:last-child {
margin-right: 10%;
margin-left: 10%;
}
.wrap {
.calendarWeekContainer .wrap {
width: 80%;
}
table {
.calendarWeekContainer table {
width: 100%;
border-collapse: collapse;
text-align: center;
margin-bottom: 4%;
}
th,
td {
.calendarWeekContainer th,
.calendarWeekContainer td {
border: 1px solid #ddd;
padding: 10px;
}
th {
.calendarWeekContainer th {
background-color: #f4f4f4;
}
td {
.calendarWeekContainer td {
background-color: #f4f4f4;
cursor: pointer;
transition: background-color 0.3s ease, transform 0.3s ease,
box-shadow 0.3s ease; /* 부드러운 애니메이션 효과 */
box-shadow 0.3s ease;
}
td:hover {
.calendarWeekContainer td:hover {
background-color: aqua;
transform: scale(1.05); /* 셀을 약간 확대 */
box-shadow: 0 0 10px rgba(0, 0, 0, 0.2); /* 셀에 그림자 추가 */
border: 1px solid #5fbdff; /* 셀 주위에 테두리 추가 */
transform: scale(1.05);
box-shadow: 0 0 10px rgba(0, 0, 0, 0.2);
border: 1px solid #5fbdff;
}
.calendarWeekContainer .selected {
background-color: #e0ffe0;
}
.selected {
background-color: #e0ffe0; /* 선택된 셀의 배경색 */
@media screen and (max-width: 768px) {
.calendarWeekContainer .wrap {
display: flex;
width: 100%;
}
}
* {
font-family: 'Noto Sans KR', sans-serif;
}
\ No newline at end of file
}
/* Media queries for responsiveness */
@media (max-width: 768px) {
form {
width: 80%;
}
}
@media (max-width: 576px) {
form {
width: 90%;
}
}
@media (max-width: 376px) {
form {
width: 77%;
}
}
\ No newline at end of file
/* HomeMake.css */
body {
background-color:white;
margin: 0;
......@@ -9,7 +7,7 @@ body {
form {
width: 50%;
padding: 20px;
margin: auto;
margin:auto;
text-align: center;
}
......@@ -31,6 +29,7 @@ h1 {
display: flex;
flex-direction: column; /* 세로 방향으로 정렬 */
align-items: center;
height: 100vh;
}
input {
......@@ -80,3 +79,9 @@ button:hover {
width: 90%;
}
}
@media (max-width: 376px) {
form {
width: 100%;
}
}
h2 {
font-size: 12px;
font-weight: lighter;
}
\ No newline at end of file
......@@ -29,17 +29,56 @@ h1 {
margin-bottom: 10px;
color: #333333;
font-size: 24px;
font-family: "Lato";
}
h2 {
font-size: 16px;
padding: 5px;
}
.not-enter {
white-space: nowrap;
}
select {
width: 20%;
padding: 5px;
font-size: 16px;
border: 1px solid #ccc;
border-radius: 5px;
outline: none;
cursor: pointer;
}
.purpose-selector {
width: 50%;
padding: 5px;
font-size: 16px;
border: 1px solid #ccc;
border-radius: 5px;
outline: none;
cursor: pointer;
}
.time-selector {
width: 100%;
font-size: 16px;
border: 1px solid #ccc;
border-radius: 5px;
outline: none;
cursor: pointer;
}
.calendarh2 {
display: flex;
white-space: nowrap;
margin: 0;
}
.num-of-people {
display: flex;
align-items: center;
justify-content: space-between;
white-space: nowrap;
margin: 0;
}
.timeStartEnd {
display: flex;
align-items: center;
......@@ -47,7 +86,13 @@ h2 {
white-space: nowrap;
margin: 0;
}
.end-time {
display: flex;
align-items: center;
justify-content: space-between;
white-space: nowrap;
margin: 0;
}
input {
width: 100%;
padding: 10px;
......@@ -57,6 +102,19 @@ input {
border-radius: 4px;
}
.voteNumber {
width: 60%;
box-sizing: border-box;
border: 1px solid #ccc;
border-radius: 4px;
}
.vote-end {
width: 60%;
box-sizing: border-box;
border: 1px solid #ccc;
border-radius: 4px;
}
button {
width: 100%;
padding: 10px;
......@@ -87,15 +145,17 @@ button:hover {
margin: auto;
}
.header button:first-child {
margin-right: 20px;
margin-right: 6em;
}
.header button:last-child {
margin-left: 20px;
margin-left: 6em;
}
.purpose-selector {
margin: 20px;
.purpose {
display: flex;
align-items: center;
}
.calendarTable {
display: flex;
justify-content: center;
......
......@@ -9,11 +9,11 @@
}
.form-container label {
display: flex;
align-items: center; /* 라디오 버튼과 텍스트를 세로 중앙에 맞춤 */
margin: 20px 0; /* 라벨 간에 여백을 추가 */
align-items: center;
margin: 20px 0;
}
.form-container input[type="radio"] {
margin-right: 10px; /* 라디오 버튼과 텍스트 사이의 공간 조정 */
margin-right: 10px;
width: 20%;
}
......@@ -24,11 +24,9 @@
.possible {
position: fixed;
right: 0; /* 화면의 오른쪽 끝에 고정 */
top: 60%; /* 화면의 상단으로부터 50%의 위치에 고정 */
transform: translateY(
-50%
); /* 상단 50% 위치에서 자기 자신의 높이의 절반을 위로 이동 */
right: 0;
top: 60%;
transform: translateY(-50%);
width: 20%;
border: 1px solid black;
padding: 20px;
......@@ -45,3 +43,15 @@
flex-direction: column;
align-items: center;
}
@media screen and (max-width: 768px) {
.possible {
position: static;
width: 40%;
margin: 200px 20px;
}
.flex-row {
flex-direction: column;
width: 100%;
}
}
......@@ -18,15 +18,60 @@
box-shadow: 0px 1px 5px rgba(95, 189, 255, 0.5); /* 구분선에 그림자 효과 추가 */
padding-top: 20px;
margin-top: 30px;
width: 50%;
overflow-x: auto;
}
.mostTime,
.possible {
border: 1px solid #ddd;
padding: 20px;
margin: 10px; /* 각 요소 주변의 공간을 더욱 명확히 */
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); /* 세련된 그림자 효과 */
border-radius: 8px; /* 모서리를 둥글게 */
background-color: #ffffff; /* 배경색 설정 */
box-sizing: border-box; /* 패딩과 보더를 너비에 포함 */
}
.mostTime {
position: fixed;
right: 0;
top: 30%;
transform: translateY(-50%);
transform: translateY(-60%);
width: 20%;
border: 1px solid black;
padding: 20px;
margin-right: 2%;
}
.possible {
position: fixed;
right: 0;
top: 60%;
transform: translateY(-40%);
width: 20%;
border: 1px solid black;
padding: 20px;
margin-right: 2%;
}
@media screen and (max-width: 768px) {
.flex-bottom-container {
display: flex;
justify-content: space-between;
width: 100%;
bottom: 0;
left: 0;
}
.mostTime,
.possible {
position: static;
width: 40%;
margin: 200px 20px;
}
.flex-row {
flex-direction: column;
width: 100%;
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment