@@ -111,18 +111,6 @@ const showcaseItems = [
111111]
112112
113113const activityHubItems = [
114- {
115- title : 'Activity log' ,
116- body : '관리자가 등록한 실제 활동 기록과 사진을 회원에게 보여줍니다.' ,
117- route : '/activities' ,
118- cta : '활동 기록 보기' ,
119- } ,
120- {
121- title : 'Monthly calendar' ,
122- body : '정기 회의, 세미나, 발표, 모집 마감을 월별 일정으로 확인합니다.' ,
123- route : '/activities' ,
124- cta : '일정 보기' ,
125- } ,
126114 {
127115 title : '공지사항' ,
128116 body : '모집, 세미나, 운영 안내를 빠르게 확인합니다.' ,
@@ -508,6 +496,27 @@ const navExtraItems = [
508496 { id : 'community' , label : 'Community' , path : '/community' , auth : true } ,
509497]
510498
499+ const activitySectionNavItems = [
500+ {
501+ id : 'activity-log' ,
502+ label : 'Activity log' ,
503+ hint : '활동 기록' ,
504+ path : '/activities' ,
505+ hash : '#activity-log' ,
506+ icon : Sparkles ,
507+ accent : 'text-rose-400' ,
508+ } ,
509+ {
510+ id : 'monthly-calendar' ,
511+ label : 'Monthly calendar' ,
512+ hint : '월별 일정' ,
513+ path : '/activities' ,
514+ hash : '#monthly-calendar' ,
515+ icon : CalendarDays ,
516+ accent : 'text-sky-500' ,
517+ } ,
518+ ]
519+
511520function getTabRoute ( id ) {
512521 return id === 'recruit' ? '/recruit' : `/${ id } `
513522}
@@ -729,6 +738,12 @@ function GlobalNavigation() {
729738 const activeKey = getActiveNavKey ( location . pathname )
730739 const mobileTabs = tabs . slice ( 0 , 3 )
731740 const showMobileTabs = mobileTabs . some ( ( tab ) => tab . id === activeKey )
741+ const primaryNavItems = [
742+ ...tabs . slice ( 0 , 2 ) . map ( ( tab ) => ( { ...tab , path : getTabRoute ( tab . id ) } ) ) ,
743+ ...activitySectionNavItems ,
744+ ...tabs . slice ( 2 ) . map ( ( tab ) => ( { ...tab , path : getTabRoute ( tab . id ) } ) ) ,
745+ ]
746+ const desktopNavItems = [ ...primaryNavItems , ...navExtraItems ]
732747
733748 useEffect ( ( ) => {
734749 if ( ! appsOpen ) return undefined
@@ -747,6 +762,15 @@ function GlobalNavigation() {
747762 }
748763 } , [ appsOpen ] )
749764
765+ useEffect ( ( ) => {
766+ if ( ! location . hash ) return undefined
767+ const sectionId = decodeURIComponent ( location . hash . slice ( 1 ) )
768+ const frame = window . requestAnimationFrame ( ( ) => {
769+ document . getElementById ( sectionId ) ?. scrollIntoView ( { block : 'start' } )
770+ } )
771+ return ( ) => window . cancelAnimationFrame ( frame )
772+ } , [ location . hash , location . pathname ] )
773+
750774 const goPageTop = ( to , options ) => {
751775 scrollToTopInstant ( )
752776 navigate ( to , options )
@@ -763,6 +787,15 @@ function GlobalNavigation() {
763787 closeAndGo ( to )
764788 }
765789
790+ const goNavItem = ( item ) => {
791+ const target = `${ item . path } ${ item . hash || '' } `
792+ if ( item . auth ) {
793+ goProtected ( target )
794+ return
795+ }
796+ goPageTop ( target )
797+ }
798+
766799 const handleLogout = async ( ) => {
767800 await logout ( )
768801 setMobileMenuOpen ( false )
@@ -790,21 +823,12 @@ function GlobalNavigation() {
790823 < span className = "whitespace-nowrap text-sm font-bold text-[var(--app-text)]" > KW COM's</ span >
791824 </ button >
792825
793- < nav className = "pointer-events-auto absolute left-1/2 top-1/2 hidden -translate-x-1/2 -translate-y-1/2 items-center gap-6 md:flex" >
794- { tabs . map ( ( tab ) => {
795- const route = getTabRoute ( tab . id )
796- return (
797- < button key = { tab . id } type = "button" onClick = { ( ) => goPageTop ( route ) } className = { navClass ( tab . id ) } >
798- { tab . label }
799- < span className = { `absolute -bottom-4 left-1/2 h-0.5 w-4 -translate-x-1/2 rounded-full bg-[var(--app-accent)] transition ${ activeKey === tab . id ? 'opacity-100' : 'opacity-0' } ` } />
800- </ button >
801- )
802- } ) }
803- { navExtraItems . map ( ( item ) => (
826+ < nav className = "pointer-events-auto absolute left-1/2 top-1/2 hidden -translate-x-1/2 -translate-y-1/2 items-center gap-3 lg:gap-5 md:flex" >
827+ { desktopNavItems . map ( ( item ) => (
804828 < button
805829 key = { item . id }
806830 type = "button"
807- onClick = { ( ) => ( item . auth ? goProtected ( item . path ) : goPageTop ( item . path ) ) }
831+ onClick = { ( ) => goNavItem ( item ) }
808832 disabled = { item . auth && authLoading }
809833 className = { `${ navClass ( item . id ) } disabled:cursor-wait disabled:opacity-60` }
810834 >
@@ -902,16 +926,13 @@ function GlobalNavigation() {
902926 className = "apple-mobile-menu-panel mx-auto md:hidden"
903927 >
904928 < div className = "flex flex-col divide-y divide-[var(--app-hairline)]" >
905- { tabs . map ( ( tab ) => {
906- const route = getTabRoute ( tab . id )
907- return (
908- < button key = { tab . id } type = "button" onClick = { ( ) => closeAndGo ( route ) } className = "apple-mobile-menu-item" >
909- < tab . icon size = { 15 } className = { tab . accent } />
910- < span > { tab . label } </ span >
911- < span className = "ml-auto text-xs text-[var(--app-muted)]" > { tab . hint } </ span >
912- </ button >
913- )
914- } ) }
929+ { primaryNavItems . map ( ( item ) => (
930+ < button key = { item . id } type = "button" onClick = { ( ) => closeAndGo ( `${ item . path } ${ item . hash || '' } ` ) } className = "apple-mobile-menu-item" >
931+ < item . icon size = { 15 } className = { item . accent } />
932+ < span > { item . label } </ span >
933+ < span className = "ml-auto text-xs text-[var(--app-muted)]" > { item . hint } </ span >
934+ </ button >
935+ ) ) }
915936 < button type = "button" onClick = { ( ) => closeAndGo ( '/notices' ) } className = "apple-mobile-menu-item" >
916937 < Megaphone size = { 15 } className = "text-cyan-500" />
917938 < span > Notices</ span >
@@ -1543,7 +1564,7 @@ function ActivityLogSection({ compact = false }) {
15431564 const isLocked = ! authLoading && ! user
15441565
15451566 return (
1546- < section className = { `activity-proof-section ${ compact ? 'activity-proof-section-compact' : '' } bg-white px-5 py-12 sm:py-16` } >
1567+ < section id = "activity-log" className = { `activity-proof-section ${ compact ? 'activity-proof-section-compact' : '' } scroll-mt-24 bg-white px-5 py-12 sm:py-16` } >
15471568 < div className = "mx-auto max-w-7xl" >
15481569 < div className = "grid gap-8 lg:grid-cols-[0.62fr_1fr] lg:items-end" >
15491570 < div >
@@ -1710,7 +1731,7 @@ function ClubCalendarSection({ compact = false }) {
17101731 }
17111732
17121733 return (
1713- < section className = { `club-calendar-section ${ compact ? 'club-calendar-section-compact' : '' } bg-[#f5f5f7] px-5 py-12 sm:py-16` } >
1734+ < section id = "monthly-calendar" className = { `club-calendar-section ${ compact ? 'club-calendar-section-compact' : '' } scroll-mt-24 bg-[#f5f5f7] px-5 py-12 sm:py-16` } >
17141735 < div className = "mx-auto max-w-7xl" >
17151736 < div className = "flex flex-wrap items-end justify-between gap-4" >
17161737 < div >
@@ -2303,7 +2324,7 @@ function HomeView() {
23032324 </ div >
23042325 < button type = "button" onClick = { ( ) => goPageTop ( '/notices' ) } className = { ghostActionBtnClass } > 최근 공지 보기</ button >
23052326 </ div >
2306- < div className = "mt-8 grid gap-3 md:grid-cols-2 xl:grid-cols-5 " >
2327+ < div className = "mt-8 grid gap-3 md:grid-cols-3 " >
23072328 { activityHubItems . map ( ( item , index ) => (
23082329 < button
23092330 key = { item . title }
0 commit comments