diff --git a/src/components/AnswerContent/index.jsx b/src/components/AnswerContent/index.jsx index fb1544b..151c72b 100644 --- a/src/components/AnswerContent/index.jsx +++ b/src/components/AnswerContent/index.jsx @@ -4,7 +4,7 @@ import { useEffect, useState } from 'react'; import formatCreatedAt from 'utils/dateUtils'; import { postAnswer } from 'api/answers'; -const AnswerContent = ({ answer, name, imageSource, id, onAnswerSubmit }) => { +const AnswerContent = ({ answer, name, imageSource, id, onAnswerSubmit, setIsKebabLoading, setIsToast }) => { AnswerContent.propTypes = { answer: PropTypes.shape({ id: PropTypes.number.isRequired, @@ -16,6 +16,8 @@ const AnswerContent = ({ answer, name, imageSource, id, onAnswerSubmit }) => { imageSource: PropTypes.string, id: PropTypes.number.isRequired, onAnswerSubmit: PropTypes.func.isRequired, + setIsKebabLoading: PropTypes.func.isRequired, + setIsToast: PropTypes.func.isRequired, }; AnswerContent.defaultProps = { @@ -45,15 +47,21 @@ const AnswerContent = ({ answer, name, imageSource, id, onAnswerSubmit }) => { let response; try { + setIsKebabLoading(true); setIsLoading(true); response = await postAnswer(id, postBody); setUpdatedAnswer(response); onAnswerSubmit(id, response); + setIsToast('등록'); } catch (err) { // eslint-disable-next-line console.error(err); } finally { + setIsKebabLoading(false); setIsLoading(false); + setTimeout(() => { + setIsToast(null); + }, 3000); } }; diff --git a/src/components/AnswerDelete/index.jsx b/src/components/AnswerDelete/index.jsx index 4603c3a..79584aa 100644 --- a/src/components/AnswerDelete/index.jsx +++ b/src/components/AnswerDelete/index.jsx @@ -3,18 +3,38 @@ import { ReactComponent as Close } from 'assets/images/icons/ic_Close.svg'; import PropTypes from 'prop-types'; import { useState } from 'react'; import { useNavigate } from 'react-router-dom'; +import ConfirmModal from 'components/ConfirmModal'; // Import the modal component -const AnswerDelete = ({ answerId, onAnswerDeleted }) => { +const AnswerDelete = ({ id, answerId, onAnswerDeleted, onKebabClick, setIsKebabLoading, setIsToast, editId, setEditId }) => { AnswerDelete.propTypes = { + id: PropTypes.number.isRequired, answerId: PropTypes.number.isRequired, onAnswerDeleted: PropTypes.func.isRequired, + onKebabClick: PropTypes.func.isRequired, + setIsKebabLoading: PropTypes.func.isRequired, + setIsToast: PropTypes.func.isRequired, + editId: PropTypes.number.isRequired, + setEditId: PropTypes.func.isRequired, }; const [isDeleting, setIsDeleting] = useState(false); - // const [error, setError] = useState(null); const navigate = useNavigate(); - const handleAnswerDelete = async () => { + const [showModal, setShowModal] = useState(false); + + const handleDelete = async () => { + setShowModal(true); // Show the modal when delete is clicked + }; + + const handleModalCancel = () => { + onKebabClick(id); + setShowModal(false); // Close the modal if canceled + }; + + const handleModalConfirm = async () => { + onKebabClick(id); + setIsKebabLoading(true); + setShowModal(false); // Close the modal setIsDeleting(true); try { @@ -23,23 +43,34 @@ const AnswerDelete = ({ answerId, onAnswerDeleted }) => { throw new Error('답변 삭제 중 오류가 발생했습니다.'); } onAnswerDeleted(answerId); + setIsToast('답변'); } catch (err) { navigate('/'); } finally { setIsDeleting(false); + setIsKebabLoading(false); + if (editId !== null) { + setEditId(null); + } + setTimeout(() => { + setIsToast(null); + }, 3000); } }; return ( - + <> + + + ); }; diff --git a/src/components/AnswerEdit/index.jsx b/src/components/AnswerEdit/index.jsx index b6f1a6f..c20af6d 100644 --- a/src/components/AnswerEdit/index.jsx +++ b/src/components/AnswerEdit/index.jsx @@ -2,21 +2,26 @@ 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 }) => { +const AnswerEdit = ({ id, editId, setEditId, answerId, onKebabClick }) => { AnswerEdit.propTypes = { id: PropTypes.number.isRequired, + editId: PropTypes.number.isRequired, setEditId: PropTypes.func.isRequired, + onKebabClick: PropTypes.func.isRequired, }; const handleEdit = () => { setEditId(answerId); + onKebabClick(id); }; return ( - + editId === null && ( + + ) ); }; diff --git a/src/components/AnswerEditForm/index.jsx b/src/components/AnswerEditForm/index.jsx index 74d0b6e..49b2cbd 100644 --- a/src/components/AnswerEditForm/index.jsx +++ b/src/components/AnswerEditForm/index.jsx @@ -1,9 +1,11 @@ import PropTypes from 'prop-types'; import { useState } from 'react'; import { putAnswer } from 'api/answers'; +import { ReactComponent as Close } from 'assets/images/icons/ic_Close.svg'; +import ConfirmModal from 'components/ConfirmModal'; // eslint-disable-next-line -const AnswerEditForm = ({ answer, name, imageSource, id, setEditId, setQuestionList }) => { +const AnswerEditForm = ({ answer, name, imageSource, id, setEditId, setQuestionList, setIsKebabLoading, setIsToast }) => { AnswerEditForm.propTypes = { answer: PropTypes.shape({ id: PropTypes.number.isRequired, @@ -14,6 +16,8 @@ const AnswerEditForm = ({ answer, name, imageSource, id, setEditId, setQuestionL name: PropTypes.string.isRequired, imageSource: PropTypes.string, id: PropTypes.number.isRequired, + setIsKebabLoading: PropTypes.func.isRequired, + setIsToast: PropTypes.func.isRequired, }; AnswerEditForm.defaultProps = { @@ -24,6 +28,7 @@ const AnswerEditForm = ({ answer, name, imageSource, id, setEditId, setQuestionL const [textareaValue, setTextareaValue] = useState(answer.content === null || answer.content === 'reject' ? '' : answer.content); const [isLoading, setIsLoading] = useState(false); const [isValid, setIsValid] = useState(false); + const [showModal, setShowModal] = useState(false); const handleTextareaChange = (event) => { const text = event.target.value; @@ -35,11 +40,13 @@ const AnswerEditForm = ({ answer, name, imageSource, id, setEditId, setQuestionL const handleAnswerPatch = async (e) => { e.preventDefault(); try { + setIsKebabLoading(true); setIsLoading(true); const result = await putAnswer(answer.id, { content: textareaValue, // textareaValue에서 내용을 가져옵니다. isRejected: false, // 필요하다면 다른 데이터도 추가 가능합니다. }); + setIsToast('수정'); setQuestionList((prevQuestions) => prevQuestions.map((question) => { if (question.id === id) { @@ -51,15 +58,32 @@ const AnswerEditForm = ({ answer, name, imageSource, id, setEditId, setQuestionL } catch (err) { // handle error here (e.g., show error message) } finally { + setIsKebabLoading(false); setIsLoading(false); setEditId(null); + setTimeout(() => { + setIsToast(null); + }, 3000); } }; + const onCancelClick = () => { + setShowModal(true); + }; + + const handleModalCancel = () => { + setShowModal(false); + }; + + const handleModalConfirm = () => { + setShowModal(false); + setEditId(null); + }; + const renderProfileImg = () => {`${name}의; const renderAnswerForm = () => ( -
+