buhapp-telegram/public/js/views/settings.js
root 21d281a1c6 Telegram WebApp: Mini App with map, chats, profile, reviews
- index.html with bottom tabbar (Map/Chats/Profile/Settings)
- Telegram WebApp SDK auth via /api/v1/auth/telegram
- Consent gate on first login (4 checkboxes)
- Map view with Leaflet (OSM) + nearby users + radius selector
- Chat list + single chat with WebSocket realtime
- Profile editing (drinks, activities, purposes)
- Review screen with stars + anonymous toggle
- User reviews list with stats
- User profile (public) with rating, write/block/report
- Legal pages (TERMS / PRIVACY / DISCLAIMER)
2026-08-20 17:43:16 +00:00

115 lines
5.5 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Settings view + consent gate + user profile (public)
const SettingsView = {
async render(root) {
const u = API.Store.user;
root.innerHTML = `
<div class="view">
<h1>⚙️ Настройки</h1>
<div class="card">
<div style="font-weight:600;">${escapeHtml(u?.name || '')}</div>
${u?.telegram_id ? `<div class="sub">Telegram ID: ${u.telegram_id}</div>` : ''}
${u?.email ? `<div class="sub">${escapeHtml(u.email)}</div>` : ''}
</div>
<div class="card">
<strong>Юридические документы</strong>
<div class="sub">
Версия 1.0 · 20.08.2026
</div>
<a class="btn ghost" href="/legal/TERMS.html" target="_blank" style="margin-top:8px;">📄 Пользовательское соглашение</a>
<a class="btn ghost" href="/legal/PRIVACY.html" target="_blank">🔒 Политика конфиденциальности</a>
<a class="btn ghost" href="/legal/DISCLAIMER.html" target="_blank">⚠️ Отказ от ответственности</a>
</div>
<button class="btn danger" id="logout">Выйти</button>
</div>
`;
document.getElementById('logout').onclick = async () => {
const ok = await TG_APP.showConfirm('Выйти из BuhApp?');
if (!ok) return;
API.Store.clear();
location.reload();
};
},
};
// Public user profile (включая рейтинг и кнопку "написать")
const UserProfileView = {
async render(root, params) {
const userId = params.userId;
const name = params.name || '';
root.innerHTML = '<div class="view"><div class="loader">Загрузка...</div></div>';
try {
const [profile, stats] = await Promise.all([
API.Auth.getUser(userId),
API.ReviewApi.stats(userId),
]);
root.innerHTML = `
<div class="view">
<button class="btn ghost" id="up-back" style="width:auto;margin-bottom:12px;">← Назад</button>
<div class="card">
<div style="display:flex;align-items:center;gap:16px;">
${profile.photo ? `<div class="avatar" style="width:80px;height:80px;background-image:url(${profile.photo});"></div>` :
`<div class="avatar" style="width:80px;height:80px;font-size:32px;">${escapeHtml((profile.name||'?')[0])}</div>`}
<div>
<div style="font-size:22px;font-weight:700;">${escapeHtml(profile.name || '')}</div>
${profile.city ? `<div class="sub">📍 ${escapeHtml(profile.city)}</div>` : ''}
${profile.age ? `<div class="sub">${profile.age} лет</div>` : ''}
</div>
</div>
<div style="display:flex;align-items:center;gap:12px;margin-top:12px;">
<div class="rating-big">${stats.rating_avg ? stats.rating_avg.toFixed(1) : '—'}</div>
<div>
<div class="stars" style="font-size:18px;">
${[1,2,3,4,5].map(n=>`<span class="${n<=Math.round(stats.rating_avg||0)?'on':''}">★</span>`).join('')}
</div>
<div class="sub">${stats.rating_count || 0} отзывов</div>
</div>
</div>
</div>
${profile.bio ? `<div class="card"><strong>О себе</strong><div style="margin-top:6px;">${escapeHtml(profile.bio)}</div></div>` : ''}
${profile.prefs ? this.renderPrefs(profile.prefs) : ''}
<button class="btn" id="up-chat">💬 Написать</button>
<button class="btn ghost" id="up-reviews">⭐ Все отзывы (${stats.rating_count || 0})</button>
<button class="btn danger" id="up-block">🚫 Заблокировать</button>
<button class="btn ghost" id="up-report">⚠️ Пожаловаться</button>
</div>
`;
document.getElementById('up-back').onclick = () => history.back();
document.getElementById('up-chat').onclick = async () => {
try {
const chat = await API.ChatApi.ensureChat(userId);
App.openChat(chat.id, userId, profile.name);
} catch (e) { TG_APP.showAlert('Ошибка: ' + e.message); }
};
document.getElementById('up-reviews').onclick = () => App.showView('user-reviews', { userId, userName: profile.name });
document.getElementById('up-block').onclick = async () => {
const ok = await TG_APP.showConfirm('Заблокировать ' + profile.name + '?');
if (!ok) return;
try { await API.ChatApi.block(userId); TG_APP.showAlert('Заблокировано'); history.back(); }
catch (e) { TG_APP.showAlert('Ошибка: ' + e.message); }
};
document.getElementById('up-report').onclick = async () => {
const reason = prompt('Причина жалобы:');
if (!reason) return;
try { await API.ChatApi.report('user', userId, reason); TG_APP.showAlert('Жалоба отправлена'); }
catch (e) { TG_APP.showAlert('Ошибка: ' + e.message); }
};
} catch (e) {
root.innerHTML = '<div class="view"><div class="empty">Ошибка: ' + e.message + '</div></div>';
}
},
renderPrefs(p) {
const blocks = [];
if (p.drinks?.length) {
blocks.push(`<div class="card"><strong>Предпочтения</strong><div style="margin-top:6px;">${p.drinks.map(d => `<span class="chip">${escapeHtml(d)}</span>`).join('')}</div></div>`);
}
return blocks.join('');
},
};
window.SettingsView = SettingsView;
window.UserProfileView = UserProfileView;