diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx
index 8d20dfbdd..91317ba3c 100644
--- a/frontend/src/App.tsx
+++ b/frontend/src/App.tsx
@@ -75,16 +75,9 @@ const App = () => {
}
/>
- {/*//todo /:clubid로 수정 필요*/}
- {/*}*/}
- {/*/>*/}
- } />
- {/*TODO: CreateForm은 관리자 기능이므로 추후 /admin/* 경로 안으로 이동 필요*/}
}
+ path='/application/:clubId'
+ element={}
/>
} />
diff --git a/frontend/src/components/common/InputField/InputField.tsx b/frontend/src/components/common/InputField/InputField.tsx
index ed1a08233..2bba35996 100644
--- a/frontend/src/components/common/InputField/InputField.tsx
+++ b/frontend/src/components/common/InputField/InputField.tsx
@@ -79,7 +79,7 @@ const InputField = ({
hasError={isError}
readOnly={readOnly}
style={{
- background: bgColor || '#F5F5F5',
+ background: bgColor || '#FFF',
color: textColor,
borderColor: borderColor,
}}
diff --git a/frontend/src/pages/AdminPage/application/CreateApplicationForm.styles.ts b/frontend/src/pages/AdminPage/application/CreateApplicationForm.styles.ts
index af2ea3f57..cc9650e56 100644
--- a/frontend/src/pages/AdminPage/application/CreateApplicationForm.styles.ts
+++ b/frontend/src/pages/AdminPage/application/CreateApplicationForm.styles.ts
@@ -31,7 +31,7 @@ export const AddQuestionButton = styled.button`
font-weight: 500;
background: white;
color: #555;
- margin-bottom: 200px;
+ margin-bottom: 60px;
cursor: pointer;
`;
@@ -41,3 +41,30 @@ export const QuestionDivider = styled.hr`
border: none;
border-top: 1px solid #ddd;
`;
+
+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/CreateApplicationForm.tsx b/frontend/src/pages/AdminPage/application/CreateApplicationForm.tsx
index 13a92f6ed..a37828f2d 100644
--- a/frontend/src/pages/AdminPage/application/CreateApplicationForm.tsx
+++ b/frontend/src/pages/AdminPage/application/CreateApplicationForm.tsx
@@ -134,6 +134,9 @@ const CreateApplicationForm = () => {
질문 추가 +
+
+ 제출하기
+
>
);
diff --git a/frontend/src/pages/AdminPage/application/answer/AnswerApplicationForm.styles.ts b/frontend/src/pages/AdminPage/application/answer/AnswerApplicationForm.styles.ts
index 02ff36905..c731731af 100644
--- a/frontend/src/pages/AdminPage/application/answer/AnswerApplicationForm.styles.ts
+++ b/frontend/src/pages/AdminPage/application/answer/AnswerApplicationForm.styles.ts
@@ -9,6 +9,12 @@ export const FormTitle = styled.h1`
margin-bottom: 46px;
`;
+export const QuestionsWrapper = styled.div`
+ display: flex;
+ flex-direction: column;
+ gap: 50px;
+`;
+
export const ButtonWrapper = styled.div`
display: flex;
justify-content: flex-end;
diff --git a/frontend/src/pages/AdminPage/application/answer/AnswerApplicationForm.tsx b/frontend/src/pages/AdminPage/application/answer/AnswerApplicationForm.tsx
index 8e4527ef4..b728f844a 100644
--- a/frontend/src/pages/AdminPage/application/answer/AnswerApplicationForm.tsx
+++ b/frontend/src/pages/AdminPage/application/answer/AnswerApplicationForm.tsx
@@ -9,8 +9,7 @@ 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 { clubId } = useParams<{ clubId: string }>();
const { data: clubDetail, error } = useGetClubDetail(clubId || '');
const { onAnswerChange, getAnswersById } = useAnswers();
if (!clubDetail) {
@@ -33,14 +32,16 @@ const AnswerApplicationForm = () => {
tags={clubDetail.tags}
/>
{mockData.form_title}
- {mockData.questions.map((q) => (
-
- ))}
+
+ {mockData.questions.map((q) => (
+
+ ))}
+
제출하기
diff --git a/frontend/src/pages/AdminPage/application/components/QuestionAnswerer/QuestionAnswerer.tsx b/frontend/src/pages/AdminPage/application/components/QuestionAnswerer/QuestionAnswerer.tsx
index 629a8a2ee..b80465c50 100644
--- a/frontend/src/pages/AdminPage/application/components/QuestionAnswerer/QuestionAnswerer.tsx
+++ b/frontend/src/pages/AdminPage/application/components/QuestionAnswerer/QuestionAnswerer.tsx
@@ -47,14 +47,13 @@ const QuestionAnswerer = ({
case 'CHOICE':
case 'MULTI_CHOICE':
return (
- <>>
- // onChange(question.id, value)}
- // />
+ onChange(question.id, value)}
+ />
);
default:
diff --git a/frontend/src/pages/AdminPage/application/fields/Choice.tsx b/frontend/src/pages/AdminPage/application/fields/Choice.tsx
index 4595bf44b..70431d8ca 100644
--- a/frontend/src/pages/AdminPage/application/fields/Choice.tsx
+++ b/frontend/src/pages/AdminPage/application/fields/Choice.tsx
@@ -4,16 +4,10 @@ import QuestionDescription from '@/pages/AdminPage/application/components/Questi
import InputField from '@/components/common/InputField/InputField';
import { APPLICATION_FORM } from '@/constants/APPLICATION_FORM';
import { ChoiceProps } from '@/types/application';
-import { useState } from 'react';
const MIN_ITEMS = 2;
const MAX_ITEMS = 6;
-// todo inputField clear 버튼 빼기
-// todo 입력 필드안에 항목 삭제 아이콘 추가
-// todo mode : answer일때 다중, 단일 조건에 따라 선택 가능하도록 UI 및 기능 추가 필요
-// todo isMulti나 질문 타입을 props로 받아서 단일 / 다중 판단 하면 될듯
-
const Choice = ({
id,
title,
@@ -25,9 +19,10 @@ const Choice = ({
items = [],
isMulti,
onItemsChange,
+ answer = [],
+ onAnswerChange,
}: ChoiceProps) => {
- const [selected, setSelected] = useState([]);
-
+ // — 아이템 텍스트 변경(빌더 모드 전용)
const handleItemChange = (index: number, newValue: string) => {
const updated = items.map((item, i) =>
i === index ? { ...item, value: newValue } : item,
@@ -35,27 +30,36 @@ const Choice = ({
onItemsChange?.(updated);
};
+ // — 아이템 추가(빌더 모드 전용)
const handleAddItem = () => {
if (items.length >= MAX_ITEMS) return;
onItemsChange?.([...items, { value: '' }]);
};
+ // — 아이템 삭제(빌더 모드 전용)
const handleDeleteItem = (index: number) => {
if (items.length <= MIN_ITEMS) return;
const updated = items.filter((_, i) => i !== index);
onItemsChange?.(updated);
};
- const handleSelect = (index: number) => {
+ const handleSelect = (idx: number) => {
if (mode !== 'answer') return;
+ const value = items[idx].value;
+ if (!value) return;
+
if (isMulti) {
- setSelected((prev) =>
- prev.includes(index)
- ? prev.filter((i) => i !== index)
- : [...prev, index],
- );
+ if (Array.isArray(answer)) {
+ if (answer.includes(value)) {
+ onAnswerChange?.(answer.filter((v) => v !== value));
+ } else {
+ onAnswerChange?.([...answer, value]);
+ }
+ }
+ // 다중 선택: 이미 포함되어 있으면 제거, 아니면 추가
} else {
- setSelected([index]);
+ // 단일 선택: 클릭된 값만 넘김
+ onAnswerChange?.(value);
}
};
@@ -73,51 +77,47 @@ const Choice = ({
mode={mode}
onDescriptionChange={onDescriptionChange}
/>
- {items.map((item, index) => (
- handleSelect(index)}
- data-selected={
- mode === 'answer' && selected.includes(index) ? 'true' : undefined
- }
- >
- handleItemChange(index, e.target.value)}
- placeholder={APPLICATION_FORM.CHOICE.placeholder}
- disabled={false}
- readOnly={mode === 'answer'}
- showClearButton={false}
- bgColor={
- mode === 'answer' && selected.includes(index)
- ? '#FFE4DA'
- : undefined
- }
- textColor={
- mode === 'answer'
- ? selected.includes(index)
- ? 'rgba(0,0,0,0.8)'
- : 'rgba(0,0,0,0.3)'
- : undefined
- }
- borderColor={
- mode === 'answer' && selected.includes(index)
- ? '#FF5414'
- : undefined
- }
- />
- {mode === 'builder' && items.length > MIN_ITEMS && (
- {
- e.stopPropagation();
- handleDeleteItem(index);
- }}
- >
- 삭제
-
- )}
-
- ))}
+
+ {items.map((item, index) => {
+ // ▶ selected 대신, answer.includes(item.value)로 판별
+ const isSelected = mode === 'answer' && answer.includes(item.value);
+
+ return (
+ handleSelect(index)}
+ data-selected={isSelected ? 'true' : undefined}
+ >
+ handleItemChange(index, e.target.value)}
+ placeholder={APPLICATION_FORM.CHOICE.placeholder}
+ readOnly={mode === 'answer'}
+ showClearButton={false}
+ bgColor={isSelected ? '#FFE4DA' : '#F5F5F5'}
+ textColor={
+ mode === 'answer'
+ ? isSelected
+ ? 'rgba(0,0,0,0.8)'
+ : 'rgba(0,0,0,0.3)'
+ : undefined
+ }
+ borderColor={isSelected ? '#FF5414' : undefined}
+ />
+
+ {mode === 'builder' && items.length > MIN_ITEMS && (
+ {
+ e.stopPropagation();
+ handleDeleteItem(index);
+ }}
+ >
+ 삭제
+
+ )}
+
+ );
+ })}
{mode === 'builder' && items.length < MAX_ITEMS && (
diff --git a/frontend/src/pages/AdminPage/components/SideBar/SideBar.tsx b/frontend/src/pages/AdminPage/components/SideBar/SideBar.tsx
index 48a30cddf..4bf4a9137 100644
--- a/frontend/src/pages/AdminPage/components/SideBar/SideBar.tsx
+++ b/frontend/src/pages/AdminPage/components/SideBar/SideBar.tsx
@@ -14,8 +14,8 @@ const tabs = [
{ label: '기본 정보 수정', path: '/admin/club-info' },
{ label: '모집 정보 수정', path: '/admin/recruit-edit' },
{ label: '활동 사진 수정', path: '/admin/photo-edit' },
- { label: '계정 관리', path: '/admin/account-edit' },
{ label: '지원서 관리', path: '/admin/application-edit' },
+ { label: '계정 관리', path: '/admin/account-edit' },
];
const SideBar = ({ clubLogo, clubName }: SideBarProps) => {
@@ -44,7 +44,6 @@ const SideBar = ({ clubLogo, clubName }: SideBarProps) => {
localStorage.removeItem('accessToken');
navigate('/admin/login', { replace: true });
} catch (error) {
- console.error(error);
alert('로그아웃에 실패했습니다.');
}
};
diff --git a/frontend/src/pages/ClubDetailPage/components/ClubApplyButton/ClubApplyButton.tsx b/frontend/src/pages/ClubDetailPage/components/ClubApplyButton/ClubApplyButton.tsx
index 78652f82a..bb0d43a94 100644
--- a/frontend/src/pages/ClubDetailPage/components/ClubApplyButton/ClubApplyButton.tsx
+++ b/frontend/src/pages/ClubDetailPage/components/ClubApplyButton/ClubApplyButton.tsx
@@ -1,5 +1,7 @@
import useMixpanelTrack from '@/hooks/useMixpanelTrack';
import styled from 'styled-components';
+import { useNavigate, useParams } from 'react-router-dom';
+import { useGetClubDetail } from '@/hooks/queries/club/useGetClubDetail';
interface ButtonProps {
recruitmentForm?: string;
@@ -39,11 +41,16 @@ const ClubApplyButton = ({
recruitmentForm,
presidentPhoneNumber,
}: ButtonProps) => {
+ const { clubId } = useParams<{ clubId: string }>();
const trackEvent = useMixpanelTrack();
+ const navigate = useNavigate();
const handleClick = () => {
trackEvent('Club Apply Button Clicked');
+ //TODO: 지원서를 작성한 동아리의 경우에만 리다이렉트
+ //navigate(`/application/${clubId}`);
+
// [x] FIXME: recruitmentForm 있을 때는 리다이렉트
if (presidentPhoneNumber) {
alert(`${presidentPhoneNumber} 으로 연락하여 지원해 주세요.`);
diff --git a/frontend/src/types/application.ts b/frontend/src/types/application.ts
index fac022d49..2296ccc02 100644
--- a/frontend/src/types/application.ts
+++ b/frontend/src/types/application.ts
@@ -41,6 +41,8 @@ export interface ChoiceProps extends QuestionComponentProps {
items?: { value: string }[];
isMulti?: boolean;
onItemsChange?: (newItems: { value: string }[]) => void;
+ answer?: string | string[];
+ onAnswerChange?: (value: string[] | string) => void;
}
export interface ApplicationFormData {