Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix-fe: 지원자 공고 생성, 상태 입력 발생되는 버그 수정 #386

Merged
merged 9 commits into from
Aug 13, 2024
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
import { useState } from 'react';

import { Question, QuestionChoice, QuestionControlActionType, QuestionOptionValue } from '@customTypes/dashboard';
import { Question, QuestionControlActionType, QuestionOptionValue } from '@customTypes/dashboard';

import InputField from '@components/common/InputField';
import Dropdown from '@components/common/Dropdown';
import ToggleSwitch from '@components/common/ToggleSwitch';
import { QUESTION_TYPE_NAME } from '@constants/constants';

import CheckBoxField from '@components/recruitment/CheckBoxField';
import RadioInputField from '@components/recruitment/RadioInputField';
import QuestionController from '../QuestionController';

import S from './style';
import QuestionChoicesBuilder from '../QuestionChoicesBuilder';

interface QuestionBuilderProps {
index: number;
Expand All @@ -23,10 +25,6 @@ interface QuestionBuilderProps {
deleteQuestion: (index: number) => void;
}

function getSortedChoices(choices: QuestionChoice[]): QuestionOptionValue[] {
return choices?.sort((a, b) => a.orderIndex - b.orderIndex).map((item) => ({ value: item.choice }));
}

export default function QuestionBuilder({
index,
question,
Expand All @@ -40,7 +38,6 @@ export default function QuestionBuilder({
}: QuestionBuilderProps) {
const [title, setTitle] = useState<string>(question?.question || '');
const [currentQuestionType, setCurrentQuestionType] = useState<Question['type']>(question?.type || 'SHORT_ANSWER');
const [choices, setChoices] = useState<QuestionOptionValue[]>(getSortedChoices(question?.choices) || []);
const [isRequired, setIsRequired] = useState<boolean>(question?.required || true);

const handleChangeTitle = (event: React.ChangeEvent<HTMLInputElement>) => {
Expand All @@ -50,14 +47,12 @@ export default function QuestionBuilder({

const handleChangeQuestionType = (type: Question['type']) => {
if (type === currentQuestionType) return;
if (type === 'SHORT_ANSWER' || type === 'LONG_ANSWER') setChoices([]);

setCurrentQuestionType(type);
setQuestionType(index)(type);
};

const handleUpdateQuestionChoices = (newChoices: QuestionOptionValue[]) => {
setChoices(newChoices);
setQuestionOptions(index)(newChoices);
};

Expand Down Expand Up @@ -99,10 +94,17 @@ export default function QuestionBuilder({
/>
</S.InputBox>

{(currentQuestionType === 'SINGLE_CHOICE' || currentQuestionType === 'MULTIPLE_CHOICE') && (
<QuestionChoicesBuilder
choices={choices}
onUpdate={handleUpdateQuestionChoices}
{currentQuestionType === 'SINGLE_CHOICE' && (
<RadioInputField
choices={question.choices}
setChoices={handleUpdateQuestionChoices}
/>
)}

{currentQuestionType === 'MULTIPLE_CHOICE' && (
<CheckBoxField
choices={question.choices}
setChoices={handleUpdateQuestionChoices}
/>
)}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ type Story = StoryObj<typeof QuestionChoicesBuilder>;
export const Default: Story = {
render: (args) => {
const [choices, setChoices] = useState<QuestionOptionValue[]>([
{ value: '첫 번째 옵션입니다.' },
{ value: '두 번째 옵션입니다.' },
{ value: '세 번째 옵션입니다.' },
{ choice: '첫 번째 옵션입니다.' },
{ choice: '두 번째 옵션입니다.' },
{ choice: '세 번째 옵션입니다.' },
]);

const onUpdate = (newChoices: QuestionOptionValue[]) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ export default function QuestionChoicesBuilder({ choices, onUpdate }: QuestionCh
const newChoices = [...choices];

if (index < newChoices.length) {
newChoices[index].value = newValue;
newChoices[index].choice = newValue;
} else {
newChoices.push({ value: newValue });
newChoices.push({ choice: newValue });
}

onUpdate(newChoices);
Expand All @@ -37,7 +37,7 @@ export default function QuestionChoicesBuilder({ choices, onUpdate }: QuestionCh
if (event.key === 'Enter') {
event.preventDefault();
if (index === choices.length - 1) {
onUpdate([...choices, { value: '' }]);
onUpdate([...choices, { choice: '' }]);
setTimeout(() => inputRefs.current[index + 1]?.focus(), 0);
} else {
inputRefs.current[index + 1]?.focus();
Expand All @@ -50,7 +50,7 @@ export default function QuestionChoicesBuilder({ choices, onUpdate }: QuestionCh
onUpdate(newChoices);
};

const choicesToRender = choices.length === 0 ? [{ value: '' }] : choices;
const choicesToRender = choices.length === 0 ? [{ choice: '' }] : choices;

return (
<S.Wrapper>
Expand All @@ -63,7 +63,7 @@ export default function QuestionChoicesBuilder({ choices, onUpdate }: QuestionCh
ref={(el) => setInputRef(el, index)}
type="text"
placeholder="옵션을 입력하세요."
value={choice.value}
value={choice.choice}
onChange={(event) => handleInputChange(event, index)}
onKeyDown={(event) => handleKeyDown(event, index)}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,12 @@ export default function Apply({
prevStep,
nextStep,
}: ApplyProps) {
const isNextBtnValid =
applyState.length === DEFAULT_QUESTION_LENGTH ||
applyState
.slice(DEFAULT_QUESTION_LENGTH)
.every((question) => question.question.trim() && question.choices.length !== 1);

return (
<S.Wrapper>
<S.Section>
Expand Down Expand Up @@ -106,7 +112,7 @@ export default function Apply({
</S.ButtonContent>
</Button>
<Button
disabled={false}
disabled={!isNextBtnValid}
onClick={nextStep}
size="sm"
color="white"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useRef, useState } from 'react';
import { useEffect, useRef, useState } from 'react';
import Button from '@components/common/Button';
import ChevronButton from '@components/common/ChevronButton';
import DateInput from '@components/common/DateInput';
Expand All @@ -23,7 +23,12 @@ export default function Recruitment({ recruitmentInfoState, setRecruitmentInfoSt
const today = new Date().toISOString().split('T')[0];
const startDateText = startDate ? formatDate(startDate) : '';
const endDateText = endDate ? formatDate(endDate) : '';
const isNextButtonValid = !!(endDate && contentText && startDate && title);

useEffect(() => {
setContentText(quillRef.current?.unprivilegedEditor?.getText());
}, [quillRef]);

const isNextButtonValid = !!(endDate && contentText?.trim() && startDate && title.trim());

const handleStartDate = (e: React.ChangeEvent<HTMLInputElement>) => {
setRecruitmentInfoState((prev) => ({
Expand Down Expand Up @@ -51,9 +56,6 @@ export default function Recruitment({ recruitmentInfoState, setRecruitmentInfoSt
...prev,
postingContent: string,
}));
};

const handlePostingContentBlur = () => {
setContentText(quillRef.current?.unprivilegedEditor?.getText());
};

Expand Down Expand Up @@ -99,7 +101,6 @@ export default function Recruitment({ recruitmentInfoState, setRecruitmentInfoSt
quillRef={quillRef}
value={recruitmentInfoState.postingContent}
onChange={handlePostingContentChange}
onBlur={handlePostingContentBlur}
/>
</S.RecruitDetailContainer>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,14 @@ const meta: Meta<typeof CheckBoxField> = {
},
tags: ['autodocs'],
argTypes: {
options: {
choices: {
description: 'CheckBoxOption으로 구성된 옵션 객체 배열입니다.',
control: { type: 'object' },
table: {
type: { summary: 'Option[]' },
},
},
setOptions: {
setChoices: {
description: '옵션 객체 배열을 설정하는 함수입니다.',
action: 'optionsChanged',
table: {
Expand All @@ -35,11 +35,11 @@ const meta: Meta<typeof CheckBoxField> = {
export default meta;
type Story = StoryObj<typeof meta>;

const defaultOptions = [{ value: '' }];
const defaultOptions = [{ choice: '' }];

export const Default: Story = {
args: {
options: defaultOptions,
choices: defaultOptions,
},
decorators: [
(Child) => {
Expand All @@ -49,8 +49,8 @@ export const Default: Story = {
<Child
args={{
...args,
setOptions: (options) => {
updateArgs({ options });
setChoices: (choices) => {
updateArgs({ choices });
},
}}
/>
Expand Down
43 changes: 22 additions & 21 deletions frontend/src/components/recruitment/CheckBoxField/index.tsx
Original file line number Diff line number Diff line change
@@ -1,39 +1,40 @@
import React, { useCallback, useEffect, useRef } from 'react';
import { QuestionOptionValue } from '@customTypes/dashboard';
import CheckBoxOption from '../CheckBoxOption';

import S from './style';
import CheckBoxOption from '../CheckBoxOption';

interface Option {
value: string;
interface ChoiceOption {
choice: string;
}

interface Props {
options: Option[];
setOptions: React.Dispatch<React.SetStateAction<Option[]>>;
choices: ChoiceOption[];
setChoices: (newChoices: QuestionOptionValue[]) => void;
}

export default function CheckBoxField({ options, setOptions }: Props) {
export default function CheckBoxField({ choices, setChoices }: Props) {
const inputRefs = useRef<(HTMLInputElement | null)[]>([]);

const handleOptionChange = (index: number, value: string) => {
const newOptions = [...options];
newOptions[index].value = value;
setOptions(newOptions);
const newOptions = [...choices];
newOptions[index].choice = value;
setChoices(newOptions);
};

const addOption = () => {
setOptions([...options, { value: '' }]);
setChoices([...choices, { choice: '' }]);
};

const deleteOption = (index: number) => {
const newOptions = options.slice();
const newOptions = choices.slice();
newOptions.splice(index, 1);
setOptions(newOptions);
setChoices(newOptions);
};

const handleOptionBlur = (index: number) => {
const isLastOption = index === options.length - 1;
const isEmptyValue = options[index].value.trim() === '';
const isLastOption = index === choices.length - 1;
const isEmptyValue = choices[index].choice.trim() === '';
if (!isLastOption && isEmptyValue) {
deleteOption(index);
}
Expand All @@ -43,13 +44,13 @@ export default function CheckBoxField({ options, setOptions }: Props) {
};

const focusLastOption = useCallback(() => {
inputRefs.current[options.length - 1]?.focus();
}, [options.length]);
inputRefs.current[choices.length - 1]?.focus();
}, [choices.length]);

const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>, index: number) => {
if ((e.key === 'Tab' || e.key === 'Enter') && !e.shiftKey) {
e.preventDefault();
if (index === options.length - 1) {
if (index === choices.length - 1) {
addOption();
}
focusLastOption();
Expand All @@ -58,23 +59,23 @@ export default function CheckBoxField({ options, setOptions }: Props) {

useEffect(() => {
focusLastOption();
}, [options.length, focusLastOption]);
}, [choices.length, focusLastOption]);

const setInputRefCallback = (index: number) => (node: HTMLInputElement) => {
inputRefs.current[index] = node;
};

return (
<S.Container>
{options.map((option, index) => (
{choices.map((choice, index) => (
<CheckBoxOption
// eslint-disable-next-line react/no-array-index-key
key={index}
isDisabled={false}
isDeleteBtn={options.length - 1 !== index}
isDeleteBtn={choices.length - 1 !== index}
onDeleteBtnClick={() => deleteOption(index)}
inputAttrs={{
value: option.value,
value: choice.choice,
ref: setInputRefCallback(index),
onChange: (e) => handleOptionChange(index, e.target.value),
onKeyDown: (e) => handleKeyDown(e, index),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,14 @@ const meta: Meta<typeof RadioInputField> = {
},
tags: ['autodocs'],
argTypes: {
options: {
choices: {
description: 'RadioInputOption으로 구성된 옵션 객체 배열입니다.',
control: { type: 'object' },
table: {
type: { summary: 'Option[]' },
},
},
setOptions: {
setChoices: {
description: '옵션 객체 배열을 설정하는 함수입니다.',
action: 'optionsChanged',
table: {
Expand All @@ -35,11 +35,11 @@ const meta: Meta<typeof RadioInputField> = {
export default meta;
type Story = StoryObj<typeof meta>;

const defaultOptions = [{ value: '' }];
const defaultOptions = [{ choice: '' }];

export const Default: Story = {
args: {
options: defaultOptions,
choices: defaultOptions,
},
decorators: [
(Child) => {
Expand All @@ -49,8 +49,8 @@ export const Default: Story = {
<Child
args={{
...args,
setOptions: (options) => {
updateArgs({ options });
setChoices: (choices) => {
updateArgs({ choices });
},
}}
/>
Expand Down
Loading
Loading