From ff7fe097142bd44eaca94927459175af9430ffdb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=8F=84=ED=98=84?= Date: Wed, 30 Nov 2022 13:31:47 +0900 Subject: [PATCH 01/76] =?UTF-8?q?feat:=20=EA=B8=80=20=EA=B2=80=EC=83=89=20?= =?UTF-8?q?=EA=B2=B0=EA=B3=BC=EB=A5=BC=20=ED=91=9C=EC=8B=9C=ED=95=98?= =?UTF-8?q?=EB=8A=94=20ArticleList=20=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80=20-=20#116?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/search/ArticleItem/index.tsx | 40 +++++++++++++++++++ .../{SearchListItem => ArticleItem}/styled.ts | 0 .../components/search/ArticleList/index.tsx | 18 ++++++--- .../search/SearchListItem/index.tsx | 39 ------------------ frontend/interfaces/article.interface.ts | 3 -- frontend/pages/search.tsx | 9 +++-- 6 files changed, 59 insertions(+), 50 deletions(-) create mode 100644 frontend/components/search/ArticleItem/index.tsx rename frontend/components/search/{SearchListItem => ArticleItem}/styled.ts (100%) delete mode 100644 frontend/components/search/SearchListItem/index.tsx diff --git a/frontend/components/search/ArticleItem/index.tsx b/frontend/components/search/ArticleItem/index.tsx new file mode 100644 index 00000000..b2b7ca71 --- /dev/null +++ b/frontend/components/search/ArticleItem/index.tsx @@ -0,0 +1,40 @@ +import Image from 'next/image'; + +import TemporaryImage from '@assets/img_profile.png'; +import { TextSmall, TextXSmall } from '@styles/common'; + +import { + ItemContent, + ItemGroup, + ItemTitle, + ItemWrapper, + ProfileDescription, + ProfileImage, + UserProfile, +} from './styled'; + +interface ArticleItemProps { + title: string; + content: string; + nickname: string; +} + +export default function ArticleItem({ title, content, nickname }: ArticleItemProps) { + return ( + + + {title} + {content} + + + + Written By + {nickname} + + + profile + + + + ); +} diff --git a/frontend/components/search/SearchListItem/styled.ts b/frontend/components/search/ArticleItem/styled.ts similarity index 100% rename from frontend/components/search/SearchListItem/styled.ts rename to frontend/components/search/ArticleItem/styled.ts diff --git a/frontend/components/search/ArticleList/index.tsx b/frontend/components/search/ArticleList/index.tsx index 10473d78..3637c8da 100644 --- a/frontend/components/search/ArticleList/index.tsx +++ b/frontend/components/search/ArticleList/index.tsx @@ -1,12 +1,20 @@ -import SearchListItem from '@components/search/SearchListItem'; +import ArticleItem from '@components/search/ArticleItem'; +import { IArticleBook } from '@interfaces'; -export default function ArticleList() { - const items = Array.from({ length: 50 }, (_, i) => i); +interface ArticleListProps { + articles: IArticleBook[]; +} +export default function ArticleList({ articles }: ArticleListProps) { return ( <> - {items.map((item) => ( - + {articles.map((article) => ( + ))} ); diff --git a/frontend/components/search/SearchListItem/index.tsx b/frontend/components/search/SearchListItem/index.tsx deleted file mode 100644 index fc063428..00000000 --- a/frontend/components/search/SearchListItem/index.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import Image from 'next/image'; - -import TemporaryImage from '@assets/img_profile.png'; -import { TextSmall, TextXSmall } from '@styles/common'; - -import { - ItemContent, - ItemGroup, - ItemTitle, - ItemWrapper, - ProfileDescription, - ProfileImage, - UserProfile, -} from './styled'; - -export default function SearchListItem() { - return ( - - - 리액트 개발 환경 세팅하기 - - 그럼 리액트를 사용하기 위한 개발 환경을 세팅해보자. 대부분의 블로그에서 리액트와 함께 - Webpack, Babel을 함께 소개하는 경우가 많다. 하지만 입문자 입장에서는 리액트만으로도 - 공부하기 벅차기 때문에 본 글에서는 내용을 공부하기 벅차기 때문에 - 가나다라마바사가나다라마바사가나다라마바사가나다라마바사가나다라마바사 - - - - - Written By - Web01 - - - profile - - - - ); -} diff --git a/frontend/interfaces/article.interface.ts b/frontend/interfaces/article.interface.ts index ffbc4198..c2c340c4 100644 --- a/frontend/interfaces/article.interface.ts +++ b/frontend/interfaces/article.interface.ts @@ -1,5 +1,3 @@ -// import { IBook } from './book.interface'; - export interface IArticle { id: number; title: string; @@ -7,5 +5,4 @@ export interface IArticle { created_at: string; deleted_at: string; book_id: number; - // book: IBook; } diff --git a/frontend/pages/search.tsx b/frontend/pages/search.tsx index 750ef634..7949580b 100644 --- a/frontend/pages/search.tsx +++ b/frontend/pages/search.tsx @@ -24,7 +24,10 @@ export default function Search() { }; useEffect(() => { - // 데이터 받아오기 + if (!debouncedKeyword) return; + + if (filter.type === 'article') searchArticles({ query: debouncedKeyword, userId: 1, page: 1 }); + else if (filter.type === 'book') searchBooks({ query: debouncedKeyword, userId: 1, page: 1 }); }, [debouncedKeyword]); const handleFilter = (value: { [value: string]: string | number }) => { @@ -41,8 +44,8 @@ export default function Search() { - {filter.type === 'article' && } - {filter.type === 'book' && } + {articles?.length > 0 && filter.type === 'article' && } + {books?.length > 0 && filter.type === 'book' && } From d94f6ea065949a31ddb42ab9ff174bb0ca6247dc Mon Sep 17 00:00:00 2001 From: dahyeon405 Date: Wed, 30 Nov 2022 13:36:16 +0900 Subject: [PATCH 02/76] =?UTF-8?q?feat:=20useIntersectionObserver=20?= =?UTF-8?q?=ED=9B=85=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/hooks/useIntersectionObserver.ts | 28 +++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 frontend/hooks/useIntersectionObserver.ts diff --git a/frontend/hooks/useIntersectionObserver.ts b/frontend/hooks/useIntersectionObserver.ts new file mode 100644 index 00000000..9668f560 --- /dev/null +++ b/frontend/hooks/useIntersectionObserver.ts @@ -0,0 +1,28 @@ +import { RefObject, useEffect, useState } from 'react'; + +const useIntersectionObserver = (elementRef: RefObject) => { + const [isIntersecting, setIntersecting] = useState(false); + + const onIntersect = ([entry]: IntersectionObserverEntry[]) => { + const isElementIntersecting = entry.isIntersecting; + setIntersecting(isElementIntersecting); + }; + + useEffect(() => { + const target = elementRef?.current; + if (!target) return undefined; + + const observer = new IntersectionObserver(onIntersect, { + threshold: 0.1, + rootMargin: '300px', + }); + + observer.observe(target); + + return () => observer.disconnect(); + }, [elementRef?.current]); + + return isIntersecting; +}; + +export default useIntersectionObserver; From 27b1dab78330682a556c56e5cb4656e9dd18a5b8 Mon Sep 17 00:00:00 2001 From: parkhyeonki Date: Wed, 30 Nov 2022 15:38:17 +0900 Subject: [PATCH 03/76] =?UTF-8?q?feat:=20=EC=B1=85=20=EC=88=98=EC=A0=95=20?= =?UTF-8?q?=EB=8D=B0=EC=9D=B4=ED=84=B0=20=EB=A1=9C=EC=A7=81=20-=20#135?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/src/apis/images/images.controller.ts | 1 + frontend/components/study/EditBook/index.tsx | 64 +++++++++++++++++--- frontend/components/study/EditBook/styled.ts | 22 ++++++- frontend/next.config.js | 10 +++ 4 files changed, 86 insertions(+), 11 deletions(-) diff --git a/backend/src/apis/images/images.controller.ts b/backend/src/apis/images/images.controller.ts index 444e178c..e895bd6e 100644 --- a/backend/src/apis/images/images.controller.ts +++ b/backend/src/apis/images/images.controller.ts @@ -3,6 +3,7 @@ import { Request, Response } from 'express'; import imagesService from '@apis/images/images.service'; const createImage = async (req: Request, res: Response) => { + console.log(req.file); const imagePath = await imagesService.createImage({ file: req.file }); res.status(201).send({ imagePath }); diff --git a/frontend/components/study/EditBook/index.tsx b/frontend/components/study/EditBook/index.tsx index ff89dbbc..c2e5fdbd 100644 --- a/frontend/components/study/EditBook/index.tsx +++ b/frontend/components/study/EditBook/index.tsx @@ -1,8 +1,14 @@ import Image from 'next/image'; +import { useEffect, useRef, useState } from 'react'; + +import { createImageApi } from '@apis/imageApi'; +import Edit from '@assets/ico_edit.svg'; import MoreContentsIcon from '@assets/ico_more_contents.svg'; import SampleThumbnail from '@assets/img_sample_thumbnail.jpg'; import Button from '@components/common/Modal/ModalButton'; +import useFetch from '@hooks/useFetch'; +import useInput from '@hooks/useInput'; import { IBookScraps } from '@interfaces'; import { FlexCenter, FlexSpaceBetween } from '@styles/layout'; @@ -17,6 +23,9 @@ import { Author, Input, BookContent, + EditBookWapper, + EditBookThumbnailWrapper, + EditBookThumbnailIcon, } from './styled'; interface BookProps { @@ -25,20 +34,55 @@ interface BookProps { export default function EditBook({ book }: BookProps) { const { id, title, user, scraps } = book; + const { value: titleData, onChange: onTitleChange } = useInput(title); + const { data: imgFile, execute: createImage } = useFetch(createImageApi); + const [scrapList, setScrapList] = useState(scraps); + + const inputFile = useRef(null); + + const handleEditBookImgClick = () => { + if (!inputFile.current) return; + inputFile.current.click(); + }; + + const handleFileUpload = (event: React.ChangeEvent) => { + event.stopPropagation(); + event.preventDefault(); + + if (!event.target.files) return; + + const formData = new FormData(); + formData.append('image', event.target.files[0]); + createImage(formData); + }; + + const handleCompletedBtnClick = () => { + console.log(id); + console.log(titleData); + console.log('click', imgFile); + console.log(scrapList); + }; return ( - <> + - - {/* 수정 버튼 추가 예정 */} + + + + profile_edit + + + - console.log(e.target.value)} - /> + by {user.nickname} @@ -61,9 +105,9 @@ export default function EditBook({ book }: BookProps) { - - + ); } diff --git a/frontend/components/study/EditBook/styled.ts b/frontend/components/study/EditBook/styled.ts index 8a678f8e..5a7141fa 100644 --- a/frontend/components/study/EditBook/styled.ts +++ b/frontend/components/study/EditBook/styled.ts @@ -3,7 +3,12 @@ import Image from 'next/image'; import styled from 'styled-components'; import { TextXSmall, TextSmall } from '@styles/common'; -import { FlexColumn } from '@styles/layout'; +import { FlexColumn, FlexColumnCenter } from '@styles/layout'; + +export const EditBookWapper = styled(FlexColumnCenter)` + width: 320px; + margin: auto; +`; export const BookWrapper = styled(FlexColumn)` width: 100%; @@ -86,3 +91,18 @@ export const Author = styled.div` display: block; margin-top: 2px; `; + +export const EditBookThumbnailWrapper = styled.div``; +export const EditBookThumbnailIcon = styled.div` + position: absolute; + display: flex; + justify-content: center; + align-items: center; + border-radius: 100%; + padding: 10px; + box-sizing: content-box; + border: 1px solid var(--grey-01-color); + transform: translate(650%, -120%); + background-color: var(--white-color); + cursor: pointer; +`; diff --git a/frontend/next.config.js b/frontend/next.config.js index 3d3bc999..eb62949c 100644 --- a/frontend/next.config.js +++ b/frontend/next.config.js @@ -2,6 +2,16 @@ const nextConfig = { reactStrictMode: true, swcMinify: true, + images: { + remotePatterns: [ + { + protocol: 'https', + hostname: 'kr.object.ncloudstorage.com', + port: '', + pathname: '/j027/**', + }, + ], + }, }; module.exports = nextConfig; From fe170f0bf5f70a01477758762a90797ccd6f15a6 Mon Sep 17 00:00:00 2001 From: dahyeon405 Date: Wed, 30 Nov 2022 15:53:20 +0900 Subject: [PATCH 04/76] =?UTF-8?q?feat:=20searchFilter=EC=97=90=20=EC=A0=84?= =?UTF-8?q?=EC=97=AD=20=EC=83=81=ED=83=9C=20=ED=99=95=EC=9D=B8=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20=EC=B6=94=EA=B0=80=20-=20#122?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/components/search/SearchFilter/index.tsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/frontend/components/search/SearchFilter/index.tsx b/frontend/components/search/SearchFilter/index.tsx index 95174ccf..27a5d7b9 100644 --- a/frontend/components/search/SearchFilter/index.tsx +++ b/frontend/components/search/SearchFilter/index.tsx @@ -1,3 +1,6 @@ +import signInStatusState from '@atoms/signInStatus'; +import { useRecoilValue } from 'recoil'; + import { FilterButton, FilterGroup, FilterLabel, FilterWrapper } from './styled'; interface SearchFilterProps { @@ -5,6 +8,8 @@ interface SearchFilterProps { } export default function SearchFilter({ handleFilter }: SearchFilterProps) { + const signInStatus = useRecoilValue(signInStatusState); + return ( @@ -25,7 +30,7 @@ export default function SearchFilter({ handleFilter }: SearchFilterProps) { handleFilter({ userId: e.target.checked ? 1 : 0 })} + onChange={(e) => handleFilter({ userId: e.target.checked ? signInStatus.id : 0 })} /> 내 책에서 검색 From 2957db750e315a50627b8c53001e02b668364c30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=8F=84=ED=98=84?= Date: Wed, 30 Nov 2022 16:11:56 +0900 Subject: [PATCH 05/76] =?UTF-8?q?feat:=20=EA=B2=80=EC=83=89=20API=20?= =?UTF-8?q?=EB=AC=B4=ED=95=9C=20=EC=8A=A4=ED=81=AC=EB=A1=A4=20=EB=8C=80?= =?UTF-8?q?=EC=9D=91=20=EC=88=98=EC=A0=95=20-=20#117,=20#120?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/src/apis/articles/articles.controller.ts | 4 ++-- backend/src/apis/articles/articles.service.ts | 5 ++++- backend/src/apis/books/books.controller.ts | 4 ++-- backend/src/apis/books/books.service.ts | 8 ++++++-- 4 files changed, 14 insertions(+), 7 deletions(-) diff --git a/backend/src/apis/articles/articles.controller.ts b/backend/src/apis/articles/articles.controller.ts index 172d58e2..a5500a01 100644 --- a/backend/src/apis/articles/articles.controller.ts +++ b/backend/src/apis/articles/articles.controller.ts @@ -7,9 +7,9 @@ import scrapsService from '@apis/scraps/scraps.service'; const searchArticles = async (req: Request, res: Response) => { const { query, page, userId } = req.query as unknown as SearchArticles; - const articles = await articlesService.searchArticles({ query, page, userId }); + const searchResult = await articlesService.searchArticles({ query, page, userId }); - res.status(200).send(articles); + res.status(200).send(searchResult); }; const getArticle = async (req: Request, res: Response) => { diff --git a/backend/src/apis/articles/articles.service.ts b/backend/src/apis/articles/articles.service.ts index c34063af..0138b69a 100644 --- a/backend/src/apis/articles/articles.service.ts +++ b/backend/src/apis/articles/articles.service.ts @@ -53,7 +53,10 @@ const searchArticles = async (searchArticles: SearchArticles) => { skip, }); - return articles; + return { + data: articles, + hasNextPage: articles.length === take, + }; }; const getArticle = async (articleId: number) => { diff --git a/backend/src/apis/books/books.controller.ts b/backend/src/apis/books/books.controller.ts index 2ac1c0cc..1a93831b 100644 --- a/backend/src/apis/books/books.controller.ts +++ b/backend/src/apis/books/books.controller.ts @@ -31,9 +31,9 @@ const getBooks = async (req: Request, res: Response) => { const getSearchedBooks = async (req: Request, res: Response) => { const { query, page, userId } = req.query as unknown as SearchBooks; - const books = await booksService.searchBooks({ query, userId, page }); + const searchResult = await booksService.searchBooks({ query, userId, page }); - res.status(200).send(books); + res.status(200).send(searchResult); }; export default { diff --git a/backend/src/apis/books/books.service.ts b/backend/src/apis/books/books.service.ts index d176c5e5..43ff79a3 100644 --- a/backend/src/apis/books/books.service.ts +++ b/backend/src/apis/books/books.service.ts @@ -97,6 +97,7 @@ const findBooks = async ({ order, take, userId, editor }: FindBooks) => { }; const searchBooks = async ({ query, userId, page }: SearchBooks) => { + const take = 10; const skip = (page - 1) * 10; const books = await prisma.book.findMany({ @@ -138,10 +139,13 @@ const searchBooks = async ({ query, userId, page }: SearchBooks) => { }, }, skip, - take: 10, + take, }); - return books; + return { + data: books, + hasNextPage: books.length === take, + }; }; export default { From f1c7652fe3a08f10b7c56c23fd5616d698da5c15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=8F=84=ED=98=84?= Date: Wed, 30 Nov 2022 16:16:52 +0900 Subject: [PATCH 06/76] =?UTF-8?q?chore:=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20=EC=BD=94=EB=93=9C=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ISSUE_TEMPLATE/pull_request_template.md | 28 ------------------- 1 file changed, 28 deletions(-) delete mode 100644 .github/ISSUE_TEMPLATE/pull_request_template.md diff --git a/.github/ISSUE_TEMPLATE/pull_request_template.md b/.github/ISSUE_TEMPLATE/pull_request_template.md deleted file mode 100644 index 44eb70f9..00000000 --- a/.github/ISSUE_TEMPLATE/pull_request_template.md +++ /dev/null @@ -1,28 +0,0 @@ ---- -name: Pull Request -about: Pull Request 작업 사항을 알려주세요! -title: '' -labels: '' -assignees: '' - ---- - -## 개요 - - - -## 변경 사항 - -- - -## 참고 사항 - -- - -## 스크린샷 - - - -## 관련 이슈 - -- From 29789ef7fdefe691203b7f5397c64c19a1e2ecfe Mon Sep 17 00:00:00 2001 From: dahyeon405 Date: Wed, 30 Nov 2022 16:19:49 +0900 Subject: [PATCH 07/76] =?UTF-8?q?feat:=20=EA=B2=80=EC=83=89=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=20=EA=B8=80=20=EB=B3=B4=EA=B8=B0=20=EB=AC=B4?= =?UTF-8?q?=ED=95=9C=EC=8A=A4=ED=81=AC=EB=A1=A4=20=EC=A0=81=EC=9A=A9=20-?= =?UTF-8?q?=20#160,=20#161,=20#167?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/pages/search.tsx | 66 +++++++++++++++++++++++++++++++++++---- 1 file changed, 60 insertions(+), 6 deletions(-) diff --git a/frontend/pages/search.tsx b/frontend/pages/search.tsx index 7949580b..2d12b783 100644 --- a/frontend/pages/search.tsx +++ b/frontend/pages/search.tsx @@ -1,4 +1,4 @@ -import { useEffect, useState } from 'react'; +import { RefObject, useEffect, useRef, useState } from 'react'; import { searchArticlesApi } from '@apis/articleApi'; import { searchBooksApi } from '@apis/bookApi'; @@ -9,16 +9,26 @@ import SearchBar from '@components/search/SearchBar'; import SearchFilter from '@components/search/SearchFilter'; import useDebounce from '@hooks/useDebounce'; import useFetch from '@hooks/useFetch'; +import useIntersectionObserver from '@hooks/useIntersectionObserver'; import { PageInnerSmall, PageWrapper } from '@styles/layout'; export default function Search() { - const { data: articles, execute: searchArticles } = useFetch(searchArticlesApi); - const { data: books, execute: searchBooks } = useFetch(searchBooksApi); + const [articles, setArticles] = useState([]); + const [books, setBooks] = useState([]); + + const { data: newArticles, execute: searchArticles } = useFetch(searchArticlesApi); + const { data: newBooks, execute: searchBooks } = useFetch(searchBooksApi); const [keyword, setKeyword] = useState(''); const [filter, setFilter] = useState({ type: 'article', userId: 0 }); const debouncedKeyword = useDebounce(keyword, 1000); + const target = useRef() as RefObject; + const isIntersecting = useIntersectionObserver(target); + + const [articlePage, setArticlePage] = useState({ hasNextPage: true, pageNumber: 2 }); + const [bookPage, setBookPage] = useState({ hasNextPage: true, pageNumber: 2 }); + const handleSearchbarOnChange = (e: React.ChangeEvent) => { setKeyword(e.target.value); }; @@ -26,9 +36,52 @@ export default function Search() { useEffect(() => { if (!debouncedKeyword) return; - if (filter.type === 'article') searchArticles({ query: debouncedKeyword, userId: 1, page: 1 }); - else if (filter.type === 'book') searchBooks({ query: debouncedKeyword, userId: 1, page: 1 }); - }, [debouncedKeyword]); + if (filter.type === 'article') { + setArticles([]); + searchArticles({ query: debouncedKeyword, userId: filter.userId, page: 1 }); + setArticlePage({ + hasNextPage: true, + pageNumber: 2, + }); + } else if (filter.type === 'book') { + setBooks([]); + searchBooks({ query: debouncedKeyword, userId: filter.userId, page: 1 }); + setBookPage({ + hasNextPage: true, + pageNumber: 2, + }); + } + }, [debouncedKeyword, filter.userId]); + + useEffect(() => { + if (!isIntersecting) return; + + if (filter.type === 'article') { + if (!articlePage.hasNextPage) return; + searchArticles({ query: debouncedKeyword, userId: filter.userId, page: articlePage }); + setArticlePage({ + ...articlePage, + pageNumber: articlePage.pageNumber + 1, + }); + } else if (filter.type === 'book') { + if (!bookPage.hasNextPage) return; + searchBooks({ query: debouncedKeyword, userId: filter.userId, page: bookPage }); + setBookPage({ + ...bookPage, + pageNumber: bookPage.pageNumber + 1, + }); + } + }, [isIntersecting]); + + useEffect(() => { + if (!newArticles) return; + setArticles(articles.concat(newArticles.data)); + }, [newArticles]); + + useEffect(() => { + if (!newBooks) return; + setBooks(books.concat(newBooks.data)); + }, [newBooks]); const handleFilter = (value: { [value: string]: string | number }) => { setFilter({ @@ -46,6 +99,7 @@ export default function Search() { {articles?.length > 0 && filter.type === 'article' && } {books?.length > 0 && filter.type === 'book' && } +
From e7b348e0e5da56aa833adab2d38438de12a95e97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=8F=84=ED=98=84?= Date: Wed, 30 Nov 2022 16:22:55 +0900 Subject: [PATCH 08/76] =?UTF-8?q?build:=20=EB=B0=B1=EC=97=94=EB=93=9C=20Es?= =?UTF-8?q?lint=20=EC=8A=A4=ED=81=AC=EB=A6=BD=ED=8A=B8=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/.eslintrc.js | 2 +- backend/package.json | 3 ++- backend/src/loaders/express.loader.ts | 3 ++- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/backend/.eslintrc.js b/backend/.eslintrc.js index 3ba8c273..4af6a157 100644 --- a/backend/.eslintrc.js +++ b/backend/.eslintrc.js @@ -19,7 +19,7 @@ module.exports = { node: true, jest: true, }, - ignorePatterns: ['.eslintrc.js'], + ignorePatterns: ['.eslintrc.js', 'dist/**'], rules: { '@typescript-eslint/interface-name-prefix': 'off', '@typescript-eslint/explicit-function-return-type': 'off', diff --git a/backend/package.json b/backend/package.json index 96a89bf9..daac1602 100644 --- a/backend/package.json +++ b/backend/package.json @@ -2,7 +2,8 @@ "scripts": { "dev": "cross-env NODE_ENV=dev tsc-watch --onSuccess \"ts-node -r tsconfig-paths/register dist/index.js\"", "build": "tsc", - "start": "NODE_ENV=prod node -r ./tsconfig-paths.js dist/index.js" + "start": "NODE_ENV=prod node -r ./tsconfig-paths.js dist/index.js", + "lint": "eslint ." }, "dependencies": { "@prisma/client": "^4.7.0", diff --git a/backend/src/loaders/express.loader.ts b/backend/src/loaders/express.loader.ts index f5fd42b1..0f99cbda 100644 --- a/backend/src/loaders/express.loader.ts +++ b/backend/src/loaders/express.loader.ts @@ -1,10 +1,11 @@ import { Application, ErrorRequestHandler, json, urlencoded } from 'express'; -import router from '@apis'; import cookieParser from 'cookie-parser'; import cors from 'cors'; import logger from 'morgan'; +import router from '@apis'; + const errorHandler: ErrorRequestHandler = (err, req, res, next) => { const { status, message } = err; From bebe840bf98656c1878c224a3cdec165de0f4fc1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=8F=84=ED=98=84?= Date: Wed, 30 Nov 2022 16:23:10 +0900 Subject: [PATCH 09/76] =?UTF-8?q?build:=20GitHub=20Action=20(CI)=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/backend-ci.yml | 66 ++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 .github/workflows/backend-ci.yml diff --git a/.github/workflows/backend-ci.yml b/.github/workflows/backend-ci.yml new file mode 100644 index 00000000..2e0db250 --- /dev/null +++ b/.github/workflows/backend-ci.yml @@ -0,0 +1,66 @@ +name: Backend CI + +on: + pull_request: + branches: [main, develop] + paths: + - 'backend/**' + +jobs: + lint: + name: Lint CI + + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Cache Dependencies + uses: actions/cache@v3 + id: backend-cache + with: + path: backend/node_modules + key: ${{ runner.OS }}-build-${{ hashFiles('**/package-lock.json') }} + restore-keys: | + ${{ runner.OS }}-build- + ${{ runner.OS }}- + + - if: ${{ steps.backend-cache.outputs.cache-hit != 'true' }} + name: Install Dependencies + run: npm install + working-directory: backend + + - name: Check Lint + run: npm run lint + env: + CI: true + working-directory: backend + + build: + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Cache Dependencies + uses: actions/cache@v3 + id: backend-cache + with: + path: backend/node_modules + key: ${{ runner.OS }}-build-${{ hashFiles('**/package-lock.json') }} + restore-keys: | + ${{ runner.OS }}-build- + ${{ runner.OS }}- + + - if: ${{ steps.backend-cache.outputs.cache-hit != 'true' }} + name: Install Dependencies + run: npm install + working-directory: backend + + - name: Build + run: npm run build + env: + CI: true + working-directory: backend From cff45e8b81ee216add9607f661f9833f3366ca61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=8F=84=ED=98=84?= Date: Wed, 30 Nov 2022 16:26:20 +0900 Subject: [PATCH 10/76] =?UTF-8?q?build:=20GitHub=20Action=20(CI)=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/backend-ci.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/backend-ci.yml b/.github/workflows/backend-ci.yml index 2e0db250..86c6a4a5 100644 --- a/.github/workflows/backend-ci.yml +++ b/.github/workflows/backend-ci.yml @@ -38,6 +38,8 @@ jobs: working-directory: backend build: + name: Build CI + runs-on: ubuntu-latest steps: From 3da34b99ea4bb68013397cef68ce0f114d9765d2 Mon Sep 17 00:00:00 2001 From: parkhyeonki Date: Wed, 30 Nov 2022 16:26:31 +0900 Subject: [PATCH 11/76] =?UTF-8?q?feat:=20=EC=B1=85=20=EC=9D=B4=EB=AF=B8?= =?UTF-8?q?=EC=A7=80,=20=ED=83=80=EC=9D=B4=ED=8B=80=20=EC=88=98=EC=A0=95?= =?UTF-8?q?=20=EC=99=84=EB=A3=8C=20-#135?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/src/apis/books/books.controller.ts | 10 ++++++++++ backend/src/apis/books/books.service.ts | 18 ++++++++++++++++++ backend/src/apis/index.ts | 1 + frontend/apis/bookApi.ts | 15 +++++++++++++++ frontend/components/common/Book/index.tsx | 12 ++++++++---- frontend/components/study/EditBook/index.tsx | 20 +++++++++++++++++--- 6 files changed, 69 insertions(+), 7 deletions(-) diff --git a/backend/src/apis/books/books.controller.ts b/backend/src/apis/books/books.controller.ts index 0fe476c1..2b760a64 100644 --- a/backend/src/apis/books/books.controller.ts +++ b/backend/src/apis/books/books.controller.ts @@ -36,9 +36,19 @@ const createBook = async (req: Request, res: Response) => { res.status(201).send(book); }; +const editBook = async (req: Request, res: Response) => { + // const userId = res.locals.user.id; + + console.log(req.body); + + const book = await booksService.editBook(req.body); + + res.status(200).send(book); +}; export default { getBook, getBooks, createBook, + editBook, }; diff --git a/backend/src/apis/books/books.service.ts b/backend/src/apis/books/books.service.ts index bc710e5d..e116901f 100644 --- a/backend/src/apis/books/books.service.ts +++ b/backend/src/apis/books/books.service.ts @@ -112,8 +112,26 @@ const createBook = async ({ title, userId }: CreateBook) => { return book; }; +const editBook = async (dto: any) => { + console.log(dto); + const { id, title, thumbnail_image, scraps } = dto; + const book = await prisma.book.update({ + where: { + id, + }, + data: { + title, + thumbnail_image: thumbnail_image, + // scraps, + }, + }); + + return book; +}; + export default { findBook, findBooks, createBook, + editBook, }; diff --git a/backend/src/apis/index.ts b/backend/src/apis/index.ts index ccd63e43..312c9e79 100644 --- a/backend/src/apis/index.ts +++ b/backend/src/apis/index.ts @@ -32,6 +32,7 @@ router.get('/books/search'); router.get('/books/:bookId', decoder, catchAsync(booksController.getBook)); router.get('/books', decoder, catchAsync(booksController.getBooks)); router.post('/books', catchAsync(guard), catchAsync(booksController.createBook)); +router.patch('/books', catchAsync(booksController.editBook)); router.post('/bookmarks', catchAsync(guard), catchAsync(bookmarksController.createBookmark)); router.delete('/bookmarks/:bookmarkId', catchAsync(bookmarksController.deleteBookmark)); diff --git a/frontend/apis/bookApi.ts b/frontend/apis/bookApi.ts index ca41ec91..61adfc75 100644 --- a/frontend/apis/bookApi.ts +++ b/frontend/apis/bookApi.ts @@ -1,3 +1,4 @@ +import { IScrap } from '@interfaces'; import api from '@utils/api'; interface GetBooksApi { @@ -5,6 +6,13 @@ interface GetBooksApi { take: number; } +interface EditBookApi { + id: number; + title: string; + thumbnail_image: any; + scraps: IScrap[]; +} + // NOTE: 서버에서 take가 없을 때 최대로 export const getBooksApi = async (data: GetBooksApi) => { @@ -45,3 +53,10 @@ export const addBookApi = async (data: { title: string }) => { return response.data; }; +export const editBookApi = async (data: EditBookApi) => { + const url = `/api/books`; + + const response = await api({ url, method: 'PATCH', data }); + + return response.data; +}; diff --git a/frontend/components/common/Book/index.tsx b/frontend/components/common/Book/index.tsx index b1847e8e..13acff6d 100644 --- a/frontend/components/common/Book/index.tsx +++ b/frontend/components/common/Book/index.tsx @@ -3,7 +3,7 @@ import Image from 'next/image'; import InactiveBookmarkIcon from '@assets/ico_bookmark_black.svg'; import ActiveBookmarkIcon from '@assets/ico_bookmark_grey_filled.svg'; import MoreContentsIcon from '@assets/ico_more_contents.svg'; -import SampleThumbnail from '@assets/img_sample_thumbnail.jpg'; +import sampleImage from '@assets/img_sample_thumbnail.jpg'; import useBookmark from '@hooks/useBookmark'; import { IBookScraps } from '@interfaces'; import { TextLarge, TextXSmall, TextSmall } from '@styles/common'; @@ -28,17 +28,21 @@ interface BookProps { } export default function Book({ book, handleEditBookModalOpen }: BookProps) { - const { id, title, user, scraps, _count, bookmarks } = book; + const { id, title, user, scraps, thumbnail_image, _count, bookmarks } = book; const { handleBookmarkClick, curBookmarkCnt, curBookmarkId } = useBookmark( bookmarks.length ? bookmarks[0].id : null, _count.bookmarks, id ); - return ( // 수정모드일때만 아래 onclick이 실행되도록 수정해야함 -> 민형님 작업 후 - + diff --git a/frontend/components/study/EditBook/index.tsx b/frontend/components/study/EditBook/index.tsx index c2e5fdbd..e5ca1fc8 100644 --- a/frontend/components/study/EditBook/index.tsx +++ b/frontend/components/study/EditBook/index.tsx @@ -2,6 +2,7 @@ import Image from 'next/image'; import { useEffect, useRef, useState } from 'react'; +import { editBookApi } from '@apis/bookApi'; import { createImageApi } from '@apis/imageApi'; import Edit from '@assets/ico_edit.svg'; import MoreContentsIcon from '@assets/ico_more_contents.svg'; @@ -33,9 +34,10 @@ interface BookProps { } export default function EditBook({ book }: BookProps) { - const { id, title, user, scraps } = book; + const { id, title, user, scraps, thumbnail_image } = book; const { value: titleData, onChange: onTitleChange } = useInput(title); const { data: imgFile, execute: createImage } = useFetch(createImageApi); + const { data: editBookData, execute: editBook } = useFetch(editBookApi); const [scrapList, setScrapList] = useState(scraps); const inputFile = useRef(null); @@ -59,15 +61,27 @@ export default function EditBook({ book }: BookProps) { const handleCompletedBtnClick = () => { console.log(id); console.log(titleData); - console.log('click', imgFile); + console.log('click', imgFile.imagePath); + console.log(thumbnail_image); console.log(scrapList); + editBook({ + id, + title: titleData, + thumbnail_image: imgFile.imagePath || thumbnail_image, + scraps: scrapList, + }); }; return ( - + profile_edit Date: Wed, 30 Nov 2022 16:48:58 +0900 Subject: [PATCH 12/76] =?UTF-8?q?feat:=20=EB=B6=81=EB=A7=88=ED=81=AC?= =?UTF-8?q?=ED=95=9C=20=EA=B8=80=20wehre=20=EC=A1=B0=EA=B1=B4=20=EC=98=B5?= =?UTF-8?q?=EC=85=98=20=EC=B6=94=EA=B0=80=20-=20#126?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/src/apis/books/books.controller.ts | 4 ++-- backend/src/apis/books/books.interface.ts | 1 + backend/src/apis/books/books.service.ts | 28 ++++++++++++++++------ 3 files changed, 24 insertions(+), 9 deletions(-) diff --git a/backend/src/apis/books/books.controller.ts b/backend/src/apis/books/books.controller.ts index d7e2eba4..3745008a 100644 --- a/backend/src/apis/books/books.controller.ts +++ b/backend/src/apis/books/books.controller.ts @@ -16,13 +16,13 @@ const getBook = async (req: Request, res: Response) => { }; const getBooks = async (req: Request, res: Response) => { - const { order, take, editor } = req.query as unknown as FindBooks; + const { order, take, editor, type } = req.query as unknown as FindBooks; let userId = res.locals.user?.id; if (!userId) userId = 0; - const books = await booksService.findBooks({ order, take: +take, userId, editor }); + const books = await booksService.findBooks({ order, take: +take, userId, editor, type }); res.status(200).send(books); }; diff --git a/backend/src/apis/books/books.interface.ts b/backend/src/apis/books/books.interface.ts index adbeee2c..6d58090a 100644 --- a/backend/src/apis/books/books.interface.ts +++ b/backend/src/apis/books/books.interface.ts @@ -10,6 +10,7 @@ export interface FindBooks { take: number; userId?: number; editor?: string; + type?: 'bookmark'; } export interface CreateBook { diff --git a/backend/src/apis/books/books.service.ts b/backend/src/apis/books/books.service.ts index 7d2ea597..ad1a8325 100644 --- a/backend/src/apis/books/books.service.ts +++ b/backend/src/apis/books/books.service.ts @@ -43,9 +43,8 @@ const findBook = async (bookId: number, userId: number) => { return book; }; -const findBooks = async ({ order, take, userId, editor }: FindBooks) => { +const findBooks = async ({ order, take, userId, editor, type }: FindBooks) => { const sortOptions = []; - if (order === 'bookmark') sortOptions.push({ bookmarks: { _count: 'desc' as const } }); if (order === 'newest') sortOptions.push({ created_at: 'desc' as const }); @@ -82,11 +81,26 @@ const findBooks = async ({ order, take, userId, editor }: FindBooks) => { }, where: { deleted_at: null, - user: { - is: { - nickname: editor ? editor : undefined, - }, - }, + user: + type === 'bookmark' + ? {} + : { + is: { + nickname: editor ? editor : undefined, + }, + }, + bookmarks: + type === 'bookmark' + ? { + some: { + user: { + is: { + nickname: editor ? editor : undefined, + }, + }, + }, + } + : {}, }, orderBy: sortOptions, take, From 718abe557cc27282dd18737999afb06058eb72b3 Mon Sep 17 00:00:00 2001 From: MinHK4 Date: Wed, 30 Nov 2022 17:02:27 +0900 Subject: [PATCH 13/76] =?UTF-8?q?feat:=20=EC=84=9C=EC=9E=AC=20=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=20=EC=97=AE=EC=9D=80,=20=EB=B6=81=EB=A7=88?= =?UTF-8?q?=ED=81=AC=ED=95=9C=20=EC=B1=85=20=EC=9A=94=EC=B2=AD=20-=20#125?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/apis/bookApi.ts | 9 ++++++++ .../components/study/BookListTab/index.tsx | 21 ++++++++----------- frontend/pages/study/[...data].tsx | 13 +++++++++++- 3 files changed, 30 insertions(+), 13 deletions(-) diff --git a/frontend/apis/bookApi.ts b/frontend/apis/bookApi.ts index 58465a99..3cdd4cc1 100644 --- a/frontend/apis/bookApi.ts +++ b/frontend/apis/bookApi.ts @@ -57,6 +57,15 @@ export const getUserKnottedBooksApi = async (nickname: string) => { return response.data; }; + +export const getUserBookmarkedBooksApi = async (nickname: string) => { + const url = `/api/books?editor=${nickname}&type=bookmark&take=12`; + + const response = await api({ url, method: 'GET' }); + + return response.data; +}; + export const addBookApi = async (data: { title: string }) => { const url = `/api/books`; diff --git a/frontend/components/study/BookListTab/index.tsx b/frontend/components/study/BookListTab/index.tsx index e676015d..260df46e 100644 --- a/frontend/components/study/BookListTab/index.tsx +++ b/frontend/components/study/BookListTab/index.tsx @@ -1,6 +1,5 @@ import React, { useEffect, useState } from 'react'; -import { getOrderedBookListApi } from '@apis/bookApi'; import Book from '@components/common/Book'; import Modal from '@components/common/Modal'; import useFetch from '@hooks/useFetch'; @@ -9,20 +8,18 @@ import { IBookScraps } from '@interfaces'; import EditBook from '../EditBook'; import { BookGrid, BookListTabWrapper, TabTitle, TabTitleContent } from './styled'; -export default function BookListTab() { +interface BookListTabProps { + knottedBookList: IBookScraps[]; + bookmarkedBookList: IBookScraps[]; +} + +export default function BookListTab({ knottedBookList, bookmarkedBookList }: BookListTabProps) { // 일단 에러 안 뜨게 새로 엮은 책 보여주기 const [isModalShown, setModalShown] = useState(false); const [curEditBook, setCurEditBook] = useState(null); - const { data: newestBookList, execute: getNewestBookList } = - useFetch(getOrderedBookListApi); - - useEffect(() => { - getNewestBookList('newest'); - }, []); - const handleEditBookModalOpen = (id: number) => { - const curbook = newestBookList?.find((v) => v.id === id); + const curbook = knottedBookList?.find((v) => v.id === id); if (!curbook) return; setModalShown(true); setCurEditBook(curbook); @@ -38,8 +35,8 @@ export default function BookListTab() { 북마크한 책 - {newestBookList && - newestBookList.map((book) => ( + {knottedBookList && + knottedBookList.map((book) => ( (null); const [isEditing, setIsEditing] = useState(false); @@ -34,6 +40,8 @@ export default function Study() { const nickname = router.query.data; getUserProfile(nickname); + getKnottedBookList(nickname); + getBookmarkedBookList(nickname); }, [router.query.data]); useEffect(() => { @@ -75,7 +83,10 @@ export default function Study() { }} /> )} - + From e25494ece7bac047d4c92e4d1ad5e6ee120d7fc9 Mon Sep 17 00:00:00 2001 From: MinHK4 Date: Wed, 30 Nov 2022 17:04:48 +0900 Subject: [PATCH 14/76] =?UTF-8?q?chore:=20=EA=B0=80=EB=8F=85=EC=84=B1=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20-=20#125?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/components/study/EditUserProfile/index.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/frontend/components/study/EditUserProfile/index.tsx b/frontend/components/study/EditUserProfile/index.tsx index 5b6f5475..533984e5 100644 --- a/frontend/components/study/EditUserProfile/index.tsx +++ b/frontend/components/study/EditUserProfile/index.tsx @@ -33,10 +33,12 @@ export default function EditUserProfile({ handleEditFinishBtnClick, }: EditUserProfileProps) { const { data: imgFile, execute: createImage } = useFetch(createImageApi); + const { value: nicknameValue, onChange: onNicknameChange } = useInput(curUserProfile.nickname); const { value: descriptionValue, onChange: onDescriptionChange } = useInput( curUserProfile.description ); + const inputFile = useRef(null); const handleEditThumbnailClick = () => { From 8dacbc805dc615d746f2c623b009c8f11c651894 Mon Sep 17 00:00:00 2001 From: dahyeon405 Date: Wed, 30 Nov 2022 17:08:41 +0900 Subject: [PATCH 15/76] =?UTF-8?q?fix:=20=EA=B2=80=EC=83=89=20=EA=B4=80?= =?UTF-8?q?=EB=A0=A8=20=EB=B2=84=EA=B7=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/src/apis/articles/articles.service.ts | 2 +- frontend/pages/search.tsx | 45 +++++++++++-------- 2 files changed, 28 insertions(+), 19 deletions(-) diff --git a/backend/src/apis/articles/articles.service.ts b/backend/src/apis/articles/articles.service.ts index 0138b69a..fb9f1d0d 100644 --- a/backend/src/apis/articles/articles.service.ts +++ b/backend/src/apis/articles/articles.service.ts @@ -11,7 +11,7 @@ const searchArticles = async (searchArticles: SearchArticles) => { const take = 10; const skip = (page - 1) * take; - const matchUserCondition = userId + const matchUserCondition = Number(userId) ? { book: { user: { diff --git a/frontend/pages/search.tsx b/frontend/pages/search.tsx index 2d12b783..0e9621f1 100644 --- a/frontend/pages/search.tsx +++ b/frontend/pages/search.tsx @@ -36,36 +36,37 @@ export default function Search() { useEffect(() => { if (!debouncedKeyword) return; - if (filter.type === 'article') { - setArticles([]); - searchArticles({ query: debouncedKeyword, userId: filter.userId, page: 1 }); - setArticlePage({ - hasNextPage: true, - pageNumber: 2, - }); - } else if (filter.type === 'book') { - setBooks([]); - searchBooks({ query: debouncedKeyword, userId: filter.userId, page: 1 }); - setBookPage({ - hasNextPage: true, - pageNumber: 2, - }); - } + setArticles([]); + searchArticles({ query: debouncedKeyword, userId: filter.userId, page: 1 }); + setArticlePage({ + hasNextPage: true, + pageNumber: 2, + }); + setBooks([]); + searchBooks({ query: debouncedKeyword, userId: filter.userId, page: 1 }); + setBookPage({ + hasNextPage: true, + pageNumber: 2, + }); }, [debouncedKeyword, filter.userId]); useEffect(() => { - if (!isIntersecting) return; + if (!isIntersecting || !debouncedKeyword) return; if (filter.type === 'article') { if (!articlePage.hasNextPage) return; - searchArticles({ query: debouncedKeyword, userId: filter.userId, page: articlePage }); + searchArticles({ + query: debouncedKeyword, + userId: filter.userId, + page: articlePage.pageNumber, + }); setArticlePage({ ...articlePage, pageNumber: articlePage.pageNumber + 1, }); } else if (filter.type === 'book') { if (!bookPage.hasNextPage) return; - searchBooks({ query: debouncedKeyword, userId: filter.userId, page: bookPage }); + searchBooks({ query: debouncedKeyword, userId: filter.userId, page: bookPage.pageNumber }); setBookPage({ ...bookPage, pageNumber: bookPage.pageNumber + 1, @@ -76,11 +77,19 @@ export default function Search() { useEffect(() => { if (!newArticles) return; setArticles(articles.concat(newArticles.data)); + setArticlePage({ + ...articlePage, + hasNextPage: newArticles.hasNextPage, + }); }, [newArticles]); useEffect(() => { if (!newBooks) return; setBooks(books.concat(newBooks.data)); + setBookPage({ + ...bookPage, + hasNextPage: newBooks.hasNextPage, + }); }, [newBooks]); const handleFilter = (value: { [value: string]: string | number }) => { From 75e49a76ab0cd7ce174afc5f63007f5d2ea9a00d Mon Sep 17 00:00:00 2001 From: MinHK4 Date: Wed, 30 Nov 2022 17:32:31 +0900 Subject: [PATCH 16/76] =?UTF-8?q?feat:=20=EB=B6=81=20=EB=A6=AC=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=ED=83=AD=20=EA=B5=AC=ED=98=84=20-=20#123?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/study/BookListTab/index.tsx | 62 ++++++++++++++----- .../components/study/BookListTab/styled.ts | 6 +- 2 files changed, 51 insertions(+), 17 deletions(-) diff --git a/frontend/components/study/BookListTab/index.tsx b/frontend/components/study/BookListTab/index.tsx index 260df46e..82f10a48 100644 --- a/frontend/components/study/BookListTab/index.tsx +++ b/frontend/components/study/BookListTab/index.tsx @@ -1,8 +1,7 @@ -import React, { useEffect, useState } from 'react'; +import React, { useState } from 'react'; import Book from '@components/common/Book'; import Modal from '@components/common/Modal'; -import useFetch from '@hooks/useFetch'; import { IBookScraps } from '@interfaces'; import EditBook from '../EditBook'; @@ -17,6 +16,7 @@ export default function BookListTab({ knottedBookList, bookmarkedBookList }: Boo // 일단 에러 안 뜨게 새로 엮은 책 보여주기 const [isModalShown, setModalShown] = useState(false); const [curEditBook, setCurEditBook] = useState(null); + const [tabStatus, setTabStatus] = useState<'knotted' | 'bookmarked'>('knotted'); const handleEditBookModalOpen = (id: number) => { const curbook = knottedBookList?.find((v) => v.id === id); @@ -31,21 +31,51 @@ export default function BookListTab({ knottedBookList, bookmarkedBookList }: Boo return ( - 엮은 책 - 북마크한 책 + { + setTabStatus('knotted'); + }} + isActive={tabStatus === 'knotted'} + > + 엮은 책 + + { + setTabStatus('bookmarked'); + }} + isActive={tabStatus === 'bookmarked'} + > + 북마크한 책 + - - {knottedBookList && - knottedBookList.map((book) => ( - { - handleEditBookModalOpen(book.id); - }} - /> - ))} - + {tabStatus === 'knotted' ? ( + + {knottedBookList && + knottedBookList.map((book) => ( + { + handleEditBookModalOpen(book.id); + }} + /> + ))} + + ) : ( + + {bookmarkedBookList && + bookmarkedBookList.map((book) => ( + { + handleEditBookModalOpen(book.id); + }} + /> + ))} + + )} + {isModalShown && ( {curEditBook && } diff --git a/frontend/components/study/BookListTab/styled.ts b/frontend/components/study/BookListTab/styled.ts index e09dd1fb..683e0e9d 100644 --- a/frontend/components/study/BookListTab/styled.ts +++ b/frontend/components/study/BookListTab/styled.ts @@ -11,9 +11,13 @@ export const TabTitle = styled.div` padding: 10px; gap: 30px; `; -export const TabTitleContent = styled(TextLinkMedium)` +export const TabTitleContent = styled(TextLinkMedium)<{ isActive: boolean }>` cursor: pointer; + font-size: 18px; + line-height: 24px; + ${(props) => (props.isActive ? 'color: var(--primary-color); text-decoration:underline' : '')} `; + export const BookGrid = styled.div` display: grid; grid-template-columns: repeat(4, 1fr); From 78f52b0674d1f53a77c2a24966a30a3daa960f22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=8F=84=ED=98=84?= Date: Wed, 30 Nov 2022 17:43:30 +0900 Subject: [PATCH 17/76] =?UTF-8?q?chore:=20=EB=8D=B0=EC=9D=B4=ED=84=B0?= =?UTF-8?q?=EB=B2=A0=EC=9D=B4=EC=8A=A4=20Full=20Text=20Search=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/prisma/schema.prisma | 2 ++ 1 file changed, 2 insertions(+) diff --git a/backend/prisma/schema.prisma b/backend/prisma/schema.prisma index ecb53465..a72b32c1 100644 --- a/backend/prisma/schema.prisma +++ b/backend/prisma/schema.prisma @@ -51,6 +51,8 @@ model Article { book_id Int scraps Scrap[] + @@fulltext([content]) + @@fulltext([title]) @@fulltext([content, title]) } From 651ff2a79ef87c35fce272e43bea4425fa27c43c Mon Sep 17 00:00:00 2001 From: dahyeon405 Date: Wed, 30 Nov 2022 17:53:46 +0900 Subject: [PATCH 18/76] =?UTF-8?q?feat:=20=EA=B2=80=EC=83=89=20=EA=B2=B0?= =?UTF-8?q?=EA=B3=BC=20=EC=B1=85=20=ED=91=9C=EC=8B=9C=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=20=EC=B6=94=EA=B0=80=20-=20#166?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/components/search/BookList/index.tsx | 21 +++++++++++++++++-- frontend/components/search/BookList/styled.ts | 15 +++++++++++++ 2 files changed, 34 insertions(+), 2 deletions(-) create mode 100644 frontend/components/search/BookList/styled.ts diff --git a/frontend/components/search/BookList/index.tsx b/frontend/components/search/BookList/index.tsx index c640766c..e8e03578 100644 --- a/frontend/components/search/BookList/index.tsx +++ b/frontend/components/search/BookList/index.tsx @@ -1,3 +1,20 @@ -export default function BookList() { - return <>Book Result; +import Book from '@components/common/Book'; +import { IBookScraps } from '@interfaces'; + +import { BookContainer, BookListWrapper } from './styled'; + +interface BookListProps { + books: IBookScraps[]; +} + +export default function BookList({ books }: BookListProps) { + return ( + + {books.map((book) => ( + + + + ))} + + ); } diff --git a/frontend/components/search/BookList/styled.ts b/frontend/components/search/BookList/styled.ts new file mode 100644 index 00000000..391cf947 --- /dev/null +++ b/frontend/components/search/BookList/styled.ts @@ -0,0 +1,15 @@ +import styled from 'styled-components'; + +export const BookContainer = styled.div` + width: 280px; +`; + +export const BookListWrapper = styled.div` + width: 100%; + + display: grid; + grid-template-columns: repeat(3, 1fr); + grid-row-gap: 30px; + + margin-bottom: 30px; +`; From 41a0f5764248a8597e6b8001556d31a84ca383e5 Mon Sep 17 00:00:00 2001 From: dahyeon405 Date: Wed, 30 Nov 2022 17:56:01 +0900 Subject: [PATCH 19/76] =?UTF-8?q?fix:=20searchBooks=20userId=20=EA=B4=80?= =?UTF-8?q?=EB=A0=A8=20=EB=B2=84=EA=B7=B8=20=EC=88=98=EC=A0=95=20-=20#120?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/src/apis/books/books.service.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/src/apis/books/books.service.ts b/backend/src/apis/books/books.service.ts index 43ff79a3..fbd17319 100644 --- a/backend/src/apis/books/books.service.ts +++ b/backend/src/apis/books/books.service.ts @@ -124,7 +124,7 @@ const searchBooks = async ({ query, userId, page }: SearchBooks) => { }, bookmarks: { where: { - user_id: userId ? Number(userId) : 0, + user_id: Number(userId) ? Number(userId) : 0, }, }, _count: { @@ -133,7 +133,7 @@ const searchBooks = async ({ query, userId, page }: SearchBooks) => { }, where: { deleted_at: null, - user_id: userId ? Number(userId) : undefined, + user_id: Number(userId) ? Number(userId) : undefined, title: { search: `${query}*`, }, From aa05fdf6a49254471bbffa65edb2822d8123f49d Mon Sep 17 00:00:00 2001 From: dahyeon405 Date: Wed, 30 Nov 2022 17:58:02 +0900 Subject: [PATCH 20/76] =?UTF-8?q?chore:=20Searchpage=20bookList=20?= =?UTF-8?q?=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=20props=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20-=20#166?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/pages/search.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/pages/search.tsx b/frontend/pages/search.tsx index 0e9621f1..6d1cb25b 100644 --- a/frontend/pages/search.tsx +++ b/frontend/pages/search.tsx @@ -107,7 +107,7 @@ export default function Search() { {articles?.length > 0 && filter.type === 'article' && } - {books?.length > 0 && filter.type === 'book' && } + {books?.length > 0 && filter.type === 'book' && }
From 9321ecf7560341bc31ad2249febbe03860013fcd Mon Sep 17 00:00:00 2001 From: MinHK4 Date: Wed, 30 Nov 2022 18:01:28 +0900 Subject: [PATCH 21/76] =?UTF-8?q?chore:=20FAB=20=EC=9C=84=EC=B9=98=20?= =?UTF-8?q?=EC=A1=B0=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/components/study/FAB/styled.ts | 4 ++-- frontend/pages/study/[...data].tsx | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/frontend/components/study/FAB/styled.ts b/frontend/components/study/FAB/styled.ts index ea8d6250..ea64c159 100644 --- a/frontend/components/study/FAB/styled.ts +++ b/frontend/components/study/FAB/styled.ts @@ -5,8 +5,8 @@ export const FabWrapper = styled.div` display: flex; justify-content: space-between; bottom: 60px; - right: 330px; - width: 110px; + right: 200px; + gap: 10px; `; export const FabButton = styled.button<{ isGreen?: boolean }>` diff --git a/frontend/pages/study/[...data].tsx b/frontend/pages/study/[...data].tsx index fce9a6ed..21371654 100644 --- a/frontend/pages/study/[...data].tsx +++ b/frontend/pages/study/[...data].tsx @@ -87,8 +87,8 @@ export default function Study() { knottedBookList={knottedBookList} bookmarkedBookList={bookmarkedBookList} /> + {signInStatus.id === curUserProfile.id && } - )} From ef8591838760d9831674b4f8c13adb753b01c304 Mon Sep 17 00:00:00 2001 From: parkhyeonki Date: Wed, 30 Nov 2022 18:09:52 +0900 Subject: [PATCH 22/76] =?UTF-8?q?chore:=20=EB=B2=84=EA=B7=B8=EC=88=98?= =?UTF-8?q?=EC=A0=95=20=EC=99=84=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/.eslintrc.json | 3 +- .../components/study/BookListTab/index.tsx | 1 + frontend/components/study/EditBook/index.tsx | 88 ++++++++++--------- frontend/components/study/EditBook/styled.ts | 4 +- frontend/interfaces/book.interface.ts | 1 + 5 files changed, 52 insertions(+), 45 deletions(-) diff --git a/frontend/.eslintrc.json b/frontend/.eslintrc.json index 96f0c8e7..8106d2a7 100644 --- a/frontend/.eslintrc.json +++ b/frontend/.eslintrc.json @@ -51,7 +51,8 @@ "react/react-in-jsx-scope": "off", "react/jsx-filename-extension": ["error", { "extensions": [".ts", ".tsx"] }], "react/jsx-props-no-spreading": "off", - "react-hooks/exhaustive-deps": "off" + "react-hooks/exhaustive-deps": "off", + "camelcase":"off" }, "settings": { "import/parsers": { diff --git a/frontend/components/study/BookListTab/index.tsx b/frontend/components/study/BookListTab/index.tsx index e676015d..06961b3b 100644 --- a/frontend/components/study/BookListTab/index.tsx +++ b/frontend/components/study/BookListTab/index.tsx @@ -26,6 +26,7 @@ export default function BookListTab() { if (!curbook) return; setModalShown(true); setCurEditBook(curbook); + console.log(curbook); }; const handleModalClose = () => { setModalShown(false); diff --git a/frontend/components/study/EditBook/index.tsx b/frontend/components/study/EditBook/index.tsx index e5ca1fc8..50396c37 100644 --- a/frontend/components/study/EditBook/index.tsx +++ b/frontend/components/study/EditBook/index.tsx @@ -6,12 +6,11 @@ import { editBookApi } from '@apis/bookApi'; import { createImageApi } from '@apis/imageApi'; import Edit from '@assets/ico_edit.svg'; import MoreContentsIcon from '@assets/ico_more_contents.svg'; -import SampleThumbnail from '@assets/img_sample_thumbnail.jpg'; import Button from '@components/common/Modal/ModalButton'; import useFetch from '@hooks/useFetch'; import useInput from '@hooks/useInput'; import { IBookScraps } from '@interfaces'; -import { FlexCenter, FlexSpaceBetween } from '@styles/layout'; +import { FlexSpaceBetween } from '@styles/layout'; import { BookWrapper, @@ -27,6 +26,7 @@ import { EditBookWapper, EditBookThumbnailWrapper, EditBookThumbnailIcon, + MoreContentsIconWrapper, } from './styled'; interface BookProps { @@ -38,7 +38,9 @@ export default function EditBook({ book }: BookProps) { const { value: titleData, onChange: onTitleChange } = useInput(title); const { data: imgFile, execute: createImage } = useFetch(createImageApi); const { data: editBookData, execute: editBook } = useFetch(editBookApi); - const [scrapList, setScrapList] = useState(scraps); + // const [scrapList, setScrapList] = useState(scraps); + + const [isContentsShown, setIsContentsShown] = useState(false); const inputFile = useRef(null); @@ -59,65 +61,65 @@ export default function EditBook({ book }: BookProps) { }; const handleCompletedBtnClick = () => { - console.log(id); - console.log(titleData); - console.log('click', imgFile.imagePath); - console.log(thumbnail_image); - console.log(scrapList); editBook({ id, title: titleData, thumbnail_image: imgFile.imagePath || thumbnail_image, - scraps: scrapList, + scraps, }); }; + const handleContentsOnClick = () => { + setIsContentsShown((prev) => !prev); + }; + return ( - - - - profile_edit - + - - + + profile_edit + + + + )} - - - - by {user.nickname} - - + {isContentsShown ? null : ( + + + + by {user.nickname} + + + )} Contents - {scraps.map( - (scrap, idx) => - idx < 4 && ( -
- {idx + 1}. {scrap.article.title} -
- ) - )} + {scraps.map((scrap, idx) => ( +
+ {idx + 1}. {scrap.article.title} +
+ ))}
- - More Contents Icon -
+ + More Contents Icon +