diff --git a/src/api/feedback/feedback.api.ts b/src/api/feedback/feedback.api.ts new file mode 100644 index 0000000..3818499 --- /dev/null +++ b/src/api/feedback/feedback.api.ts @@ -0,0 +1,10 @@ +import api from '@/api/api'; + +interface FeedbackRequest { + feedbackContent: string; +} + +export const postFeedback = async (body: FeedbackRequest) => { + const res = await api.post('/feedback', body); + return res.data; +}; \ No newline at end of file diff --git a/src/api/review/myBookmark.api.ts b/src/api/review/myBookmark.api.ts new file mode 100644 index 0000000..612538f --- /dev/null +++ b/src/api/review/myBookmark.api.ts @@ -0,0 +1,14 @@ +import api from '@/api/api'; +import type { PaginatedBookmarkResponse } from '@/types/review'; + +interface PageRequest { + page: number; + size: number; +} + +export const getMyBookmarks = async ( + params: PageRequest +): Promise => { + const res = await api.get('/profile/bookmark', { params }); + return res.data.data; +}; \ No newline at end of file diff --git a/src/api/review/myReview.api.ts b/src/api/review/myReview.api.ts new file mode 100644 index 0000000..7d9ff69 --- /dev/null +++ b/src/api/review/myReview.api.ts @@ -0,0 +1,22 @@ +import api from '@/api/api'; +import type { ReviewItem } from '@/types/review'; + +interface PageRequest { + page: number; + size: number; +} + +interface PaginatedReviewResponse { + content: ReviewItem[]; + page: number; + size: number; + totalPages: number; + totalElements: number; +} + +export const getMyReviews = async ( + params: PageRequest +): Promise => { + const res = await api.get('/profile/reviews', { params }); + return res.data.data; +}; \ No newline at end of file diff --git a/src/pages/my/MyBookmark.tsx b/src/pages/my/MyBookmark.tsx index 16ca1c2..38f9464 100644 --- a/src/pages/my/MyBookmark.tsx +++ b/src/pages/my/MyBookmark.tsx @@ -1,36 +1,63 @@ +import { useEffect, useState } from 'react'; import { ReviewCard, Header } from '@/components'; -import { mockMyReviews } from '@/__mocks/mockReviews'; +import { getMyBookmarks } from '@/api/review/myBookmark.api'; +import type { BookmarkReviewItem } from '@/types/review'; const MyBookmarkPage = () => { + const [bookmarks, setBookmarks] = useState([]); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + + useEffect(() => { + const fetchBookmarks = async () => { + try { + const { content } = await getMyBookmarks({ page: 1, size: 10 }); + setBookmarks(content); + } catch (err: unknown) { + console.error(err); + if (err instanceof Error) { + setError(err.message); + } else { + setError('북마크를 불러오지 못했습니다.'); + } + } finally { + setLoading(false); + } + }; + + fetchBookmarks(); + }, []); + return ( -
-
-
console.log('케밥버튼 클릭')} - className="bg-gray-900" - > - 북마크 -
+
+
console.log('케밥버튼 클릭')} + className="bg-gray-900" + > + 북마크 +
- {/* 리뷰 목록 */} -
- {mockMyReviews.map((review) => ( - { - console.log(`Review ${review.id} clicked`); - }} - /> - ))} -
-
+
+ {loading &&

로딩 중...

} + {error &&

{error}

} + {!loading && !error && bookmarks.length === 0 && ( +

북마크한 후기가 없습니다.

+ )} + {bookmarks.map((review) => ( + tag.hashTagName)} + title={review.title} + description={review.content} + likeCount={review.heartCount} + onClick={() => { + console.log(`Bookmark ${review.reviewId} clicked`); + }} + /> + ))} +
); }; diff --git a/src/pages/my/MyFeedback.tsx b/src/pages/my/MyFeedback.tsx index 7bdd556..1be6abb 100644 --- a/src/pages/my/MyFeedback.tsx +++ b/src/pages/my/MyFeedback.tsx @@ -1,33 +1,43 @@ +import { useNavigate } from 'react-router-dom'; +import { useToastStore } from '@/store'; +import { postFeedback } from '@/api//feedback/feedback.api'; import { useState } from 'react'; import { Button, Header } from '@/components'; export default function MyFeedbackPage() { const [feedbackText, setFeedbackText] = useState(''); const MAX_LENGTH = 1000; - const isButtonDisabled = feedbackText.length === 0; + const navigate = useNavigate(); + const { show } = useToastStore(); + const handleTextChange = (e: React.ChangeEvent) => { - if (e.target.value.length > MAX_LENGTH) { - setFeedbackText(e.target.value.slice(0, MAX_LENGTH)); - } else { - setFeedbackText(e.target.value); + const value = e.target.value; + setFeedbackText(value.length > MAX_LENGTH ? value.slice(0, MAX_LENGTH) : value); + }; + + const handleSubmit = async () => { + try { + await postFeedback({ feedbackContent: feedbackText }); + show('SEEAT 팀에게 의견을 보냈어요!'); + navigate('/my'); + } catch (err) { + console.error('피드백 전송 실패:', err); + show('의견 보내기에 실패했어요.'); } }; return (
- {/* 헤더 */} -
+
의견 보내기
- - {/* textarea와 글자수 카운터를 감싸는 컨테이너 */}