This commit is contained in:
hamid zarghami
2025-12-08 16:29:21 +03:30
parent f93c3cc7d0
commit f1058e46b3
4 changed files with 170 additions and 30 deletions
+66 -19
View File
@@ -123,7 +123,48 @@ function AboutPage() {
)
}
const formatDate = (dateString: string): string => {
const date = new Date(dateString);
return date.toLocaleDateString('fa-IR', {
year: 'numeric',
month: 'long',
day: 'numeric'
});
};
const secondTab = () => {
const reviews = (reviewsData?.data || []).filter(review => review.isApproved);
const sortedReviews = [...reviews].sort((a, b) => {
if (sorting === '0') {
// جدیدترین
return new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime();
} else {
// قدیمی ترین
return new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime();
}
});
const averageRating = reviews.length > 0
? reviews.reduce((sum, review) => sum + review.rating, 0) / reviews.length
: 0;
const ratingCounts = {
5: reviews.filter(r => r.rating === 5).length,
4: reviews.filter(r => r.rating === 4).length,
3: reviews.filter(r => r.rating === 3).length,
2: reviews.filter(r => r.rating === 2).length,
1: reviews.filter(r => r.rating === 1).length,
};
const totalReviews = reviews.length;
const ratingPercentages = {
5: totalReviews > 0 ? Math.round((ratingCounts[5] / totalReviews) * 100) : 0,
4: totalReviews > 0 ? Math.round((ratingCounts[4] / totalReviews) * 100) : 0,
3: totalReviews > 0 ? Math.round((ratingCounts[3] / totalReviews) * 100) : 0,
2: totalReviews > 0 ? Math.round((ratingCounts[2] / totalReviews) * 100) : 0,
1: totalReviews > 0 ? Math.round((ratingCounts[1] / totalReviews) * 100) : 0,
};
return (
<section aria-labelledby="reviews-title" className='py-4'>
@@ -131,14 +172,14 @@ function AboutPage() {
aria-label='امتیاز کاربران'
className="bg-container rounded-container shadow-container p-4 py-6 grid grid-cols-2 items-center">
<div className="text-center font-bold text-5xl">
4.1
{averageRating.toFixed(1)}
</div>
<div className="">
<RateBar className='mt-1' content='5' percentage='55' />
<RateBar className='mt-1' content='4' percentage='32' />
<RateBar className='mt-1' content='3' percentage='70' />
<RateBar className='mt-1' content='2' percentage='12' />
<RateBar className='mt-1' content='1' percentage='89' />
<div className="flex flex-col w-fit items-end">
<RateBar className='mt-1' content='5' percentage={String(ratingPercentages[5])} />
<RateBar className='mt-1' content='4' percentage={String(ratingPercentages[4])} />
<RateBar className='mt-1' content='3' percentage={String(ratingPercentages[3])} />
<RateBar className='mt-1' content='2' percentage={String(ratingPercentages[2])} />
<RateBar className='mt-1' content='1' percentage={String(ratingPercentages[1])} />
</div>
</section>
@@ -151,18 +192,24 @@ function AboutPage() {
</button>
</div>
<div className="">
<article>
<Comment
className='pt-8'
user='علیرضا عابدزاده'
rating={5.0}
date='18 آذر 1401'
text='لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ، و با استفاده از طراحان گرافیک است، چاپگرها و متون بلکه روزنامه و مجله در ستون و سطرآنچنان که لازم است'
tags={['پیتزا قارچ مخصوص', 'چیز برگر ژیوان']}
/>
</article>
{sortedReviews.length > 0 ? (
sortedReviews.map((review) => (
<article key={review.id}>
<Comment
className='pt-8'
user={review.user?.firstName + ' ' + review.user?.lastName}
rating={review.rating}
date={formatDate(review.createdAt)}
text={review.comment}
tags={[review.food.title]}
positivePoints={review.positivePoints}
negativePoints={review.negativePoints}
/>
</article>
))
) : (
<p className='text-sm2 text-center py-8 text-disabled-text'>هنوز نظری ثبت نشده است</p>
)}
</div>
</section>
<AnimatedBottomSheet title="مرتب کردن بر اساس" visible={sortingModal} inDelay={150} onClick={toggleSortingModal}>
@@ -1,12 +1,14 @@
import { api } from "@/config/axios";
import { AboutResponse } from "../types/Types";
import { AboutResponse, ReviewsResponse } from "../types/Types";
export const getAbout = async (name: string): Promise<AboutResponse> => {
const { data } = await api.get<AboutResponse>(`/public/restaurants/${name}`);
return data;
};
export const getReviews = async (name: string) => {
const { data } = await api.get(`/public/reviews/restuarant/${name}`);
export const getReviews = async (name: string): Promise<ReviewsResponse> => {
const { data } = await api.get<ReviewsResponse>(
`/public/reviews/restuarant/${name}`
);
return data;
};
@@ -29,4 +29,68 @@ export interface Restaurant {
vat: number;
}
export interface ReviewFood {
id: string;
createdAt: string;
updatedAt: string;
deletedAt: string | null;
restaurant: string;
category: string;
title: string;
desc: string | null;
content: string | null;
price: number;
points: number | null;
order: number | null;
prepareTime: number | null;
sat: boolean;
sun: boolean;
mon: boolean;
breakfast: boolean;
noon: boolean;
dinner: boolean;
stock: number;
stockDefault: number;
isActive: boolean;
images: string[];
inPlaceServe: boolean;
pickupServe: boolean;
rate: number;
discount: number;
isSpecialOffer: boolean;
}
export interface ReviewUser {
id: string;
createdAt: string;
updatedAt: string;
deletedAt: string | null;
firstName: string;
lastName: string;
birthDate: string;
marriageDate: string;
referrer: string | null;
isActive: boolean;
gender: boolean;
wallet: number;
points: number;
phone: string;
}
export interface Review {
id: string;
createdAt: string;
updatedAt: string;
deletedAt: string | null;
order: string;
food: ReviewFood;
user: ReviewUser;
comment: string;
rating: number;
positivePoints: string[];
negativePoints: string[];
isApproved: boolean;
}
export type AboutResponse = BaseResponse<Restaurant>;
export type ReviewsResponse = BaseResponse<Review[]>;
+35 -8
View File
@@ -2,6 +2,7 @@
import { Star1 } from 'iconsax-react'
import React from 'react'
import { useTranslation } from 'react-i18next';
type Props = {
user: string;
@@ -9,10 +10,19 @@ type Props = {
date: string;
text: string;
tags: Array<string>;
positivePoints?: string[];
negativePoints?: string[];
className?: string;
}
function Comment({ user, rating, date, text, tags, className = '' }: Props) {
function Comment({ user, rating, date, text, tags, positivePoints = [], negativePoints = [], className = '' }: Props) {
const { t } = useTranslation("rating");
const getTranslationKey = (point: string, isPositive: boolean): string => {
const basePath = isPositive ? 'Tabs.Strengths.Options' : 'Tabs.Weeknesses.Options';
return `${basePath}.${point}`;
};
return (
<div className={className}>
<div className="flex items-center justify-start">
@@ -29,13 +39,30 @@ function Comment({ user, rating, date, text, tags, className = '' }: Props) {
{text}
</p>
<ul className='mt-3 flex flex-wrap items-center justify-start gap-2'>
{tags.map((v, i) => (
<li key={i} className='text-xs py-1.5 px-3 bg-[#F2F2F2] dark:bg-neutral-700 dark:text-disabled-text rounded-sm text-center'>
{v}
</li>
))}
</ul>
{tags.length > 0 && (
<ul className='mt-3 flex flex-wrap items-center justify-start gap-2'>
{tags.map((v, i) => (
<li key={i} className='text-xs py-1.5 px-3 bg-[#F2F2F2] dark:bg-neutral-700 dark:text-disabled-text rounded-sm text-center'>
{v}
</li>
))}
</ul>
)}
{(positivePoints.length > 0 || negativePoints.length > 0) && (
<ul className='mt-5 flex flex-wrap items-center justify-start gap-2'>
{positivePoints.map((point, i) => (
<li key={`positive-${i}`} className='text-xs py-1.5 px-3 bg-green-100 dark:bg-green-900/30 text-green-700 dark:text-green-400 rounded-sm text-center'>
{t(getTranslationKey(point, true))}
</li>
))}
{negativePoints.map((point, i) => (
<li key={`negative-${i}`} className='text-xs py-1.5 px-3 bg-red-100 dark:bg-red-900/30 text-red-700 dark:text-red-400 rounded-sm text-center'>
{t(getTranslationKey(point, false))}
</li>
))}
</ul>
)}
</div>
)
}