Skip to content

✨ feat : 케밥 답변수정 구현 #59

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

Merged
merged 1 commit into from
Dec 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
23 changes: 23 additions & 0 deletions src/components/AnswerEdit/index.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import PropTypes from 'prop-types';
import { ReactComponent as Edit } from 'assets/images/icons/ic_Edit.svg';

// eslint-disable-next-line
const AnswerEdit = ({ id, setEditId, answerId }) => {
AnswerEdit.propTypes = {
id: PropTypes.number.isRequired,
setEditId: PropTypes.func.isRequired,
};

const handleEdit = () => {
setEditId(answerId);
};

return (
<button type='button' className='flex justify-center items-center gap-2 rounded-lg w-[103px] h-[30px] text-gray-50 hover:text-gray-60 hover:bg-gray-20' onClick={handleEdit}>
<Edit className='w-3.5 h-3.5 fill-current' />
답변수정
</button>
);
};

export default AnswerEdit;
86 changes: 86 additions & 0 deletions src/components/AnswerEditForm/index.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import PropTypes from 'prop-types';
import { useState } from 'react';
import { putAnswer } from 'api/answers';

// eslint-disable-next-line
const AnswerEditForm = ({ answer, name, imageSource, id, setEditId, setQuestionList }) => {
AnswerEditForm.propTypes = {
answer: PropTypes.shape({
id: PropTypes.number.isRequired,
content: PropTypes.string.isRequired,
isRejected: PropTypes.bool,
createdAt: PropTypes.string.isRequired,
}),
name: PropTypes.string.isRequired,
imageSource: PropTypes.string,
id: PropTypes.number.isRequired,
};

AnswerEditForm.defaultProps = {
imageSource: 'https://fastly.picsum.photos/id/772/200/200.jpg?hmac=9euSj4JHTPr7uT5QWVmeNJ8JaqAXY8XmJnYfr_DfBJc',
};

// 초기값 설정 시 answer.content가 null일 경우 빈 문자열로 처리
const [textareaValue, setTextareaValue] = useState(answer.content === null || answer.content === 'reject' ? '' : answer.content);
const [isLoading, setIsLoading] = useState(false);
const [isValid, setIsValid] = useState(false);

const handleTextareaChange = (event) => {
const text = event.target.value;
setTextareaValue(text);
const isFormValid = text.trim() !== answer.content.trim() && text !== '' && text !== 'reject';
setIsValid(isFormValid);
};

const handleAnswerPatch = async (e) => {
e.preventDefault();
try {
setIsLoading(true);
const result = await putAnswer(answer.id, {
content: textareaValue, // textareaValue에서 내용을 가져옵니다.
isRejected: false, // 필요하다면 다른 데이터도 추가 가능합니다.
});
setQuestionList((prevQuestions) =>
prevQuestions.map((question) => {
if (question.id === id) {
return { ...question, answer: result };
}
return question;
}),
);
} catch (err) {
// handle error here (e.g., show error message)
} finally {
setIsLoading(false);
setEditId(null);
}
};

const renderProfileImg = () => <img src={imageSource} alt={`${name}의 프로필`} className='w-[32px] h-[32px] md:w-[48px] md:h-[48px] rounded-full object-cover' />;

const renderAnswerForm = () => (
<form onSubmit={handleAnswerPatch} className='flex w-full flex-col gap-[8px]'>
<textarea
className='w-full h-[186px] resize-none rounded-lg border-none p-[16px] bg-gray-20 text-base leading-[22px] text-secondary-900 placeholder:text-base placeholder:leading-[22px] placeholder:text-gray-40 focus:outline-brown-40'
placeholder='답변을 입력해주세요'
value={textareaValue}
onChange={handleTextareaChange}
/>
<button type='submit' className='py-[12px] rounded-lg bg-brown-40 text-base leading-[22px] text-gray-10 disabled:bg-brown-30' disabled={!isValid || isLoading}>
수정 완료
</button>
</form>
);

return (
<div className='flex gap-[12px]'>
{renderProfileImg()}
<div className='flex-1'>
<p className='mb-[4px] mr-[8px] inline-block text-sm leading-[18px] md:text-lg md:leading-[24px]'>{name}</p>
{renderAnswerForm()}
</div>
</div>
);
};

export default AnswerEditForm;
11 changes: 5 additions & 6 deletions src/components/Kebab/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ import { useEffect, useRef } from 'react';
import QuestionDelete from 'components/QuestionDelete';
import AnswerRejection from 'components/AnswerRejection';
import kebab from 'assets/images/icons/ic_Kebab.svg';
import { ReactComponent as Edit } from 'assets/images/icons/ic_Edit.svg';
import AnswerDelete from 'components/AnswerDelete';
import AnswerEdit from 'components/AnswerEdit';

const Kebab = ({ id, isAnswer, isKebabOpen, onKebabClick, onDeleteQuestion, onAnswerDeleted, setQuestionList }) => {
const Kebab = ({ id, isAnswer, isKebabOpen, onKebabClick, onDeleteQuestion, onAnswerDeleted, setQuestionList, setEditId, answerId }) => {
Kebab.propTypes = {
id: PropTypes.number.isRequired,
isAnswer: PropTypes.shape({
Expand All @@ -21,6 +21,8 @@ const Kebab = ({ id, isAnswer, isKebabOpen, onKebabClick, onDeleteQuestion, onAn
onDeleteQuestion: PropTypes.func.isRequired,
onAnswerDeleted: PropTypes.func.isRequired,
setQuestionList: PropTypes.func.isRequired,
setEditId: PropTypes.func.isRequired,
answerId: PropTypes.number.isRequired,
};

const menuRef = useRef(null);
Expand Down Expand Up @@ -69,10 +71,7 @@ const Kebab = ({ id, isAnswer, isKebabOpen, onKebabClick, onDeleteQuestion, onAn
) : (
<>
<div className='flex justify-center items-center'>
<button type='button' className='flex justify-center items-center gap-2 rounded-lg w-[103px] h-[30px] text-gray-50 hover:text-gray-60 hover:bg-gray-20'>
<Edit className='w-3.5 h-3.5 fill-current' />
<p>수정하기</p>
</button>
<AnswerEdit id={id} setEditId={setEditId} answerId={answerId} />
</div>
<div className='flex justify-center items-center'>
<QuestionDelete id={id} onDeleteQuestion={onDeleteQuestion} />
Expand Down
24 changes: 21 additions & 3 deletions src/components/QnAList/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import AnswerContent from 'components/AnswerContent';
import CountFavorite from 'components/CountFavorite';
import Kebab from 'components/Kebab';
import questionBoxImg from 'assets/images/img_QuestionBox.svg';
import AnswerEditForm from 'components/AnswerEditForm';

const QnAList = ({ name, imageSource, questionList, setQuestionList, onDeleteQuestion }) => {
QnAList.propTypes = {
Expand All @@ -33,9 +34,9 @@ const QnAList = ({ name, imageSource, questionList, setQuestionList, onDeleteQue
};

const [visibleMenuId, setVisibleMenuId] = useState(null);

const location = useLocation();
const isAnswerPage = location.pathname.startsWith('/post/') && location.pathname.includes('/answer');
const [editId, setEditId] = useState(null);

const handleKebabClick = (id) => {
setVisibleMenuId((prevVisibleMenuId) => (prevVisibleMenuId === id ? null : id));
Expand Down Expand Up @@ -79,11 +80,28 @@ const QnAList = ({ name, imageSource, questionList, setQuestionList, onDeleteQue
onDeleteQuestion={handleDeleteQuestion}
onAnswerDeleted={handleAnswerDeleted}
setQuestionList={setQuestionList}
setEditId={setEditId}
answerId={question.answer ? question.answer.id : null}
/>
)}
</div>
<QuestionContent createdAt={question.createdAt} content={question.content} />
<AnswerContent answer={question.answer} name={name} imageSource={imageSource} id={question.id} onAnswerSubmit={handleAnswerSubmit} />
<QuestionContent
createdAt={question.createdAt}
content={question.content || ''} // content가 null일 경우 기본 값 제공
/>
{question.answer && question.answer.id === editId ? (
<AnswerEditForm
answer={question.answer || { content: '' }} // answer가 없으면 빈 객체를 넘겨줌
name={name}
imageSource={imageSource}
id={question.id}
setEditId={setEditId}
setQuestionList={setQuestionList}
/>
) : (
<AnswerContent answer={question.answer} name={name} imageSource={imageSource} id={question.id} onAnswerSubmit={handleAnswerSubmit} />
)}

<CountFavorite like={question.like} dislike={question.dislike} />
</li>
))}
Expand Down
Loading