diff --git a/main_dc/BB/bbvue/src/views/Reviews.vue b/main_dc/BB/bbvue/src/views/Reviews.vue index e2ed58a..d9e6274 100644 --- a/main_dc/BB/bbvue/src/views/Reviews.vue +++ b/main_dc/BB/bbvue/src/views/Reviews.vue @@ -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 {