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/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 @@
+
diff --git a/src/components/common/Filter.tsx b/src/components/common/Filter.tsx
new file mode 100644
index 00000000..6afab5f2
--- /dev/null
+++ b/src/components/common/Filter.tsx
@@ -0,0 +1,80 @@
+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;
+ background: ${colors.black.main};
+`;
diff --git a/src/components/common/SearchBar.tsx b/src/components/common/SearchBar.tsx
deleted file mode 100644
index 3aea8c77..00000000
--- a/src/components/common/SearchBar.tsx
+++ /dev/null
@@ -1,48 +0,0 @@
-import styled from '@emotion/styled';
-import searchIcon from '../../assets/searchBar/search.svg';
-import { IconButton } from './IconButton';
-
-interface SearchBarProps {
- placeholder: string;
-}
-
-const SearchBar = ({ placeholder }: 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/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;
diff --git a/src/components/group/CompletedGroupModal.tsx b/src/components/group/CompletedGroupModal.tsx
index 64af830c..b0954499 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;
@@ -91,7 +92,7 @@ const CompletedGroupModal = ({ onClose }: CompletedGroupModalProps) => {
{userName}님이 참여했던 모임방들을 확인해보세요.
{dummyCompletedGroups.map(group => (
-
+
))}
@@ -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/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}
-
-
-
- {group.participants}
- / {group.maximumParticipants}명
-
- {type === 'recruiting' ? (
- {group.deadLine}일 뒤 모집 마감
- ) : (
- {group.deadLine}일 뒤 종료
- )}
-
-
-
- );
-});
+export const GroupCard = forwardRef(
+ ({ group, isOngoing, type = 'main' }, ref) => {
+ return (
+
+
+
+ {group.title}
+
+
+
+ {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;
diff --git a/src/components/group/Modal.styles.ts b/src/components/group/Modal.styles.ts
new file mode 100644
index 00000000..c00958aa
--- /dev/null
+++ b/src/components/group/Modal.styles.ts
@@ -0,0 +1,24 @@
+import { colors } from '@/styles/global/global';
+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: ${colors.black.main};
+ z-index: 110;
+`;
+
+export const Modal = styled.div`
+ display: flex;
+ flex-direction: column;
+ 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 d636da46..dc036d90 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;
@@ -116,7 +117,8 @@ export const MyGroupModal = ({ onClose }: MyGroupModalProps) => {
))}
@@ -125,28 +127,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/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 => (
-
+
))}
diff --git a/src/components/search/RecentSearchTabs.tsx b/src/components/search/RecentSearchTabs.tsx
new file mode 100644
index 00000000..8ee0859e
--- /dev/null
+++ b/src/components/search/RecentSearchTabs.tsx
@@ -0,0 +1,71 @@
+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;
+ handleRecentSearchClick: (term: string) => void;
+}
+
+const RecentSearchTabs = ({
+ recentSearches,
+ handleDelete,
+ handleRecentSearchClick,
+}: RecentSearchTabsProps) => {
+ return (
+
+ 최근 검색어
+
+ {recentSearches.length === 0 ? (
+ 최근 검색어가 아직 없어요.
+ ) : (
+ recentSearches.map(recentSearch => (
+
+ handleRecentSearchClick(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/components/search/SearchBar.tsx b/src/components/search/SearchBar.tsx
new file mode 100644
index 00000000..0d6db926
--- /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 0;
+ 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/components/search/SearchResult.tsx b/src/components/search/SearchResult.tsx
new file mode 100644
index 00000000..c65d3a11
--- /dev/null
+++ b/src/components/search/SearchResult.tsx
@@ -0,0 +1,210 @@
+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);
+ };
+
+ const isEmptyShowGroup = () => {
+ if (showGroup.length === 0) {
+ return true;
+ } else return false;
+ };
+
+ return (
+ <>
+
+ {GENRE.map(tab => (
+ handleSelectTab(tab)}>
+ {tab}
+
+ ))}
+
+
+ 전체 {showGroup.length}
+
+
+
+ {isEmptyShowGroup() ? (
+
+ 해당하는 모임방이 없어요
+ 직접 모임방을 만들어보세요.
+
+ ) : (
+ 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};
+`;
+
+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;
+`;
diff --git a/src/pages/group/Group.tsx b/src/pages/group/Group.tsx
index ca342442..9c69b761 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';
@@ -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';
import makegroupfab from '../../assets/common/makegroupfab.svg';
const dummyMyGroups: GroupType[] = [
@@ -117,6 +118,7 @@ const sections: Section[] = [
];
const Group = () => {
+ const navigate = useNavigate();
const [isMyGroupModalOpen, setIsMyGroupModalOpen] = useState(false);
const [isCompletedGroupModalOpen, setIsCompletedGroupModalOpen] = useState(false);
@@ -125,12 +127,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
new file mode 100644
index 00000000..c1d73dad
--- /dev/null
+++ b/src/pages/groupSearch/GroupSearch.tsx
@@ -0,0 +1,73 @@
+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/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',
+ '당근',
+ '수박245',
+ '참',
+ '메론1',
+ ]);
+
+ const handleSearch = (term: string) => {
+ setIsSearching(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);
+ };
+
+ const handleBackButton = () => {
+ navigate('/group');
+ };
+ return (
+
+
+ }
+ onLeftClick={handleBackButton}
+ />
+ {
+ if (searchTerm.trim()) handleSearch(searchTerm.trim());
+ }}
+ />
+ {isSearching ? (
+
+ ) : (
+
+ )}
+
+
+ );
+};
+
+export default GroupSearch;
diff --git a/src/pages/index.tsx b/src/pages/index.tsx
index 3b2b967d..b5c5db30 100644
--- a/src/pages/index.tsx
+++ b/src/pages/index.tsx
@@ -12,6 +12,7 @@ import SignupDone from './signup/SignupDone';
import CreateGroup from './group/CreateGroup';
import Group from './group/Group';
import Feed from './feed/Feed';
+import GroupSearch from './groupSearch/groupSearch';
const Router = () => {
const router = createBrowserRouter(
@@ -25,6 +26,7 @@ const Router = () => {
} />
} />
} />
+ } />
} />
>,
),