// Review screens — create + user-reviews list const ReviewView = { rating: 5, chatId: null, otherId: null, otherName: null, async render(root, params) { this.chatId = params.chatId; this.otherId = params.otherId; this.otherName = params.otherName; this.rating = 5; root.innerHTML = `

⭐ Отзыв

О ${escapeHtml(this.otherName || 'пользователе')}
Ваша оценка
${[1,2,3,4,5].map(n => `★`).join('')}
5 из 5
`; // звёзды document.querySelectorAll('#stars .star').forEach(s => { s.onclick = () => { this.rating = parseInt(s.dataset.n, 10); document.querySelectorAll('#stars .star').forEach((x, i) => { x.classList.toggle('on', i < this.rating); }); const h = document.getElementById('r-rating-hint'); h.textContent = this.rating + ' из 5'; h.style.color = 'var(--success)'; TG_APP.haptic('selection'); }; }); // чекбокс как switch document.getElementById('r-anon-sw').onclick = () => { const sw = document.getElementById('r-anon-sw'); const cb = document.getElementById('r-anon'); const newVal = !cb.checked; cb.checked = newVal; sw.classList.toggle('on', newVal); }; // счётчик textarea document.getElementById('r-body').addEventListener('input', (e) => { const c = document.getElementById('r-counter'); const n = e.target.value.length; c.textContent = n + ' / 2000'; c.className = 'hint ' + (n > 1800 ? 'err' : 'mute'); }); document.getElementById('r-submit').onclick = () => this.submit(); }, async submit() { const btn = document.getElementById('r-submit'); const orig = btn.textContent; btn.className = 'btn-b load'; btn.textContent = '⏳ Отправляем…'; try { await API.ReviewApi.create( this.chatId, this.rating, document.getElementById('r-body').value.trim(), document.getElementById('r-anon').checked ); TG_APP.haptic('notification'); btn.className = 'btn-b ok'; btn.textContent = '✓ Готово!'; App._toast('Спасибо за отзыв!', 'ok'); setTimeout(() => { btn.className = 'btn-b'; btn.textContent = orig; App.showView('chats'); }, 1200); } catch (e) { btn.className = 'btn-b err'; btn.textContent = '⚠️ ' + (e.message || 'Ошибка'); App._toast('Не удалось отправить', 'err'); setTimeout(() => { btn.className = 'btn-b'; btn.textContent = orig; }, 2000); } }, }; const UserReviewsView = { async render(root, params) { const userId = params.userId; const userName = params.userName || 'Пользователь'; root.innerHTML = `

⭐ Отзывы о ${escapeHtml(userName)}

⏳ Загрузка…
`; try { const [stats, list] = await Promise.all([ API.ReviewApi.stats(userId), API.ReviewApi.listForUser(userId), ]); const avg = stats.rating_avg ? Number(stats.rating_avg).toFixed(1) : '—'; const header = document.getElementById('ur-header'); header.className = 'card-b'; header.innerHTML = `
${avg}
${[1,2,3,4,5].map(n => `★`).join('')}
${stats.rating_count || 0} отзывов
`; const box = document.getElementById('ur-list'); if (!list.reviews || list.reviews.length === 0) { box.innerHTML = '
Пока нет отзывов
'; } else { list.reviews.forEach((r) => { const card = document.createElement('div'); card.className = 'card-b'; card.style.padding = '14px'; card.innerHTML = `
${[1,2,3,4,5].map(n=>`★`).join('')}
${formatDate(r.created_at)}
${r.anonymous ? 'Анонимно' : 'ID: ' + (r.reviewer_id || '').slice(0,8)}
${r.body ? '
' + escapeHtml(r.body) + '
' : ''} `; box.appendChild(card); }); } } catch (e) { document.getElementById('ur-header').innerHTML = '
⚠️ Ошибка загрузки
'; App._toast('Не удалось загрузить отзывы', 'err'); } }, }; window.ReviewView = ReviewView; window.UserReviewsView = UserReviewsView;