diff --git a/src/components/common/Filter.tsx b/src/components/common/Filter.tsx
index 6afab5f2..e3023c45 100644
--- a/src/components/common/Filter.tsx
+++ b/src/components/common/Filter.tsx
@@ -20,9 +20,7 @@ export const Filter = ({ filters, selectedFilter, setSelectedFilter }: FilterPro
};
const handleModalClick = () => {
- if (!isOpenModal) {
- setIsOpenModal(true);
- }
+ setIsOpenModal(!isOpenModal);
};
return (
diff --git a/src/components/common/NavBar.tsx b/src/components/common/NavBar.tsx
index b6a6bb54..028e0471 100644
--- a/src/components/common/NavBar.tsx
+++ b/src/components/common/NavBar.tsx
@@ -90,7 +90,7 @@ const NavBar = ({ src, path }: FabProps) => {
);
})}
-
+ {path && }
);
diff --git a/src/components/search/BookSearchResult.tsx b/src/components/search/BookSearchResult.tsx
new file mode 100644
index 00000000..39b26624
--- /dev/null
+++ b/src/components/search/BookSearchResult.tsx
@@ -0,0 +1,132 @@
+import type { SearchedBook } from '@/pages/search/Search';
+import styled from '@emotion/styled';
+import { colors, typography } from '@/styles/global/global';
+import { useNavigate } from 'react-router-dom';
+
+interface BookSearchResultProps {
+ type: 'searching' | 'searched';
+ searchedBookList: SearchedBook[];
+}
+
+export function BookSearchResult({ type, searchedBookList }: BookSearchResultProps) {
+ const navigate = useNavigate();
+ const isEmptySearchedBookList = () => {
+ if (searchedBookList.length === 0) return true;
+ else return false;
+ };
+
+ const handleApplyBook = () => {
+ navigate('/search/applybook');
+ };
+ return (
+
+
+ {type === 'searching' ? <>> : 전체 {searchedBookList.length}}
+
+ {isEmptySearchedBookList() ? (
+
+ 현재 등록된 책이 없어요.
+ 원하는 책을 신청해주세요!
+ 책 신청하기
+
+ ) : (
+ searchedBookList.map(book => (
+
+
+
+ {book.title}
+
+ {book.author} 저 · {book.publisher}
+
+
+
+ ))
+ )}
+
+
+ );
+}
+
+const Wrapper = styled.div`
+ display: flex;
+ flex-direction: column;
+ padding: 0 20px;
+ margin-bottom: 72px;
+`;
+
+const List = styled.div`
+ display: flex;
+ flex-direction: column;
+`;
+
+const BookItem = styled.div`
+ display: flex;
+ border-bottom: 1px solid ${colors.darkgrey.dark};
+ padding: 12px 0;
+`;
+
+const ResultHeader = styled.div`
+ font-size: ${typography.fontSize.sm};
+ font-weight: ${typography.fontWeight.medium};
+ color: ${colors.white};
+ padding-bottom: 8px;
+ border-bottom: 1px solid ${colors.darkgrey.dark};
+`;
+
+const Cover = styled.img`
+ width: 80px;
+ height: 107px;
+ object-fit: cover;
+`;
+
+const BookInfo = styled.div`
+ display: flex;
+ flex-direction: column;
+ margin-left: 12px;
+`;
+
+const Title = styled.h3`
+ font-size: ${typography.fontSize.base};
+ font-weight: ${typography.fontWeight.semibold};
+ color: ${colors.white};
+`;
+
+const Subtitle = styled.span`
+ font-size: ${typography.fontSize.xs};
+ font-weight: ${typography.fontWeight.medium};
+ color: ${colors.grey[200]};
+ margin-top: 8px;
+`;
+
+const EmptyWrapper = styled.div`
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ height: 60vh;
+`;
+
+const MainText = styled.p`
+ color: ${colors.white};
+ font-size: ${typography.fontSize.lg};
+ font-weight: ${typography.fontWeight.semibold};
+ margin-bottom: 8px;
+`;
+
+const SubText = styled.p`
+ color: ${colors.grey[100]};
+ font-size: ${typography.fontSize.sm};
+ font-weight: ${typography.fontWeight.regular};
+ margin-bottom: 20px;
+`;
+
+const RequestButton = styled.button`
+ background-color: ${colors.purple.main};
+ color: ${colors.white};
+ padding: 10px 12px;
+ font-size: ${typography.fontSize.base};
+ border: none;
+ border-radius: 12px;
+ font-weight: ${typography.fontWeight.semibold};
+ cursor: pointer;
+`;
diff --git a/src/components/search/SearchResult.tsx b/src/components/search/GroupSearchResult.tsx
similarity index 98%
rename from src/components/search/SearchResult.tsx
rename to src/components/search/GroupSearchResult.tsx
index 898f4343..1c07f27b 100644
--- a/src/components/search/SearchResult.tsx
+++ b/src/components/search/GroupSearchResult.tsx
@@ -84,7 +84,7 @@ const dummyMyGroups: Group[] = [
},
];
-const SearchResult = () => {
+const GroupSearchResult = () => {
const [selected, setSelected] = useState('');
const [showGroup] = useState(dummyMyGroups);
const [selectedFilter, setSelectedFilter] = useState('마감임박순');
@@ -139,7 +139,7 @@ const SearchResult = () => {
);
};
-export default SearchResult;
+export default GroupSearchResult;
const TabContainer = styled.div`
display: flex;
diff --git a/src/components/search/MostSearchedBooks.tsx b/src/components/search/MostSearchedBooks.tsx
new file mode 100644
index 00000000..a485b25c
--- /dev/null
+++ b/src/components/search/MostSearchedBooks.tsx
@@ -0,0 +1,153 @@
+import { colors, typography } from '@/styles/global/global';
+import styled from '@emotion/styled';
+
+interface Book {
+ id: number;
+ title: string;
+ coverUrl: string;
+}
+
+const dummyBooks: Book[] = [
+ {
+ id: 1,
+ title: '토마토 컵라면',
+ coverUrl: 'https://cdn.imweb.me/upload/S20230204e049098f5e744/e6fd3d849546d.jpg',
+ },
+ {
+ id: 2,
+ title: '사슴',
+ coverUrl: 'https://cdn.imweb.me/upload/S20230204e049098f5e744/e6fd3d849546d.jpg',
+ },
+ {
+ id: 3,
+ title: '호르몬 체인지지',
+ coverUrl: 'https://cdn.imweb.me/upload/S20230204e049098f5e744/e6fd3d849546d.jpg',
+ },
+ {
+ id: 4,
+ title: '호르몬 체인지지',
+ coverUrl: 'https://cdn.imweb.me/upload/S20230204e049098f5e744/e6fd3d849546d.jpg',
+ },
+ {
+ id: 5,
+ title: '호르몬 체인지지',
+ coverUrl: 'https://cdn.imweb.me/upload/S20230204e049098f5e744/e6fd3d849546d.jpg',
+ },
+ {
+ id: 6,
+ title: '호르몬 체인지지',
+ coverUrl: 'https://cdn.imweb.me/upload/S20230204e049098f5e744/e6fd3d849546d.jpg',
+ },
+];
+
+export default function MostSearchedBooks() {
+ return (
+
+
+ 가장 많이 검색된 책
+ {/* 서버 응답 포맷을 모르기에 우선 하드 코딩 */}
+ 01.12. 기준
+
+ {dummyBooks.length === 0 ? (
+
+ 아직 순위가 집계되지 않았어요.
+ 조금만 기다려주세요!
+
+ ) : (
+
+ {dummyBooks.map((book, index) => (
+
+ {index + 1}.
+
+ {book.title}
+
+ ))}
+
+ )}
+
+ );
+}
+
+const Container = styled.div`
+ display: flex;
+ flex-direction: column;
+ padding: 36px 20px 0 20px;
+ background-color: var(--color-black-main);
+ margin-bottom: 72px;
+`;
+
+const Header = styled.div`
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 16px;
+`;
+
+const Title = styled.h2`
+ font-size: ${typography.fontSize.lg};
+ color: ${colors.white};
+ font-weight: ${typography.fontWeight.semibold};
+`;
+
+const DateText = styled.span`
+ font-size: ${typography.fontSize.xs};
+ color: ${colors.grey[300]};
+ font-weight: ${typography.fontWeight.regular};
+`;
+
+const BookList = styled.ul`
+ display: flex;
+ flex-direction: column;
+ margin-top: 4px;
+`;
+
+const BookItem = styled.li`
+ display: flex;
+ align-items: center;
+ padding: 12px 0;
+ border-bottom: 1px solid ${colors.darkgrey.dark};
+`;
+
+const Rank = styled.span`
+ font-size: ${typography.fontSize.base};
+ color: ${colors.white};
+ font-weight: ${typography.fontWeight.medium};
+ width: 24px;
+`;
+
+const Cover = styled.img`
+ width: 45px;
+ height: 60px;
+ object-fit: cover;
+`;
+
+const BookTitle = styled.span`
+ font-size: ${typography.fontSize.sm};
+ color: ${colors.white};
+ font-weight: ${typography.fontWeight.regular};
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ margin-left: 8px;
+`;
+
+const EmptyMessage = styled.div`
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ height: 60vh;
+ text-align: center;
+`;
+
+const MainText = styled.div`
+ font-size: ${typography.fontSize.lg};
+ color: ${colors.white};
+ font-weight: ${typography.fontWeight.semibold};
+ margin-bottom: 8px;
+`;
+
+const SubText = styled.div`
+ font-size: ${typography.fontSize.sm};
+ color: ${colors.grey[100]};
+ font-weight: ${typography.fontWeight.regular};
+`;
diff --git a/src/components/search/SearchBar.tsx b/src/components/search/SearchBar.tsx
index 0d6db926..489b66e4 100644
--- a/src/components/search/SearchBar.tsx
+++ b/src/components/search/SearchBar.tsx
@@ -1,6 +1,5 @@
import styled from '@emotion/styled';
import searchIcon from '../../assets/searchBar/search.svg';
-
import deleteIcon from '../../assets/searchBar/delete.svg';
import { IconButton } from '../common/IconButton';
@@ -10,9 +9,17 @@ interface SearchBarProps {
onChange?: (v: string) => void;
onClick?: () => void;
onSearch?: () => void;
+ isSearched?: boolean;
}
-const SearchBar = ({ placeholder, value, onChange, onClick, onSearch }: SearchBarProps) => {
+const SearchBar = ({
+ placeholder,
+ value,
+ onChange,
+ onClick,
+ onSearch,
+ isSearched,
+}: SearchBarProps) => {
return (
- {value && onChange?.('')} />}
+ {value && !isSearched && (
+ onChange?.('')} />
+ )}
onSearch?.()} />
diff --git a/src/pages/group/Group.tsx b/src/pages/group/Group.tsx
index 9c69b761..00adab86 100644
--- a/src/pages/group/Group.tsx
+++ b/src/pages/group/Group.tsx
@@ -129,7 +129,7 @@ const Group = () => {
const closeCompletedGroupModal = () => setIsCompletedGroupModalOpen(false);
const handleSearchBarClick = () => {
- navigate('/groupSearch');
+ navigate('/groupsearch');
};
return (
diff --git a/src/pages/groupSearch/GroupSearch.tsx b/src/pages/groupSearch/GroupSearch.tsx
index c1d73dad..447ed10d 100644
--- a/src/pages/groupSearch/GroupSearch.tsx
+++ b/src/pages/groupSearch/GroupSearch.tsx
@@ -5,7 +5,7 @@ import { useNavigate } from 'react-router-dom';
import SearchBar from '@/components/search/SearchBar';
import { useState } from 'react';
import RecentSearchTabs from '@/components/search/RecentSearchTabs';
-import SearchResult from '@/components/search/SearchResult';
+import GroupSearchResult from '@/components/search/GroupSearchResult';
const GroupSearch = () => {
const navigate = useNavigate();
@@ -57,7 +57,7 @@ const GroupSearch = () => {
}}
/>
{isSearching ? (
-
+
) : (
{
@@ -31,6 +33,8 @@ const Router = () => {
} />
} />
} />
+ } />
+ } />
} />
} />
} />
diff --git a/src/pages/search/ApplyBook.tsx b/src/pages/search/ApplyBook.tsx
new file mode 100644
index 00000000..41344c62
--- /dev/null
+++ b/src/pages/search/ApplyBook.tsx
@@ -0,0 +1,51 @@
+import TitleHeader from '@/components/common/TitleHeader';
+import { colors, typography } from '@/styles/global/global';
+import styled from '@emotion/styled';
+import leftArrow from '../../assets/common/leftArrow.svg';
+import { useNavigate } from 'react-router-dom';
+import { Wrapper } from '@/components/common/Wrapper';
+
+const ApplyBook = () => {
+ const navigate = useNavigate();
+ const handleBackButton = () => {
+ navigate(-1);
+ };
+ return (
+
+ }
+ onLeftClick={handleBackButton}
+ />
+
+ texthip2025@gmail.com
+ 이메일로 책 제목, 출판사를 보내주시면
+ 빠른 시일내로 책을 추가해드릴게요!
+
+
+ );
+};
+
+export default ApplyBook;
+
+const TextWrapper = styled.div`
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ height: 60vh;
+`;
+
+const MainText = styled.p`
+ color: ${colors.white};
+ font-size: ${typography.fontSize.lg};
+ font-weight: ${typography.fontWeight.semibold};
+ margin-bottom: 12px;
+`;
+
+const SubText = styled.p`
+ color: ${colors.grey[100]};
+ font-size: ${typography.fontSize.sm};
+ font-weight: ${typography.fontWeight.regular};
+ margin-bottom: 6px;
+`;
diff --git a/src/pages/search/Search.tsx b/src/pages/search/Search.tsx
new file mode 100644
index 00000000..b5f4007a
--- /dev/null
+++ b/src/pages/search/Search.tsx
@@ -0,0 +1,188 @@
+import NavBar from '@/components/common/NavBar';
+import TitleHeader from '@/components/common/TitleHeader';
+import { BookSearchResult } from '@/components/search/BookSearchResult';
+import MostSearchedBooks from '@/components/search/MostSearchedBooks';
+import RecentSearchTabs from '@/components/search/RecentSearchTabs';
+import SearchBar from '@/components/search/SearchBar';
+import { colors, typography } from '@/styles/global/global';
+import styled from '@emotion/styled';
+import { useEffect, useState } from 'react';
+import leftArrow from '../../assets/common/leftArrow.svg';
+
+export interface SearchedBook {
+ id: number;
+ title: string;
+ author: string;
+ publisher: string;
+ coverUrl: string;
+}
+
+const dummySearchedBook: SearchedBook[] = [
+ {
+ id: 1,
+ title: '채식주의자',
+ author: '한강',
+ publisher: '창비',
+ coverUrl: 'https://image.yes24.com/goods/17122707/XL',
+ },
+ {
+ id: 2,
+ title: '채소 마스터 클래스',
+ author: '백지혜',
+ publisher: '세미콜론',
+ coverUrl: 'https://image.yes24.com/goods/109378551/XL',
+ },
+ {
+ id: 3,
+ title: '채소 식탁',
+ author: '김경민',
+ publisher: '래디시',
+ coverUrl: 'https://image.yes24.com/goods/117194041/XL',
+ },
+];
+const Search = () => {
+ const [searchTerm, setSearchTerm] = useState('');
+ const [isSearching, setIsSearching] = useState(false);
+ const [isSearched, setIsSearched] = useState(false);
+
+ const [recentSearches, setRecentSearches] = useState([
+ '딸기12',
+ '당근',
+ '수박245',
+ '참',
+ '메론1',
+ ]);
+
+ const handleChange = (value: string) => {
+ setSearchTerm(value);
+ setIsSearched(false);
+ setIsSearching(value.trim() !== '');
+ };
+
+ const handleSearch = (term: string) => {
+ if (!term.trim()) return;
+ setIsSearching(true);
+ setIsSearched(true);
+ setRecentSearches(prev => {
+ const filtered = prev.filter(t => t !== term);
+ return [term, ...filtered].slice(0, 5);
+ });
+ };
+
+ const handleDelete = (recentSearch: string) => {
+ setRecentSearches(prev => prev.filter(t => t !== recentSearch));
+ };
+
+ const handleRecentSearchClick = (recentSearch: string) => {
+ setSearchTerm(recentSearch);
+ setIsSearched(true);
+ setIsSearching(true);
+ };
+
+ const handleBackButton = () => {
+ setSearchTerm('');
+ };
+
+ useEffect(() => {
+ if (searchTerm.trim() === '') {
+ setIsSearching(false);
+ setIsSearched(false);
+ }
+ }, [searchTerm]);
+
+ return (
+
+ {isSearching ? (
+ }
+ onLeftClick={handleBackButton}
+ />
+ ) : (
+
+ )}
+
+ handleSearch(searchTerm.trim())}
+ isSearched={isSearched}
+ />
+
+
+ {isSearching ? (
+ <>
+ (
+ {isSearched ? (
+
+ ) : (
+
+ )}
+ )
+ >
+ ) : (
+ <>
+
+
+ >
+ )}
+
+
+
+ );
+};
+
+export default Search;
+
+const Wrapper = styled.div`
+ display: flex;
+ position: relative;
+ flex-direction: column;
+ min-width: 320px;
+ max-width: 767px;
+ height: 100vh;
+ margin: 0 auto;
+ background: ${colors.black.main};
+`;
+
+const Header = styled.div`
+ display: flex;
+ width: 100%;
+ position: fixed;
+ top: 0;
+ left: 0;
+ right: 0;
+ max-width: 767px;
+ margin: 0 auto;
+ color: ${colors.white};
+ font-size: ${typography.fontSize['2xl']};
+ font-weight: ${typography.fontWeight['bold']};
+ padding: 16px 20px;
+ background: ${colors.black.main};
+ z-index: 1;
+`;
+
+const SearchBarContainer = styled.div`
+ position: fixed;
+ top: 0;
+ left: 0;
+ right: 0;
+ max-width: 767px;
+ margin: 0 auto;
+ background: ${colors.black.main};
+`;
+
+const Content = styled.div`
+ margin-top: 132px;
+`;