Новые изменения в проекте
This commit is contained in:
parent
ecb720f751
commit
ddc0a26635
13 changed files with 950 additions and 48 deletions
146
frontend/src/components/reviews/ReviewsList.astro
Normal file
146
frontend/src/components/reviews/ReviewsList.astro
Normal file
|
|
@ -0,0 +1,146 @@
|
|||
---
|
||||
import ReviewCard from './ReviewCard.astro';
|
||||
import VotingSummary from './VotingSummary.astro';
|
||||
|
||||
interface ReviewRecord {
|
||||
id: string;
|
||||
name: string;
|
||||
surname?: string;
|
||||
profession?: string;
|
||||
text: string;
|
||||
rating: number;
|
||||
status: string;
|
||||
votesCount: number;
|
||||
created: string;
|
||||
expand?: {
|
||||
user?: {
|
||||
id: string;
|
||||
name: string;
|
||||
firstName?: string;
|
||||
email: string;
|
||||
avatar?: string;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
const POCKETBASE_URL = import.meta.env.POCKETBASE_URL || 'http://127.0.0.1:8090';
|
||||
|
||||
interface Props {
|
||||
status?: string;
|
||||
}
|
||||
|
||||
const { status = 'published' } = Astro.props;
|
||||
|
||||
// Загрузка данных с сервера
|
||||
let reviews: ReviewRecord[] = [];
|
||||
let error: string | null = null;
|
||||
let averageRating = 0;
|
||||
let totalVotes = 0;
|
||||
let totalReviews = 0;
|
||||
let ratingDistribution: Record<number, number> = { 1: 0, 2: 0, 3: 0, 4: 0, 5: 0 };
|
||||
|
||||
try {
|
||||
const filter = `status = "${status}"`;
|
||||
const expand = 'user';
|
||||
const sort = '-created';
|
||||
|
||||
const response = await fetch(
|
||||
`${POCKETBASE_URL}/api/collections/reviews/records?expand=${expand}&filter=${encodeURIComponent(filter)}&sort=${sort}`
|
||||
);
|
||||
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
reviews = data.items || [];
|
||||
|
||||
// Расчёт статистики
|
||||
totalReviews = reviews.length;
|
||||
totalVotes = reviews.reduce((sum, r) => sum + (r.votesCount || 0), 0);
|
||||
const totalRating = reviews.reduce((sum, r) => sum + r.rating, 0);
|
||||
averageRating = totalReviews > 0 ? parseFloat((totalRating / totalReviews).toFixed(1)) : 0;
|
||||
|
||||
ratingDistribution = reviews.reduce((acc, r) => {
|
||||
acc[r.rating] = (acc[r.rating] || 0) + 1;
|
||||
return acc;
|
||||
}, {} as Record<number, number>);
|
||||
} else {
|
||||
error = 'Не удалось загрузить отзывы';
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('[ReviewsList] Error:', e);
|
||||
error = 'Ошибка загрузки отзывов';
|
||||
}
|
||||
|
||||
// Цвета для аватарок
|
||||
const colors = [
|
||||
'bg-blue-100 text-blue-600', 'bg-teal-100 text-teal-600', 'bg-orange-100 text-orange-600',
|
||||
'bg-pink-100 text-pink-600', 'bg-purple-100 text-purple-600', 'bg-indigo-100 text-indigo-600',
|
||||
'bg-green-100 text-green-600', 'bg-yellow-100 text-yellow-600', 'bg-red-100 text-red-600'
|
||||
];
|
||||
|
||||
const getAvatarInfo = (name: string) => {
|
||||
const initial = name.charAt(0).toUpperCase();
|
||||
const color = colors[initial.charCodeAt(0) % colors.length];
|
||||
return { initial, color };
|
||||
};
|
||||
---
|
||||
|
||||
{error && (
|
||||
<div class="text-center py-12 text-red-500">
|
||||
<p>{error}</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{!error && (
|
||||
<>
|
||||
{reviews.length > 0 && (
|
||||
<VotingSummary
|
||||
averageRating={averageRating}
|
||||
totalVotes={totalVotes}
|
||||
totalReviews={totalReviews}
|
||||
ratingDistribution={ratingDistribution}
|
||||
/>
|
||||
)}
|
||||
|
||||
<div class="reviews-grid">
|
||||
{reviews.length > 0 ? (
|
||||
reviews.map((review) => {
|
||||
const avatarInfo = getAvatarInfo(review.name);
|
||||
const fullName = `${review.name} ${review.surname || ''}`.trim();
|
||||
return (
|
||||
<ReviewCard
|
||||
name={fullName}
|
||||
profession={review.profession || 'Клиент'}
|
||||
text={review.text}
|
||||
rating={review.rating}
|
||||
initial={avatarInfo.initial}
|
||||
color={avatarInfo.color}
|
||||
date={review.created}
|
||||
votesCount={review.votesCount || 0}
|
||||
reviewId={review.id}
|
||||
/>
|
||||
);
|
||||
})
|
||||
) : (
|
||||
<div class="text-center py-12 text-gray-500" style="grid-column: 1 / -1;">
|
||||
<p>Пока нет отзывов. Будьте первым!</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
<style>
|
||||
.reviews-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(350px, 1fr));
|
||||
gap: 2rem;
|
||||
margin: 3rem 0;
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
.reviews-grid {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 1.5rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Loading…
Add table
Add a link
Reference in a new issue