From ddf0951e0caaa284fc6a810a35cfece4d16bc390 Mon Sep 17 00:00:00 2001 From: HYEON <74049556+dohy-eon@users.noreply.github.com> Date: Fri, 4 Apr 2025 13:32:36 +0900 Subject: [PATCH 1/9] =?UTF-8?q?hotfix:=20=ED=95=99=EC=88=A0=ED=8C=80?= =?UTF-8?q?=EC=9E=A5=20=EA=B9=83=ED=97=88=EB=B8=8C=20=EC=A3=BC=EC=86=8C?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/CoreMembers.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 From c4887b75d08f00dcef7e0b7bc050065b69cc1940 Mon Sep 17 00:00:00 2001 From: sooh329 Date: Wed, 9 Apr 2025 16:20:26 +0900 Subject: [PATCH 2/9] =?UTF-8?q?feat:=20=EC=86=9C=EC=BB=A4=ED=86=A4=20?= =?UTF-8?q?=EB=AA=A8=EC=A7=91=20=ED=8F=BC=20=EA=B5=AC=ED=98=84=EC=99=84?= =?UTF-8?q?=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/App.tsx | 4 + src/components/UI/RecruitUI.tsx | 34 ++++++ src/pages/SomkathonRecruit.tsx | 183 ++++++++++++++++++++++++++++++++ src/pages/SomkathonSubmit.tsx | 21 ++++ 4 files changed, 242 insertions(+) create mode 100644 src/pages/SomkathonRecruit.tsx create mode 100644 src/pages/SomkathonSubmit.tsx diff --git a/src/App.tsx b/src/App.tsx index e24ee21..7dbbf55 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -24,6 +24,8 @@ 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' function App() { @@ -59,6 +61,8 @@ function AppContent() { } /> } /> } /> + } /> + } /> {/* 관리자 페이지 */} } /> diff --git a/src/components/UI/RecruitUI.tsx b/src/components/UI/RecruitUI.tsx index 88b3209..500b044 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/SomkathonRecruit.tsx b/src/pages/SomkathonRecruit.tsx new file mode 100644 index 0000000..be8b2bb --- /dev/null +++ b/src/pages/SomkathonRecruit.tsx @@ -0,0 +1,183 @@ +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: '', + 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() + console.log(formData) + 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/result') + } catch (error: any) { + if (error.response) { + const errorData = error.response.data + + if (error.response.status === 400 && errorData.code === 'C001') { + alert('입력 정보를 다시 확인해주세요.') + } else if (error.response.status === 400 && errorData.code === 'C002'){ + 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 From 8ea202201a4d155a828036b1555ac7434b670cf7 Mon Sep 17 00:00:00 2001 From: sooh329 Date: Wed, 9 Apr 2025 16:40:28 +0900 Subject: [PATCH 3/9] =?UTF-8?q?feat:=20header=20=EC=86=9C=EC=BB=A4?= =?UTF-8?q?=ED=86=A4=20=EC=A7=80=EC=9B=90=ED=95=98=EA=B8=B0=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/UI/Header.tsx | 32 +++++++++----------------------- 1 file changed, 9 insertions(+), 23 deletions(-) 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() + }} + > + 솜커톤 지원하기 +
  • )} From bbd437cd238334d90eb26b6e55414468bc95f261 Mon Sep 17 00:00:00 2001 From: sooh329 Date: Wed, 9 Apr 2025 17:47:31 +0900 Subject: [PATCH 4/9] =?UTF-8?q?fix:=20navigate=EC=A3=BC=EC=86=8C=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20=EB=B0=8F=20=EC=98=A4=EB=A5=98=EC=B2=98?= =?UTF-8?q?=EB=A6=AC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/UI/RecruitUI.tsx | 4 ++-- src/pages/SomkathonRecruit.tsx | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/components/UI/RecruitUI.tsx b/src/components/UI/RecruitUI.tsx index 500b044..add893e 100644 --- a/src/components/UI/RecruitUI.tsx +++ b/src/components/UI/RecruitUI.tsx @@ -194,7 +194,7 @@ export const SomRecruitUI: React.FC = () => { return(

    - 솜커톤에서 멋진 프로젝트를 만들어주실 학우 여러분들을 모집합니다! + 솜커톤에서 멋진 프로젝트를 만들어 주실 학우 여러분들을 모집합니다!

    📅 모집 일정 :

    @@ -208,7 +208,7 @@ export const SomRecruitUI: React.FC = () => {

    🌿 신청 조건 :

    - 컴퓨터공학부 학생, 시각디자인과 동아리 구미래 부원 + 컴퓨터공학부 학생
    diff --git a/src/pages/SomkathonRecruit.tsx b/src/pages/SomkathonRecruit.tsx index be8b2bb..94fa10f 100644 --- a/src/pages/SomkathonRecruit.tsx +++ b/src/pages/SomkathonRecruit.tsx @@ -17,7 +17,7 @@ const SomkathonRecruit: React.FC = () => { participantName: '', studentId: '', department: '', - grade: '', + grade: '1', contact: '', email: '', }) @@ -130,14 +130,14 @@ const SomkathonRecruit: React.FC = () => { } }) - navigate('/somkathon/result') + navigate('/somkathon/submit') } catch (error: any) { if (error.response) { const errorData = error.response.data - if (error.response.status === 400 && errorData.code === 'C001') { + if (error.response.status === 400 && errorData.code === 'C001' || errorData.code === 'C007') { alert('입력 정보를 다시 확인해주세요.') - } else if (error.response.status === 400 && errorData.code === 'C002'){ + } else if (error.response.status === 400 && errorData.code === 'C013'){ alert('이미 등록된 학번입니다.') } } else { From 77f11f535a0cd06ff6d2440a954b001e868f3f24 Mon Sep 17 00:00:00 2001 From: sooh329 Date: Wed, 9 Apr 2025 18:07:01 +0900 Subject: [PATCH 5/9] =?UTF-8?q?fix:=20console.log=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/SomkathonRecruit.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/pages/SomkathonRecruit.tsx b/src/pages/SomkathonRecruit.tsx index 94fa10f..d9484fa 100644 --- a/src/pages/SomkathonRecruit.tsx +++ b/src/pages/SomkathonRecruit.tsx @@ -116,7 +116,6 @@ const SomkathonRecruit: React.FC = () => { const handleSubmit = async (e: React.FormEvent) => { e.preventDefault() - console.log(formData) if (!formData.participantName || !formData.studentId || !formData.contact || !formData.email || !formData.department) { alert('모든 필수 정보를 입력해주세요.') return From 0d26c16333bb02ebfbe0550a21b8d7be4ac3759b Mon Sep 17 00:00:00 2001 From: sooh329 Date: Wed, 9 Apr 2025 18:10:27 +0900 Subject: [PATCH 6/9] =?UTF-8?q?fix:=20placeholder=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/SomkathonRecruit.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/SomkathonRecruit.tsx b/src/pages/SomkathonRecruit.tsx index d9484fa..8c514d4 100644 --- a/src/pages/SomkathonRecruit.tsx +++ b/src/pages/SomkathonRecruit.tsx @@ -156,7 +156,7 @@ const SomkathonRecruit: React.FC = () => { required minLength={1} maxLength={16} /> - + Date: Wed, 9 Apr 2025 22:31:30 +0900 Subject: [PATCH 7/9] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 연동 | From 6062408d52f8d58cbce42b8d0c72d95ae8cb0c62 Mon Sep 17 00:00:00 2001 From: limtjdghks Date: Thu, 10 Apr 2025 12:35:43 +0900 Subject: [PATCH 8/9] =?UTF-8?q?feat:=20=EC=86=9C=EC=BB=A4=ED=86=A4=20?= =?UTF-8?q?=EC=9D=B8=EC=9B=90=20=EA=B4=80=EB=A6=AC=20=ED=8E=98=EC=9D=B4?= =?UTF-8?q?=EC=A7=80=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/App.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/App.tsx b/src/App.tsx index e24ee21..cd79844 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -25,6 +25,7 @@ import { RecruitSubmit } from './pages/RecruitSubmit' import RecruitCheck from './pages/RecruitCheck' import RecruitCheckFinal from './pages/RecruitCheckFinal' import ProtectedRoute from './components/layout/ProtectRoute' +import SomkatonApplicants from './pages/admin/SomkatonApplicants' function App() { return ( @@ -69,6 +70,7 @@ function AppContent() { } /> } /> } /> + }/> ) From f76d540a4d3f58162c905b2498953a5b2d765d02 Mon Sep 17 00:00:00 2001 From: limtjdghks Date: Thu, 10 Apr 2025 12:36:04 +0900 Subject: [PATCH 9/9] =?UTF-8?q?feat:=20=EC=86=9C=EC=BB=A4=ED=86=A4=20?= =?UTF-8?q?=EC=9D=B8=EC=9B=90=20=EA=B4=80=EB=A6=AC=20=ED=8E=98=EC=9D=B4?= =?UTF-8?q?=EC=A7=80=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/admin/AdminMain.tsx | 1 + src/pages/admin/SomkatonApplicants.tsx | 132 +++++++++++++++++++++++++ 2 files changed, 133 insertions(+) create mode 100644 src/pages/admin/SomkatonApplicants.tsx 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 && (
    + {detailInfo && } +
    )} +
    + + + ) + } + + const DetailItem = ({label, value} : {label:string, value:string}) => { + return ( +
    +
    {label}
    +
    {value}
    +
    + ) + } + + const ApplicantDetailInfo = ({applicant} : {applicant:any}) => { + if(!applicant) return null + return ( +
    + + + + +
    + + +
    +
    + ) + } + + return ( +
    +
    +
    {count}명의 지원자가 있습니다.
    +
    + + + + + + + + + + + {applicants.map((applicant) => ( + + ))} + +
    ID이름학번상세정보
    +
    + ) +} + +export default SomkatonApplicants \ No newline at end of file