diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index d130cb460..8d20dfbdd 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -14,7 +14,7 @@ import AccountEditTab from '@/pages/AdminPage/tabs/AccountEditTab/AccountEditTab import LoginTab from '@/pages/AdminPage/auth/LoginTab/LoginTab'; import PrivateRoute from '@/pages/AdminPage/auth/PrivateRoute/PrivateRoute'; import PhotoEditTab from '@/pages/AdminPage/tabs/PhotoEditTab/PhotoEditTab'; -import ApplicationForm from '@/pages/AdminPage/application/ApplicationForm'; +import AnswerApplicationForm from '@/pages/AdminPage/application/answer/AnswerApplicationForm'; import CreateApplicationForm from '@/pages/AdminPage/application/CreateApplicationForm'; const queryClient = new QueryClient(); @@ -75,7 +75,17 @@ const App = () => { } /> - } /> + {/*//todo /:clubid로 수정 필요*/} + {/*}*/} + {/*/>*/} + } /> + {/*TODO: CreateForm은 관리자 기능이므로 추후 /admin/* 경로 안으로 이동 필요*/} + } + /> } /> diff --git a/frontend/src/hooks/useAnswers.ts b/frontend/src/hooks/useAnswers.ts new file mode 100644 index 000000000..6cf5dfead --- /dev/null +++ b/frontend/src/hooks/useAnswers.ts @@ -0,0 +1,33 @@ +import { useState } from 'react'; +import { AnswerItem } from '@/types/application'; + +export const useAnswers = () => { + const [answers, setAnswers] = useState([]); + + const updateSingleAnswer = (id: number, value: string) => { + setAnswers((prev) => [ + ...prev.filter((a) => a.id !== id), + { id, answer: value }, + ]); + }; + + const updateMultiAnswer = (id: number, values: string[]) => { + setAnswers((prev) => [ + ...prev.filter((a) => a.id !== id), + ...values.map((v) => ({ id, answer: v })), + ]); + }; + + const onAnswerChange = (id: number, value: string | string[]) => { + if (Array.isArray(value)) { + updateMultiAnswer(id, value); + } else { + updateSingleAnswer(id, value); + } + }; + + const getAnswersById = (id: number) => + answers.filter((a) => a.id === id).map((a) => a.answer); + + return { onAnswerChange, getAnswersById }; +}; diff --git a/frontend/src/pages/AdminPage/application/ApplicationForm.tsx b/frontend/src/pages/AdminPage/application/ApplicationForm.tsx deleted file mode 100644 index 51b2bb839..000000000 --- a/frontend/src/pages/AdminPage/application/ApplicationForm.tsx +++ /dev/null @@ -1,71 +0,0 @@ -// 지원하기 : 지원서 입력 컴포넌트 -// 특정 지원서를 사용자들이 입력할 수 있는 지원서 작성 부분 -// Todo 추후 특정 지원서를 받아서 answer 모드로 렌더링 -import { useState } from 'react'; -import ShortText from '@/pages/AdminPage/application/fields/ShortText'; -import Choice from '@/pages/AdminPage/application/fields/Choice'; - -interface QuestionData { - title: string; - description: string; - items?: { value: string }[]; -} - -interface AnswerData { - [id: number]: string; -} - -const ApplicationForm = () => { - const [questions] = useState>({ - 1: { - title: '이름을 입력해주세요', - description: '본명을 입력해 주세요', - }, - 2: { - title: '자기소개를 해주세요', - description: '300자 이내로 입력해주세요', - }, - 3: { - title: '지원 분야를 선택해주세요', - description: '중복 선택은 불가능합니다', - items: [ - { value: '프론트엔드' }, - { value: '백엔드' }, - { value: '디자인' }, - ], - }, - 4: { - title: '희망하는 활동 시간을 선택해주세요', - description: '가장 편한 시간을 골라주세요', - items: [], - }, - 5: { - title: '이전에 프로젝트 경험이 있나요?', - description: '간단하게 작성해주세요', - items: [{ value: 'React' }], - }, - 6: { - title: '사용 가능한 기술 스택을 선택해주세요', - description: '복수 선택 가능', - items: [ - { value: 'React' }, - { value: 'Node.js' }, - { value: 'Python' }, - { value: 'Figma' }, - ], - }, - }); - - const [answers, setAnswers] = useState({}); - - const handleAnswerChange = (id: number) => (value: string) => { - setAnswers((prev) => ({ - ...prev, - [id]: value, - })); - }; - - return <>; -}; - -export default ApplicationForm; diff --git a/frontend/src/pages/AdminPage/application/answer/AnswerApplicationForm.styles.ts b/frontend/src/pages/AdminPage/application/answer/AnswerApplicationForm.styles.ts new file mode 100644 index 000000000..02ff36905 --- /dev/null +++ b/frontend/src/pages/AdminPage/application/answer/AnswerApplicationForm.styles.ts @@ -0,0 +1,37 @@ +import styled from 'styled-components'; + +export const FormTitle = styled.h1` + font-size: 2.5rem; + font-weight: 700; + border: none; + outline: none; + margin-top: 20px; + margin-bottom: 46px; +`; + +export const ButtonWrapper = styled.div` + display: flex; + justify-content: flex-end; +`; + +export const submitButton = styled.button` + padding: 10px 56px; + background-color: #ff5414; + border-radius: 10px; + border: none; + color: #fff; + font-size: 1.25rem; + font-weight: 600; + letter-spacing: -0.4px; + transition: background-color 0.2s; + margin: 50px 0; + + &:hover { + background-color: #ffad8e; + animation: pulse 0.4s ease-in-out; + } + + &:active { + transform: scale(0.95); + } +`; diff --git a/frontend/src/pages/AdminPage/application/answer/AnswerApplicationForm.tsx b/frontend/src/pages/AdminPage/application/answer/AnswerApplicationForm.tsx new file mode 100644 index 000000000..8e4527ef4 --- /dev/null +++ b/frontend/src/pages/AdminPage/application/answer/AnswerApplicationForm.tsx @@ -0,0 +1,52 @@ +import { mockData } from '@/mocks/data/mockData'; +import { PageContainer } from '@/styles/PageContainer.styles'; +import * as Styled from './AnswerApplicationForm.styles'; +import Header from '@/components/common/Header/Header'; +import { useParams } from 'react-router-dom'; +import { useGetClubDetail } from '@/hooks/queries/club/useGetClubDetail'; +import ClubProfile from '@/pages/ClubDetailPage/components/ClubProfile/ClubProfile'; +import { useAnswers } from '@/hooks/useAnswers'; +import QuestionAnswerer from '@/pages/AdminPage/application/components/QuestionAnswerer/QuestionAnswerer'; + +const AnswerApplicationForm = () => { + //const { clubId } = useParams<{ clubId: string }>(); + const clubId = '67e54ae51cfd27718dd40bec'; // 하드코딩된 club ID + const { data: clubDetail, error } = useGetClubDetail(clubId || ''); + const { onAnswerChange, getAnswersById } = useAnswers(); + if (!clubDetail) { + return null; + } + + if (error) { + return
에러가 발생했습니다.
; + } + + return ( + <> +
+ + + {mockData.form_title} + {mockData.questions.map((q) => ( + + ))} + + 제출하기 + + + + ); +}; + +export default AnswerApplicationForm; diff --git a/frontend/src/pages/AdminPage/application/components/QuestionAnswerer/QuestionAnswerer.tsx b/frontend/src/pages/AdminPage/application/components/QuestionAnswerer/QuestionAnswerer.tsx new file mode 100644 index 000000000..629a8a2ee --- /dev/null +++ b/frontend/src/pages/AdminPage/application/components/QuestionAnswerer/QuestionAnswerer.tsx @@ -0,0 +1,65 @@ +import { Question } from '@/types/application'; +import ShortText from '@/pages/AdminPage/application/fields/ShortText'; +import LongText from '@/pages/AdminPage/application/fields/LongText'; +import Choice from '@/pages/AdminPage/application/fields/Choice'; + +interface QuestionAnswererProps { + question: Question; + selectedAnswers: string[]; + onChange: (id: number, value: string | string[]) => void; +} + +const QuestionAnswerer = ({ + question, + selectedAnswers, + onChange, +}: QuestionAnswererProps) => { + const baseProps = { + id: question.id, + title: question.title, + description: question.description, + required: question.options.required, + mode: 'answer' as const, + }; + + switch (question.type) { + case 'NAME': + case 'EMAIL': + case 'PHONE_NUMBER': + case 'SHORT_TEXT': + return ( + onChange(question.id, value)} + /> + ); + + case 'LONG_TEXT': + return ( + onChange(question.id, value)} + /> + ); + + case 'CHOICE': + case 'MULTI_CHOICE': + return ( + <> + // onChange(question.id, value)} + // /> + ); + + default: + return
지원하지 않는 질문 유형입니다: {question.type}
; + } +}; + +export default QuestionAnswerer; diff --git a/frontend/src/pages/AdminPage/application/components/QuestionTitle/QuestionTitle.styles.ts b/frontend/src/pages/AdminPage/application/components/QuestionTitle/QuestionTitle.styles.ts index bec9f9b71..53c4c2333 100644 --- a/frontend/src/pages/AdminPage/application/components/QuestionTitle/QuestionTitle.styles.ts +++ b/frontend/src/pages/AdminPage/application/components/QuestionTitle/QuestionTitle.styles.ts @@ -20,7 +20,7 @@ export const QuestionTitleText = styled.input` font-size: 1.25rem; font-weight: 700; line-height: normal; - width: 100%; + field-sizing: content; &::placeholder { color: #c5c5c5; diff --git a/frontend/src/types/application.ts b/frontend/src/types/application.ts index 5030c9e35..fac022d49 100644 --- a/frontend/src/types/application.ts +++ b/frontend/src/types/application.ts @@ -47,3 +47,8 @@ export interface ApplicationFormData { form_title: string; questions: Question[]; } + +export interface AnswerItem { + id: number; + answer: string; +}