diff --git a/README.md b/README.md
index 64c1a33..2deccb0 100644
--- a/README.md
+++ b/README.md
@@ -16,7 +16,7 @@
| **분야** | **이름** | **포지션** |
| --- | --- | --- |
-| PM | 최도현 | **프론트엔드 리드**, 프론트 인프라 구축 & 서버 연동 및 배포, 화면 UI 구현,
UI/UX, GUI 디자인, 백엔드 API 및 DB 구축 |
+| PM | 최도현 | **프론트엔드 리드**, 프론트 인프라 구축 & 서버 연동 및 배포, 화면 UI 구현,
UI/UX, GUI 디자인, 백엔드 API 및 DB 구축 |
| 백엔드 | 유승완 | **백엔드 리드**, 백엔드 인프라 구축 & 서버 연동 및 배포, API 및 DB 구축 |
| 백엔드 | 윤도훈 | **백엔드**, API 및 DB 구축 |
| 프론트엔드 | 김수현 | **프론트엔드**, 화면 UI 구현, API 연동 |
diff --git a/src/App.tsx b/src/App.tsx
index e24ee21..42dffe3 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -24,7 +24,10 @@ import FAQ from './pages/FAQ'
import { RecruitSubmit } from './pages/RecruitSubmit'
import RecruitCheck from './pages/RecruitCheck'
import RecruitCheckFinal from './pages/RecruitCheckFinal'
+import SomkathonRecruit from './pages/SomkathonRecruit'
+import SomkathonSubmit from './pages/SomkathonSubmit'
import ProtectedRoute from './components/layout/ProtectRoute'
+import SomkatonApplicants from './pages/admin/SomkatonApplicants'
function App() {
return (
@@ -59,6 +62,8 @@ function AppContent() {
} />
} />
} />
+ } />
+ } />
{/* 관리자 페이지 */}
} />
@@ -69,6 +74,7 @@ function AppContent() {
} />
} />
} />
+ }/>
>
)
diff --git a/src/components/UI/Header.tsx b/src/components/UI/Header.tsx
index 2a90c35..c7eb173 100644
--- a/src/components/UI/Header.tsx
+++ b/src/components/UI/Header.tsx
@@ -132,29 +132,15 @@ export const Header = (): JSX.Element => {
>
FAQ
-
- {/* 모집 기간에 따라 버튼 다르게 표시 */}
- {isRecruiting ? (
- {
- navigate('/recruit')
- toggleMenu()
- }}
- >
- 34기 지원하기
-
- ) : (
- {
- navigate('/recruit/result')
- toggleMenu()
- }}
- >
- 34기 합격여부 확인하기
-
- )}
+ {
+ navigate('/somkathon')
+ toggleMenu()
+ }}
+ >
+ 솜커톤 지원하기
+
)}
diff --git a/src/components/UI/RecruitUI.tsx b/src/components/UI/RecruitUI.tsx
index 88b3209..add893e 100644
--- a/src/components/UI/RecruitUI.tsx
+++ b/src/components/UI/RecruitUI.tsx
@@ -188,4 +188,38 @@ export const RecruitUI_FINAL2: React.FC = ({ name }) => {
)
+}
+
+export const SomRecruitUI: React.FC = () => {
+ return(
+
+
+ 솜커톤에서 멋진 프로젝트를 만들어 주실 학우 여러분들을 모집합니다!
+
+
+
📅 모집 일정 :
+
4월 11일 (금) ~ 4월 16일 (수)
+
+
+
+
📝 모집 대상 :
+
25년도 1학기 솜커톤에 참가하는 학우 여러분
+
+
+
+
🌿 신청 조건 :
+
컴퓨터공학부 학생
+
+
+
+
🍀 참가비 :
+
10,000원
+
+ 참가비는 솜커톤 행사 운영 자금으로 사용됩니다.
+ 납부 방법은 추후 안내드리겠습니다.
+
+
+
👀 의지가 있으며 교류를 중시하는 분을 기다립니다.
+
+ )
}
\ No newline at end of file
diff --git a/src/pages/CoreMembers.tsx b/src/pages/CoreMembers.tsx
index f816405..cbf5ce7 100644
--- a/src/pages/CoreMembers.tsx
+++ b/src/pages/CoreMembers.tsx
@@ -14,7 +14,7 @@ const profiles: Profile[] = [
{ id: 1, name: '윤도훈', roll: '회장', github_username: 'hodoon' },
{ id: 2, name: '공석', roll: '부회장', github_username: '' },
{ id: 3, name: '유승완', roll: '기술팀장', github_username: 'ysw789' },
- { id: 4, name: '최도현', roll: '학술팀장', github_username: 'titeotty' },
+ { id: 4, name: '최도현', roll: '학술팀장', github_username: 'dohy-eon' },
{ id: 5, name: '공석', roll: '학술차장', github_username: '' },
{ id: 6, name: '김수현', roll: '홍보팀장', github_username: 'sooh329' },
{ id: 7, name: '임성환', roll: '서기', github_username: 'limtjdghks' },
@@ -127,4 +127,4 @@ const CoreMembers: React.FC = () => {
)
}
-export default CoreMembers
\ No newline at end of file
+export default CoreMembers
diff --git a/src/pages/SomkathonRecruit.tsx b/src/pages/SomkathonRecruit.tsx
new file mode 100644
index 0000000..8c514d4
--- /dev/null
+++ b/src/pages/SomkathonRecruit.tsx
@@ -0,0 +1,182 @@
+import React, { useState, useEffect, useRef } from 'react'
+import axios from 'axios'
+import MobileLayout from '../components/layout/MobileLayout'
+import { useNavigate } from 'react-router-dom'
+import { SomRecruitUI, RecruitHeader } from '../components/UI/RecruitUI'
+import { InputField } from '../components/UI/Recruit_InputField'
+import { Button } from '../components/UI/Recruit_Button'
+
+
+const SomkathonRecruit: React.FC = () => {
+ const navigate = useNavigate()
+ const [contact, setContact] = useState('')
+ const [isRecruiting, setIsRecruiting] = useState(null)
+ const alertShown = useRef(false)
+
+ const [formData, setFormData] = useState({
+ participantName: '',
+ studentId: '',
+ department: '',
+ grade: '1',
+ contact: '',
+ email: '',
+ })
+
+ useEffect(() => {
+ const checkRecruitmentPeriod = async () => {
+ const startDate = new Date('2025-04-08T00:00:00')
+ const endDate = new Date('2025-04-17T00:00:00')
+ const now = new Date()
+
+ if (now >= startDate && now <= endDate) {
+ setIsRecruiting(true)
+ } else {
+ setIsRecruiting(false)
+ if (!alertShown.current) {
+ alertShown.current = true
+ alert('현재 모집 기간이 아닙니다.')
+ navigate('/')
+ }
+ }
+ }
+
+ checkRecruitmentPeriod()
+ }, [navigate])
+
+ if (isRecruiting === false) return null
+
+ // 입력값들 제약조건 설정
+ const handleInputChange = (e: React.ChangeEvent) => {
+ const { name, value } = e.target
+ let newValue = value
+
+ if (name === 'contact') {
+ let formattedValue = value.replace(/[^0-9]/g, '')
+
+
+ if (formattedValue.length > 11) {
+ formattedValue = formattedValue.slice(0, 11)
+ }
+
+ if (formattedValue.length === 10) {
+ formattedValue = formattedValue.replace(/^(\d{3})(\d{3})(\d{4})$/, '$1-$2-$3')
+ } else if (formattedValue.length === 11) {
+ formattedValue = formattedValue.replace(/^(\d{3})(\d{4})(\d{4})$/, '$1-$2-$3')
+ }
+
+ setContact(formattedValue)
+ setFormData((prevData) => ({
+ ...prevData,
+ contact: formattedValue
+ }))
+ }
+ else if (name === 'participantName') {
+ if (value.length <= 16) {
+ setFormData((prevData) => ({
+ ...prevData,
+ participantName: value
+ }))
+ }
+ } else if (name === 'studentId') {
+ let formattedValue = value.replace(/[^0-9]/g, '')
+ formattedValue = formattedValue.slice(0, 8)
+
+ setFormData((prevData) => ({
+ ...prevData,
+ studentId: formattedValue
+ }))
+ } else {
+ setFormData((prevData) => ({
+ ...prevData,
+ [name]: newValue
+ }))
+ }
+ }
+
+ const handleKeyPress = (e: React.KeyboardEvent) => {
+ if (e.key === 'Enter') {
+ e.preventDefault()
+
+ const form = e.currentTarget.form
+ if (!form) return
+
+ const elements = Array.from(form.elements) as HTMLElement[]
+ const index = elements.indexOf(e.currentTarget)
+
+
+ for (let i = index + 1; i < elements.length; i++) {
+ const nextElement = elements[i]
+ if (nextElement instanceof HTMLInputElement || nextElement instanceof HTMLTextAreaElement || nextElement instanceof HTMLSelectElement) {
+ nextElement.focus()
+ break
+ }
+ }
+ }
+ }
+
+ const handleSubmit = async (e: React.FormEvent) => {
+ e.preventDefault()
+ if (!formData.participantName || !formData.studentId || !formData.contact || !formData.email || !formData.department) {
+ alert('모든 필수 정보를 입력해주세요.')
+ return
+ }
+
+ try {
+ await axios.post('https://dmu-dasom-api.or.kr/api/somkathon/participants/create', formData, {
+ headers: {
+ 'Content-Type': 'application/json',
+ 'Accept': 'application/json'
+ }
+ })
+
+ navigate('/somkathon/submit')
+ } catch (error: any) {
+ if (error.response) {
+ const errorData = error.response.data
+
+ if (error.response.status === 400 && errorData.code === 'C001' || errorData.code === 'C007') {
+ alert('입력 정보를 다시 확인해주세요.')
+ } else if (error.response.status === 400 && errorData.code === 'C013'){
+ alert('이미 등록된 학번입니다.')
+ }
+ } else {
+ console.error('API 요청 중 오류 발생:', error)
+ alert('네트워크 오류가 발생했습니다.')
+ }
+ }
+ }
+
+ return (
+
+
+
+
+
+
+
+ )
+}
+
+export default SomkathonRecruit
\ No newline at end of file
diff --git a/src/pages/SomkathonSubmit.tsx b/src/pages/SomkathonSubmit.tsx
new file mode 100644
index 0000000..aa7e0a9
--- /dev/null
+++ b/src/pages/SomkathonSubmit.tsx
@@ -0,0 +1,21 @@
+import React from 'react'
+import MobileLayout from '../components/layout/MobileLayout'
+import { Header } from '../components/UI/Header'
+import { SomRecruitUI, RecruitHeader } from '../components/UI/RecruitUI'
+import { Recruit_InfoBanner } from '../components/UI/Recruit_InfoBanner'
+
+const SomkathonSubmit: React.FC = () => {
+ return(
+
+
+
+
+
+
+ )
+}
+
+export default SomkathonSubmit
\ No newline at end of file
diff --git a/src/pages/admin/AdminMain.tsx b/src/pages/admin/AdminMain.tsx
index aa32bdc..819976d 100644
--- a/src/pages/admin/AdminMain.tsx
+++ b/src/pages/admin/AdminMain.tsx
@@ -42,6 +42,7 @@ const AdminMain: React.FC = () => {
+
{
+ const [applicants, setApplicants] = useState
([])
+ const [detailInfo, setDetailInfo] = useState(null)
+ const [selectedId, setSelectedId] = useState (null)
+ const [count, setCount] = useState(0)
+ const accessToken = localStorage.getItem('accessToken')
+
+ // 지원자 전체 조회
+ const getData = async () => {
+ try {
+ if(!accessToken) {
+ alert('로그인이 필요합니다.')
+ return
+ }
+ const response = await axios.get('https://dmu-dasom-api.or.kr/api/somkathon/participants')
+ console.log(response.data)
+ setApplicants(response.data)
+ setCount(response.data.length)
+ } catch (e:any) {
+ alert('지원자 목록 불러오기 실패')
+ console.log(e)
+ }
+ }
+
+ useEffect(() => {
+ getData()
+ },[])
+
+ // 지원자 상세 조회
+ const toggleDetail = async (id:number) => {
+ if(selectedId === id) {
+ setSelectedId(null)
+ setDetailInfo(null)
+ return
+ }
+
+ try {
+ const response = await axios.get(`https://dmu-dasom-api.or.kr/api/somkathon/participants/${id}`)
+ setDetailInfo(response.data)
+ setSelectedId(id)
+ } catch (e:any) {
+ console.log(e)
+ alert('지원자 상세정보 불러오기 실패')
+ }
+ }
+
+ const handleDelete = async (id:number) => {
+ try {
+ await axios.delete(`https://dmu-dasom-api.or.kr/api/somkathon/participants/${id}`)
+ setDetailInfo(null)
+ getData()
+ } catch (e:any) {
+ console.log(e)
+ alert('지원자 삭제 실패')
+ }
+ }
+
+ const ApplicantInfo = ({applicant} : {applicant:any}) => {
+ return(
+
+ | {applicant.id} |
+ {applicant.participantName} |
+ {applicant.studentId} |
+
+
+
+ {selectedId === applicant.id && ( )}
+
+ |
+
+ )
+ }
+
+ const DetailItem = ({label, value} : {label:string, value:string}) => {
+ return (
+
+ )
+ }
+
+ const ApplicantDetailInfo = ({applicant} : {applicant:any}) => {
+ if(!applicant) return null
+ return (
+
+
+
+
+
+
+
+
+
+
+ )
+ }
+
+ return (
+
+
+
+
+
+ | ID |
+ 이름 |
+ 학번 |
+ 상세정보 |
+
+
+
+ {applicants.map((applicant) => (
+
+ ))}
+
+
+
+ )
+}
+
+export default SomkatonApplicants
\ No newline at end of file