Skip to content
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
5 changes: 5 additions & 0 deletions apps/member/src/api/library/api.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,14 @@ export type postBookLoanRequest = {
bookId: number;
};

export type deleteBookLoanRecordRequest = {
bookLoanRecordId: number;
};

export type BookLoanStatus = "PENDING" | "APPROVED" | "REJECTED" | "RETURNED";

export type getBooksLoanConditionsRequest = {
borrowerId?: string;
status: BookLoanStatus;
page: number;
size: number;
Expand Down
19 changes: 19 additions & 0 deletions apps/member/src/api/library/api.query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,18 @@ import {
} from "@tanstack/react-query";

import { DEFAULT_PAGE_SIZE } from "@/api/config";
import { TOAST_MESSAGES } from "@/constants";
import { showErrorToast } from "@/utils/toast";

import type {
deleteBookLoanRecordRequest,
getBooksRequest,
getBooksResponse,
getBooksDetailRequest,
getBooksLoanConditionsRequest,
postBookLoanRequest,
} from "./api.model";
import { deleteBookLoanRecord } from "./deleteBookLoanRecord";
import { getBooks } from "./getBooks";
import { getBooksDetail } from "./getBooksDetail";
import { getBooksLoanConditions } from "./getBooksLoanConditions";
Expand Down Expand Up @@ -58,6 +62,7 @@ export const libraryQueries = {
}),
getBooksLoanConditionsQuery: (
request: getBooksLoanConditionsRequest = {
borrowerId: undefined,
status: "PENDING",
page: 0,
size: 20,
Expand All @@ -80,5 +85,19 @@ export const libraryQueries = {
throw new Error(res.error.message ?? "대출 신청에 실패했습니다.");
return res.data;
},
onError: () => {
showErrorToast(TOAST_MESSAGES.BOOK_LOAN_APPLY);
},
}),
deleteBookLoanRecordMutation: mutationOptions({
mutationFn: async (request: deleteBookLoanRecordRequest) => {
const res = await deleteBookLoanRecord(request);
if (!res.ok)
throw new Error(res.error.message ?? "대출 신청 취소에 실패했습니다.");
return res.data;
},
onError: () => {
showErrorToast(TOAST_MESSAGES.BOOK_LOAN_CANCEL);
},
}),
};
8 changes: 7 additions & 1 deletion apps/member/src/api/library/api.type.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
import type { getBooksDetailResponse, getBooksResponse } from "./api.model";
import type {
getBooksDetailResponse,
getBooksLoanConditionsResponse,
getBooksResponse,
} from "./api.model";

export type Book = getBooksResponse["data"]["items"][number];

export type BookDetail = getBooksDetailResponse["data"];
export type BookLoanCondition =
getBooksLoanConditionsResponse["data"]["items"][number];
10 changes: 10 additions & 0 deletions apps/member/src/api/library/deleteBookLoanRecord.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { authApi, END_POINT } from "../config";
import type { deleteBookLoanRecordRequest } from "./api.model";
import type { BaseApiResponse } from "../config/api-base-types";

export const deleteBookLoanRecord = ({
bookLoanRecordId,
}: deleteBookLoanRecordRequest) =>
authApi.del<BaseApiResponse<number>>(
`${END_POINT.LIBRARY.BOOKS_LOAN}/${bookLoanRecordId}`,
);
3 changes: 3 additions & 0 deletions apps/member/src/api/library/getBooksLoanConditions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ export const getBooksLoanConditions = (
size: request.size,
sortBy: request.sortBy,
sortDirection: request.sortDirection,
...(request.borrowerId !== undefined && {
borrowerId: request.borrowerId,
}),
},
},
);
20 changes: 19 additions & 1 deletion apps/member/src/components/auth/AuthInitializer.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import { useQueryClient } from "@tanstack/react-query";
import { useEffect, useState } from "react";

import { useIsLoggedIn } from "@/model/auth";
import { useAuthStore } from "@/model/common";

import { postReissueToken } from "@/api/auth/postReissueToken";
import { userQueries } from "@/api/user/api.query";
import {
getAccessToken,
getRefreshToken,
Expand All @@ -12,15 +15,29 @@ import {

export function AuthInitializer({ children }: { children: React.ReactNode }) {
const { updateLogged } = useIsLoggedIn();
const queryClient = useQueryClient();
const setMemberId = useAuthStore((s) => s.setMemberId);
const [isLoading, setIsLoading] = useState(true);

useEffect(() => {
const restoreMemberId = async () => {
try {
const userInfo = await queryClient.fetchQuery({
...userQueries.getUserInfoQuery(),
});
setMemberId(userInfo.data.id);
} catch {
// 사용자 정보 조회 실패 시에도 로그인 상태는 유지, 나중에 로그아웃 처리가 필요하다면 그 때 추가할깨요.
}
};

const initialize = async () => {
const accessToken = getAccessToken();
const refreshToken = getRefreshToken();

if (accessToken && refreshToken) {
updateLogged(true);
await restoreMemberId();
return;
}

Expand All @@ -30,6 +47,7 @@ export function AuthInitializer({ children }: { children: React.ReactNode }) {
await postReissueToken(refreshToken);
setTokens(newAccess, newRefresh);
updateLogged(true);
await restoreMemberId();
} catch {
removeTokens();
updateLogged(false);
Expand All @@ -43,7 +61,7 @@ export function AuthInitializer({ children }: { children: React.ReactNode }) {
initialize().finally(() => {
queueMicrotask(() => setIsLoading(false));
});
}, [updateLogged]);
}, [updateLogged, queryClient, setMemberId]);

if (isLoading) {
return null;
Expand Down
9 changes: 9 additions & 0 deletions apps/member/src/components/common/EmptyState.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import type { ReactNode } from "react";

interface EmptyStateProps {
children: ReactNode;
}

export default function EmptyState({ children }: EmptyStateProps) {
return <p className="text-14-regular text-gray-4 py-lg">{children}</p>;
}
1 change: 1 addition & 0 deletions apps/member/src/components/common/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ export { default as ProfileImage } from "./ProfileImage";
export { default as ConfirmModal } from "./ConfirmModal";
export { default as FileDownloadList } from "./FileDownloadList";
export { default as ImageInlineList } from "./ImageInlineList";
export { default as EmptyState } from "./EmptyState";
62 changes: 62 additions & 0 deletions apps/member/src/components/library/LoanBookCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import type { ReactNode } from "react";
import { useNavigate } from "react-router";

import type { BookLoanCondition } from "@/api/library/api.type";
import { ROUTE } from "@/constants";

interface LoanBookCardProps {
loan: BookLoanCondition;
action?: ReactNode;
}

export default function LoanBookCard({ loan, action }: LoanBookCardProps) {
const navigate = useNavigate();

return (
<div className="border-gray-1 flex items-center gap-4 border-b py-3">
<div
className="flex min-w-0 flex-1 cursor-pointer items-center gap-4"
role="button"
tabIndex={0}
onClick={() => navigate(`${ROUTE.LIBRARY}/${loan.bookId}`)}
onKeyDown={(e) => {
if (e.key === "Enter" || e.key === " ") {
Comment thread
yummjin marked this conversation as resolved.
Resolved
e.preventDefault();
navigate(`${ROUTE.LIBRARY}/${loan.bookId}`);
}
}}
>
<div className="bg-gray-1 h-20 w-16 flex-shrink-0 overflow-hidden rounded">
{loan.bookImageUrl && loan.bookImageUrl !== "없음" ? (
<img
src={loan.bookImageUrl}
alt={loan.bookTitle}
className="h-full w-full object-cover"
/>
) : (
<div className="flex h-full w-full items-center justify-center">
<span className="text-gray-3 text-12-regular text-center">
이미지 없음
</span>
</div>
)}
</div>
<div className="min-w-0 flex-1">
<h3 className="text-14-medium line-clamp-2 text-black">
{loan.bookTitle}
</h3>
<p className="text-12-regular text-gray-4 mt-1">
신청일: {loan.borrowedAt?.split("T")[0] ?? "-"}
{loan.dueDate && (
<>
<br />
반납 예정일: {loan.dueDate?.split("T")[0] ?? "-"}
</>
)}
</p>
</div>
</div>
{action && <div className="flex-shrink-0">{action}</div>}
</div>
);
}
1 change: 1 addition & 0 deletions apps/member/src/components/library/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ export { default as LibrarySearchBar } from "./LibrarySearchBar";
export { default as LibraryFilter } from "./LibraryFilter";
export { default as LibraryBookList } from "./LibraryBookList";
export { default as LibraryBookItem } from "./LibraryBookItem";
export { default as LoanBookCard } from "./LoanBookCard";
8 changes: 8 additions & 0 deletions apps/member/src/constants/toast-messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,4 +87,12 @@ export const TOAST_MESSAGES = {
success: "답변이 삭제되었습니다.",
error: "답변 삭제에 실패했습니다.",
},
BOOK_LOAN_APPLY: {
success: "도서 대출 신청이 완료되었습니다.",
error: "도서 대출 신청에 실패했습니다.",
},
BOOK_LOAN_CANCEL: {
success: "도서 대출 신청이 취소되었습니다.",
error: "도서 대출 신청 취소에 실패했습니다.",
},
} as const;
Loading
Loading