Skip to content
Open
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
8 changes: 3 additions & 5 deletions api/history/winnerHistoryApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,13 @@ import baseURL from '../baseURL';

export async function getWinnerHistory({
userToken,
cursor,
size,
size = 5,
}: {
userToken: string;
cursor: number;
size: number;
size?: number;
}) {
try {
const response = await fetch(`${baseURL}/api/v1/winner_history?cursor=${cursor}&size=${size}`, {
const response = await fetch(`${baseURL}/api/v1/winner_history?size=${size}`, {
method: 'GET',
headers: {
Authorization: `Bearer ${userToken}`,
Expand Down
1 change: 1 addition & 0 deletions api/raffle/purchaseItemApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ async function postPurchaseItem({ raffleId, userToken }: { raffleId: number; use
return response.json();
} catch (error) {
console.error('상품 구매 실패', error);
throw error;
}
}

Expand Down
85 changes: 85 additions & 0 deletions api/review/reviewApi.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import baseURL from '../baseURL';

/**
* @description 모든 리뷰 목록 불러오는 API 함수
*/
async function getAllReviewList({ userToken, size = 5 }: { userToken: string; size?: number }) {
try {
const response = await fetch(`${baseURL}/api/v1/reviews?size=${size}`, {
method: 'GET',
headers: {
Authorization: `Bearer ${userToken}`,
'Content-Type': 'application/json',
},
});
if (!response.ok) {
throw new Error('리뷰 목록 불러오기 실패');
}
return response.json();
} catch (error) {
console.error('리뷰 목록 불러오기 실패', error);
throw error;
}
}

/**
* @description 응모 상품 당첨자 리뷰 작성 API 함수
*/
async function createUserReview({
userToken,
reviewData,
}: {
userToken: string;
reviewData: {
title: string;
description: string;
imageUrl: string;
};
}) {
try {
const params = new URLSearchParams();
Object.entries(reviewData).forEach(([key, value]) => {
params.append(key, value);
});

console.log(params);

const response = await fetch(`${baseURL}/api/v1/user/reviews?${params.toString()}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${userToken}`,
},
});

if (!response.ok) {
throw new Error('리뷰 작성 실패');
}
console.log(response);
return response;
} catch (error) {
console.error('리뷰 작성 실패', error);
throw error;
}
}

async function getUserReviewList({ size = 5, userToken }: { size?: number; userToken: string }) {
try {
const response = await fetch(`${baseURL}/api/v1/user/reviews?&size=${size}`, {
method: 'GET',
headers: {
Authorization: `Bearer ${userToken}`,
'Content-Type': 'application/json',
},
});
if (!response.ok) {
throw new Error('리뷰 목록 불러오기 실패');
}
return response.json();
} catch (error) {
console.error('리뷰 목록 불러오기 실패', error);
throw error;
}
}

export { createUserReview, getAllReviewList, getUserReviewList };
131 changes: 131 additions & 0 deletions app/(banner-layout)/review/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
'use client';

import { useMutation, useQuery } from '@tanstack/react-query';
import { useState } from 'react';
import { createUserReview, getAllReviewList } from '../../../api/review/reviewApi';
import useAuthStore from '../../../lib/store/useAuthStore';

type ReviewData = {
title: string;
description: string;
imageUrl: string;
};

type ReviewListData = {
data: {
id: number;
title: string;
description: string;
imageUrl: string;
}[];
};

export default function ReviewPage() {
const userToken = useAuthStore<string>((state) => state.userToken);

const [reviewData, setReviewData] = useState<ReviewData>({
title: '',
description: '',
imageUrl: '',
});

const {
data: ReviewListData,
isLoading,
error,
isError,
} = useQuery<ReviewListData>({
queryKey: ['getAllReviewList'],
queryFn: () =>
getAllReviewList({
userToken,
}),
enabled: !!userToken,
});

const mutation = useMutation({
mutationFn: () => createUserReview({ userToken, reviewData }),
onSuccess: () => {
setReviewData({ title: '', description: '', imageUrl: '' });
alert('리뷰가 성공적으로 등록되었습니다.');
},
onError: (error) => {
alert(`리뷰 등록 실패: ${error instanceof Error ? error.message : 'Unknown error'}`);
},
});

const handleSubmit = (event: React.FormEvent) => {
event.preventDefault();
mutation.mutate();
};

const handleChange = (event: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
const { name, value } = event.target;
setReviewData((prevData) => ({
...prevData,
[name]: value,
}));
};

if (!userToken) {
return <div>로그인이 필요합니다.</div>;
}

if (isLoading) {
return <div>Loading...</div>;
}

if (isError) {
return <div>Error: {error instanceof Error ? error.message : JSON.stringify(error)}</div>;
}

return (
<div>
<h1 className="text-3xl font-bold mb-6 text-primary">Review Page</h1>
<div>
{ReviewListData &&
ReviewListData?.data.map((review) => (
<div key={review.id}>
<p>{review.id}번</p>
이미지:
{review.imageUrl && <img src={review.imageUrl} alt={review.title} />}
<h2>제목: {review.title}</h2>
<p>설명: {review.description}</p>
</div>
))}
</div>
<form onSubmit={handleSubmit}>
<div>
<label htmlFor="title">Title:</label>
<input
type="text"
name="title"
value={reviewData.title}
onChange={handleChange}
required
/>
</div>
<div>
<label htmlFor="description">Description:</label>
<textarea
name="description"
value={reviewData.description}
onChange={handleChange}
required
/>
</div>
<div>
<label htmlFor="imageUrl">Image URL</label>
<input
type="file"
name="imageUrl"
value={reviewData.imageUrl}
onChange={handleChange}
required
/>
</div>
<button type="submit">Submit Review</button>
</form>
</div>
);
}
6 changes: 2 additions & 4 deletions app/myInfo/winnerHistory/page.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,20 @@
'use client';

import { useQuery } from '@tanstack/react-query';
import React, { useState } from 'react';
import { getWinnerHistory } from '../../../api/history/winnerHistoryApi';
import useAuthStore from '../../../lib/store/useAuthStore';

export default function WinnerHistoryPage() {
const userToken = useAuthStore<string>((state) => state.userToken);
const [cursor, setCursor] = useState<number>(0);
const [size] = useState<number>(5);

const {
data: winnerData,
isLoading,
isError,
error,
} = useQuery({
queryKey: ['getWinnerHistory'],
queryFn: () => getWinnerHistory({ userToken, cursor, size }),
queryFn: () => getWinnerHistory({ userToken }),
enabled: !!userToken,
});

Expand Down
9 changes: 9 additions & 0 deletions components/Header/HeaderNav.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,15 @@ export default function HeaderNav() {
Contact
</Link>
</li>
<li className="sm:border-none border-solid border-2 border-primary rounded-xl bg-slate-100 p-2 flex items-center justify-center ">
<Link
href="/review"
className={`p-2 w-full text-center ${pathName === '/review' ? 'text-blue-700 font-bold' : 'text-gray-500'}`}
onClick={closeMenu}
>
Review
</Link>
</li>
</ul>
<ul className="flex gap-3 items-center">
<li>
Expand Down