diff --git a/apps/member/src/api/book.ts b/apps/member/src/api/book.ts index 95ab853d..0b80556e 100644 --- a/apps/member/src/api/book.ts +++ b/apps/member/src/api/book.ts @@ -6,12 +6,21 @@ import type { BookLoanRecordConditionType, BookLoanRecordItem, } from '@type/book'; -import type { BaseResponse, PaginationType } from '@type/api'; +import type { + BaseResponse, + PaginationPramsType, + PaginationType, +} from '@type/api'; interface PostBorrowBookArgs extends BookLoanRecordItem { memberId: string; } +interface GetBookLoanRecordConditionsPrams extends PaginationPramsType { + bookId?: number; + borrowerId?: string; + isReturned?: boolean; +} /** * 도서 목록 조회 */ @@ -23,7 +32,6 @@ export const getBooks = async (page: number, size: number) => { return data; }; - /** * 도서 상세 조회 */ @@ -34,7 +42,6 @@ export const getBookDetail = async (id: number) => { return data; }; - /** * 나의 대출내역 조회 */ @@ -46,7 +53,6 @@ export const getMyBooks = async (id: string, page: number, size: number) => { return data.items.filter((book) => book.borrowerId === id); }; - /** * 도서 대출 */ @@ -59,7 +65,6 @@ export const postBorrowBook = async (body: PostBorrowBookArgs) => { return { memberId: body.memberId, bookId: body.bookId, data }; }; - /** * 도서 반납 */ @@ -71,7 +76,6 @@ export const postReturnBook = async (body: BookLoanRecordItem) => { return { memberId: body.borrowerId, bookId: body.bookId, data }; }; - /** * 도서 연장 */ @@ -83,20 +87,26 @@ export const postExtendBook = async (body: BookLoanRecordItem) => { return { memberId: body.borrowerId, bookId: data }; }; - /** - * 도서 대출 내역 검색 + * 도서 대출 내역 조회 */ -export const getBookLoanByMemberId = async ( - borrowerId: string, +export const getBookLoanRecordConditions = async ({ + bookId, + borrowerId, + isReturned, page = 0, size = 20, -) => { - const params = { borrowerId, page, size }; +}: GetBookLoanRecordConditionsPrams) => { const { data } = await server.get< PaginationType >({ - url: createCommonPagination(END_POINT.BOOK_LOAN_CONDITIONS, params), + url: createCommonPagination(END_POINT.BOOK_LOAN_CONDITIONS, { + bookId, + borrowerId, + isReturned, + page, + size, + }), }); return data; diff --git a/apps/member/src/assets/svg/yes24.svg b/apps/member/src/assets/svg/yes24.svg new file mode 100644 index 00000000..3a88f611 --- /dev/null +++ b/apps/member/src/assets/svg/yes24.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/member/src/assets/webp/aladin.webp b/apps/member/src/assets/webp/aladin.webp new file mode 100644 index 00000000..b34d7a3d Binary files /dev/null and b/apps/member/src/assets/webp/aladin.webp differ diff --git a/apps/member/src/assets/webp/kyobobook.webp b/apps/member/src/assets/webp/kyobobook.webp new file mode 100644 index 00000000..62efb42e Binary files /dev/null and b/apps/member/src/assets/webp/kyobobook.webp differ diff --git a/apps/member/src/components/library/BookCard/BookCard.tsx b/apps/member/src/components/library/BookCard/BookCard.tsx index 5990ce92..807f1332 100644 --- a/apps/member/src/components/library/BookCard/BookCard.tsx +++ b/apps/member/src/components/library/BookCard/BookCard.tsx @@ -19,7 +19,7 @@ const BookCard = ({ return (
navigate(PATH_FINDER.LIBRARY_DETAIL(id))} > {title} -
+

{title}

diff --git a/apps/member/src/components/library/BookDetailSection/BookDetailSection.tsx b/apps/member/src/components/library/BookDetailSection/BookDetailSection.tsx index 0be06ee5..5f058ca3 100644 --- a/apps/member/src/components/library/BookDetailSection/BookDetailSection.tsx +++ b/apps/member/src/components/library/BookDetailSection/BookDetailSection.tsx @@ -1,89 +1,86 @@ import Section from '@components/common/Section/Section'; -import { Link } from 'react-router-dom'; -import { Button } from '@clab/design-system'; import Image from '@components/common/Image/Image'; +import { Button, DetailsList, Badge, Grid, Tabs } from '@clab/design-system'; import { BOOK_STATE } from '@constants/state'; import { useBookLoanBorrowMutation } from '@hooks/queries/useBookLoanBorrowMutation'; import { useMyProfile } from '@hooks/queries/useMyProfile'; +import { useCallback } from 'react'; +import { createImageUrl } from '@utils/api'; +import { SELECT_DEFAULT_OPTION } from '@constants/select'; +import kyoboIcon from '@assets/webp/kyobobook.webp'; +import aladinIcon from '@assets/webp/aladin.webp'; +import yes24Icon from '@assets/svg/yes24.svg'; import type { BookItem } from '@type/book'; interface BookDetailSectionProps { data: BookItem; } -interface BookInfoRowProps { - to?: string; - label: string; - content: string; -} - -const BookInfoRow = ({ to, label, content }: BookInfoRowProps) => { - return ( -

-

{label}

- {to ? ( - - {content} - - ) : ( -

{content}

- )} -
- ); -}; +const tabsOptions = [ + { + icon: , + value: '교보문고', + }, + { + icon: , + value: '예스24', + }, + { + icon: , + value: '알라딘', + }, +]; const BookDetailSection = ({ data }: BookDetailSectionProps) => { const { data: myInfo } = useMyProfile(); const { bookBorrowMutate } = useBookLoanBorrowMutation(); const { id, borrowerId, category, title, author, publisher, imageUrl } = data; - const onClickBorrow = (bookId: number) => { - bookBorrowMutate({ - memberId: myInfo.id, - bookId: bookId, - borrowerId: myInfo.id, - }); - }; + const handleBorrowClick = useCallback( + (bookId: number) => { + bookBorrowMutate({ + memberId: myInfo.id, + bookId: bookId, + borrowerId: myInfo.id, + }); + }, + [bookBorrowMutate, myInfo.id], + ); return (
-
-
-
- {title} -
-
-
-

{title}

- - - - -
-
- {borrowerId ? ( -

- 이미 대여된 도서예요! 조금만 기다려주세요 ⏳ -

- ) : ( - - )} -
+ + {title} +
+
+

{title}

+ + {author} + {publisher} + {category} + + + {borrowerId ? BOOK_STATE.BORROWED : BOOK_STATE.AVAILABLE} + + + +
+
-
+
); }; diff --git a/apps/member/src/components/library/BookLoanHistorySection/BookLoanHistorySection.tsx b/apps/member/src/components/library/BookLoanHistorySection/BookLoanHistorySection.tsx new file mode 100644 index 00000000..24a53616 --- /dev/null +++ b/apps/member/src/components/library/BookLoanHistorySection/BookLoanHistorySection.tsx @@ -0,0 +1,41 @@ +import Section from '@components/common/Section/Section'; +import { Table, Badge } from '@clab/design-system'; +import { useBookLoanRecordConditions } from '@hooks/queries'; +import { formattedDate } from '@utils/date'; +import { TABLE_HEAD } from '@constants/head'; + +interface BookLoanHistorySectionProps { + bookId: number; +} + +const BookLoanHistorySection = ({ bookId }: BookLoanHistorySectionProps) => { + const { data } = useBookLoanRecordConditions({ bookId }); + + return ( +
+ + 최근에 신청된 대여 내역이에요 + + + + {data.items.map(({ borrowerId, borrowedAt, returnedAt }, index) => ( + + {borrowerId} + {formattedDate(borrowedAt)} + + {returnedAt ? formattedDate(returnedAt) : '예정'} + + + + {returnedAt ? '반납완료' : '대여중'} + + + + ))} +
+
+
+ ); +}; + +export default BookLoanHistorySection; diff --git a/apps/member/src/components/library/LibraryNewBooksSection/LibraryNewBooksSection.tsx b/apps/member/src/components/library/LibraryNewBooksSection/LibraryNewBooksSection.tsx new file mode 100644 index 00000000..911e60d0 --- /dev/null +++ b/apps/member/src/components/library/LibraryNewBooksSection/LibraryNewBooksSection.tsx @@ -0,0 +1,42 @@ +import Section from '@components/common/Section/Section'; +import { BookItem } from '@type/book'; +import Image from '@components/common/Image/Image'; +import { Link } from 'react-router-dom'; +import { PATH_FINDER } from '@constants/path'; + +interface LibraNewBooksSectionProps { + data: BookItem[]; +} + +const LibraryNewBooksSection = ({ data }: LibraNewBooksSectionProps) => { + return ( +
+ + 최근에 들어온 도서들을 확인해보세요 + + + {data.map(({ id, imageUrl, title, author, publisher }) => ( + +
+ {title} +
+ +
+
+

{title}

+

+ {author} | {publisher} +

+
+ + ))} + +
+ ); +}; + +export default LibraryNewBooksSection; diff --git a/apps/member/src/components/membership/MembershipInfoModal/MembershipInfoModal.tsx b/apps/member/src/components/membership/MembershipInfoModal/MembershipInfoModal.tsx index cf34264b..aeb02128 100644 --- a/apps/member/src/components/membership/MembershipInfoModal/MembershipInfoModal.tsx +++ b/apps/member/src/components/membership/MembershipInfoModal/MembershipInfoModal.tsx @@ -4,22 +4,13 @@ import MembershipCategoryBadge from '../MembershipCategoryBadge/MembershipCatego import MembershipStatusBadge from '../MembershipStatusBadge/MembershipStatusBadge'; import { formatWon } from '@utils/string'; import { formattedDate } from '@utils/date'; +import { DetailsList } from '@clab/design-system'; import type { MembershipFeeType } from '@type/membershipFee'; interface MembershipInfoModalProps { data: MembershipFeeType; } -interface MembershipInfoModalUlProps { - title?: string; - children: React.ReactNode; -} - -interface MembershipInfoModalLiProps { - label: string; - children: React.ReactNode; -} - const MembershipInfoModal = ({ data }: MembershipInfoModalProps) => { const { imageUrl, @@ -39,44 +30,26 @@ const MembershipInfoModal = ({ data }: MembershipInfoModalProps) => {
- - + + - - + + - - {content} - + + {content} + {`₩${formatWon(amount)}`} - - + + {`${memberName} (${memberId})`} - - + + {formattedDate(createdAt)} - - + +
); }; -MembershipInfoModal.Ul = ({ title, children }: MembershipInfoModalUlProps) => { - return ( -
    - {title &&

    {title}

    } - {children} -
- ); -}; - -MembershipInfoModal.Li = ({ label, children }: MembershipInfoModalLiProps) => { - return ( -
  • - {label} - {children} -
  • - ); -}; - export default MembershipInfoModal; diff --git a/apps/member/src/components/panels/BookPanel/BookPanel.tsx b/apps/member/src/components/panels/BookPanel/BookPanel.tsx index 0d364f62..bc1d25c8 100644 --- a/apps/member/src/components/panels/BookPanel/BookPanel.tsx +++ b/apps/member/src/components/panels/BookPanel/BookPanel.tsx @@ -7,13 +7,13 @@ import useModal from '@hooks/common/useModal'; import { useBookLoanReturnMutation } from '@hooks/queries/useBookLoanReturnMutation'; import { useBookLoanExtendMutation } from '@hooks/queries/useBookLoanExtendMutation'; import Select from '@components/common/Select/Select'; -import { useBookLoanRecordByMemberId } from '@hooks/queries/useBookLoanRecordById'; import { checkDueDate, checkExtendProgress, checkProgress, now, } from '@utils/date'; +import { useBookLoanRecordConditions } from '@hooks/queries'; interface BookPanelProps { memberId: string; @@ -40,7 +40,9 @@ const ActionButton = ({ const BookPanel = ({ memberId, data }: BookPanelProps) => { const { openModal } = useModal(); - const { data: myLoanBookData } = useBookLoanRecordByMemberId(memberId); + const { data: myLoanBookData } = useBookLoanRecordConditions({ + borrowerId: memberId, + }); const { bookReturnMutate } = useBookLoanReturnMutation(); const { bookExtendMutate } = useBookLoanExtendMutation(); diff --git a/apps/member/src/constants/head.ts b/apps/member/src/constants/head.ts index 8d63d704..66555b71 100644 --- a/apps/member/src/constants/head.ts +++ b/apps/member/src/constants/head.ts @@ -2,4 +2,5 @@ export const TABLE_HEAD = { ACTIVITY_GROUP_PARTICIPANTS: ['번호', '학번', '이름'], ACTIVITY_GROUP_APPLIES: ['번호', '학번', '이름', '상태', '기능'], SUPPORT_HISTORY: ['번호', '상태', '구분', '요청자', '신청일'], + BOOK_LOAN_RECORD: ['대여자', '대여일', '반납일', '상태'], } as const; diff --git a/apps/member/src/constants/state.ts b/apps/member/src/constants/state.ts index 822fd9e2..e637e63e 100644 --- a/apps/member/src/constants/state.ts +++ b/apps/member/src/constants/state.ts @@ -1,5 +1,5 @@ export const BOOK_STATE = { - BORROWED: '대출중', + BORROWED: '대여중', AVAILABLE: '대여가능', } as const; diff --git a/apps/member/src/hooks/queries/index.ts b/apps/member/src/hooks/queries/index.ts index e5544a5e..f1cde51c 100644 --- a/apps/member/src/hooks/queries/index.ts +++ b/apps/member/src/hooks/queries/index.ts @@ -30,3 +30,4 @@ export * from './usePosts'; export * from './useSchedule'; export * from './useUserInfoMutation'; export * from './useActivityGroupMemberMy'; +export * from './useBookLoanRecordConditions'; diff --git a/apps/member/src/hooks/queries/useBookLoanRecordById.ts b/apps/member/src/hooks/queries/useBookLoanRecordById.ts deleted file mode 100644 index 3948057d..00000000 --- a/apps/member/src/hooks/queries/useBookLoanRecordById.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { getBookLoanByMemberId } from '@api/book'; -import { QUERY_KEY } from '@constants/key'; -import { useSuspenseQuery } from '@tanstack/react-query'; - -/** - * 도서를 대출한 회원의 ID를 기준으로 조회합니다. - */ -export const useBookLoanRecordByMemberId = ( - borrowerId: string, - page = 0, - size = 20, -) => { - return useSuspenseQuery({ - queryFn: () => getBookLoanByMemberId(borrowerId, page, size), - queryKey: [QUERY_KEY.BOOK_LOAN_RECORD, borrowerId], - }); -}; diff --git a/apps/member/src/hooks/queries/useBookLoanRecordConditions.ts b/apps/member/src/hooks/queries/useBookLoanRecordConditions.ts new file mode 100644 index 00000000..14f84850 --- /dev/null +++ b/apps/member/src/hooks/queries/useBookLoanRecordConditions.ts @@ -0,0 +1,33 @@ +import { getBookLoanRecordConditions } from '@api/book'; +import { QUERY_KEY } from '@constants/key'; +import { useSuspenseQuery } from '@tanstack/react-query'; +import type { PaginationPramsType } from '@type/api'; + +interface UseBookLoanRecordConditionsPrams extends PaginationPramsType { + bookId?: number; + borrowerId?: string; + isReturned?: boolean; +} + +/** + * 도서 대출 내역을 조회합니다. + */ +export const useBookLoanRecordConditions = ({ + bookId, + borrowerId, + isReturned, + page = 0, + size = 20, +}: UseBookLoanRecordConditionsPrams) => { + return useSuspenseQuery({ + queryFn: () => + getBookLoanRecordConditions({ + bookId, + borrowerId, + isReturned, + page, + size, + }), + queryKey: [QUERY_KEY.BOOK_LOAN_RECORD, borrowerId], + }); +}; diff --git a/apps/member/src/pages/LibraryDetailPage/LibraryDetailPage.tsx b/apps/member/src/pages/LibraryDetailPage/LibraryDetailPage.tsx index 77c32677..b19f0add 100644 --- a/apps/member/src/pages/LibraryDetailPage/LibraryDetailPage.tsx +++ b/apps/member/src/pages/LibraryDetailPage/LibraryDetailPage.tsx @@ -4,6 +4,7 @@ import Header from '@components/common/Header/Header'; import BookDetailSection from '@components/library/BookDetailSection/BookDetailSection'; import { useBookDetails } from '@hooks/queries/useBookDetails'; import { LIBRARY_MESSAGE } from '@constants/message'; +import BookLoanHistorySection from '@components/library/BookLoanHistorySection/BookLoanHistorySection'; const LibraryDetailPage = () => { const { id } = useParams<{ id: string }>(); @@ -16,6 +17,7 @@ const LibraryDetailPage = () => {
    + ); }; diff --git a/apps/member/src/pages/LibraryPage/LibraryPage.tsx b/apps/member/src/pages/LibraryPage/LibraryPage.tsx index 1abbb51c..08e94e7a 100644 --- a/apps/member/src/pages/LibraryPage/LibraryPage.tsx +++ b/apps/member/src/pages/LibraryPage/LibraryPage.tsx @@ -1,3 +1,4 @@ +import { useState } from 'react'; import Content from '@components/common/Content/Content'; import Header from '@components/common/Header/Header'; import { Button } from '@clab/design-system'; @@ -7,7 +8,7 @@ import { useBooks } from '@hooks/queries/useBooks'; import Section from '@components/common/Section/Section'; import LibraryBookList from '@components/library/LibraryBookList/LibraryBookList'; import Pagination from '@components/common/Pagination/Pagination'; -import { useState } from 'react'; +import LibraryNewBooksSection from '@components/library/LibraryNewBooksSection/LibraryNewBooksSection'; const LibraryPage = () => { const navigate = useNavigate(); @@ -30,25 +31,19 @@ const LibraryPage = () => { 희망도서 신청하기
    +
    - - - - -
    -
    - + + + -
    - -
    diff --git a/apps/member/src/pages/MyPage/MyPage.tsx b/apps/member/src/pages/MyPage/MyPage.tsx index 314aa84e..0bc844b3 100644 --- a/apps/member/src/pages/MyPage/MyPage.tsx +++ b/apps/member/src/pages/MyPage/MyPage.tsx @@ -12,7 +12,7 @@ import { useMyProfile } from '@hooks/queries/useMyProfile'; import { Grid } from '@clab/design-system'; import MyMembershipHistorySection from '@components/my/MyMembershipHistorySection/MyMembershipHistorySection'; import MyActivityGroupSection from '@components/my/MyActivityGroupSection/MyActivityGroupSection'; -import { useBookLoanRecordByMemberId } from '@hooks/queries/useBookLoanRecordById'; +import { useBookLoanRecordConditions } from '@hooks/queries/useBookLoanRecordConditions'; const MyPage = () => { const { data: myProfile } = useMyProfile(); @@ -23,7 +23,9 @@ const MyPage = () => { memberId: myProfile.id, size: 5, }); - const { data: myBookLoanRecord } = useBookLoanRecordByMemberId(myProfile.id); + const { data: myBookLoanRecord } = useBookLoanRecordConditions({ + borrowerId: myProfile.id, + }); const { data: myActivityGroup } = useActivityGroupMemberMy(); return ( diff --git a/packages/design-system/src/DetailsList/DetailsList.tsx b/packages/design-system/src/DetailsList/DetailsList.tsx new file mode 100644 index 00000000..7e96fd1f --- /dev/null +++ b/packages/design-system/src/DetailsList/DetailsList.tsx @@ -0,0 +1,29 @@ +import React from 'react'; +import type { + DetailsListItemProps, + DetailsListProps, +} from './DetailsList.types'; + +const DetailsList = ({ title, children }: DetailsListProps) => { + return ( +
      + {title &&

      {title}

      } + {children} +
    + ); +}; + +const DetailsListItem = ({ label, children }: DetailsListItemProps) => { + return ( +
  • + {label} + {children} +
  • + ); +}; + +DetailsList.Item = DetailsListItem; + +DetailsList.displayName = 'DetailsList'; + +export default DetailsList; diff --git a/packages/design-system/src/DetailsList/DetailsList.types.ts b/packages/design-system/src/DetailsList/DetailsList.types.ts new file mode 100644 index 00000000..bf656ebb --- /dev/null +++ b/packages/design-system/src/DetailsList/DetailsList.types.ts @@ -0,0 +1,10 @@ +import { PropsWithChildren } from 'react'; + +export interface DetailsListProps extends PropsWithChildren { + title?: string; +} + +export interface DetailsListItemProps { + label: string; + children: React.ReactNode; +} diff --git a/packages/design-system/src/DetailsList/index.ts b/packages/design-system/src/DetailsList/index.ts new file mode 100644 index 00000000..124e4653 --- /dev/null +++ b/packages/design-system/src/DetailsList/index.ts @@ -0,0 +1 @@ +export { default as DetailsList } from './DetailsList'; diff --git a/packages/design-system/src/Tabs/Tabs.tsx b/packages/design-system/src/Tabs/Tabs.tsx index 529ff24b..f82a3fe7 100644 --- a/packages/design-system/src/Tabs/Tabs.tsx +++ b/packages/design-system/src/Tabs/Tabs.tsx @@ -15,18 +15,16 @@ const Tabs = ({ options, value, onChange }: TabsProps) => { ); return ( -
    +
    {options.map((option, index) => ( handleOptionClick(option.value)} > - - {option.icon} - + {option.icon} {option.value} ))} @@ -39,7 +37,7 @@ const Option = ({ className, children, ...rest }: TabsOptionProps) => {