diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..7ce93e5 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "cSpell.words": ["myworry", "prscr"] +} diff --git a/public/icon/art_icon.png b/public/icon/art_icon.png new file mode 100644 index 0000000..e6d0d9f Binary files /dev/null and b/public/icon/art_icon.png differ diff --git a/public/icon/general_icon.png b/public/icon/general_icon.png new file mode 100644 index 0000000..51554de Binary files /dev/null and b/public/icon/general_icon.png differ diff --git a/public/icon/history_icon.png b/public/icon/history_icon.png new file mode 100644 index 0000000..6154a68 Binary files /dev/null and b/public/icon/history_icon.png differ diff --git a/public/icon/language_icon.png b/public/icon/language_icon.png new file mode 100644 index 0000000..2a9ed9e Binary files /dev/null and b/public/icon/language_icon.png differ diff --git a/public/icon/literature_icon.png b/public/icon/literature_icon.png new file mode 100644 index 0000000..de6a16c Binary files /dev/null and b/public/icon/literature_icon.png differ diff --git a/public/icon/pharmacy_icon.png b/public/icon/pharmacy_icon.png new file mode 100644 index 0000000..0a92547 Binary files /dev/null and b/public/icon/pharmacy_icon.png differ diff --git a/public/icon/pharmacy_icon_2.png b/public/icon/pharmacy_icon_2.png new file mode 100644 index 0000000..8f6cfbd Binary files /dev/null and b/public/icon/pharmacy_icon_2.png differ diff --git a/public/icon/philosophy_icon.png b/public/icon/philosophy_icon.png new file mode 100644 index 0000000..06e511d Binary files /dev/null and b/public/icon/philosophy_icon.png differ diff --git a/public/icon/religion_icon.png b/public/icon/religion_icon.png new file mode 100644 index 0000000..4f46b58 Binary files /dev/null and b/public/icon/religion_icon.png differ diff --git a/public/icon/science_icon.png b/public/icon/science_icon.png new file mode 100644 index 0000000..5e438d3 Binary files /dev/null and b/public/icon/science_icon.png differ diff --git a/public/icon/search_icon.png b/public/icon/search_icon.png new file mode 100644 index 0000000..9096da5 Binary files /dev/null and b/public/icon/search_icon.png differ diff --git a/public/icon/search_icon_light.png b/public/icon/search_icon_light.png new file mode 100644 index 0000000..61d8e81 Binary files /dev/null and b/public/icon/search_icon_light.png differ diff --git a/public/icon/social_icon.png b/public/icon/social_icon.png new file mode 100644 index 0000000..409c9e0 Binary files /dev/null and b/public/icon/social_icon.png differ diff --git a/public/icon/tech_icon.png b/public/icon/tech_icon.png new file mode 100644 index 0000000..655cebd Binary files /dev/null and b/public/icon/tech_icon.png differ diff --git a/public/index.html b/public/index.html index 0da0dcf..dc27a27 100644 --- a/public/index.html +++ b/public/index.html @@ -2,14 +2,14 @@ - + - + - React App + 책국 diff --git a/public/loading_thumbnail_x4.png b/public/loading_thumbnail_x4.png new file mode 100644 index 0000000..47b235e Binary files /dev/null and b/public/loading_thumbnail_x4.png differ diff --git a/public/logo_2x.png b/public/logo_2x.png new file mode 100644 index 0000000..4bf8ab5 Binary files /dev/null and b/public/logo_2x.png differ diff --git a/public/manifest.json b/public/manifest.json index 080d6c7..7c86642 100644 --- a/public/manifest.json +++ b/public/manifest.json @@ -1,25 +1,25 @@ { - "short_name": "React App", - "name": "Create React App Sample", - "icons": [ - { - "src": "favicon.ico", - "sizes": "64x64 32x32 24x24 16x16", - "type": "image/x-icon" - }, - { - "src": "logo192.png", - "type": "image/png", - "sizes": "192x192" - }, - { - "src": "logo512.png", - "type": "image/png", - "sizes": "512x512" - } - ], - "start_url": ".", - "display": "standalone", - "theme_color": "#000000", - "background_color": "#ffffff" + "short_name": "React App", + "name": "Create React App Sample", + "icons": [ + { + "src": "favicon.ico", + "sizes": "64x64 32x32 24x24 16x16", + "type": "image/x-icon" + }, + { + "src": "logo192.png", + "type": "image/png", + "sizes": "192x192" + }, + { + "src": "logo512.png", + "type": "image/png", + "sizes": "512x512" + } + ], + "start_url": ".", + "display": "standalone", + "theme_color": "#000000", + "background_color": "#ffffff" } diff --git a/src/App.js b/src/App.js index c342c2a..f7d9436 100644 --- a/src/App.js +++ b/src/App.js @@ -13,7 +13,6 @@ import Login from './pages/Login'; import Join from './pages/Join'; import Signup2 from './pages/Signup2'; import Signup3 from './pages/Signup3'; -import FindUserInfo from './pages/FindUserInfo'; import Registration from './pages/Registration'; import BookList from './pages/BookList'; import SmallCategory from './pages/SmallCategory'; @@ -22,6 +21,11 @@ import UserInfo from './pages/UserInfo'; // 처방전 관련 페이지들 import Counseling from './pages/Counseling'; +import MyWorry from './pages/MyWorry'; +import WorryDetail from './pages/WorryDetail'; +import PrescriptionWrite from './pages/PrescriptionWrite'; +import PrescriptionWriteStep2 from './pages/PrescriptionWriteStep2'; +import PrescriptionDetail from './pages/Prescription/PrescriptionDetail'; // STYLE import GlobalStyles from './styles/GlobalStyles'; @@ -54,15 +58,32 @@ function App() { } /> } /> } /> + + {/* 검색 */} } /> } /> + + {/* 마이페이지 */} } /> } /> } /> } /> + {/* 처방전 관련 */} } /> + } /> + } /> + } + /> + } + /> + } /> + {/* 회원가입 및 로그인 */} }> } /> } /> @@ -72,14 +93,15 @@ function App() { element={} /> + } /> } /> } /> } /> } /> - {/* } /> */} } /> + {/* 책 정보 */} } /> } /> } /> diff --git a/src/assets/loading_test_img.png b/src/assets/loading_test_img.png new file mode 100644 index 0000000..7b45f5f Binary files /dev/null and b/src/assets/loading_test_img.png differ diff --git a/src/assets/loading_thumbnail_x4.png b/src/assets/loading_thumbnail_x4.png new file mode 100644 index 0000000..47b235e Binary files /dev/null and b/src/assets/loading_thumbnail_x4.png differ diff --git a/src/assets/myworry-category-literature.png b/src/assets/myworry-category-literature.png new file mode 100644 index 0000000..6d2e23f Binary files /dev/null and b/src/assets/myworry-category-literature.png differ diff --git a/src/components/BookCard.js b/src/components/BookCard.js index 965897c..01833e2 100644 --- a/src/components/BookCard.js +++ b/src/components/BookCard.js @@ -9,11 +9,15 @@ const BookCard = ({ title, author, img, isbn }) => {
- 썸네일 +
-
{title}
-
{author}
+
+ {title} +
+
+ {author} +
diff --git a/src/components/BookDetailCard.jsx b/src/components/BookDetailCard.jsx index 3e2f88e..00a6962 100644 --- a/src/components/BookDetailCard.jsx +++ b/src/components/BookDetailCard.jsx @@ -1,6 +1,9 @@ import React from 'react'; import { Link } from 'react-router-dom'; +// ASSETS +import loading_thumbnail from '../assets/loading_thumbnail_x4.png'; + //COMPONENTS import HashTag from '../components/HashTag'; @@ -16,6 +19,7 @@ const BookDetailCard = ({ type, }) => { let cardType = ['expModal'].includes(type) ? type : ''; + // let image = imageUrl === ' ' ? console.log(imageUrl) : console.log(imageUrl); const renderCard = (type) => { if (type === 'expModal') { @@ -27,8 +31,8 @@ const BookDetailCard = ({
썸네일
@@ -57,8 +61,8 @@ const BookDetailCard = ({
썸네일
diff --git a/src/components/BookListSlide.js b/src/components/BookListSlide.js index b5babc9..b3cba41 100644 --- a/src/components/BookListSlide.js +++ b/src/components/BookListSlide.js @@ -1,10 +1,10 @@ -import { useState, useEffect } from 'react'; -import Slider from 'react-slick'; +import React from 'react'; import { Swiper, SwiperSlide } from 'swiper/react'; -import axios from 'axios'; + +// ASSETS +import loading_thumbnail from '../assets/loading_thumbnail_x4.png'; // COMPONENTS -import Title from './ArrowTitle'; import BookCard from './BookCard'; // STYLES @@ -14,24 +14,8 @@ import 'slick-carousel/slick/slick-theme.css'; import 'swiper/css'; import 'swiper/css/navigation'; import 'swiper/css/pagination'; -import { set } from 'react-hook-form'; - -const BookListSlide = ({ - list, - bigCategory, - midCategoryTitle, - title, - author, - imageUrl, -}) => { - var settings = { - dots: true, - infinite: false, - speed: 500, - slidesToShow: 1, - slidesToScroll: 1, - }; +const BookListSlide = ({ list }) => { return ( <>
@@ -50,7 +34,9 @@ const BookListSlide = ({ key={item.isbn} title={item.title} author={item.author} - img={item.imageUrl} + img={ + item.imageUrl === '' ? loading_thumbnail : item.imageUrl + } isbn={item.isbn} /> diff --git a/src/components/Header.js b/src/components/Header.js index 63e4b53..7a9763a 100644 --- a/src/components/Header.js +++ b/src/components/Header.js @@ -1,5 +1,8 @@ -import React from 'react'; -import { Link } from 'react-router-dom'; +import React, { useEffect } from 'react'; +import { Link, NavLink, useLocation } from 'react-router-dom'; + +// SERVICE +import api from '../services/api'; // COMPONENTS import Btn from '../components/Button'; @@ -9,12 +12,35 @@ import '../styles/Header.css'; const Header = () => { const logout = () => { - let token = localStorage.getItem('token'); - console.log('logout!'); - localStorage.clear(); + api.get('/logout').then((res) => { + console.log(res.data); + }); // window.location.replace('http://localhost:3000/'); }; + // 현재 url 주소 불러옴 + const location = useLocation(); + + // 해당하는 주소 url일 때, 적용될 스타일 + const activeStyle = { + color: '#000', + fontWeight: 700, + }; + + // 고민상담페이지 -> 처방전 작성까지 주소 + let prscrLocation = [ + '/worry/detail', + '/prescription/write', + '/prescription/write/2', + '/prescription/detail', + ]; + + // 검색페이지 -> 책 정보 상세보기 페이지까지 주소 + let bookLocation = ['/book-detail', '/book/list', '/book']; + + // 마이페이지 -> 내 정보 수정 + let myLocation = ['/edit', '/edit/nickname', '/edit/password', '/edit/job']; + return (
@@ -24,16 +50,63 @@ const Header = () => { @@ -41,6 +114,7 @@ const Header = () => { { logout(); + // window.location.replace('/'); }} text={'로그아웃'} type="logout" diff --git a/src/components/Modal/SearchBook.jsx b/src/components/Modal/SearchBook.jsx new file mode 100644 index 0000000..400f598 --- /dev/null +++ b/src/components/Modal/SearchBook.jsx @@ -0,0 +1,41 @@ +import React, { useState, useEffect } from 'react'; + +// SERVIce +import api from '../../services/api'; + +// COMPONENTS + +// STYLE +import '../../styles/SearchBookModal.css'; + +const SearchBook = ({ onClose, author, isClick, active }) => { + // 검색 결과 선택 여부 + const handleModalIsClick = async () => { + onClose(); + isClick(); + }; + + // 검색 리스트 가져오기 + // const fetchSearchData = async () => { + // if (input.trim() === '') return; + // api.get(`/api/search/book?author=${author}&target=modal`).then((res) => { + // console.log(res.data); + // setSearchData(res.data); + // }); + // }; + + // useEffect(() => { + // fetchSearchData(); + // }, [author]); + + return ( + <> +
+ + ); +}; + +export default SearchBook; diff --git a/src/components/Pagination.jsx b/src/components/Pagination.jsx index 4b25e64..71b5a7a 100644 --- a/src/components/Pagination.jsx +++ b/src/components/Pagination.jsx @@ -2,12 +2,18 @@ import React from "react"; import { Link } from "react-router-dom"; import styled from "styled-components"; -const Pagination = ({ booksPerPage, totalBooks, paginate, bookTitle }) => { +const Pagination = ({ + booksPerPage, + totalBooks, + paginate, + bookTitle, + currentPage, +}) => { const pageNumbers = []; - console.log(totalBooks, booksPerPage); + // console.log(totalBooks, booksPerPage); for (let i = 1; i <= Math.ceil(totalBooks / booksPerPage); i++) { pageNumbers.push(i); - console.log(pageNumbers); + // console.log(pageNumbers); } return ( @@ -15,27 +21,17 @@ const Pagination = ({ booksPerPage, totalBooks, paginate, bookTitle }) => { {pageNumbers.map((number) => { return ( -
  • { + window.scrollTo(0, 0); + paginate(number); }} > - {/* paginate(number)} href={bookTitle} className="page-link"> */} - { - window.scrollTo(0, 0); - paginate(number); - }} - > - {number} - - {/* */} -
  • + {number} + ); })}
    @@ -49,3 +45,22 @@ const PaginationContainer = styled.ul` display: flex; justify-content: center; `; + +const PageItem = styled.li` + text-align: center; + margin-top: 30px; + margin-bottom: 60px; + padding: 4px; + color: ${(props) => + props.isCurrent + ? "#00A2BC" + : "black"}; // 현재 페이지일 때 빨간색, 아니면 검은색 + font-weight: ${(props)=> props.isCurrent ? "bold" : "normal"}; + + &:hover { + cursor: pointer; + background-color: #d5d5d5; + border-radius: 50%; + text-decoration: underline; + } +`; \ No newline at end of file diff --git a/src/components/Pill.jsx b/src/components/Pill.jsx new file mode 100644 index 0000000..513229c --- /dev/null +++ b/src/components/Pill.jsx @@ -0,0 +1,26 @@ +import React from 'react' +import styled from 'styled-components' + +const Pill = ({ text, onClick }) => { + return ( + + {text} × + + ); +} + +export default Pill + +const KeywordPill = styled.span` + height: 30px; + display: flex; + align-items: center; + /* gap: 5px; */ + margin-right: 5px; + color: black; + background-color: #c8edf2; + padding: 5px 10px; + border-radius: 16px; + font-size: 16px; + cursor: pointer; +`; \ No newline at end of file diff --git a/src/components/Prescription/CounselingView.jsx b/src/components/Prescription/CounselingView.jsx index 91a7e6c..597e36a 100644 --- a/src/components/Prescription/CounselingView.jsx +++ b/src/components/Prescription/CounselingView.jsx @@ -19,7 +19,7 @@ const CounselingView = () => {
    새로운 곳에 적응하기 힘들어요
    - +
    diff --git a/src/components/Prescription/PrescriptionCard.jsx b/src/components/Prescription/PrescriptionCard.jsx new file mode 100644 index 0000000..21a9c77 --- /dev/null +++ b/src/components/Prescription/PrescriptionCard.jsx @@ -0,0 +1,60 @@ +import React from 'react'; +import { Link } from 'react-router-dom'; + +// STYLE +import '../../styles/Counseling/PrescriptionCard.css'; + +const PrescriptionCard = () => { + return ( + <> + +
    +
    + +
    +
    + "새로운 곳에 +
    + 적응하기 힘들어요" +
    +
    +
    이름 없는 새
    + 귀하 +
    +
    +
    +
    +
    +
    처방전
    +
    + +
    +
    +

    책 제목

    +

    저자

    +

    출판사/출판연도

    + +
    +
    +
    +
    +
    2000 년 00 월 00일
    +
    +
    + + + ); +}; + +export default PrescriptionCard; diff --git a/src/components/Prescription/ProcessTitle.jsx b/src/components/Prescription/ProcessTitle.jsx new file mode 100644 index 0000000..6d00856 --- /dev/null +++ b/src/components/Prescription/ProcessTitle.jsx @@ -0,0 +1,59 @@ +import React from 'react'; +import { Link } from 'react-router-dom'; + +// STYLES +import '../../styles/Counseling/ProcessTitle.css'; + +const ProcessTitle = ({ type, value }) => { + const renderTitle = (type) => { + if (type === 'normal') { + return ( + <> +
    +
    진단서
    +
    + 이름 없는 새 님의 진단서 +
    +
    + + ); + } else if (type === 'detail') { + return ( + <> +
    +
    처방전
    +
    + 유저1 님이 처방한{' '} + 이름 없는 새 님의 + 진단서 +
    +
    + + ); + } else { + return ( + <> +
    +
    + 이름 없는 새 님을 위한 처방전 + 작성 중... +
    +
    + +

    {value}%

    +
    +
    + + ); + } + }; + + return <>{renderTitle(type)}; +}; + +export default ProcessTitle; diff --git a/src/components/SearchBox.jsx b/src/components/SearchBox.jsx index 6eb091b..a8d0166 100644 --- a/src/components/SearchBox.jsx +++ b/src/components/SearchBox.jsx @@ -1,9 +1,10 @@ -import React, { useState } from 'react' -import SearchResultListModal from './SearchResultListModal'; +import React from "react"; +import SearchResultListModal from "./SearchResultListModal"; // style import "../styles/SearchStyles.css"; -import styled from 'styled-components'; +import styled from "styled-components"; +import Pill from "./Pill"; const SearchBox = ({ input, @@ -14,9 +15,12 @@ const SearchBox = ({ isShow, setIsShow, searchData, + handleSelectKeyword, + selectedKeyword, + selectedKeywordSet, + handleRemoveKeyword, + inputRef, }) => { - - return (
    */} setSearchType(e.target.value)} + onChange={(e) => { + setSearchType(e.target.value); + setInput(""); + }} name="" id="" // className="search-select" > - @@ -50,7 +59,19 @@ const SearchBox = ({ {searchType === "keyword" ? ( <> + {selectedKeyword.map((keyword, index) => { + return ( + { + handleRemoveKeyword(keyword); + }} + /> + ); + })} { e.stopPropagation(); }} @@ -112,7 +134,7 @@ const SearchInputWrap = styled.div` width: 100%; height: 60px; box-shadow: 0px 2px 4px #00000033; - padding: 10px 0px 10px 1rem; + padding: 10px 0px 10px 16px; border-radius: 5px; border: 1px solid #b0b0b0; display: flex; @@ -122,10 +144,11 @@ const SearchInputWrap = styled.div` `; const SelectMenu = styled.select` - width: 140px; + min-width: 140px; font-size: 20px; border: none; - /* padding-left: 10px; */ + /* border-right: 1px solid #c0c0c0; */ + /* padding-r: 10px; */ text-align: center; &:focus { outline: none; @@ -134,12 +157,13 @@ const SelectMenu = styled.select` const SearchInput = styled.input` border: none; - border-left: 1px solid #c0c0c0; - margin-left: 1rem; + /* border-left: 1px solid #c0c0c0; */ + /* margin-left: 1rem; */ padding-left: 1rem; font-size: 20px; - width: 100%; + /* width: 100%; */ + flex: 1; &:focus { outline: none; } -`; \ No newline at end of file +`; diff --git a/src/components/SearchResultListModal.jsx b/src/components/SearchResultListModal.jsx index c77de4f..e23e509 100644 --- a/src/components/SearchResultListModal.jsx +++ b/src/components/SearchResultListModal.jsx @@ -1,31 +1,35 @@ -import React from 'react' +import React from "react"; import "../styles/SearchResultList.css"; -import { Link } from 'react-router-dom'; +import { Link } from "react-router-dom"; +import styled from "styled-components"; -const SearchResultListModal = ({ book, type, updateBook, addInput }) => { +const SearchResultListModal = ({ + book, + type, + updateBook, + addInput, + selectedKeywordSet, +}) => { let listType = ["myBook"].includes(type) ? type : "search"; - const updateBookTitle = (pickTitle) => { - updateBook(pickTitle); - }; - return ( <>
    {book.map((item) => { - console.log(item); + // console.log(item); // 작가에서 누구 지음 이걸 빼주도록 하기 -> 작가 이름을 어떻게 필터링 처리를 할까? let title = item && item.title; let author = item && item.author; let thumbnail = item && item.imageUrl; let keyword = item && item.name; + let isbn = item && item.isbn; // 책 제목 && 작가 UI if (title !== undefined) { return ( -
      - +
        +
      • @@ -44,23 +48,42 @@ const SearchResultListModal = ({ book, type, updateBook, addInput }) => { // 키워드 UI if (keyword !== undefined) { - return ( -

        addInput(`#${keyword}`)} + onClick={() => addInput(keyword)} > {keyword} -

        + + ) : ( + //

        addInput(keyword)} + // > + // {keyword} + //

        + <> ); } + return null; })}
        ); -} +}; SearchResultListModal.defaultProps = { type: "search", }; -export default SearchResultListModal \ No newline at end of file +export default SearchResultListModal; + +const KeywordItem = styled.p` + font-size: 20px; + margin-bottom: 10px; + &:hover { + background-color: #d6d6d6; + border-radius: 4px; + } +`; diff --git a/src/components/TodayPrescription.jsx b/src/components/TodayPrescription.jsx deleted file mode 100644 index e93a496..0000000 --- a/src/components/TodayPrescription.jsx +++ /dev/null @@ -1,9 +0,0 @@ -import React from 'react' - -function TodayPrescription() { - return ( -
        TodayPrescription
        - ) -} - -export default TodayPrescription \ No newline at end of file diff --git a/src/pages/BookDetail.jsx b/src/pages/BookDetail.jsx index 266b1ba..a9ed967 100644 --- a/src/pages/BookDetail.jsx +++ b/src/pages/BookDetail.jsx @@ -1,20 +1,22 @@ import React, { useState, useRef, useEffect } from 'react'; import { useParams, useSearchParams } from 'react-router-dom'; +// ASSETS +import loading_thumbnail from '../assets/loading_thumbnail_x4.png'; + // SERVICE import api from '../services/api'; // COMPONENTS import Header from '../components/Header'; -import Btn from '../components/Button'; import HashTag from '../components/HashTag'; import Review from '../components/Review'; import Title from '../components/ArrowTitle'; import BookCard from '../components/BookCard'; +import BookListSlide from '../components/BookListSlide'; import Footer from '../components/Footer'; import ModalPortal from '../components/Modal/Portal'; import ExpModal from '../components/Modal/Experience'; -import MyListModal from '../components/Modal/MyListModal'; // STYLES import '../styles/BookDetail.css'; @@ -58,13 +60,14 @@ const BookDetail = () => {
        -
        - {`${bookInfo.title}썸네일`} -
        +
        @@ -134,58 +137,6 @@ const BookDetail = () => {
        책 정보

        {bookInfo.content}

        - {/*

        - 해리 포터 세대의, 해리 포터 세대를 위한, 해리 포터 세대에 의한 - 새 번역!‘ 21세기 대표 아이콘’에 걸맞은 완성도 높은 작품으로 - 재탄생하다! -

        -

        - 1997년 영국에서 출간된 이래 『해리 포터』 시리즈는 지금까지 - 200개국 이상 80개의 언어로 번역되고 출간되어 5억 부 이상을 - 판매했다. 국내에서도 1999년 『해리 포터와 마법사의 돌』의 출간을 - 필두로 지금까지 약 1,500만 부가 판매되었으며, 현재에도 - 독자들에게 변함없는 사랑을 받고 있다. 이 시리즈는 여덟 편의 - 영화로도 제작되어 전 세계 곳곳에서 흥행을 거두었고, 영화와 - 관련된 새로운 도서가 출간되고 테마 파크가 조성되는 등 놀라운 - 기현상을 빚어냈다. -

        -

        - 뿐만 아니라 『해리 포터』 시리즈에서 또 다른 작품들이 - 문화상품으로 파생되어 지금도 꾸준히 독자들을 만나고 있다. ‘해리 - 포터’의 다음 세대인 자녀들의 이야기를 다룬 『해리 포터와 저주 - 받은 아이』는 시나리오로 출간된 이후 연극으로 만들어져 영국을 - 시작으로 미국, 호주, 독일, 캐나다 등 세계 곳곳에서 열띤 호응을 - 얻으며 공연 중이고, 『해리 포터』의 세계관이 확장된 『신비한 - 동물 사전』 시리즈는 계속해서 영화로 제작되고 있다. 이제 『해리 - 포터』는 소설이라는 단순한 문학 장르에 머무르지 않고 ‘21세기를 - 대표하는 시대의 아이콘’으로 불리며 일종의 사회문화 현상으로 - 받아들여지고 있다. -

        -

        - 20주년을 맞아 새롭게 출간한 『해리 포터』 시리즈는 ‘21세기 - 고전’이라 불릴 만한 품격에 맞춰 작품의 완성도를 높였다. 7권 - 『해리 포터와 죽음의 성물』로 완간된 기존의 『해리 포터』 - 시리즈는 빈틈없는 소설적 구성과 생생한 캐릭터 그리고 마법 세계를 - 정교하게 묘사하며 풍부한 상상력이 돋보이면서도 정밀한 세계관을 - 구축해 나갔다. 하지만 지금까지 출간된 책들은 J.K. 롤링이 펼쳐 - 나가는 판타지 세계의 규모가 어느 정도이며 그 속에 어떠한 소설적 - 장치를 심어 놓았는지 알 수 없는 상태에서 번역 작업이 이루어졌다. - 또한 1~7편 모두 완결성을 갖추었지만, 시리즈의 특성상 편과 편을 - 이어 주며 작품 전체를 관통하는 서사의 개연성과 완결성은 마지막 - 편이 출간된 이후에나 파악할 수밖에 없었다. 그러다 보니 작가가 - 어느 장면에 복선을 깔아 두었고, 어느 장면이 작가가 창조한 - 세계관을 이해하는 중요한 역할을 하는지 의미를 파악하며 - 번역하기에는 한계가 있었다. -

        -

        - 이번에 선보이는 『해리 포터』 시리즈에는 J.K. 롤링이 작품 속에 - 이룩해놓은 문학적 성취가 완벽하게 구현되어 있다. 복선과 반전을 - 선사하는 문학적 장치들을 보다 정교하고 세련되게 다듬었으며, - 인물들 사이의 관계나 그들의 숨겨진 비밀 그리고 성격이 도드라지는 - 말투의 미세한 뉘앙스까지 점검했다. 『해리 포터』의 세계에 처음 - 발을 들여놓는 독자는 물론, 그동안 『해리 포터』의 세계를 즐겨 - 찾아왔던 독자 모두에게 완성도 높은 만족과 감동을 선사할 것이다. -

        */}
        @@ -235,14 +186,46 @@ const BookDetail = () => { > <div className="BookList_container"> - <BookCard title={'책 제목 1'} author={'저자'} /> - <BookCard title={'책 제목 2'} author={'저자'} /> - <BookCard title={'책 제목 3'} author={'저자'} /> - <BookCard title={'책 제목 4'} author={'저자'} /> - <BookCard title={'책 제목 5'} author={'저자'} /> - <BookCard title={'책 제목 6'} author={'저자'} /> - <BookCard title={'책 제목 7'} author={'저자'} /> - <BookCard title={'책 제목 8'} author={'저자'} /> + <BookCard + title={'책 제목 1'} + author={'저자'} + img={loading_thumbnail} + /> + <BookCard + title={'책 제목 2'} + author={'저자'} + img={loading_thumbnail} + /> + <BookCard + title={'책 제목 3'} + author={'저자'} + img={loading_thumbnail} + /> + <BookCard + title={'책 제목 4'} + author={'저자'} + img={loading_thumbnail} + /> + <BookCard + title={'책 제목 5'} + author={'저자'} + img={loading_thumbnail} + /> + <BookCard + title={'책 제목 6'} + author={'저자'} + img={loading_thumbnail} + /> + <BookCard + title={'책 제목 7'} + author={'저자'} + img={loading_thumbnail} + /> + <BookCard + title={'책 제목 8'} + author={'저자'} + img={loading_thumbnail} + /> </div> </div> </section> diff --git a/src/pages/Counseling.jsx b/src/pages/Counseling.jsx index ff8003e..e82ebb6 100644 --- a/src/pages/Counseling.jsx +++ b/src/pages/Counseling.jsx @@ -1,14 +1,37 @@ -import React from 'react'; +import React, { useEffect } from 'react'; // COMPONENTS import Header from '../components/Header'; -import Footer from '../components/Footer'; import CnsFeed from '../components/Prescription/CounselingView'; // STYLES -import '../styles/Counseling.css'; +import '../styles/Counseling/Counseling.css'; const Counseling = () => { + // useEffect(() => { + // const observer = new IntersectionObserver( + // (items) => { + // items.forEach((item) => { + // if (item.isIntersecting) { + // console.log('visible!'); + // item.target.classList.add('visible'); + // } else { + // console.log('no'); + // item.target.classList.remove('visible'); + // } + // }); + // }, + // { threshold: 0.5 }, + // ); + + // // const target = document.getElementById('cn_target'); + // const items = document.querySelectorAll('.cnsFeed_card_wrapper'); + // items.forEach((item) => { + // console.log(item); + // observer.observe(item); + // }); + // }, []); + return ( <> <Header /> @@ -16,19 +39,126 @@ const Counseling = () => { <div className="counseling_category_wrapper"> <div className="cns_category_title">분야 선택</div> <div className="cns_category_content_wrapper"> - <div>1</div> - <div>2</div> - <div>3</div> - <div>4</div> - <div>5</div> + <div className="cns_category"> + <img + src="/icon/art_icon.png" + alt="예술" + className="cns_category_img" + /> + <span className="cns_category_text">예술</span> + </div> + <div className="cns_category"> + <img + src="/icon/history_icon.png" + alt="역사" + className="cns_category_img" + /> + <span className="cns_category_text">역사</span> + </div> + <div className="cns_category"> + <img + src="/icon/philosophy_icon.png" + alt="철학" + className="cns_category_img" + /> + <span className="cns_category_text">철학</span> + </div> + <div className="cns_category"> + <img + src="/icon/social_icon.png" + alt="사회과학" + className="cns_category_img" + /> + <span className="cns_category_text">사회과학</span> + </div> + <div className="cns_category"> + <img + src="/icon/tech_icon.png" + alt="기술과학" + className="cns_category_img" + /> + <span className="cns_category_text">기술과학</span> + </div> + <div className="cns_category"> + <img + src="/icon/science_icon.png" + alt="자연과학" + className="cns_category_img" + /> + <span className="cns_category_text">자연과학</span> + </div> + <div className="cns_category"> + <img + src="/icon/religion_icon.png" + alt="역사" + className="cns_category_img" + /> + <span className="cns_category_text">역사</span> + </div> + <div className="cns_category"> + <img + src="/icon/general_icon.png" + alt="총류" + className="cns_category_img" + /> + <span className="cns_category_text">총류</span> + </div> + <div className="cns_category"> + <img + src="/icon/language_icon.png" + alt="언어" + className="cns_category_img" + /> + <span className="cns_category_text">언어</span> + </div> + <div className="cns_category"> + <img + src="/icon/literature_icon.png" + alt="문학" + className="cns_category_img" + /> + <span className="cns_category_text">문학</span> + </div> </div> </div> <div className="counseling_feed_wrapper"> - <div className="view_feed">안녕</div> - <CnsFeed /> + <div id="cnsFeed_card_wrapper"> + <CnsFeed /> + </div> + <div className="cnsFeed_card_wrapper"> + <CnsFeed /> + </div> + <div className="cnsFeed_card_wrapper"> + <CnsFeed /> + </div> + <div className="cnsFeed_card_wrapper"> + <CnsFeed /> + </div> + <div className="cnsFeed_card_wrapper"> + <CnsFeed /> + </div> + <div className="cnsFeed_card_wrapper"> + <CnsFeed /> + </div> + <div className="cnsFeed_card_wrapper"> + <CnsFeed /> + </div> + <div className="cnsFeed_card_wrapper"> + <CnsFeed /> + </div> + <div className="cnsFeed_card_wrapper"> + <CnsFeed /> + </div> + + <div className="cnsFeed_card_wrapper"> + <CnsFeed /> + </div> + <div className="cnsFeed_card_wrapper"> + <CnsFeed /> + </div> </div> + <div id="cn_target"></div> </div> - <Footer /> </> ); }; diff --git a/src/pages/Feed.jsx b/src/pages/Feed.jsx index f194a65..b28263d 100644 --- a/src/pages/Feed.jsx +++ b/src/pages/Feed.jsx @@ -6,8 +6,8 @@ import api from '../services/api'; // COMPONENTS import Header from '../components/Header'; -import FeedGrid from '../components/FeedGrid'; import FeedCard from '../components/FeedCard'; +import PrescriptionCard from '../components/Prescription/PrescriptionCard'; // STYLES import '../styles/Feed.css'; @@ -56,10 +56,11 @@ const Feed = () => { // setFeedArr(res.data.content); // setFeedArr((prevData) => [...prevData, ...res.data.content]); // }); - const API_URL = - 'https://port-0-backend-book-pharmacy-umnqdut2blqqhv7sd.sel5.cloudtype.app/api/feeds/all?page=0&size=9'; - const response = await axios.get(API_URL, feedFetchData); + const response = await api.get( + '/api/feeds/all?page=0&size=9', + feedFetchData, + ); // .then((res) => { // // console.log(res.data.content); // setFeedArr(res.data.content); @@ -94,16 +95,18 @@ const Feed = () => { <div className="feed_title">추천 피드</div> </div> <div className="feed_content_wrapper"> + <PrescriptionCard /> {feedArr.map((item, idx) => { return ( - <FeedCard - key={`${idx}-item.id`} - title={item.title} - author={item.author} - comment={item.comment} - imgUrl={item.image} - nickname={item.nickName} - /> + // <FeedCard + // key={`${idx}-item.id`} + // title={item.title} + // author={item.author} + // comment={item.comment} + // imgUrl={item.image} + // nickname={item.nickName} + // /> + <PrescriptionCard /> ); })} </div> diff --git a/src/pages/MyWorry.jsx b/src/pages/MyWorry.jsx new file mode 100644 index 0000000..b238fd7 --- /dev/null +++ b/src/pages/MyWorry.jsx @@ -0,0 +1,148 @@ +import React from "react"; +import Header from "../components/Header"; +import styled from "styled-components"; +import literatureIcon from "../assets/myworry-category-literature.png"; + +const MyWorry = () => { + return ( + <> + <Header /> + <Title /> + <StyledContainer> + <Select /> + <Items /> + </StyledContainer> + </> + ); +}; + +export default MyWorry; + +const Title = () => { + return <StlyedTitle>내가 등록한 증상</StlyedTitle>; +}; + +const Select = () => { + return ( + <> + <StlyedSelect name="symptomStatus" id="symptom-status-select"> + <option value="all">전체보기</option> + <option value="success">처방 성공</option> + <option value="inProgress">처방중</option> + <option value="failed">처방 실패</option> + </StlyedSelect> + </> + ); +}; + +const Items = () => { + const registrationDate = "2000.00.00"; + const prescriptionCount = 10; + + return ( + <> + <StyledUl> + {Array.from({ length: 9 }, (_, index) => { + return ( + <StyledLi key={index}> + <ItemContainer> + <ItemBigCategory> + <ItemCategoryIcon + src={literatureIcon} + alt="카테고리 아이콘" + /> + <div>문학</div> + </ItemBigCategory> + <ItemTitle> + <h2>“새로운 곳에 적응하기 힘들어요”</h2> + </ItemTitle> + </ItemContainer> + + <ItemInfo> + <div>처방전 갯수: {prescriptionCount}개</div> + <div>{registrationDate}</div> + </ItemInfo> + </StyledLi> + ); + })} + </StyledUl> + </> + ); +}; + +const StlyedTitle = styled.nav` + height: 55px; + font-size: 24px; + text-align: center; + line-height: 55px; + font-weight: bold; + box-shadow: 0px 2px 4px #00000040; +`; + +const StyledContainer = styled.main` + max-width: 1114px; + padding: 90px 0px; + margin: 0 auto; +`; + +const StlyedSelect = styled.select` + border: none; + width: 140px; + height: 30px; + font-weight: bold; + font-size: 24px; + outline: none; + margin-bottom: 50px; +`; + +const StyledUl = styled.ul` + display: grid; + grid-template-columns: repeat(3, 1fr); + flex-wrap: wrap; + gap: 30px; +`; + +const StyledLi = styled.li` + width: 350px; + height: 300px; + border-radius: 10px; + box-shadow: 10px 10px 26px #00000040; +`; + +const ItemContainer = styled.div` +border-top-left-radius:10px; +border-top-right-radius:10px; + height: 260px; + background: transparent linear-gradient(180deg, #a4c4dd 0%, #dce9ec 100%) 0% + 0% no-repeat padding-box; + padding: 15px 20px 0px 20px; +`; + +const ItemBigCategory = styled.div` + font-size: 18px; + display: flex; + align-items: center; +`; + +const ItemCategoryIcon = styled.img` + margin-right: 10px; +`; + +const ItemTitle = styled.div` + display: flex; + align-items: center; + justify-content: center; + height: 200px; + font-size: 20px; + font-weight: bold; +`; + +const ItemInfo = styled.div` + display: flex; + justify-content: space-between; + align-items: center; + padding: 0px 8px; + height: 40px; + color: #868686; +`; + diff --git a/src/pages/Prescription/PrescriptionDetail.jsx b/src/pages/Prescription/PrescriptionDetail.jsx new file mode 100644 index 0000000..de902ff --- /dev/null +++ b/src/pages/Prescription/PrescriptionDetail.jsx @@ -0,0 +1,60 @@ +import React from 'react'; + +// ASSETS +import loading_thumbnail from '../../assets/loading_thumbnail_x4.png'; + +// COMPONENTS +import Header from '../../components/Header.js'; +import Title from '../../components/Prescription/ProcessTitle.jsx'; +import PrescriptionCard from '../../components/Prescription/PrescriptionCard.jsx'; + +// STYLE +import '../../styles/Prescription/PrescriptionDetail.css'; + +const PrescriptionDetail = () => { + return ( + <> + <Header /> + <Title type={'detail'} /> + <div className="prscr_detail_top_container"> + <div className="prscr_detail_top_info_wrapper"> + <div className="prscr_detail_top_wrapper"> + <div className="dt_prscr_title_wrapper"> + <span id="dt_prscr_title">"새로운 곳에 적응하기 힘들어요"</span> +  에 대한 <span id="dt_from_nickname"> 유저 1</span>님의 + 처방전 + </div> + <div className="prscr_dt_bookInfo_wrapper"> + <div className="prscr_dt_left_wrapper"> + <img src={loading_thumbnail} alt="" id="prscr_dt_loading_img" /> + </div> + <div className="prscr_dt_right_wrapper"> + <p className="prscr_dt_right_title">책 제목</p> + <p className="prscr_dt_right_author">저자</p> + <p className="prscr_dt_right_bookCompany">출판사</p> + </div> + </div> + </div> + </div> + </div> + <div className="prscr_detail_bottom_wrapper"> + <div className="prscr_detail_res_container"> + <div className="prscr_dt_res_title">처방사유</div> + </div> + <div className="prscr_other_container"> + <div className="other_list_title">다른 처방전 확인하기</div> + <div className="other_list_wrapper"> + <PrescriptionCard /> + <PrescriptionCard /> + <PrescriptionCard /> + <PrescriptionCard /> + <PrescriptionCard /> + <PrescriptionCard /> + </div> + </div> + </div> + </> + ); +}; + +export default PrescriptionDetail; diff --git a/src/pages/PrescriptionWrite.jsx b/src/pages/PrescriptionWrite.jsx new file mode 100644 index 0000000..ce44718 --- /dev/null +++ b/src/pages/PrescriptionWrite.jsx @@ -0,0 +1,174 @@ +import React, { useState, useEffect } from 'react'; +import { Link } from 'react-router-dom'; + +// COMPONENTS +import Header from '../components/Header'; +import Title from '../components/Prescription/ProcessTitle'; +import SearchBookModal from '../components/Modal/SearchBook'; + +// ASSETS +import loading_img from '../assets/loading_thumbnail_x4.png'; +import loading_test_img from '../assets/loading_test_img.png'; + +// STYLE +import '../styles/Counseling/PrescriptionWrite.css'; + +const PrescriptionWrite = () => { + const [input, setInput] = useState(''); + const [isShow, setIsShow] = useState(false); // 검색 모달창 + + // 모달창을 클릭한 여부 + const [modalIsClick, setModalIsClick] = useState(false); + let [searchData, setSearchData] = useState(0); + + const handleModalClose = async () => { + setIsShow(false); + }; + + const handleModalShow = async () => { + setIsShow(true); + }; + + const handleModalIsClick = async () => { + setModalIsClick(true); + setSearchData(searchData + 1); + searchData = searchData + 1; + // console.log(searchData); + }; + + useEffect(() => { + const observer = new IntersectionObserver( + (items) => { + items.forEach((item) => { + if (item.isIntersecting) { + // console.log(item.target, 'is visible!'); + if (modalIsClick === true) { + chTarget.classList.add('visible'); + chTarget.classList.remove('no'); + } else { + chTarget.classList.add('visible'); + chTarget.classList.remove('no'); + } + } else { + if (modalIsClick === false) { + // chTarget.classList.add('no'); + // chTarget.classList.remove('visible'); + chTarget.classList.add('visible'); + } else { + chTarget.classList.add('no'); + chTarget.classList.remove('visible'); + } + } + }); + }, + { + threshold: 0.05, + }, + ); + // 특정 dom 요소가 화면에 등장하는 지 여부를 감시함. + const box = document.getElementById('observe_target'); + const chTarget = document.getElementById('prscr_write_box'); + observer.observe(box); + }); + + return ( + <> + <Header /> + <Title type={'process'} value={'30'} /> + <div className="prescription_content_container"> + <section className="prescription_content_up_container"> + <div className="prscr_bookInfo_wrapper"> + <div className="prscr_left_wrapper"> + <img + src={modalIsClick ? loading_test_img : loading_img} + alt="로딩 썸네일" + className="prscr_img_wrapper" + /> + {/* <button type="submit" className="search_res_no_btn"> + 찾는 책이 없어요 + </button> */} + </div> + <div className="prscr_right_wrapper"> + <div className="prscr_searchBar_wrapper"> + <img + src="/icon/search_icon.png" + className="prscr_search_icon" + /> + + <input + type="text" + placeholder="처방할 책을 검색해주세요" + value={input} + className="prscr_search_text" + onChange={(e) => { + setInput(e.target.value); + handleModalShow(); + }} + /> + {input.length > 0 ? ( + <button + className="prscr_search_close_btn" + onClick={() => { + setInput(''); + handleModalClose(); + }} + > + X + </button> + ) : null} + </div> + + {/* 처음에 모달 클릭되었을 때 책 정보 나타나는 에러 방지 */} + {isShow === false && modalIsClick === true + ? input.length > 0 + ? null + : setModalIsClick(false) + : null} + + {isShow && input.length > 0 ? ( + <SearchBookModal + onClose={handleModalClose} + isClick={handleModalIsClick} + author={input} + active={isShow} + /> + ) : ( + <SearchBookModal + onClose={handleModalClose} + isClick={handleModalIsClick} + author={input} + active={isShow} + /> + )} + + {isShow === false && modalIsClick && searchData > 0 ? ( + <div className="prscr_search_res_wrapper"> + <p className="search_res_bookTitle">책 제목</p> + <p className="search_res_bookAuthor">저자</p> + <p className="search_res_bookCompany">출판사</p> + </div> + ) : null} + </div> + </div> + </section> + <section + className="prescription_content_bottom_container" + id="observe_target" + > + <div id="prscr_write_box"> + <p>처방사유</p> + <textarea type="text" placeholder="처방사유를 작성하세요" /> + </div> + </section> + </div> + <div className="prescription_btn_container"> + <button className="prscr_cancel_btn">취소하기</button> + <Link to={'/prescription/write/2'}> + <button className="prscr_apply_btn">처방전 작성하기</button> + </Link> + </div> + </> + ); +}; + +export default PrescriptionWrite; diff --git a/src/pages/PrescriptionWriteStep2.jsx b/src/pages/PrescriptionWriteStep2.jsx new file mode 100644 index 0000000..138e41f --- /dev/null +++ b/src/pages/PrescriptionWriteStep2.jsx @@ -0,0 +1,41 @@ +import React, { useState } from 'react'; +import { Link } from 'react-router-dom'; + +// COMPONENTS +import Header from '../components/Header'; +import Title from '../components/Prescription/ProcessTitle'; + +// ASSETS +import loading_img from '../assets/loading_thumbnail_x4.png'; + +// STYLE +import '../styles/Counseling/PrescriptionWriteStep2.css'; + +const PrescriptionWriteStep2 = () => { + return ( + <> + <Header /> + <Title type={'process'} value={'60'} /> + <div className="prescription_info_container"> + <div className="prscr_left_wrapper"> + <img + src={loading_img} + alt="로딩 썸네일" + className="prscr_img_wrapper" + /> + </div> + </div> + <div className="prescription_mid_wrapper"> + <div className="prscr_write_wrapper"> + <span className="prscr_write_title">처방사유</span> + </div> + </div> + <div className="prescription_btn_container"> + <button className="prscr_cancel_btn">취소하기</button> + <button className="prscr_apply_btn">처방전 등록하기</button> + </div> + </> + ); +}; + +export default PrescriptionWriteStep2; diff --git a/src/pages/Search.jsx b/src/pages/Search.jsx index d1bacbc..fa783f3 100644 --- a/src/pages/Search.jsx +++ b/src/pages/Search.jsx @@ -1,329 +1,316 @@ -import { useContext, useEffect, useState } from "react"; -import { Link, useNavigate } from "react-router-dom"; -import axios from "axios"; +import { useContext, useEffect, useState } from 'react'; +import { Link, useNavigate } from 'react-router-dom'; + +// SERVICE +import api from '../services/api'; // COMPONENTS -import Header from "../components/Header"; -import SearchResultListModal from "../components/SearchResultListModal"; +import Header from '../components/Header'; +import SearchResultListModal from '../components/SearchResultListModal'; // STYLES -import "../styles/SearchStyles.css"; -import bookImg1 from "../assets/category-book-총류.jpg"; -import bookImg2 from "../assets/category-book-철학.jpg"; -import bookImg3 from "../assets/category-book-종교.jpg"; -import bookImg4 from "../assets/category-book-사회과학.jpg"; -import bookImg5 from "../assets/category-book-자연과학.jpg"; -import bookImg6 from "../assets/category-book-기술과학.jpg"; -import bookImg7 from "../assets/category-book-예술.jpg"; -import bookImg8 from "../assets/category-book-언어.jpg"; -import bookImg9 from "../assets/category-book-문학.png"; -import bookImg10 from "../assets/category-book-역사.jpg"; -import { LoginContext } from "../contexts/LoginContextProvider"; -import api from "../services/api"; - +import '../styles/SearchStyles.css'; +import bookImg1 from '../assets/category-book-총류.jpg'; +import bookImg2 from '../assets/category-book-철학.jpg'; +import bookImg3 from '../assets/category-book-종교.jpg'; +import bookImg4 from '../assets/category-book-사회과학.jpg'; +import bookImg5 from '../assets/category-book-자연과학.jpg'; +import bookImg6 from '../assets/category-book-기술과학.jpg'; +import bookImg7 from '../assets/category-book-예술.jpg'; +import bookImg8 from '../assets/category-book-언어.jpg'; +import bookImg9 from '../assets/category-book-문학.png'; +import bookImg10 from '../assets/category-book-역사.jpg'; +import { LoginContext } from '../contexts/LoginContextProvider'; const Search = () => { - const baseURL = "https://api.bookpharmacy.store/api"; - const navigate = useNavigate(); - const [input, setInput] = useState(""); // 검색 데이터 - const [inputKeyword, setInputKeyword] = useState([]); // 키워드 검색 데이터 - const [searchData, setSearchData] = useState([]); // 검색 결과 데이터 - const [categories, setCategories] = useState([]); // 카테고리 데이터 - const [isShow, setIsShow] = useState(false); // 검색창 모달창 - const [searchType, setSearchType] = useState("title"); // 검색 유형 상태 - - const { userId, userPwd } = useContext(LoginContext); - const loginData = { username: userId, password: userPwd }; - - // 카테고리 배경 색상(10개) && 카테고리별 대표 책 이미지 정보 - const categoriesInfo = [ - { color: "#D4F4FF", image: bookImg1 }, - { color: "#FFF2EC", image: bookImg2 }, - { color: "#FFE3B5", image: bookImg3 }, - { color: "#FFF4B6", image: bookImg4 }, - { color: "#D6D6D6", image: bookImg5 }, - { color: "#C2E2FF", image: bookImg6 }, - { color: "#FFCACD", image: bookImg7 }, - { color: "#DFFFF8", image: bookImg8 }, - { color: "#CBD4F0", image: bookImg9 }, - { color: "#D6CABC", image: bookImg10 }, - ]; + const navigate = useNavigate(); + const [input, setInput] = useState(''); // 검색 데이터 + const [inputKeyword, setInputKeyword] = useState([]); // 키워드 검색 데이터 + const [searchData, setSearchData] = useState([]); // 검색 결과 데이터 + const [categories, setCategories] = useState([]); // 카테고리 데이터 + const [isShow, setIsShow] = useState(false); // 검색창 모달창 + const [searchType, setSearchType] = useState('title'); // 검색 유형 상태 + const { userId, userPwd } = useContext(LoginContext); + const loginData = { username: userId, password: userPwd }; + // 카테고리 배경 색상(10개) && 카테고리별 대표 책 이미지 정보 + const categoriesInfo = [ + { color: '#D4F4FF', image: bookImg1 }, + { color: '#FFF2EC', image: bookImg2 }, + { color: '#FFE3B5', image: bookImg3 }, + { color: '#FFF4B6', image: bookImg4 }, + { color: '#D6D6D6', image: bookImg5 }, + { color: '#C2E2FF', image: bookImg6 }, + { color: '#FFCACD', image: bookImg7 }, + { color: '#DFFFF8', image: bookImg8 }, + { color: '#CBD4F0', image: bookImg9 }, + { color: '#D6CABC', image: bookImg10 }, + ]; - // 카테고리 대분류, 중분류 GET 요청 및 요청 데이터 사용하기 쉽게 처리 - useEffect(() => { - let username = localStorage.getItem("id"); - let password = localStorage.getItem("password"); + // 카테고리 대분류, 중분류 GET 요청 및 요청 데이터 사용하기 쉽게 처리 + useEffect(() => { + let username = localStorage.getItem('id'); + let password = localStorage.getItem('password'); - const fetchCategories = async () => { - try { - axios - .post( - "https://api.bookpharmacy.store/login", - { username: username, password: password }, - { withCredentials: true } - ) - .then(async () => { - // console.log('성공'); - axios - .get("https://api.bookpharmacy.store/api/category/big", { - withCredentials: true, - }) - .then((res) => { - setCategories(res.data); - }); - }) - .catch((err) => { - console.log(err); - }); - axios - .get("https://api.bookpharmacy.store/api/category/big", { - withCredentials: true, - }) - .then((res) => { - setCategories(res.data); - }); - } catch (error) { - console.error("Error fetching categories:", error); - } - }; + const fetchCategories = async () => { + try { + api + .post( + '/login', + { username: username, password: password }, + { withCredentials: true }, + ) + .then(async () => { + // console.log('성공'); + api + .get('/api/category/big', { + withCredentials: true, + }) + .then((res) => { + setCategories(res.data); + }); + }) + .catch((err) => { + console.log(err); + }); + api + .get('/api/category/big', { + withCredentials: true, + }) + .then((res) => { + setCategories(res.data); + }); + } catch (error) { + console.error('Error fetching categories:', error); + } + }; - fetchCategories(); - }, []); + fetchCategories(); + }, []); - // 카테고리 아이템을 렌더링 함수 - const renderCategoryItem = ({ title, subtitle, image, color }, index) => ( - <Link to={`/book/list/${title}`} key={index}> - <div className="category-item-wrapper"> - <div - className="category-grid-item" - style={{ - backgroundColor: color, // 여기서 색상 적용 - }} - > - <div className="category-grid-description"> - <h2 className="category-grid-item-title">{title}</h2> - <h3 className="category-grid-item-subtitle">{subtitle}</h3> - </div> - <img - src={image} - alt="카테고리 대표 이미지" - className="category-grid-item-image" - /> - </div> - </div> - </Link> - ); + // 카테고리 아이템을 렌더링 함수 + const renderCategoryItem = ({ title, subtitle, image, color }, index) => ( + <Link to={`/book/list/${title}`} key={index}> + <div className="category-item-wrapper"> + <div + className="category-grid-item" + style={{ + backgroundColor: color, // 여기서 색상 적용 + }} + > + <div className="category-grid-description"> + <h2 className="category-grid-item-title">{title}</h2> + <h3 className="category-grid-item-subtitle">{subtitle}</h3> + </div> + <img + src={image} + alt="카테고리 대표 이미지" + className="category-grid-item-image" + /> + </div> + </div> + </Link> + ); - // 함수로 추천 키워드 리스트를 생성하는 함수 - const renderKeywordList = (title, keywords) => ( - <section className="recommend-word-wrapper"> - <h2 className="recommend-title">{title}</h2> - <ul className="recommend-keyword-wrapper"> - {keywords.map((keyword, index) => ( - <li key={index}> - <Link to={`/result/${keyword}-책목록-페이지`}>{keyword}</Link> - </li> - ))} - </ul> - </section> - ); + // 함수로 추천 키워드 리스트를 생성하는 함수 + const renderKeywordList = (title, keywords) => ( + <section className="recommend-word-wrapper"> + <h2 className="recommend-title">{title}</h2> + <ul className="recommend-keyword-wrapper"> + {keywords.map((keyword, index) => ( + <li key={index}> + <Link to={`/result/${keyword}-책목록-페이지`}>{keyword}</Link> + </li> + ))} + </ul> + </section> + ); - // 추천 검색어 리스트 - const recommendedSearchKeywords = [ - "감정", - "해리포터", - "화장품", - "하늘 높이 비상", - "감정", - // Add more keywords as needed - ]; + // 추천 검색어 리스트 + const recommendedSearchKeywords = [ + '감정', + '해리포터', + '화장품', + '하늘 높이 비상', + '감정', + // Add more keywords as needed + ]; - // 사용자 추천 키워드 리스트 - const userRecommendedKeywords = [ - "#감정", - "#해리포터", - "#화장품", - "#하늘 높이 비상", - // Add more keywords as needed - ]; + // 사용자 추천 키워드 리스트 + const userRecommendedKeywords = [ + '#감정', + '#해리포터', + '#화장품', + '#하늘 높이 비상', + // Add more keywords as needed + ]; - // 검색할 때, 0.1초 딜레이 걸기 -> 끊기는 느낌을 방지 - useEffect(() => { - const timer = setTimeout(() => { - fetchBooks(input); - }, 100); + // 검색할 때, 0.1초 딜레이 걸기 -> 끊기는 느낌을 방지 + useEffect(() => { + const timer = setTimeout(() => { + fetchBooks(input); + }, 100); - // cleanup 함수를 반환하여 컴포넌트가 언마운트될 때 타이머를 해제합니다. - return () => clearTimeout(timer); - }, [input]); + // cleanup 함수를 반환하여 컴포넌트가 언마운트될 때 타이머를 해제합니다. + return () => clearTimeout(timer); + }, [input]); - const fetchBooks = async (searchInput) => { - if (input.trim() === "") return; // 빈 문자열일 때 API 호출 방지 + const fetchBooks = async (searchInput) => { + if (input.trim() === '') return; // 빈 문자열일 때 API 호출 방지 - let endpoint = ""; - if (searchType === "title") { - endpoint = `/api/search/book?title=${searchInput}&target=modal`; - } - if (searchType === "author") { - endpoint = `/api/search/book?author=${searchInput}&target=modal`; - } - if (searchType === "keyword") { - endpoint = `/api/search/keyword?name=${searchInput}&target=modal`; - } + let endpoint = ''; + if (searchType === 'title') { + endpoint = `/api/search/book?title=${searchInput}&target=modal`; + } + if (searchType === 'author') { + endpoint = `/api/search/book?author=${searchInput}&target=modal`; + } + if (searchType === 'keyword') { + endpoint = `/api/search/keyword?name=${searchInput}&target=modal`; + } - try { - const response = await api.get( - endpoint - ); - console.log("test", searchType,response.data); - setSearchData(response.data); - } catch (error) { - console.error("Failed to fetch books:", error); - } - }; + try { + const response = await api.get(endpoint); + // console.log("test", searchType,response.data); + setSearchData(response.data); + } catch (error) { + // console.error("Failed to fetch books:", error); + } + }; - // 검색창 엔터 및 버튼 이벤트 처리 + // 검색창 엔터 및 버튼 이벤트 처리 - const searchBook = (evt) => { - if (evt.key === "Enter") { - fetchBooks(input); - navigate(`/search/result/${input}`) - } - }; + const searchBook = (evt) => { + if (evt.key === 'Enter') { + fetchBooks(input); + navigate(`/search/result/${input}`); + } + }; - const handleSearchResultClose = () => { - setIsShow(false); - }; + const handleSearchResultClose = () => { + setIsShow(false); + }; - const handleSearchResultShow = () => { - setIsShow(true); - }; + const handleSearchResultShow = () => { + setIsShow(true); + }; - const handleSelectChange = (e) => { - setSearchType(e.target.value); - setInput(""); - }; + const handleSelectChange = (e) => { + setSearchType(e.target.value); + setInput(''); + }; - return ( - <div onClick={handleSearchResultClose}> - <Header /> + return ( + <div onClick={handleSearchResultClose}> + <Header /> - {/* 검색 페이지 전체 */} - <section className="search-container"> - {/* 검색 창 */} - <section - className="search-wrapper" - onClick={(e) => { - e.stopPropagation(); - handleSearchResultShow(); - }} - > - <label> - <div className="search-wrap-inner"> - {/* 검색창에 라벨 적용해보기 */} - {/* 책 렌더링했던 유튜브 영상을 활용해서 검색창 누르면 밑에 책보여주는 방법으로 활용하기 */} - {/* <button + {/* 검색 페이지 전체 */} + <section className="search-container"> + {/* 검색 창 */} + <section + className="search-wrapper" + onClick={(e) => { + e.stopPropagation(); + handleSearchResultShow(); + }} + > + <label> + <div className="search-wrap-inner"> + {/* 검색창에 라벨 적용해보기 */} + {/* 책 렌더링했던 유튜브 영상을 활용해서 검색창 누르면 밑에 책보여주는 방법으로 활용하기 */} + {/* <button className="search-button" onClick={searchBook} name="search-button" /> */} - <select - value={searchType} - onChange={handleSelectChange} - name="" - id="" - className="search-select" - > - <option value="title" selected> - 책제목 - </option> - <option value="author">작가</option> - <option value="keyword">키워드</option> - </select> - {searchType === "keyword" ? ( - <> - <input - type="text" - placeholder="검색어를 입력하세요" - className="search-input" - value={input} - onChange={(e) => { - setInput(e.target.value); - }} - onKeyPress={searchBook} - /> - </> - ) : ( - <> - <input - type="text" - placeholder="검색어를 입력하세요" - className="search-input" - value={input} - onChange={(e) => { - setInput(e.target.value); - }} - onKeyPress={searchBook} - /> - {input.length > 0 ? ( - <button - className="search-close-button" - onClick={(e) => { - setInput(""); - }} - > - X - </button> - ) : null} - </> - )} - </div> - </label> - {input.length > 0 && isShow && searchData.length > 0 ? ( - // <SearchResultList - // book={searchData} - // onClick={(e) => { - // e.stopPropagation(); - // }} - // /> - <SearchResultListModal - book={searchData} - addInput={setInput} - onClick={(e) => { - e.stopPropagation(); - }} - /> - ) : null} - </section> + <select + value={searchType} + onChange={handleSelectChange} + name="" + id="" + className="search-select" + > + <option value="title" selected> + 책제목 + </option> + <option value="author">작가</option> + <option value="keyword">키워드</option> + </select> + {searchType === 'keyword' ? ( + <> + <input + type="text" + placeholder="검색어를 입력하세요" + className="search-input" + value={input} + onChange={(e) => { + setInput(e.target.value); + }} + /> + </> + ) : ( + <> + <input + type="text" + placeholder="검색어를 입력하세요" + className="search-input" + value={input} + onChange={(e) => { + setInput(e.target.value); + }} + /> + {input.length > 0 ? ( + <button + className="search-close-button" + onClick={(e) => { + setInput(''); + }} + > + X + </button> + ) : null} + </> + )} + </div> + </label> + {input.length > 0 && isShow && searchData.length > 0 ? ( + <SearchResultListModal + book={searchData} + addInput={setInput} + onClick={(e) => { + e.stopPropagation(); + }} + /> + ) : null} + </section> - {/* 추천 검색어 */} - {renderKeywordList("추천검색어", recommendedSearchKeywords)} + {/* 추천 검색어 */} + {renderKeywordList('추천검색어', recommendedSearchKeywords)} - {/* 사용자 추천 키워드 */} - {renderKeywordList("사용자 추천 키워드", userRecommendedKeywords)} + {/* 사용자 추천 키워드 */} + {renderKeywordList('사용자 추천 키워드', userRecommendedKeywords)} - {/* 카테고리 */} - <section className="category-wrapper"> - <h2 className="recommend-title">카테고리</h2> - <div className="category-items"> - {Object.keys(categories).map((key, index) => { - const title = key; - const subtitle = categories[key].join(", "); - const infoIndex = index % categoriesInfo.length; // 나머지로 0~9만 접근하도록 길이제한 - const color = categoriesInfo[infoIndex].color; - const image = categoriesInfo[infoIndex].image; - return renderCategoryItem( - { title, subtitle, color, image }, - index - ); - })} - </div> - </section> - </section> - </div> - ); + {/* 카테고리 */} + <section className="category-wrapper"> + <h2 className="recommend-title">카테고리</h2> + <div className="category-items"> + {Object.keys(categories).map((key, index) => { + const title = key; + const subtitle = categories[key].join(', '); + const infoIndex = index % categoriesInfo.length; // 나머지로 0~9만 접근하도록 길이제한 + const color = categoriesInfo[infoIndex].color; + const image = categoriesInfo[infoIndex].image; + return renderCategoryItem( + { title, subtitle, color, image }, + index, + ); + })} + </div> + </section> + </section> + </div> + ); }; export default Search; diff --git a/src/pages/SearchResult.jsx b/src/pages/SearchResult.jsx index ca55c50..1b0be57 100644 --- a/src/pages/SearchResult.jsx +++ b/src/pages/SearchResult.jsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from "react"; +import React, { useEffect, useRef, useState } from "react"; import { Link, useNavigate, useParams } from "react-router-dom"; import Header from "../components/Header"; import styled from "styled-components"; @@ -7,6 +7,7 @@ import starIcon from "../assets/icons8-별-30 (1).png"; import api from "./../services/api"; import Pagination from "../components/Pagination"; import SearchBox from "../components/SearchBox"; +import Pill from "../components/Pill"; const SearchResult = () => { const navigate = useNavigate(); @@ -15,12 +16,19 @@ const SearchResult = () => { const [books, setBooks] = useState([]); // 책 정보 const [currentPage, setCurrentPage] = useState(1); // 현재 페이지 const [booksPerPage, setBooksPerPage] = useState(10); // 페이지당 책 수 - const [loading, setLoading] = useState(false); // 로딩 + + const [loading, setLoading] = useState(false); // 로딩 상태 const [input, setInput] = useState(""); // 검색 데이터 const [searchType, setSearchType] = useState("title"); // 검색 유형 상태 const [searchData, setSearchData] = useState([]); // 검색 결과 데이터 const [isShow, setIsShow] = useState(false); // 검색창 모달창 + const inputRef = useRef(null); + + const [selectedKeywords, setSelectedKeywords] = useState([]); + const [selectedKeywordSet, setSelectedKeywordSet] = useState(new Set()); + + const [searchedSelectedKeywords, setSearchedSelectedKeywords] = useState([]); useEffect(() => { const timer = setTimeout(() => { @@ -47,7 +55,7 @@ const SearchResult = () => { try { const response = await api.get(endpoint); - console.log("test", searchType, response.data); + // console.log("test", searchType, response.data); setSearchData(response.data); } catch (error) { console.error("Failed to fetch books:", error); @@ -57,23 +65,36 @@ const SearchResult = () => { const searchBook = (evt) => { if (evt.key === "Enter") { fetchBooks(input); - if (input.length > 0) navigate(`/search/result/${input}`); + if (input.length > 0 && selectedKeywords.length === 0) + navigate(`/search/result/${input}`); + if (selectedKeywords.length > 0) + navigate(`/search/result/${selectedKeywords.join(" ")} ${input}`); + if (input.length === 0 && searchType !== "keyword") + alert("검색 키워드가 없습니다!"); + if (searchType === "keyword" && selectedKeywords.length === 0) + alert("키워드를 선택하여 검색해주세요!"); setIsShow(false); } }; - // 현재 책들 정보 + // 키워드로 책 필터링 + const filteredBooks = books.filter((book) => + searchedSelectedKeywords.every( + (keyword) => + book.bookKeywordList.some( + (bookKeyword) => bookKeyword.name === keyword + ) || keyword === book.middleCategoryName + ) + ); + const indexOfLastBook = currentPage * booksPerPage; const indexOfFirstBook = indexOfLastBook - booksPerPage; - console.log(indexOfFirstBook, indexOfLastBook); - const currentBooks = books.slice(indexOfFirstBook, indexOfLastBook); + // const currentBooks = books.slice(indexOfFirstBook, indexOfLastBook); + const currentBooks = filteredBooks.slice(indexOfFirstBook, indexOfLastBook); // 페이지 변경 const paginate = (pageNumber) => setCurrentPage(pageNumber); - console.log(books.length); - console.log(currentBooks); - useEffect(() => { const getSearchResults = async () => { setLoading(true); @@ -82,7 +103,6 @@ const SearchResult = () => { // `/api/search/book?title=${title}&target=page&page=${currentPage-1}&size=${booksPerPage}` `/api/search/book?title=${title}&target=page&page=0&size=999` ); - console.log(response.data); setBooks(response.data); setLoading(false); } catch (error) { @@ -94,28 +114,50 @@ const SearchResult = () => { let searchResultsCount = books.length; searchResultsCount = searchResultsCount.toLocaleString(); - let searchResultsKeywordCount = 123; let reviewCount = 123; + // 10개, 50개, 100개에 따른 한페이지에 보여주는 책의 수 const handleSizeChange = (event) => { setBooksPerPage(event.target.value); }; + // 키워드 선택 + const handleSelectKeyword = (keyword) => { + setSelectedKeywords([...selectedKeywords, keyword]); + setSelectedKeywordSet(new Set([...selectedKeywordSet, keyword])); + setInput(""); + setSearchData([]); + inputRef.current.focus(); + }; + + // 키워드 삭제 + const handleRemoveKeyword = (keyword) => { + const updatedKeywords = selectedKeywords.filter( + (selectedKeyword) => selectedKeyword !== keyword + ); + setSelectedKeywords(updatedKeywords); + + const updatedKeywordSet = new Set(selectedKeywordSet); + updatedKeywordSet.delete(keyword); + setSelectedKeywordSet(updatedKeywordSet); + }; + + // 검색된 키워드 삭제 + const handleRemoveSearchedKeyword = (keyword) => { + setSearchedSelectedKeywords( + searchedSelectedKeywords.filter((k) => k !== keyword) + ); + }; + + // 아래 키 기능 + + // 요청했던 책에 대한 키워드 + return ( <> <Header /> <Main onClick={() => setIsShow(false)}> {/* 검색창 컴포넌트 만들어야함 */} - {/* <SearchInputWrap> - <SelectMenu> - <option value="title" selected> - 제목 - </option> - <option value="author">저자</option> - <option value="keyword">키워드</option> - </SelectMenu> - <SearchInput type="text" placeholder="검색어를 입력하세요" /> - </SearchInputWrap> */} <SearchBox input={input} setInput={setInput} @@ -125,128 +167,84 @@ const SearchResult = () => { setIsShow={setIsShow} searchBook={searchBook} searchData={searchData} + handleSelectKeyword={handleSelectKeyword} + selectedKeyword={selectedKeywords} + selectedKeywordSet={selectedKeywordSet} + handleRemoveKeyword={handleRemoveKeyword} + inputRef={inputRef} /> - <section id="search-title" style={{ marginBottom: "80px" }}> - <h1 style={{ fontSize: "30px", fontWeight: "bold" }}> - <span style={{ color: "#67B6C1" }}>"{title}"</span> 에 대한 - <span style={{ color: "#67B6C1" }}> - {" "} - {searchResultsCount} 개의 검색 결과 - </span> - </h1> - </section> + <SearchTitle id="search-title"> + <Title> + <Highlight>"{title}"</Highlight> 에 대한 + <Highlight> {searchResultsCount} 개의 검색 결과</Highlight> + +
        {/* 헤더 영역 */} -
        + - 전체{" "} - - {searchResultsKeywordCount}건 - + 전체 {filteredBooks.length}
        - - -
        + - - -
        + +
        -
        + {/* 키워드 영역 */}
        { }} >
          - - {/*
          저주
          */} - 저주 - -
          + {searchedSelectedKeywords.map((keyword, index) => ( + handleRemoveSearchedKeyword(keyword)} + /> + ))}
        @@ -276,11 +268,12 @@ const SearchResult = () => {
        {viewMode ? ( - {/* {books.map((book, index) => ( */} {currentBooks.map((book, index) => ( - +
      • { ) : ( - {/* {books.map((book, index) => ( */} {currentBooks.map((book, index) => (
      • @@ -476,9 +468,10 @@ const SearchResult = () => {
      • @@ -489,12 +482,183 @@ const SearchResult = () => { export default SearchResult; +const KeywordSearchBox = ({ + bookData, + selectedKeywords, + setSelectedKeywords, +}) => { + const [totalBooksOfKeywords, setTotalBooksOfKeywords] = useState([]); // 전체 키워드 + const [isShowModal, setIsShowModal] = useState(false); // 모달 상태 + const [searchTerm, setSearchTerm] = useState(""); + // 모달과 모달을 열기 위한 버튼에 대한 참조 생성 + const modalRef = useRef(null); + const buttonRef = useRef(null); + + useEffect(() => { + setTotalBooksOfKeywords([]); // 키워드 초기화 + const newKeywords = bookData.flatMap((data) => { + const keywords = data.bookKeywordList.map((keyword) => keyword.name); + return [data.middleCategoryName, ...keywords]; + }); + setTotalBooksOfKeywords((prev) => [...prev, ...new Set(newKeywords)]); // 중복 제거 + }, [bookData]); + + useEffect(() => { + const handleClickOutside = (event) => { + // 모달이 열려 있고, 클릭한 요소가 모달 또는 버튼이 아닐 때만 모달을 닫음 + if ( + isShowModal && + modalRef.current && + !modalRef.current.contains(event.target) && + !buttonRef.current.contains(event.target) + ) { + setIsShowModal(false); + } + }; + + // 문서에 클릭 이벤트 리스너 추가 + document.addEventListener("mousedown", handleClickOutside); + return () => { + // 컴포넌트 언마운트 시 이벤트 리스너 제거 + document.removeEventListener("mousedown", handleClickOutside); + }; + }, [isShowModal]); + + const handleShowModal = () => { + setIsShowModal(true); + }; + + const handleSelectKeyword = (keyword) => { + if (!selectedKeywords.includes(keyword)) { + setSelectedKeywords((prevKeywords) => [...prevKeywords, keyword]); + } + }; + + const availableKeywords = totalBooksOfKeywords.filter( + (keyword) => + // 선택된 키워드 포함 x, 입력된 문자 포함 + !selectedKeywords.includes(keyword) && + keyword.toLowerCase().includes(searchTerm.toLowerCase()) + ); + + return ( + <> + setSearchTerm(e.target.value)} + /> + {isShowModal ? ( +
        + +
        + ) : null} + + ); +}; + +const KeywordSearchBoxModal = ({ keywordData, onSelectKeyword }) => { + return ( + <> + +
          + {keywordData.map((keyword, index) => ( +
        • onSelectKeyword(keyword)}> + {keyword} +
        • + ))} +
        +
        + + ); +}; + +const ModalStyled = styled.div` + width: 100%; + background-color: aliceblue; + cursor: pointer; + max-height: 300px; + overflow-y: auto; + border: 1px solid #ccc; + border-radius: 10px; + padding: 10px; + margin-top: 4px; + + li { + padding: 5px; + } +`; + const Main = styled.main` - padding: 48px 52px 0px 52px; + padding-top: 48px; max-width: 1440px; margin: 0 auto; `; +// 검색창 관련 + +// 타이틀 관련 + +// 키워드 검색 관련 + +// 책 결과 관련 +const ContentTitle = styled.h2` + font-size: 20px; + font-weight: bold; + margin-bottom: 20px; +`; + +const Highlight = styled.span` + color: #67b6c1; +`; + +const SelectStyled = styled.select` + width: 130px; + border: 1px solid #c0c0c0; + padding: 0px 10px; + border-radius: 5px; + margin-right: 10px; +`; + +const HeaderArea = styled.div` + width: 100%; + display: flex; + justify-content: space-between; + align-items: center; + padding-bottom: 20px; + border-bottom: 1px solid #c4bebe; +`; + +const ViewModeContainer = styled.div` + display: flex; + width: 75px; + border: 1.5px solid #c0c0c0; + border-radius: 5px; +`; + +const ViewButton = styled.button` + padding: 10px; + width: 100%; + background-color: ${(props) => (props.active ? "#d0d0d0" : "white")}; + border-right: ${(props) => + props.rightBorder ? "1px solid #C0C0C0" : "none"}; +`; + +const SearchTitle = styled.section` + margin-bottom: 80px; +`; + +const Title = styled.h1` + font-size: 30px; + font-weight: bold; +`; + const SearchInputWrap = styled.div` width: 100%; height: 60px; @@ -535,9 +699,11 @@ const ContentsWrap = styled.div` display: flex; `; -const ContentTitle = styled.h2` - font-size: 20px; - font-weight: bold; +const Input = styled.input` + border-radius: 10px; + border: 1px solid #6b6565; + padding: 8px 8px 8px 12px; + font-size: 1rem; `; const SearchKeyword = styled.li` diff --git a/src/pages/SmallCategory.jsx b/src/pages/SmallCategory.jsx index 464322c..9128542 100644 --- a/src/pages/SmallCategory.jsx +++ b/src/pages/SmallCategory.jsx @@ -1,6 +1,11 @@ import React, { useState, useEffect } from 'react'; import { useParams } from 'react-router-dom'; -import axios from 'axios'; + +// ASSETS +import loading_thumbnail from '../assets/loading_thumbnail_x4.png'; + +// SERVICE +import api from '../services/api'; // COMPONENTS import Header from '../components/Header'; @@ -20,10 +25,8 @@ const SmallCategory = () => { setChoiceCategory(category); // 중분류에 해당하는 책 목록 가져오기 - axios - .get( - `https://port-0-backend-book-pharmacy-umnqdut2blqqhv7sd.sel5.cloudtype.app/api/book/list/middle?name=${category}&page=0&size=30&sort=string`, - ) + api + .get(`/api/book/list/middle?name=${category}&page=0&size=30&sort=string`) .then((res) => { setSmCategoryBookList(res.data); }); @@ -49,7 +52,9 @@ const SmallCategory = () => { title={data.title} author={data.author} key={data.isbn} - imageUrl={data.imageUrl} + imageUrl={ + data.imageUrl === '' ? loading_thumbnail : data.imageUrl + } bookKeywordList={data.bookKeywordList} /> ); diff --git a/src/pages/WorryDetail.jsx b/src/pages/WorryDetail.jsx new file mode 100644 index 0000000..4bbec91 --- /dev/null +++ b/src/pages/WorryDetail.jsx @@ -0,0 +1,59 @@ +import React from 'react'; +import { Link } from 'react-router-dom'; + +// COMPONENTS +import Header from '../components/Header'; +import Title from '../components/Prescription/ProcessTitle'; +import PrescriptionCard from '../components/Prescription/PrescriptionCard'; + +// STYLE +import '../styles/Counseling/WorryDetail.css'; + +const WorryDetail = () => { + const move = () => { + window.location.replace('/pre/write'); + }; + + return ( + <> +
        +
        + + <div className="worry_detail_title_wrapper"> + <div className="wd_user_wrapper"> + <div className="wd_user_left_wrapper"> + <div className="wd_user_img"></div> + <div className="wd_user_info_wrapper"> + <div className="wd_user_name">이름없는 새</div> + <div className="wd_user_date">2024.00.00</div> + </div> + </div> + </div> + <div className="wd_title_text_wrapper"> + 새로운 곳에 적응하기 힘들어요! + </div> + </div> + <div className="worry_detail_content_wrapper"> + <div className="wd_content_detail_wrapper"> + <span className="wd_content_detail_title">상세 고민 내용</span> + <div className="wd_content_detail_text">아바바</div> + </div> + <div className="worry_detail_prscr_wrapper"> + <div className="wd_prscr_list_title">처방전 확인하기</div> + <div className="wd_prscr_list_wrapper"> + <PrescriptionCard /> + <PrescriptionCard /> + <PrescriptionCard /> + <PrescriptionCard /> + </div> + </div> + <Link to={`/prescription/write?prscrId=123`}> + <button className="prscr_btn">처방하러 가기</button> + </Link> + </div> + </div> + </> + ); +}; + +export default WorryDetail; diff --git a/src/styles/BookCard.module.css b/src/styles/BookCard.module.css index 4d55c2d..796857e 100644 --- a/src/styles/BookCard.module.css +++ b/src/styles/BookCard.module.css @@ -35,6 +35,7 @@ } .book_title { + width: 100%; margin-top: 16px; color: #000; font-family: 'Pretendard-Regular'; @@ -42,9 +43,14 @@ font-style: normal; font-weight: 700; line-height: normal; + + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; } .book_author { + width: 100%; margin-top: 8px; color: #000; font-family: 'Pretendard-Regular'; @@ -52,4 +58,8 @@ font-style: normal; font-weight: 500; line-height: normal; + + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; } diff --git a/src/styles/BookDetail.css b/src/styles/BookDetail.css index 75fc7eb..68af0e9 100644 --- a/src/styles/BookDetail.css +++ b/src/styles/BookDetail.css @@ -1,11 +1,3 @@ -@font-face { - font-family: 'Pretendard-Regular'; - src: url('https://cdn.jsdelivr.net/gh/Project-Noonnu/noonfonts_2107@1.1/Pretendard-Regular.woff') - format('woff'); - font-weight: 400; - font-style: normal; -} - .bookDetail_content { box-sizing: border-box; max-width: 1440px; @@ -34,14 +26,12 @@ .summary_left_img { width: 278px; height: 394px; - border-radius: 5px; - background: #a1a1a1; margin-right: 60px; } .up_left_book_title { color: #000; - font-family: 'Pretendard-Regular'; + font-family: var(--basic-font); font-size: 24px; font-style: normal; font-weight: 700; @@ -50,7 +40,7 @@ .up_left_book_author { color: #000; - font-family: 'Pretendard-Regular'; + font-family: var(--basic-font); font-size: 16px; font-style: normal; font-weight: 400; @@ -100,7 +90,7 @@ .bottom_text_bookList { color: #000; text-align: center; - font-family: 'Pretendard-Regular'; + font-family: var(--basic-font); font-size: 20px; font-style: normal; font-weight: 600; @@ -128,7 +118,7 @@ .bookInfo_title { color: #000; text-align: center; - font-family: 'Pretendard-Regular'; + font-family: var(--basic-font); font-size: 24px; font-style: normal; font-weight: 700; @@ -142,7 +132,7 @@ .content_bold { color: #000; - font-family: 'Pretendard-Regular'; + font-family: var(--basic-font); font-size: 16px; font-style: normal; font-weight: 700 !important; @@ -152,7 +142,7 @@ .content_normal { margin-top: 20px; color: #000; - font-family: 'Pretendard-Regular'; + font-family: var(--basic-font); font-size: 16px; font-style: normal; font-weight: 500; @@ -170,7 +160,7 @@ .bookComment_title { color: #000; - font-family: 'Pretendard-Regular'; + font-family: var(--basic-font); font-size: 24px; font-style: normal; font-weight: 700; diff --git a/src/styles/Counseling.css b/src/styles/Counseling.css deleted file mode 100644 index 7eaf277..0000000 --- a/src/styles/Counseling.css +++ /dev/null @@ -1,45 +0,0 @@ -.counseling_content { - display: flex; - flex-direction: column; - align-items: center; - max-width: 1440px; -} - -.counseling_category_wrapper { - display: flex; - flex-direction: column; - align-items: center; - width: 100%; - height: 110px; -} - -.cns_category_title { - width: 100%; - font-family: var(--basic-font); - font-size: 20px; - font-style: normal; - font-weight: 700; - height: auto; - display: flex; - justify-content: center; - align-items: center; - margin: 40px; -} - -.cns_category_content_wrapper { - display: grid; - grid-template-columns: repeat(5, 1fr); - margin: 0 40px; - height: auto; - gap: 90px; -} - -.counseling_feed_wrapper { - display: flex; - flex-direction: column; - align-items: center; - width: 100%; - margin-top: 30px; - justify-content: center; - height: 60vh; -} diff --git a/src/styles/Counseling/Counseling.css b/src/styles/Counseling/Counseling.css new file mode 100644 index 0000000..088534c --- /dev/null +++ b/src/styles/Counseling/Counseling.css @@ -0,0 +1,94 @@ +.counseling_content { + display: flex; + flex-direction: column; + align-items: center; + width: 100%; +} + +.counseling_category_wrapper { + display: flex; + flex-direction: column; + align-items: center; + width: 100%; + height: 200px; +} + +.cns_category_title { + width: 100%; + font-family: var(--basic-font); + font-size: 24px; + line-height: 29px; + font-style: normal; + font-weight: 700; + height: auto; + display: flex; + justify-content: center; + align-items: center; + margin: 40px; +} + +.cns_category_content_wrapper { + display: grid; + grid-template-columns: repeat(10, 1fr); + margin: 0 40px; + height: auto; + gap: 70px; +} + +.cns_category { + display: flex; + flex-direction: column; + align-items: center; + cursor: pointer; +} + +.cns_category_img { + width: 50px; + height: 50px; + object-fit: contain; + transition: all 0.2s ease-in-out; +} + +.cns_category_img:hover { + transform: scale(1.2, 1.2); +} + +.cns_category_text { + margin-top: 8px; + font-family: var(--basic-font); + font-size: 18px; + font-style: normal; + font-weight: 400; + line-height: 21px; +} + +.counseling_feed_wrapper { + display: flex; + flex-direction: column; + align-items: center; + width: 100%; + margin-top: 80px; + justify-content: center; + /* height: 60vh; */ +} + +.cnsFeed_card_wrapper { + width: 100%; + height: 200px; + display: flex; + justify-content: center; + margin: 20px 0; + /* transition: all 0.5s ease-in-out; */ + /* transform: scale(1.5); */ + opacity: 1; +} + +.cnsFeed_card_wrapper .visible { + transform: scale(1); + opacity: 0; +} + +#cn_target { + width: 100%; + height: 10px; +} diff --git a/src/styles/Counseling/CounselingView.css b/src/styles/Counseling/CounselingView.css index f66faa8..a7a689a 100644 --- a/src/styles/Counseling/CounselingView.css +++ b/src/styles/Counseling/CounselingView.css @@ -1,7 +1,7 @@ .cnsView_container { display: flex; flex-direction: column; - width: 800px; + width: 1120px; height: 200px; border-radius: 15px; padding: 20px 30px; diff --git a/src/styles/Counseling/PrescriptionCard.css b/src/styles/Counseling/PrescriptionCard.css new file mode 100644 index 0000000..75ebbd7 --- /dev/null +++ b/src/styles/Counseling/PrescriptionCard.css @@ -0,0 +1,167 @@ +.prscrCard_container { + width: 310px; + height: 420px; + background-color: #fff; + border: 5px solid #a4d6dd; + border-radius: 15px; + padding: 9px; +} + +.prscrCard_top_wrapper { + position: sticky; + display: flex; + flex-direction: column; + align-items: center; + width: 100%; + height: 150px; +} + +.prscr_vector_icon { + position: absolute; + left: 3px; + width: 39px; + height: 39px; + object-fit: contain; +} + +.prscrCard_title_wrapper { + display: flex; + width: 100%; + height: 40px; + flex-direction: column; + align-items: center; + margin-top: 39px; + text-align: center; +} + +.prscrCard_title { + color: #00a2bc; + font-family: 'SangSangAnt'; + font-size: 35px; + font-style: normal; + font-weight: 500; + line-height: 29px; +} + +.prscrCard_user_name_wrapper { + display: flex; + justify-content: space-between; + margin-top: 24px; + width: 232px; + height: 20px; + font-family: 'HakgyoansimWoojuR'; + font-size: 16px; + line-height: 19px; + border-bottom: 1px solid #000000; +} + +.prscrCard_user_name { + display: flex; + justify-content: center; + align-items: center; + width: 86%; +} + +.prscrCard_mid_wrapper { + position: sticky; + margin-top: 8px; + width: 100%; + height: 200px; + display: flex; + justify-content: center; +} + +#prscrCard_mid_title { + width: 50px; + top: -12px; + left: 60px; + display: flex; + justify-content: center; + align-items: center; + padding: 4px; + background-color: #fff; + position: absolute; + z-index: 1; + color: #67b6c1; + font-family: 'HakgyoansimWoojuR'; + font-size: 14px; + line-height: 17px; +} + +.prscrCard_bookInfo_wrapper { + display: flex; + padding: 20px 15px; + width: 233px; + height: 176px; + border: #a4d6dd 2px solid; + border-radius: 15px; +} + +.bookInfo_left_wrapper { + display: flex; + align-items: center; + width: 105px; +} + +.bookInfo_img { + width: 90px; + height: 130px; + object-fit: cover; + border-radius: 5px; +} + +.bookInfo_right_wrapper { + position: sticky; + display: flex; + flex-direction: column; + width: 100%; + padding-top: 10px; + padding-left: 14px; +} + +.prscrCard_bookInfo_title { + width: 100%; + font-family: var(--basic-font); + font-size: 14px; + font-style: normal; + font-weight: 700; + line-height: 18px; +} + +.prscrCard_bookInfo_author, +.prscrCard_bookInfo_date { + width: 100%; + color: #868686; + font-family: var(--basic-font); + font-size: 11px; + font-weight: 400; + font-style: normal; + line-height: 13px; + margin-top: 5px; +} + +.pharmacy_icon_group { + position: absolute; + bottom: -15px; + right: -5px; + width: 60px; + height: 60px; + object-fit: contain; +} + +.prscrCard_bottom_wrapper { + display: flex; + width: 100%; + height: 40px; + justify-content: center; + align-items: center; +} + +.prscrCard_update_date { + font-family: 'HakgyoansimWoojuR'; + font-weight: 500; + font-style: bold; + font-size: 17px; + line-height: 20px; + color: #00a2bc; +} diff --git a/src/styles/Counseling/PrescriptionWrite.css b/src/styles/Counseling/PrescriptionWrite.css new file mode 100644 index 0000000..c4902ae --- /dev/null +++ b/src/styles/Counseling/PrescriptionWrite.css @@ -0,0 +1,272 @@ +.prescription_content_container { + display: flex; + flex-direction: column; + width: 100%; + height: 180vh; +} + +.prescription_content_up_container { + width: 100%; + height: 580px; + display: flex; + flex-direction: column; + /* align-items: center; */ + background-color: #d3e7f6; + padding: 30px 120px; +} + +.prscr_bookInfo_wrapper { + position: sticky; + display: flex; + width: 100%; + height: fit-content; +} + +.prscr_img_wrapper { + width: 280px; + height: 390px; + border-radius: 5px; + object-fit: cover; +} + +.prscr_left_wrapper { + margin-right: 70px; + margin-left: 40px; + margin-bottom: 50px; + height: 100%; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +} + +.search_res_no_btn { + width: 200px; + height: 40px; + margin-top: 30px; + padding: 4px; + text-align: center; + font-family: var(--basic-font); + font-weight: 400; + font-style: normal; + font-size: 20px; + color: #454545; + background-color: #fff; + border: 3px solid #67b6c1; + border-radius: 15px; +} + +.prscr_right_wrapper { + width: 100%; + height: 100%; + display: flex; + flex-direction: column; + padding-top: 25px; + justify-content: flex-start; +} + +.prscr_searchBar_wrapper { + position: sticky; + z-index: 1; + width: 85%; + /* height: 50px; */ + display: flex; + align-items: center; + padding: 12px 12px; + background-color: #fff; + border-radius: 5px; + box-shadow: 0px 2px 4px rgba(0, 0, 0, 0.33); +} + +.prscr_search_icon { + color: #454545; + width: 20px; + height: 20px; + margin-right: 10px; +} + +.prscr_search_text { + /* input에 기본으로 적용되는 스타일 제거 */ + appearance: none; + border: none; + width: 100%; + height: 100%; + font-family: var(--basic-font); + font-size: 16px; + font-style: normal; + font-weight: 500; + line-height: 19px; + color: #454545; +} + +.prscr_search_text:focus { + outline: none; + color: #454545; +} + +.prscr_search_close_btn { + background-color: #fff; + color: #454545; + font-weight: 700; +} + +.prscr_search_res_wrapper { + display: flex; + flex-direction: column; + width: 100%; + height: 100%; + margin-top: 50px; +} + +.prscr_search_res_wrapper > p, +button { + font-family: var(--basic-font); + font-style: normal; +} + +.search_res_bookTitle { + font-size: 24px; + font-weight: 600; + line-height: 29px; + color: #454545; + margin-bottom: 20px; +} + +.search_res_bookAuthor, +.search_res_bookCompany { + font-size: 20px; + font-weight: 600; + line-height: 24px; + color: #868686; + margin-bottom: 10px; +} + +.prescription_content_bottom_container { + position: relative; + display: flex; + flex-direction: column; + align-items: center; + width: 100%; + height: 100vh; +} + +.prescription_btn_container { + display: flex; + align-items: center; + justify-content: center; + width: 100%; + height: 100px; + background-color: #fff; + padding-bottom: 15px; +} + +.prscr_cancel_btn { + width: 280px; + height: 45px; + border-radius: 11px; + background-color: #ffdadf; + font-family: var(--basic-font); + font-weight: 600; + font-style: normal; + font-size: 20px; + line-height: 24px; + margin-right: 200px; +} + +.prscr_apply_btn { + width: 280px; + height: 45px; + border-radius: 11px; + background-color: #c8edf2; + font-family: var(--basic-font); + font-weight: 600; + font-style: normal; + font-size: 20px; + line-height: 24px; +} + +.prscr_apply_btn:hover { + background-color: #67b6c1; +} + +@keyframes show { + from { + transform: translateY(50px); + } + to { + transform: translateY(-200px); + } +} + +@keyframes up { + 0% { + transform: translateY(10%); + opacity: 0; + } + 50% { + opacity: 0.5; + } + 100% { + transform: translateY(0); + opacity: 1; + } +} + +@keyframes down { + 0% { + transform: translateY(-11%); + opacity: 1; + } + 100% { + transform: translateY(5%); + opacity: 0; + } +} + +#prscr_write_box { + width: 80%; + height: 100vh; + padding: 40px; + border-radius: 20px; + background-color: #fff; + margin-top: 80px; + box-shadow: 4px 4px 20px rgba(0, 0, 0, 0.4); +} + +#prscr_write_box > p { + font-family: var(--basic-font); + font-style: normal; + font-weight: 600; + font-size: 20px; + line-height: 24px; +} + +#prscr_write_box > textarea { + width: 100%; + height: 70%; + margin-top: 30px; + text-align: start; + font-family: var(--basic-font); + font-style: normal; + font-size: 16px; + font-weight: 400; + line-height: 19px; + appearance: none; + border: none; + outline: none; + resize: none; +} + +.visible, +.noVisible { + position: absolute; + top: -160px; + animation: up 0.5s ease-in; +} + +.no { + position: absolute; + top: -50px; + animation: down 0.5s ease-out; + opacity: 0; +} diff --git a/src/styles/Counseling/PrescriptionWriteStep2.css b/src/styles/Counseling/PrescriptionWriteStep2.css new file mode 100644 index 0000000..fa7a67f --- /dev/null +++ b/src/styles/Counseling/PrescriptionWriteStep2.css @@ -0,0 +1,33 @@ +.prescription_mid_wrapper { + /* position: static; */ + display: flex; + flex-direction: column; + width: 100%; + height: 400px; + justify-content: center; + align-items: center; + background-color: #fff; + z-index: -1; + padding: 0 120px; +} + +.prscr_write_wrapper { + display: flex; + flex-direction: column; + position: relative; + top: -70px; + width: 94%; + height: 450px; + background-color: #fff; + border-radius: 20px; + box-shadow: 4px 4px 20px rgba(0, 0, 0, 0.4); + padding: 30px; +} + +.prscr_write_title { + font-family: var(--basic-font); + font-weight: 700; + font-style: normal; + font-size: 20px; + line-height: 24px; +} diff --git a/src/styles/Counseling/ProcessTitle.css b/src/styles/Counseling/ProcessTitle.css new file mode 100644 index 0000000..e237dd0 --- /dev/null +++ b/src/styles/Counseling/ProcessTitle.css @@ -0,0 +1,94 @@ +.process_normal_title_wrapper, +.process_detail_title_wrapper { + position: sticky; + background-color: #fff; + display: flex; + flex-direction: column; + align-items: center; + width: 100%; + height: 150px; + border-bottom: 0.5px solid #dfdfdf; + box-shadow: 0px 4px 4px 0px rgba(0, 0, 0, 0.25); + z-index: 1; + padding: 34px 0; +} + +.process_normal_title, +.process_detail_title { + font-family: var(--basic-font); + font-style: normal; + font-weight: 1000; + font-size: 24px; + line-height: 29px; + margin-bottom: 30px; +} + +.process_normal_subTitle, +.process_detail_subTitle { + font-family: var(--basic-font); + font-style: normal; + font-weight: 600; + font-size: 20px; + line-height: 24px; +} + +#normal_nickname, +#process_nickname, +#detail_from_nickname, +#detail_target_nickname { + color: #00a2bc; +} + +.process_title_wrapper { + position: sticky; + display: flex; + flex-direction: column; + justify-content: center; + width: 100%; + height: 150px; + border-bottom: 0.5px solid #dfdfdf; + box-shadow: 0px 4px 4px 0px rgba(0, 0, 0, 0.25); + z-index: 1; + padding: 45px 160px; +} + +.process_title { + font-family: var(--basic-font); + font-style: normal; + font-weight: 600; + font-size: 24px; + line-height: 29px; + margin-bottom: 15px; +} + +.progress_bar_wrapper { + display: flex; + align-items: center; +} + +#progress { + appearance: none; + width: 95%; + height: 12px; + margin-right: 32px; +} + +/* process bar 배경 */ +#progress::-webkit-progress-bar { + background: #ececec; + border-radius: 5px; +} + +/* process의 진행 bar */ +#progress::-webkit-progress-value { + border-radius: 5px; + background: #a4c4dd; +} + +#progress_text { + color: #c0c0c0; + font-family: var(--basic-font); + font-size: 18px; + font-weight: 500; + font-style: normal; +} diff --git a/src/styles/Counseling/WorryDetail.css b/src/styles/Counseling/WorryDetail.css new file mode 100644 index 0000000..a834b83 --- /dev/null +++ b/src/styles/Counseling/WorryDetail.css @@ -0,0 +1,156 @@ +.worry_detail_content { + display: flex; + flex-direction: column; + align-items: center; + width: 100%; +} + +.worry_detail_title_wrapper { + /* position: sticky; */ + display: flex; + flex-direction: column; + width: 100%; + height: 580px; + background-color: #c8edf2; +} + +.wd_user_wrapper { + display: flex; + justify-content: space-between; + align-items: center; + padding: 100px 150px; +} + +.wd_user_left_wrapper { + width: 500px; + display: flex; + align-items: center; +} + +.wd_user_img { + width: 120px; + height: 120px; + border-radius: 100%; + background-color: #ffffff; + margin-right: 40px; +} + +.wd_user_info_wrapper { + display: flex; + flex-direction: column; +} + +.wd_user_name { + font-family: var(--basic-font); + font-style: normal; + font-weight: 700; + font-size: 24px; + margin-bottom: 10px; +} + +.wd_user_date { + font-family: var(--basic-font); + font-style: normal; + font-weight: 400; + font-size: 16px; + color: #8e8e8e; +} + +.wd_title_text_wrapper { + display: flex; + justify-content: center; + align-items: center; + font-family: var(--basic-font); + font-weight: 700; + font-style: normal; + font-size: 40px; + color: black; +} + +.worry_detail_content_wrapper { + display: flex; + flex-direction: column; + align-items: center; + width: 100%; + height: 1360px; + background-color: #f3fdff; + position: static; +} + +.wd_content_detail_wrapper { + position: relative; + width: 1120px; + height: 350px; + z-index: 1; + top: -80px; + background-color: #fff; + border-radius: 20px; + display: flex; + flex-direction: column; + padding: 40px; + + font-family: var(--basic-font); + font-style: normal; + + box-shadow: 4px 4px 20px rgba(0, 0, 0, 0.4); +} + +.wd_content_detail_title { + font-weight: 600; + font-size: 20px; + line-height: 24px; + margin-bottom: 30px; +} + +.worry_detail_prscr_wrapper { + display: flex; + flex-direction: column; + position: relative; + top: -20px; + width: 1120px; + height: 750px; + padding: 40px; + margin-top: 10px; + background-color: #fff; + border-radius: 20px; + box-shadow: 4px 4px 20px rgba(0, 0, 0, 0.4); +} + +.wd_prscr_list_title { + font-family: var(--basic-font); + font-weight: 600; + font-size: 20px; + line-height: 24px; + margin-bottom: 30px; +} + +.wd_prscr_list_wrapper { + display: grid; + grid-template-columns: repeat(3, 1fr); + overflow-y: scroll; + width: 102%; + column-gap: 30px; + justify-items: center; + row-gap: 44px; +} + +.wd_prscr_list_wrapper::-webkit-scrollbar { + visibility: hidden; +} + +.prscr_btn { + width: 700px; + height: 70px; + background-color: #c8edf2; + border-radius: 11px; + margin-top: 70px; + margin-bottom: 95px; + + font-family: var(--basic-font); + font-weight: 700; + font-size: 30px; + line-height: 36px; + font-style: normal; + + box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.4); +} diff --git a/src/styles/GlobalStyles.js b/src/styles/GlobalStyles.js index 502174c..3da6ccd 100644 --- a/src/styles/GlobalStyles.js +++ b/src/styles/GlobalStyles.js @@ -1,5 +1,5 @@ -import { createGlobalStyle } from "styled-components"; -import reset from "styled-reset"; +import { createGlobalStyle } from 'styled-components'; +import reset from 'styled-reset'; const GlobalStyles = createGlobalStyle` ${reset} @@ -28,6 +28,20 @@ const GlobalStyles = createGlobalStyle` font-style: normal; } + @font-face { + font-family: 'SangSangAnt'; + src: url('https://cdn.jsdelivr.net/gh/projectnoonnu/noonfonts_three@1.0/SangSangAnt.woff') format('woff'); + font-weight: normal; + font-style: normal; +} + +@font-face { + font-family: 'HakgyoansimWoojuR'; + src: url('https://cdn.jsdelivr.net/gh/projectnoonnu/noonfonts_2307-2@1.0/HakgyoansimWoojuR.woff2') format('woff2'); + font-weight: normal; + font-style: normal; +} + body{ line-height: 1; diff --git a/src/styles/Header.css b/src/styles/Header.css index ca74788..bf7e180 100644 --- a/src/styles/Header.css +++ b/src/styles/Header.css @@ -33,7 +33,7 @@ .header_list > li { margin: 0 40px; color: #000; - font-family: 'Pretendard-Regular'; + font-family: var(--basic-font); font-size: 16px; font-style: normal; font-weight: 400; diff --git a/src/styles/Prescription/PrescriptionDetail.css b/src/styles/Prescription/PrescriptionDetail.css new file mode 100644 index 0000000..ef205d0 --- /dev/null +++ b/src/styles/Prescription/PrescriptionDetail.css @@ -0,0 +1,162 @@ +.prscr_detail_top_container { + position: sticky; + width: 100%; + height: 680px; + display: flex; + flex-direction: column; + align-items: center; + background-color: #c1ecd4; + padding: 32px 160px; + font-family: var(--basic-font); +} + +.prscr_detail_top_info_wrapper { + max-width: 1440px; + width: 100%; + display: flex; + flex-direction: column; + justify-content: center; +} + +.prscr_detail_top_wrapper { + width: 100%; + height: 50px; + text-align: start; +} + +.dt_prscr_title_wrapper { + display: flex; + align-items: center; + width: 100%; + height: 100%; + margin-bottom: 70px; + font-family: var(--basic-font); + font-style: normal; + font-size: 20px; + font-weight: 600; + line-height: 24px; +} + +#dt_prscr_title { + font-size: 24px; + font-weight: 600; + line-height: 29px; +} + +#dt_from_nickname { + font-size: 20px; + font-weight: 600; + line-height: 24px; +} + +#prscr_dt_loading_img { + width: 280px; + height: 390px; +} + +.prscr_dt_bookInfo_wrapper { + width: 100%; + height: 100%; + display: flex; +} + +.prscr_dt_right_wrapper { + width: 100%; + height: 100%; + display: flex; + flex-direction: column; + margin-left: 80px; + align-items: flex-start; + font-family: var(--basic-font); + font-style: normal; +} + +.prscr_dt_right_title { + font-weight: 700; + font-size: 24px; + line-height: 29px; + color: #454545; + margin: 30px 0; +} + +.prscr_dt_right_author, +.prscr_dt_right_bookCompany { + font-weight: 600; + font-size: 20px; + line-height: 24px; + color: #868686; + margin-bottom: 10px; +} + +.prscr_detail_bottom_wrapper { + width: 100%; + height: 180vh; + background-color: #edf9f3; + display: flex; + flex-direction: column; + align-items: center; +} + +.prscr_detail_res_container { + position: relative; + bottom: 80px; + max-width: 1440px; + width: 80%; + height: 350px; + background-color: #fff; + border-radius: 20px; + box-shadow: 4px 4px 20px rgba(0, 0, 0, 0.4); + display: flex; + flex-direction: column; + padding: 40px; + z-index: 1; +} + +.prscr_dt_res_title { + font-family: var(--basic-font); + font-size: 20px; + font-weight: 600; + font-style: normal; + line-height: 24px; + margin-bottom: 30px; +} + +.prscr_other_container { + display: flex; + width: 80%; + max-width: 1440px; + height: 750px; + flex-direction: column; + margin: 50px 0; + padding: 40px 60px; + font-family: var(--basic-font); + font-style: normal; + background-color: #fff; + border-radius: 20px; + box-shadow: 4px 4px 20px rgba(0, 0, 0, 0.4); +} + +.other_list_title { + font-size: 20px; + font-weight: 600; + line-height: 24px; + color: #000; + margin-bottom: 30px; + margin-left: 15px; +} + +.other_list_wrapper { + width: 100%; + display: grid; + align-self: center; + grid-template-columns: repeat(3, 0.8fr); + row-gap: 40px; + column-gap: 40px; + margin-left: 20px; + overflow-y: scroll; + align-items: center; +} + +.other_list_wrapper::-webkit-scrollbar { + visibility: hidden; +} diff --git a/src/styles/SearchBookModal.css b/src/styles/SearchBookModal.css new file mode 100644 index 0000000..cde5813 --- /dev/null +++ b/src/styles/SearchBookModal.css @@ -0,0 +1,50 @@ +@keyframes showModal { + from { + top: 5px; + opacity: 0; + z-index: -1; + } + to { + top: 70px; + opacity: 1; + z-index: 1; + } +} + +@keyframes closeModal { + from { + top: 70px; + opacity: 1; + z-index: 1; + } + to { + top: 5px; + opacity: 0; + z-index: -1; + } +} + +.searchBook_modal_container_true { + position: absolute; + z-index: 1; + top: 70px; + display: flex; + align-items: center; + width: 755px; + height: 330px; + background-color: #fff; + border-radius: 5px; + margin-top: 10px; + box-shadow: 0px 2px 4px rgba(0, 0, 0, 0.33); + animation: showModal 0.5s ease-in-out; +} + +.searchBook_modal_container_false { + position: absolute; + width: 755px; + height: 330px; + z-index: -1; + top: 5px; + animation: closeModal 2s ease-in-out; + transition: all 1s; +} diff --git a/src/styles/SearchStyles.css b/src/styles/SearchStyles.css index 428c1bd..a0c0173 100644 --- a/src/styles/SearchStyles.css +++ b/src/styles/SearchStyles.css @@ -58,8 +58,8 @@ width: 50px; height: 40px; font-size: 1.5rem; - position: absolute; - right: 70px; + position: relative; + right: 0; background-color: #fff; }