Feat: 리뷰 디테일 구현, test 못함
This commit is contained in:
parent
745a1198d8
commit
ea344dca19
@ -1,86 +1,152 @@
|
|||||||
import useReviewDetailQuery from '@/queries/reviews/useReviewDetailQuery';
|
import { useState } from 'react';
|
||||||
import useUpdateReviewQuery from '@/queries/reviews/useUpdateReviewQuery';
|
|
||||||
import useDeleteReviewQuery from '@/queries/reviews/useDeleteReviewQuery';
|
|
||||||
import { useParams } from 'react-router-dom';
|
import { useParams } from 'react-router-dom';
|
||||||
|
import Slider from 'react-slick';
|
||||||
|
import useReviewDetailQuery from '@/queries/reviews/useReviewDetailQuery';
|
||||||
|
import useUpdateReviewStatusQuery from '@/queries/reviews/useUpdateReviewStatusQuery';
|
||||||
|
import useProjectMembersQuery from '@/queries/projects/useProjectMembersQuery';
|
||||||
|
import useAuthStore from '@/stores/useAuthStore';
|
||||||
|
import { Button } from '@/components/ui/button';
|
||||||
|
import 'slick-carousel/slick/slick.css';
|
||||||
|
import 'slick-carousel/slick/slick-theme.css';
|
||||||
|
|
||||||
export default function ReviewDetail() {
|
export default function ReviewDetail(): JSX.Element {
|
||||||
const { projectId, reviewId } = useParams<{ projectId: string; reviewId: string }>();
|
const { projectId, reviewId } = useParams<{ projectId: string; reviewId: string }>();
|
||||||
const memberId = 1;
|
const profile = useAuthStore((state) => state.profile);
|
||||||
|
const memberId = profile?.id || 0;
|
||||||
|
|
||||||
const { data: reviewDetail } = useReviewDetailQuery(Number(projectId), Number(reviewId), memberId);
|
const { data: reviewDetail } = useReviewDetailQuery(Number(projectId), Number(reviewId), memberId);
|
||||||
const updateReview = useUpdateReviewQuery();
|
const { data: projectMembers } = useProjectMembersQuery(Number(projectId), memberId);
|
||||||
const deleteReview = useDeleteReviewQuery();
|
|
||||||
|
|
||||||
const handleUpdate = () => {
|
const updateReviewStatus = useUpdateReviewStatusQuery();
|
||||||
updateReview.mutate({
|
const [activeTab, setActiveTab] = useState<'content' | 'images'>('content');
|
||||||
|
|
||||||
|
const handleApprove = () => {
|
||||||
|
updateReviewStatus.mutate({
|
||||||
projectId: Number(projectId),
|
projectId: Number(projectId),
|
||||||
reviewId: Number(reviewId),
|
reviewId: Number(reviewId),
|
||||||
memberId,
|
memberId,
|
||||||
reviewData: {
|
reviewStatus: 'APPROVED',
|
||||||
title: reviewDetail.title,
|
|
||||||
content: reviewDetail.content,
|
|
||||||
imageIds: reviewDetail.images.map((image) => image.id),
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleDelete = () => {
|
const handleReject = () => {
|
||||||
deleteReview.mutate({
|
updateReviewStatus.mutate({
|
||||||
projectId: Number(projectId),
|
projectId: Number(projectId),
|
||||||
reviewId: Number(reviewId),
|
reviewId: Number(reviewId),
|
||||||
memberId,
|
memberId,
|
||||||
|
reviewStatus: 'REJECTED',
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!reviewDetail) return <p>Loading...</p>;
|
const settings = {
|
||||||
|
dots: true,
|
||||||
const { title, content, reviewStatus, images } = reviewDetail;
|
infinite: true,
|
||||||
|
speed: 500,
|
||||||
|
slidesToShow: 1,
|
||||||
|
slidesToScroll: 1,
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col gap-4 bg-white p-6">
|
<div className="review-detail-container p-4">
|
||||||
<header className="flex items-center justify-between">
|
<div className="header mb-4">
|
||||||
<h1 className="text-2xl font-bold text-[#333238]">{title}</h1>
|
<h1 className="text-2xl font-bold">{reviewDetail.title}</h1>
|
||||||
<div className="rounded-full bg-[#cbe2f9] px-3 py-0.5 text-xs text-[#0b5cad]">{reviewStatus}</div>
|
<p className="text-sm text-gray-500">
|
||||||
</header>
|
작성자: {reviewDetail.nickname} ({reviewDetail.email})
|
||||||
|
</p>
|
||||||
<div className="flex items-center gap-2">
|
<p className="text-sm text-gray-500">작성일: {new Date(reviewDetail.createAt).toLocaleDateString()}</p>
|
||||||
<p className="text-sm text-[#737278]">by 김용수</p>
|
<p className="text-sm text-gray-500">수정일: {new Date(reviewDetail.updateAt).toLocaleDateString()}</p>
|
||||||
<p className="text-sm text-[#737278]">|</p>
|
|
||||||
<p className="text-sm text-[#737278]">8 hours ago</p>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="border-b pb-4">
|
<div className="relative w-full px-4">
|
||||||
<h2 className="text-xl font-semibold">내용</h2>
|
<div className="flex w-full items-center border-b-[0.67px] border-solid border-[#dcdcde]">
|
||||||
<p className="mt-2 text-sm text-[#333238]">{content}</p>
|
{['content', 'images'].map((tab) => (
|
||||||
</div>
|
<button
|
||||||
|
key={tab}
|
||||||
<div className="flex flex-col">
|
className={`flex h-12 w-[100px] items-center justify-center px-3 ${
|
||||||
<h2 className="text-xl font-semibold">이미지 목록</h2>
|
activeTab === tab ? 'shadow-[inset_0px_-2px_0px_#1f75cb]' : ''
|
||||||
<ul className="mt-2 list-inside list-disc">
|
}`}
|
||||||
{images.map((image) => (
|
onClick={() => setActiveTab(tab as 'content' | 'images')}
|
||||||
<li
|
|
||||||
key={image.id}
|
|
||||||
className="text-sm text-[#737278]"
|
|
||||||
>
|
>
|
||||||
{image.imageTitle} (status: {image.status})
|
<span className={`text-sm ${activeTab === tab ? 'font-semibold' : 'font-normal'} text-[#333238]`}>
|
||||||
|
{tab === 'content' ? '내용' : '이미지'}
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="content mt-4">
|
||||||
|
{activeTab === 'content' ? (
|
||||||
|
<p className="text-gray-700">{reviewDetail.content}</p>
|
||||||
|
) : (
|
||||||
|
<div className="images mt-4">
|
||||||
|
{reviewDetail.images.length > 0 ? (
|
||||||
|
<Slider {...settings}>
|
||||||
|
{reviewDetail.images.map((image) => (
|
||||||
|
<div key={image.id}>
|
||||||
|
<img
|
||||||
|
src={image.imagePath}
|
||||||
|
alt="리뷰 이미지"
|
||||||
|
className="h-auto w-full rounded"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</Slider>
|
||||||
|
) : (
|
||||||
|
<p className="text-gray-500">이미지가 없습니다.</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{reviewDetail.reviewStatus === 'APPROVED' && (
|
||||||
|
<div className="reviewer-info mt-6">
|
||||||
|
<h2 className="text-lg font-semibold">리뷰어</h2>
|
||||||
|
<div className="flex items-center">
|
||||||
|
<img
|
||||||
|
src={reviewDetail.reviewerProfileImage}
|
||||||
|
alt="리뷰어 프로필"
|
||||||
|
className="h-10 w-10 rounded-full"
|
||||||
|
/>
|
||||||
|
<div className="ml-4">
|
||||||
|
<p className="font-bold">{reviewDetail.reviewerNickname}</p>
|
||||||
|
<p className="text-gray-500">{reviewDetail.reviewerEmail}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div className="meta-info mt-6">
|
||||||
|
<h2 className="text-lg font-semibold">프로젝트 멤버</h2>
|
||||||
|
<ul className="list-disc pl-6">
|
||||||
|
{projectMembers.map((member) => (
|
||||||
|
<li
|
||||||
|
key={member.memberId}
|
||||||
|
className="text-gray-700"
|
||||||
|
>
|
||||||
|
{member.nickname} - {member.privilegeType}
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex gap-2">
|
<div className="actions mt-6 flex justify-end space-x-2">
|
||||||
<button
|
{reviewDetail.reviewStatus !== 'APPROVED' && (
|
||||||
onClick={handleUpdate}
|
<Button
|
||||||
className="rounded-lg bg-blue-500 px-4 py-2 text-white hover:bg-blue-600"
|
variant="default"
|
||||||
|
onClick={handleApprove}
|
||||||
>
|
>
|
||||||
수정하기
|
승인
|
||||||
</button>
|
</Button>
|
||||||
<button
|
)}
|
||||||
onClick={handleDelete}
|
{reviewDetail.reviewStatus !== 'REJECTED' && (
|
||||||
className="rounded-lg bg-red-500 px-4 py-2 text-white hover:bg-red-600"
|
<Button
|
||||||
|
variant="destructive"
|
||||||
|
onClick={handleReject}
|
||||||
>
|
>
|
||||||
삭제하기
|
거부
|
||||||
</button>
|
</Button>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
Loading…
Reference in New Issue
Block a user