test commit

This commit is contained in:
Kirill
2026-05-19 11:25:23 +05:00
parent f8867f6457
commit 5adbe9baa7
81 changed files with 6549 additions and 3108 deletions
@@ -1,2 +1,3 @@
export { ReviewSection } from './ui/ReviewSection'
export { ReviewDialog } from './ui/ReviewDialog'
export { ProductReviewsList } from './ui/ProductReviewsList'
@@ -0,0 +1,106 @@
import Alert from '@mui/material/Alert'
import Box from '@mui/material/Box'
import Paper from '@mui/material/Paper'
import Rating from '@mui/material/Rating'
import Stack from '@mui/material/Stack'
import Typography from '@mui/material/Typography'
import { useQuery } from '@tanstack/react-query'
import { Star } from 'lucide-react'
import { fetchPublicProductReviews } from '@/entities/review/api/reviews-api'
import type { PublicProductReviewItem } from '@/entities/review/api/reviews-api'
import { reviewsCountRu } from '@/shared/lib/reviews-count-ru'
import { OptimizedImage } from '@/shared/ui/OptimizedImage'
import { RichTextMessageContent } from '@/shared/ui/RichTextMessageContent'
function ReviewItem({ rv }: { rv: PublicProductReviewItem }) {
const body = typeof rv.text === 'string' && rv.text.trim() ? rv.text.trim() : null
return (
<Paper variant="outlined" sx={{ p: 1.5, borderRadius: 2 }}>
<Stack spacing={0.75}>
<Stack direction={{ xs: 'column', sm: 'row' }} spacing={1} sx={{ justifyContent: 'space-between' }}>
<Typography sx={{ fontWeight: 700 }}>{rv.authorDisplay}</Typography>
<Typography variant="caption" color="text.secondary">
{new Date(rv.createdAt).toLocaleString('ru-RU')}
</Typography>
</Stack>
<Rating
value={rv.rating}
readOnly
size="small"
icon={<Star fontSize="inherit" />}
emptyIcon={<Star fontSize="inherit" />}
/>
{body ? (
<Box sx={{ color: 'text.secondary' }}>
<RichTextMessageContent value={body} tone="review" />
</Box>
) : (
<Typography variant="caption" color="text.secondary">
Без текстового комментария.
</Typography>
)}
{rv.imageUrl && (
<Box
sx={{
width: 140,
height: 140,
borderRadius: 1.5,
border: 1,
borderColor: 'divider',
overflow: 'hidden',
}}
>
<OptimizedImage
src={rv.imageUrl}
alt="Фото к отзыву"
widths={[320, 640]}
sizes="140px"
sx={{
width: '100%',
height: '100%',
objectFit: 'cover',
}}
/>
</Box>
)}
</Stack>
</Paper>
)
}
export function ProductReviewsList({ productId }: { productId: string }) {
const reviewsQuery = useQuery({
queryKey: ['products', 'public', productId, 'reviews', { page: 1, pageSize: 30 }],
queryFn: () => fetchPublicProductReviews(productId, { page: 1, pageSize: 30 }),
enabled: Boolean(productId),
})
if (reviewsQuery.isLoading) return <Typography color="text.secondary">Загрузка отзывов</Typography>
if (reviewsQuery.isError) return <Alert severity="warning">Не удалось загрузить отзывы.</Alert>
if (reviewsQuery.data && reviewsQuery.data.total === 0) {
return (
<Box sx={{ py: 3 }}>
<Typography variant="h6" color="text.secondary" sx={{ mb: 1 }}>
Отзывов пока нет
</Typography>
<Typography variant="body2" color="text.secondary" sx={{ maxWidth: 400 }}>
Будьте первым, кто оставит отзыв на этот товар. Ваше мнение поможет улучшить качество наших изделий.
</Typography>
</Box>
)
}
if (!reviewsQuery.data || reviewsQuery.data.items.length === 0) return null
return (
<Stack spacing={1.25}>
{reviewsQuery.data.items.map((rv) => (
<ReviewItem key={rv.id} rv={rv} />
))}
{reviewsQuery.data.total > reviewsQuery.data.items.length && (
<Typography variant="caption" color="text.secondary" sx={{ display: 'block', mt: 1 }}>
Всего {reviewsCountRu(reviewsQuery.data.total)} ниже показаны последние {reviewsQuery.data.items.length}.
</Typography>
)}
</Stack>
)
}