Skip to content

Commit 4d54ce5

Browse files
committed
마이 북
1 parent ee9871c commit 4d54ce5

File tree

5 files changed

+145
-6
lines changed

5 files changed

+145
-6
lines changed
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { MyBook } from 'pages/MyBooks/useMyBooksQuery';
2+
3+
import { BookCard } from '../../shared/BookCard';
4+
5+
type ReadingBooksGridProps = {
6+
books: MyBook[];
7+
};
8+
9+
export const ReadingBooksGrid = (props: ReadingBooksGridProps) => {
10+
const { books } = props;
11+
12+
return (
13+
<div className="grid grid-cols-3 gap-x-4 gap-y-7 pb-6">
14+
{books.map((book) => (
15+
<BookCard key={book.id} book={book} />
16+
))}
17+
</div>
18+
);
19+
};
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
import { MyBook } from 'pages/MyBooks/useMyBooksQuery';
2+
3+
type ReadingBooksListProps = {
4+
books: MyBook[];
5+
};
6+
7+
const MSG_MYBOOKS_READING_STATUS_READING = '읽는중';
8+
const MSG_MYBOOKS_READING_STATUS_STOPPED = '중단';
9+
const MSG_MYBOOKS_LIST_AUTHOR_PLACEHOLDER = '작가명을 입력해주세요';
10+
const MSG_MYBOOKS_LIST_PERIOD_PLACEHOLDER = '00.00.00 ~ 00.00.00';
11+
12+
const getReadCountLabel = (readCount: number) => `${readCount}회독`;
13+
14+
const getStatusLabel = (book: MyBook) => {
15+
if (book.readingStatus === MSG_MYBOOKS_READING_STATUS_READING) return MSG_MYBOOKS_READING_STATUS_READING;
16+
if (book.readingStatus === MSG_MYBOOKS_READING_STATUS_STOPPED) return MSG_MYBOOKS_READING_STATUS_STOPPED;
17+
18+
return getReadCountLabel(book.readCount);
19+
};
20+
21+
const getRatingLabel = (rating: number) => `(${rating})`;
22+
23+
const renderStars = (rating: number) => {
24+
const fullStars = Math.floor(rating);
25+
const hasHalf = rating % 1 >= 0.5;
26+
const emptyStars = 5 - fullStars - (hasHalf ? 1 : 0);
27+
28+
return '★'.repeat(fullStars) + (hasHalf ? '★' : '') + '☆'.repeat(emptyStars);
29+
};
30+
31+
export const ReadingBooksList = (props: ReadingBooksListProps) => {
32+
const { books } = props;
33+
34+
return (
35+
<div className="flex flex-col pb-6">
36+
{books.map((book) => {
37+
const statusLabel = getStatusLabel(book);
38+
const isReading = book.readingStatus === MSG_MYBOOKS_READING_STATUS_READING;
39+
40+
return (
41+
<div key={book.id} className="-mb-[21px] flex flex-col items-center">
42+
<div className="flex w-full items-center gap-5 px-4 py-2">
43+
<div className="relative h-[111px] w-20 shrink-0">
44+
<div className="absolute inset-0 overflow-hidden rounded bg-gradient-to-l from-[#D9D9D9] via-[#FFFFFF] to-[#F9F9F9]">
45+
<img src={book.cover} alt={book.title} className="size-full object-cover" loading="lazy" />
46+
</div>
47+
<div className="absolute inset-y-0 left-0 w-[9px] bg-gradient-to-r from-[#FFFFFF] to-[#E0E0E0] opacity-90" />
48+
</div>
49+
50+
<div className="flex min-w-0 flex-1 flex-col justify-between gap-1 self-stretch pt-1">
51+
<div className="flex flex-col gap-1">
52+
<span className="w-fit rounded-full border border-[#CACACA] px-2 py-0.5 text-caption1 text-[#888888]">
53+
{statusLabel}
54+
</span>
55+
56+
<div className="flex flex-col">
57+
<p className="line-clamp-2 text-title3 text-[#303030]">{book.title}</p>
58+
<p className="line-clamp-1 text-caption1 text-[#555555]">{MSG_MYBOOKS_LIST_AUTHOR_PLACEHOLDER}</p>
59+
</div>
60+
</div>
61+
62+
{!isReading && (
63+
<div className="flex items-center gap-2">
64+
<span className="text-[12px] leading-[1.2] text-[#FFAA00]">{renderStars(book.rating)}</span>
65+
<span className="text-caption3 text-[#888888]">{getRatingLabel(book.rating)}</span>
66+
</div>
67+
)}
68+
69+
{isReading && (
70+
<div className="flex items-center gap-2">
71+
<span className="text-[12px] leading-[1.2] text-[#FFAA00]">{renderStars(book.rating)}</span>
72+
<span className="text-caption3 text-neutral-80">{getRatingLabel(book.rating)}</span>
73+
</div>
74+
)}
75+
</div>
76+
</div>
77+
78+
<div className="w-full px-4">
79+
<p className="text-caption1 text-neutral-40">{MSG_MYBOOKS_LIST_PERIOD_PLACEHOLDER}</p>
80+
</div>
81+
82+
<div className="w-full">
83+
<div className="h-[30px] bg-gradient-to-b from-neutral-20 to-neutral-0" />
84+
<div className="h-[35px] bg-gradient-to-b from-neutral-20 via-[#F3F3F3] to-neutral-0" />
85+
</div>
86+
</div>
87+
);
88+
})}
89+
</div>
90+
);
91+
};

src/pages/MyBooks/ReadingSection/index.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,14 @@ import { RefObject } from 'react';
33
import { TextButton } from 'components/Button';
44
import { IconArrowDown, IconMenu } from 'components/icons';
55

6-
import { ReadingBooksGrid } from '../shared/ReadingBooksGrid';
7-
import { ReadingBooksList } from '../shared/ReadingBooksList';
6+
import { ReadingBooksGrid } from './ReadingBooksGrid';
7+
import { ReadingBooksList } from './ReadingBooksList';
88
import { MyBook } from '../useMyBooksQuery';
99

1010
type ReadingSectionProps = {
1111
books: MyBook[];
1212
totalCount: number;
13-
isGridView: boolean;
13+
viewMode: 'grid' | 'list';
1414
isLoading: boolean;
1515
observerTarget: RefObject<HTMLDivElement>;
1616
onOpenFilterLayer: () => void;
@@ -19,10 +19,10 @@ type ReadingSectionProps = {
1919

2020
const MSG_MYBOOKS_ALL_BOOKS = '모든 책';
2121
const MSG_MYBOOKS_SORT_LATEST = '최신순';
22-
const MSG_MYBOOKS_LOADING = '불러오는 중...';
2322

2423
export const ReadingSection = (props: ReadingSectionProps) => {
25-
const { books, totalCount, isGridView, isLoading, observerTarget, onOpenFilterLayer, onOpenSortLayer } = props;
24+
const { books, totalCount, viewMode, isLoading, observerTarget, onOpenFilterLayer, onOpenSortLayer } = props;
25+
const isGridView = viewMode === 'grid';
2626

2727
return (
2828
<>
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { RefObject } from 'react';
2+
3+
import { ReadingBooksList } from '../ReadingSection/ReadingBooksList';
4+
import { MyBook } from '../useMyBooksQuery';
5+
6+
type WishlistSectionProps = {
7+
books: MyBook[];
8+
isLoading: boolean;
9+
observerTarget: RefObject<HTMLDivElement>;
10+
};
11+
12+
const MSG_MYBOOKS_LOADING = '불러오는 중...';
13+
14+
export const WishlistSection = (props: WishlistSectionProps) => {
15+
const { books, isLoading, observerTarget } = props;
16+
17+
return (
18+
<div className="flex flex-1 flex-col overflow-y-auto">
19+
<ReadingBooksList books={books} />
20+
21+
{isLoading && (
22+
<div className="flex justify-center py-4">
23+
<span className="text-caption1 text-neutral-60">{MSG_MYBOOKS_LOADING}</span>
24+
</div>
25+
)}
26+
<div ref={observerTarget} className="h-4 w-full" />
27+
</div>
28+
);
29+
};

src/pages/MyBooks/index.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ const MyBooks = () => {
104104
<ReadingSection
105105
books={books}
106106
totalCount={totalCount}
107-
isGridView={isGridView}
107+
viewMode={viewType}
108108
isLoading={isLoading}
109109
observerTarget={observerTarget}
110110
onOpenFilterLayer={handleOpenFilterLayer}

0 commit comments

Comments
 (0)