comments
This commit is contained in:
@@ -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[]>;
|
||||
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user