From 84efbc476d2e19c38a9815bd74a0a3e3f4817d82 Mon Sep 17 00:00:00 2001 From: Ji Ho June <129824629+ho0010@users.noreply.github.com> Date: Fri, 11 Jul 2025 20:35:52 +0900 Subject: [PATCH 01/12] =?UTF-8?q?refactor:=20modal=20styles=20=EB=B6=84?= =?UTF-8?q?=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/group/CompletedGroupModal.tsx | 23 +------------------- src/components/group/Modal.styles.ts | 23 ++++++++++++++++++++ src/components/group/MyGroupModal.tsx | 23 +------------------- src/pages/groupSearch/GroupSearch.tsx | 12 ++++++++++ src/pages/index.tsx | 2 ++ 5 files changed, 39 insertions(+), 44 deletions(-) create mode 100644 src/components/group/Modal.styles.ts create mode 100644 src/pages/groupSearch/GroupSearch.tsx diff --git a/src/components/group/CompletedGroupModal.tsx b/src/components/group/CompletedGroupModal.tsx index 64af830c..968e2c37 100644 --- a/src/components/group/CompletedGroupModal.tsx +++ b/src/components/group/CompletedGroupModal.tsx @@ -3,6 +3,7 @@ import leftArrow from '../../assets/common/leftArrow.svg'; import type { Group } from './MyGroupBox'; import { GroupCard } from './GroupCard'; import TitleHeader from '../common/TitleHeader'; +import { Modal, Overlay } from './modal.styles'; interface CompletedGroupModalProps { onClose: () => void; @@ -101,28 +102,6 @@ const CompletedGroupModal = ({ onClose }: CompletedGroupModalProps) => { export default CompletedGroupModal; -const Overlay = styled.div` - display: flex; - justify-content: center; - align-items: flex-start; - position: fixed; - top: 0; - left: 0; - width: 100vw; - height: 100vh; - background: rgba(0, 0, 0, 1); - z-index: 110; -`; - -const Modal = styled.div` - display: flex; - flex-direction: column; - background-color: var(--color-main-black); - width: 100%; - max-width: 767px; - height: 100vh; -`; - const Text = styled.p` font-size: var(--font-size-medium01); font-weight: var(--font-weight-regular); diff --git a/src/components/group/Modal.styles.ts b/src/components/group/Modal.styles.ts new file mode 100644 index 00000000..0c83ae37 --- /dev/null +++ b/src/components/group/Modal.styles.ts @@ -0,0 +1,23 @@ +import styled from '@emotion/styled'; + +export const Overlay = styled.div` + display: flex; + justify-content: center; + align-items: flex-start; + position: fixed; + top: 0; + left: 0; + width: 100vw; + height: 100vh; + background: rgba(0, 0, 0, 1); + z-index: 110; +`; + +export const Modal = styled.div` + display: flex; + flex-direction: column; + background-color: var(--color-main-black); + width: 100%; + max-width: 767px; + height: 100vh; +`; diff --git a/src/components/group/MyGroupModal.tsx b/src/components/group/MyGroupModal.tsx index d636da46..806a81a8 100644 --- a/src/components/group/MyGroupModal.tsx +++ b/src/components/group/MyGroupModal.tsx @@ -4,6 +4,7 @@ import TitleHeader from '../common/TitleHeader'; import leftArrow from '../../assets/common/leftArrow.svg'; import type { Group } from './MyGroupBox'; import { GroupCard } from './GroupCard'; +import { Modal, Overlay } from './modal.styles'; interface MyGroupModalProps { onClose: () => void; @@ -125,28 +126,6 @@ export const MyGroupModal = ({ onClose }: MyGroupModalProps) => { ); }; -const Overlay = styled.div` - display: flex; - justify-content: center; - align-items: flex-start; - position: fixed; - top: 0; - left: 0; - width: 100vw; - height: 100vh; - background: rgba(0, 0, 0, 1); - z-index: 110; -`; - -const Modal = styled.div` - display: flex; - flex-direction: column; - background-color: var(--color-main-black); - width: 100%; - max-width: 767px; - height: 100vh; -`; - const TabContainer = styled.div` display: flex; gap: 8px; diff --git a/src/pages/groupSearch/GroupSearch.tsx b/src/pages/groupSearch/GroupSearch.tsx new file mode 100644 index 00000000..cede4f48 --- /dev/null +++ b/src/pages/groupSearch/GroupSearch.tsx @@ -0,0 +1,12 @@ +import { Modal, Overlay } from '@/components/group/modal.styles'; +import styled from '@emotion/styled'; + +const GroupSearch = () => { + return ( + + + + ); +}; + +export default GroupSearch; diff --git a/src/pages/index.tsx b/src/pages/index.tsx index 20918a3d..3f5eba7c 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -11,6 +11,7 @@ import SignupNickname from './signup/SignupNickname'; import SignupDone from './signup/SignupDone'; import Group from './group/Group'; import Feed from './feed/Feed'; +import GroupSearch from './groupSearch/groupSearch'; const Router = () => { const router = createBrowserRouter( @@ -23,6 +24,7 @@ const Router = () => { } /> } /> + } /> } /> , ), From 4f3948a7267b9c85e1944344b436df6adead63b4 Mon Sep 17 00:00:00 2001 From: Ji Ho June <129824629+ho0010@users.noreply.github.com> Date: Fri, 11 Jul 2025 21:03:08 +0900 Subject: [PATCH 02/12] =?UTF-8?q?design(TitleHeader.tsx):=20height=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/common/TitleHeader.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/common/TitleHeader.tsx b/src/components/common/TitleHeader.tsx index 72c60e9c..255cf953 100644 --- a/src/components/common/TitleHeader.tsx +++ b/src/components/common/TitleHeader.tsx @@ -30,7 +30,7 @@ const NextButton = styled.div<{ active: boolean }>` const InnerHeader = styled.div` position: relative; width: 100%; - height: 56px; + height: 24px; display: flex; flex-direction: row; justify-content: space-between; From 85ae348de6324783bb90cd65283752fb4252c4e1 Mon Sep 17 00:00:00 2001 From: Ji Ho June <129824629+ho0010@users.noreply.github.com> Date: Fri, 11 Jul 2025 22:09:17 +0900 Subject: [PATCH 03/12] feat: assets --- src/assets/searchBar/delete.svg | 4 ++++ src/assets/searchBar/tabDelete.svg | 3 +++ 2 files changed, 7 insertions(+) create mode 100644 src/assets/searchBar/delete.svg create mode 100644 src/assets/searchBar/tabDelete.svg diff --git a/src/assets/searchBar/delete.svg b/src/assets/searchBar/delete.svg new file mode 100644 index 00000000..5838fa55 --- /dev/null +++ b/src/assets/searchBar/delete.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/assets/searchBar/tabDelete.svg b/src/assets/searchBar/tabDelete.svg new file mode 100644 index 00000000..ac5997ee --- /dev/null +++ b/src/assets/searchBar/tabDelete.svg @@ -0,0 +1,3 @@ + + + From ed7bfa557a6209ec65a12e70daf73b48eb72fa97 Mon Sep 17 00:00:00 2001 From: Ji Ho June <129824629+ho0010@users.noreply.github.com> Date: Fri, 11 Jul 2025 22:09:46 +0900 Subject: [PATCH 04/12] feat: RecentSearchTabs --- src/components/common/SearchBar.tsx | 7 ++- src/components/group/CompletedGroupModal.tsx | 2 +- src/components/group/Modal.styles.ts | 5 +- src/components/group/MyGroupModal.tsx | 2 +- src/components/search/RecentSearchTabs.tsx | 62 ++++++++++++++++++++ src/pages/group/Group.tsx | 8 ++- src/pages/groupSearch/GroupSearch.tsx | 38 +++++++++++- 7 files changed, 114 insertions(+), 10 deletions(-) create mode 100644 src/components/search/RecentSearchTabs.tsx diff --git a/src/components/common/SearchBar.tsx b/src/components/common/SearchBar.tsx index 3aea8c77..f8d3bb6e 100644 --- a/src/components/common/SearchBar.tsx +++ b/src/components/common/SearchBar.tsx @@ -1,15 +1,18 @@ import styled from '@emotion/styled'; import searchIcon from '../../assets/searchBar/search.svg'; import { IconButton } from './IconButton'; +import deleteIcon from '../../assets/searchBar/delete.svg'; interface SearchBarProps { placeholder: string; + onClick?: () => void; } -const SearchBar = ({ placeholder }: SearchBarProps) => { +const SearchBar = ({ placeholder, onClick }: SearchBarProps) => { return ( - + + ); diff --git a/src/components/group/CompletedGroupModal.tsx b/src/components/group/CompletedGroupModal.tsx index 968e2c37..1fa31e3c 100644 --- a/src/components/group/CompletedGroupModal.tsx +++ b/src/components/group/CompletedGroupModal.tsx @@ -3,7 +3,7 @@ import leftArrow from '../../assets/common/leftArrow.svg'; import type { Group } from './MyGroupBox'; import { GroupCard } from './GroupCard'; import TitleHeader from '../common/TitleHeader'; -import { Modal, Overlay } from './modal.styles'; +import { Modal, Overlay } from './Modal.styles'; interface CompletedGroupModalProps { onClose: () => void; diff --git a/src/components/group/Modal.styles.ts b/src/components/group/Modal.styles.ts index 0c83ae37..c00958aa 100644 --- a/src/components/group/Modal.styles.ts +++ b/src/components/group/Modal.styles.ts @@ -1,3 +1,4 @@ +import { colors } from '@/styles/global/global'; import styled from '@emotion/styled'; export const Overlay = styled.div` @@ -9,14 +10,14 @@ export const Overlay = styled.div` left: 0; width: 100vw; height: 100vh; - background: rgba(0, 0, 0, 1); + background: ${colors.black.main}; z-index: 110; `; export const Modal = styled.div` display: flex; flex-direction: column; - background-color: var(--color-main-black); + background: ${colors.black.main}; width: 100%; max-width: 767px; height: 100vh; diff --git a/src/components/group/MyGroupModal.tsx b/src/components/group/MyGroupModal.tsx index 806a81a8..c729e1f8 100644 --- a/src/components/group/MyGroupModal.tsx +++ b/src/components/group/MyGroupModal.tsx @@ -4,7 +4,7 @@ import TitleHeader from '../common/TitleHeader'; import leftArrow from '../../assets/common/leftArrow.svg'; import type { Group } from './MyGroupBox'; import { GroupCard } from './GroupCard'; -import { Modal, Overlay } from './modal.styles'; +import { Modal, Overlay } from './Modal.styles'; interface MyGroupModalProps { onClose: () => void; diff --git a/src/components/search/RecentSearchTabs.tsx b/src/components/search/RecentSearchTabs.tsx new file mode 100644 index 00000000..e9a18146 --- /dev/null +++ b/src/components/search/RecentSearchTabs.tsx @@ -0,0 +1,62 @@ +import { colors, typography } from '@/styles/global/global'; +import styled from '@emotion/styled'; +import tabDeleteIcon from '../../assets/searchBar/tabDelete.svg'; +import { IconButton } from '../common/IconButton'; + +interface RecentSearchTabsProps { + recentSearches: string[]; + handleDelete: (term: string) => void; +} + +const RecentSearchTabs = ({ recentSearches, handleDelete }: RecentSearchTabsProps) => { + return ( + + 최근 검색어 + + {recentSearches.map(recentSearch => ( + + {recentSearch} + handleDelete(recentSearch)} /> + + ))} + + + ); +}; + +export default RecentSearchTabs; + +const Container = styled.div` + display: flex; + flex-direction: column; + gap: 16px; + margin: 0 20px; +`; + +const Title = styled.h2` + color: ${colors.grey[100]}; + font-size: ${typography.fontSize.lg}; + font-weight: ${typography.fontWeight.semibold}; +`; + +const TabContainer = styled.div` + display: flex; + gap: 16px; + flex-wrap: wrap; +`; + +const Tab = styled.span` + display: flex; + justify-content: space-between; + border: 1px solid ${colors.grey[300]}; + border-radius: 20px; + padding: 8px 12px; +`; + +const Text = styled.p` + color: ${colors.grey[200]}; + font-size: ${typography.fontSize.sm}; + font-weight: ${typography.fontWeight.regular}; + text-align: center; + line-height: 24px; +`; diff --git a/src/pages/group/Group.tsx b/src/pages/group/Group.tsx index 97261dca..13387333 100644 --- a/src/pages/group/Group.tsx +++ b/src/pages/group/Group.tsx @@ -9,6 +9,7 @@ import { RecruitingGroupCarousel, type Section } from '@/components/group/Recrui import { useState } from 'react'; import { MyGroupModal } from '@/components/group/MyGroupModal'; import CompletedGroupModal from '@/components/group/CompletedGroupModal'; +import { useNavigate } from 'react-router-dom'; const dummyMyGroups: GroupType[] = [ { @@ -116,6 +117,7 @@ const sections: Section[] = [ ]; const Group = () => { + const navigate = useNavigate(); const [isMyGroupModalOpen, setIsMyGroupModalOpen] = useState(false); const [isCompletedGroupModalOpen, setIsCompletedGroupModalOpen] = useState(false); @@ -124,12 +126,16 @@ const Group = () => { const openCompletedGroupModal = () => setIsCompletedGroupModalOpen(true); const closeCompletedGroupModal = () => setIsCompletedGroupModalOpen(false); + + const handleSearchBarClick = () => { + navigate('/groupSearch'); + }; return ( {isMyGroupModalOpen && } {isCompletedGroupModalOpen && } - + diff --git a/src/pages/groupSearch/GroupSearch.tsx b/src/pages/groupSearch/GroupSearch.tsx index cede4f48..00501db8 100644 --- a/src/pages/groupSearch/GroupSearch.tsx +++ b/src/pages/groupSearch/GroupSearch.tsx @@ -1,10 +1,42 @@ -import { Modal, Overlay } from '@/components/group/modal.styles'; -import styled from '@emotion/styled'; +import TitleHeader from '@/components/common/TitleHeader'; +import { Modal, Overlay } from '@/components/group/Modal.styles'; +import leftArrow from '../../assets/common/leftArrow.svg'; +import { useNavigate } from 'react-router-dom'; +import SearchBar from '@/components/common/SearchBar'; +import { useState } from 'react'; +import RecentSearchTabs from '@/components/search/RecentSearchTabs'; const GroupSearch = () => { + const navigate = useNavigate(); + const [recentSearches, setRecentSearches] = useState([ + '딸기12', + '당근', + '수박245', + '참', + '메론1', + ]); + + const handleDelete = (recentSearch: string) => { + setRecentSearches(prev => prev.filter(t => t !== recentSearch)); + }; + + const handleBackButton = () => { + navigate('/group'); + }; return ( - + + } + onLeftClick={handleBackButton} + /> + + + ); }; From 4e9326c9be1fc63263a76a2771e22c6e76e3d877 Mon Sep 17 00:00:00 2001 From: Ji Ho June <129824629+ho0010@users.noreply.github.com> Date: Fri, 11 Jul 2025 22:22:44 +0900 Subject: [PATCH 05/12] =?UTF-8?q?feat:=20GroupSearch=EC=97=90=20SearchBar?= =?UTF-8?q?=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/common/SearchBar.tsx | 51 ----------------- src/components/search/SearchBar.tsx | 80 +++++++++++++++++++++++++++ src/pages/group/Group.tsx | 2 +- src/pages/groupSearch/GroupSearch.tsx | 20 ++++++- 4 files changed, 99 insertions(+), 54 deletions(-) delete mode 100644 src/components/common/SearchBar.tsx create mode 100644 src/components/search/SearchBar.tsx diff --git a/src/components/common/SearchBar.tsx b/src/components/common/SearchBar.tsx deleted file mode 100644 index f8d3bb6e..00000000 --- a/src/components/common/SearchBar.tsx +++ /dev/null @@ -1,51 +0,0 @@ -import styled from '@emotion/styled'; -import searchIcon from '../../assets/searchBar/search.svg'; -import { IconButton } from './IconButton'; -import deleteIcon from '../../assets/searchBar/delete.svg'; - -interface SearchBarProps { - placeholder: string; - onClick?: () => void; -} - -const SearchBar = ({ placeholder, onClick }: SearchBarProps) => { - return ( - - - - - - ); -}; - -export default SearchBar; - -const SearchBarWrapper = styled.div` - display: flex; - align-items: center; - width: 100%; - height: 40px; - background-color: var(--color-darkgrey-main); - border-radius: 12px; - width: calc(100% - 40px); - margin: 76px 20px 16px 20px; - padding: 16px 20px; - box-sizing: border-box; -`; - -const Input = styled.input` - flex: 1; - border: none; - background: transparent; - color: #ffffff; - font-size: 14px; - padding: 8px 12px; - box-sizing: border-box; - - &::placeholder { - color: var(--color-grey-300); - } - &:focus { - outline: none; - } -`; diff --git a/src/components/search/SearchBar.tsx b/src/components/search/SearchBar.tsx new file mode 100644 index 00000000..2678664b --- /dev/null +++ b/src/components/search/SearchBar.tsx @@ -0,0 +1,80 @@ +import styled from '@emotion/styled'; +import searchIcon from '../../assets/searchBar/search.svg'; + +import deleteIcon from '../../assets/searchBar/delete.svg'; +import { IconButton } from '../common/IconButton'; + +interface SearchBarProps { + placeholder?: string; + value: string; + onChange: (v: string) => void; + onClick?: () => void; + onSearch?: () => void; +} + +const SearchBar = ({ placeholder, value, onChange, onClick, onSearch }: SearchBarProps) => { + return ( + + onChange(e.target.value)} + onKeyDown={e => { + if (e.key === 'Enter') { + onSearch?.(); + } + }} + /> + + {value && onChange('')} />} + onSearch?.()} /> + + + ); +}; + +export default SearchBar; + +const SearchBarWrapper = styled.div` + display: flex; + align-items: center; + width: 100%; + height: 40px; + background-color: var(--color-darkgrey-main); + border-radius: 12px; + width: calc(100% - 40px); + margin: 76px 20px 16px 20px; + padding: 16px 20px; + box-sizing: border-box; + + .delete-btn { + opacity: 0; + pointer-events: none; + transition: opacity 0.2s; + } + &:focus-within .delete-btn { + opacity: 1; + pointer-events: auto; + } +`; + +const Input = styled.input` + flex: 1; + border: none; + background: transparent; + color: #ffffff; + font-size: 14px; + padding: 8px 12px; + box-sizing: border-box; + + &::placeholder { + color: var(--color-grey-300); + } + &:focus { + outline: none; + } +`; +const IconButtonContainer = styled.div` + display: flex; + gap: 20px; +`; diff --git a/src/pages/group/Group.tsx b/src/pages/group/Group.tsx index 13387333..c8b5a79d 100644 --- a/src/pages/group/Group.tsx +++ b/src/pages/group/Group.tsx @@ -1,6 +1,6 @@ import MainHeader from '@/components/common/MainHeader'; import NavBar from '@/components/common/NavBar'; -import SearchBar from '@/components/common/SearchBar'; +import SearchBar from '@/components/search/SearchBar'; import type { Group as GroupType } from '@/components/group/MyGroupBox'; import { MyGroupBox } from '../../components/group/MyGroupBox'; import Blank from '@/components/common/Blank'; diff --git a/src/pages/groupSearch/GroupSearch.tsx b/src/pages/groupSearch/GroupSearch.tsx index 00501db8..c89a2616 100644 --- a/src/pages/groupSearch/GroupSearch.tsx +++ b/src/pages/groupSearch/GroupSearch.tsx @@ -2,12 +2,14 @@ import TitleHeader from '@/components/common/TitleHeader'; import { Modal, Overlay } from '@/components/group/Modal.styles'; import leftArrow from '../../assets/common/leftArrow.svg'; import { useNavigate } from 'react-router-dom'; -import SearchBar from '@/components/common/SearchBar'; +import SearchBar from '@/components/search/SearchBar'; import { useState } from 'react'; import RecentSearchTabs from '@/components/search/RecentSearchTabs'; const GroupSearch = () => { const navigate = useNavigate(); + const [searchTerm, setSearchTerm] = useState(''); + const [recentSearches, setRecentSearches] = useState([ '딸기12', '당근', @@ -16,6 +18,13 @@ const GroupSearch = () => { '메론1', ]); + const handleSearch = (term: string) => { + 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)); }; @@ -31,7 +40,14 @@ const GroupSearch = () => { leftIcon={뒤로 가기} onLeftClick={handleBackButton} /> - + { + if (searchTerm.trim()) handleSearch(searchTerm.trim()); + }} + />{' '} Date: Fri, 11 Jul 2025 22:36:22 +0900 Subject: [PATCH 06/12] =?UTF-8?q?feat(RecentSearchBar.tsx):=20recentSearch?= =?UTF-8?q?es=20=EA=B0=92=EC=9D=B4=20=EC=97=86=EC=9D=84=20=EB=95=8C=20?= =?UTF-8?q?=EC=98=88=EC=99=B8=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/search/RecentSearchTabs.tsx | 16 ++++++++++------ src/components/search/SearchBar.tsx | 8 ++++---- src/pages/groupSearch/GroupSearch.tsx | 2 +- 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/src/components/search/RecentSearchTabs.tsx b/src/components/search/RecentSearchTabs.tsx index e9a18146..b3950f87 100644 --- a/src/components/search/RecentSearchTabs.tsx +++ b/src/components/search/RecentSearchTabs.tsx @@ -13,12 +13,16 @@ const RecentSearchTabs = ({ recentSearches, handleDelete }: RecentSearchTabsProp 최근 검색어 - {recentSearches.map(recentSearch => ( - - {recentSearch} - handleDelete(recentSearch)} /> - - ))} + {recentSearches.length === 0 ? ( + 최근 검색어가 아직 없어요. + ) : ( + recentSearches.map(recentSearch => ( + + {recentSearch} + handleDelete(recentSearch)} /> + + )) + )} ); diff --git a/src/components/search/SearchBar.tsx b/src/components/search/SearchBar.tsx index 2678664b..2a70fa96 100644 --- a/src/components/search/SearchBar.tsx +++ b/src/components/search/SearchBar.tsx @@ -6,8 +6,8 @@ import { IconButton } from '../common/IconButton'; interface SearchBarProps { placeholder?: string; - value: string; - onChange: (v: string) => void; + value?: string; + onChange?: (v: string) => void; onClick?: () => void; onSearch?: () => void; } @@ -18,7 +18,7 @@ const SearchBar = ({ placeholder, value, onChange, onClick, onSearch }: SearchBa onChange(e.target.value)} + onChange={e => onChange?.(e.target.value)} onKeyDown={e => { if (e.key === 'Enter') { onSearch?.(); @@ -26,7 +26,7 @@ const SearchBar = ({ placeholder, value, onChange, onClick, onSearch }: SearchBa }} /> - {value && onChange('')} />} + {value && onChange?.('')} />} onSearch?.()} /> diff --git a/src/pages/groupSearch/GroupSearch.tsx b/src/pages/groupSearch/GroupSearch.tsx index c89a2616..eba466ea 100644 --- a/src/pages/groupSearch/GroupSearch.tsx +++ b/src/pages/groupSearch/GroupSearch.tsx @@ -47,7 +47,7 @@ const GroupSearch = () => { onSearch={() => { if (searchTerm.trim()) handleSearch(searchTerm.trim()); }} - />{' '} + /> Date: Fri, 11 Jul 2025 22:47:19 +0900 Subject: [PATCH 07/12] feat(RecentSearchTabs.tsx): tab click event --- src/components/search/RecentSearchTabs.tsx | 9 +++++++-- src/components/search/SearchBar.tsx | 2 +- src/pages/groupSearch/GroupSearch.tsx | 5 +++++ 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/components/search/RecentSearchTabs.tsx b/src/components/search/RecentSearchTabs.tsx index b3950f87..8ee0859e 100644 --- a/src/components/search/RecentSearchTabs.tsx +++ b/src/components/search/RecentSearchTabs.tsx @@ -6,9 +6,14 @@ import { IconButton } from '../common/IconButton'; interface RecentSearchTabsProps { recentSearches: string[]; handleDelete: (term: string) => void; + handleRecentSearchClick: (term: string) => void; } -const RecentSearchTabs = ({ recentSearches, handleDelete }: RecentSearchTabsProps) => { +const RecentSearchTabs = ({ + recentSearches, + handleDelete, + handleRecentSearchClick, +}: RecentSearchTabsProps) => { return ( 최근 검색어 @@ -18,7 +23,7 @@ const RecentSearchTabs = ({ recentSearches, handleDelete }: RecentSearchTabsProp ) : ( recentSearches.map(recentSearch => ( - {recentSearch} + handleRecentSearchClick(recentSearch)}>{recentSearch} handleDelete(recentSearch)} /> )) diff --git a/src/components/search/SearchBar.tsx b/src/components/search/SearchBar.tsx index 2a70fa96..0d6db926 100644 --- a/src/components/search/SearchBar.tsx +++ b/src/components/search/SearchBar.tsx @@ -64,7 +64,7 @@ const Input = styled.input` background: transparent; color: #ffffff; font-size: 14px; - padding: 8px 12px; + padding: 8px 0; box-sizing: border-box; &::placeholder { diff --git a/src/pages/groupSearch/GroupSearch.tsx b/src/pages/groupSearch/GroupSearch.tsx index eba466ea..15ee79fd 100644 --- a/src/pages/groupSearch/GroupSearch.tsx +++ b/src/pages/groupSearch/GroupSearch.tsx @@ -29,6 +29,10 @@ const GroupSearch = () => { setRecentSearches(prev => prev.filter(t => t !== recentSearch)); }; + const handleRecentSearchClick = (recentSearch: string) => { + setSearchTerm(recentSearch); + }; + const handleBackButton = () => { navigate('/group'); }; @@ -51,6 +55,7 @@ const GroupSearch = () => { From b2b837fb493c441024ae2ffae8c44e10f5086cfd Mon Sep 17 00:00:00 2001 From: Ji Ho June <129824629+ho0010@users.noreply.github.com> Date: Sat, 12 Jul 2025 16:44:25 +0900 Subject: [PATCH 08/12] =?UTF-8?q?feat(GroupCard.tsx):=20=ED=99=95=EC=9E=A5?= =?UTF-8?q?=EC=84=B1=20=EA=B3=A0=EB=A0=A4=20type=20=EB=8F=84=EC=9E=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/group/GroupCard.tsx | 75 ++++++++++++++++-------------- 1 file changed, 41 insertions(+), 34 deletions(-) diff --git a/src/components/group/GroupCard.tsx b/src/components/group/GroupCard.tsx index 6fca81f4..f12b94d3 100644 --- a/src/components/group/GroupCard.tsx +++ b/src/components/group/GroupCard.tsx @@ -2,53 +2,60 @@ import { forwardRef } from 'react'; import styled from '@emotion/styled'; import peopleIcon from '@/assets/common/darkPeople.svg'; import type { Group } from './MyGroupBox'; +import { colors, typography } from '@/styles/global/global'; interface Props { group: Group; - type?: 'recruiting' | 'ongoing'; + isOngoing?: boolean; + type?: 'main' | 'search' | 'modal'; } -export const GroupCard = forwardRef(({ group, type }, ref) => { - return ( - - - - {group.title} - - - people -

{group.participants}

- / {group.maximumParticipants}명 -
- {type === 'recruiting' ? ( - {group.deadLine}일 뒤 모집 마감 - ) : ( - {group.deadLine}일 뒤 종료 - )} -
-
-
- ); -}); +export const GroupCard = forwardRef( + ({ group, isOngoing, type = 'main' }, ref) => { + return ( + + + + {group.title} + + + people +

{group.participants}

+ / {group.maximumParticipants}명 +
+ {isOngoing === true ? ( + {group.deadLine}일 뒤 모집 마감 + ) : ( + {group.deadLine}일 뒤 종료 + )} +
+
+
+ ); + }, +); -const Card = styled.div` +const Card = styled.div<{ cardType: 'main' | 'search' | 'modal' }>` display: flex; align-items: center; - background: var(--color-darkgrey-main); - border-radius: 12px; + background: ${({ cardType }) => + cardType === 'search' ? 'var(--color-black-main)' : 'var(--color-darkgrey-main)'}; + border-radius: ${({ cardType }) => (cardType === 'search' ? `none` : '12px')}; box-sizing: border-box; - padding: 12px; + padding: ${({ cardType }) => (cardType === 'search' ? '24px 12px 12px 12px' : '12px')}; gap: 12px; width: 100%; - border: 1px solid var(--color-grey-300); + border: ${({ cardType }) => (cardType === 'main' ? '1px solid var(--color-grey-300)' : 'none')}; + border-top: ${({ cardType }) => + cardType === 'search' ? `1px solid ${colors.darkgrey.dark}` : ''}; `; -const Cover = styled.img` - width: 80px; - height: 107px; - border-radius: 6px; +const Cover = styled.img<{ cardType: 'main' | 'search' | 'modal' }>` object-fit: cover; flex-shrink: 0; + + width: ${({ cardType }) => (cardType === 'search' ? '60px' : '80px')}; + height: ${({ cardType }) => (cardType === 'search' ? '80px' : '107px')}; `; const Info = styled.div` @@ -59,8 +66,8 @@ const Info = styled.div` `; const Title = styled.h3` - font-size: var(--font-size-large01); - font-weight: var(--font-weight-bold); + font-size: ${typography.fontSize.lg}; + font-weight: ${typography.fontWeight.semibold}; color: #ffffff; margin-bottom: 10px; line-height: 1.4; From c6229f6d48520bd4093e9d59949a0444c3db4abf Mon Sep 17 00:00:00 2001 From: Ji Ho June <129824629+ho0010@users.noreply.github.com> Date: Sat, 12 Jul 2025 16:45:11 +0900 Subject: [PATCH 09/12] =?UTF-8?q?feat:=20GroupCard=20=EB=B3=80=EA=B2=BD?= =?UTF-8?q?=EC=97=90=20=EB=94=B0=EB=A5=B8=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/group/CompletedGroupModal.tsx | 2 +- src/components/group/MyGroupModal.tsx | 3 ++- src/components/group/RecruitingGroupBox.tsx | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/components/group/CompletedGroupModal.tsx b/src/components/group/CompletedGroupModal.tsx index 1fa31e3c..b0954499 100644 --- a/src/components/group/CompletedGroupModal.tsx +++ b/src/components/group/CompletedGroupModal.tsx @@ -92,7 +92,7 @@ const CompletedGroupModal = ({ onClose }: CompletedGroupModalProps) => { {userName}님이 참여했던 모임방들을 확인해보세요. {dummyCompletedGroups.map(group => ( - + ))} diff --git a/src/components/group/MyGroupModal.tsx b/src/components/group/MyGroupModal.tsx index c729e1f8..dc036d90 100644 --- a/src/components/group/MyGroupModal.tsx +++ b/src/components/group/MyGroupModal.tsx @@ -117,7 +117,8 @@ export const MyGroupModal = ({ onClose }: MyGroupModalProps) => { ))} diff --git a/src/components/group/RecruitingGroupBox.tsx b/src/components/group/RecruitingGroupBox.tsx index 2ca0c816..2b8c134b 100644 --- a/src/components/group/RecruitingGroupBox.tsx +++ b/src/components/group/RecruitingGroupBox.tsx @@ -27,7 +27,7 @@ export function RecruitingGroupBox({ groups, title }: Props) { {filtered.map(group => ( - + ))}
From c3469bfe382006da1bc6b9e74c787a3f68b98f86 Mon Sep 17 00:00:00 2001 From: Ji Ho June <129824629+ho0010@users.noreply.github.com> Date: Sat, 12 Jul 2025 17:33:09 +0900 Subject: [PATCH 10/12] feat: Filter --- src/assets/filter/filterDown.svg | 3 ++ src/assets/filter/filterUp.svg | 3 ++ src/components/common/Filter.tsx | 79 ++++++++++++++++++++++++++++++++ 3 files changed, 85 insertions(+) create mode 100644 src/assets/filter/filterDown.svg create mode 100644 src/assets/filter/filterUp.svg create mode 100644 src/components/common/Filter.tsx diff --git a/src/assets/filter/filterDown.svg b/src/assets/filter/filterDown.svg new file mode 100644 index 00000000..d586d99e --- /dev/null +++ b/src/assets/filter/filterDown.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/filter/filterUp.svg b/src/assets/filter/filterUp.svg new file mode 100644 index 00000000..1f06b9c4 --- /dev/null +++ b/src/assets/filter/filterUp.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/components/common/Filter.tsx b/src/components/common/Filter.tsx new file mode 100644 index 00000000..7a64fa1c --- /dev/null +++ b/src/components/common/Filter.tsx @@ -0,0 +1,79 @@ +import { colors, typography } from '@/styles/global/global'; +import styled from '@emotion/styled'; +import { useState } from 'react'; +import { IconButton } from './IconButton'; +import downImg from '../../assets/filter/filterDown.svg'; +import upImg from '../../assets/filter/filterUp.svg'; + +interface FilterProps { + filters: string[]; + selectedFilter: string; + setSelectedFilter: (filter: string) => void; +} + +export const Filter = ({ filters, selectedFilter, setSelectedFilter }: FilterProps) => { + const [isOpenModal, setIsOpenModal] = useState(false); + + const handleFilterClick = (filter: string) => { + setIsOpenModal(false); + setSelectedFilter(filter); + }; + + const handleModalClick = () => { + if (!isOpenModal) { + setIsOpenModal(true); + } + }; + + return ( + + {selectedFilter} + + {isOpenModal ? ( + + {filters.map(filter => + filter === selectedFilter ? ( + handleFilterClick(filter)}>{filter} + ) : ( + handleFilterClick(filter)}>{filter} + ), + )} + + ) : ( + <> + )} + + ); +}; + +const Container = styled.div` + display: flex; + position: relative; + justify-content: center; + align-items: center; + width: 85px; +`; + +const Text = styled.p` + color: ${colors.grey[200]}; + font-size: ${typography.fontSize.sm}; + font-weight: ${typography.fontWeight.regular}; +`; + +const SelectedText = styled.p` + color: ${colors.white}; + font-size: ${typography.fontSize.sm}; + font-weight: ${typography.fontWeight.regular}; +`; + +const Modal = styled.div` + display: flex; + flex-direction: column; + gap: 16px; + padding: 12px 20px; + position: absolute; + top: 30px; + left: -20px; + border: solid 1px ${colors.grey[200]}; + border-radius: 16px; +`; From a23d8cf641054d364e898bb12e6d14755b51ebb5 Mon Sep 17 00:00:00 2001 From: Ji Ho June <129824629+ho0010@users.noreply.github.com> Date: Sat, 12 Jul 2025 17:37:40 +0900 Subject: [PATCH 11/12] feat: SearchResult --- src/components/common/Filter.tsx | 1 + src/components/search/SearchResult.tsx | 173 +++++++++++++++++++++++++ src/pages/groupSearch/GroupSearch.tsx | 18 ++- 3 files changed, 187 insertions(+), 5 deletions(-) create mode 100644 src/components/search/SearchResult.tsx diff --git a/src/components/common/Filter.tsx b/src/components/common/Filter.tsx index 7a64fa1c..6afab5f2 100644 --- a/src/components/common/Filter.tsx +++ b/src/components/common/Filter.tsx @@ -76,4 +76,5 @@ const Modal = styled.div` left: -20px; border: solid 1px ${colors.grey[200]}; border-radius: 16px; + background: ${colors.black.main}; `; diff --git a/src/components/search/SearchResult.tsx b/src/components/search/SearchResult.tsx new file mode 100644 index 00000000..df92ecb8 --- /dev/null +++ b/src/components/search/SearchResult.tsx @@ -0,0 +1,173 @@ +import styled from '@emotion/styled'; +import { useState } from 'react'; +import type { Group } from '../group/MyGroupBox'; +import { GroupCard } from '../group/GroupCard'; +import { colors, typography } from '@/styles/global/global'; +import { Filter } from '../common/Filter'; + +const GENRE = ['문학', '과학·IT', '사회과학', '인문학', '예술']; + +const FILTER = ['마감임박순', '인기순']; + +const dummyMyGroups: Group[] = [ + { + id: '1', + title: '시집만 읽는 사람들 3월', + userName: 'hoho2', + participants: 15, + maximumParticipants: 30, + coverUrl: + 'https://marketplace.canva.com/EAF9zlwqylI/1/0/1003w/canva-%EB%B2%A0%EC%9D%B4%EC%A7%80-%EC%A3%BC%ED%99%A9-%EA%B7%80%EC%97%BD%EA%B3%A0-%EB%AF%B8%EB%8B%88%EB%A9%80%ED%95%9C-%EC%9D%BC%EB%9F%AC%EC%8A%A4%ED%8A%B8-e%EB%B6%81-%EC%9C%84%EB%A1%9C-%EC%A2%8B%EC%9D%80%EA%B8%80-%EC%B1%85%ED%91%9C%EC%A7%80-zrZ6hI8_IWo.jpg', + deadLine: 1, + genre: '문학', + isOnGoing: true, + }, + { + id: '2', + title: '시집만 읽는 사람들 3월', + userName: 'hoho2', + participants: 15, + maximumParticipants: 30, + coverUrl: + 'https://marketplace.canva.com/EAF9zlwqylI/1/0/1003w/canva-%EB%B2%A0%EC%9D%B4%EC%A7%80-%EC%A3%BC%ED%99%A9-%EA%B7%80%EC%97%BD%EA%B3%A0-%EB%AF%B8%EB%8B%88%EB%A9%80%ED%95%9C-%EC%9D%BC%EB%9F%AC%EC%8A%A4%ED%8A%B8-e%EB%B6%81-%EC%9C%84%EB%A1%9C-%EC%A2%8B%EC%9D%80%EA%B8%80-%EC%B1%85%ED%91%9C%EC%A7%80-zrZ6hI8_IWo.jpg', + deadLine: 2, + genre: '문학', + isOnGoing: true, + }, + { + id: '3', + title: '시집만 읽는 사람들 3월', + userName: 'hoho2', + participants: 15, + maximumParticipants: 30, + coverUrl: + 'https://marketplace.canva.com/EAF9zlwqylI/1/0/1003w/canva-%EB%B2%A0%EC%9D%B4%EC%A7%80-%EC%A3%BC%ED%99%A9-%EA%B7%80%EC%97%BD%EA%B3%A0-%EB%AF%B8%EB%8B%88%EB%A9%80%ED%95%9C-%EC%9D%BC%EB%9F%AC%EC%8A%A4%ED%8A%B8-e%EB%B6%81-%EC%9C%84%EB%A1%9C-%EC%A2%8B%EC%9D%80%EA%B8%80-%EC%B1%85%ED%91%9C%EC%A7%80-zrZ6hI8_IWo.jpg', + deadLine: 3, + genre: '문학', + isOnGoing: true, + }, + { + id: '4', + title: '시집만 읽는 사람들 3월', + userName: 'hoho2', + participants: 15, + maximumParticipants: 30, + coverUrl: + 'https://marketplace.canva.com/EAF9zlwqylI/1/0/1003w/canva-%EB%B2%A0%EC%9D%B4%EC%A7%80-%EC%A3%BC%ED%99%A9-%EA%B7%80%EC%97%BD%EA%B3%A0-%EB%AF%B8%EB%8B%88%EB%A9%80%ED%95%9C-%EC%9D%BC%EB%9F%AC%EC%8A%A4%ED%8A%B8-e%EB%B6%81-%EC%9C%84%EB%A1%9C-%EC%A2%8B%EC%9D%80%EA%B8%80-%EC%B1%85%ED%91%9C%EC%A7%80-zrZ6hI8_IWo.jpg', + deadLine: 4, + genre: '문학', + isOnGoing: true, + }, + { + id: '5', + title: '시집만 읽는 사람들 3월', + userName: 'hoho2', + participants: 15, + maximumParticipants: 30, + coverUrl: + 'https://marketplace.canva.com/EAF9zlwqylI/1/0/1003w/canva-%EB%B2%A0%EC%9D%B4%EC%A7%80-%EC%A3%BC%ED%99%A9-%EA%B7%80%EC%97%BD%EA%B3%A0-%EB%AF%B8%EB%8B%88%EB%A9%80%ED%95%9C-%EC%9D%BC%EB%9F%AC%EC%8A%A4%ED%8A%B8-e%EB%B6%81-%EC%9C%84%EB%A1%9C-%EC%A2%8B%EC%9D%80%EA%B8%80-%EC%B1%85%ED%91%9C%EC%A7%80-zrZ6hI8_IWo.jpg', + deadLine: 4, + genre: '과학·IT', + isOnGoing: false, + }, + { + id: '6', + title: '시집만 읽는 사람들 3월', + userName: 'hoho2', + participants: 15, + maximumParticipants: 30, + coverUrl: + 'https://marketplace.canva.com/EAF9zlwqylI/1/0/1003w/canva-%EB%B2%A0%EC%9D%B4%EC%A7%80-%EC%A3%BC%ED%99%A9-%EA%B7%80%EC%97%BD%EA%B3%A0-%EB%AF%B8%EB%8B%88%EB%A9%80%ED%95%9C-%EC%9D%BC%EB%9F%AC%EC%8A%A4%ED%8A%B8-e%EB%B6%81-%EC%9C%84%EB%A1%9C-%EC%A2%8B%EC%9D%80%EA%B8%80-%EC%B1%85%ED%91%9C%EC%A7%80-zrZ6hI8_IWo.jpg', + deadLine: 4, + genre: '과학·IT', + isOnGoing: false, + }, +]; + +const SearchResult = () => { + const [selected, setSelected] = useState(''); + const [showGroup, setShowGroup] = useState(dummyMyGroups); + const [selectedFilter, setSelectedFilter] = useState('마감임박순'); + + const handleSelectTab = (tab: string) => { + if (selected === tab) { + setSelected(''); + } else setSelected(tab); + }; + + return ( + <> + + {GENRE.map(tab => ( + handleSelectTab(tab)}> + {tab} + + ))} + + + 전체 {showGroup.length} + + + + {showGroup.map(group => ( + + ))} + + + ); +}; + +export default SearchResult; + +const TabContainer = styled.div` + display: flex; + flex-wrap: wrap; + gap: 12px; + padding: 0 20px; + margin-bottom: 24px; +`; + +const Tab = styled.button<{ selected?: boolean }>` + white-space: nowrap; + padding: 8px 12px; + font-size: ${typography.fontSize.xs}; + font-weight: ${typography.fontWeight.regular}; + border: none; + border-radius: 16px; + background: ${({ selected }) => + selected ? 'var(--color-purple-main)' : 'var(--color-darkgrey-main)'}; + color: #fff; + cursor: pointer; +`; + +const Content = styled.div` + display: flex; + flex-direction: column; + gap: 20px; + overflow-y: auto; + padding: 0 20px; +`; + +const GroupCardHeader = styled.div` + display: flex; + justify-content: space-between; + padding: 0 20px; + margin-bottom: 10px; +`; + +const GroupNum = styled.span` + display: flex; + align-items: center; + color: ${colors.grey[100]}; + font-size: ${typography.fontSize.sm}; + font-weight: ${typography.fontWeight.medium}; +`; diff --git a/src/pages/groupSearch/GroupSearch.tsx b/src/pages/groupSearch/GroupSearch.tsx index 15ee79fd..c1d73dad 100644 --- a/src/pages/groupSearch/GroupSearch.tsx +++ b/src/pages/groupSearch/GroupSearch.tsx @@ -5,10 +5,12 @@ 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'; const GroupSearch = () => { const navigate = useNavigate(); const [searchTerm, setSearchTerm] = useState(''); + const [isSearching, setIsSearching] = useState(false); const [recentSearches, setRecentSearches] = useState([ '딸기12', @@ -19,6 +21,8 @@ const GroupSearch = () => { ]); const handleSearch = (term: string) => { + setIsSearching(true); + setRecentSearches(prev => { const filtered = prev.filter(t => t !== term); return [term, ...filtered].slice(0, 5); @@ -52,11 +56,15 @@ const GroupSearch = () => { if (searchTerm.trim()) handleSearch(searchTerm.trim()); }} /> - + {isSearching ? ( + + ) : ( + + )} ); From 24633578688b7d0df87825e6058043aec6096f41 Mon Sep 17 00:00:00 2001 From: Ji Ho June <129824629+ho0010@users.noreply.github.com> Date: Sat, 12 Jul 2025 17:51:29 +0900 Subject: [PATCH 12/12] =?UTF-8?q?feat(SearchResult.tsx):=20showGroup?= =?UTF-8?q?=EC=9D=B4=20=EC=97=86=EC=9D=84=EB=95=8C=20Text?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/search/SearchResult.tsx | 53 ++++++++++++++++++++++---- 1 file changed, 45 insertions(+), 8 deletions(-) diff --git a/src/components/search/SearchResult.tsx b/src/components/search/SearchResult.tsx index df92ecb8..c65d3a11 100644 --- a/src/components/search/SearchResult.tsx +++ b/src/components/search/SearchResult.tsx @@ -95,6 +95,12 @@ const SearchResult = () => { } else setSelected(tab); }; + const isEmptyShowGroup = () => { + if (showGroup.length === 0) { + return true; + } else return false; + }; + return ( <> @@ -113,14 +119,21 @@ const SearchResult = () => { > - {showGroup.map(group => ( - - ))} + {isEmptyShowGroup() ? ( + + 해당하는 모임방이 없어요 + 직접 모임방을 만들어보세요. + + ) : ( + showGroup.map(group => ( + + )) + )} ); @@ -171,3 +184,27 @@ const GroupNum = styled.span` font-size: ${typography.fontSize.sm}; font-weight: ${typography.fontWeight.medium}; `; + +const EmptyContent = styled.div` + display: flex; + height: 100vh; + flex-direction: column; + justify-content: center; + align-items: center; + gap: 8px; +`; + +const EmptyMainText = styled.p` + color: ${colors.white}; + font-size: ${typography.fontSize.lg}; + font-weight: ${typography.fontWeight.semibold}; + text-align: center; + justify-self: center; +`; +const EmptySubText = styled.p` + color: ${colors.grey[100]}; + font-size: ${typography.fontSize.sm}; + font-weight: ${typography.fontWeight.regular}; + text-align: center; + justify-self: center; +`;