Skip to content
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
2 changes: 2 additions & 0 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import OnboardingPage from './pages/Onboarding';
import RandomLettersPage from './pages/RandomLetters';
import RollingPaperPage from './pages/RollingPaper';
import WritePage from './pages/Write';
import ShareApprovalPage from './pages/Share';

const App = () => {
useViewport();
Expand Down Expand Up @@ -56,6 +57,7 @@ const App = () => {
<Route path="letter" element={<LetterBoardPage />} />
</Route>
<Route path="letter/:id" element={<LetterBoardDetailPage />} />
<Route path="share/:shareProposalId" element={<ShareApprovalPage />} />
</Route>
<Route path="mypage" element={<Layout />}>
<Route index element={<MyPage />} />
Expand Down
38 changes: 35 additions & 3 deletions src/apis/share.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,26 @@ export interface ShareProposal {
status: 'REJECTED' | 'APPROVED' | 'PENDING';
}

//편지 공유 요청 상세 조회
export interface ShareProposalLetter {
id: number;
content: string;
writerZipCode: string;
receiverZipCode: string;
createdAt: string;
}

export interface ShareProposalDetail {
shareProposalId: number;
requesterZipCode: string;
recipientZipCode: string;
message: string;
status: 'PENDING' | 'ACCEPTED' | 'REJECTED';
letters: ShareProposalLetter[];
}

// 편지 공유 수락 / 거절
export interface SharePostApproval {
export interface ShareProposalApproval {
shareProposalId: number;
status: 'APPROVED' | 'REJECTED';
sharePostId: number;
Expand Down Expand Up @@ -106,13 +124,27 @@ export const getShareProposalList = async () => {
}
};

// 편지 공유 요청 상세 조회
export const getShareProposalDetail = async (
shareProposalId: number,
): Promise<ShareProposalDetail> => {
try {
const response = await client.get(`/api/share-proposals/${shareProposalId}`);
console.log(`😎공유 요청 상세 조회 데이터 `, response.data);
return response.data.data;
} catch (error) {
console.error('❌ 편지 공유 요청을 상세 조회하던 중 에러가 발생했습니다', error);
throw error;
}
};

// 편지 공유 수락 / 거절
export const postShareProposalApproval = async (
shareProposalId: number,
action: 'approve' | 'reject',
): Promise<SharePostApproval> => {
): Promise<ShareProposalApproval> => {
try {
const response = await client.patch(`/api/share-proposal/${shareProposalId}/${action}`);
const response = await client.patch(`/api/share-proposals/${shareProposalId}/${action}`);
return response.data;
} catch (error) {
console.error(
Expand Down
8 changes: 4 additions & 4 deletions src/pages/Home/components/ShowShareAccessModal.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React, { useEffect, useState } from 'react';
import { useNavigate } from 'react-router';

import { getSharePostDetail } from '@/apis/share';
import { getShareProposalDetail } from '@/apis/share';
import { getShareProposalList } from '@/apis/share';
import { ShareProposal } from '@/apis/share';

Expand Down Expand Up @@ -30,9 +30,9 @@ const ShowShareAccessModal = ({ onClose }: ShowShareAccessModalProps) => {

const handleNavigation = async (shareProposalId: number) => {
try {
const postDetail = await getSharePostDetail(shareProposalId);
navigate(`/board/letter/${shareProposalId}`, {
state: { postDetail, isShareLetterPreview: true },
const proposalDetail = await getShareProposalDetail(shareProposalId);
navigate(`/board/share/${shareProposalId}`, {
state: { proposalDetail },
});
} catch (error) {
console.error('❌ 게시글 상세 페이지로 이동하는 데에 실패했습니다.', error);
Expand Down
2 changes: 1 addition & 1 deletion src/pages/MyPage/constants/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@ export const TEMPERATURE_RANGE = [
{ min: 40, max: 55, description: '마음이 따뜻한 따숨님' },
{ min: 55, max: 70, description: '훈훈한 따숨님' },
{ min: 70, max: 80, description: '정말 따뜻한 따숨님' },
{ min: 85, max: 100, description: '사랑이 넘치는 따숨님' },
{ min: 85, max: 105, description: '사랑이 넘치는 따숨님' },
];
93 changes: 93 additions & 0 deletions src/pages/Share/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import { useEffect, useState } from 'react';
import { useNavigate, useParams } from 'react-router';

import { getShareProposalDetail } from '@/apis/share';
import { postShareProposalApproval, ShareProposalDetail } from '@/apis/share';

import { twMerge } from 'tailwind-merge';
import Letter from '../LetterBoardDetail/components/Letter';

import BlurImg from '@/assets/images/landing-blur.png';

const ShareApprovalPage = () => {
const navigate = useNavigate();
const { shareProposalId } = useParams();
console.log(shareProposalId);

const [proposalDetail, setProposalDetail] = useState<ShareProposalDetail>();

const handleProposalApproval = async (action: 'approve' | 'reject') => {
try {
const result = await postShareProposalApproval(Number(shareProposalId), action);
console.log(`✅ 편지 공유 ${action === 'approve' ? '수락' : '거절'}됨:`, result);
navigate('/');
} catch (error) {
console.error('❌공유 요청 처리 중 에러 발생', error);
}
};
useEffect(() => {
const fetchProposalDetail = async (id: string) => {
try {
const data = await getShareProposalDetail(Number(id));
setProposalDetail(data);
} catch (error) {
console.error('❌ 공유 요청 상세 조회에 실패했습니다.', error);
throw error;
}
};

if (shareProposalId) {
fetchProposalDetail(shareProposalId);
}
}, [shareProposalId]);

return (
<div className="grow bg-white">
<main className="px-5 pt-18 pb-3">
<p className="body-b mb-6 px-5">FROM. {proposalDetail?.requesterZipCode}</p>
<p
className={twMerge(
'body-r bg-[repeating-linear-gradient(transparent,transparent_25px,#ffe6e3_26px)] px-5 whitespace-pre-wrap',
'leading-[26px]',
)}
>
{proposalDetail?.message}
</p>
<section className="flex flex-col gap-6.5 px-5 py-6.5">
{proposalDetail?.letters.map((letter, index) => (
<Letter
key={index}
letter={letter}
isWriter={letter.writerZipCode === proposalDetail.requesterZipCode}
/>
))}
</section>

{proposalDetail && (
<>
<img src={BlurImg} alt="landing blur" className="fixed bottom-0 left-0 z-10 w-screen" />
<section className="fixed bottom-[30px] left-1/2 z-20 flex w-73 translate-x-[-50%] gap-6">
<button
type="button"
className="body-m secondary-btn h-10 flex-1 basis-1/2"
onClick={() => handleProposalApproval('reject')}
>
거부하기
</button>

<button
type="button"
className="primary-btn body-m h-10 flex-1 basis-1/2"
onClick={() => handleProposalApproval('approve')}
>
승인하기
</button>
</section>
</>
)}
</main>
</div>
);
};

export default ShareApprovalPage;