diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 0000000..70c937f --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,36 @@ +name: AWS_CICD + +on: + push: + branches: [feat/#105] + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - name: Checkout source code + uses: actions/checkout@v2 + + - name: Install dependencies + run: yarn install + + # 빌드 + - name: Build + run: yarn build + + # 배포 + - name: S3 Deploy + run: aws s3 sync ./build s3://<생성한 bucket 이름>/ --acl bucket-owner-full-control + env: + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + AWS_REGION: ${{ secrets.AWS_REGION }} + + # 기존 캐시 무효화 + - name: Invalidate CloudFront Cache + uses: chetan/invalidate-cloudfront-action@master + env: + AWS_DISTRIBUTION: ${{ secrets.AWS_DISTRIBUTION_ID }} + PATHS: "/index.html" + continue-on-error: true diff --git a/package.json b/package.json index fc1adc5..4c6bc80 100644 --- a/package.json +++ b/package.json @@ -48,7 +48,7 @@ "react-dnd": "^16.0.1", "react-dnd-html5-backend": "^16.0.1", "react-dom": "^18.2.0", - "react-icons": "^4.8.0", + "react-icons": "^5.3.0", "react-loading-skeleton": "^3.4.0", "react-multi-carousel": "^2.8.4", "react-query": "^3.39.3", diff --git a/src/api/config.ts b/src/api/config.ts index 4476cef..e438c80 100644 --- a/src/api/config.ts +++ b/src/api/config.ts @@ -1,7 +1,5 @@ import axios from 'axios' - -const BASE_URL = 'http://localhost:8081/api/v1' -// const BASE_URL = 'http://223.130.159.53:8081/api/v1' +const BASE_URL = import.meta.env.VITE_REACT_APP_API_KEY export const baseInstance = axios.create({ withCredentials: true, baseURL: BASE_URL, // 기본 URL 설정 diff --git a/src/components/Header/LoginHeader.tsx b/src/components/Header/LoginHeader.tsx index 05fa518..383e062 100644 --- a/src/components/Header/LoginHeader.tsx +++ b/src/components/Header/LoginHeader.tsx @@ -1,4 +1,4 @@ -import React, { FC } from 'react' +import { FC } from 'react' import logo from '../../../public/logo.png' import { useNavigate } from 'react-router-dom' diff --git a/src/components/Layout.tsx b/src/components/Layout.tsx index e64027a..f75b5b5 100644 --- a/src/components/Layout.tsx +++ b/src/components/Layout.tsx @@ -4,7 +4,7 @@ const Layout = () => { return (
- + {}} />
diff --git a/src/components/Modal/Modal.tsx b/src/components/Modal/Modal.tsx index 362aef6..c5261fe 100644 --- a/src/components/Modal/Modal.tsx +++ b/src/components/Modal/Modal.tsx @@ -1,4 +1,4 @@ -import React, { ReactNode, useState } from 'react' +import { ReactNode, useState } from 'react' import ReactDOM from 'react-dom' import { useMyContext } from '../Context/MyContext' import { baseInstance } from '../../api/config' @@ -25,7 +25,7 @@ const Modal = ({ onClose, children }: ReadingModalProps) => { setIsActive, } = useMyContext() const access = localStorage.getItem('accessToken') - const [congrats, setCongrats] = useState(false) + const [_, setCongrats] = useState(false) const onHandlePages = async () => { const requestData = { diff --git a/src/components/Modal/ModalAtom/Bar.tsx b/src/components/Modal/ModalAtom/Bar.tsx index f225ea0..5286173 100644 --- a/src/components/Modal/ModalAtom/Bar.tsx +++ b/src/components/Modal/ModalAtom/Bar.tsx @@ -1,9 +1,15 @@ import { useEffect, useState } from 'react' -import ReturnIcon from '../../../assets/svgs/reset.svg?react' +import { BsArrowCounterclockwise } from 'react-icons/bs' import { baseInstance } from '../../../api/config' import { useMyContext } from '../../Context/MyContext' -const Bar = ({ book }) => { +type Book = { + id: number + title: string + pages: number +} + +const Bar = ({ book }: { book: Book }) => { const [initialPercentages, setInitialPercentages] = useState(0) const [lastPage, setLastPage] = useState(0) // const [newLastPage, setNewLastPage] = useState(0) @@ -28,10 +34,13 @@ const Bar = ({ book }) => { const response = await baseInstance.get(`/readings/percentages?bid=${book.id}`, { headers: { Authorization: `Bearer ${access}` }, }) - + if (response.data.data.percentage >= 100) { + setPercentages(100) + } else { + setPercentages(response.data.data.percentage) // 읽은 퍼센트 + } setLastPage(response.data.data.lastPage) // 읽은 페이지 localStorage.setItem('lastPage', response.data.data.lastPage) - setPercentages(response.data.data.percentage) // 읽은 퍼센트 setInitialPercentages(response.data.data.percentage) //초기 퍼센트 저장 localStorage.setItem('percentage', response.data.data.percentage) setNewLastPage(response.data.data.lastPage) // 초기 페이지 설정 @@ -64,11 +73,11 @@ const Bar = ({ book }) => { return (
-
+

독서량

{isEditing && ( )}
diff --git a/src/components/Modal/ModalAtom/StarRating.tsx b/src/components/Modal/ModalAtom/StarRating.tsx index 22e7ef4..b1694ed 100644 --- a/src/components/Modal/ModalAtom/StarRating.tsx +++ b/src/components/Modal/ModalAtom/StarRating.tsx @@ -1,5 +1,6 @@ import { useEffect, useState } from 'react' -import StarIcon from '../../../assets/svgs/star.svg?react' +import { TiStarFullOutline } from 'react-icons/ti' + import { useMyContext } from '../../Context/MyContext' import { baseInstance } from '../../../api/config' const StarRating = () => { @@ -41,11 +42,13 @@ const StarRating = () => {
{[1, 2, 3, 4, 5].map((star) => ( - starHandle(star)}> + size={24} + color={star <= (grading === 0 ? rating : grading) ? '#f6ce0b' : '#e4e4e4'} // 색상 설정 + className="cursor-pointer" + onClick={() => starHandle(star)} + /> ))}
diff --git a/src/components/Modal/ModalAtom/StateBtn.tsx b/src/components/Modal/ModalAtom/StateBtn.tsx index d20bc7b..c77ea7a 100644 --- a/src/components/Modal/ModalAtom/StateBtn.tsx +++ b/src/components/Modal/ModalAtom/StateBtn.tsx @@ -1,11 +1,12 @@ -import { useState } from 'react' -import FinishIcon from '../../../assets/svgs/finish.svg?react' -import BookIcon from '../../../assets/svgs/book.svg?react' import { useMyContext } from '../../Context/MyContext' +import { RiBook3Line } from 'react-icons/ri' +import { LuFlagTriangleRight } from 'react-icons/lu' -const StateBtn = (modalHandle: () => {}) => { + + +const StateBtn = () => { const { - newLastPage, + // newLastPage, setNewLastPage, selectedBook, setIsEditing, @@ -28,7 +29,7 @@ const StateBtn = (modalHandle: () => {}) => { className={`flex items-center justify-center w-[7rem] h-[2.785rem] px-4 py-2 rounded ${ isActive ? 'bg-toggle' : 'bg-inactive' }`}> - + 도서 중 @@ -41,7 +42,7 @@ const StateBtn = (modalHandle: () => {}) => { className={`flex w-[7rem] h-[2.785rem] items-center justify-center px-4 py-2 rounded ${ !isActive ? 'bg-toggle' : 'bg-inactive' }`}> - + 완독! diff --git a/src/components/Tab/Pannels/ReadBooks.tsx b/src/components/Tab/Pannels/ReadBooks.tsx index 7f7566e..b7625e6 100644 --- a/src/components/Tab/Pannels/ReadBooks.tsx +++ b/src/components/Tab/Pannels/ReadBooks.tsx @@ -4,6 +4,9 @@ import { useMyContext } from '../../Context/MyContext' import { baseInstance } from '../../../api/config' import { useEffect, useState } from 'react' import useInfiniteScroll from '../../../hooks/useInfiniteScroll' +import Lottie from 'lottie-react' +import Empty from '../../../assets/lotties/Animation - 1724653336505.json' +import { useNavigate } from 'react-router-dom' type BookType = { cover_image_url: string @@ -18,6 +21,7 @@ const ReadBook = () => { const [hasMore, setHasMore] = useState(true) const [isLoading, setIsLoading] = useState(true) const { isModalOpen, setIsModalOpen, setSelectedBook } = useMyContext() + const navigate = useNavigate() const handleBookClick = (book: any) => { setSelectedBook({ ...book, status: 'Read' }) @@ -29,12 +33,9 @@ const ReadBook = () => { setIsLoading(true) const access = localStorage.getItem('accessToken') - const response = await baseInstance.get( - `/readings?status=READ&page=${page}`, - { - headers: { Authorization: `Bearer ${access}` }, - }, - ) + const response = await baseInstance.get(`/readings?status=READ&page=${page}`, { + headers: { Authorization: `Bearer ${access}` }, + }) const readBooks = response.data.bookInfos.content if (response.data.bookInfos.empty) { @@ -60,39 +61,58 @@ const ReadBook = () => { const setTarget = useInfiniteScroll({ hasMore, onLoadMore: loadMore }) + const goBookSearch = () => navigate('/booksearch') + return (
-
- {isLoading && books.length === 0 - ? Array.from({ length: 12 }).map((_, index) => ( + {books.length === 0 ? ( +
+ +

완독한 내역이 없습니다

+ +
+ ) : ( +
+ {isLoading && books.length === 0 + ? Array.from({ length: 12 }).map((_, index) => ( +
+ )) + : books.map((book, index) => ( + handleBookClick(book)}> + {book.grade === 0 ? ( + + ) : ( + + )} + + ))} + {isLoading && + books.length > 0 && + Array.from({ length: 6 }).map((_, index) => (
- )) - : books.map((book, index) => ( - handleBookClick(book)}> - {book.grade === 0 ? ( - - ) : ( - - )} - ))} - {isLoading && - books.length > 0 && - Array.from({ length: 6 }).map((_, index) => ( -
- ))} -
+
+ )}
) diff --git a/src/components/Tab/Pannels/ReadingBooks.tsx b/src/components/Tab/Pannels/ReadingBooks.tsx index dd1e998..a0cf65f 100644 --- a/src/components/Tab/Pannels/ReadingBooks.tsx +++ b/src/components/Tab/Pannels/ReadingBooks.tsx @@ -4,6 +4,9 @@ import { useMyContext } from '../../Context/MyContext' import { baseInstance } from '../../../api/config' import { useEffect, useState } from 'react' import useInfiniteScroll from '../../../hooks/useInfiniteScroll' +import Lottie from 'lottie-react' +import Empty from '../../../assets/lotties/Animation - 1724653336505.json' +import { useNavigate } from 'react-router-dom' type BookType = { cover_image_url: string @@ -20,6 +23,7 @@ const ReadingBook = () => { const [page, setPage] = useState(1) const [hasMore, setHasMore] = useState(true) const [isLoading, setIsLoading] = useState(true) + const navigate = useNavigate() const getReadingBooks = async () => { try { @@ -57,11 +61,27 @@ const ReadingBook = () => { setSelectedBook({ ...book, status: 'Reading' }) setIsModalOpen(true) } + const goBookSearch = () => navigate('/booksearch') return ( <> {books.length === 0 && !isLoading ? ( -
읽고 있는 책이 없습니다.
+
+ +

독서 중인 책이 없습니다

+ +
) : (
@@ -102,4 +122,4 @@ const ReadingBook = () => { ) } -export default ReadingBook \ No newline at end of file +export default ReadingBook diff --git a/src/components/Tab/Pannels/WishList.tsx b/src/components/Tab/Pannels/WishList.tsx index 1610f5e..3f36142 100644 --- a/src/components/Tab/Pannels/WishList.tsx +++ b/src/components/Tab/Pannels/WishList.tsx @@ -1,5 +1,5 @@ import BookBox from '../../shelf/BookBox' -import HeartIcon from '../../../assets/svgs/heart.svg?react' +import { GoHeartFill } from 'react-icons/go' import { useEffect, useState } from 'react' import { baseInstance } from '../../../api/config' import { useMyContext } from '../../Context/MyContext' @@ -21,8 +21,8 @@ const WishList = () => { const [page, setPage] = useState(1) const [hasMore, setHasMore] = useState(true) const access = localStorage.getItem('accessToken') - const { selectedBook, setSelectedBook } = useMyContext() - const [isLoading, setIsLoading] = useState(true) + const { setSelectedBook } = useMyContext() + const [isLoading, _] = useState(true) const navigate = useNavigate() const Toast = Swal.mixin({ @@ -109,7 +109,7 @@ const WishList = () => {
) : ( @@ -127,10 +127,11 @@ const WishList = () => { img={book.cover_image_url} title={book.title} writer={book.author}> -
- + toggleWhishList(book)} + size={20} + style={{ color: book.isLiked ? '#4ebb00' : 'gray' }} />
diff --git a/src/components/Tab/Tab.tsx b/src/components/Tab/Tab.tsx index e83e676..694ba1f 100644 --- a/src/components/Tab/Tab.tsx +++ b/src/components/Tab/Tab.tsx @@ -8,7 +8,6 @@ interface TabProps { const Tab = ({ label, id }: TabProps) => { const { activeTab, setActiveTab } = useMyContext() const tabStyle = activeTab === id ? 'font-bold' : '' - const active = localStorage.getItem('activeTab') return (
void text: string - + disabled?: boolean // disabled 속성 추가 } -const StartNavigator: React.FC = ({ onClick, text }) => ( +const StartNavigator: React.FC = ({ onClick, text, disabled = false }) => (