diff --git a/src/asset/css/BookDetail.css b/src/asset/css/BookDetail.css index 1f6b4e6b..0b7a1e0d 100644 --- a/src/asset/css/BookDetail.css +++ b/src/asset/css/BookDetail.css @@ -37,9 +37,11 @@ .book-detail__photo { margin: 0 0rem 1rem 0; + width: 34.8rem; + height: 51.1rem; } -.book-detail__photo img { +.book-detail__photo-location { width: 34.8rem; height: 51.1rem; } @@ -107,20 +109,21 @@ font-weight: bold; } -.book-likes { - display: flex; - justify-content: center; -} - -.like_button_box { - font-size: 16px; -} - .like_button { background: none; - padding: 0; + padding: 1rem; border: 0; cursor: pointer; + font-size: 1.6rem; + border: solid 0.1rem #c0bebe; + border-radius: 1.5rem; + display: flex; + align-items: center; +} + +.like_button:disabled { + cursor: not-allowed; + pointer-events: none; } .like__icon { @@ -128,18 +131,36 @@ margin-right: 0.5rem; } +.book-detail_buttons { + display: flex; + justify-content: center; + gap: 1rem; +} + +.location_button { + background: none; + padding: 1rem; + border: 0; + cursor: pointer; + font-size: 1.6rem; + border: solid 0.1rem #c0bebe; + border-radius: 1.5rem; +} + @media screen and (min-width: 768px) and (max-width: 1200px) { .book-detail { width: 55%; } - .book-detail__photo img { + .book-detail__photo-location { width: 32rem; height: 47rem; } .book-detail__photo { margin: 0 0 1rem 0; + width: 32rem; + height: 47rem; } .book-detail__info-value { @@ -192,11 +213,9 @@ .book-detail__photo { margin: 0 auto 1rem auto !important; - width: 80%; - height: 25%; } - .book-detail__photo img { + .book-detail__photo-location { width: 100%; height: 100%; } diff --git a/src/asset/css/BookLocation.css b/src/asset/css/BookLocation.css new file mode 100644 index 00000000..e321bb5c --- /dev/null +++ b/src/asset/css/BookLocation.css @@ -0,0 +1,91 @@ +.book-location__map { + height: 100%; + background-color: #f4f4f4; + padding: 0 1rem 0 1rem; + display: grid; + grid-template-columns: repeat(4, 1fr); + grid-template-rows: repeat(10, 1fr); + grid-gap: 0.5rem; + grid-template-areas: + "cluster . . shelf-1" + "cluster selfservice . shelf-1" + "cluster entrance . return" + "cluster sofa-1 . shelf-2" + "cluster sofa-1 . shelf-2" + "cluster shelf-5 . sofa-3" + "cluster shelf-5 . sofa-3" + "cluster sofa-2 . shelf-3" + "cluster sofa-2 . shelf-3" + "cluster shelf-4 shelf-4 shelf-4"; +} + +.book-location__map > div { + border-radius: 0.5rem; + padding: 0.5rem; + display: flex; + justify-content: center; + align-items: center; + font-size: 1.2rem; + background-color: #e9e9e9; + color: #5f3f36; +} + +.book-location__map > .map-highlight { + background-color: #736757; + color: white; +} + +.book-location__map > .map-shelf { + font-weight: bold; + font-size: 1.5rem; +} + +.book-location__map > .map-selfservice { + grid-area: selfservice; +} + +.book-location__map > .map-entrance { + grid-area: entrance; + background-color: #f4f4f4; +} + +.book-location__map > .map-cluster { + grid-area: cluster; + background-color: #f4f4f4; +} + +.book-location__map > .map-return { + grid-area: return; +} + +.book-location__map > .map-sofa-1 { + grid-area: sofa-1; +} + +.book-location__map > .map-sofa-2 { + grid-area: sofa-2; +} + +.book-location__map > .map-sofa-3 { + grid-area: sofa-3; +} + +.book-location__map > .map-shelf-1 { + grid-area: shelf-1; +} + +.book-location__map > .map-shelf-2 { + grid-area: shelf-2; +} + +.book-location__map > .map-shelf-3 { + grid-area: shelf-3; +} + +.book-location__map > .map-shelf-4 { + grid-area: shelf-4; +} + +.book-location__map > .map-shelf-5 { + grid-area: shelf-5; +} diff --git a/src/component/book/BookDetail.tsx b/src/component/book/BookDetail.tsx index 0c99ec70..16c094c6 100644 --- a/src/component/book/BookDetail.tsx +++ b/src/component/book/BookDetail.tsx @@ -1,12 +1,14 @@ -import { useEffect, useRef } from "react"; +import { useEffect, useRef, useState } from "react"; import { useLocation, useParams } from "react-router-dom"; import { useGetBooksInfoId } from "~/api/books/useGetBooksInfoId"; import BookReservation from "~/component/book/BookReservation"; import BookStatus from "~/component/book/BookStatus"; +import BookLocation from "./location/BookLocation"; import Review from "~/component/book/review/Review"; import Banner from "~/component/utils/Banner"; import Image from "~/component/utils/Image"; import Like from "~/component/book/like/Like"; +import LocationButton from "~/component/book/location/LocationButton"; import TagWrapper from "~/component/book/tag/TagWrapper"; import "~/asset/css/BookDetail.css"; import { Book } from "~/type"; @@ -30,7 +32,7 @@ const BookDetail = () => { const myRef = useRef(null); const recentScrollPosition = useRef(0); const location = useLocation(); - + const [isBookLocationVisible, setIsBookLocationVisible] = useState(false); useEffect(() => { recentScrollPosition.current = window.scrollY; myRef.current?.scrollIntoView(); @@ -53,6 +55,10 @@ const BookDetail = () => { ); } + const handleBookLocationVisible = () => { + setIsBookLocationVisible(!isBookLocationVisible); + }; + const isAvailableReservation = () => { const { books } = bookDetailInfo; const noProblemBooksCnt = books?.filter(book => book.status === 0).length; @@ -83,10 +89,22 @@ const BookDetail = () => {
- {bookDetailInfo.title} + {isBookLocationVisible ? ( + + ) : ( + {bookDetailInfo.title} + )}
-
+
+
diff --git a/src/component/book/like/Like.tsx b/src/component/book/like/Like.tsx index ab658397..6cdcab68 100644 --- a/src/component/book/like/Like.tsx +++ b/src/component/book/like/Like.tsx @@ -1,6 +1,5 @@ import { useGetLike, usePostLike, useDeleteLike } from "~/api/like"; import { usePermission } from "~/hook/usePermission"; - import Image from "~/component/utils/Image"; import FilledLike from "~/asset/img/like_filled.svg"; import EmptyLike from "~/asset/img/like_empty.svg"; @@ -11,7 +10,6 @@ type Props = { const Like = ({ bookInfoId }: Props) => { const { is42Authenticated } = usePermission(); - const { like, setLike } = useGetLike({ bookInfoId: +bookInfoId }); const { setBookInfoId: requestdelete } = useDeleteLike({ setLike }); const { setBookInfoId: requestPost } = usePostLike({ setLike }); @@ -24,21 +22,22 @@ const Like = ({ bookInfoId }: Props) => { }; return ( -
- {is42Authenticated ? ( - - ) : null} - {`좋아요 ${like.likeNum}`} + ) : null} + {`좋아요 ${like.likeNum}`} +
); }; diff --git a/src/component/book/location/BookLocation.tsx b/src/component/book/location/BookLocation.tsx new file mode 100644 index 00000000..3e15f5fd --- /dev/null +++ b/src/component/book/location/BookLocation.tsx @@ -0,0 +1,28 @@ +import { BookInfo } from "../../../type"; +import "~/asset/css/BookLocation.css"; +import "~/asset/css/BookDetail.css"; +import { memo } from "react"; +import { findBookShelfIndex } from "~/util/bookShelfLocation"; +import BookLocationMap from "~/component/book/location/BookLocationMap"; + +type BookLocationProps = { + bookDetailInfo: BookInfo; +}; + +const BookLocation = memo(({ bookDetailInfo }: BookLocationProps) => { + const { books } = bookDetailInfo; + + if (books === undefined) { + return null; + } + + const callSignFirstChar = books[0].callSign[0].at(0) ?? ""; + const bookShelfIndex = findBookShelfIndex(callSignFirstChar); + return ( +
+ +
+ ); +}); + +export default BookLocation; diff --git a/src/component/book/location/BookLocationMap.tsx b/src/component/book/location/BookLocationMap.tsx new file mode 100644 index 00000000..648d7044 --- /dev/null +++ b/src/component/book/location/BookLocationMap.tsx @@ -0,0 +1,32 @@ +import { callSignAtShelf } from "~/util/bookShelfLocation"; + +type Props = { + highlightIndex: number; +}; + +const BookLocationMap = ({ highlightIndex }: Props) => { + const shelves = callSignAtShelf; + return ( +
+
무인대출/반납
+
소파
+
소파
+
소파
+
입구
+
반납서가
+
5클러스터
+ {shelves.map((shelf, index) => ( +
+ {shelf.length > 5 ? "비개발 도서" : shelf.join(" ")} +
+ ))} +
+ ); +}; + +export default BookLocationMap; diff --git a/src/component/book/location/LocationButton.tsx b/src/component/book/location/LocationButton.tsx new file mode 100644 index 00000000..11366b9c --- /dev/null +++ b/src/component/book/location/LocationButton.tsx @@ -0,0 +1,23 @@ +type Props = { + isBookLocationVisible: boolean; + onToggleVisibility: () => void; +}; + +const LocationButton = ({ + isBookLocationVisible, + onToggleVisibility, +}: Props) => { + return ( +
+ +
+ ); +}; + +export default LocationButton; diff --git a/src/util/bookShelfLocation.ts b/src/util/bookShelfLocation.ts new file mode 100644 index 00000000..ec2eb5dd --- /dev/null +++ b/src/util/bookShelfLocation.ts @@ -0,0 +1,29 @@ +const lowerCaseRange = Array.from( + { length: "z".charCodeAt(0) - "b".charCodeAt(0) + 1 }, + (_, i) => String.fromCharCode("b".charCodeAt(0) + i), +); + +/** +선반 별 청구기호 목록이 일정하지 않기 때문에 callSignAtShelf 배열을 생성 +*/ +export const callSignAtShelf = [ + ["A", "D", "F", "I"], + ["B", "E", "G", "N", "J"], + ["K", "H", "O"], + lowerCaseRange, + ["C", "L", "M", "a"], +]; + +/** +현재 책의 청구기호 첫 글자를 받아 현재 책이 위치한 선반 번호를 반환하는 함수 +* @param callSignFirstChar - 현재 책의 청구기호 첫 글자 +* @example const bookShelfIndex = findBookShelfIndex("A"); +* @returns {number} - 현재 책이 위치한 선반 번호 +*/ + +export const findBookShelfIndex = (callSignFirstChar: string) => { + const shelfIndex = callSignAtShelf.findIndex(shelf => + shelf.includes(callSignFirstChar), + ); + return shelfIndex; +};