Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

도서관 API 변경에 따른 리펙토링 완료 #114

Merged
merged 11 commits into from
Apr 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 40 additions & 38 deletions apps/member/src/api/book.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import { END_POINT } from '@constants/api';
import { createCommonPagination } from '@utils/api';
import { createCommonPagination, createPath } from '@utils/api';

import type {
BaseResponse,
PaginationPramsType,
PaginationType,
WithPaginationPrams,
} from '@type/api';
Expand All @@ -15,92 +14,87 @@ import type {

import { server } from './server';

export interface BorrowerBookInfo {
export interface BookLoanRequestData {
bookId: number;
borrowerId: string;
}

export interface GetBookLoanRecordConditionsPrams extends PaginationPramsType {
export interface BookLoanRecordSearchOptions extends WithPaginationPrams {
bookId?: number;
borrowerId?: string;
isReturned?: boolean;
}
/**
* 도서 목록 조회
*/
export const getBooks = async (page: number, size: number) => {
const params = { page, size };
export async function getBooks(page: number, size: number) {
const { data } = await server.get<PaginationType<BookItem>>({
url: createCommonPagination(END_POINT.BOOK, params),
url: createCommonPagination(END_POINT.BOOK, { page, size }),
});

return data;
};
}
/**
* 도서 상세 조회
*/
export const getBookDetail = async (id: number) => {
export async function getBookDetail(id: number) {
const { data } = await server.get<BaseResponse<BookItem>>({
url: END_POINT.BOOK_DETAIL(id),
});

return data;
};
}
/**
* 나의 대출내역 조회
*/
export const getMyBooks = async (id: string, page: number, size: number) => {
const params = { page, size };
export async function getMyBooks(id: string, page: number, size: number) {
const { data } = await server.get<PaginationType<BookItem>>({
url: createCommonPagination(END_POINT.BOOK, params),
url: createCommonPagination(END_POINT.BOOK, { page, size }),
});

return data.items.filter((book) => book.borrowerId === id);
};
}
/**
* 도서 대출
*/
export const postBorrowBook = async (body: BorrowerBookInfo) => {
const borrowUrl = END_POINT.BOOK_LOAN_BORROW;
const { data } = await server.post<BorrowerBookInfo, BaseResponse<number>>({
url: borrowUrl,
export async function postBorrowBook(body: BookLoanRequestData) {
return server.post<BookLoanRequestData, BaseResponse<number>>({
url: END_POINT.BOOK_LOAN_BORROW,
body,
});

return data;
};
}
/**
* 도서 반납
*/
export const postReturnBook = async (body: BorrowerBookInfo) => {
const { data } = await server.post<BorrowerBookInfo, BaseResponse<number>>({
url: END_POINT.BOOK_LOAN_RETURN,
body,
});
export async function postReturnBook(body: BookLoanRequestData) {
const { data } = await server.post<BookLoanRequestData, BaseResponse<number>>(
{
url: END_POINT.BOOK_LOAN_RETURN,
body,
},
);

return data;
};
}
/**
* 도서 연장
*/
export const postExtendBook = async (body: BorrowerBookInfo) => {
const { data } = await server.post<BorrowerBookInfo, BaseResponse<number>>({
export function postExtendBook(body: BookLoanRequestData) {
return server.post<BookLoanRequestData, BaseResponse<number>>({
url: END_POINT.BOOK_LOAN_EXTEND,
body,
});

return data;
};
}
/**
* 도서 대출 내역 조회
*/
export const getBookLoanRecordConditions = async ({
export async function getBookLoanRecordConditions({
bookId,
borrowerId,
isReturned,
page = 0,
size = 20,
}: GetBookLoanRecordConditionsPrams) => {
}: BookLoanRecordSearchOptions) {
const { data } = await server.get<
PaginationType<BookLoanRecordConditionType>
>({
Expand All @@ -114,14 +108,14 @@ export const getBookLoanRecordConditions = async ({
});

return data;
};
}
/**
* 도서 연체자 조회
*/
export const getBookLoanRecordOverdue = async ({
export async function getBookLoanRecordOverdue({
page,
size,
}: WithPaginationPrams) => {
}: WithPaginationPrams) {
const { data } = await server.get<
PaginationType<BookLoanRecordOverDueResponse>
>({
Expand All @@ -132,4 +126,12 @@ export const getBookLoanRecordOverdue = async ({
});

return data;
};
}
/**
* 도서 대출 승인
*/
export function patchBookLoanRecordApprove(id: number) {
return server.patch<null, BaseResponse<number>>({
url: createPath(END_POINT.BOOK_LOAN_RECORD_APPROVE, id),
});
}
13 changes: 9 additions & 4 deletions apps/member/src/components/common/Header/Header.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,27 @@
import { Fragment } from 'react';
import { GrNext } from 'react-icons/gr';
import { Link } from 'react-router-dom';

interface HeaderProps {
title: string | string[];
path?: string | string[];
children?: React.ReactNode;
}

const Header = ({ title, children }: HeaderProps) => {
const Header = ({ title, path, children }: HeaderProps) => {
const RenderTitle = () => {
if (Array.isArray(title)) {
// 배열일 경우, 제목이 여러 개일 경우
return (
<div className="flex items-center text-xl font-bold">
{title.map((name, index) => (
<Fragment key={index}>
<span className="cursor-pointer rounded-lg px-2 transition-colors hover:bg-gray-100">
<Fragment key={name}>
<Link
to={path?.[index] || ''}
className="rounded-lg px-2 transition-colors hover:bg-gray-100"
>
{name}
</span>
</Link>
{index !== title.length - 1 && <GrNext />}
</Fragment>
))}
Expand Down
54 changes: 26 additions & 28 deletions apps/member/src/components/common/Image/Image.tsx
Original file line number Diff line number Diff line change
@@ -1,59 +1,57 @@
import { SyntheticEvent, useState } from 'react';
import { ComponentPropsWithRef, SyntheticEvent, useState } from 'react';

import { NOT_FOUND_IMG } from '@constants/path';
import classNames from 'classnames';
import { cn } from '@utils/string';

interface ImageProps {
src?: string;
alt: string;
interface ImageProps extends ComponentPropsWithRef<'img'> {
width?: string;
height?: string;
className?: string;
onClick?: () => void;
overflow?: boolean;
}

type Status = 'loading' | 'error' | 'loaded';

const Image = ({
src,
alt,
width,
height,
src,
overflow,
className,
onClick,
overflow,
...rest
}: ImageProps) => {
const [loading, setLoading] = useState(true);
const [error, setError] = useState(false);

if (!src) src = NOT_FOUND_IMG;
const [status, setStatus] = useState<Status>('loading');

const _width = width ? width : 'w-full';
const _height = height ? height : 'h-full';
const _width = width ?? 'w-full';
const _height = height ?? 'h-full';

const handleError = (e: SyntheticEvent<HTMLImageElement>) => {
e.currentTarget.src = NOT_FOUND_IMG;
setError(true);
setLoading(false);
setStatus('error');
};

return (
<div
className={classNames(_width, _height, {
className={cn(_width, _height, {
'overflow-hidden': overflow,
})}
onClick={onClick}
>
<img
className={classNames('h-full w-full', className, {
'animate-pulse bg-gray-200': loading,
'bg-gray-50': error,
'cursor-pointer': onClick,
})}
src={src}
alt={alt}
onClick={onClick}
onLoad={() => setLoading(false)}
className={cn(
{
'animate-pulse bg-gray-200': status === 'loading',
'bg-gray-50': status === 'error',
},
_width,
_height,
className,
)}
src={src ?? NOT_FOUND_IMG}
onLoad={() => setStatus('loaded')}
onError={handleError}
loading="lazy"
{...rest}
/>
</div>
);
Expand Down
18 changes: 8 additions & 10 deletions apps/member/src/components/common/Nav/Nav.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,7 @@ const Nav = () => {
const navigate = useNavigate();
const { openModal } = useModal();

const isSelected = (path: string) => {
return location.pathname.endsWith(path);
};
const pathName = location.pathname;

const handleMenubarItemClick = useCallback(
(path: string) => {
Expand All @@ -40,44 +38,44 @@ const Nav = () => {
</Link>
<Menubar gap="xl" className="text-sm">
<MenubarItem
selected={isSelected(PATH.MAIN)}
selected={pathName === PATH.MAIN}
onClick={() => handleMenubarItemClick(PATH.MAIN)}
>
</MenubarItem>
<MenubarItem
selected={isSelected(PATH.CALENDER)}
selected={pathName.startsWith(PATH.CALENDER)}
onClick={() => handleMenubarItemClick(PATH.CALENDER)}
>
일정
</MenubarItem>
<MenubarItem
selected={isSelected(PATH.ACTIVITY)}
selected={pathName.startsWith(PATH.ACTIVITY)}
onClick={() => handleMenubarItemClick(PATH.ACTIVITY)}
>
활동
</MenubarItem>
<MenubarItem
selected={isSelected(PATH.COMMUNITY)}
selected={pathName.startsWith(PATH.COMMUNITY)}
onClick={() => handleMenubarItemClick(PATH.COMMUNITY)}
>
커뮤니티
</MenubarItem>
<MenubarItem
selected={isSelected(PATH.LIBRARY)}
selected={pathName.startsWith(PATH.LIBRARY)}
onClick={() => handleMenubarItemClick(PATH.LIBRARY)}
>
도서관
</MenubarItem>
<MenubarItem
selected={isSelected(PATH.SUPPORT)}
selected={pathName.startsWith(PATH.SUPPORT)}
onClick={() => handleMenubarItemClick(PATH.SUPPORT)}
>
회비
</MenubarItem>
{MODE !== 'production' && (
<MenubarItem
selected={isSelected(PATH.MANAGE)}
selected={pathName.startsWith(PATH.MANAGE)}
onClick={() => handleMenubarItemClick(PATH.MANAGE)}
>
관리
Expand Down
Loading