Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
6475ce9
feat: QuestionTitle 컴포넌트 추가
oesnuj May 26, 2025
5e3e5e7
feat: Question Description 컴포넌트 추가
oesnuj May 26, 2025
c8310bf
feat: 단답형 입력 필드 ShortText 컴포넌트 추가
oesnuj May 26, 2025
1c4e26b
feat: 선택형 질문 컴포넌트 Choice 추가
oesnuj May 26, 2025
03b96d5
feat: 질문 입력 필드용 placeholder 상수 추가
oesnuj May 26, 2025
4fbce29
feat: 질문 빌더 컴포넌트 QuestionBuilder 추가
oesnuj May 26, 2025
7c87edc
feat: 드롭다운 버튼 아이콘 SVG 파일 추가
oesnuj May 26, 2025
c5d7b7c
feat: 지원서 생성 컴포넌트 CreateForm 구현
oesnuj May 26, 2025
e23ab73
feat: 지원서 입력 컴포넌트(ApplicationForm) 추가
oesnuj May 26, 2025
4179669
feat: 지원서 뷰 및 작성 페이지 라우팅 추가
oesnuj May 26, 2025
54d8826
refactor: QuestionBuilder 스타일 파일 분리
oesnuj May 26, 2025
31b8d6e
feat: CreateForm 컴포넌트에 Todo 주석 추가
oesnuj May 26, 2025
7186329
feat: 질문 설명 입력란에 placeholder와 접근성 속성 추가
oesnuj May 26, 2025
a6fae14
fix: LONG_TEXT 질문 타입 최대 500자로 placeholder를 수정했습니다.
oesnuj May 26, 2025
b64e5e1
chore: 관리자 기능에 대한 CreateForm 경로 이동 필요 TODO 주석 추가
oesnuj May 26, 2025
30fb4a2
feat: 질문 항목에 옵션 필드 추가 및 답변 필수 여부 하드코딩 제거
oesnuj May 26, 2025
ecd73a3
feat: mock 데이터 안내 문구 수정
oesnuj May 26, 2025
6b14dc9
feat: 질문 제목 입력 필드에 placeholder와 접근성 속성 추가
oesnuj May 26, 2025
4906413
refactor: 잘못된 props명 options를 items로 변경
oesnuj May 26, 2025
9dd308b
refactor: QuestionBuilder의 required 상태 처리 로직 개선
oesnuj May 28, 2025
c31a915
refactor: Choice 컴포넌트 및 질문 타입 변경 로직 개선
oesnuj May 28, 2025
be67290
refactor: QuestionBuilder에서 불필요한 questionType 상태 제거
oesnuj May 30, 2025
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
5 changes: 5 additions & 0 deletions frontend/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
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 CreateForm from '@/pages/AdminPage/application/CreateForm';

Check warning on line 18 in frontend/src/App.tsx

View check run for this annotation

Codecov / codecov/patch

frontend/src/App.tsx#L17-L18

Added lines #L17 - L18 were not covered by tests

const queryClient = new QueryClient();

Expand Down Expand Up @@ -69,6 +71,9 @@
</AdminClubProvider>
}
/>
<Route path='view-application' element={<ApplicationForm />} />
{/*TODO: CreateForm은 관리자 기능이므로 추후 /admin/* 경로 안으로 이동 필요*/}
<Route path='create-application' element={<CreateForm />} />
<Route path='*' element={<Navigate to='/' replace />} />
</Routes>
</BrowserRouter>
Expand Down
3 changes: 3 additions & 0 deletions frontend/src/assets/images/icons/drop_button_icon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
12 changes: 12 additions & 0 deletions frontend/src/constants/APPLICATION_FORM.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
const APPLICATION_FORM = {

Check warning on line 1 in frontend/src/constants/APPLICATION_FORM.ts

View check run for this annotation

Codecov / codecov/patch

frontend/src/constants/APPLICATION_FORM.ts#L1

Added line #L1 was not covered by tests
SHORT_TEXT: {
placeholder: '답변입력란(최대 20자)',
},
LONG_TEXT: {
placeholder: '답변입력란(최대 500자)',
},
CHOICE: {
placeholder: '항목(최대 20자)',
},
} as const;
export default APPLICATION_FORM;

Check warning on line 12 in frontend/src/constants/APPLICATION_FORM.ts

View check run for this annotation

Codecov / codecov/patch

frontend/src/constants/APPLICATION_FORM.ts#L12

Added line #L12 was not covered by tests
71 changes: 71 additions & 0 deletions frontend/src/pages/AdminPage/application/ApplicationForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// 지원하기 : 지원서 입력 컴포넌트
// 특정 지원서를 사용자들이 입력할 수 있는 지원서 작성 부분
// Todo 추후 특정 지원서를 받아서 answer 모드로 렌더링
import { useState } from 'react';

Check warning on line 4 in frontend/src/pages/AdminPage/application/ApplicationForm.tsx

View check run for this annotation

Codecov / codecov/patch

frontend/src/pages/AdminPage/application/ApplicationForm.tsx#L4

Added line #L4 was not covered by tests
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<Record<number, QuestionData>>({

Check warning on line 19 in frontend/src/pages/AdminPage/application/ApplicationForm.tsx

View check run for this annotation

Codecov / codecov/patch

frontend/src/pages/AdminPage/application/ApplicationForm.tsx#L18-L19

Added lines #L18 - L19 were not covered by tests
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<AnswerData>({});

Check warning on line 59 in frontend/src/pages/AdminPage/application/ApplicationForm.tsx

View check run for this annotation

Codecov / codecov/patch

frontend/src/pages/AdminPage/application/ApplicationForm.tsx#L59

Added line #L59 was not covered by tests

const handleAnswerChange = (id: number) => (value: string) => {
setAnswers((prev) => ({

Check warning on line 62 in frontend/src/pages/AdminPage/application/ApplicationForm.tsx

View check run for this annotation

Codecov / codecov/patch

frontend/src/pages/AdminPage/application/ApplicationForm.tsx#L61-L62

Added lines #L61 - L62 were not covered by tests
...prev,
[id]: value,
}));
};

return <></>;

Check warning on line 68 in frontend/src/pages/AdminPage/application/ApplicationForm.tsx

View check run for this annotation

Codecov / codecov/patch

frontend/src/pages/AdminPage/application/ApplicationForm.tsx#L68

Added line #L68 was not covered by tests
};

export default ApplicationForm;

Check warning on line 71 in frontend/src/pages/AdminPage/application/ApplicationForm.tsx

View check run for this annotation

Codecov / codecov/patch

frontend/src/pages/AdminPage/application/ApplicationForm.tsx#L71

Added line #L71 was not covered by tests
142 changes: 142 additions & 0 deletions frontend/src/pages/AdminPage/application/CreateForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
// 지원서 제작하기 : 지원서 제작 컴포넌트
// 지원서 수정과 제작을 맡을 컴포넌트
// Todo: 질문 삭제 및 질문 추가 기능 구현
import { useState } from 'react';
import QuestionBuilder from '@/pages/AdminPage/application/components/QuestionBuilder/QuestionBuilder';

Check warning on line 5 in frontend/src/pages/AdminPage/application/CreateForm.tsx

View check run for this annotation

Codecov / codecov/patch

frontend/src/pages/AdminPage/application/CreateForm.tsx#L4-L5

Added lines #L4 - L5 were not covered by tests
Comment on lines +1 to +5
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

테스트 커버리지가 부족합니다.

정적 분석에서 지적한 대로 대부분의 라인이 테스트로 커버되지 않았습니다. 컴포넌트의 핵심 기능에 대한 단위 테스트 추가를 고려하세요.

단위 테스트 작성을 도와드릴까요? 다음과 같은 테스트 케이스들을 생성할 수 있습니다:

  • 초기 상태 렌더링 테스트
  • 질문 타입 변경 핸들러 테스트
  • 필수 여부 토글 테스트
🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 4-5: frontend/src/pages/AdminPage/application/CreateForm.tsx#L4-L5
Added lines #L4 - L5 were not covered by tests

🤖 Prompt for AI Agents
In frontend/src/pages/AdminPage/application/CreateForm.tsx lines 1 to 5, the
component lacks sufficient test coverage. Add unit tests covering core
functionalities such as initial state rendering, handling question type changes,
and toggling required fields. Write tests that render the component, simulate
user interactions for changing question types and toggling required flags, and
assert expected state and UI updates.


type QuestionType =
| 'SHORT_TEXT'
| 'LONG_TEXT'
| 'CHOICE'
| 'MULTI_CHOICE'
| 'EMAIL'
| 'PHONE_NUMBER'
| 'NAME';

type Question = {
title: string;
description: string;
type: QuestionType;
options: {
required: boolean;
};
items?: { value: string }[];
};

const CreateForm = () => {
const [questions, setQuestions] = useState<Record<number, Question>>({

Check warning on line 27 in frontend/src/pages/AdminPage/application/CreateForm.tsx

View check run for this annotation

Codecov / codecov/patch

frontend/src/pages/AdminPage/application/CreateForm.tsx#L26-L27

Added lines #L26 - L27 were not covered by tests
1: {
title: '이름을 입력해주세요',
description: '본명을 입력해 주세요',
options: {
required: true,
},
type: 'SHORT_TEXT',
},
2: {
title: '지원 분야를 선택해주세요',
description: '중복 선택 가능합니다',
options: {
required: false,
},
items: [
{ value: '프론트엔드' },
{ value: '백엔드' },
{ value: '디자인' },
],
type: 'MULTI_CHOICE',
},
});

const handleTitleChange = (id: number) => (value: string) => {
setQuestions((prev) => ({

Check warning on line 52 in frontend/src/pages/AdminPage/application/CreateForm.tsx

View check run for this annotation

Codecov / codecov/patch

frontend/src/pages/AdminPage/application/CreateForm.tsx#L51-L52

Added lines #L51 - L52 were not covered by tests
...prev,
[id]: {
...prev[id],
title: value,
},
}));
};

const handleDescriptionChange = (id: number) => (value: string) => {
setQuestions((prev) => ({

Check warning on line 62 in frontend/src/pages/AdminPage/application/CreateForm.tsx

View check run for this annotation

Codecov / codecov/patch

frontend/src/pages/AdminPage/application/CreateForm.tsx#L61-L62

Added lines #L61 - L62 were not covered by tests
...prev,
[id]: {
...prev[id],
description: value,
},
}));
};

const handleItemsChange = (id: number) => (newItems: { value: string }[]) => {
setQuestions((prev) => ({

Check warning on line 72 in frontend/src/pages/AdminPage/application/CreateForm.tsx

View check run for this annotation

Codecov / codecov/patch

frontend/src/pages/AdminPage/application/CreateForm.tsx#L71-L72

Added lines #L71 - L72 were not covered by tests
...prev,
[id]: {
...prev[id],
items: newItems,
},
}));
};

const handleTypeChange = (id: number) => (newType: QuestionType) => {
setQuestions((prev) => {
const prevQuestion = prev[id];

Check warning on line 83 in frontend/src/pages/AdminPage/application/CreateForm.tsx

View check run for this annotation

Codecov / codecov/patch

frontend/src/pages/AdminPage/application/CreateForm.tsx#L81-L83

Added lines #L81 - L83 were not covered by tests
const isChoiceType = newType === 'CHOICE' || newType === 'MULTI_CHOICE';

const updatedQuestion: Question = {

Check warning on line 86 in frontend/src/pages/AdminPage/application/CreateForm.tsx

View check run for this annotation

Codecov / codecov/patch

frontend/src/pages/AdminPage/application/CreateForm.tsx#L86

Added line #L86 was not covered by tests
...prevQuestion,
type: newType,
};

if (isChoiceType) {
updatedQuestion.items =

Check warning on line 92 in frontend/src/pages/AdminPage/application/CreateForm.tsx

View check run for this annotation

Codecov / codecov/patch

frontend/src/pages/AdminPage/application/CreateForm.tsx#L92

Added line #L92 was not covered by tests
!prevQuestion.items || prevQuestion.items.length < 2
? [{ value: '' }, { value: '' }]
: prevQuestion.items;
} else {
delete updatedQuestion.items;

Check warning on line 97 in frontend/src/pages/AdminPage/application/CreateForm.tsx

View check run for this annotation

Codecov / codecov/patch

frontend/src/pages/AdminPage/application/CreateForm.tsx#L97

Added line #L97 was not covered by tests
}

return {

Check warning on line 100 in frontend/src/pages/AdminPage/application/CreateForm.tsx

View check run for this annotation

Codecov / codecov/patch

frontend/src/pages/AdminPage/application/CreateForm.tsx#L100

Added line #L100 was not covered by tests
...prev,
[id]: updatedQuestion,
};
});
};

const handleRequiredChange = (id: number) => (value: boolean) => {
setQuestions((prev) => ({

Check warning on line 108 in frontend/src/pages/AdminPage/application/CreateForm.tsx

View check run for this annotation

Codecov / codecov/patch

frontend/src/pages/AdminPage/application/CreateForm.tsx#L107-L108

Added lines #L107 - L108 were not covered by tests
...prev,
[id]: {
...prev[id],
options: {
...prev[id].options,
required: value,
},
},
}));
};

return (

Check warning on line 120 in frontend/src/pages/AdminPage/application/CreateForm.tsx

View check run for this annotation

Codecov / codecov/patch

frontend/src/pages/AdminPage/application/CreateForm.tsx#L120

Added line #L120 was not covered by tests
<>
{Object.entries(questions).map(([id, question]) => (

Check warning on line 122 in frontend/src/pages/AdminPage/application/CreateForm.tsx

View check run for this annotation

Codecov / codecov/patch

frontend/src/pages/AdminPage/application/CreateForm.tsx#L122

Added line #L122 was not covered by tests
<QuestionBuilder
key={id}
id={Number(id)}
title={question.title}
description={question.description}
required={question.options?.required ?? false}
items={question.items}
onTitleChange={handleTitleChange(Number(id))}
onDescriptionChange={handleDescriptionChange(Number(id))}
onItemsChange={handleItemsChange(Number(id))}
onTypeChange={handleTypeChange(Number(id))}
onRequiredChange={handleRequiredChange(Number(id))}
type={question.type}
/>
))}
</>
);
};

export default CreateForm;

Check warning on line 142 in frontend/src/pages/AdminPage/application/CreateForm.tsx

View check run for this annotation

Codecov / codecov/patch

frontend/src/pages/AdminPage/application/CreateForm.tsx#L142

Added line #L142 was not covered by tests
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import styled from 'styled-components';

Check warning on line 1 in frontend/src/pages/AdminPage/application/components/QuestionBuilder/QuestionBuilder.styles.ts

View check run for this annotation

Codecov / codecov/patch

frontend/src/pages/AdminPage/application/components/QuestionBuilder/QuestionBuilder.styles.ts#L1

Added line #L1 was not covered by tests

export const QuestionMenu = styled.div`

Check warning on line 3 in frontend/src/pages/AdminPage/application/components/QuestionBuilder/QuestionBuilder.styles.ts

View check run for this annotation

Codecov / codecov/patch

frontend/src/pages/AdminPage/application/components/QuestionBuilder/QuestionBuilder.styles.ts#L3

Added line #L3 was not covered by tests
display: flex;
width: 140px;
flex-direction: column;
gap: 4px;
`;

export const RequiredToggleButton = styled.div`

Check warning on line 10 in frontend/src/pages/AdminPage/application/components/QuestionBuilder/QuestionBuilder.styles.ts

View check run for this annotation

Codecov / codecov/patch

frontend/src/pages/AdminPage/application/components/QuestionBuilder/QuestionBuilder.styles.ts#L10

Added line #L10 was not covered by tests
display: flex;
border: none;
padding: 12px 16px;
justify-content: space-between;
align-items: center;
border-radius: 0.375rem;
background: #f5f5f5;
cursor: pointer;
margin: 0;
color: #787878;
font-size: 0.875rem;
font-weight: 600;
`;

export const RequiredToggleCircle = styled.span<{ active?: boolean }>`

Check warning on line 25 in frontend/src/pages/AdminPage/application/components/QuestionBuilder/QuestionBuilder.styles.ts

View check run for this annotation

Codecov / codecov/patch

frontend/src/pages/AdminPage/application/components/QuestionBuilder/QuestionBuilder.styles.ts#L25

Added line #L25 was not covered by tests
position: relative;
width: 16px;
height: 16px;
border-radius: 50%;
border: 1px solid ${(props) => (props.active ? '#ff5000' : '#ccc')};
background-color: ${(props) => (props.active ? '#ff5000' : 'white')};

${({ active }) =>
active &&
`
background-color: #fff;
&::after {
content: '';
width: 10px;
height: 10px;
background-color: #ff5000;
border-radius: 50%;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
`}
`;

export const DropDownWrapper = styled.div`

Check warning on line 51 in frontend/src/pages/AdminPage/application/components/QuestionBuilder/QuestionBuilder.styles.ts

View check run for this annotation

Codecov / codecov/patch

frontend/src/pages/AdminPage/application/components/QuestionBuilder/QuestionBuilder.styles.ts#L51

Added line #L51 was not covered by tests
position: relative;
width: 100%;
`;

export const Dropdown = styled.select`

Check warning on line 56 in frontend/src/pages/AdminPage/application/components/QuestionBuilder/QuestionBuilder.styles.ts

View check run for this annotation

Codecov / codecov/patch

frontend/src/pages/AdminPage/application/components/QuestionBuilder/QuestionBuilder.styles.ts#L56

Added line #L56 was not covered by tests
display: flex;
width: 100%;
border: none;
padding: 12px 16px;
border-radius: 0.375rem;
background: #f5f5f5;
color: #787878;
font-size: 0.875rem;
font-weight: 600;
cursor: pointer;
appearance: none;
`;

export const DropdownIcon = styled.img`

Check warning on line 70 in frontend/src/pages/AdminPage/application/components/QuestionBuilder/QuestionBuilder.styles.ts

View check run for this annotation

Codecov / codecov/patch

frontend/src/pages/AdminPage/application/components/QuestionBuilder/QuestionBuilder.styles.ts#L70

Added line #L70 was not covered by tests
position: absolute;
top: 50%;
right: 19px;
transform: translateY(-50%);
pointer-events: none;
`;

export const SelectionToggleWrapper = styled.div`

Check warning on line 78 in frontend/src/pages/AdminPage/application/components/QuestionBuilder/QuestionBuilder.styles.ts

View check run for this annotation

Codecov / codecov/patch

frontend/src/pages/AdminPage/application/components/QuestionBuilder/QuestionBuilder.styles.ts#L78

Added line #L78 was not covered by tests
display: flex;
background-color: #f7f7f7;
border-radius: 0.375rem;
padding: 2px;
`;

export const SelectionToggleButton = styled.button<{ active: boolean }>`

Check warning on line 85 in frontend/src/pages/AdminPage/application/components/QuestionBuilder/QuestionBuilder.styles.ts

View check run for this annotation

Codecov / codecov/patch

frontend/src/pages/AdminPage/application/components/QuestionBuilder/QuestionBuilder.styles.ts#L85

Added line #L85 was not covered by tests
border: none;
background-color: ${(props) => (props.active ? '#ddd' : 'transparent')};
color: #787878;
font-size: 0.875rem;
border-radius: 0.375rem;
padding: 10px;
font-weight: 600;
cursor: pointer;
letter-spacing: -0.42px;
white-space: nowrap;
`;

export const QuestionWrapper = styled.div`

Check warning on line 98 in frontend/src/pages/AdminPage/application/components/QuestionBuilder/QuestionBuilder.styles.ts

View check run for this annotation

Codecov / codecov/patch

frontend/src/pages/AdminPage/application/components/QuestionBuilder/QuestionBuilder.styles.ts#L98

Added line #L98 was not covered by tests
display: flex;
gap: 36px;
`;
Comment on lines +1 to +101
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

색상 상수화 및 디자인 토큰 도입을 고려해주세요.

여러 색상값(#ff5000, #f5f5f5, #787878 등)이 반복 사용되고 있습니다. 디자인 시스템의 일관성을 위해 색상을 상수화하는 것을 권장합니다.

색상 상수 파일을 만들어 다음과 같이 사용하는 것을 제안합니다:

// theme/colors.ts
export const COLORS = {
  PRIMARY: '#ff5000',
  GRAY_LIGHT: '#f5f5f5',
  GRAY_MEDIUM: '#787878',
  GRAY_BORDER: '#ccc',
  GRAY_DARK: '#ddd',
  WHITE: '#ffffff',
} as const;
+import { COLORS } from '@/theme/colors';

export const RequiredToggleButton = styled.div`
  // ...
-  background: #f5f5f5;
+  background: ${COLORS.GRAY_LIGHT};
-  color: #787878;
+  color: ${COLORS.GRAY_MEDIUM};
  // ...
`;
🤖 Prompt for AI Agents
In
frontend/src/pages/AdminPage/application/components/QuestionBuilder/QuestionBuilder.styles.ts,
there are multiple hardcoded color values such as #ff5000, #f5f5f5, and #787878.
To improve maintainability and ensure design consistency, define these colors as
constants in a separate theme/colors.ts file and import them into this styles
file. Replace all instances of these color codes with the corresponding
constants from the color palette.

Loading