From f8bddfb42877bff2a7cd04956c967bc357ebaf8e Mon Sep 17 00:00:00 2001 From: hamid zarghami Date: Wed, 15 Jul 2026 12:30:13 +0330 Subject: [PATCH] public review --- .../[id]/components/FoodReviewsSection.tsx | 243 ++++++++++++++++++ .../Enum.ts => [id]/enum/ReviewPoints.ts} | 0 .../[name]/(Main)/[id]/hooks/useFoodData.ts | 22 +- src/app/[name]/(Main)/[id]/page.tsx | 19 +- src/app/[name]/(Main)/[id]/service/Service.ts | 11 + .../[name]/(Main)/[id]/types/ReviewTypes.ts | 12 + src/app/[name]/(Main)/order/history/page.tsx | 6 - .../(Main)/order/rate/[orderId]/page.tsx | 224 ---------------- .../(Main)/order/rate/hooks/useRateData.ts | 8 - .../(Main)/order/rate/service/RateService.ts | 7 - .../[name]/(Main)/order/rate/types/Types.ts | 8 - src/config/const.ts | 2 +- 12 files changed, 301 insertions(+), 261 deletions(-) create mode 100644 src/app/[name]/(Main)/[id]/components/FoodReviewsSection.tsx rename src/app/[name]/(Main)/{order/rate/enum/Enum.ts => [id]/enum/ReviewPoints.ts} (100%) create mode 100644 src/app/[name]/(Main)/[id]/types/ReviewTypes.ts delete mode 100644 src/app/[name]/(Main)/order/rate/[orderId]/page.tsx delete mode 100644 src/app/[name]/(Main)/order/rate/hooks/useRateData.ts delete mode 100644 src/app/[name]/(Main)/order/rate/service/RateService.ts delete mode 100644 src/app/[name]/(Main)/order/rate/types/Types.ts diff --git a/src/app/[name]/(Main)/[id]/components/FoodReviewsSection.tsx b/src/app/[name]/(Main)/[id]/components/FoodReviewsSection.tsx new file mode 100644 index 0000000..3629961 --- /dev/null +++ b/src/app/[name]/(Main)/[id]/components/FoodReviewsSection.tsx @@ -0,0 +1,243 @@ +"use client"; + +import ToggleButton, { ToggleButtonClassNames } from "@/components/button/ToggleButton"; +import TabContainer, { TabContainerClassNames, TabContainerRenderType } from "@/components/tab/TabContainer"; +import { TabHeader } from "@/components/tab/TabHeader"; +import { toast, toastLoginRequired } from "@/components/Toast"; +import Comment from "@/components/utils/Comment"; +import RateSelectionBar from "@/components/utils/RateSelectionBar"; +import { Button } from "@/components/ui/button"; +import { getToken } from "@/lib/api/func"; +import { extractErrorMessage } from "@/lib/func"; +import { glassSurfaceFlat } from "@/lib/styles/glassSurface"; +import clsx from "clsx"; +import { useParams, useRouter } from "next/navigation"; +import React, { useState } from "react"; +import { useTranslation } from "react-i18next"; +import { useGetProfile } from "../../../(Profile)/profile/hooks/userProfileData"; +import { NegativePointEnum, PositivePointEnum } from "../enum/ReviewPoints"; +import { useCreateReview, useGetFoodReviews } from "../hooks/useFoodData"; +import { CreateReviewType } from "../types/ReviewTypes"; + +type Props = { + foodId: string; +}; + +const positivePointsArray = [ + PositivePointEnum.GREAT_TASTE, + PositivePointEnum.FAST_DELIVERY, + PositivePointEnum.GOOD_QUALITY, + PositivePointEnum.GOOD_PORTION_SIZE, + PositivePointEnum.FRIENDLY_SERVICE, +]; + +const negativePointsArray = [ + NegativePointEnum.SMALL_PORTION, + NegativePointEnum.SLOW_DELIVERY, + NegativePointEnum.POOR_QUALITY, + NegativePointEnum.OVERPRICED, + NegativePointEnum.UNFRIENDLY_SERVICE, +]; + +function FoodReviewsSection({ foodId }: Props) { + const { t } = useTranslation("rating"); + const { name } = useParams(); + const router = useRouter(); + const { isSuccess } = useGetProfile(); + const { data: reviewsData, isLoading: reviewsLoading } = useGetFoodReviews(foodId); + const { mutate: createReview, isPending } = useCreateReview(foodId); + + const [rating, setRating] = useState(5); + const [comment, setComment] = useState(""); + const [positivePoints, setPositivePoints] = useState([]); + const [negativePoints, setNegativePoints] = useState([]); + const [formKey, setFormKey] = useState(0); + + const reviews = (reviewsData?.data || []).filter((review) => review.isApproved); + const sortedReviews = [...reviews].sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()); + + const getTranslationKey = (point: string, isPositive: boolean): string => { + const basePath = isPositive ? "Tabs.Strengths.Options" : "Tabs.Weeknesses.Options"; + return `${basePath}.${point}`; + }; + + const handleToggle = (pointValue: string, isPositive: boolean) => (checked: boolean) => { + const setter = isPositive ? setPositivePoints : setNegativePoints; + + if (checked) { + setter((prev) => [...prev, pointValue]); + } else { + setter((prev) => prev.filter((p) => p !== pointValue)); + } + }; + + const handleRatingChange = (value: number[]) => { + setRating(value[0]); + }; + + const resetForm = () => { + setRating(5); + setComment(""); + setPositivePoints([]); + setNegativePoints([]); + setFormKey((prev) => prev + 1); + }; + + const formatDate = (dateString: string) => { + const date = new Date(dateString); + return date.toLocaleDateString("fa-IR", { + year: "numeric", + month: "long", + day: "numeric", + }); + }; + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + + const token = await getToken(); + if (!token || !isSuccess) { + toastLoginRequired(() => router.push(`/${name}/auth`), "error"); + return; + } + + if (rating === 0) { + toast(t("Errors.RatingRequired") || "لطفا امتیاز خود را انتخاب کنید", "error"); + return; + } + + const reviewData: CreateReviewType = { + foodId, + rating, + comment, + positivePoints, + negativePoints, + }; + + createReview(reviewData, { + onSuccess: () => { + toast(t("Success") || "نظر شما با موفقیت ثبت شد", "success"); + resetForm(); + }, + onError: (error) => { + toast(extractErrorMessage(error, t("Errors.SubmitFailed") || "خطا در ثبت نظر"), "error"); + }, + }); + }; + + const negativePointsView = ( +
+ {negativePointsArray.map((point) => ( + + {t(getTranslationKey(point, false))} + + ))} +
+ ); + + const positivePointsView = ( +
+ {positivePointsArray.map((point) => ( + + {t(getTranslationKey(point, true))} + + ))} +
+ ); + + return ( +
+

ثبت نظر

+ +
+
+ + +
+ + + + + +
+ +