diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 5e2e23096..4f6b76937 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -19,6 +19,8 @@ import PhotoEditTab from '@/pages/AdminPage/tabs/PhotoEditTab/PhotoEditTab'; import ApplicationFormPage from './pages/ApplicationFormPage/ApplicationFormPage'; import ApplicantsTab from './pages/AdminPage/tabs/ApplicantsTab/ApplicantsTab'; import ApplicantDetailPage from './pages/AdminPage/tabs/ApplicantsTab/ApplicantDetailPage/ApplicantDetailPage'; +import ClubUnionPage from './pages/ClubUnionPage/ClubUnionPage'; + const queryClient = new QueryClient(); @@ -72,8 +74,6 @@ const App = () => { path='account-edit' element={} /> - {/*πŸ”’ 메인 λΈŒλžœμΉ˜μ—μ„œλŠ” μ ‘κ·Ό 차단 (배포용 차단 λͺ©μ )*/} - {/*develop-fe λΈŒλžœμΉ˜μ—μ„œλŠ” μ ‘κ·Ό κ°€λŠ₯ν•˜λ„λ‘ ν’€κ³  개발 μ˜ˆμ •*/} } @@ -92,12 +92,11 @@ const App = () => { } /> - {/*πŸ”’ μ‚¬μš©μžμš© μ§€μ›μ„œ μž‘μ„± νŽ˜μ΄μ§€λ„ λ©”μΈμ—μ„œλŠ” λΉ„ν™œμ„±ν™” 처리 */} - {/*πŸ›  develop-feμ—μ„œλŠ” λ‹€μ‹œ λ…ΈμΆœ μ˜ˆμ •*/} } /> + } /> } /> diff --git a/frontend/src/components/common/Header/Header.tsx b/frontend/src/components/common/Header/Header.tsx index 6a571ca54..9f9532221 100644 --- a/frontend/src/components/common/Header/Header.tsx +++ b/frontend/src/components/common/Header/Header.tsx @@ -1,139 +1,156 @@ +import { memo } from 'react'; import { useLocation } from 'react-router-dom'; import * as Styled from './Header.styles'; + import SearchBox from '@/pages/MainPage/components/SearchBox/SearchBox'; -import useHeaderService from '@/services/header/useHeaderService'; -import useMobileMenu from '@/services/header/useMobileMenu'; import useIsMobile from '@/hooks/useIsMobile'; +import useMobileMenu from '@/services/header/useMobileMenu'; +import useHeaderService from '@/services/header/useHeaderService'; + import DesktopMainIcon from '@/assets/images/moadong_name_logo.svg'; import MobileMainIcon from '@/assets/images/logos/moadong_mobile_logo.svg'; -import MenuBar from '@/assets/images/icons/menu_button_icon.svg'; +import MenuBarIcon from '@/assets/images/icons/menu_button_icon.svg'; import DeleteIcon from '@/assets/images/introduce/delete.png'; -interface MobileHeaderProps { - handleHomeClick: (device: 'mobile' | 'desktop') => void; - handleMenuClick: () => void; +interface NavLinkData { + label: string; + handler: () => void; } interface DesktopHeaderProps { isAdminPage: boolean; - handleHomeClick: (device: 'mobile' | 'desktop') => void; - handleIntroduceClick: () => void; + navLinks: NavLinkData[]; + onHomeClick: () => void; } -interface MobileMenuProp { +interface MobileHeaderProps { + onHomeClick: () => void; + onMenuClick: () => void; +} + +interface MobileMenuDrawerProps { isOpen: boolean; onClose: () => void; - handleHomeClick: (device: 'mobile' | 'desktop') => void; - handleIntroduceClick: () => void; + navLinks: NavLinkData[]; + onHomeClick: () => void; } -const MobileMenuDrawer = ({ - isOpen, - onClose, - handleHomeClick, - handleIntroduceClick, -}: MobileMenuProp) => { - return ( +const DesktopHeader = memo( + ({ isAdminPage, navLinks, onHomeClick }: DesktopHeaderProps) => ( + + + + + λͺ¨μ•„동 둜고 + + {!isAdminPage && + navLinks.map((link) => ( + + {link.label} + + ))} + + {!isAdminPage && } + + + ), +); + +const MobileMenuDrawer = memo( + ({ isOpen, onClose, navLinks, onHomeClick }: MobileMenuDrawerProps) => ( - - - handleHomeClick('mobile')} - /> - - - - λͺ¨μ•„동 μ†Œκ°œ + + + + + {navLinks.map((link) => ( + { + link.handler(); + onClose(); + }} + > + {link.label} - + ))} - ); -}; + ), +); -const MobileHeader = ({ - handleHomeClick, - handleMenuClick, -}: MobileHeaderProps) => ( +const MobileHeader = memo(({ onHomeClick, onMenuClick }: MobileHeaderProps) => ( - - ν™ˆ λ²„νŠΌ handleHomeClick('mobile')} - /> + + λͺ¨μ•„동 둜고 - - 메뉴 λ²„νŠΌ + + -); - -const DesktopHeader = ({ - isAdminPage, - handleHomeClick, - handleIntroduceClick, -}: DesktopHeaderProps) => ( - - - - - ν™ˆ λ²„νŠΌ handleHomeClick('desktop')} - /> - - {!isAdminPage && ( - - λͺ¨μ•„동 μ†Œκ°œ - - )} - - {!isAdminPage && } - - -); +)); const Header = () => { const isMobile = useIsMobile(); const location = useLocation(); const isAdminPage = location.pathname.startsWith('/admin'); - const { handleHomeClick, handleIntroduceClick, handleMenuClick } = - useHeaderService(); + const { + handleHomeClick, + handleIntroduceClick, + handleClubUnionClick, + handleMenuClick, + } = useHeaderService(); const { isMenuOpen, openMenu, closeMenu } = useMobileMenu({ handleMenuClick, }); - return isMobile ? ( + const navLinks: NavLinkData[] = [ + { label: 'λͺ¨μ•„동 μ†Œκ°œ', handler: handleIntroduceClick }, + { label: 'μ΄λ™μ•„λ¦¬μ—°ν•©νšŒ μ†Œκ°œ', handler: handleClubUnionClick }, + ]; + + return ( <> - + {isMobile ? ( + handleHomeClick('mobile')} + onMenuClick={openMenu} + /> + ) : ( + handleHomeClick('desktop')} + /> + )} { + handleHomeClick('mobile'); + closeMenu(); + }} /> - ) : ( - ); }; diff --git a/frontend/src/constants/CLUB_UNION_INFO.ts b/frontend/src/constants/CLUB_UNION_INFO.ts new file mode 100644 index 000000000..f3e004ef9 --- /dev/null +++ b/frontend/src/constants/CLUB_UNION_INFO.ts @@ -0,0 +1,122 @@ +import PresidentAvatar from '@/assets/images/icons/category_button/category_all_button_icon.svg'; +import ReligionAvatar from '@/assets/images/icons/category_button/category_religion_button_icon.svg'; +import HobbyAvatar from '@/assets/images/icons/category_button/category_hobby_button_icon.svg'; +import StudyAvatar from '@/assets/images/icons/category_button/category_study_button_icon.svg'; +import VolunteerAvatar from '@/assets/images/icons/category_button/category_volunteer_button_icon.svg'; +import PerformanceAvatar from '@/assets/images/icons/category_button/category_performance_button_icon.svg'; +import SportAvatar from '@/assets/images/icons/category_button/category_sport_button_icon.svg'; + +export interface ClubUnionMember { + id: number; + name: string; + role: string; + description: string; + imageSrc: string; +} + +const MEMBER_AVATARS = { + PRESIDENT: PresidentAvatar, + VICE_PRESIDENT: PresidentAvatar, + SECRETARY: PresidentAvatar, + PROMOTION: PresidentAvatar, + RELIGION: ReligionAvatar, + HOBBY: HobbyAvatar, + STUDY: StudyAvatar, + VOLUNTEER: VolunteerAvatar, + PERFORMANCE: PerformanceAvatar, + SPORT: SportAvatar, +}; + +// 개발자 κ°€μ΄λ“œ: description ν•„λ“œλŠ” UIκ°€ κΉ¨μ§€μ§€ μ•Šλ„λ‘ κΈ€μž 수λ₯Ό μ œν•œν•©λ‹ˆλ‹€. +// (ꢌμž₯) λͺ¨λ°”일: 50자 이내, λ°μŠ€ν¬ν†±: 100자 이내 +export const CLUB_UNION_MEMBERS: ClubUnionMember[] = [ + { + id: 1, + name: '이정은', + role: '회μž₯', + description: 'λΆ€κ²½λŒ€ν•™κ΅μ˜ 쀑앙동아리, 온 μ΄λ™μ•„λ¦¬μ—°ν•©νšŒκ°€ μ±…μž„μ§€κ² μŠ΅λ‹ˆλ‹€.', + imageSrc: MEMBER_AVATARS.PRESIDENT, + }, + { + id: 2, + name: 'κΉ€νƒœμ—°', + role: 'λΆ€νšŒμž₯', + description: + 'μ—¬λŸ¬λΆ„μ˜ 동아리 μƒν™œμ΄ ν’μš”λ‘œμ›Œμ§ˆ 수 μžˆλ„λ‘ νž˜μ„ λ³΄νƒœκ² μŠ΅λ‹ˆλ‹€.', + imageSrc: MEMBER_AVATARS.VICE_PRESIDENT, + }, + { + id: 3, + name: 'μ΅œμ§€ν˜„', + role: '사무ꡭμž₯', + description: '동아리λ₯Ό μœ„ν•΄ λ…Έλ ₯ν•˜λŠ” 온 μ΄λ™μ—°μ—κ²Œ λ§Žμ€ 관심 λΆ€νƒλ“œλ¦½λ‹ˆλ‹€.', + imageSrc: MEMBER_AVATARS.SECRETARY, + }, + { + id: 4, + name: '이주은', + role: '홍보ꡭμž₯', + description: 'μ΄λ™μ—°μ˜ κ°€μΉ˜λ₯Ό μ•Œλ¦¬κ³  μ†Œν†΅μ„ μ΄λ„λŠ” 홍보ꡭμž₯μž…λ‹ˆλ‹€!', + imageSrc: MEMBER_AVATARS.PROMOTION, + }, + { + id: 5, + name: 'κΉ€λ„ν•˜', + role: '쒅ꡐ뢄과μž₯', + description: 'λ―Ώκ³  λ”°λ₯΄λŠ” λ“ λ“ ν•œ 총동연을 μœ„ν•΄ λ…Έλ ₯ν•˜κ² μŠ΅λ‹ˆλ‹€.', + imageSrc: MEMBER_AVATARS.RELIGION, + }, + { + id: 6, + name: '이정원', + role: '취미ꡐ양뢄과μž₯', + description: '2025λ…„ λ™μ•„λ¦¬λ“€μ˜ ν™œλ™μ΄ 잘 이루어지도둝 μ—΄μ‹¬νžˆ ν•˜κ² μŠ΅λ‹ˆλ‹€.', + imageSrc: MEMBER_AVATARS.HOBBY, + }, + { + id: 7, + name: '성기일', + role: 'ν•™μˆ λΆ„κ³Όμž₯', + description: '곡뢀도 동아리도 포기 λͺ» ν•˜λŠ” ν•™μˆ λΆ„κ³Όμž₯μž…λ‹ˆλ‹€!', + imageSrc: MEMBER_AVATARS.STUDY, + }, + { + id: 8, + name: 'κΉ€ν˜„μ§„', + role: '봉사뢄과μž₯', + description: 'λŒ€ν•™ μƒν™œμ˜ 꽃, 봉사동아리 λ§Žμ€ 관심 λΆ€νƒλ“œλ¦½λ‹ˆλ‹€!', + imageSrc: MEMBER_AVATARS.VOLUNTEER, + }, + { + id: 9, + name: 'λ°•μ§€μœ€', + role: '곡연1λΆ„κ³Όμž₯', + description: + '더 λ‚˜μ€ 동아리 μƒν™œμ„ λ§Œλ“€κΈ° μœ„ν•΄ μ±…μž„κ°μ„ κ°€μ§€κ³  μž„ν•˜κ² μŠ΅λ‹ˆλ‹€!', + imageSrc: MEMBER_AVATARS.PERFORMANCE, + }, + { + id: 10, + name: '고보민', + role: '곡연2λΆ„κ³Όμž₯', + description: + '곡연2λΆ„κ³Όμ˜ μ›ν™œν•œ 운영과 λ©‹μ§„ λ¬΄λŒ€λ₯Ό μœ„ν•΄ μ΅œμ„ μ„ λ‹€ν•˜κ² μŠ΅λ‹ˆλ‹€!', + imageSrc: MEMBER_AVATARS.PERFORMANCE, + }, + { + id: 11, + name: 'μ΄κΈˆμ •', + role: 'μš΄λ™1λΆ„κ³Όμž₯', + description: + 'μ—΄μ • κ°€λ“ν•œ μš΄λ™1λΆ„κ³Ό! λΆ€κ²½λŒ€ ν•™μš° μ—¬λŸ¬λΆ„μ˜ ν™œκΈ°μ°¬ 동아리 ν™œλ™μ„ μœ„ν•΄ μ΅œμ„ μ„ λ‹€ν•˜κ² μŠ΅λ‹ˆλ‹€!', + imageSrc: MEMBER_AVATARS.SPORT, + }, + { + id: 12, + name: '이상민', + role: 'μš΄λ™2λΆ„κ³Όμž₯', + description: + 'μž¬λ―Έμ™€ 건강 두 마리 토끼λ₯Ό μž‘μ„ 수 μžˆλŠ” μš΄λ™2λΆ„κ³Όμ—μ„œ μ—¬λŸ¬λΆ„μ„ λ§Œλ‚¬μœΌλ©΄ μ’‹κ² μŠ΅λ‹ˆλ‹€!', + imageSrc: MEMBER_AVATARS.SPORT, + }, +]; diff --git a/frontend/src/constants/eventName.ts b/frontend/src/constants/eventName.ts index fc9440017..76cb84657 100644 --- a/frontend/src/constants/eventName.ts +++ b/frontend/src/constants/eventName.ts @@ -15,4 +15,5 @@ export const EVENT_NAME = { MOBILE_MENU_BUTTON_CLICKED: 'Mobile Menu Button Clicked' as const, MOBILE_MENU_DELETE_BUTTON_CLICKED: 'Mobile Menubar delete Button Clicked' as const, + CLUB_UNION_BUTTON_CLICKED: 'Club Union Button Clicked' as const, } as const; diff --git a/frontend/src/pages/ClubUnionPage/ClubUnionPage.styles.ts b/frontend/src/pages/ClubUnionPage/ClubUnionPage.styles.ts new file mode 100644 index 000000000..93bd8d237 --- /dev/null +++ b/frontend/src/pages/ClubUnionPage/ClubUnionPage.styles.ts @@ -0,0 +1,139 @@ +import styled from 'styled-components'; +import { media } from '@/styles/mediaQuery'; + +export const Title = styled.h1` + font-size: 2.5rem; + font-weight: 800; + margin-top: 100px; + margin-bottom: 40px; + text-align: center; + color: #222; + + ${media.mobile} { + font-size: 2rem; + margin-top: 80px; + margin-bottom: 30px; + } +`; + +export const IntroductionText = styled.p` + font-size: 1.1rem; + line-height: 1.7; + text-align: center; + color: #555; + max-width: 600px; + margin: 0 auto 60px; + + ${media.mobile} { + font-size: 1rem; + margin-bottom: 40px; + } +`; + +export const ProfileGrid = styled.div` + display: grid; + grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); + gap: 40px 30px; + + ${media.tablet} { + grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); + gap: 30px 20px; + } + + ${media.mobile} { + grid-template-columns: repeat(2, 1fr); + gap: 20px 15px; + } +`; + +export const InfoOverlay = styled.div` + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-color: rgba(0, 0, 0, 0.65); + color: white; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + text-align: center; + padding: 15px; + opacity: 0; + transition: opacity 0.3s ease; + border-radius: 50%; + box-sizing: border-box; +`; + +export const ProfileImage = styled.img` + width: 100%; + height: 100%; + object-fit: cover; + transition: + transform 0.3s ease, + filter 0.3s ease; +`; + +export const NameBadge = styled.div` + position: absolute; + bottom: 10%; + left: 50%; + transform: translateX(-50%); + background-color: rgba(255, 255, 255, 0.8); + color: #333; + padding: 5px 15px; + border-radius: 15px; + font-weight: 600; + font-size: 1rem; + transition: opacity 0.3s ease; + white-space: nowrap; +`; + +export const ProfileCardContainer = styled.div` + position: relative; + aspect-ratio: 1 / 1; + border-radius: 50%; + overflow: hidden; + cursor: pointer; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); + + &:hover { + ${ProfileImage} { + transform: scale(1.1); + filter: brightness(0.5); + } + ${InfoOverlay} { + opacity: 1; + } + ${NameBadge} { + opacity: 0; + } + } +`; + +// μ˜€λ²„λ ˆμ΄ λ‚΄λΆ€ ν…μŠ€νŠΈ μŠ€νƒ€μΌ +export const Role = styled.p` + font-size: 1.1rem; + font-weight: 700; + color: #ff5414; + margin: 0 0 8px; +`; + +export const Name = styled.p` + font-size: 1.3rem; + font-weight: 800; + margin: 0 0 12px; +`; + +export const Description = styled.p` + font-size: 0.9rem; + line-height: 1.5; + margin: 0; +`; + +export const Contact = styled.p` + font-size: 0.9rem; + margin-top: 12px; + opacity: 0.8; +`; diff --git a/frontend/src/pages/ClubUnionPage/ClubUnionPage.tsx b/frontend/src/pages/ClubUnionPage/ClubUnionPage.tsx new file mode 100644 index 000000000..b7dba7ef6 --- /dev/null +++ b/frontend/src/pages/ClubUnionPage/ClubUnionPage.tsx @@ -0,0 +1,43 @@ +import Header from '@/components/common/Header/Header'; +import * as Styled from './ClubUnionPage.styles'; +import { CLUB_UNION_MEMBERS } from '@/constants/CLUB_UNION_INFO'; +import { PageContainer } from '@/styles/PageContainer.styles'; +import Footer from '@/components/common/Footer/Footer'; + +const ClubUnionPage = () => { + return ( + <> +
+ + μ΄λ™μ•„λ¦¬μ—°ν•©νšŒ μ†Œκ°œ + + μ•ˆλ…•ν•˜μ„Έμš”! λΆ€κ²½λŒ€ν•™κ΅ 제16λŒ€ μ΄λ™μ•„λ¦¬μ—°ν•©νšŒ '온'μž…λ‹ˆλ‹€. +
온 동아리λ₯Ό μœ„ν•˜μ—¬, 온 νž˜μ„ λ‹€ν•΄. +
+ + {CLUB_UNION_MEMBERS.map((member) => ( + + + + {/* ν‰μ†Œμ— λ³΄μ΄λŠ” 이름 λ°°μ§€ */} + {member.name} + + {/* ν˜Έλ²„ μ‹œ λ‚˜νƒ€λ‚˜λŠ” 정보 */} + + {member.role} + {member.name} + {member.description} + + + ))} + +
+