modified: main_dc/BB/bbvue/src/views/Reviews.vue

add reviews on reviews page begushiybashkir.ru site
This commit is contained in:
2025-11-19 05:14:49 +05:00
parent a574a7eac7
commit a8acfbbed3
+171 -31
View File
@@ -351,7 +351,91 @@ export default {
improvement: '',
trainings: 0
},
reviews: []
reviews: [],
// Захардкоженные отзывы как fallback
hardcodedReviews: [
{
id: 1,
author: {
first_name: 'Данил',
last_name: '',
id: 1
},
rating: 5,
text: 'В общем, краткие выводы и мысли от Бэкъярда: попробовать стоит, интересный опыт. Главное знать когда остановиться, чтобы потом не пришлось восстанавливаться долго. Подметил для себя 4 момента:\n- питаться надо привычной едой + соли различные, изотоник (регидрон), минералка, газировка. Горячее питание от организаторов хорошо заходило.\n- круг надо бегать в темпе 6:20-6:50, чтобы во время отдыха успевать немного восстановиться, но при этом сильно не остывать. Быстрее 06:00 если бежать, то можешь закислиться.\n- надо сразу психологию проработать, не ставить себе рамки и научиться не думать о дистанции и кругах, это реально выматывает.\n- саппорт (поддержка) нужная вещь, чтобы и массаж делать, и питание приготовить. Я был 16 кругов один, поэтому не всегда успевал нормально все делать.\n\nОтдельно хочу поблагодорить Загира, Сергея и Гаяза. Ваша помощь неоценима, пришли в самый тяжелый момент и как следует восстановили и смотивировали меня. Без вас планировал 15 (максимум 16) кругов сделать и сойти, так как мышцы уже реально "полнаелись". А так сделал 18 кругов и потенциально мог ещё 2 круга пробежать.\nЕщё хочу поблагодарить Марата, который оказывал психологическую поддержку.\nПарни, спасибо Вам большое.\n\nПриехал домой, умылся, чай попил, теперь восстанавливаться🤝',
achievement: '18 кругов на Бэкъярде',
distance: 'Ультрамарафон',
improvement: 'Преодоление психологических барьеров',
trainings: 50,
verified: true,
created_at: '2024-06-15T10:00:00Z'
},
{
id: 2,
author: {
first_name: 'Айгуль',
last_name: '',
id: 2
},
rating: 5,
text: 'У всех результаты, марафоны, цели, темпы….но а я просто любитель🤗\nВчера, как всегда, было весело, душевно, уютно, комфортно. Мой результат на 5 км для многих из вас - это просто пешком пройти😁 Даже если так, я очень довольна собой, что за день до марафона решилась идти, ведь я была в центре уфимских событий! Кстати, это была моя давняя мечта пробежать именно этот сентябрьский марафон✨ Но долгое отсутствие тренировок дало о себе знать, бежала тяжело(\nНезабываемые ощущения, эмоции от нашей точки поддержки - где мы уже по ту сторону стоим и поддерживаем бегунов - башкирская музыка, танцы, кумыс… и это круто!\nСпасибо тренеру, однокласснику, что он уговорил меня как-то прийти и попробовать в его клуб, долго откладывала это событие)\nСпасибо Всем вам, смотря на вас хочется лучше результаты и брать еще большие км, возможно когда-нибудь🙈🌝\nЗавершение вечера в MusicHall просто 🔥❤️',
achievement: 'Первый забег 5 км',
distance: '5 км',
improvement: 'Преодоление страха перед стартом',
trainings: 12,
verified: true,
created_at: '2024-09-10T14:30:00Z'
},
{
id: 3,
author: {
first_name: 'Гаяз',
last_name: 'Давлетбеков',
id: 3
},
rating: 5,
text: '🏃‍♂️🔥 «Белые Ночи – 3:34:33! Как Башкир сбросил час и покорил Питер» 🔥🏃‍♂️\n\n21 ноября 2024 я написал Загиру: «Хочу из 4х».\n5 июля 2025 я финишировал с 3:34:33 — и до сих пор не верю!\n\nЭто не просто личный рекорд. Это революция.\nС 4:45 на Московском марафоне до 3:34 в Петербурге – за 8 месяцев.\nМинус 71 минута – вот что значит правильный тренер, команда и… бурек!\n\n---\n\nКто помогал лететь:\n✊ Загир – тренер, который верил, даже когда я сам сомневался.\n💪 Сергей – друг-соперник, который не давал расслабляться (спасибо за «гонку» на тренировках!).\n👊 Вся семья «Бегущего Башкира» – без вас этот результат был бы просто цифрой.\n\n-Секреты успеха:\n✅ Образ «Бегущего Башкира»– бүрек + фирменная футболка = мощнейшая поддержка трибун. Казахи кричали «Жарайсың!», русские – «Давай, Башкир!», а я просто бежал и чувствовал силу предков которые передовались от их слов.\n✅ Тактика: Первые 30 км – как часы, последние 12 – как воин.\n✅ Питер – бежал по Дворцовому мосту, дышал невским ветром и ловил взгляды на стрелке Васильевского.\n\nФинишные эмоции:\nКогда увидел часы с 3:34 – понял: «Это не сон».\nЗагир сказал: «Ты мог бы и 3:30» – но я знаю, это только начало.\n\nСледующая цель? Может, Казань или Москва – но точно с бүреком на голове!\n\n### Благодарности:\n📣 Зрителям – ваши крики «Башкир, давай!» грели даже под питерским дождём.\n🏙️ Городу – ты сказочный, и твои мосты стали лучшими «подъёмами» на дистанции.\n🏆 Себе – за то, что не сдался, когда было тяжело.\n\nP.S. Теперь я знаю: «Невозможное» – это просто слово. Главное – найти свою команду и свой образ.\n\n*(Прикрепляю фото с финиша – лицо счастья и бүрек, переживший все 42 км!)* 😍🎽\n\n---\nP.P.S. Загир, готовь новый план – теперь мы летим за 3:20! 🚀',
achievement: 'Марафон 3:34:33',
distance: '42.2 км',
improvement: 'Улучшение на 71 минуту',
trainings: 120,
verified: true,
created_at: '2025-07-05T18:15:00Z'
},
{
id: 4,
author: {
first_name: 'Камила',
last_name: '',
id: 4
},
rating: 5,
text: 'Ғаяз, Айгөл, Ринат ҙуууур рәхмәт шундай байрам өсөн! Өҫтәл мул булды 👍🔥шулай ҡыҙыҡ тыуған көндәрегеҙ бер ваҡытта, тигәндәй! Ә хәҙер бер командалаһығыҙ! Гел шулай сағыу, мул, дәртле тормош алып барығыҙ! Һеҙ - Афариндар!!! 💪🤝🔥😎🔥',
achievement: 'Активная участница мероприятий',
distance: '5 км',
improvement: 'Участие в командных забегах',
trainings: 25,
verified: true,
created_at: '2024-08-20T09:45:00Z'
},
{
id: 5,
author: {
first_name: 'Зарема',
last_name: '',
id: 5
},
rating: 4,
text: 'Мне тоже очень понравилось!\nИ азарт появился\nНа след год тоже надо участвовать обязательно.\n\nКлассное окружение, все заряженные.\nСпасибо всем за эту атмосферу!\nИ благодарность тренеру, конечно ❤️\n\nСпасибо большое за угощения вкусные именинникам.\nВсех еще раз поздравляю 🥳',
achievement: 'Участница клубных мероприятий',
distance: '10 км',
improvement: 'Регулярное участие в забегах',
trainings: 30,
verified: true,
created_at: '2024-07-12T16:20:00Z'
}
],
useHardcodedData: false // Флаг для использования захардкоженных данных
}
},
computed: {
@@ -381,9 +465,9 @@ export default {
},
showContent() {
this.isContentVisible = true
// Эмитим событие для показа хедера
this.$emit('show-header')
},
async loadReviews() {
this.loading = true
try {
@@ -402,13 +486,23 @@ export default {
})
const response = await apiClient.get('/reviews', { params })
this.reviews = response.data.reviews || []
this.totalPages = response.data.total_pages || 1
this.totalItems = response.data.total_items || 0
this.currentPage = response.data.current_page || 1
if (response.data.reviews && response.data.reviews.length > 0) {
// Используем данные из API
this.reviews = response.data.reviews || []
this.totalPages = response.data.total_pages || 1
this.totalItems = response.data.total_items || 0
this.currentPage = response.data.current_page || 1
this.useHardcodedData = false
} else {
// Если API вернуло пустой ответ, используем захардкоженные данные
this.useHardcodedFallback('reviews')
}
} catch (error) {
console.error('Ошибка при загрузке отзывов:', error)
this.showNotification('error', 'Ошибка', 'Не удалось загрузить отзывы')
// При ошибке используем захардкоженные данные
this.useHardcodedFallback('reviews')
this.showNotification('error', 'Ошибка', 'Не удалось загрузить отзывы. Показаны демо-данные.')
} finally {
this.loading = false
}
@@ -417,17 +511,72 @@ export default {
async loadStats() {
try {
const response = await apiClient.get('/reviews/stats')
this.stats = response.data
if (response.data) {
this.stats = response.data
this.useHardcodedData = false
} else {
this.useHardcodedFallback('stats')
}
} catch (error) {
console.error('Ошибка при загрузке статистики:', error)
// Устанавливаем значения по умолчанию при ошибке
this.stats = {
total_reviews: 0,
average_rating: 0,
success_stories: 0,
rating_distribution: {5: 0, 4: 0, 3: 0, 2: 0, 1: 0}
// Используем захардкоженную статистику при ошибке
this.useHardcodedFallback('stats')
}
},
useHardcodedFallback(type) {
this.useHardcodedData = true
if (type === 'reviews') {
let filteredReviews = [...this.hardcodedReviews];
// Применяем фильтрацию по рейтингу
if (this.activeRatingFilter !== 'all') {
const minRating = parseInt(this.activeRatingFilter);
filteredReviews = filteredReviews.filter(review => review.rating >= minRating);
}
// Не показываем ошибку пользователю для статистики
// Применяем сортировку
switch (this.sortBy) {
case 'newest':
filteredReviews.sort((a, b) => new Date(b.created_at) - new Date(a.created_at));
break;
case 'oldest':
filteredReviews.sort((a, b) => new Date(a.created_at) - new Date(b.created_at));
break;
case 'highest':
filteredReviews.sort((a, b) => b.rating - a.rating);
break;
case 'lowest':
filteredReviews.sort((a, b) => a.rating - b.rating);
break;
}
// Пагинация
const startIndex = (this.currentPage - 1) * this.reviewsPerPage;
const endIndex = startIndex + this.reviewsPerPage;
this.reviews = filteredReviews.slice(startIndex, endIndex);
this.totalItems = filteredReviews.length;
this.totalPages = Math.ceil(filteredReviews.length / this.reviewsPerPage);
} else if (type === 'stats') {
// Рассчитываем статистику на основе захардкоженных отзывов
const totalReviews = this.hardcodedReviews.length;
const averageRating = this.hardcodedReviews.reduce((sum, review) => sum + review.rating, 0) / totalReviews;
const successStories = this.hardcodedReviews.filter(review => review.achievement).length;
const ratingDistribution = {5: 0, 4: 0, 3: 0, 2: 0, 1: 0};
this.hardcodedReviews.forEach(review => {
ratingDistribution[review.rating]++;
});
this.stats = {
total_reviews: totalReviews,
average_rating: Math.round(averageRating * 10) / 10,
success_stories: successStories,
rating_distribution: ratingDistribution
};
}
},
@@ -436,7 +585,6 @@ export default {
const token = localStorage.getItem('auth_token')
if (token) {
try {
// Пытаемся получить профиль пользователя для проверки авторизации
const response = await apiClient.get('/user/profile')
this.isAuthenticated = true
this.currentUser = response.data
@@ -458,7 +606,6 @@ export default {
this.isAuthenticated = false
this.currentUser = null
localStorage.removeItem('auth_token')
// НЕ перенаправляем на login, чтобы пользователь мог просматривать отзывы
},
getInitials(author) {
@@ -580,7 +727,6 @@ export default {
trainings: review.trainings || 0
}
// Прокручиваем к форме
document.getElementById('add-review').scrollIntoView({
behavior: 'smooth'
})
@@ -629,7 +775,6 @@ export default {
},
showNotification(type, title, text) {
// Используем существующую систему уведомлений или создаем простую альтернативу
if (this.$notify) {
this.$notify({
type: type,
@@ -637,7 +782,6 @@ export default {
text: text
})
} else {
// Простая альтернатива, если $notify не доступен
const notification = document.createElement('div')
notification.className = `notification notification-${type}`
notification.innerHTML = `
@@ -668,7 +812,6 @@ export default {
this.loadStats()
},
beforeUnmount() {
// Убираем обработчики при размонтировании
window.removeEventListener('scroll', this.handleFirstInteraction)
window.removeEventListener('click', this.handleFirstInteraction)
window.removeEventListener('touchstart', this.handleFirstInteraction)
@@ -688,8 +831,6 @@ export default {
</script>
<style scoped>
/* Стили остаются без изменений, как в вашем исходном файле */
/* Добавляем только стили для уведомлений */
.notification {
position: fixed;
@@ -1336,7 +1477,7 @@ export default {
}
.review-form {
background: #f8fff8;
background: #f5fdf5;
padding: 2rem;
border-radius: 15px;
border: 2px solid #e9ecef;
@@ -1819,29 +1960,28 @@ textarea:focus-visible {
/* Темная тема */
@media (prefers-color-scheme: dark) {
.reviews-page {
background: linear-gradient(135deg, #1a1a1a 0%, #2d2d2d 100%);
background: linear-gradient(135deg, #ffffff 0%, #85ff60 100%);
}
.review-card {
background: #2d2d2d;
color: #ffffff;
background: #bababa;
color: #6e6e6e;
}
.review-text {
color: #cccccc;
color: #6d6d6d;
}
.form-group input,
.form-group select,
.form-group textarea {
background: #3d3d3d;
background: #767676;
border-color: #555;
color: white;
}
.review-form {
background: #2d2d2d;
border-color: #555;
background: #b8c9ba;
}
}