From d81af616b09bee9835f0b5e7030cf6ae2657f0bb Mon Sep 17 00:00:00 2001 From: "wl990@naver.com" Date: Tue, 4 Mar 2025 21:51:42 +0900 Subject: [PATCH 01/16] =?UTF-8?q?feat=20:=20=EA=B2=8C=EC=8B=9C=EA=B8=80=20?= =?UTF-8?q?=EC=8B=A0=EA=B3=A0=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/apis/admin.ts | 20 ++++-- src/components/ReportModal.tsx | 67 +++++++++++++----- src/pages/LetterBoardDetail/index.tsx | 5 +- src/pages/LetterDetail/index.tsx | 69 ++++++++++++------- .../components/MatchedLetter.tsx | 6 +- src/types/admin.d.ts | 11 ++- src/types/letterDetail.d.ts | 3 + 7 files changed, 125 insertions(+), 56 deletions(-) diff --git a/src/apis/admin.ts b/src/apis/admin.ts index 53ec1f3..f1c69fc 100644 --- a/src/apis/admin.ts +++ b/src/apis/admin.ts @@ -1,5 +1,17 @@ import client from './client'; +const postReports = async (postReportRequest: PostReportRequest) => { + try { + const res = await client.post(`/api/reports`, postReportRequest); + console.log(res); + if (res.status === 200) { + return res; + } + } catch (error) { + console.error(error); + } +}; + const getReports = async ( setReports: React.Dispatch>, queryString: string = '', @@ -15,10 +27,10 @@ const getReports = async ( } }; -const patchReport = async (reportId: number, reportRequest: ReportRequest) => { +const patchReport = async (reportId: number, patchReportRequest: PatchReportRequest) => { try { - console.log(`/api/reports/${reportId}`, reportRequest); - const res = await client.patch(`/api/reports/${reportId}`, reportRequest); + console.log(`/api/reports/${reportId}`, patchReportRequest); + const res = await client.patch(`/api/reports/${reportId}`, patchReportRequest); console.log(res); } catch (error) { console.error(error); @@ -61,4 +73,4 @@ const patchBadWords = async ( } }; -export { getReports, patchReport, getBadWords, postBadWords, patchBadWords }; +export { getReports, patchReport, getBadWords, postBadWords, patchBadWords, postReports }; diff --git a/src/components/ReportModal.tsx b/src/components/ReportModal.tsx index 923ec42..3e3981b 100644 --- a/src/components/ReportModal.tsx +++ b/src/components/ReportModal.tsx @@ -1,57 +1,88 @@ -import { useState } from 'react'; +import { useEffect, useState } from 'react'; import { twMerge } from 'tailwind-merge'; +import { postReports } from '@/apis/admin'; + import ConfirmModal from './ConfirmModal'; import TextareaField from './TextareaField'; interface ReportModalProps { + reportType: ReportType; + letterId: number | null; onClose: () => void; } +const ReportModal = ({ reportType, letterId, onClose }: ReportModalProps) => { + interface ReportReason { + name: string; + type: Reason; + } + const REPORT_REASON: ReportReason[] = [ + { name: '욕설', type: 'ABUSE' }, + { name: '비방', type: 'DEFAMATION' }, + { name: '폭언', type: 'HARASSMENT' }, + { name: '성희롱', type: 'THREATS' }, + { name: '기타', type: 'ETC' }, + ]; -const REPORT_REASON = ['욕설', '비방', '폭언', '성희롱', '기타']; - -const ReportModal = ({ onClose }: ReportModalProps) => { - const [selected, setSelected] = useState(''); - const [additionalReason, setAdditionalReason] = useState(''); + const [reportRequest, setReportRequest] = useState({ + reportType: reportType, + reasonType: '', + reason: '', + letterId: letterId, + }); - const handleReasonClick = (reason: string) => { - if (selected === reason) setSelected(''); - else setSelected(reason); + const handleReasonClick = (reason: Reason) => { + if (reportRequest.reasonType === reason) + setReportRequest((cur) => ({ ...cur, reasonType: '' })); + else setReportRequest((cur) => ({ ...cur, reasonType: reason })); }; - const handleSubmit = () => { - onClose(); + const handleSubmit = async () => { + const res = await postReports(reportRequest); + if (res?.status === 200) { + alert('신고처리되었습니다(토스트UI로 바꾸자)'); + onClose(); + } }; + useEffect(() => { + if (!letterId) { + alert('신고창을 여는 도중 오류가 발생했습니다(페이지를 새로고침 해주세요'); + onClose(); + } + setReportRequest((cur) => ({ ...cur })); + }, [letterId, onClose]); + return (
- {REPORT_REASON.map((reason) => ( + {REPORT_REASON.map((reason, idx) => ( ))}
setAdditionalReason(e.target.value)} + value={reportRequest.reason} + onChange={(e) => setReportRequest((cur) => ({ ...cur, reason: e.target.value }))} />
); diff --git a/src/pages/LetterBoardDetail/index.tsx b/src/pages/LetterBoardDetail/index.tsx index 4eb2395..d951443 100644 --- a/src/pages/LetterBoardDetail/index.tsx +++ b/src/pages/LetterBoardDetail/index.tsx @@ -71,7 +71,10 @@ const LetterBoardDetailPage = ({ confirmDisabled }: ShareLetterPreviewProps) => return ( <> - {activeReportModal && setActiveReportModal(false)} />} + {/* MEMO : 안지원 왔다감 ^o^/ 신고 처리 API 작업을 하면서 추가 사항이 생겼습니다! reportType(이건 제가 추가해놓겠습니당), letterId(해당 게시글 또는 편지 id값, 추가 필요!)을 담아야 신고 모달을 정상적으로 사용 가능합니다!! */} + {activeReportModal && ( + setActiveReportModal(false)} /> + )}
{ const params = useParams(); const navigate = useNavigate(); - // 상대방의 우편번호도 데이터에 포함되어야 할 거 같음!!! + const [letterDetail, setLetterDetail] = useState(null); + const userZipCode = useAuthStore((state) => state.zipCode); const DEGREES = [ { icon: , title: '따뜻해요' }, @@ -55,6 +57,7 @@ const LetterDetailPage = () => { const handleGetLetter = async (letterId: string) => { const res = await getLetter(letterId); if (res?.status === 200) { + console.log(res); setLetterDetail(res.data.data); } else { alert( @@ -71,9 +74,17 @@ const LetterDetailPage = () => { document.body.removeEventListener('click', handleOutsideClick); }; }, [params.id, navigate]); + return ( <> - {reportModalOpen && setReportModalOpen(false)} />} + {reportModalOpen && ( + setReportModalOpen(false)} + /> + )} +
{
- - - + {userZipCode !== letterDetail?.zipCode && ( + + )} + {userZipCode === letterDetail?.zipCode && ( + + )} + {userZipCode !== letterDetail?.zipCode && ( + + )} {degreeModalOpen && (
{DEGREES.map((degree, idx) => { diff --git a/src/pages/RandomLetters/components/MatchedLetter.tsx b/src/pages/RandomLetters/components/MatchedLetter.tsx index 1e05d46..3ff4907 100644 --- a/src/pages/RandomLetters/components/MatchedLetter.tsx +++ b/src/pages/RandomLetters/components/MatchedLetter.tsx @@ -1,21 +1,17 @@ -import { useEffect, useState } from 'react'; +import { useEffect } from 'react'; import { useNavigate } from 'react-router'; import { twMerge } from 'tailwind-merge'; import BackButton from '@/components/BackButton'; -import ReportModal from '@/components/ReportModal'; import { FONT_TYPE_OBJ, PAPER_TYPE_OBJ } from '@/pages/Write/constants'; const MatchedLetter = ({ matchedLetter }: { matchedLetter: MatchedLetter }) => { const navigate = useNavigate(); - const [reportModalOpen, setReportModalOpen] = useState(false); - useEffect(() => {}, [matchedLetter]); return ( <> - {reportModalOpen && setReportModalOpen(false)} />}
Date: Wed, 5 Mar 2025 18:59:56 +0900 Subject: [PATCH 02/16] =?UTF-8?q?feat=20:=20=EC=B9=B4=ED=85=8C=EA=B3=A0?= =?UTF-8?q?=EB=A6=AC=20=EC=A0=84=EC=B2=B4=20=EC=84=A0=ED=83=9D=20=EC=95=88?= =?UTF-8?q?=EB=90=98=EB=8A=94=20=EC=98=A4=EB=A5=98=20=EC=88=98=EC=A0=95=20?= =?UTF-8?q?+=20=EB=8B=B5=EC=9E=A5=20=EC=A0=84=EC=86=A1=EC=8B=9C=20?= =?UTF-8?q?=EB=8F=84=EC=B0=A9=EC=8B=9C=EA=B0=84=201=EC=8B=9C=EA=B0=84?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20=ED=85=8D=EC=8A=A4=ED=8A=B8=20=EA=B3=A0?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/apis/write.ts | 2 +- src/pages/RandomLetters/components/MatchingSelect.tsx | 4 ++-- src/pages/RandomLetters/constants/index.ts | 4 ++-- src/pages/Write/CategorySelect.tsx | 4 +--- 4 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/apis/write.ts b/src/apis/write.ts index 7046e1f..13089e6 100644 --- a/src/apis/write.ts +++ b/src/apis/write.ts @@ -26,7 +26,7 @@ const postFirstReply = async (data: FirstReplyRequest) => { const getPrevLetter = async (letterId: string) => { try { - const res = await client.get(`/api/letters/${letterId}/previous`); + const res = await client.get(`/api/v1/letters/${letterId}/previous`); console.log(res); return res; } catch (error) { diff --git a/src/pages/RandomLetters/components/MatchingSelect.tsx b/src/pages/RandomLetters/components/MatchingSelect.tsx index 78f2614..b7f2540 100644 --- a/src/pages/RandomLetters/components/MatchingSelect.tsx +++ b/src/pages/RandomLetters/components/MatchingSelect.tsx @@ -18,10 +18,10 @@ export default function MatchingSelect({ setOpenModal: React.Dispatch>; setSelectedLetter: React.Dispatch>; }) { - const [selectedCategory, setSelectedCategory] = useState('CONSOLATION'); + const [selectedCategory, setSelectedCategory] = useState('ALL'); const [randomLetters, setRandomLetters] = useState([]); - const handleGetRandomLetters = async (selectedCategory: Category) => { + const handleGetRandomLetters = async (selectedCategory: Category | 'ALL') => { const res = await getRandomLetters(selectedCategory); if (res?.status === 200) { setRandomLetters(res.data.data); diff --git a/src/pages/RandomLetters/constants/index.ts b/src/pages/RandomLetters/constants/index.ts index 47fa7f5..b10f948 100644 --- a/src/pages/RandomLetters/constants/index.ts +++ b/src/pages/RandomLetters/constants/index.ts @@ -1,5 +1,5 @@ -const CATEGORY_LIST: { title: string; category: Category | null }[] = [ - { title: '전체', category: null }, +const CATEGORY_LIST: { title: string; category: Category | 'ALL' }[] = [ + { title: '전체', category: 'ALL' }, { title: '위로와 공감', category: 'CONSOLATION' }, { title: '축하와 응원', category: 'CELEBRATION' }, { title: '고민 상담', category: 'CONSULT' }, diff --git a/src/pages/Write/CategorySelect.tsx b/src/pages/Write/CategorySelect.tsx index bdabcbb..7244b0b 100644 --- a/src/pages/Write/CategorySelect.tsx +++ b/src/pages/Write/CategorySelect.tsx @@ -59,9 +59,7 @@ export default function CategorySelect({
작성하신 편지는 - {'00'}시간 - {'00'}분 - {'00'}초 후에 도착합니다. + 1시간 후에 도착합니다.
From 26ab9c002652131fb5a646a6c512764d692edd6b Mon Sep 17 00:00:00 2001 From: "wl990@naver.com" Date: Wed, 5 Mar 2025 23:01:15 +0900 Subject: [PATCH 03/16] =?UTF-8?q?feat=20:=20getPrevLetter=20api=20?= =?UTF-8?q?=EC=97=94=EB=93=9C=ED=8F=AC=EC=9D=B8=ED=8A=B8=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/apis/write.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/apis/write.ts b/src/apis/write.ts index 13089e6..7046e1f 100644 --- a/src/apis/write.ts +++ b/src/apis/write.ts @@ -26,7 +26,7 @@ const postFirstReply = async (data: FirstReplyRequest) => { const getPrevLetter = async (letterId: string) => { try { - const res = await client.get(`/api/v1/letters/${letterId}/previous`); + const res = await client.get(`/api/letters/${letterId}/previous`); console.log(res); return res; } catch (error) { From cb7053b254ae115b8e0c1ec58186f514038da04b Mon Sep 17 00:00:00 2001 From: "wl990@naver.com" Date: Thu, 6 Mar 2025 00:53:22 +0900 Subject: [PATCH 04/16] =?UTF-8?q?feat=20:=20=EB=94=94=ED=85=8C=EC=9D=BC=20?= =?UTF-8?q?=ED=8E=98=EC=9D=B4=EC=A7=80=20=EB=8B=B5=EC=9E=A5=EB=B2=84?= =?UTF-8?q?=ED=8A=BC=20=EB=B6=84=EA=B8=B0=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/LetterDetail/index.tsx | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/pages/LetterDetail/index.tsx b/src/pages/LetterDetail/index.tsx index edbcf8a..4bb206c 100644 --- a/src/pages/LetterDetail/index.tsx +++ b/src/pages/LetterDetail/index.tsx @@ -157,15 +157,17 @@ const LetterDetailPage = () => { )} > FROM. {'12E12'} - + {userZipCode !== letterDetail?.zipCode && ( + + )} {deleteModalOpen && ( Date: Thu, 6 Mar 2025 09:45:25 +0900 Subject: [PATCH 05/16] =?UTF-8?q?feat=20:=20=ED=8E=B8=EC=A7=80=EC=83=81?= =?UTF-8?q?=EC=84=B8=ED=8E=98=EC=9D=B4=EC=A7=80=20zipCode=EB=B0=94?= =?UTF-8?q?=EC=9D=B8=EB=94=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/LetterDetail/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/LetterDetail/index.tsx b/src/pages/LetterDetail/index.tsx index 4bb206c..5a6ee80 100644 --- a/src/pages/LetterDetail/index.tsx +++ b/src/pages/LetterDetail/index.tsx @@ -156,7 +156,7 @@ const LetterDetailPage = () => { letterDetail && FONT_TYPE_OBJ[letterDetail.fontType], )} > - FROM. {'12E12'} + FROM. {letterDetail?.zipCode} {userZipCode !== letterDetail?.zipCode && ( + )} + {userZipCode === letterDetail?.zipCode && ( + + )} + {userZipCode !== letterDetail?.zipCode && ( + + )} + {degreeModalOpen && ( +
+ {DEGREES.map((degree, idx) => { + return ( + + ); + })} +
+ )} +
+
+ ); +} diff --git a/src/pages/LetterDetail/components/LetterDetailReplyButton.tsx b/src/pages/LetterDetail/components/LetterDetailReplyButton.tsx new file mode 100644 index 0000000..b46aa37 --- /dev/null +++ b/src/pages/LetterDetail/components/LetterDetailReplyButton.tsx @@ -0,0 +1,19 @@ +import { useNavigate } from 'react-router'; + +interface LetterDetailReplyButton { + letterDetail: LetterDetail | null; +} +export default function LetterDetailReplyButton({ letterDetail }: LetterDetailReplyButton) { + const navigate = useNavigate(); + return ( + + ); +} diff --git a/src/pages/LetterDetail/index.tsx b/src/pages/LetterDetail/index.tsx index 5a6ee80..cb5173d 100644 --- a/src/pages/LetterDetail/index.tsx +++ b/src/pages/LetterDetail/index.tsx @@ -1,22 +1,17 @@ -import { useEffect, useRef, useState } from 'react'; +import { useEffect, useState } from 'react'; import { useNavigate, useParams } from 'react-router'; import { twMerge } from 'tailwind-merge'; import { deleteLetter, getLetter } from '@/apis/letterDetail'; -import { - CloudIcon, - DeleteIcon, - SirenOutlinedIcon, - SnowIcon, - ThermostatIcon, - WarmIcon, -} from '@/assets/icons'; -import BackButton from '@/components/BackButton'; import ConfirmModal from '@/components/ConfirmModal'; import ReportModal from '@/components/ReportModal'; -import { FONT_TYPE_OBJ, PAPER_TYPE_OBJ } from '@/pages/Write/constants'; +import { PAPER_TYPE_OBJ } from '@/pages/Write/constants'; import useAuthStore from '@/stores/authStore'; +import LetterDetailContent from './components/LetterDetailContent'; +import LetterDetailHeader from './components/LetterDetailHeader'; +import LetterDetailReplyButton from './components/LetterDetailReplyButton'; + const LetterDetailPage = () => { const params = useParams(); const navigate = useNavigate(); @@ -24,24 +19,9 @@ const LetterDetailPage = () => { const [letterDetail, setLetterDetail] = useState(null); const userZipCode = useAuthStore((state) => state.zipCode); - const DEGREES = [ - { icon: , title: '따뜻해요' }, - { icon: , title: '그럭저럭' }, - { icon: , title: '앗! 차가워' }, - ]; - const [degreeModalOpen, setDegreeModalOpen] = useState(false); const [reportModalOpen, setReportModalOpen] = useState(false); const [deleteModalOpen, setDeleteModalOpen] = useState(false); - const degreeButtonRef = useRef(null); - const handleOutsideClick = (event: MouseEvent) => { - const target = event.target as Node; - if (!target || degreeButtonRef.current?.contains(target)) { - return; - } - setDegreeModalOpen(false); - }; - const handleDeleteLetter = async (letterId: string) => { const res = await deleteLetter(letterId); if (res?.status === 200) { @@ -52,13 +32,11 @@ const LetterDetailPage = () => { }; useEffect(() => { - document.body.addEventListener('click', handleOutsideClick); - const handleGetLetter = async (letterId: string) => { const res = await getLetter(letterId); if (res?.status === 200) { - console.log(res); - setLetterDetail(res.data.data); + const data: LetterDetail = res.data.data; + setLetterDetail(data); } else { alert( '에러가 발생했거나 존재하지 않거나 따숨님의 편지가 아니에요(임시) - 이거 에러코드 따른 처리 달리해야할듯', @@ -69,10 +47,6 @@ const LetterDetailPage = () => { if (params.id) { handleGetLetter(params.id); } - - return () => { - document.body.removeEventListener('click', handleOutsideClick); - }; }, [params.id, navigate]); return ( @@ -91,82 +65,14 @@ const LetterDetailPage = () => { letterDetail && PAPER_TYPE_OBJ[letterDetail.paperType], )} > -
- -
- {userZipCode !== letterDetail?.zipCode && ( - - )} - {userZipCode === letterDetail?.zipCode && ( - - )} - {userZipCode !== letterDetail?.zipCode && ( - - )} - {degreeModalOpen && ( -
- {DEGREES.map((degree, idx) => { - return ( - - ); - })} -
- )} -
-
-
- TO. 따숨이 - {letterDetail?.title} -
- - FROM. {letterDetail?.zipCode} + + {userZipCode !== letterDetail?.zipCode && ( - + )} {deleteModalOpen && ( Date: Thu, 6 Mar 2025 16:59:31 +0900 Subject: [PATCH 07/16] =?UTF-8?q?feat=20:=20=ED=8E=B8=EC=A7=80=20=EC=83=81?= =?UTF-8?q?=EC=84=B8=20=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20=EB=B6=84=EB=A6=AC=20+=20=ED=8E=B8=EC=A7=80=20?= =?UTF-8?q?=ED=8F=89=EA=B0=80=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84=20?= =?UTF-8?q?=EC=99=84=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/apis/letterDetail.ts | 15 +++- src/apis/write.ts | 6 +- .../components/DegreeSelector.tsx | 61 ++++++++++++++++ .../components/LetterDetailContent.tsx | 8 +-- .../components/LetterDetailDegreeButton.tsx | 50 ++++++++++++++ .../components/LetterDetailHeader.tsx | 69 ++++--------------- .../components/LetterDetailReplyButton.tsx | 4 +- src/pages/LetterDetail/index.tsx | 8 ++- src/types/letterDetail.d.ts | 2 + 9 files changed, 153 insertions(+), 70 deletions(-) create mode 100644 src/pages/LetterDetail/components/DegreeSelector.tsx create mode 100644 src/pages/LetterDetail/components/LetterDetailDegreeButton.tsx diff --git a/src/apis/letterDetail.ts b/src/apis/letterDetail.ts index d321438..6a72b66 100644 --- a/src/apis/letterDetail.ts +++ b/src/apis/letterDetail.ts @@ -23,4 +23,17 @@ const deleteLetter = async (letterId: string) => { } }; -export { getLetter, deleteLetter }; +const postEvaluateLetter = async (letterId: number, evaluation: LetterEvaluation) => { + try { + const res = await client.post(`/api/letters/${letterId}/evaluate`, { + evaluation: evaluation, + }); + if (!res) throw new Error('편지 삭제 요청 도중 에러가 발생했습니다.'); + console.log(res); + return res; + } catch (error) { + console.error(error); + } +}; + +export { getLetter, deleteLetter, postEvaluateLetter }; diff --git a/src/apis/write.ts b/src/apis/write.ts index 7046e1f..65a45f1 100644 --- a/src/apis/write.ts +++ b/src/apis/write.ts @@ -2,10 +2,10 @@ import client from './client'; const postLetter = async (data: LetterRequest) => { + console.log('request', data); try { const res = await client.post('/api/letters', data); if (!res) throw new Error('편지 전송과정중에서 오류가 발생했습니다.'); - console.log(`api 주소 : /api/letters, 전송타입 : post`); return res; } catch (error) { console.error(error); @@ -13,11 +13,10 @@ const postLetter = async (data: LetterRequest) => { }; const postFirstReply = async (data: FirstReplyRequest) => { + console.log('Firstrequest', data); try { const res = await client.post('/api/random-letters/matching', data); if (!res) throw new Error('최초 답장 전송과정중에서 오류가 발생했습니다.'); - console.log(`api 주소 : /api/random-letters/matching, 전송타입 : post`); - console.log(res); return res; } catch (error) { console.error(error); @@ -27,7 +26,6 @@ const postFirstReply = async (data: FirstReplyRequest) => { const getPrevLetter = async (letterId: string) => { try { const res = await client.get(`/api/letters/${letterId}/previous`); - console.log(res); return res; } catch (error) { console.error(error); diff --git a/src/pages/LetterDetail/components/DegreeSelector.tsx b/src/pages/LetterDetail/components/DegreeSelector.tsx new file mode 100644 index 0000000..86ae0b7 --- /dev/null +++ b/src/pages/LetterDetail/components/DegreeSelector.tsx @@ -0,0 +1,61 @@ +import { postEvaluateLetter } from '@/apis/letterDetail'; +import { CloudIcon, SnowIcon, WarmIcon } from '@/assets/icons'; + +interface DegreeSelector { + letterDetail: LetterDetail | null; + setLetterDetail: React.Dispatch>; +} +export default function DegreeSelector({ letterDetail, setLetterDetail }: DegreeSelector) { + const handlePostEvaluateLetter = async ( + letterId: number | undefined, + evaluation: LetterEvaluation, + ) => { + if (!letterId) return alert('편지id값이 담겨있지 않습니다.'); + const res = await postEvaluateLetter(letterId, evaluation); + if (res?.status === 200) { + console.log('평가완료'); + setLetterDetail((cur) => ({ ...cur, evaluated: true })); + } + }; + const DEGREES = [ + { + icon: , + title: '따뜻해요', + onClick: () => { + handlePostEvaluateLetter(letterDetail?.letterId, 'GOOD'); + }, + }, + { + icon: , + title: '그럭저럭', + onClick: () => { + handlePostEvaluateLetter(letterDetail?.letterId, 'SOSO'); + }, + }, + { + icon: , + title: '앗! 차가워', + onClick: () => { + handlePostEvaluateLetter(letterDetail?.letterId, 'BAD'); + }, + }, + ]; + return ( +
+ {DEGREES.map((degree, idx) => { + return ( + + ); + })} +
+ ); +} diff --git a/src/pages/LetterDetail/components/LetterDetailContent.tsx b/src/pages/LetterDetail/components/LetterDetailContent.tsx index 962ac81..23478cb 100644 --- a/src/pages/LetterDetail/components/LetterDetailContent.tsx +++ b/src/pages/LetterDetail/components/LetterDetailContent.tsx @@ -3,24 +3,24 @@ import { twMerge } from 'tailwind-merge'; import { FONT_TYPE_OBJ } from '@/pages/Write/constants'; interface LetterDetailContent { - letterDetail: LetterDetail | null; + letterDetail: LetterDetail; } export default function LetterDetailContent({ letterDetail }: LetterDetailContent) { return ( <>
TO. 따숨이 - {letterDetail?.title} + {letterDetail.title}
- FROM. {letterDetail?.zipCode} + FROM. {letterDetail.zipCode} ); } diff --git a/src/pages/LetterDetail/components/LetterDetailDegreeButton.tsx b/src/pages/LetterDetail/components/LetterDetailDegreeButton.tsx new file mode 100644 index 0000000..769aa51 --- /dev/null +++ b/src/pages/LetterDetail/components/LetterDetailDegreeButton.tsx @@ -0,0 +1,50 @@ +import { useEffect, useRef } from 'react'; + +import { ThermostatIcon } from '@/assets/icons'; + +interface LetterDetailDegreeButton { + letterDetail: LetterDetail | null; + setDegreeModalOpen: React.Dispatch>; +} +export default function LetterDetailDegreeButton({ + letterDetail, + setDegreeModalOpen, +}: LetterDetailDegreeButton) { + const degreeButtonRef = useRef(null); + + useEffect(() => { + const handleOutsideClick = (event: MouseEvent) => { + const target = event.target as Node; + if (!target || degreeButtonRef.current?.contains(target)) { + return; + } + setDegreeModalOpen(false); + }; + + document.body.addEventListener('click', handleOutsideClick); + + return () => { + document.body.removeEventListener('click', handleOutsideClick); + }; + }, [setDegreeModalOpen]); + return ( + <> + {letterDetail?.evaluated ? ( +
+ 온도 측정된 편지에요! +
+ ) : ( + + )} + + ); +} diff --git a/src/pages/LetterDetail/components/LetterDetailHeader.tsx b/src/pages/LetterDetail/components/LetterDetailHeader.tsx index e6be009..17cc18c 100644 --- a/src/pages/LetterDetail/components/LetterDetailHeader.tsx +++ b/src/pages/LetterDetail/components/LetterDetailHeader.tsx @@ -1,65 +1,37 @@ -import { useEffect, useRef, useState } from 'react'; +import { useState } from 'react'; -import { - CloudIcon, - DeleteIcon, - SirenOutlinedIcon, - SnowIcon, - ThermostatIcon, - WarmIcon, -} from '@/assets/icons'; +import { DeleteIcon, SirenOutlinedIcon } from '@/assets/icons'; import BackButton from '@/components/BackButton'; import useAuthStore from '@/stores/authStore'; +import DegreeSelector from './DegreeSelector'; +import LetterDetailDegreeButton from './LetterDetailDegreeButton'; + interface LetterDetailHeader { - letterDetail: LetterDetail | null; + letterDetail: LetterDetail; + setLetterDetail: React.Dispatch>; setDeleteModalOpen: React.Dispatch>; setReportModalOpen: React.Dispatch>; } export default function LetterDetailHeader({ letterDetail, + setLetterDetail, setDeleteModalOpen, setReportModalOpen, }: LetterDetailHeader) { const [degreeModalOpen, setDegreeModalOpen] = useState(false); - const DEGREES = [ - { icon: , title: '따뜻해요' }, - { icon: , title: '그럭저럭' }, - { icon: , title: '앗! 차가워' }, - ]; const userZipCode = useAuthStore((state) => state.zipCode); - const degreeButtonRef = useRef(null); - const handleOutsideClick = (event: MouseEvent) => { - const target = event.target as Node; - if (!target || degreeButtonRef.current?.contains(target)) { - return; - } - setDegreeModalOpen(false); - }; - useEffect(() => { - document.body.addEventListener('click', handleOutsideClick); - return () => { - document.body.removeEventListener('click', handleOutsideClick); - }; - }, []); - return (
{userZipCode !== letterDetail?.zipCode && ( - + )} {userZipCode === letterDetail?.zipCode && ( - ); - })} -
+ )}
diff --git a/src/pages/LetterDetail/components/LetterDetailReplyButton.tsx b/src/pages/LetterDetail/components/LetterDetailReplyButton.tsx index b46aa37..60c8ad3 100644 --- a/src/pages/LetterDetail/components/LetterDetailReplyButton.tsx +++ b/src/pages/LetterDetail/components/LetterDetailReplyButton.tsx @@ -1,7 +1,7 @@ import { useNavigate } from 'react-router'; interface LetterDetailReplyButton { - letterDetail: LetterDetail | null; + letterDetail: LetterDetail; } export default function LetterDetailReplyButton({ letterDetail }: LetterDetailReplyButton) { const navigate = useNavigate(); @@ -9,7 +9,7 @@ export default function LetterDetailReplyButton({ letterDetail }: LetterDetailRe