modified: main_dc/BB/bbvue/src/views/Reviews.vue
add reviews on reviews page begushiybashkir.ru site
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user