Skip to content

Commit

Permalink
Merge pull request #154 from jihwooon/issue-82
Browse files Browse the repository at this point in the history
도서 상세 페이지 이미지 표시 및 서버 도메인 모델 업데이트
  • Loading branch information
jihwooon authored Mar 6, 2024
2 parents 796b0db + 649f37b commit 2ada589
Show file tree
Hide file tree
Showing 4 changed files with 177 additions and 102 deletions.
58 changes: 58 additions & 0 deletions client/src/components/common/EllipisisBox.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { useState } from "react";
import { FaAngleDown } from "react-icons/fa";
import { styled } from "styled-components";
import Button from "./Button";

interface Prpos {
children: React.ReactNode;
linelimit: number;
}

const EllipsisBox = ({ children, linelimit }: Prpos) => {
const [expanded, setExpanded] = useState(false);

return (
<EllipsisBoxStyle linelimit={linelimit} $expanded={expanded}>
<p>{children}</p>
<div className="toggle">
<Button
size="small"
scheme="normal"
onClick={() => {
setExpanded(!expanded);
}}
>
{expanded ? "접기" : "펼치기"} <FaAngleDown />
</Button>
</div>
</EllipsisBoxStyle>
);
};

interface EllipsisBoxStyleProps {
linelimit: number;
$expanded: boolean;
}

const EllipsisBoxStyle = styled.div<EllipsisBoxStyleProps>`
p {
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: ${({ linelimit, $expanded }) => ($expanded ? "none" : linelimit)};
- webkit - box - orient : vertical;
padding: 20px 0 0 0;
margin: 0;
}
.toggle {
display: flex;
justify-content: end;
}
svg {
transform: ${({ $expanded }) => ($expanded ? "rotate(180deg)" : "rotate(0)")}
}
`;

export default EllipsisBox;
216 changes: 116 additions & 100 deletions client/src/pages/BookDetail.tsx
Original file line number Diff line number Diff line change
@@ -1,123 +1,139 @@
import { Link, useParams } from "react-router-dom";
import styled from "styled-components";
import EllipsisBox from "../components/common/EllipisisBox";
import Title from "../components/common/Title";
import { useBook } from "../hooks/useBook";
import { BookDetail as IBookDetail } from "../models/book.model";
import { formatDate, formatNumber } from "../utils/format";
import { getImgSrc } from "../utils/image";

const bookInfoList = [
{
label: "카테고리",
key: "categoryName",
filter: (book: IBookDetail) => (
<Link to={`/books?category_id=${book.categoryId}`}>
{book.categoryName}
</Link>
)
},
{
label: "포맷",
key: "form"
},
{
label: "페이지",
key: "pages"
},
{
label: "ISBN",
key: "isbn"
},
{
label: "출간일",
key: "pubDate",
filter: (book: IBookDetail) => {
return formatDate(book.pubDate)
}
},
{
label: "가격",
key: "price",
filter: (book: IBookDetail) => {
return `${formatNumber(book.price)} 원`
}
}
]
{
label: "카테고리",
key: "categoryName",
filter: (book: IBookDetail) => (
<Link to={`/books?category_id=${book.categoryId}`}>
{book.categoryName}
</Link>
),
},
{
label: "포맷",
key: "form",
},
{
label: "페이지",
key: "pages",
},
{
label: "ISBN",
key: "isbn",
},
{
label: "출간일",
key: "pubDate",
filter: (book: IBookDetail) => {
return formatDate(book.pubDate);
},
},
{
label: "가격",
key: "price",
filter: (book: IBookDetail) => {
return `${formatNumber(book.price)} 원`;
},
},
];

const BookDetail = () => {
const { bookId } = useParams();
const { book } = useBook(bookId);
const { bookId } = useParams();
const { book } = useBook(bookId);

if (!book) return null;
if (!book) return null;

return (
<BookDetailStyle>
<header className="header">
<div className="img">
<img src={getImgSrc(book.imgId)} alt={book.title}/>
</div>
<div className="info">
<Title size="large" color="text">
{book.title}
</Title>
{
bookInfoList.map((item) => (
<dl>
<dt>{item.label}</dt>
<dd>{item.filter ? item.filter(book) : book
[item.key as keyof IBookDetail]}</dd>
</dl>
))
}
<p className="summary">{book.summary}</p>
return (
<BookDetailStyle>
<header className="header">
<div className="img">
<img src={getImgSrc(book.imgId)} alt={book.title} />
</div>
<div className="info">
<Title size="large" color="text">
{book.title}
</Title>
{bookInfoList.map((item) => (
<dl>
<dt>{item.label}</dt>
<dd>
{item.filter
? item.filter(book)
: book[item.key as keyof IBookDetail]}
</dd>
</dl>
))}
<p className="summary">{book.summary}</p>

<div className="like">
Like
</div>
<div className="like">Like</div>

<div className="add-cart">
Cart
</div>
</div>
</header>
</BookDetailStyle>
)
}
<div className="add-cart">Cart</div>
</div>
</header>
<div className="content">
<Title size="medium">상세 설명</Title>
<EllipsisBox linelimit={4}>{book.detail}</EllipsisBox>

<Title size="medium">목차</Title>
<p className="index">{book.contents}</p>
</div>
</BookDetailStyle>
);
};

const BookDetailStyle = styled.div`
.header {
display: flex;
align-items: start;
gap: 24px;
padding: 0 0 24px 0;
display: flex;
align-items: start;
gap: 24px;
padding: 0 0 24px 0;
.img {
flex: 1;
img {
width: 100%;
height: auto;
}
}
.img {
flex: 1;
img {
width: 100%;
height: auto;
}
}
.info {
flex: 1;
display: flex;
flex-direction: column;
gap: 12px;
.info {
flex: 1;
display: flex;
flex-direction: column;
gap: 12px;
dl {
display: flex;
margin: 0;
dt {
width: 80px;
color: ${({ theme }) => theme.color.secondary};
}
a {
color: ${({ theme }) => theme.color.primary};
}
}
dl {
display: flex;
margin: 0;
dt {
width: 80px;
color: ${({ theme }) => theme.color.secondary}
}
a {
color: ${({ theme }) => theme.color.primary}
}
}
}
}
`
.content {
.detail {
height: 200px;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 4;
-webkit-box-orient: vertical;
}
}
}
}
`;

export default BookDetail;
2 changes: 1 addition & 1 deletion server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
"test": "jest --runInBand --detectOpenHandles --forceExit",
"test:only-changed": "jest --runInBand --onlyChanged",
"test:watch": "jest --watch --runInBand --detectOpenHandles --forceExit",
"start": "tsx watch src/index.ts"
"start": "tsx watch -r dotenv/config src/index.ts"
},
"keywords": [],
"author": "",
Expand Down
3 changes: 2 additions & 1 deletion server/src/books/domain/books.repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ export const findAll = async (
export const findWithCategory = async (userId: number, bookId: number): Promise<Book> => {
const [rows] = await doQuery((connection) =>
connection.execute<RowDataPacket[]>(
`SELECT b.id, b.title, b.category_id, c.name as category_name, b.form, b.isbn, b.summary, b.detail, b.author, b.pages, b.contents, b.price, b.pub_date,
`SELECT b.id, b.title, b.category_id, c.name as category_name, b.form, b.isbn, b.summary, b.detail, b.author, b.pages, b.contents, b.price, b.pub_date, b.img_id,
(SELECT count(*) FROM likes WHERE b.id = liked_book_id) as Likes,
(SELECT EXISTS(SELECT * FROM likes WHERE user_id = ? AND liked_book_id)) as liked
FROM books b
Expand Down Expand Up @@ -88,6 +88,7 @@ export const findWithCategory = async (userId: number, bookId: number): Promise<
price: row.price,
likes: row.likes,
pubDate: row.pub_date,
imgId: row.img_id,
});
};

Expand Down

0 comments on commit 2ada589

Please sign in to comment.