Commit 61a30671 authored by Yoon, Daeki's avatar Yoon, Daeki 😅
Browse files

final error

parent 45c8051b
...@@ -8,8 +8,20 @@ ...@@ -8,8 +8,20 @@
"@testing-library/user-event": "^12.1.10", "@testing-library/user-event": "^12.1.10",
"react": "^17.0.1", "react": "^17.0.1",
"react-dom": "^17.0.1", "react-dom": "^17.0.1",
"react-router-dom": "^5.2.0",
"react-scripts": "4.0.0", "react-scripts": "4.0.0",
"web-vitals": "^0.2.4" "web-vitals": "^0.2.4",
"@toast-ui/react-calendar": "^1.0.5",
"axios": "^0.20.0",
"bcrypt": "^5.0.0",
"bootstrap": "^4.5.3",
"formik": "^2.1.5",
"jquery": "^3.5.1",
"moment": "^2.29.0",
"popper.js": "^1.16.1",
"react-bootstrap": "^1.4.0",
"styled-components": "^5.2.0",
"yup": "^0.29.3"
}, },
"scripts": { "scripts": {
"start": "react-scripts start", "start": "react-scripts start",
...@@ -34,5 +46,6 @@ ...@@ -34,5 +46,6 @@
"last 1 firefox version", "last 1 firefox version",
"last 1 safari version" "last 1 safari version"
] ]
} },
"proxy": "http://localhost:3030"
} }
// import React from 'react';
// import { Redirect, Route } from "react-router-dom";
// export const AdminPrivateRoute = ({ component: Component, ...rest }) => (
// <Route
// {...rest}
// render={props =>
// (localStorage.getItem("token") !==null) ? (
// <Component {...props} />
// ) : (
// <Redirect to={{
// pathname: "/login",
// state: {match: props.location}
// }} />
// )
// }
// />
// )
\ No newline at end of file
import React, { useContext } from 'react';
import { Link } from 'react-router-dom';
import axios from 'axios';
import { Card, Accordion, Col, AccordionContext, useAccordionToggle, Button } from 'react-bootstrap';
import styled from 'styled-components';
const Text = styled(Card.Body)`
& .WRAP {
display: inline-block;
text-overflow: ellipsis;
width: 100%;
white-space: initial;
}
`
function Notice({ card_id, card_index, title, date, content, admin }) {
function ContextAwareToggle({ children, eventKey, callback }) {
const currentEventKey = useContext(AccordionContext);
const decoratedOnClick = useAccordionToggle(
eventKey,
() => callback && callback(eventKey),
);
const isCurrentEventKey = currentEventKey === eventKey;
return (
<div
className={isCurrentEventKey ? "text-wrap whiteSpace-initial" : "text-truncate"}
onClick={decoratedOnClick}
>
{children}
</div>
);
}
function dateForm(day) {
const post_day = new Date(day);
let year = post_day.getFullYear();
let month = post_day.getMonth() + 1;
let date = post_day.getDate();
month = month < 10 ? '0' + month : month;
date = date < 10 ? '0' + date : date;
const new_date = year + "-" + month + "-" + date;
return new_date
}
function remove (card_id) {
axios.delete(`/notices/${card_id}`)
.then(res => {
if (res.status === 404) return alert(res.data.error)
alert("삭제되었습니다!");
window.location.reload();
})
.catch(err => {
alert(err.error)
});
}
return (
<Card className="w-100">
<Card.Header className="row flex-row py-3">
<Col md={10} xs={8} >
<ContextAwareToggle variant="link" eventKey={card_index + 1}>{title}</ContextAwareToggle>
</Col>
<Col md={2} xs={4} className="p-0" >{dateForm(date)}</Col>
</Card.Header>
<Accordion.Collapse eventKey={card_index + 1}>
<Text>
{content.split("\n").map((i, key) => {
return <div key={key}>{i}</div>;
})}
{admin === "admin" ? (
<div className="d-flex justify-content-end">
<Button variant="primary" size="sm" as={Link} to={`/modify/${card_id}`}>수정</Button>
<Button variant="danger" size="sm" onClick={() => remove(card_id)}>삭제</Button>
</div>) : null}
</Text>
</Accordion.Collapse>
</Card >
)
}
export default Notice;
\ No newline at end of file
import React, { useState, useEffect } from 'react';
import { Link, Redirect } from 'react-router-dom';
import styled from 'styled-components';
import axios from 'axios';
import { Navbar, Nav, NavLink } from 'react-bootstrap';
const MENU = styled(Navbar)`
background-color: #7B031D;
a {
color : white;
}
`
function Menu() {
const [state, setState] = useState()
const [user, setUser] = useState({ role: "" })
const name = localStorage.getItem('name');
function logout() {
localStorage.clear();
alert("로그아웃 되었습니다.");
setState(true);
}
useEffect(() => {
acheck();
}, [])
function acheck() {
axios.get(`/users/${localStorage.getItem('_id')}`, {
headers: { authorization: localStorage.getItem('token') },
}).then(res => {
if (res.data.role == "admin") {
setUser(res.data)
}
}).catch(err => {
alert(err.error)
});
}
if (state) return <Redirect to="/" />
return (
<MENU expand="md" variant="dark">
<Navbar.Brand as={Link} to="/home">대관 서비스</Navbar.Brand>
<Navbar.Toggle aria-controls="basic-navbar-nav" />
<Navbar.Collapse id="basic-navbar-nav">
<Nav className="mr-auto">
<NavLink as={Link} to="/notice">공지사항</NavLink>
<NavLink as={Link} to="/home">대관 현황</NavLink>
<NavLink as={Link} to={{
pathname: `/apply/${localStorage.getItem('_id')}`,
state: { id: localStorage.getItem('_id') },
}} className="nav-link">대관 신청</NavLink>
<NavLink as={Link} to={{
pathname: `/check/${localStorage.getItem('_id')}`,
state: { id: localStorage.getItem('_id') },
}} className="nav-link">
대관 확인/취소</NavLink>
{/* {user.role === "admin" ? (
<NavLink as={Link} to={{
pathname: `/acheck/${localStorage.getItem('_id')}`,
state: { id: localStorage.getItem('_id') },
}} className="nav-link">
대관 확인/취소(관리자)</NavLink>) : null} */}
</Nav>
<Nav >
<NavLink>
<small className="d-flex flex-row justify-content-end">
<div className="text-white text-right font-weight-light pr-2">{name}</div>
<NavLink className="p-0" as={Link} to={{
pathname: `/change/${localStorage.getItem('_id')}`,
state: { id: localStorage.getItem('_id') },
}}> 비밀번호 변경 </NavLink> / <NavLink className="p-0" onClick={logout} > 로그아웃</NavLink></small>
</NavLink>
</Nav>
</Navbar.Collapse>
</MENU >
)
}
export default Menu
\ No newline at end of file
import React from 'react';
import { Redirect, Route } from "react-router-dom";
export const PrivateRoute = ({ component: Component, ...rest }) => (
<Route
{...rest}
render={props =>
(localStorage.getItem("token") !==null) ? (
<Component {...props} />
) : (
<Redirect to={{
pathname: "/login",
state: {match: props.location}
}} />
)
}
/>
)
\ No newline at end of file
import React, { useState, useEffect, useRef } from 'react';
import Calendar from '@toast-ui/react-calendar';
import "tui-calendar/dist/tui-calendar.css";
import "tui-date-picker/dist/tui-date-picker.css";
import "tui-time-picker/dist/tui-time-picker.css";
import moment from 'moment';
import axios from 'axios';
import { Image, Button } from 'react-bootstrap';
import leftArrow from '../caret-left-fill.svg';
import rightArrow from '../caret-right-fill.svg';
function Cal(props) {
const calendarRef = useRef(null);
const [reserve, setReserve] = useState([]);
const [period, setPeriod] = useState();
const [myTheme, setMyTheme] = useState({
'common.dayname.color': '#333',
'common.today.color': '#333',
'common.creationGuide.backgroundColor': 'gray',
});
function getReserve(room) {
axios.get(`/reserves/room/${room}`, {
headers: { authorization: localStorage.getItem('token') },
})
.then(res => {
const reserves = res.data.map(item => ({
id: item._id,
start: item.start,
end: item.end,
calendarId: 'Subject',
category: 'time',
}))
setReserve(reserves);
})
.catch(err => {
alert(err.error)
});
}
function getDataAction(target) {
return target.dataset ? target.dataset.action : target.getAttribute('data-action');
}
function onClickNavi(e) {
const cal = calendarRef.current.getInstance();
const action = getDataAction(e.target);
switch (action) {
case 'move-prev':
cal.prev();
break;
case 'move-next':
cal.next();
break;
case 'move-today':
cal.today();
break;
default:
return;
}
setRenderRangeText();
}
function setRenderRangeText() {
const cal = calendarRef.current.getInstance();
let html = [];
html.push(moment(cal.getDateRangeStart().getTime()).format('YYYY.MM.DD'));
html.push(' ~ ');
html.push(moment(cal.getDateRangeEnd().getTime()).format(' MM.DD'));
setPeriod(html.join(''))
}
useEffect(() => {
setRenderRangeText();
getReserve(props.room);
}, [props.room])
return (
<div>
<div id="menu" className="p-2">
<span id="menu-navi" onClick={(e) => onClickNavi(e)}>
<Button variant="default" size="sm" className="move-today" data-action="move-today">Today</Button>
<Button variant="default" size="sm" className="move-day" data-action="move-prev">
<Image class="calendar-icon" src={leftArrow} data-action="move-prev"></Image>
</Button>
<Button variant="default" size="sm" className="move-day" data-action="move-next">
<Image className="calendar-icon" src={rightArrow} data-action="move-next"></Image>
</Button>
</span>
<span id="renderRange" className="render-range ml-2" style={{ height: "5em" }}>{period}</span>
</div>
<Calendar
ref={calendarRef}
height="100%"
calendars={[
{
id: 'Subject',
bgColor: '#a9a9a9',
borderColor: '#a9a9a9',
isReadOnly: 'true'
}
]}
view="week"
disableDblClick={false}
disableClick={true}
isReadOnly={true}
schedules={reserve}
scheduleView={['time']}
taskView={false}
theme={myTheme}
timezones={[
{
timezoneOffset: 540,
displayLabel: 'GMT+09:00',
tooltip: 'Seoul'
},
]}
useDetailPopup
useCreationPopup
view={"week"}
week={{
workweek: true,
hourStart: 8,
hourEnd: 23
}}
/>
</div>
)
}
export default Cal
\ No newline at end of file
import React, { useState, useEffect } from 'react';
import Menu from '../Components/Menu';
import axios from 'axios';
import { Redirect } from 'react-router-dom';
import { Container, Table } from 'react-bootstrap';
import styled from 'styled-components';
const Ta = styled(Table)`
& th, & td {
padding: 0;
vertical-align: middle;
};
& tr {
display: d-flex;
width: 50px;
};
& td {
align-items: center;
width: 70px;
};
`
function ACheck(props) {
const [state, setState] = useState()
const [reserve, setReserve] = useState([]);
useEffect(() => {
getReserve();
}, [])
function getReserve() {
axios.get(`/users/admin/${props.match.params.id}`, {
headers: { authorization: localStorage.getItem('token') },
})
.then(res => {
if (res.status === 404) {
alert(res.data.error)
setState(true);
}
setReserve(res.data);
})
.catch(err => {
alert(err.error)
});
}
if (state) return <Redirect to="/" />;
function remove(index) {
axios.put(`/reserves/${reserve[index]._id}`)
.then(res => {
if (res.status === 404) return alert(res.data.error)
alert("승인을 거절했습니다!");
getReserve();
})
.catch(err => {
alert(err.error)
});
};
function admit(index) {
axios.put(`/reserves/${reserve[index]._id}`, {
approve: true,
})
.then(res => {
if (res.status === 404) return alert(res.data.error)
alert("승인되었습니다!");
getReserve();
})
.catch(err => {
alert(err.error)
});
};
return (
<div>
<Menu />
<Container fluid>
<Ta responsive="lg">
<thead className="thead-light">
<tr>
<th>대표자</th>
<th>날짜</th>
<th>시간</th>
<th>강의실</th>
<th>사용 인원</th>
<th>승인 여부</th>
</tr>
</thead>
<tbody>
{reserve != "" ? (
reserve.map((reserve, index) => {
return (
<tr key={index}>
<td>{reserve.user.name}</td>
<td>{reserve.date}</td>
<td>{reserve.starttime}~{(Number(reserve.starttime) + reserve.usetime)}</td>
<td>{reserve.room}</td>
<td>{reserve.num}</td>
<td>
<button onClick={() => admit(index)} className="btn btn-primary btn-sm">
승인
</button>
<button onClick={() => remove(index)} className="btn btn-danger btn-sm">
거절
</button>
</td>
</tr>
)
})) : <div>최근 대관 신청 내역이 없습니다.</div>}
</tbody>
</Ta>
</Container>
</div>
)
}
export default ACheck
import React, { useState, useEffect } from 'react';
import { Formik, Field, ErrorMessage, FieldArray } from 'formik';
import Menu from '../Components/Menu';
import axios from 'axios';
import * as Yup from 'yup';
import { Redirect } from 'react-router-dom';
import { Col, Container, Row } from 'react-bootstrap';
function Apply(props) {
const [state, setState] = useState({ok:""});
const [user, setUser] = useState({ name: "" });
const [room_Num, setRoom_Num] = useState({ "9-116": 5, "7-234": 7, "25-101": 10 });
useEffect(() => {
getUser();
}, [])
if (state.ok==="no") return <Redirect to="/" />;
if (state.ok==="ok") {
return <Redirect to={{
pathname: `/check/${props.match.params.id}`,
state: { id: props.match.params.id },
}} />;
}
function time(starttime) {
if (starttime == 21) {
return (<Field as="select" name="usetime" className="col-12">
<option value="">이용시간선택</option>
<option value="1">1시간</option>
</Field>)
}
if (starttime == 20) {
return (<Field as="select" name="usetime" className="col-12">
<option value="">이용시간선택</option>
<option value="1">1시간</option>
<option value="2">2시간</option>
</Field>)
}
return (<Field as="select" name="usetime" className="col-12">
<option value="">이용시간선택</option>
<option value="1">1시간</option>
<option value="2">2시간</option>
<option value="3">3시간</option>
</Field>)
}
function getUser() {
axios.get(`/users/${props.match.params.id}`, {
headers: { authorization: localStorage.getItem('token') },
})
.then(res => {
if (res.status !== 201) {
alert(res.data.error);
localStorage.clear();
setState({ok:"no"});
}
setUser(res.data);
})
.catch(err => {
alert(err.error)
});
}
function addRoomInfo(values) {
for (let room in room_Num) {
if (room === values.room) {
values.roomInfo = room_Num[room]
}
};
}
return (
<div>
<Menu />
<Container fluid className="mt-3">
<Row className="justify-content-center">
<Col md={5}>
<Formik
initialValues={{
_id: `${props.match.params.id}`,
date: '',
starttime: '',
usetime: '',
room: '',
reason: '',
students: [
{
member: '',
},
],
}}
validationSchema={Yup.object({
date: Yup.string()
.required('날짜를 입력해주세요.'),
reason: Yup.string()
.required('대관목적을 입력해주세요.'),
})}
onSubmit={(values, { setSubmitting }) => {
addRoomInfo(values);
axios({
method: 'post',
url: '/reserves',
data: values
}).then(res => {
if (res.status === 404) {
alert(res.data.error)
return window.location.reload();
}
alert("신청이 완료되었습니다!");
setState({ok:"ok"});
})
.catch(err => {
alert(err.error)
});
setTimeout(() => {
setSubmitting(false);
}, 400); // finish the cycle in handler
}}
>
{({
errors,
touched,
values,
handleSubmit,
getFieldProps,
isSubmitting,
}) => (
<form onSubmit={handleSubmit} className="d-flex flex-column">
<h3 className="form-group font-weight-bold">
<label className="pr-2">대표자 :</label>{user.name}
</h3>
<div className="form-group mb-4">
<div className={touched.date && errors.date ? "text-danger" : ""}>신청날짜</div>
<input
className={(touched.date && errors.date ? 'form-control is-invalid' : "form-control")}
type="text"
name="date"
{...getFieldProps('date')}
placeholder="yyyy-mm-dd"
/>
</div>
<Row className="form-group mb-4">
<div className="col-6 pr-0">
<label>이용시작시간</label>
<div>
<Field as="select" name="starttime" className="col-12">
<option value="">이용시작시간</option>
<option value="09">9</option>
<option value="10">10</option>
<option value="11">11</option>
<option value="12">12</option>
<option value="13">13</option>
<option value="14">14</option>
<option value="15">15</option>
<option value="16">16</option>
<option value="17">17</option>
<option value="18">18</option>
<option value="19">19</option>
<option value="20">20</option>
<option value="21">21</option>
</Field>
</div>
</div>
<div className="col-6 pl-0">
<label>이용시간</label>
<div>
{time(values.starttime)}
</div>
</div>
</Row>
<div className="form-group mb-4">
<div className={touched.room && errors.room ? "text-danger" : ""}>강의실</div>
<Field as="select" name="room" className="col-6">
<option value="">강의실 선택</option>
<option value="9-116">9-116 (5)</option>
<option value="7-234">7-234 (7)</option>
<option value="25-101">25-101 (10)</option>
</Field>
</div>
<div className="form-group mb-4">
<div className={touched.reason && errors.reason ? "text-danger" : ""}>대관 목적</div>
<input
className={(touched.reason && errors.reason ? 'form-control is-invalid' : "form-control")}
type="text"
name="reason"
{...getFieldProps('reason')}
placeholder="대관목적을 입력해 주세요."
/>
</div>
<div className="form-group mb-4">
<FieldArray name="students">
{({ remove, push }) => (
<div>
<div className={touched.date && errors.date ? "text-danger" : ""}>이용자</div>
{values.students.map((student, index) => (
<div key={index}>
<Field
name={`students.${index}.member`}
placeholder="이용자 이름 입력"
type="text"
className="col-6 mr-1"
/>
<ErrorMessage
name={`friends.${index}.name`}
component="div"
className="field-error"
/>
<button
type="button"
className="secondary"
onClick={() => remove(index)}
>X</button>
</div>
))}
<button
type="button"
className="btn btn-primary"
onClick={() => push({ member: '' })}
>추가</button>
</div>
)}
</FieldArray>
</div>
<button type="submit" className="btn btn-dark" disabled={isSubmitting}>
신청하기
</button>
</form>
)}
</Formik>
</Col>
</Row>
</Container>
</div>
)
}
export default Apply
\ No newline at end of file
import React, { useState } from 'react';
import { Formik } from 'formik';
import * as Yup from 'yup';
import axios from 'axios';
import Menu from '../Components/Menu';
import { Redirect } from 'react-router-dom';
import { Container, Button, Navbar, Col } from 'react-bootstrap';
import styled from 'styled-components';
const Check = styled.div`
& #reCheck::after {
content: '새로운 비밀번호를 다시 입력하세요';
}
& #reCheck:not(.right) {
content: '비밀번호가 일치하지 않습니다.';
color: red;
}
`
function Change(props) {
const [state, setState] = useState();
const [checkPw, setCheckPw] = useState(true);
if (state) {
return <Redirect to="/" />;
}
return (
<div className="">
{(localStorage.getItem("token") !== null) ? (
<Menu />
) : (
<Menu expand="md" variant="dark">
<Navbar.Brand>회원가입</Navbar.Brand>
</Menu>
)}
<Container fluid className="p-0 vh-90">
<Check className="row justify-content-center m-0">
<Col md={4} className="pt-5">
<Formik
initialValues={{ password: '' }}
validationSchema={Yup.object({
password: Yup.string()
.required('비밀번호를 입력해주세요.')
.min(8, '8자 이상 입력해주세요.'),
password2: Yup.string()
.required('비밀번호를 다시 입력해주세요.')
.min(8, '8자 이상 입력해주세요.')
.oneOf([Yup.ref("password"), null], '비밀번호가 일치하지 않습니다.'),
})}
onSubmit={(values, { setSubmitting }) => {
axios.put(`/users/change/${props.location.state.id}`, { ...values },
)
.then(res => {
console.log(res.data);
if (res.status === 404) return alert(res.data.error)
alert("회원정보가 수정되었습니다!")
setState(true);
})
.catch(err => {
alert(err.error)
});
setTimeout(() => {
setSubmitting(false);
}, 400); // finish the cycle in handler
}}
>
{({
errors,
touched,
handleSubmit,
getFieldProps, // contain values, handleChange, handleBlur
isSubmitting,
}) => (
<form onSubmit={handleSubmit} className="d-flex flex-column">
<div className="form-group">
<div className={touched.password && errors.password ? "text-danger" : ""}> 비밀번호를 입력하세요(8자리 이상)</div>
<input
className={(touched.password && errors.password ? 'form-control is-invalid' : "form-control")}
type="password"
name="password"
{...getFieldProps('password')}
placeholder="새로운 비밀번호"
/>
</div>
<div className="form-group">
{touched.password2 && errors.password2 ? setCheckPw(false) : null}
<div id="reCheck" className={checkPw ? "right" : "err"}></div>
<input
className={(touched.password2 && errors.password2 ? 'form-control is-invalid' : "form-control")}
type="password"
name="password2"
{...getFieldProps('password2')}
placeholder="새 비밀번호를 다시 입력해주세요."
/>
</div>
<Button type="submit" variant="secondary" disabled={isSubmitting}>저장하기</Button>
</form>
)}
</Formik>
</Col>
</Check>
</Container>
</div >
);
}
export default Change
\ No newline at end of file
import React, { useState, useEffect } from 'react';
import Menu from '../Components/Menu';
import { Redirect } from 'react-router-dom';
import axios from 'axios';
import { Container, Table } from 'react-bootstrap';
import styled from 'styled-components';
const Ta = styled(Table)`
margin-top: 0.5em;
& th, & td {
padding: 0;
vertical-align: middle;
font-size: 0.9rem;
margin-left : auto; margin-right : auto;
border-spacing: initial;
};
& tr {
display: d-flex;
width: 150px;
};
& td {
align-items: center;
margin: 10px;
};
`
function Check(props) {
const [reserve, setReserve] = useState([]);
const [state, setState] = useState()
useEffect(() => {
getReserve();
}, [])
if (state) return <Redirect to="/" />;
function getReserve() {
axios.get(`/reserves/${props.match.params.id}`, {
headers: { authorization: localStorage.getItem('token') },
})
.then(res => {
if (res.status === 404) {
alert(res.data.error);
}
if (res.status === 419) {
alert(res.data.error);
localStorage.clear();
setState(true);
}
const reserves = res.data.filter(function(item) {
return item !== '';
});
setReserve(reserves);
})
.catch(err => {
alert(err.error)
});
}
function remove(index) {
axios.delete(`/reserves/${reserve[index]._id}`)
.then(res => {
if (res.status === 404) return alert(res.data.error)
alert("삭제되었습니다!");
getReserve();
})
.catch(err => {
alert(err.error)
});
};
return (
<div>
<Menu />
<Container fluid>
<Ta responsive="lg ml-2rem">
<thead className="thead-light">
<tr>
<th className="text-center">날짜</th>
<th className="text-center">시간</th>
<th className="text-center">강의실</th>
<th className="text-center">사용인원</th>
{/* <th>승인여부</th> */}
<th className="text-center">예약취소</th>
</tr>
</thead>
<tbody>
{reserve.map((reserve, index) => {
return (
<tr key={index}>
<td className="text-center">{reserve.date}</td>
<td className="text-center">{reserve.starttime}~{(Number(reserve.starttime) + reserve.usetime)}</td>
<td className="text-center">{reserve.room}</td>
<td className="text-center">{reserve.num}</td>
{/* <td>{reserve.check ? (reserve.approve ? "사용가능" : "사용불가") : "승인대기중"}</td> */}
<td className="text-center">
<button onClick={() => remove(index)} className="btn btn-danger btn-sm">
취소
</button>
</td>
</tr>
)
})}
</tbody>
</Ta>
</Container>
</div>
)
}
export default Check
\ No newline at end of file
import React, { useState } from 'react';
import { Field, Formik } from 'formik';
import * as Yup from 'yup';
import axios from 'axios';
import { Link, Redirect } from 'react-router-dom';
import styled from 'styled-components';
import { Navbar, Container, Row, Col, Button } from 'react-bootstrap';
const Menu = styled(Navbar)`
background-color: #7B031D;
a {
color : white;
}
`
function Find() {
const [state, setState] = useState(false);
if (state) {
return <Redirect to={{
pathname: `/change/${localStorage.getItem('_id')}`,
state: { id: localStorage.getItem('_id') },
}} />;
}
return (
<div className="vh-100">
<Menu expand="md" variant="dark">
<Navbar.Brand>비밀번호 찾기</Navbar.Brand>
</Menu>
<Container fluid>
<Row className="justify-content-center">
<Col md={3} xs={11} className="p-0">
<Formik
initialValues={{ id: '', question: '', answer: '' }}
validationSchema={Yup.object({
id: Yup.string()
.required('학번을 입력해주세요.'),
answer: Yup.string()
.required('답변을 입력해주세요.'),
})}
onSubmit={(values, { setSubmitting }) => {
axios({
method: 'post',
url: '/login/find',
data: values,
}).then(res => {
if (res.status === 404) return alert(res.data.error)
localStorage.setItem('_id', res.data.users._id)
setState(true);
})
.catch(err => {
alert(err.error)
});
console.log(values);
setTimeout(() => {
setSubmitting(false);
}, 400); // finish the cycle in handler
}}
>
{({
errors,
touched,
handleSubmit,
getFieldProps, // contain values, handleChange, handleBlur
isSubmitting,
}) => (
<form onSubmit={handleSubmit} className="d-flex flex-column pt-5">
<div className="form-group pb-2">
<div className={touched.id && errors.id ? "text-danger" : ""}>학번을 입력하세요</div>
<input
className={(touched.id && errors.id ? 'form-control is-invalid' : "form-control")}
type="number"
name="id"
{...getFieldProps('id')}
placeholder="Input Student Id"
/>
</div>
<div className="form-group pb-2">
<label className="pr-2">본인 확인 질문</label>
<Field as="select" name="question">
<option value="">질문을 선택하세요</option>
<option value="life">자신의 인생 좌우명은?</option>
<option value="school">자신이 다녔던 초등학교의 이름은?</option>
<option value="place">기억에 남는 추억의 장소는?</option>
</Field>
</div>
<div className="form-group pb-2">
<div className={touched.answer && errors.answer ? "text-danger" : ""}>답변을 입력해주세요.</div>
<input
className={(touched.answer && errors.answer ? 'form-control is-invalid' : "form-control")}
type="text"
name="answer"
{...getFieldProps('answer')}
placeholder="Input answer" />
</div>
<Button className="mb-2" variant="secondary" type="submit" disabled={isSubmitting}>비밀번호 찾기</Button>
<Button variant="outline-secondary" as={Link} to="/login">로그인하러 가기</Button>
</form>
)}
</Formik>
</Col>
</Row>
</Container >
</div >
);
}
export default Find
\ No newline at end of file
import React, { useState, useEffect } from 'react';
import { Redirect } from 'react-router-dom';
import axios from 'axios';
import Menu from '../Components/Menu';
import Schedule from '../Components/Schedule';
import { Container, Tabs, Tab } from 'react-bootstrap';
function Home() {
const [key, setKey] = useState('9-116');
const [state, setState] = useState()
useEffect(() => {
tcheck();
}, []);
if (state) return <Redirect to="/" />;
function tcheck() {
axios.get(`/users/${localStorage.getItem('_id')}`, {
headers: { authorization: localStorage.getItem('token') },
})
.then(res => {
if (res.status !== 201) {
alert(res.data.error);
localStorage.clear();
setState(true);
}
}).catch(err => {
alert(err.error)
});
}
return (
<div>
<Menu />
<Container className="col-md-10 mt-3">
<h2>대관 현황</h2>
<p>
<strong>대관 가능 시간</strong>
<ul className="pl-4">
<li>평일: 9 - 22/ 예약가능 시간 이후 폐쇄</li>
<li>주말: 이용 불가</li>
</ul>
</p>
<p>
<strong>유의사항</strong>
<ul className="pl-4">
<li>강의실 사용시 최소인원 수에 맞춰서 명단 작성이 필요합니다.</li>
<li>1 대관시 최대 3시간까지 이용이 가능합니다. (1시간 단위로 대관 가능)</li>
<li><strong style={{ color: "red" }}>대관 시간 이외 강의실을 이용하다 적발될 경우 한달 강의실 이용이 불가합니다.</strong></li>
</ul>
</p>
<Tabs defaultActiveKey="9-116" id="uncontrolled-tab-example" onSelect={(k) => setKey(k)}>
<Tab eventKey="9-116" title="9-116">
<Schedule room={key} />
</Tab>
<Tab eventKey="7-234" title="7-234">
<Schedule room={key} />
</Tab>
<Tab eventKey="25-101" title="25-101">
<Schedule room={key} />
</Tab>
</Tabs>
</Container>
</div>
)
}
export default Home
\ No newline at end of file
import React, { useState, useEffect } from 'react';
import styled from 'styled-components';
import { Link, Redirect } from 'react-router-dom';
import { Formik } from 'formik';
import * as Yup from 'yup';
import axios from 'axios';
import Logo from '../icon.png';
import { Container, Row, Button } from 'react-bootstrap';
const Col_1 = styled.div`
background-color: #7B031D;
&.web {
display : flex;
align-items: center;
}
&.mobile {
height : 20vh;
display : flex;
padding:0;
}
& .mob-head {
display: flex;
flex-direction: row;
height : 100%;
width: 100%;
justify-content: space-evenly;
}
& .mob-img {
max-width: 30vw;
}
`
const Col_2 = styled.div`
background-color: rgb(239, 218, 200);
a {
color : #7B031D;
}
& .mob-formik {
height : 80vh;
width: 100%;
display: flex;
justify-content: center;
align-items: center;
}
& .web-form {
height: 100%;
display: flex;
align-items: center;
justify-content: center;
}
& .mob-container {
display: flex;
flex-direction: column;
}
& .webb {
flex-direction: column;
}
& .web-container {
display: flex;
height: 12vh;
width: 30vw;
}
& .web-input-form {
width: 80%;
display: flex;
flex-direction: column;
justify-content: space-around;
}
& .mob-input-form {
display: flex;
flex-direction: column;
justify-content: space-around;
}
`
function Login() {
const [state, setState] = useState(false);
const [mobile, setMobile] = useState(false);
useEffect(() => {
if (window.innerWidth < 960) {
setMobile(true)
} else {
setMobile(false)
}
}, []);
if (state) {
return <Redirect to="/home" />;
}
return (
<Container fluid className="p-0">
<Row className="vw-100 vh-100 m-0 " >
<Col_1 className={"col-md-4 col-12" + (mobile ? " mobile" : " web")}>
<div className={mobile ? "mob-head" : ""}>
<img className={mobile ? "mob-img" : "img-fluid"} src={Logo} />
<div className={"d-flex " + (mobile ? "align-items-center" : "justify-content-center")}>
<h1 className="font-weight-bold text-white text-center">고려대학교<br />대관 서비스</h1>
</div>
</div>
</Col_1>
<Col_2 className="col-md-8 col-12" >
<Formik
initialValues={{ id: '', password: '' }}
validationSchema={Yup.object({
id: Yup.string()
.required('학번을 입력해주세요.'),
password: Yup.string()
.required('비밀번호를 입력해주세요.')
.min(8, '8자 이상 입력해주세요.'),
})}
onSubmit={(values, { setSubmitting }) => {
axios({
method: 'post',
url: '/login',
data: values,
}).then(res => {
if (res.status === 404) return alert(res.data.error)
localStorage.setItem('token', res.data.token);
localStorage.setItem('_id', res.data.users._id);
localStorage.setItem('name', res.data.users.name);
setState(true);
})
.catch(err => {
alert(err.error)
});
setTimeout(() => {
setSubmitting(false);
}, 400); // finish the cycle in handler
}}
>
{({
errors,
touched,
handleSubmit,
getFieldProps, // contain values, handleChange, handleBlur
isSubmitting,
}) => (
<div className={mobile ? " mob-formik p-0" : " web-form"}>
<form onSubmit={handleSubmit} className={mobile ? "w-75" : "d-flex webb"}>
<div className={mobile ? "mob-container" : "web-container"}>
<div className={mobile ? "mob-input-form" : "web-input-form mr-2"}>
<div className={"form-group m-0" + (mobile ? " mb-2" : " ")}>
<input
className={(touched.id && errors.id ? 'form-control is-invalid' : "form-control")}
type="text"
name="id"
{...getFieldProps('id')}
placeholder="Input Student Id"
/>
</div>
<div className={"form-group m-0"+ (mobile ? " mb-2" : " ")}>
<input
className={(touched.password && errors.password ? 'form-control is-invalid' : "form-control")}
type="password"
name="password"
{...getFieldProps('password')}
placeholder="Input Password"
/>
</div>
</div>
<Button type="submit" variant="dark" className={mobile ? " w-100" : " w-20"} disabled={isSubmitting}> Login </Button>
</div>
<div><Link to="/find">비밀번호를 잊으셨나요?</Link></div>
<div><Link to="/signup">회원이 아니신가요?</Link></div>
</form>
</div>
)}
</Formik>
</Col_2>
</Row>
</Container>
)
}
export default Login
\ No newline at end of file
import React, { useState, useEffect } from 'react';
import { Redirect } from 'react-router-dom';
import Menu from '../Components/Menu';
import * as Yup from 'yup';
import axios from 'axios';
import { Container, Row, Col, Button } from 'react-bootstrap';
import { Field, Formik } from 'formik';
function Modify({ match }) {
const [state, setState] = useState(false);
const [modification, setModification] = useState({ title: "", content: "" });
const [isadmin, setIsadmin] = useState({ ok: "" });
const [user, setUser] = useState({ name: "", role: "" })
useEffect(() => {
acheck();
getOne(match.params.id);
}, [])
if (isadmin.ok === "no") return <Redirect to="/" />;
if (state) {
return <Redirect to="/notice" />;
}
function getOne(id) {
if (id) {
axios.get(`/notices/${match.params.id}`)
.then(res => {
if (res.status !== 201) {
alert(res.data.error);
}
setModification({ title: res.data.notice_title, content: res.data.notice_content })
})
.catch(err => {
alert(err.error)
});
}
};
function acheck() {
axios.get(`/users/admin/${localStorage.getItem('_id')}`, {
headers: { authorization: localStorage.getItem('token') },
})
.then(res => {
if (res.status !== 201) {
alert(res.data.error);
setIsadmin({ ok: "no" });
}
setUser({ name: res.data.name, role: res.data.role })
}).catch(err => {
alert(err.error)
});
}
return (
<div>
<Menu />
<Container fluid>
{console.log(modification)}
<Row className="justify-content-center">
<Col md={5} xs={11} className="pt-3" >
<Formik
initialValues={{ name: user.name, title: modification.title, content: modification.content }}
enableReinitialize={true}
validationSchema={Yup.object({
title: Yup.string()
.required('제목을 입력해주세요.'),
content: Yup.string()
.required('내용을 입력해주세요.'),
})}
onSubmit={(values, { setSubmitting }) => {
axios({
method: 'put',
url: `/writes/${match.params.id}`,
data: values,
})
.then(res => {
if (res.status === 404) return alert(res.data.error)
alert("공지 수정이 완료되었습니다.")
setState(true);
})
.catch(err => {
alert(err.error)
});
setTimeout(() => {
setSubmitting(false);
}, 400); // finish the cycle in handler
}}
>{({
errors,
touched,
handleSubmit,
getFieldProps, // contain values, handleChange, handleBlur
isSubmitting,
}) => (
<form onSubmit={handleSubmit} className="d-flex flex-column">
<div className="form-group">
<div className={touched.name && errors.name ? "text-danger" : ""}>제목</div>
<input className={(touched.name && errors.name ? 'form-control is-invalid' : "form-control")}
type="text"
title="title"
{...getFieldProps('title')}
disabled />
</div>
<div className="form-group">
<div className={touched.name && errors.name ? "text-danger" : ""}>내용</div>
<Field as="textarea" rows={8} style={{ "min-width": "100%" }}
{...getFieldProps('content')} />
</div>
<Button className="mb-2" variant="dark" type="submit" disabled={isSubmitting}>공지 수정</Button>
</form>
)}
</Formik>
</Col>
</Row>
</Container>
</div>
)
}
export default Modify
\ No newline at end of file
import React, { useState, useEffect } from 'react';
import Menu from '../Components/Menu';
import axios from 'axios';
import { Link, Redirect } from 'react-router-dom';
import { Container, Row, Col, Accordion, Button } from 'react-bootstrap';
import CARD from '../Components/Card';
function Notice() {
const [notices, setNotices] = useState([]);
const [user, setUser] = useState({ role: "" })
const [state, setState] = useState()
useEffect(() => {
acheck();
getNotice();
}, []);
if (state) return <Redirect to="/" />;
function acheck() {
axios.get(`/users/${localStorage.getItem('_id')}`, {
headers: { authorization: localStorage.getItem('token') },
})
.then(res => {
if (res.status !== 201) {
alert(res.data.error);
localStorage.clear();
setState(true);
}
if (res.data.role == "admin") {
setUser(res.data)
}
}).catch(err => {
alert(err.error)
});
}
function getNotice() {
axios.get(`/notices`)
.then(res => {
console.log('res.data=', res.data)
if (res.status !== 201) {
alert(res.data.error);
}
setNotices(res.data);
})
.catch(err => {
alert(err.error)
});
}
return (
<div>
<Menu />
<Container fluid>
<Row className="justify-content-center vw-100 vh-90">
<Col md={7}>
<h2 className="p-3 border-bottom d-flex justify-content-between">공지사항 {user.role === "admin" ? (
<Button as={Link} to="/write"> 작성</Button>) : null}</h2>
<Accordion>
{notices.map((notice, index) => <CARD card_id={notice._id} card_index={index} title={notice.notice_title} date={notice.post_date} content={notice.notice_content} admin={user.role} />
)}
</Accordion>
</Col>
</Row>
</Container>
</div>
)
}
export default Notice
\ No newline at end of file
import React, { useState } from 'react';
import { Field, Formik } from 'formik';
import * as Yup from 'yup';
import axios from 'axios';
import { Link, Redirect } from 'react-router-dom';
import styled from 'styled-components';
import { Col, Container, Navbar, Button } from 'react-bootstrap';
const Menu = styled(Navbar)`
background-color: #7B031D;
a {
color : white;
}
`
const Wow = styled.div`
height: 90vh;
& #reCheck::after {
content: '비밀번호를 다시 입력하세요';
}
& #reCheck:not(.right) {
content: '비밀번호가 일치하지 않습니다.';
color: red;
}
`
function Signup() {
const [state, setState] = useState(false);
const [checkPw, setCheckPw] = useState(true);
if (state) {
return <Redirect to="/login" />;
}
return (
<div className="vh-100">
<Menu expand="md" variant="dark">
<Navbar.Brand>회원가입</Navbar.Brand>
</Menu>
<Container fluid>
<Wow className="row justify-content-center">
<Col md={3} xs={11} className="p-0">
<Formik
initialValues={{ name: '', id: '', password: '', password2: '', question: '', answer: '' }}
validationSchema={Yup.object({
name: Yup.string()
.required('이름을 입력해주세요.'),
id: Yup.string()
.required('학번을 입력해주세요.'),
password: Yup.string()
.required('비밀번호를 입력해주세요.')
.min(8, '8자 이상 입력해주세요.'),
password2: Yup.string()
.required('비밀번호를 다시 입력해주세요.')
.min(8, '8자 이상 입력해주세요.')
.oneOf([Yup.ref("password"), null], '비밀번호가 일치하지 않습니다.'),
answer: Yup.string()
.required('답변을 입력해주세요.'),
})}
onSubmit={(values, { setSubmitting }) => {
axios({
method: 'post',
url: '/users',
data: values,
}).then(res => {
if (res.status === 404) return alert(res.data.error)
alert("회원가입이 완료되었습니다!")
setState(true);
})
.catch(err => {
alert(err.error)
});
setTimeout(() => {
setSubmitting(false);
}, 400); // finish the cycle in handler
}}
>
{({
errors,
touched,
handleSubmit,
getFieldProps, // contain values, handleChange, handleBlur
isSubmitting,
}) => (
<form onSubmit={handleSubmit} className="d-flex flex-column pt-3">
<div className="form-group ">
<div className={touched.name && errors.name ? "text-danger" : ""}>이름을 입력하세요</div>
<input
className={(touched.name && errors.name ? 'form-control is-invalid' : "form-control")}
type="text"
name="name"
{...getFieldProps('name')}
placeholder="이름" />
</div>
<div className="form-group">
<div className={touched.id && errors.id ? "text-danger" : ""}>학번을 입력하세요</div>
<input
className={(touched.id && errors.id ? 'form-control is-invalid' : "form-control")}
type="text"
name="id"
{...getFieldProps('id')}
placeholder="학번/교번"
/>
</div>
<div className="form-group">
<div className={touched.password && errors.password ? "text-danger" : ""}>비밀번호를 입력하세요(8자리 이상)</div>
<input
className={(touched.password && errors.password ? 'form-control is-invalid' : "form-control")}
type="password"
name="password"
{...getFieldProps('password')}
placeholder="비밀번호"
/>
</div>
<div className="form-group">
{touched.password2 && errors.password2 ? setCheckPw(false) : null}
<div id="reCheck" className={checkPw ? "right" : "err"}></div>
<input
className={touched.password2 && errors.password2 ? "form-control is-invalid" : "form-control"}
type="password"
name="password2"
{...getFieldProps('password2')}
placeholder="비밀번호 확인"
/>
</div>
<div className="form-group">
<label>본인 확인 질문</label>
<Field as="select" name="question">
<option value="">질문을 선택하세요</option>
<option value="life">자신의 인생 좌우명은?</option>
<option value="school">자신이 다녔던 초등학교의 이름은?</option>
<option value="place">기억에 남는 추억의 장소는?</option>
</Field>
</div>
<div className="form-group">
<div className={touched.answer && errors.answer ? "text-danger" : ""}>답변을 입력해주세요.</div>
<input
className={(touched.answer && errors.answer ? 'form-control is-invalid' : "form-control")}
type="text"
name="answer"
{...getFieldProps('answer')}
placeholder="Input answer" />
</div>
<Button type="submit" variant="secondary" className="mb-2" disabled={isSubmitting}>회원가입</Button>
<Button variant="outline-secondary" as={Link} to="/login">로그인하러 가기</Button>
</form>
)}
</Formik>
</Col>
</Wow>
</Container>
</div >
);
}
export default Signup
\ No newline at end of file
import React, { useState, useEffect } from 'react';
import { Redirect } from 'react-router-dom';
import Menu from '../Components/Menu';
import * as Yup from 'yup';
import axios from 'axios';
import { Container, Row, Col, Button } from 'react-bootstrap';
import { Field, Formik } from 'formik';
function Write() {
const [state, setState] = useState(false);
const [isadmin, setIsadmin] = useState({ ok: "" });
const [user, setUser] = useState({ name: "" })
useEffect(() => {
acheck();
}, [])
if (isadmin.ok === "no") return <Redirect to="/" />;
if (state) {
return <Redirect to="/notice" />;
}
function acheck() {
axios.get(`/users/admin/${localStorage.getItem('_id')}`, {
headers: { authorization: localStorage.getItem('token') },
})
.then(res => {
if (res.status !== 201) {
alert(res.data.error);
console.log(res.data)
setIsadmin({ ok: "no" });
}
setUser({ name: res.data.name })
}).catch(err => {
alert(err.error)
});
}
return (
<div>
<Menu />
<Container fluid>
<Row className="justify-content-center">
<Col md={5} xs={11} className="pt-3" >
<Formik
initialValues={{ name: user.name, title: '', content: '' }}
enableReinitialize={true}
validationSchema={Yup.object({
title: Yup.string()
.required('제목을 입력해주세요.'),
content: Yup.string()
.required('내용을 입력해주세요.'),
})}
onSubmit={(values, { setSubmitting }) => {
console.log(values)
axios({
method: 'post',
url: '/writes',
data: values,
}).then(res => {
if (res.status === 404) return alert(res.data.error)
alert("공지 등록이 완료되었습니다.")
setState(true);
})
.catch(err => {
alert(err.error)
});
setTimeout(() => {
setSubmitting(false);
}, 400); // finish the cycle in handler
}}
>{({
errors,
touched,
handleSubmit,
getFieldProps, // contain values, handleChange, handleBlur
isSubmitting,
}) => (
<form onSubmit={handleSubmit} className="d-flex flex-column">
<div className="form-group">
<div className={touched.name && errors.name ? "text-danger" : ""}>제목</div>
<input className={(touched.name && errors.name ? 'form-control is-invalid' : "form-control")}
type="text"
title="title"
{...getFieldProps('title')}
placeholder="제목" />
</div>
<div className="form-group">
<div className={touched.name && errors.name ? "text-danger" : ""}>내용</div>
<Field as="textarea" rows={8} style={{ "min-width": "100%" }}
{...getFieldProps('content')} />
</div>
<Button className="mb-2" variant="dark" type="submit" disabled={isSubmitting}>공지 등록</Button>
</form>
)}
</Formik>
</Col>
</Row>
</Container>
</div>
)
}
export default Write
\ No newline at end of file
<svg width="1em" height="1em" viewBox="0 0 16 16" class="bi bi-caret-left-fill" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
<path d="M3.86 8.753l5.482 4.796c.646.566 1.658.106 1.658-.753V3.204a1 1 0 0 0-1.659-.753l-5.48 4.796a1 1 0 0 0 0 1.506z"/>
</svg>
\ No newline at end of file
<svg width="1em" height="1em" viewBox="0 0 16 16" class="bi bi-caret-right-fill" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
<path d="M12.14 8.753l-5.482 4.796c-.646.566-1.658.106-1.658-.753V3.204a1 1 0 0 1 1.659-.753l5.48 4.796a1 1 0 0 1 0 1.506z"/>
</svg>
\ No newline at end of file
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment