// 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;