diff --git a/src/asset/css/Tags.css b/src/asset/css/Tags.css index 0561c8ce..2d7fb64e 100644 --- a/src/asset/css/Tags.css +++ b/src/asset/css/Tags.css @@ -13,7 +13,7 @@ line-height: 3rem; font-size: 1.5rem; font-weight: bold; - padding: 1rem 2rem 1rem 2rem; + padding: 1rem 2rem 1rem 1rem; text-align: center; margin: 1rem; border-radius: 10px; @@ -43,12 +43,18 @@ } .button_tag-create-box { - position: flex; line-height: 4rem; font-size: large; margin: 1rem; background-color: rgba(95, 63, 54, 1); color: rgba(255, 255, 255, 0.9); + width: 100%; +} + +@media screen and (min-width: 767px) { + .button_tag-create-box { + width: 50%; + } } .button_tag-create-box::placeholder { @@ -66,7 +72,6 @@ height: 100%; min-height: 12rem; overflow: hidden; - padding: 1.5rem 1rem; margin: 6rem 0rem 0rem 0rem; background-color: rgb(232, 232, 232, 0.9); border-radius: 1rem; @@ -83,7 +88,6 @@ height: 100%; min-height: 12rem; overflow: hidden; - padding: 1.5rem 1rem; margin: -1.3rem 0rem 10rem 0rem; background-color: rgb(232, 232, 232, 0.9); border-radius: 1rem; @@ -116,7 +120,18 @@ background-color: rgba(00, 00, 00, 0); z-index: 1; top: 50%; - left: 50%; + left: 0; +} + +.button_tag-image-button-disabled { + display: flex; + cursor: not-allowed; + opacity: 0.4; + pointer-events: none; +} + +.tooltip_cursor-message { + cursor: "help"; } @keyframes rotateImage { diff --git a/src/component/book/tag/TagList.tsx b/src/component/book/tag/TagList.tsx index d974dae1..aeaf9a7f 100644 --- a/src/component/book/tag/TagList.tsx +++ b/src/component/book/tag/TagList.tsx @@ -1,12 +1,14 @@ import { useState, useRef, useEffect } from "react"; import { TagType } from "../../../type/TagType"; import { useLocation } from "react-router-dom"; -import { AxiosResponse, AxiosError } from "axios"; import { useApi } from "../../../hook/useApi"; import Tag from "./Tag"; import TagModal from "./TagModal"; import plusicon from "../../../asset/img/tag_plus.svg"; import Tooltip from "../../utils/Tooltip"; +import { useRecoilValue } from "recoil"; +import { userAtom } from "../../../atom/userAtom"; +import { AxiosError, AxiosResponse } from "axios"; type TagListProps = { tagData: TagType[]; @@ -14,6 +16,7 @@ type TagListProps = { }; const TagList = ({ tagData, setTagData }: TagListProps) => { + const { isLogin } = useRecoilValue(userAtom); const inputRef = useRef(null); const location = useLocation(); const bookId = location.pathname.split("/")[2]; @@ -22,12 +25,25 @@ const TagList = ({ tagData, setTagData }: TagListProps) => { const [lastPress, setLastPress] = useState(Date.now()); const [active, setActive] = useState(false); const [errorCode, setErrorCode] = useState(null); + const errorCodeRef = useRef(null); const [showErrorMassege, setShowErrorMassege] = useState(false); - const { request } = useApi("post", `/tags/default`, { + const { request } = useApi("post", "tags/default", { bookInfoId: +bookId, content: createTag.trim().replace(/ /g, "_"), }); + useEffect(() => { + if (errorCode !== null && errorCode > 0) { + errorActive(); + } + }, [errorCode]); + + const onSuccess = (response: AxiosResponse) => { + const resTagdata: TagType = response.data; + setTagData(prev => [...prev, resTagdata]); + resetCreateContent(); + }; + const onError = (error: AxiosError) => { setErrorCode(parseInt(error?.response?.data?.errorCode, 10)); errorActive(); @@ -42,14 +58,11 @@ const TagList = ({ tagData, setTagData }: TagListProps) => { setTimeout(() => { setShowErrorMassege(false); }, 2500); + errorCodeRef.current = null; }; const postTag = () => { - request((res: AxiosResponse) => { - const resTagdata: TagType = res.data; - setTagData(prev => [...prev, resTagdata]); - resetCreateContent(); - }, onError); + request(onSuccess, onError); }; const openModalFunc = (tagId: number) => { @@ -79,15 +92,19 @@ const TagList = ({ tagData, setTagData }: TagListProps) => { const now = Date.now(); if (now - lastPress < 300) return; setLastPress(now); + if (!isLogin) { + setErrorCode(102); + errorCodeRef.current = 102; + } if (createTag === "") { setErrorCode(1); + errorCodeRef.current = 1; } else if (createTag.length > 42) { setErrorCode(2); + errorCodeRef.current = 2; } - if (errorCode !== null && errorCode > 0) { - errorActive(); - } else { + if (errorCodeRef.current === null) { postTag(); } if (inputRef.current !== null) { @@ -97,7 +114,10 @@ const TagList = ({ tagData, setTagData }: TagListProps) => { }; const onChange = (event: React.ChangeEvent) => { - if (errorCode !== null) setErrorCode(null); + if (errorCode !== null) { + setErrorCode(null); + errorCodeRef.current = null; + } setCreateTag(event.target.value); }; @@ -167,14 +187,15 @@ const TagList = ({ tagData, setTagData }: TagListProps) => { onKeyUp={handleKeyPress} /> - + plus { - onClickCreateButton(); - }} + onClick={onClickCreateButton} /> diff --git a/src/component/rent/RentModalBooks.tsx b/src/component/rent/RentModalBooks.tsx new file mode 100644 index 00000000..d28cb7ba --- /dev/null +++ b/src/component/rent/RentModalBooks.tsx @@ -0,0 +1,48 @@ +import { memo } from 'react'; +import { Book } from "../../type"; +import BookInformationWithCover from "../utils/BookInformationWithCover"; +import TextWithLabel from "../utils/TextWithLabel"; +import TextareaWithLabel from "../utils/TextareaWithLabel"; + +interface RentModalBooksProps { + selectedBooks: Book[]; + handleRemarkChange: (index: number, value: string) => void; + remarksRef: React.MutableRefObject; +} + +const RentModalBooks = ({ selectedBooks, handleRemarkChange, remarksRef }: RentModalBooksProps) => { + return ( +
+ {selectedBooks.map((selectBook, index) => ( +
+ + + handleRemarkChange(index, value)} + isTextareaFocusedOnMount={index === 0} + isVisibleBottomMessage={!remarksRef.current[index]?.length} + bottomMessageText="비고를 입력해주세요" + bottomMessageColor="red" + /> + +
+ ))} +
+ ); +}; + +export default memo(RentModalBooks); \ No newline at end of file diff --git a/src/component/rent/RentModalConfirm.tsx b/src/component/rent/RentModalConfirm.tsx index c9ce7296..065a9928 100644 --- a/src/component/rent/RentModalConfirm.tsx +++ b/src/component/rent/RentModalConfirm.tsx @@ -1,12 +1,10 @@ -import { FormEventHandler, useState } from "react"; +import { FormEventHandler, useCallback, useState, useRef } from "react"; import Button from "../utils/Button"; -import BookInformationWithCover from "../utils/BookInformationWithCover"; -import TextWithLabel from "../utils/TextWithLabel"; -import TextareaWithLabel from "../utils/TextareaWithLabel"; import { usePostLendingsMultiple } from "../../api/lendings/usePostLendingsMultiple"; import { Book, User } from "../../type"; import "../../asset/css/RentModalConfirm.css"; import { userRoleStatusEnum } from "~/constant/status"; +import RentModalBooks from "./RentModalBooks"; type Props = { selectedUser: User; @@ -23,7 +21,8 @@ const RentModalConfirm = ({ setSelectedBooks, closeModal, }: Props) => { - const [remarks, setRemarks] = useState([]); + const remarksRef = useRef([]); + const [isRentable, setIsRentable] = useState(false); const { requestLending } = usePostLendingsMultiple({ selectedBooks, @@ -35,20 +34,19 @@ const RentModalConfirm = ({ const postData: FormEventHandler = e => { e.preventDefault(); - requestLending(remarks); - }; - - const isRentable = - selectedBooks.length > 1 - ? remarks.slice(0, selectedBooks.length).every(remark => remark.length > 0) - : remarks[0]?.length > 0; - - const handleRemarkChange = (index: number, value: string) => { - const updatedRemarks = [...remarks]; - updatedRemarks[index] = value; - setRemarks(updatedRemarks); + requestLending(remarksRef.current); }; + const handleRemarkChange = useCallback((index: number, value: string) => { + remarksRef.current = [...remarksRef.current]; + remarksRef.current[index] = value; + + const newIsRentable = selectedBooks.length > 1 + ? remarksRef.current.slice(0, selectedBooks.length).every(remark => remark?.length > 0) + : remarksRef.current[0]?.length > 0; + setIsRentable(newIsRentable); + }, [selectedBooks.length]); + return (
@@ -64,41 +62,11 @@ const RentModalConfirm = ({
)} -
- {selectedBooks.map((selectBook, index) => { - const isFirst = index === 0; - - return ( -
- - - handleRemarkChange(index, value)} - isTextareaFocusedOnMount={index === 0} - isVisibleBottomMessage={!remarks[index]?.length} - bottomMessageText="비고를 입력해주세요" - bottomMessageColor="red" - /> - -
- ); - })} -
+