Refactor: ReviewDetail API 응답 수정, 리뷰 리팩토링

This commit is contained in:
홍창기 2024-10-02 10:55:05 +09:00
parent 69cead34b1
commit d26fb5327d
7 changed files with 52 additions and 33 deletions

View File

@ -2,6 +2,8 @@ import { Briefcase, Tag, Box, Layers } from 'lucide-react';
import { Link } from 'react-router-dom';
import useProjectQuery from '@/queries/projects/useProjectQuery';
import useAuthStore from '@/stores/useAuthStore';
import { cn } from '@/lib/utils';
import formatDateTime from '@/utils/formatDateTime';
interface ReviewItemProps {
title: string;
@ -42,11 +44,11 @@ export default function ReviewItem({
>
<div className="flex h-[100px] w-full items-center justify-between border-b-[0.67px] border-[#ececef] bg-[#fbfafd] p-4">
<div className="flex flex-col">
<p className="text-sm font-semibold text-[#333238]">{title}</p>
<p className="mt-1 text-xs text-[#737278]">by {creatorName}</p>
<p className="text-sm font-semibold text-black">{title}</p>
<p className="mt-1 text-xs text-gray-500">by {creatorName}</p>
<div className="mt-1 flex items-center">
<Briefcase className="h-3 w-3 text-[#737278]" />
<p className="ml-1 text-xs text-[#737278]">{projectData?.title}</p>
<Briefcase className="h-3 w-3 text-gray-500" />
<p className="ml-1 text-xs text-gray-500">{projectData?.title}</p>
</div>
{type && (
<div
@ -59,8 +61,19 @@ export default function ReviewItem({
)}
</div>
<div className="flex flex-col items-end gap-1">
<div className="rounded-full bg-[#cbe2f9] px-3 py-0.5 text-center text-xs text-[#0b5cad]">{status}</div>
<p className="text-xs text-[#737278]">Created at {createdTime}</p>
<div
className={cn(
'rounded-full px-3 py-0.5 text-center text-xs',
status === 'APPROVED'
? 'bg-green-100 text-green-600'
: status === 'REJECTED'
? 'bg-red-100 text-red-600'
: 'bg-blue-100 text-blue-600'
)}
>
{status}
</div>
<p className="text-xs text-gray-500">Created at {formatDateTime(createdTime)}</p>
</div>
</div>
</Link>

View File

@ -4,8 +4,8 @@ import { ReviewResponse } from '@/types';
interface ReviewListProps {
reviews: ReviewResponse[];
activeTab: 'REQUESTED' | 'APPROVED' | 'REJECTED' | 'all';
setActiveTab: React.Dispatch<React.SetStateAction<'REQUESTED' | 'APPROVED' | 'REJECTED' | 'all'>>;
activeTab: 'REQUESTED' | 'APPROVED' | 'REJECTED' | 'ALL';
setActiveTab: React.Dispatch<React.SetStateAction<'REQUESTED' | 'APPROVED' | 'REJECTED' | 'ALL'>>;
setSearchQuery: React.Dispatch<React.SetStateAction<string>>;
sortValue: string;
setSortValue: React.Dispatch<React.SetStateAction<string>>;
@ -22,18 +22,18 @@ export default function ReviewList({
workspaceId,
}: ReviewListProps) {
return (
<div className="relative w-full">
<div className="relative w-full px-4">
<div className="flex w-full items-center border-b-[0.67px] border-solid border-[#dcdcde]">
{['REQUESTED', 'APPROVED', 'REJECTED', 'all'].map((tab) => (
<div className="relative w-full">
<div className="flex w-full items-center border-b-[1px] border-solid border-gray-300">
{['REQUESTED', 'APPROVED', 'REJECTED', 'ALL'].map((tab) => (
<button
key={tab}
className={`flex h-12 w-[100px] items-center justify-between px-3 ${
activeTab === tab ? 'shadow-[inset_0px_-2px_0px_#1f75cb]' : ''
className={`flex h-12 w-[100px] items-center justify-center px-3 ${
activeTab === tab ? 'border-b-[3px] border-blue-500' : 'border-b-[3px] border-transparent'
}`}
onClick={() => setActiveTab(tab as typeof activeTab)}
>
<span className={`text-sm ${activeTab === tab ? 'font-semibold' : 'font-normal'} text-[#333238]`}>
<span className={`text-sm ${activeTab === tab ? 'font-semibold' : 'font-normal'} text-black`}>
{tab === 'REQUESTED' ? '요청' : tab === 'APPROVED' ? '승인' : tab === 'REJECTED' ? '거부' : '전체'}
</span>
</button>
@ -41,7 +41,7 @@ export default function ReviewList({
</div>
</div>
<div className="relative w-full px-4">
<div className="relative w-full">
<ReviewSearchInput
onSearchChange={setSearchQuery}
onSortChange={setSortValue}
@ -49,7 +49,7 @@ export default function ReviewList({
/>
</div>
<div className="relative w-full overflow-y-auto px-4">
<div className="relative w-full overflow-y-auto">
{reviews.length === 0 ? (
<div className="py-4 text-center"> .</div>
) : (

View File

@ -10,12 +10,12 @@ export default function ProjectReviewList() {
const profile = useAuthStore((state) => state.profile);
const memberId = profile?.id || 0;
const [activeTab, setActiveTab] = useState<'REQUESTED' | 'APPROVED' | 'REJECTED' | 'all'>('REQUESTED');
const [activeTab, setActiveTab] = useState<'REQUESTED' | 'APPROVED' | 'REJECTED' | 'ALL'>('REQUESTED');
const [, setSearchQuery] = useState('');
const [sortValue, setSortValue] = useState('latest');
const sortDirection = sortValue === 'latest' ? 0 : 1;
const reviewStatus = activeTab !== 'all' ? activeTab : undefined;
const reviewStatus = activeTab !== 'ALL' ? activeTab : undefined;
const { data, fetchNextPage, hasNextPage, isFetchingNextPage, refetch } = useReviewByStatusQuery(
Number(projectId),

View File

@ -60,25 +60,25 @@ export default function ReviewDetail(): JSX.Element {
return (
<div className="review-detail-container p-4">
<div className="header mb-4">
<h1 className="text-2xl font-bold">{reviewDetail.title}</h1>
<p className="text-sm text-gray-500">
: {reviewDetail.author.nickname} ({reviewDetail.author.email})
<h1 className="heading mb-2">{reviewDetail.title}</h1>
<p className="body-small text-gray-500">
: {reviewDetail.author.nickname} ({reviewDetail.author.email})
</p>
<p className="text-sm text-gray-500">: {new Date(reviewDetail.createAt).toLocaleDateString()}</p>
<p className="text-sm text-gray-500">: {new Date(reviewDetail.updateAt).toLocaleDateString()}</p>
<p className="body-small text-gray-500"> : {new Date(reviewDetail.createdAt).toLocaleDateString()}</p>
<p className="body-small text-gray-500"> : {new Date(reviewDetail.updatedAt).toLocaleDateString()}</p>
</div>
<div className="relative w-full px-4">
<div className="flex w-full items-center border-b-[0.67px] border-solid border-[#dcdcde]">
<div className="relative w-full">
<div className="flex w-full items-center border-b-[1px] border-solid border-gray-300">
{['content', 'images'].map((tab) => (
<button
key={tab}
className={`flex h-12 w-[100px] items-center justify-center px-3 ${
activeTab === tab ? 'shadow-[inset_0px_-2px_0px_#1f75cb]' : ''
activeTab === tab ? 'border-b-[3px] border-blue-500' : 'border-b-[3px] border-transparent'
}`}
onClick={() => setActiveTab(tab as 'content' | 'images')}
>
<span className={`text-sm ${activeTab === tab ? 'font-semibold' : 'font-normal'} text-[#333238]`}>
<span className={`text-sm ${activeTab === tab ? 'font-semibold' : 'font-normal'} text-black`}>
{tab === 'content' ? '내용' : '이미지'}
</span>
</button>
@ -126,7 +126,7 @@ export default function ReviewDetail(): JSX.Element {
<p className="font-bold">{reviewDetail.reviewer.nickname}</p>
<p className="text-gray-500">{reviewDetail.reviewer.email}</p>
<p className="text-gray-500">
{reviewDetail.reviewStatus === 'APPROVED' ? '승인한 사람:' : '거부한 사람:'}{' '}
{reviewDetail.reviewStatus === 'APPROVED' ? '승인한 사람 : ' : '거부한 사람 : '}
{reviewDetail.reviewer.nickname}
</p>
</div>

View File

@ -10,12 +10,12 @@ export default function WorkspaceReviewList() {
const profile = useAuthStore((state) => state.profile);
const memberId = profile?.id || 0;
const [activeTab, setActiveTab] = useState<'REQUESTED' | 'APPROVED' | 'REJECTED' | 'all'>('REQUESTED');
const [activeTab, setActiveTab] = useState<'REQUESTED' | 'APPROVED' | 'REJECTED' | 'ALL'>('REQUESTED');
const [, setSearchQuery] = useState('');
const [sortValue, setSortValue] = useState('latest');
const sortDirection = sortValue === 'latest' ? 0 : 1;
const reviewStatus = activeTab !== 'all' ? activeTab : undefined;
const reviewStatus = activeTab !== 'ALL' ? activeTab : undefined;
const { data, fetchNextPage, hasNextPage, isFetchingNextPage, refetch } = useWorkspaceReviewsQuery(
Number(workspaceId),

View File

@ -37,8 +37,8 @@ export interface ReviewDetailResponse {
content: string;
reviewStatus: 'REQUESTED' | 'APPROVED' | 'REJECTED';
images: ReviewImageResponse[];
createAt: string;
updateAt: string;
createdAt: string;
updatedAt: string;
author: MemberResponse;
reviewer: MemberResponse;
}

View File

@ -0,0 +1,6 @@
export default function formatDateTime(dateTimeString: string): string {
const [date, time] = dateTimeString.split('T');
const [hours, minutes] = time.split(':');
return `${date} ${hours}:${minutes}`;
}