diff --git a/src/App.css b/src/App.css index 720e392..f59b5c5 100644 --- a/src/App.css +++ b/src/App.css @@ -2,6 +2,6 @@ text-align: center; height: 100vh; width: 100vw; - min-height: 700px; + min-height: 680px; display: flex; } \ No newline at end of file diff --git a/src/App.js b/src/App.js index d9b4345..4938896 100644 --- a/src/App.js +++ b/src/App.js @@ -17,6 +17,11 @@ import ResourceManage from 'pages/admin/resource/ResourceManage'; import ResourceManageAdd from "./pages/admin/resource/ResourceManageAdd"; import ResourceManageDetail from 'pages/admin/resource/ResourceManageDetail'; import OfficeManageDetail from 'pages/admin/office/OfficeManageDetail'; +import SelectCar from "./pages/basic/booking/car/SelectCar"; +import CarBooking from "./pages/basic/booking/car/CarBooking"; +import CarBookingCheck from "./pages/basic/booking/car/CarBookingCheck"; +import CarManage from "./pages/admin/car/CarManage"; +import CarBookingManage from "./pages/admin/carBookings/CarBookingManage"; function App() { @@ -34,13 +39,16 @@ function App() { }> } /> } /> + } /> + } /> + } /> + } /> } /> } /> } /> } /> } /> - } /> - } /> + } /> } /> } /> } /> @@ -51,6 +59,9 @@ function App() { } /> } /> } /> + } /> + } /> + } /> } /> diff --git a/src/components/card/ResourceInfo.js b/src/components/card/ResourceInfo.js index b78d63a..73befe6 100644 --- a/src/components/card/ResourceInfo.js +++ b/src/components/card/ResourceInfo.js @@ -19,13 +19,16 @@ const ResourceTitleContainer = styled.div` align-items: center; ` -function moveToDetail(resourceId) { - window.location.href = "/resourceBooking/"+resourceId +function moveToDetail(id, type) { + if (type === 'resource') + window.location.href = "/resourceBooking/"+id + else if (type === 'car') + window.location.href = "/carBooking/"+id } function ResourceInfo(props) { return ( - moveToDetail(props.resourceId)}> + moveToDetail(props.id, props.type)}> diff --git a/src/components/sidebar/MenuLineStyle.js b/src/components/sidebar/MenuLineStyle.js index c3858a5..8b2ba34 100644 --- a/src/components/sidebar/MenuLineStyle.js +++ b/src/components/sidebar/MenuLineStyle.js @@ -3,7 +3,7 @@ import {Link} from "react-router-dom"; export const InactiveMenuLine = styled(Link)` padding: 3px 20px; - margin-bottom: 13px; + margin-bottom: 10px; cursor: pointer; display: flex; align-items: center; diff --git a/src/components/sidebar/Sidebar.js b/src/components/sidebar/Sidebar.js index bf94076..a4c50ac 100644 --- a/src/components/sidebar/Sidebar.js +++ b/src/components/sidebar/Sidebar.js @@ -45,13 +45,9 @@ const SubMenus = styled.div` display: ${props => props.active ? 'block' : 'none'} ` -const MyBox = styled.div` - margin-bottom: 20px; -` - const MyInfo = styled.div` padding: 3px 0; - margin: 0 20px 18px 20px; + margin: 0 0 10px 20px; width: fit-content; display: flex; align-items: center; @@ -60,6 +56,12 @@ const MyInfo = styled.div` border-left: 5px solid white; ` +const ManageBtn = styled.div` + text-decoration: underline; + cursor: pointer; + margin-left: 10px; +` + const Logout = styled(MyInfo)` color: #A65959; cursor: pointer; @@ -67,8 +69,8 @@ const Logout = styled(MyInfo)` // 해당 상위 메뉴의 하위 메뉴 활성화 여부 function useIsSubMenuActive(subMenuList) { - var currentPath = useLocation().pathname - for (var i = 0; i < subMenuList.length; i++) { + const currentPath = useLocation().pathname; + for (let i = 0; i < subMenuList.length; i++) { if (currentPath.startsWith(subMenuList[i].path)) return true } @@ -101,9 +103,8 @@ function Sidebar() { navigateToLogin() const [userName, setUserName] = useState("") - const [info, setInfo] = useState("") - // 이름, 직급 + // 이름 const getUserInfo = () => { UsersAxios.get("/position", { headers: { @@ -112,7 +113,6 @@ function Sidebar() { }) .then((response) => { setUserName(response.data.data.name) - setInfo(response.data.data.position) }) .catch((error) => { basicError(error) @@ -157,13 +157,13 @@ function Sidebar() { - +
{/* 사원 정보 */} - {userName} {info} + {userName}관리 {/* 로그아웃 */} 로그아웃 - - +
+ ) } diff --git a/src/pages/admin/car/CarManage.js b/src/pages/admin/car/CarManage.js new file mode 100644 index 0000000..8521c94 --- /dev/null +++ b/src/pages/admin/car/CarManage.js @@ -0,0 +1,80 @@ +import React, {useEffect, useState} from "react"; +import {RightContainer, TitleText, WhiteContainer} from "components/rightContainer/RightContainer"; +import {Bar, BookedTable, BookedThead, NoLineTr, TableContainer} from "pages/basic/myBookings/BookedList"; +import CarManageTableCell from "./CarManageTableCell"; +import ManageSearchBar from "components/searchBar/ManageSearchBar"; +import {getToken} from "utils/IsLoginUtil"; +import {basicError} from "utils/ErrorHandlerUtil"; +import {AdminCarsAxios} from "api/AxiosApi"; + +function CarManage(props) { + + const [cars, setCars] = useState([]); + + const getCars = (name) => { + AdminCarsAxios.get(`?keyword=${name}`, { + headers: { + Authorization: getToken() + } + }) + .then((Response) => { setCars(Response.data.data.content) }) + .catch((error) => {basicError(error)}) + }; + + const searchCars = (e) => { + getCars(e.target.value) + }; + + useEffect(() => { + getCars(""); + }, []) + + const moveToAdd = () => { + window.location.href = `/admin/cars/add` + } + + return ( + + 차량 관리 + + + + + + + + 차량명 + 제조사 + 현재위치 + 책임자 + 설명 + + + + + { cars.length === 0 ? + + 차량 내역이 없습니다. + + : cars.map((car) => + + )} + + + + + + ); +} + +export default CarManage; diff --git a/src/pages/admin/car/CarManageAdd.js b/src/pages/admin/car/CarManageAdd.js new file mode 100644 index 0000000..3b269e7 --- /dev/null +++ b/src/pages/admin/car/CarManageAdd.js @@ -0,0 +1,407 @@ +import React, {useEffect, useRef, useState} from "react"; +import styled from "styled-components"; + +import {RightContainer, TitleText, WhiteContainer} from "components/rightContainer/RightContainer"; +import {Bar} from "../../basic/myBookings/BookedList"; +import {ManageAddButton, ManageAddButtonImage} from "components/searchBar/ManageSearchBar"; + +import {getToken} from "utils/IsLoginUtil"; +import {basicError} from "utils/ErrorHandlerUtil"; +import {AdminResourcesAxios, ImageUrlAxios, ResourcesAxios, UsersAxios} from "api/AxiosApi"; + +import AddImageImage from "../../../assets/images/AddImage.svg" +import SearchButtonImage from "../../../assets/images/SearchPlus.svg" +import {ExitBtn} from "../../../components/modal/BigModal"; +import axios from "axios"; +import {useParams} from "react-router-dom"; + +const MarginWhiteContainer = styled(WhiteContainer)` + padding: 50px; + box-sizing: border-box; +` + +const ColumnContainer = styled.div` + height: 40px; + display: flex; + justify-content: flex-start; + align-items: center; + margin-bottom: 40px; +` + +const TitleLabel = styled.label` + color: #8741CB; + font-size: 22px; + width: 130px; + text-align: left; +` + +const InfoInput = styled.input.attrs({type: 'text'})` + flex: 1; + height: 20px; + border-radius: 8px; + border: 2px solid #E6E6E6; + font-size: 20px; + padding: 10px; +` + +const StaffInputContainer = styled.div` + width: 30%; + height: 100%; + display: block; + z-index: 11; + background: white; +` + +const StaffInfoInput = styled(InfoInput)` + width: 100%; +` + +const StaffSelectUl = styled.div` + width: 100%; + max-height: 120px; + overflow: scroll; + border-radius: 0 0 8px 8px; + border: 2px solid #E6E6E6; + padding: 10px; + z-index: 3; + background: white; + margin-top: -5px; +` + +const StaffNameLabel = styled.p` + color: #4C4C4C; + font-size: 20px; + text-align: left; + z-index: 4; +` + +const DescriptionContainer = styled(ColumnContainer)` + height: 60px; +` + +const DescriptionInput = styled.textarea` + flex: 1; + height: 100%; + border-radius: 8px; + border: 2px solid #E6E6E6; + font-size: 20px; + padding: 10px; +` + +const AddButtonContainer = styled.div` + width: 90%; + height: 50px; + display: flex; + justify-content: flex-end; + align-items: center; + margin: -50px 80px 0 80px; +` + +const ImageAddContainer = styled.div` + width: 112px; + height: 40px; + flex-shrink: 0; + margin-left: 52px; +` + +const ImageAddButton = styled.img` + width: 100%; + height: 100%; +` + +const ImageInfoContainer = styled.div` + width: 32%; + height: 40px; + border-radius: 8px; + border: 2px solid #E6E6E6; + display: flex; + justify-content: space-between; +` + +const ImageInfoLabel = styled.label` + font-size: 20px; + padding: 5px; + margin-left: 20px; +` + +function CarManageAdd(props) { + + let {resourceId} = useParams() + const [name, setName] = useState(""); + const [place, setPlace] = useState(""); + const [staff, setStaff] = useState({ + userId: 1, + name: "" + }); + const [staffList, setStaffList] = useState([]); + const [description, setDescription] = useState(""); + const [imageFile, setImageFile] = useState(null); + const [imageUrl, setImageUrl] = useState(null); + const [isUpload, setIsUpload] = useState(false); + + + const [isFocusStaffInput, setIsFocusStaffInput] = useState(false); + + const [resourceInfo, setResourceInfo] = useState(null) + + const changeName = (e) => { + setName(e.target.value); + }; + + const changePlace = (e) => { + setPlace(e.target.value); + }; + + const changeStaff = (e) => { + setStaff({...staff, name: e.target.value}); + }; + + const changeDescription = (e) => { + setDescription(e.target.value) + }; + + const imageInput = useRef(null); + + const changeImageFile = () => { + imageInput.current.click(); + }; + + const handleChange = (e) => { + setImageFile(e.target.files[0]) + }; + + const deleteImageFile = () => { + setImageFile(null); + }; + + const getImageUrl = () => { + if (resourceInfo !== null) { + if (imageFile !== null) { + ImageUrlAxios.get(`?ext=${imageFile.type.split("/", 2)[1]}&dir=photo`) + .then((Response) => { + setImageUrl(Response.data); + }) + .catch((error) => { + console.log(error) + }); + } else { + editResource(); + } + } else { + if (imageFile !== null) { + ImageUrlAxios.get(`?ext=${imageFile.type.split("/", 2)[1]}&dir=photo`) + .then((Response) => { + setImageUrl(Response.data); + }) + .catch((error) => { + console.log(error) + }); + } else { + alert("이미지를 업로드해주세요."); + } + } + } + + const uploadImage = () => { + const formData = new FormData(); + formData.append("files", imageFile); + + axios.put(imageUrl.presignedUrl, formData) + .then(function (rep) { + setIsUpload(true); + }) + .catch(function (err) { + alert("이미지 등록에 실패했습니다. 다시 시도해주세요."); + }); + } + + const addResource = () => { + AdminResourcesAxios.post(``, { + responsibility: staff.userId, + description: description, + location: place, + name: name, + imgKey: imageUrl.imgKey, + }, + { + headers: { + Authorization: getToken() + }, + }) + .then((Response) => { + alert("장비 등록이 완료되었습니다."); + window.location.href = `/admin/resources` + }) + .catch((error) => { + basicError(error) + }); + }; + + const editResource = () => { + + + AdminResourcesAxios.patch(`/${resourceId}`, { + responsibility: staff.userId, + description: description, + location: place, + name: name, + imgKey: imageFile === null ? imageUrl : imageUrl.imgKey, + }, + { + headers: { + Authorization: getToken() + }, + }) + .then((Response) => { + alert("장비 수정이 완료되었습니다."); + window.location.href = `/admin/resources/${resourceId}` + }) + .catch((error) => { + basicError(error) + }); + } + + const getResourceInfo = () => { + ResourcesAxios.get(`/${resourceId}`, { + headers: { + Authorization: getToken() + } + }) + .then((Response) => { + setResourceInfo(Response.data.data); + setStaff({ + ...staff, + name: Response.data.data.responsibilityName, + userId: Response.data.data.responsibilityId + }); + setDescription(Response.data.data.description); + setPlace(Response.data.data.location); + setName(Response.data.data.name); + setImageUrl(Response.data.data.imgUrl) + }) + .catch((Error) => { + basicError(Error) + console.log(Error) + window.alert("장비 정보를 불러올 수 없습니댜.") + window.history.back() + }) + } + + + const getStaffList = () => { + UsersAxios.get(`managers?name=${staff.name}`, { + headers: { + Authorization: getToken() + } + }) + .then((Response) => { + console.log(Response.data.data) + setStaffList(Response.data.data.responsibilityList); + }) + .catch((error) => { + basicError(error) + }); + }; + + useEffect(() => { + if (resourceId !== undefined) { + getResourceInfo(); + } + }, []) + + useEffect(() => { + if (resourceInfo !== null && imageFile === null) { + return; + } + if (imageUrl !== null) { + if (staff.userId === -1) { + alert("책임자를 선택해주세요."); + } else { + uploadImage(); + } + } + }, [imageUrl]); + + useEffect(() => { + if (isUpload) { + if (resourceInfo !== null) { + editResource(); + } else { + addResource(); + } + } + }, [isUpload]); + + useEffect(() => { + getStaffList(); + }, [staff.name]); + + return ( + + {resourceInfo === null ? "장비 추가" : "장비 수정"} + + + + 장비명 + + + + + 보관장소 + + + + + 책임자 + + setIsFocusStaffInput(true)} + onBlur={() => setIsFocusStaffInput(false)}/> + {isFocusStaffInput && + + {staffList.map((staff, index) => + setStaff(staff)}>{staff.name} + )} + + } + + + + + + 설명 + + + + + 첨부사진 + + {imageFile !== null ? imageFile.name : ""} + {imageFile === null ? <> : X} + + + + + + + + + + + {resourceInfo === null ? "대여 장비 추가" : "대여 장비 수정"} + + + + + + ); +} + +export default CarManageAdd; diff --git a/src/pages/admin/car/CarManageDetail.js b/src/pages/admin/car/CarManageDetail.js new file mode 100644 index 0000000..81cd12e --- /dev/null +++ b/src/pages/admin/car/CarManageDetail.js @@ -0,0 +1,184 @@ +import React, {useEffect, useState} from 'react'; +import styled from "styled-components" +import {AdminResourcesAxios, ResourcesAxios} from 'api/AxiosApi'; +import {useParams} from 'react-router-dom'; +import Capsule from 'components/capsule/Capsule'; +import {basicError} from 'utils/ErrorHandlerUtil'; +import {Bar} from 'pages/basic/myBookings/BookedList'; +import {RightContainer, TitleText, WhiteContainer} from 'components/rightContainer/RightContainer'; +import { + MainTextContainer, + NameSubTitleText, + SubTextContainer, + DetailSubTitleText +} from 'components/officeBooking/SubTitleBar'; +import {getToken} from 'utils/IsLoginUtil'; +import MoreButtonIcon from "../../../assets/images/button/triple_dot_icon.svg" +import ResourceDetailInfo from "../../../components/card/ResourceDetailInfo"; +import ImageFullButton from "../../../components/button/ImageFullButton"; + +export const MoreButton = styled.button` + border: none; + background-color: #2A3042; + float: right; +` +export const OptionsView = styled.div` + display: ${props => props.isShowing ? 'table-cell' : 'none'}; + width: 85px; + height: 96px; + background: white; + border: 1px solid #545F71; + border-radius: 12px; + position: absolute; + top: 35px; + right: 30px; + vertical-align: middle; +` + +export const OptionButton = styled.button` + width: 85px; + height: 48px; + background: none; + border: none; + color: ${props => props.isDelete ? '#A65959' : '#545F71'}; + font-size: 16px; +` + +export const InfoTable = styled.table` + width: 94%; + margin: 40px 0 40px 40px; + border-collapse: collapse; + border: 1px solid #959494; + font-size: 17px; + align: center; +` + +export const InfoTableData = styled.td` + border: 1px solid #959494; + height: 45px +` + +function CarManageDetail() { + let {resourceId} = useParams() + + const [isShowingOptions, setOptionViewShowing] = useState(false) + const [resourceInfo, setResourceInfo] = useState([]) + const [bookingsInfo, setBookingsInfo] = useState([]) + + const getResourceInfo = () => { + ResourcesAxios.get(`/${resourceId}`, { + headers: { + Authorization: getToken() + } + }) + .then((Response) => { + console.log(Response.data.data) + setResourceInfo(Response.data.data) + }) + .catch((Error) => { + basicError(Error) + console.log(Error) + window.alert("장비 정보를 불러올 수 없습니댜.") + window.history.back() + }) + } + + const getResourceBookingListInfo = () => { + AdminResourcesAxios.get(`${resourceId}`, { + headers: { + Authorization: getToken() + } + }) + .then((Response) => { + console.log(Response.data.data) + setBookingsInfo(Response.data.data.resourcesLists) + }) + .catch((Error) => { + basicError(Error) + console.log(Error) + window.alert("예약 정보를 불러올 수 없습니댜.") + window.history.back() + }) + } + + const deleteResource = () => { + if (window.confirm("장비을 삭제하시겠습니까?")) { + AdminResourcesAxios.delete(`${resourceId}`, { + headers: { + Authorization: getToken() + } + }) + .then((Response) => { + if (Response.data.status === 200) { + alert('장비을 성공적으로 삭제하였습니다.') + window.history.back() + } + }) + .catch((Error) => { + basicError(Error) + console.log(Error) + }) + setOptionViewShowing(false) + } + } + + useEffect(() => { + getResourceInfo() + getResourceBookingListInfo() + }, []); + + return + 장비 관리 + + + +
+ {resourceInfo.name} + {resourceInfo.location} +
+ { + setOptionViewShowing(!isShowingOptions) + }}/> + + { + window.location.href = `/admin/resources/edit/${resourceId}` + }}>수정 + 삭제 + +
+ + + +
+ +
+ + + + 요청자 + 예약일자 + 목적 + 상태 + + {bookingsInfo.map(function (info) { + return ( + + {info.reservatorName} ({info.reservatorPhone}) + {info.startDateTime} ~
{info.endDateTime}
+ {info.goal} + {info.bookingStatus} + + ) + })} +
+ +
+
+} + +export default CarManageDetail; diff --git a/src/pages/admin/car/CarManageTableCell.js b/src/pages/admin/car/CarManageTableCell.js new file mode 100644 index 0000000..d3a7a6e --- /dev/null +++ b/src/pages/admin/car/CarManageTableCell.js @@ -0,0 +1,34 @@ +import React from 'react'; +import {BookedLineTr} from 'pages/basic/myBookings/BookedList'; +import {Toggle} from "components/toggle/Toggle"; +import {Link} from "react-router-dom"; +import {AdminCarsAxios} from "api/AxiosApi"; +import {getToken} from "utils/IsLoginUtil"; +import {basicError} from "utils/ErrorHandlerUtil"; + +function CarManageTableCell(props) { + + const changeToggle = (isEnable) => { + AdminCarsAxios.patch(`/${props.id}/activation`, null, { + headers: { + Authorization: getToken() + } + }) + .then((Response) => { }) + .catch((error) => {basicError(error)}) + console.log(isEnable) + } + + return ( + + {props.name} + {props.manufacturer} + {props.location} + {props.user}
({props.userPhone}) + {props.description} + +
+ ) +} + +export default CarManageTableCell; \ No newline at end of file diff --git a/src/pages/admin/carBookings/CarBookingManage.js b/src/pages/admin/carBookings/CarBookingManage.js new file mode 100644 index 0000000..5ea9ab8 --- /dev/null +++ b/src/pages/admin/carBookings/CarBookingManage.js @@ -0,0 +1,68 @@ +import React, {useEffect, useState} from "react"; +import {RightContainer, TitleText, WhiteContainer} from "components/rightContainer/RightContainer"; +import {Bar, BookedTable, BookedThead, TableContainer} from "../../basic/myBookings/BookedList"; +import CarBookingManageCell from "./CarBookingManageCell"; +import {AdminBookingAxios} from "api/AxiosApi"; +import {getToken} from "utils/IsLoginUtil"; +import {basicError} from "utils/ErrorHandlerUtil"; + + +function CarBookingManage(props) { + + const [carBookings, setCarBookings] = useState([]); + + useEffect(() => { + getCarBooking(); + }, []) + + const getCarBooking = () => { + AdminBookingAxios.get("cars", { + headers: { + Authorization: getToken() + } + }) + .then((Response) => { setCarBookings(Response.data.data.content) }) + .catch((error) => {basicError(error)}) + } + + return ( + + 차량 예약 관리 + + + + + + + + 차량명 + 현재위치 + 예약일시 + 예약자 + 상태 + 설정 + + + + {carBookings.map((carBooking, index) => + + )} + + + + + + ); +} + +export default CarBookingManage; \ No newline at end of file diff --git a/src/pages/admin/carBookings/CarBookingManageCell.js b/src/pages/admin/carBookings/CarBookingManageCell.js new file mode 100644 index 0000000..7ba8b0b --- /dev/null +++ b/src/pages/admin/carBookings/CarBookingManageCell.js @@ -0,0 +1,96 @@ +import React from 'react'; +import {BookedLineTr} from '../../basic/myBookings/BookedList'; +import {StatusCircle, StatusContainer, StatusText} from 'components/booking/StatusTag'; +import {BOOKED, findStatus, USING} from 'constants/BookingStatus'; +import {AdminBookingAxios} from 'api/AxiosApi'; +import {getToken} from 'utils/IsLoginUtil'; +import {basicError} from 'utils/ErrorHandlerUtil'; +import {SettingButton, SettingButtonContainer} from "../officeBookings/OfficeBookingManageCell"; + +function CarBookingManageCell(props) { + + const rejectResource = () => { + if (window.confirm(`${props.name}의 예약을 반려하시겠습니까?`)) { + AdminBookingAxios.patch(`cars/${props.id}/reject`, null, { + headers: { + Authorization: getToken() + } + }) + .then((Response) => { + alert('반려 완료되었습니다.') + window.location.reload() + }) + .catch((error) => { + basicError(error) + }) + props.refresh() + } else { + alert("예약 반려를 취소하셨습니다.") + } + }; + + const returnResource = () => { + if (window.confirm(`${props.name}를 반납하시겠습니까?`)) { + AdminBookingAxios.patch(`cars/${props.id}/return`, null, { + headers: { + Authorization: getToken() + } + }) + .then((Response) => { + alert('반납 완료되었습니다.') + window.location.reload() + }) + .catch((error) => { + basicError(error) + }) + + props.refresh() + } else { + alert("반납을 취소하셨습니다.") + } + }; + + const moveToDetail = () => { + window.location.href = `/admin/carBooking/${props.id}` + }; + + var status = findStatus(props.status) + + var cancelButton = ( + 상세보기) + + var usingButton = ( + + 반납 | 상세보기 + ) + + var bookingButton = ( + + 반려 | 상세보기 + + ); + + return ( + + {props.name} + {props.location} + {props.startDateTime} ~
{props.endDateTime} + {props.reservatorName}
({props.reservatorPhone}) + + + + {props.status} + + + + {status === USING ? usingButton : + status === BOOKED ? bookingButton : cancelButton + } + +
+ ) +} + +export default CarBookingManageCell; diff --git a/src/pages/admin/resource/ResourceManage.js b/src/pages/admin/resource/ResourceManage.js index 5ec13fb..b96f527 100644 --- a/src/pages/admin/resource/ResourceManage.js +++ b/src/pages/admin/resource/ResourceManage.js @@ -49,8 +49,9 @@ function ResourceManage(props) { 장비명 - 보관장소 - 책임자 + 제조사 + 현재위치 + 책임자 설명 diff --git a/src/pages/admin/resource/ResourceManageTableCell.js b/src/pages/admin/resource/ResourceManageTableCell.js index d77d0ec..ecd9af9 100644 --- a/src/pages/admin/resource/ResourceManageTableCell.js +++ b/src/pages/admin/resource/ResourceManageTableCell.js @@ -22,8 +22,9 @@ function ResourceManageTableCell(props) { return ( {props.name} + {props.manufacturer} {props.location} - {props.user}({props.userPhone}) + {props.user}
({props.userPhone}) {props.description}
diff --git a/src/pages/basic/booking/car/CarBooking.js b/src/pages/basic/booking/car/CarBooking.js new file mode 100644 index 0000000..a6f6b38 --- /dev/null +++ b/src/pages/basic/booking/car/CarBooking.js @@ -0,0 +1,234 @@ +import React, {useEffect, useState} from 'react'; +import styled from "styled-components" +import Calendar from 'react-calendar'; +import 'react-calendar/dist/Calendar.css'; +import moment from 'moment'; +import {CarsAxios, ResourcesAxios} from 'api/AxiosApi'; +import {useParams} from 'react-router-dom'; +import Capsule from 'components/capsule/Capsule'; +import {DetailSubTitleText, NameSubTitleText} from 'components/officeBooking/SubTitleBar'; +import {BookingPurposeContainer} from 'components/officeBooking/BookingPurpose'; +import {BookingContentContainer, RequestButtonContainer} from 'components/officeBooking/BookingTimeBar'; +import {RightContainer, TitleText, WhiteContainer} from 'components/rightContainer/RightContainer'; +import styles from "pages/basic/booking/resource/CustomCalendar.css"; +import {basicError} from 'utils/ErrorHandlerUtil'; +import SmallButton from 'components/button/SmallButton'; +import {Bar} from 'pages/basic/myBookings/BookedList'; +import {getToken} from 'utils/IsLoginUtil'; +import ResourceDetailInfo from "components/card/ResourceDetailInfo"; + +var startDate = ''; +var endDate = ''; + +export const BookingDateText = styled.text` + padding-left: 10px; + color: #575757; + font-size: 22px; + text-align: left; +` + +export const PurposeTextarea = styled.textarea` + width: 100%; + padding: 10px; + border-radius: 12px; + border: 1px solid #E6E6E6; + font-size: 20px; + line-height: 25px; + text-align: left; + margin: 0 20px 0 10px; + box-sizing: border-box; +` + +export const BookingDateContainer = styled.div` + padding-top: 7%; +` + +export const DateContainer = styled.div` + padding-left: 1%; +` + +var currentMonth = moment(new Date()).format('YYYY-MM') + +function CarBooking(props) { + let {carId} = useParams(); + + const [carInfo, setCarInfo] = useState([]); + const [dates, setBookedDates] = useState([]); + var [start, setStartDate] = useState(); + var [end, setEndDate] = useState(); + const [changed, setCurrentMonth] = useState(); + + const getCarInfo = () => { + CarsAxios.get(`/${carId}`, { + headers: { + Authorization: getToken() + } + }) + .then((Response) => { + setCarInfo(Response.data.data) + }) + .catch((Error) => { + basicError(Error) + console.log(Error) + window.alert("차량 정보를 불러올 수 없습니댜.") + window.history.back() + }); + }; + + const getBookedDates = () => { + const params = {month: currentMonth}; + ResourcesAxios.get(`/${carId}/booking-state`, { + params, headers: { + Authorization: getToken() + } + }) + .then((Response) => { + var temp = []; + Response.data.data.map(function (date) { + temp.push(new Date(date)) + }) + setBookedDates(temp) + }) + .catch((Error) => { + basicError(Error) + console.log(Error) + window.alert("예약 정보를 불러올 수 없습니댜.") + window.history.back() + }); + } + + const changeDate = e => { + const startDateFormat = moment(e[0]).format("YYYY-MM-DD"); + const endDateFormat = moment(e[1]).format("YYYY-MM-DD"); + + setStartDate(startDateFormat) + setEndDate(endDateFormat) + + startDate = startDateFormat + endDate = endDateFormat + + for (var i = 0; i < dates.length; i++) { + var temp = new Date(dates[i]) + temp = moment(temp).format("YYYY-MM-DD") + + if (startDateFormat <= temp && endDateFormat >= temp) { + alert('예약된 일자를 포함한 날짜는 선택할 수 없습니다.') + startDate = ''; + endDate = ''; + setStartDate(startDate) + setEndDate(endDate) + window.location.reload() + return + } + } + }; + + const onActiveStartDateChange = (e) => { + const changed = moment(e.activeStartDate).format("YYYY-MM") + setCurrentMonth(changed) + currentMonth = changed + getBookedDates() + } + + const requestBookingResource = () => { + var bookingPurpose = document.getElementById("bookingPurpose").value; + + if (window.confirm("예약하시겠습니까?")) { + ResourcesAxios.post(`/${carId}`, + { + "endDate": endDate, + "memo": bookingPurpose, + "startDate": startDate + }, + { + headers: {Authorization: getToken()} + }, + ) + .then(function (response) { + if (response.data.status === '200') { + alert('예약에 성공하였습니다!') + } else { + alert(response.data.message); + } + window.location.reload() + }) + .catch((Error) => { + basicError(Error) + console.log(Error) + window.alert("차량 예약에 실패하였습니다.") + window.history.back() + }); + } + } + + useEffect(() => { + getCarInfo() + getBookedDates() + }, []); + + return ( + + 차량 예약 + + + + {carInfo.name} + {carInfo.location} + + + + + + + + {start || "시작일"} + ~ + {end || "마감일"} + + + moment(date).format("D")} + minDate={new Date()} + showNeighboringMonth={false} + next2Label={null} + prev2Label={null} + formatShortWeekday={(locale, date) => + ["S", "M", "T", "W", "T", "F", "S"][date.getDay()] + } + tileDisabled={({date, view}) => + (view === 'month') && + dates.some(disabledDate => + date.getFullYear() === disabledDate.getFullYear() && + date.getMonth() === disabledDate.getMonth() && + date.getDate() === disabledDate.getDate() + )} + onActiveStartDateChange={onActiveStartDateChange} + + /> + + + + + + + + + + + + + + + + + + ) +} + +export default CarBooking; \ No newline at end of file diff --git a/src/pages/basic/booking/car/CarBookingCheck.js b/src/pages/basic/booking/car/CarBookingCheck.js new file mode 100644 index 0000000..8ff3f88 --- /dev/null +++ b/src/pages/basic/booking/car/CarBookingCheck.js @@ -0,0 +1,125 @@ +import React, {useEffect, useState} from 'react'; +import {AdminBookingAxios, BookingsAxios, CarsAxios} from 'api/AxiosApi'; +import {useParams} from 'react-router-dom'; +import Capsule from 'components/capsule/Capsule'; +import {DetailSubTitleText, NameSubTitleText} from 'components/officeBooking/SubTitleBar'; +import {BookingPurposeContainer, PurposeContainer} from 'components/officeBooking/BookingPurpose'; +import {BookingContentContainer} from 'components/officeBooking/BookingTimeBar'; +import {StatusCircle, StatusContainer, StatusText} from 'components/booking/StatusTag'; +import {findStatus} from 'constants/BookingStatus'; +import {RightContainer, TitleText, WhiteContainer} from 'components/rightContainer/RightContainer'; +import {BookingDateText} from './CarBooking'; +import {getToken} from 'utils/IsLoginUtil'; +import {basicError} from 'utils/ErrorHandlerUtil'; +import {Bar} from 'pages/basic/myBookings/BookedList'; +import ResourceDetailInfo from "components/card/ResourceDetailInfo"; + +function CarBookingCheck(props) { + let {bookingId} = useParams(); + + const [carInfo, setCarInfo] = useState([]); + const [bookingInfo, setBookingDetail] = useState([]); + const [bookingStatus, setStatus] = useState([]); + + const getCarInfo = (carId) => { + CarsAxios.get(`/${carId}`, + { + headers: { + Authorization: getToken() + } + }) + .then((Response) => { + setCarInfo(Response.data.data) + }) + .catch((Error) => { + basicError(Error) + console.log(Error) + window.alert("차량 정보를 불러올 수 없습니댜.") + window.history.back() + }); + }; + const getBookingInfo = () => { + (props.isAdmin + ? AdminBookingAxios.get(`/cars/${bookingId}`, { + headers: { + Authorization: getToken() + } + }) + : BookingsAxios.get(`/cars/${bookingId}`, + { + headers: { + Authorization: getToken() + } + })) + .then((Response) => { + setBookingDetail(Response.data.data) + setStatus(findStatus(Response.data.data.status)) + getCarInfo(Response.data.data.carId) + }) + .catch((Error) => { + basicError(Error) + console.log(Error) + window.alert("예약 정보를 불러올 수 없습니댜.") + window.history.back() + }); + }; + + useEffect(() => { + getBookingInfo(); + }, []); + + return + 차량 예약 내역 + + + +
+ {carInfo.name}{carInfo.manufacture} + {carInfo.location} +
+ + + {bookingStatus.name} + +
+ + + + + +
+ {bookingInfo.startDate || "시작일"} + ~ + {bookingInfo.endDate || "마감일"} +
+
+ + + + {getReturnDateStr(bookingInfo.returnDateTime)} + + + + + + {(bookingInfo.memo === null || bookingInfo.memo === "") + ? '* 저장된 예약목적이 없습니다' + : bookingInfo.memo} + + + +
+
+} + +export default CarBookingCheck; + + +function getReturnDateStr(returnDateTime) { + return (returnDateTime == null) ? "미반납" : returnDateTime +} \ No newline at end of file diff --git a/src/pages/basic/booking/car/SelectCar.js b/src/pages/basic/booking/car/SelectCar.js new file mode 100644 index 0000000..3bc2d8b --- /dev/null +++ b/src/pages/basic/booking/car/SelectCar.js @@ -0,0 +1,112 @@ +import {RightContainer, TitleText, WhiteContainer} from "components/rightContainer/RightContainer"; +import SearchButtonImg from 'assets/images/Search.svg' +import React, {useEffect, useRef, useState} from "react"; +import ResourceInfo from "components/card/ResourceInfo"; +import {CarsAxios} from "api/AxiosApi"; +import {basicError} from 'utils/ErrorHandlerUtil'; +import {getToken} from "utils/IsLoginUtil"; +import {NoCard} from "components/card/Card"; +import ImagePaddingButton from "components/button/ImagePaddingButton"; +import {TimeDropBox} from "components/capsule/DropBox"; +import { + SearchBarContainer, + SearchDateContainer, + SearchDateInput, + SearchTextInput, + SearchTitleContainer, + SearchTitleText +} from "components/searchBar/SearchBar"; + +function SelectCar(props) { + + const [carList, setCarList] = useState([]); + const carName = useRef(""); + const [startDate, setStartDate] = useState(""); + const [endDate, setEndDate] = useState(""); + const [startTime, setStartTime] = useState("00:00"); + const [endTime, setEndTime] = useState("00:00"); + + useEffect(() => { + searchCar(); + }, []); + + const changeCarName = (e) => { + carName.current = e.target.value + searchCar() + } + + const changeStartDate = (e) => { + setStartDate(e.target.value) + } + + const changeEndDate = (e) => { + setEndDate(e.target.value) + } + + const changeStartTime = (e) => { + setStartTime(e.target.value) + } + + const changeEndTime = (e) => { + setEndTime(e.target.value) + } + + const searchCar = () => { + let url = `?carName=${carName.current}`; + if (startDate !== "" && endDate !== "") + url = `?carName=${carName.current}&startDate=${startDate} ${startTime}&endDate=${endDate} ${endTime}`; + CarsAxios.get(url, + { + headers: { + Authorization: getToken() + } + }) + .then((Response) => { + setCarList(Response.data.data.content) + }) + .catch((error) => { + basicError(error) + }) + } + + return ( + + 차량 예약 + + + 예약 가능 차량 검색 + + + + + + + + ~ + + + + + + + + +
+ {carList.length === 0 ? + 예약 가능한 차량이 없습니다. + : carList.map((car) => + )} +
+
+
+ ); +} + +export default SelectCar; diff --git a/src/pages/basic/booking/resource/ResourceBooking.js b/src/pages/basic/booking/resource/ResourceBooking.js index 29c58a0..8d17aea 100644 --- a/src/pages/basic/booking/resource/ResourceBooking.js +++ b/src/pages/basic/booking/resource/ResourceBooking.js @@ -6,17 +6,8 @@ import moment from 'moment'; import {ResourcesAxios} from 'api/AxiosApi'; import {useParams} from 'react-router-dom'; import Capsule from 'components/capsule/Capsule'; -import { - MainTextContainer, - NameSubTitleText, - SubTextContainer, - DetailSubTitleText -} from 'components/officeBooking/SubTitleBar'; -import { - BookingCapsuleContainer, - BookingPurposeContainer, - BookingPurposeTextFieldContainer -} from 'components/officeBooking/BookingPurpose'; +import {DetailSubTitleText, NameSubTitleText} from 'components/officeBooking/SubTitleBar'; +import {BookingPurposeContainer} from 'components/officeBooking/BookingPurpose'; import {BookingContentContainer, RequestButtonContainer} from 'components/officeBooking/BookingTimeBar'; import {RightContainer, TitleText, WhiteContainer} from 'components/rightContainer/RightContainer'; import styles from "./CustomCalendar.css"; diff --git a/src/pages/basic/booking/resource/ResourceBookingCheck.js b/src/pages/basic/booking/resource/ResourceBookingCheck.js index eac7143..c1ef0ec 100644 --- a/src/pages/basic/booking/resource/ResourceBookingCheck.js +++ b/src/pages/basic/booking/resource/ResourceBookingCheck.js @@ -1,18 +1,9 @@ -import React, {useEffect, useRef, useState} from 'react'; +import React, {useEffect, useState} from 'react'; import {AdminBookingAxios, BookingsAxios, ResourcesAxios} from 'api/AxiosApi'; import {useParams} from 'react-router-dom'; import Capsule from 'components/capsule/Capsule'; -import { - MainTextContainer, - NameSubTitleText, - SubTextContainer, - DetailSubTitleText -} from 'components/officeBooking/SubTitleBar'; -import { - BookingCapsuleContainer, - BookingPurposeContainer, - PurposeContainer -} from 'components/officeBooking/BookingPurpose'; +import {DetailSubTitleText, NameSubTitleText} from 'components/officeBooking/SubTitleBar'; +import {BookingPurposeContainer, PurposeContainer} from 'components/officeBooking/BookingPurpose'; import {BookingContentContainer} from 'components/officeBooking/BookingTimeBar'; import {StatusCircle, StatusContainer, StatusText} from 'components/booking/StatusTag'; import {findStatus} from 'constants/BookingStatus'; @@ -20,8 +11,8 @@ import {RightContainer, TitleText, WhiteContainer} from 'components/rightContain import {BookingDateText} from './ResourceBooking'; import {getToken} from 'utils/IsLoginUtil'; import {basicError} from 'utils/ErrorHandlerUtil'; -import {Bar} from '../../myBookings/BookedList'; -import ResourceDetailInfo from "../../../../components/card/ResourceDetailInfo"; +import {Bar} from 'pages/basic/myBookings/BookedList'; +import ResourceDetailInfo from "components/card/ResourceDetailInfo"; function ResourceBookingCheck(props) { let {bookingId} = useParams(); @@ -29,10 +20,9 @@ function ResourceBookingCheck(props) { const [resourceInfo, setResourceInfo] = useState([]); const [bookingInfo, setBookingDetail] = useState([]); const [bookingStatus, setStatus] = useState([]); - const resourceId = useRef(""); - const getResourceInfo = () => { - ResourcesAxios.get(`/${resourceId.current}`, + const getResourceInfo = (resourceId) => { + ResourcesAxios.get(`/${resourceId}`, { headers: { Authorization: getToken() @@ -64,8 +54,7 @@ function ResourceBookingCheck(props) { .then((Response) => { setBookingDetail(Response.data.data) setStatus(findStatus(Response.data.data.status)) - resourceId.current = Response.data.data.resourceId - getResourceInfo(resourceId.current) + getResourceInfo(Response.data.data.resourceId) }) .catch((Error) => { basicError(Error) @@ -76,7 +65,6 @@ function ResourceBookingCheck(props) { }; useEffect(() => { - getResourceInfo(); getBookingInfo(); }, []); @@ -86,8 +74,8 @@ function ResourceBookingCheck(props) {
- {resourceInfo.name} - {resourceInfo.category} + {resourceInfo.name}({resourceInfo.manufacture}) + {resourceInfo.location}
@@ -125,7 +113,6 @@ function ResourceBookingCheck(props) { -
} diff --git a/src/pages/basic/booking/resource/SelectResource.js b/src/pages/basic/booking/resource/SelectResource.js index f98376b..e9dc458 100644 --- a/src/pages/basic/booking/resource/SelectResource.js +++ b/src/pages/basic/booking/resource/SelectResource.js @@ -1,15 +1,13 @@ import {RightContainer, TitleText, WhiteContainer} from "components/rightContainer/RightContainer"; -import SearchButtonImg from '../../../../assets/images/Search.svg' +import SearchButtonImg from 'assets/images/Search.svg' import React, {useEffect, useRef, useState} from "react"; import ResourceInfo from "components/card/ResourceInfo"; import {ResourcesAxios} from "api/AxiosApi"; -import {useNavigate} from "react-router-dom"; import {basicError} from 'utils/ErrorHandlerUtil'; -import {getToken} from "../../../../utils/IsLoginUtil"; -import {NoCard} from "../../../../components/card/Card"; -import ImagePaddingButton from "../../../../components/button/ImagePaddingButton"; -import {DropBox, TimeDropBox} from "../../../../components/capsule/DropBox"; -import {TimeList} from "../../../../constants/ToggleList"; +import {getToken} from "utils/IsLoginUtil"; +import {NoCard} from "components/card/Card"; +import ImagePaddingButton from "components/button/ImagePaddingButton"; +import {TimeDropBox} from "components/capsule/DropBox"; import { SearchBarContainer, SearchDateContainer, @@ -17,10 +15,9 @@ import { SearchTextInput, SearchTitleContainer, SearchTitleText -} from "../../../../components/searchBar/SearchBar"; +} from "components/searchBar/SearchBar"; function SelectResource(props) { - const navigate = useNavigate(); const [resourceList, setResourceList] = useState([]); const resourceName = useRef(""); @@ -55,7 +52,6 @@ function SelectResource(props) { } const searchResource = () => { - // var bookingPurpose = document.getElementById("bookingPurpose").value; let url = `?resourceName=${resourceName.current}`; if (startDate !== "" && endDate !== "") url = `?resourceName=${resourceName.current}&startDate=${startDate} ${startTime}&endDate=${endDate} ${endTime}`; @@ -91,20 +87,22 @@ function SelectResource(props) { - +
{resourceList.length === 0 ? 예약 가능한 장비가 없습니다. - : resourceList.map((resource, index) => - + )} + description={resource.description} + type='resource'/>)}