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
11 changes: 11 additions & 0 deletions next.config.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,17 @@
/** @type {import('next').NextConfig} */
const nextConfig = {
reactStrictMode: true,
swcMinify: true,
images: {
remotePatterns: [
{
protocol: 'https',
hostname: 'sprint-fe-project.s3.ap-northeast-2.amazonaws.com',
port: '',
pathname: '/**',
},
],
},

webpack(config) {
config.module.rules = config.module.rules.filter(
Expand Down
8 changes: 8 additions & 0 deletions src/api/wineid.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// import { createSeverApiInstance, RetryRequestConfig } from './apiServer';
import { GetWineInfoResponse } from '@/types/WineTypes';

import apiClient from './apiClient';

export const getWineInfoForClient = (wineid: number): Promise<GetWineInfoResponse> => {
return apiClient.get(`/${process.env.NEXT_PUBLIC_TEAM}/wines/${wineid}`);
};
17 changes: 17 additions & 0 deletions src/assets/icons/noReview.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
11 changes: 11 additions & 0 deletions src/assets/icons/wine-placeholder.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
38 changes: 29 additions & 9 deletions src/components/common/card/ImageCard.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
import React from 'react';
import React, { useState } from 'react';

import Image from 'next/image';

import WinePlaceholder from '@/assets/icons/wine-placeholder.svg';
import { cn } from '@/lib/utils';

interface ImageCardProps {
imageSrc: string;
children?: React.ReactNode;
rightSlot?: React.ReactNode;
className?: string;
imageClassName?: string;
imageClassName: string;
}

export function ImageCard({
Expand All @@ -19,17 +20,24 @@ export function ImageCard({
className,
imageClassName,
}: ImageCardProps) {
const [hasImageError, setHasImageError] = useState(false);

return (
<div className={cn('flex w-full rounded-xl bg-white p-4 border border-gray-300', className)}>
{/* μ™Όμͺ½ 이미지 */}
<div className='flex-shrink-0'>
<Image
src={imageSrc}
alt='와인 이미지'
width={80}
height={112}
className={cn('h-28 w-20 rounded-md object-cover', imageClassName)}
/>
{imageSrc !== '' && hasImageError ? (
<WineFallback className={imageClassName} />
) : (
<Image
src={imageSrc}
alt='와인 이미지'
width={80}
height={112}
className={cn('h-28 w-20 rounded-md object-cover', imageClassName)}
onError={() => setHasImageError(true)}
/>
)}
</div>

{/* 였λ₯Έμͺ½ ν…μŠ€νŠΈ & 상단 λ²„νŠΌ */}
Expand All @@ -42,3 +50,15 @@ export function ImageCard({
</div>
);
}

interface WineFallbackProps {
className: string;
}

function WineFallback({ className }: WineFallbackProps) {
return (
<div className={cn('text-primary', className, 'bottom-[-10px]')}>
<WinePlaceholder />
</div>
);
}
7 changes: 5 additions & 2 deletions src/components/common/card/ReviewCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import ShowMoreBtn from '@/assets/icons/showMoreBtn.svg';
import Star from '@/assets/icons/star.svg';
import { Badge } from '@/components/ui/badge';
import { Button } from '@/components/ui/button';
import { calculateRelativeTime } from '@/lib/calculateRelativeTime';
import { cn } from '@/lib/utils';
import useReviewCardStore from '@/stores/reviewCardStore';

Expand All @@ -27,7 +28,7 @@ export function ReviewCard({ children }: ReviewCardProps) {
}

ReviewCard.UserHeader = function UserHeader({ userIcon, reviewId, children }: UserHeaderProps) {
const username = useReviewCardStore((state) => state.allReviews[reviewId]?.user.name);
const username = useReviewCardStore((state) => state.allReviews[reviewId]?.user.nickname);
const timeAgo = useReviewCardStore((state) => state.allReviews[reviewId]?.updatedAt);

return (
Expand All @@ -38,7 +39,9 @@ ReviewCard.UserHeader = function UserHeader({ userIcon, reviewId, children }: Us
</div>
<div className='flex flex-col'>
<span className='custom-text-lg-semibold text-gray-900'>{username}</span>
<span className='custom-text-md-regular text-gray-500'>{timeAgo}</span>
<span className='custom-text-md-regular text-gray-500'>
{calculateRelativeTime(timeAgo)}
</span>
</div>
</div>

Expand Down
8 changes: 4 additions & 4 deletions src/components/common/card/ReviewCardTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,21 @@ interface ReviewCardProps {

interface UserHeaderProps {
userIcon: ReactNode;
reviewId: string;
reviewId: number;
children: ReactNode;
}

interface TagAndRatingProps {
reviewId: string;
reviewId: number;
}

interface ReviewBodyProps {
reviewId: string;
reviewId: number;
flavorSliderSlot: ReactNode;
}

interface ToggleButtonProps {
reviewId: string;
reviewId: number;
}

export type {
Expand Down
27 changes: 24 additions & 3 deletions src/components/wineDetail/LikeButton.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,41 @@
import { useState } from 'react';

import apiClient from '@/api/apiClient';
import FullLikeIcon from '@/assets/icons/fullLike.svg';
import LikeIcon from '@/assets/icons/like.svg';
import { Button } from '@/components/ui/button';
import { cn } from '@/lib/utils';

async function postLike(reviewId: number) {
console.log('μ’‹μ•„μš”!');
return apiClient.post(`${process.env.NEXT_PUBLIC_TEAM}/reviews/${reviewId}/like`);
}

async function deleteLike(reviewId: number) {
console.log('μ‹«μ–΄μš”!');
Comment on lines +9 to +15
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ν…ŒμŠ€νŠΈ μ™„λ£Œλ˜μ‹œλ©΄ μ½˜μ†” μ‚­μ œ λΆ€νƒλ“œλ¦½λ‹ˆλ‹€!

return apiClient.delete(`${process.env.NEXT_PUBLIC_TEAM}/reviews/${reviewId}/like`);
}

interface Props {
isLike?: boolean;
reviewId: number;
}

function LikeButton({ isLike }: Props) {
function LikeButton({ isLike, reviewId }: Props) {
const [isClicked, setIsClicked] = useState(isLike);

function handleToggle() {
setIsClicked(!isClicked);
async function handleToggle() {
setIsClicked((prev) => !prev); //미리 μ—…λ°μ΄νŠΈ
//μ’‹μ•„μš” api μš”μ²­ 보내기
// /{teamId}/reviews/{id}/like
Comment on lines +28 to +30
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

μ‚¬μš©μž κ³ λ €ν•΄μ„œ μ’‹μ•„μš” μ²˜λ¦¬κ°€ 미리 λ˜λŠ”κ²Œ μ’‹λ„€μš”


try {
isClicked === true ? await deleteLike(reviewId) : await postLike(reviewId);
} catch (err) {
//λͺ¨λ‹¬ 호좜 ν›„ μ§‘μ–΄ λ„£κΈ°

setIsClicked((prev) => !prev); //μ‹€νŒ¨ν•˜λ©΄ μ—…λ°μ΄νŠΈ ν–ˆλ˜ κ±° μ·¨μ†Œ
}
}

return (
Expand Down
28 changes: 28 additions & 0 deletions src/components/wineDetail/NoReviews.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import React from 'react';

import NoReviewIcon from '@/assets/icons/noReview.svg';

import { Button } from '../ui/button';

interface Props {
className: string;
}

function NoReviews({ className }: Props) {
return (
<div className={className}>
<div className='w-[150px] h-[158px] text-gray-400 mx-auto mb-10 md:mb-12 xl:mt-[152px]'>
<NoReviewIcon />
</div>
<Button
variant='purpleDark'
size='xs'
className='w-fit md:w-fit block px-5 py-2 mx-auto mb-[139px] md:mb-[426px] xl:mb[252px]'
>
리뷰 남기기
</Button>
</div>
);
}

export default NoReviews;
30 changes: 30 additions & 0 deletions src/components/wineDetail/Reviews.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import React from 'react';

import { WineReview } from '@/types/WineTypes';

import NoReviews from './NoReviews';
import WineReviewCard from './WineReviewCard';

interface Props {
reviews: WineReview[];
reviewCount: number;
}

function Reviews({ reviews, reviewCount }: Props) {
return (
<ul>
{/* μΆ”ν›„ 리뷰 νƒ€μž… λ„£κΈ° */}
{reviewCount > 0 ? (
reviews.map((review: WineReview) => (
<li key={review.id} className='mb-[16px] md:mb-[24px] xl:mb-[20px]'>
<WineReviewCard review={review} />
</li>
))
) : (
<NoReviews className='w-full xl:w-[1140px] pt-[80px]' />
)}
</ul>
);
}

export default Reviews;
50 changes: 28 additions & 22 deletions src/components/wineDetail/WineRating.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,30 +17,36 @@ function WineRating({ rating, reviewCount, ratings }: Props) {
className='mb-10 md:mb-[60px] md:px-[63px] xl:px-0 w-full xl:max-w-[280px] mx-auto xl:mx-0 order-1 xl:order-2
flex flex-col md:flex-row md:gap-20 xl:gap-0 xl:flex-col xl:relative'
>
<div className='flex justify-between order-1 md:flex-col xl:mb-5'>
<div className='flex items-center md:mb-[30px] xl:mb-0'>
<span className='text-[36px] md:text-[54px] font-extrabold mr-4 md:mr-5'>{rating}</span>
<div>
<AverageStar rating={rating} />
<div className='custom-text-md-regular text-gray-500'>{reviewCount}개의 ν›„κΈ°</div>
{reviewCount > 0 && (
<>
<div className='flex justify-between order-1 md:flex-col xl:mb-5'>
<div className='flex items-center md:mb-[30px] xl:mb-0'>
<span className='text-[36px] md:text-[54px] font-extrabold mr-4 md:mr-5'>
{rating}
</span>
<div>
<AverageStar rating={rating} />
<div className='custom-text-md-regular text-gray-500'>{reviewCount}개의 ν›„κΈ°</div>
</div>
</div>
<Button
variant='purpleDark'
size='xs'
className='w-fit md:w-fit inline-block px-5 py-2 xl:absolute xl:left-0 xl:top-[270px]'
>
리뷰 남기기
</Button>
</div>
</div>
<Button
variant='purpleDark'
size='xs'
className='w-fit md:w-fit inline-block px-5 py-2 xl:absolute xl:left-0 xl:top-[270px]'
>
리뷰 남기기
</Button>
</div>
<div className='flex gap-2 flex-col w-full order-2 '>
{ratings.map((rating, i) => (
<div key={`${5 - i}points`} className='flex items-center justify-between'>
<span className='block w-8 text-gray-500 mr-4'>{5 - i}점</span>
<Progress className='h-[6px]' value={rating} />
<div className='flex gap-2 flex-col w-full order-2 '>
{ratings.map((rating, i) => (
<div key={`${5 - i}points`} className='flex items-center justify-between'>
<span className='block w-8 text-gray-500 mr-4'>{5 - i}점</span>
<Progress className='h-[6px]' value={rating} />
</div>
))}
</div>
))}
</div>
</>
)}
</div>
);
}
Expand Down
Loading