// Bug Reporting & Community Feedback Chat System window.PvZFeedback = (function() { const STORAGE_KEY = 'pvz_cached_bugs'; let cachedReports = []; let currentFilter = 'all'; function getLocalFallback() { try { const saved = localStorage.getItem(STORAGE_KEY); if (saved) return JSON.parse(saved); } catch (e) {} return [ { id: 1, author: "Player_Kopri", category: "bug", text: "Зомби после поедания растения не шел дальше, а оставался стоять на месте.", status: "исправлено", timestamp: "2026-09-16 09:30", adminReply: "Исправлено в v1.6: сброшен флаг isEating и таймер поедания." }, { id: 2, author: "DaveFan", category: "bug", text: "Режим 'Я, Зомби' не работал: сразу после старта выводилось сообщение 'победа'.", status: "исправлено", timestamp: "2026-09-16 09:45", adminReply: "Исправлено в v1.6: устранено состояние гонки при инициализации газона." }, { id: 3, author: "ZombossHater", category: "visual", text: "Элементы главного меню и кнопки вываливаются за границы экрана на небольших дисплеях.", status: "исправлено", timestamp: "2026-09-16 10:50", adminReply: "Исправлено в v2.2: адаптивная панель навигации, компактные карточки и вертикальный скролл." } ]; } return { init: function() { cachedReports = getLocalFallback(); this.fetchReports(); }, getReports: function() { return cachedReports; }, fetchReports: async function(onDone) { try { const resp = await fetch('/api/bugs', { cache: 'no-cache' }); if (resp.ok) { const data = await resp.json(); if (Array.isArray(data)) { cachedReports = data; try { localStorage.setItem(STORAGE_KEY, JSON.stringify(data)); } catch (e) {} } } } catch (err) { console.warn('Could not fetch online bugs, using local cache:', err); if (cachedReports.length === 0) { cachedReports = getLocalFallback(); } } if (typeof onDone === 'function') onDone(cachedReports); this.renderFeed(currentFilter); }, sendReport: async function(author, category, text, onResult) { const payload = { author: (author || 'Гость').trim(), category: category || 'bug', text: (text || '').trim(), timestamp: new Date().toISOString() }; if (!payload.text) { if (onResult) onResult({ success: false, message: 'Введите описание проблемы' }); return; } try { const resp = await fetch('/api/bugs', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload) }); if (resp.ok) { const res = await resp.json(); await this.fetchReports(); if (onResult) onResult({ success: true, report: res.report }); return; } } catch (err) { console.warn('Failed to POST to server, storing locally:', err); } // Offline fallback: save into local list const newId = (cachedReports.reduce((m, r) => Math.max(m, r.id || 0), 0) || 0) + 1; const localReport = { id: newId, author: payload.author, category: payload.category, text: payload.text, status: 'новый', timestamp: new Date().toLocaleString('ru-RU', { hour: '2-digit', minute: '2-digit', day: '2-digit', month: '2-digit' }), adminReply: null }; cachedReports.push(localReport); try { localStorage.setItem(STORAGE_KEY, JSON.stringify(cachedReports)); } catch (e) {} this.renderFeed(currentFilter); if (onResult) onResult({ success: true, report: localReport, offline: true }); }, setFilter: function(filter) { currentFilter = filter; this.renderFeed(filter); }, renderFeed: function(filter = currentFilter) { currentFilter = filter; const feedEl = document.getElementById('bugreport-feed'); const countBadge = document.getElementById('bug-count-badge'); if (countBadge) { const total = cachedReports.length; const word = total % 10 === 1 && total % 100 !== 11 ? 'репорт' : (total % 10 >= 2 && total % 10 <= 4 && (total % 100 < 10 || total % 100 >= 20) ? 'репорта' : 'репортов'); countBadge.textContent = `${total} ${word}`; } if (!feedEl) return; let list = [...cachedReports]; if (filter === 'bug') list = list.filter(r => r.category === 'bug'); else if (filter === 'feature') list = list.filter(r => r.category === 'feature'); else if (filter === 'visual') list = list.filter(r => r.category === 'visual'); else if (filter === 'balance') list = list.filter(r => r.category === 'balance'); else if (filter === 'resolved') list = list.filter(r => r.status === 'исправлено'); if (list.length === 0) { feedEl.innerHTML = '