Skip to content

Commit

Permalink
Yuhyun (#28)
Browse files Browse the repository at this point in the history
* feat: 리뷰 조회 기능

* feat: 리뷰 등록 기능 (+별점)

* conflict 해결

* feat: 리뷰 삭제 진행 중

* feat: 리뷰 삭제

* feat: 호텔 정보 추가

* feat: 리뷰 조회 테스트 페이지 생성

* fix: 리뷰 등록, 조회 페이지 변경

* conflict 해결

* .

* feat : 리뷰 등록 완료 시 호텔 상세 페이지로 이동

* feat: 리뷰 수정 진행 중

* feat: 리뷰 수정

* feat: 리뷰 수정폼 수정(+ 별점)

* design: 리뷰 디자인 수정

* design: 리뷰 목록, 등록폼 디자인 수정

* feat: 로그인 한 사용자만 리뷰 등록

* feat: 리뷰 작성자만 수정, 삭제 가능

* feat: 리뷰 등록, 수정일 표시

* feat: 전체 리뷰 평균 평점 추가

* feat: 리뷰  정렬 방식 추가

* .

---------

Co-authored-by: kyumho kim <[email protected]>
  • Loading branch information
leeyuhyun0104 and kyumho kim authored Feb 14, 2024
1 parent bfe2314 commit 68fb51d
Show file tree
Hide file tree
Showing 2 changed files with 148 additions and 85 deletions.
16 changes: 16 additions & 0 deletions src/components/review/ReviewForm.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,22 @@ const ReviewForm = ({ reservationId, hotelId, onReviewSubmit }) => {
const handleSubmit = async (e) => {
e.preventDefault()

if (!body) {
toast.error("리뷰 내용을 작성해주세요.")
return
}

const isValidRating = (rating) => rating > 0 && rating <= 5

if (
!isValidRating(ratings.amenities) ||
!isValidRating(ratings.staffService) ||
!isValidRating(ratings.cleanliness)
) {
toast.error("모든 항목의 별점을 입력해주세요.")
return
}

try {
await axios.post(
`${process.env.NEXT_PUBLIC_BASE_URL}/api/v1/review/add/${hotelId}/${reservationId}`,
Expand Down
217 changes: 132 additions & 85 deletions src/components/review/ReviewList.js
Original file line number Diff line number Diff line change
@@ -1,99 +1,129 @@
// src/components/review/ReviewList.js
import React, { useState, useEffect } from "react";
import axios from "@/config/axios-config";
import { useUser } from "@/hooks/useUser";
import { toast } from "react-toastify";
import "react-toastify/dist/ReactToastify.css";
import React, { useState, useEffect } from "react"
import axios from "@/config/axios-config"
import { useUser } from "@/hooks/useUser"
import { toast } from "react-toastify"
import "react-toastify/dist/ReactToastify.css"
import {
Button,
Modal,
ModalContent,
ModalBody,
ModalFooter,
} from "@nextui-org/react";
import EditReviewForm from "./EditReviewForm";
} from "@nextui-org/react"
import EditReviewForm from "./EditReviewForm"

const ReviewList = ({ hotelId, onReviewEdit }) => {
const [recentReviews, setRecentReviews] = useState([]);
const [allReviews, setAllReviews] = useState([]);
const [showModal, setShowModal] = useState(false);
const [modalSize, setModalSize] = useState();
const [editingReviewId, setEditingReviewId] = useState(null);
const [isEditing, setIsEditing] = useState(false);
const [averageAmenities, setAverageAmenities] = useState(0);
const [averageCleanliness, setAverageCleanliness] = useState(0);
const [averageStaffService, setAverageStaffService] = useState(0);
const [totalRating, setTotalRating] = useState(0);
const [recentReviews, setRecentReviews] = useState([])
const [allReviews, setAllReviews] = useState([])
const [showModal, setShowModal] = useState(false)
const [modalSize, setModalSize] = useState()
const [editingReviewId, setEditingReviewId] = useState(null)
const [isEditing, setIsEditing] = useState(false)
const [averageAmenities, setAverageAmenities] = useState(0)
const [averageCleanliness, setAverageCleanliness] = useState(0)
const [averageStaffService, setAverageStaffService] = useState(0)
const [totalRating, setTotalRating] = useState(0)
const [sortBy, setSortBy] = useState("recent")

const { user } = useUser();
const { user } = useUser()

const handleSortChange = (event) => {
setSortBy(event.target.value)
}

const fetchReviews = async () => {
try {
const response = await axios.get(
`${process.env.NEXT_PUBLIC_BASE_URL}/api/v1/review/${hotelId}`
);
console.log("Server Response:", response.data);
setAllReviews(response.data);
)
console.log("Server Response:", response.data)

// 최근 4개 리뷰
const recentReviewsData = response.data.slice(0, 4);
setRecentReviews(recentReviewsData);
const amenitiesAvg = calculateAverage(response.data, "amenities");
const cleanlinessAvg = calculateAverage(response.data, "cleanliness");
const staffServiceAvg = calculateAverage(response.data, "staffService");
const ratingAvg = calculateAverage(response.data, "rating");
const recentReviewsData = response.data.slice(0, 4)
setRecentReviews(recentReviewsData)

// 정렬 기준에 따라 전체 리뷰를 가져옴
const sortedReviews =
sortBy === "recent"
? [...response.data].sort((a, b) =>
b.createdAt.localeCompare(a.createdAt)
)
: sortBy === "highRating"
? [...response.data].sort((a, b) => b.rating - a.rating)
: sortBy === "lowRating"
? [...response.data].sort((a, b) => a.rating - b.rating)
: response.data

setAllReviews(sortedReviews)

const amenitiesAvg = calculateAverage(sortedReviews, "amenities")
const cleanlinessAvg = calculateAverage(sortedReviews, "cleanliness")
const staffServiceAvg = calculateAverage(sortedReviews, "staffService")
const ratingAvg = calculateAverage(sortedReviews, "rating")

const trimTrailingZeros = (num) => {
const str = num.toString();
const str = num.toString()
if (str.includes(".")) {
return str.replace(/(?:\.0+|(\.\d+?)0+)$/, "$1");
return str.replace(/(?:\.0+|(\.\d+?)0+)$/, "$1")
}
return str;
};
return str
}

setAverageAmenities(trimTrailingZeros(amenitiesAvg.toFixed(2)));
setAverageCleanliness(trimTrailingZeros(cleanlinessAvg.toFixed(2)));
setAverageStaffService(trimTrailingZeros(staffServiceAvg.toFixed(2)));
setTotalRating(trimTrailingZeros(ratingAvg.toFixed(2)));
setAverageAmenities(trimTrailingZeros(amenitiesAvg.toFixed(2)))
setAverageCleanliness(trimTrailingZeros(cleanlinessAvg.toFixed(2)))
setAverageStaffService(trimTrailingZeros(staffServiceAvg.toFixed(2)))
setTotalRating(trimTrailingZeros(ratingAvg.toFixed(2)))
} catch (error) {
console.error("리뷰를 불러오는 중 에러 발생:", error);
console.log("Recent Reviews:", recentReviews);
console.error("리뷰를 불러오는 중 에러 발생:", error)
console.log("Recent Reviews:", recentReviews)
}
};
}

const calculateAverage = (reviews, field) => {
const total = reviews.reduce((acc, review) => acc + review[field], 0);
return reviews.length > 0 ? total / reviews.length : 0;
};
const total = reviews.reduce((acc, review) => acc + review[field], 0)
return reviews.length > 0 ? total / reviews.length : 0
}

useEffect(() => {
fetchReviews();
}, [hotelId]);
fetchReviews()
}, [hotelId, sortBy])

const sortReviews = (reviews, sortBy) => {
if (sortBy === "recent") {
return reviews.sort(
(a, b) => new Date(b.createdAt) - new Date(a.createdAt)
)
} else if (sortBy === "rating") {
return reviews.sort((a, b) => b.rating - a.rating)
}
return reviews
}

const handleShowModal = (size) => {
setShowModal(true);
setModalSize(size);
};
setShowModal(true)
setModalSize(size)
}

const handleCloseModal = () => {
setShowModal(false);
setEditingReviewId(null);
};
setShowModal(false)
setEditingReviewId(null)
}

const renderStarsWithScore = (rating) => {
const fullStars = Math.floor(rating);
const remainder = rating - fullStars;
const fullStars = Math.floor(rating)
const remainder = rating - fullStars

const stars = Array.from({ length: 5 }, (_, index) => {
const starValue = index + 1;
let starColor = "lightgray";
const starValue = index + 1
let starColor = "lightgray"

if (starValue <= fullStars) {
starColor = "gold";
starColor = "gold"
} else if (starValue === fullStars + 1 && remainder > 0) {
const gradientColor = `linear-gradient(to right, gold ${
remainder * 100
}%, lightgray ${remainder * 100}%)`;
}%, lightgray ${remainder * 100}%)`
return (
<span
key={starValue}
Expand All @@ -108,7 +138,7 @@ const ReviewList = ({ hotelId, onReviewEdit }) => {
>
</span>
);
)
}

return (
Expand All @@ -122,16 +152,16 @@ const ReviewList = ({ hotelId, onReviewEdit }) => {
>
</span>
);
});
)
})

return (
<>
{stars}
{` (${rating})`}
</>
);
};
)
}

const handleDeleteReview = async (id) => {
try {
Expand All @@ -141,21 +171,21 @@ const ReviewList = ({ hotelId, onReviewEdit }) => {
...axios.defaults,
useAuth: true,
}
);
console.log("리뷰가 삭제되었습니다.");
fetchReviews();
toast.success("리뷰가 삭제되었습니다.");
)
console.log("리뷰가 삭제되었습니다.")
fetchReviews()
toast.success("리뷰가 삭제되었습니다.")
} catch (error) {
console.error("리뷰 삭제 중 오류 발생:", error);
toast.error("리뷰 삭제에 실패했습니다. 다시 시도해주세요.");
console.error("리뷰 삭제 중 오류 발생:", error)
toast.error("리뷰 삭제에 실패했습니다. 다시 시도해주세요.")
}
};
}

const handleEditReview = (id) => {
// 수정할 리뷰 ID를 상태에 저장하고 모달을 열기
setEditingReviewId(id);
setShowModal(true);
};
setEditingReviewId(id)
setShowModal(true)
}

const renderTopBox = () => {
return (
Expand Down Expand Up @@ -187,11 +217,11 @@ const ReviewList = ({ hotelId, onReviewEdit }) => {
</p>
</div>
</div>
);
};
)
}
const renderRecentReviewsGrid = () => {
if (!Array.isArray(recentReviews) || recentReviews.length === 0) {
return <p>등록된 리뷰가 없습니다.</p>;
return <p>등록된 리뷰가 없습니다.</p>
}

return (
Expand Down Expand Up @@ -265,14 +295,14 @@ const ReviewList = ({ hotelId, onReviewEdit }) => {
))}
</div>
</>
);
};
)
}

return (
<div style={{ textAlign: "left" }}>
<h2 className="text-xl font-semibold mb-4 mt-5">이용 후기</h2>
<div className="w-[40vw]">
<div className="border-t-2 border-gray-200 mt-4 pt-4"></div>
<h2 className='text-xl font-semibold mb-4 mt-5'>이용 후기</h2>
<div className='w-[40vw]'>
<div className='border-t-2 border-gray-200 mt-4 pt-4'></div>
</div>

{renderRecentReviewsGrid()}
Expand All @@ -295,7 +325,7 @@ const ReviewList = ({ hotelId, onReviewEdit }) => {
isOpen={showModal}
onOpenChange={handleCloseModal}
size={modalSize}
placement="auto"
placement='auto'
>
<ModalContent
style={{
Expand All @@ -314,10 +344,10 @@ const ReviewList = ({ hotelId, onReviewEdit }) => {
reviewId={editingReviewId}
onClose={() => {
// 수정 폼에서 닫기 버튼을 눌렀을 때 호출되는 함수
handleCloseModal();
handleCloseModal()
// 추가적인 처리가 필요하다면 onReviewEdit를 호출하여 처리할 수 있음
if (onReviewEdit) {
onReviewEdit();
onReviewEdit()
}
}}
/>
Expand All @@ -332,6 +362,23 @@ const ReviewList = ({ hotelId, onReviewEdit }) => {
전체 리뷰
</h4>
<ModalBody>
<div
style={{
margin: "10px",
fontSize: "15px",
}}
>
<label htmlFor='sortSelect'>정렬:</label>
<select
id='sortSelect'
value={sortBy}
onChange={handleSortChange}
>
<option value='recent'>최신순</option>
<option value='highRating'>높은 평점순</option>
<option value='lowRating'>낮은 평점순</option>
</select>
</div>
{Array.isArray(allReviews) && allReviews.length > 0 ? (
<ul
style={{
Expand Down Expand Up @@ -416,7 +463,7 @@ const ReviewList = ({ hotelId, onReviewEdit }) => {
<Button
onPress={() => {
if (onReviewEdit) {
onReviewEdit();
onReviewEdit()
}
}}
style={{ paddingRight: "15px" }}
Expand All @@ -431,7 +478,7 @@ const ReviewList = ({ hotelId, onReviewEdit }) => {
</ModalContent>
</Modal>
</div>
);
};
)
}

export default ReviewList;
export default ReviewList

0 comments on commit 68fb51d

Please sign in to comment.