diff --git a/src/apis/share.ts b/src/apis/share.ts index 447037a..8f43c95 100644 --- a/src/apis/share.ts +++ b/src/apis/share.ts @@ -1,5 +1,72 @@ import client from './client'; +//공유 게시글 상세 페이지 편지 +interface ShareLetter { + id: number; + content: string; + writerZipCode: string; + receiverZipCode: string; +} + +// 공유 게시글 목록 조회 타입 +export interface SharePost { + writerZipCode: number; + receiverZipCode: number; + content: string; + createdAt: string; + active: boolean; + sharePostId: number; + sharePostContent: string; + letters: ShareLetter[]; +} + +// 페이징 포함 +export interface SharePostResponse { + content: SharePost[]; + currentPage: number; + size: number; + totalElements: number; + totalPages: number; +} + +// 편지 공유 수락 / 거절 +export interface SharePostApproval { + shareProposalId: number; + status: 'APPROVED' | 'REJECTED'; + sharePostId: number; +} + +// 공유 게시글 목록 조회 +export const getSharePostList = async ( + page: number = 1, + size: number = 10, +): Promise => { + try { + const response = await client.get('/api/share-posts', { + params: { page, size }, + }); + console.log(`🌟공유 게시글 목록`, response.data); + + return response.data; + } catch (error) { + console.error('❌ 편지 공유 게시글 목록을 조회하던 중 에러가 발생했습니다', error); + throw new Error('편지 공유 게시글 목록 조회 실패'); + } +}; + +// 공유 게시글 상세 조회 +export const getSharePostDetail = async (sharePostId: number): Promise => { + try { + const response = await client.get(`/api/share-posts/${sharePostId}`); + console.log(`🔥공유 게시글 상세 데이터`, response.data); + return response.data.data; + } catch (error) { + console.error('❌ 편지 공유 게시글을 상세 조회하던 중 에러가 발생했습니다', error); + throw new Error('편지 공유 게시글 상세 조회 실패'); + } +}; + +// 공유 요청 보내기 export const postShareProposals = async ( letterIds: number[], requesterId: number, @@ -9,13 +76,31 @@ export const postShareProposals = async ( try { const response = await client.post('/api/share-proposals', { letterIds: letterIds, - requesterId: requesterId, - recipientId: recipientId, - message: message, + requesterId, + recipientId, + message, }); if (!response) throw new Error('error while fetching mailbox data'); - return response; + return response.data; + } catch (error) { + console.error('❌ 공유 요청 보내기 중 에러가 발생했습니다', error); + throw new Error('공유 요청 실패'); + } +}; + +// 편지 공유 수락 / 거절 +export const postShareProposalApproval = async ( + shareProposalId: number, + action: 'approve' | 'reject', +): Promise => { + try { + const response = await client.patch(`/api/share-proposal/${shareProposalId}/${action}`); + return response.data; } catch (error) { - console.error(error); + console.error( + `❌ 편지 공유 ${action === 'approve' ? '수락' : '거절'} 중 에러가 발생했습니다`, + error, + ); + throw new Error(`편지 공유 ${action === 'approve' ? '수락' : '거부'} 실패`); } }; diff --git a/src/pages/Home/components/GoToLetterBoard.tsx b/src/pages/Home/components/GoToLetterBoard.tsx index 7275cfd..2f86778 100644 --- a/src/pages/Home/components/GoToLetterBoard.tsx +++ b/src/pages/Home/components/GoToLetterBoard.tsx @@ -4,7 +4,7 @@ import goToLetterBoard from '@/assets/images/go-to-letter-board.png'; const GoToLetterBoard = () => { return ( -
+

게시판

diff --git a/src/pages/Home/components/GoToLetterBox.tsx b/src/pages/Home/components/GoToLetterBox.tsx index 436da23..54bef21 100644 --- a/src/pages/Home/components/GoToLetterBox.tsx +++ b/src/pages/Home/components/GoToLetterBox.tsx @@ -2,17 +2,18 @@ import { Link } from 'react-router'; import goToLetterBoxNewLetters from '@/assets/images/go-to-letter-box-new-letters.png'; import goToLetterBox from '@/assets/images/go-to-letter-box.png'; +import { useIncomingLettersStore } from '@/stores/incomingLettersStore'; const GoToLetterBox = () => { - //TODO : hasNewLetters 전역으로 상태 관리하기 - const hasNewLetters = true; + const arrivedCount = useIncomingLettersStore((state) => state.arrivedCount); + return ( -
+

내 편지함

go to letter box diff --git a/src/pages/Home/components/HomeRight.tsx b/src/pages/Home/components/HomeRight.tsx index e8b32dc..9d20d0b 100644 --- a/src/pages/Home/components/HomeRight.tsx +++ b/src/pages/Home/components/HomeRight.tsx @@ -11,7 +11,7 @@ const HomeRight = () => { const { arrivedCount, fetchIncomingLetters } = useIncomingLettersStore(); useEffect(() => { fetchIncomingLetters(); - }, []); + }, [fetchIncomingLetters]); return (
diff --git a/src/pages/Home/components/NewLetterModal.tsx b/src/pages/Home/components/NewLetterModal.tsx index 5a33817..03614c0 100644 --- a/src/pages/Home/components/NewLetterModal.tsx +++ b/src/pages/Home/components/NewLetterModal.tsx @@ -1,14 +1,8 @@ -import { useEffect } from 'react'; - import { useIncomingLettersStore } from '@/stores/incomingLettersStore'; //TODO: 내 편지함 상세 조회에서 해당 편지를 조회하면 arrivedCount가 1 감소하도록 const NewLetterModal = () => { - const { arrivedCount, fetchIncomingLetters } = useIncomingLettersStore(); - - useEffect(() => { - fetchIncomingLetters(); - }, []); + const arrivedCount = useIncomingLettersStore((state) => state.arrivedCount); return (

diff --git a/src/pages/Home/components/ShowIncomingLettersModal.tsx b/src/pages/Home/components/ShowIncomingLettersModal.tsx index 2259854..abc14f8 100644 --- a/src/pages/Home/components/ShowIncomingLettersModal.tsx +++ b/src/pages/Home/components/ShowIncomingLettersModal.tsx @@ -1,5 +1,6 @@ import React, { useEffect } from 'react'; import { useNavigate } from 'react-router'; + import ModalBackgroundWrapper from '@/components/ModalBackgroundWrapper'; import ModalOverlay from '@/components/ModalOverlay'; import { useIncomingLettersStore } from '@/stores/incomingLettersStore'; @@ -22,7 +23,7 @@ const ShowIncomingLettersModal = ({ onClose }: ShowIncomingLettersModalProps) => useEffect(() => { fetchIncomingLetters(); - }); + }, []); return ( diff --git a/src/pages/Home/components/ShowShareAccessModal.tsx b/src/pages/Home/components/ShowShareAccessModal.tsx index 21d6c98..e1e40df 100644 --- a/src/pages/Home/components/ShowShareAccessModal.tsx +++ b/src/pages/Home/components/ShowShareAccessModal.tsx @@ -1,6 +1,8 @@ -import React from 'react'; +import React, { useEffect, useState } from 'react'; import { useNavigate } from 'react-router'; +import { getSharePostDetail, getSharePostList } from '@/apis/share'; +import { SharePostResponse } from '@/apis/share'; import ModalBackgroundWrapper from '@/components/ModalBackgroundWrapper'; import ModalOverlay from '@/components/ModalOverlay'; @@ -9,21 +11,35 @@ interface ShowShareAccessModalProps { onClose: () => void; } -const DUMMY_SHARE_ACCESS = [ - { id: 1, zip_code: '235EA' }, - { id: 2, zip_code: '711PO' }, - { id: 3, zip_code: '105CF' }, - { id: 4, zip_code: '299EB' }, -]; - const ShowShareAccessModal = ({ onClose }: ShowShareAccessModalProps) => { const navigate = useNavigate(); - const handleNavigation = (accessId: number) => { - navigate(`/board/letter/${accessId}`, { - state: { isShareLetterPreview: true }, - }); + const [sharePosts, setSharePosts] = useState(); + + useEffect(() => { + const fetchPosts = async () => { + try { + const data = await getSharePostList(1, 10); + setSharePosts(data); + } catch (error) { + console.error('❌ 게시글 목록을 불러오는 데 실패했습니다.', error); + } + }; + + fetchPosts(); + }, []); + + const handleNavigation = async (sharePostId: number) => { + try { + const postDetail = await getSharePostDetail(sharePostId); + navigate(`/board/letter/${sharePostId}`, { + state: { postDetail, isShareLetterPreview: true }, + }); + } catch (error) { + console.error('❌ 게시글 상세 페이지로 이동하는 데에 실패했습니다.', error); + } }; + return (

@@ -39,14 +55,14 @@ const ShowShareAccessModal = ({ onClose }: ShowShareAccessModalProps) => { 허락 여부를 체크해주세요!

-
- {DUMMY_SHARE_ACCESS.map((access) => ( +
+ {sharePosts?.content.map((post) => ( ))}
diff --git a/src/pages/LetterBoardDetail/components/Letter.tsx b/src/pages/LetterBoardDetail/components/Letter.tsx index c279b76..58c80a2 100644 --- a/src/pages/LetterBoardDetail/components/Letter.tsx +++ b/src/pages/LetterBoardDetail/components/Letter.tsx @@ -2,20 +2,20 @@ import MemoWrapper from '@/components/MemoWrapper'; interface LetterProps { letter: { - receiver: string; + receiverZipCode: string; content: string; - sender: string; + writerZipCode: string; }; - isSender?: boolean; + isWriter?: boolean; } -const Letter = ({ letter, isSender = false }: LetterProps) => { +const Letter = ({ letter, isWriter = false }: LetterProps) => { return ( - +
-

To. {letter.receiver}

+

To. {letter.receiverZipCode}

{letter.content}

-

From. {letter.sender}

+

From. {letter.writerZipCode}

); diff --git a/src/pages/LetterBoardDetail/index.tsx b/src/pages/LetterBoardDetail/index.tsx index 137eb67..4eb2395 100644 --- a/src/pages/LetterBoardDetail/index.tsx +++ b/src/pages/LetterBoardDetail/index.tsx @@ -1,32 +1,20 @@ -import { useState } from 'react'; -import { useLocation } from 'react-router'; +import { useEffect, useState } from 'react'; +import { useLocation, useNavigate } from 'react-router'; import { twMerge } from 'tailwind-merge'; +import { getSharePostDetail, postShareProposalApproval, SharePost } from '@/apis/share'; import BlurImg from '@/assets/images/landing-blur.png'; import ReportModal from '@/components/ReportModal'; import Header from './components/Header'; import Letter from './components/Letter'; -const DUMMY_LETTER = { - receiver: '12E21', - content: - '편지 내용 어쩌구저쩌구 뾰로롱 편지 내용 어쩌구저쩌구 뾰로롱편지 내용 어쩌구 뾰로롱편지 내용 어쩌구 뾰로롱편지 내용 어쩌구 뾰로롱편지 내용 어쩌구 뾰로롱편지 내용 어쩌구 뾰로롱편지 내용 어쩌구 뾰로롱편지 내용 어쩌구 뾰로롱편지 내용 어쩌구 뾰로롱편지 내용 어쩌구 저쩌구 끝~!!', - sender: '12345', -}; - interface ShareLetterPreviewProps { confirmDisabled?: boolean; children?: React.ReactNode; - onCancel?: () => void; - onConfirm?: () => void; } -const LetterBoardDetailPage = ({ - confirmDisabled, - onCancel, - onConfirm, -}: ShareLetterPreviewProps) => { +const LetterBoardDetailPage = ({ confirmDisabled }: ShareLetterPreviewProps) => { const [likeCount, setLikeCount] = useState(122); const [isLike, setIsLike] = useState(false); const isWriter = false; @@ -38,9 +26,48 @@ const LetterBoardDetailPage = ({ }; const location = useLocation(); + const navigate = useNavigate(); + + const isShareLetterPreview = location.state?.isShareLetterPreview || false; + const [postDetail, setPostDetail] = useState(); + + useEffect(() => { + const fetchPostDetail = async () => { + console.log('location.state:', location.state); + + try { + if (location.state?.postDetail) { + const { sharePostId } = location.state.postDetail; + + console.log('sharePostId:', sharePostId); - const isShareLetterPreview = location.state?.isShareLetterPreview || false; // state가 없다면 false로 기본값 설정 - console.log(location); + const data = await getSharePostDetail(sharePostId); + + setPostDetail(data); + } else { + console.warn('postDetail not found in location.state'); + } + } catch (error) { + console.error('❌ 공유 게시글 상세 조회에 실패했습니다.', error); + } + }; + + fetchPostDetail(); + }, [location.state]); + + const handleProposalApproval = async ( + action: 'approve' | 'reject', + shareProposalId: number = location.state?.postDetail?.sharePostId, + ) => { + try { + const result = await postShareProposalApproval(shareProposalId, action); + console.log(`✅ 편지 공유 ${action === 'approve' ? '수락' : '거절'}됨:`, result); + + navigate('/'); + } catch (error) { + console.error(error); + } + }; return ( <> @@ -55,43 +82,42 @@ const LetterBoardDetailPage = ({ isShareLetterPreview={isShareLetterPreview} />
-

FROM. 12E31

+

FROM. {postDetail?.writerZipCode}

- 내가 최근 먀먀먀를 했음. 그런데 이런 고민을 부모님께 말씀드리기에는 너무 죄송했음. - 이런저런 조언을 구하지 못해 편지를 작성했는데 너무나도 따뜻한 조언과 이야기를 받고 힘이 - 났음. 다른 분들께도 내가 받았던 응원을 함께 공유하고 싶음. + {postDetail?.sharePostContent}

- - - - - - - + {postDetail?.letters.map((letter, index) => ( + + ))}
{isShareLetterPreview && ( <> - landing blur -
+ landing blur +
+