diff --git a/src/api/editreview.ts b/src/api/editreview.ts new file mode 100644 index 0000000..c9a5aab --- /dev/null +++ b/src/api/editreview.ts @@ -0,0 +1,28 @@ +import apiClient from '@/api/apiClient'; + +interface UpdateReviewRequest { + reviewId: number; + rating: number; + lightBold: number; + smoothTannic: number; + drySweet: number; + softAcidic: number; + aroma: string[]; + content: string; +} + +interface UpdateReviewResponse { + success: boolean; + message?: string; +} + +export const updateReview = async ({ + reviewId, + ...body +}: UpdateReviewRequest): Promise => { + const response = await apiClient.patch( + `/${process.env.NEXT_PUBLIC_TEAM}/reviews/${reviewId}`, + body, + ); + return response.data; +}; diff --git a/src/components/Modal/ReviewModal/EditReviewModal.tsx b/src/components/Modal/ReviewModal/EditReviewModal.tsx new file mode 100644 index 0000000..6815fe9 --- /dev/null +++ b/src/components/Modal/ReviewModal/EditReviewModal.tsx @@ -0,0 +1,305 @@ +import React, { useState } from 'react'; + +import { useMutation, useQueryClient } from '@tanstack/react-query'; +import Image from 'next/image'; +import { useForm } from 'react-hook-form'; + +import { updateReview } from '@/api/editreview'; +import BasicModal from '@/components/common/Modal/BasicModal'; +import StarRating from '@/components/Modal/ReviewModal/StarRating'; +import { cn } from '@/lib/utils'; + +import FlavorSlider from '../../common/slider/FlavorSlider'; +import { Badge } from '../../ui/badge'; +import { Button } from '../../ui/button'; + +interface ReviewForm { + rating: number; + sliderLightBold: number; + sliderSmoothTanic: number; + sliderdrySweet: number; + slidersoftAcidic: number; + aroma: Array; + content: string; +} + +interface ReviewData { + reviewId: number; + rating: number; + sliderLightBold: number; + sliderSmoothTanic: number; + sliderdrySweet: number; + slidersoftAcidic: number; + aroma: string[]; + content: string; +} + +const aromaOptions = [ + '체리', + '베리', + '오크', + '바닐라', + '후추', + '제빵', + '풀', + '사과', + '복숭아', + '시트러스', + '트로피컬', + '미네랄', + '꽃', + '담뱃잎', + '흙', + '초콜릿', + '스파이스', + '카라멜', + '가죽', +]; + +const aromaMap: Record = { + 체리: 'CHERRY', + 베리: 'BERRY', + 오크: 'OAK', + 바닐라: 'VANILLA', + 후추: 'PEPPER', + 제빵: 'BAKERY', + 풀: 'GRASS', + 사과: 'APPLE', + 복숭아: 'PEACH', + 시트러스: 'CITRUS', + 트로피컬: 'TROPICAL', + 미네랄: 'MINERAL', + 꽃: 'FLOWER', + 담뱃잎: 'TOBACCO', + 흙: 'EARTH', + 초콜릿: 'CHOCOLATE', + 스파이스: 'SPICE', + 카라멜: 'CARAMEL', + 가죽: 'LEATHER', +}; + +const EditReviewModal = ({ + wineName, + reviewData, +}: { + wineName: string; + reviewData: ReviewData; +}) => { + const [showEditModal, setShowEditModal] = useState(false); + const queryClient = useQueryClient(); + + const updateReviewMutation = useMutation({ + mutationFn: updateReview, + onSuccess: () => { + console.log('리뷰 수정 완료'); + queryClient.invalidateQueries({ queryKey: ['reviews'] }); + setShowEditModal(false); + }, + onError: (error) => { + console.log('리뷰 수정 실패', error); + }, + }); + + const { + register, + handleSubmit, + formState: { errors }, + clearErrors, + reset, + watch, + setValue, + setError, + } = useForm({ + defaultValues: { + rating: reviewData.rating, + sliderLightBold: reviewData.sliderLightBold, + sliderSmoothTanic: reviewData.sliderSmoothTanic, + sliderdrySweet: reviewData.sliderdrySweet, + slidersoftAcidic: reviewData.slidersoftAcidic, + content: reviewData.content, + aroma: reviewData.aroma.map((eng) => { + const kor = Object.keys(aromaMap).find((key) => aromaMap[key] === eng); + return kor || eng; + }), + }, + }); + + const aroma = watch('aroma'); + const isSelected = (item: string) => aroma?.includes(item); + + const toggleAroma = (item: string) => { + if (!aroma) return; + if (aroma.includes(item)) { + setValue( + 'aroma', + aroma.filter((a) => a !== item), + ); + } else { + setValue('aroma', [...aroma, item]); + } + clearErrors('aroma'); + }; + + const onSubmit = async (data: ReviewForm) => { + if (!data.aroma || data.aroma.length === 0) { + setError('aroma', { type: 'errmsg', message: '최소 하나의 향을 선택해주세요.' }); + return; + } + const fullData = { + reviewId: reviewData.reviewId, + rating: data.rating, + lightBold: data.sliderLightBold, + smoothTannic: data.sliderSmoothTanic, + drySweet: data.sliderdrySweet, + softAcidic: data.slidersoftAcidic, + aroma: data.aroma.map((a) => aromaMap[a]).filter(Boolean), + content: data.content, + }; + updateReviewMutation.mutate(fullData); + }; + + const closeModalReset = (isOpen: boolean) => { + setShowEditModal(isOpen); + if (!isOpen) { + reset(); + } + }; + + return ( +
+ + + 수정 완료 + + } + > +
+
+ 리뷰 아이콘 +
+ {wineName} +
+ setValue('rating', val)} /> +
+
+
+ +