Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
312 changes: 162 additions & 150 deletions src/components/UI/ActivityStatus.tsx
Original file line number Diff line number Diff line change
@@ -1,179 +1,191 @@
import React from 'react'
import React, { useEffect, useState } from 'react'
import { motion } from 'framer-motion'
import DasomLogo from '../../assets/images/dasomLogo.svg'
import ActivityBar from '../../assets/images/activityBar.svg'
import { ActivityStatusProps, ActivitySection } from './types'
import axios from 'axios'

interface ApiActivity {
id: number
monthDay: string
title: string
award: string | null
}

interface ApiSection {
id: number
section: string
activities: ApiActivity[]
}

interface ApiYearData {
year: number
sections: ApiSection[]
}

// 페이드 인 및 위로 이동 애니메이션
const FadeInSection: React.FC<{ children: React.ReactNode }> = ({
children,
}) => {
return (
<motion.div
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 0.8, ease: 'easeOut' }}
viewport={{ once: false }}
>
{children}
</motion.div>
)
}
}) => (
<motion.div
initial={{ opacity: 0, y: 10 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, ease: 'easeOut' }}
viewport={{ once: true, amount: 0.2 }}
>
{children}
</motion.div>
)

const ActivityStatus: React.FC<ActivityStatusProps> = ({
year,
const ActivityStatus: React.FC<ActivityStatusProps> = ({
year,
activityData: customActivityData,
className = ''
className = '',
}) => {
// 2024년도 기본 활동 데이터
const defaultActivityData2024: ActivitySection[] = [
{
category: '코엑스 한국전자전',
items: [
{
award: '장려상',
subtitle: '2024 동양미래 EXPO',
},
],
},
{
category: '외부 경진대회 / 전시회',
items: [
{
award: '동상',
subtitle: '교육장비 개발 및 아이디어 경진대회',
},
],
},
{
category: '교내 경진대회',
items: [
{
award: '최우수상',
subtitle: '컴퓨터 공학부 경진대회',
},
],
},
{
category: '세미나 실적',
items: [
{ title: '현직 백엔드 개발자 특강 - ', subtitle: '20명 대상' },
{ title: '웹 개발 세미나 - ', subtitle: '10명 대상' },
],
},
{
category: '기타 활동',
items: [
{ title: '컴퓨터공학부 최초 해커톤 개최' },
{ title: '전공동아리 내부 팀 프로젝트 발표회 개최' },
{ title: 'DASOM MAKERS 스터디 및 홈페이지 제작' },
{ title: '시험기간 간식 행사' },
{ title: '할로윈 행사' },
{ title: '동계, 하계 MT' },
],
},
]

// 2025년도 기본 활동 데이터
const defaultActivityData2025: ActivitySection[] = [
{
category: '신규 프로젝트',
items: [
{
title: 'NFT 기반 타임캡슐 서비스 - ',
subtitle: ' 기획, 디자인 및 시연',
},
],
},
{
category: '세미나 및 워크샵',
items: [
{ title: '나의 커리어 디자인하기 - 나에게 맞는 회사 고민하기, 성장 전략은? ', subtitle: ' ' },
],
},
{
category: '대회 참가',
items: [
{
award: '장려상 ',
subtitle: '생성형 AI를 활용한 문제해결 해커톤',
},
],
},
{
category: '2025년 기타 활동',
items: [
{ title: '스프링 부트, 팀 프로젝트 기획 개발 스터디 그룹 운영' },
{ title: '컴퓨터공학부 + 시각디자인학부 협업 해커톤 개최' },
{ title: '대학생 IT 연합동아리 DND, UMC 활동' },
{ title: '오픈소스 프로젝트 기여 활동' },
{ title: '2025년 동계 MT 계획' },
{ title: '미니퀴즈 간식행사' },
],
},
]

// 커스텀 데이터가 있으면 사용하고, 없으면 연도에 따른 기본 데이터 사용
const data = customActivityData || (year === '2025' ? defaultActivityData2025 : defaultActivityData2024)
const [apiData, setApiData] = useState<ActivitySection[]>([])
const [loading, setLoading] = useState(true)

useEffect(() => {
const fetchActivities = async () => {
try {
setLoading(true)

const response = await axios.get<ApiYearData[]>(
'https://dmu-dasom-api.or.kr/api/activities'
)

const result = response.data

const currentYearData = result.find(
item => item.year === Number(year)
)

if (!currentYearData) {
setApiData([])
return
}

const transformed: ActivitySection[] =
currentYearData.sections.map(section => ({
category: section.section,
items: section.activities.map(activity => ({
title: activity.title,
award: activity.award ?? undefined,
subtitle: activity.monthDay
? `(${activity.monthDay})`
: undefined,
})),
}))

setApiData(transformed)
} catch (error) {
console.error('활동 데이터 불러오기 실패:', error)
setApiData([])
} finally {
setLoading(false)
}
}

fetchActivities()
}, [year])

const defaultActivityData2024: ActivitySection[] = []
const defaultActivityData2025: ActivitySection[] = []

const data =
customActivityData ||
(apiData.length > 0
? apiData
: year === '2025'
? defaultActivityData2025
: defaultActivityData2024)

if (loading) {
return <div className="text-white text-center py-10">Loading...</div>
}

return (
<FadeInSection>
<div className={`max-w-[400px] bg-mainBlack p-4 rounded-xl text-white ${className}`}>
<div className='flex items-center gap-2 mb-3'>
<img src={DasomLogo} className='w-7 h-7' alt='Dasom Icon' />
<FadeInSection key={year}>
<div
className={`max-w-[400px] bg-mainBlack p-4 rounded-xl text-white ${className}`}
>
{/* 헤더 */}
<div className="flex items-center gap-2 mb-6">
<img src={DasomLogo} className="w-7 h-7" alt="Dasom Icon" />
<div>
<div className='text-[16px] font-pretendardBold'>활동 현황</div>
<div className='text-mainColor text-[13px] font-pretendardSemiBold'>
<div className="text-[16px] font-pretendardBold">
활동 현황
</div>
<div className="text-mainColor text-[13px] font-pretendardSemiBold">
{year}
</div>
</div>
</div>
<div className='flex items-start gap-3'>
<img
src={ActivityBar}
className='w-4 h-[300px] mt-1.5'
alt='Activitybar'
/>
<div className='space-y-3'>

{/* 타임라인 */}
<div className="relative">

<div className="space-y-6">
{data.map((section, index) => (
<FadeInSection key={index}>
<div>
<div className='text-white text-[12px] font-pretendardBold'>
{section.category}
<div className="flex gap-4 relative">

{/* 점 + 선 영역 */}
<div className="relative w-6 flex justify-center">

{/* 세로 점선 (가운데 정렬) */}
<div
className="absolute top-0 bottom-0 left-1/2
-translate-x-1/2
border-l-2 border-dashed border-mainColor/40"
/>

{/* 점 */}
<span
className="relative z-10 w-1.5 h-1.5 rounded-full bg-mainColor
shadow-[0_0_10px_rgba(0,255,200,0.8)]"
/>
</div>

{/* 콘텐츠 */}
<div className="flex-1">
<div className="text-white text-[12px] font-pretendardBold mb-1">
{section.category}
</div>

<ul className="space-y-1">
{section.items.map((activity, idx) => (
<li
key={idx}
className="flex flex-wrap text-[10.5px] leading-tight"
>
{activity.award && (
<span className="font-pretendardBold text-mainColor mr-1">
{activity.award}
</span>
)}
{activity.title && (
<span className="font-pretendardRegular">
{activity.title}
</span>
)}
{activity.subtitle && (
<span className="text-subGrey font-pretendardRegular ml-1">
{activity.subtitle}
</span>
)}
</li>
))}
</ul>
</div>
<ul className='space-y-1'>
{section.items.map((activity, idx) => (
<li
key={idx}
className='flex flex-wrap text-[10.5px] leading-tight'
>
{activity.title && (
<span className='font-pretendardRegular'>
{activity.title}
</span>
)}
{activity.award && (
<span className='font-pretendardBold text-mainColor mr-1'>
{activity.award}
</span>
)}
{activity.subtitle && (
<span className='text-subGrey font-pretendardRegular'>
{' '}
{activity.subtitle}
</span>
)}
</li>
))}
</ul>

</div>
</FadeInSection>
))}
</div>
</div>

</div>
</FadeInSection>
)
}

export default ActivityStatus
export default ActivityStatus
2 changes: 1 addition & 1 deletion src/components/UI/FAQ_Section.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ const FAQSection: React.FC = () => {
id: 2,
question: '동아리 지원은 어떻게 하나요?',
answer:
'우측 상단 메뉴에 34기 지원하기 클릭 후 지원 폼 작성을 통해 지원이 가능합니다.',
'우측 상단 메뉴에 35기 지원하기 클릭 후 지원 폼 작성을 통해 지원이 가능합니다.',
},
{
id: 3,
Expand Down
2 changes: 1 addition & 1 deletion src/components/UI/PythonEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ const PythonEditor: React.FC = () => {
className='h-4 w-4 mr-2.5'
alt='Python Scroll Down Icon'
/>
DASOM 34기 부원 모집
DASOM 35기 부원 모집
</div>
<div className='text-xl font-semibold text-stone-300 flex items-center'>
<img
Expand Down
Loading
Loading