Fix: UserProfileView для тапа на маркер человека
Проблема: app.js вызывал UserProfileView.render при showView('user-profile'),
но view был не создан. ReferenceError.
Создал UserProfileView в profile.js:
- Читает /users/:id + /users/:id/stats
- Card с avatar-b.lg, name, city, ★ rating (если есть отзывы)
- bio
- Кнопки: 'Написать' (ensureChat → openChat) + 'Все отзывы' (UserReviewsView)
- Error/toast на любой сбой
API: добавил UserApi.get|reviews|stats namespace.
This commit is contained in:
parent
c5f8ac67ad
commit
f6fa3fedab
@ -8,7 +8,7 @@
|
||||
<script src="https://telegram.org/js/telegram-web-app.js"></script>
|
||||
<link rel="stylesheet" href="https://unpkg.com/maplibre-gl@4.7.1/dist/maplibre-gl.css">
|
||||
<script src="https://unpkg.com/maplibre-gl@4.7.1/dist/maplibre-gl.js"></script>
|
||||
<link rel="stylesheet" href="/css/app.css?v=24">
|
||||
<link rel="stylesheet" href="/css/app.css?v=25">
|
||||
</head>
|
||||
<body>
|
||||
<button id="mode-toggle" title="Переключить режим">📱 Мобильный</button>
|
||||
@ -34,15 +34,15 @@
|
||||
</button>
|
||||
</nav>
|
||||
</div>
|
||||
<script src="/js/tg.js?v=24"></script>
|
||||
<script src="/js/api.js?v=24"></script>
|
||||
<script src="/js/views/map.js?v=24"></script>
|
||||
<script src="/js/views/chats.js?v=24"></script>
|
||||
<script src="/js/views/chat.js?v=24"></script>
|
||||
<script src="/js/views/profile.js?v=24"></script>
|
||||
<script src="/js/views/settings.js?v=24"></script>
|
||||
<script src="/js/views/onboarding.js?v=24"></script>
|
||||
<script src="/js/views/review.js?v=24"></script>
|
||||
<script src="/js/app.js?v=24"></script>
|
||||
<script src="/js/tg.js?v=25"></script>
|
||||
<script src="/js/api.js?v=25"></script>
|
||||
<script src="/js/views/map.js?v=25"></script>
|
||||
<script src="/js/views/chats.js?v=25"></script>
|
||||
<script src="/js/views/chat.js?v=25"></script>
|
||||
<script src="/js/views/profile.js?v=25"></script>
|
||||
<script src="/js/views/settings.js?v=25"></script>
|
||||
<script src="/js/views/onboarding.js?v=25"></script>
|
||||
<script src="/js/views/review.js?v=25"></script>
|
||||
<script src="/js/app.js?v=25"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@ -103,6 +103,13 @@ const Auth = {
|
||||
getUser: function (id) { return apiGet('/api/v1/users/' + id); },
|
||||
};
|
||||
|
||||
// === API namespaces ===
|
||||
const UserApi = {
|
||||
get: function (userId) { return apiGet('/api/v1/users/' + userId); },
|
||||
reviews: function (userId) { return apiGet('/api/v1/users/' + userId + '/reviews'); },
|
||||
stats: function (userId) { return apiGet('/api/v1/users/' + userId + '/stats'); },
|
||||
};
|
||||
|
||||
const ChatApi = {
|
||||
ensureChat: function (otherId) { return apiPost('/api/v1/chats', { other_id: otherId }); },
|
||||
listChats: function () { return apiGet('/api/v1/chats'); },
|
||||
|
||||
@ -199,3 +199,90 @@ const ProfileView = {
|
||||
};
|
||||
|
||||
window.ProfileView = ProfileView;
|
||||
|
||||
// UserProfileView — чужой профиль (read-only) — открывается по тапу на маркер на карте
|
||||
const UserProfileView = {
|
||||
userId: null,
|
||||
userName: null,
|
||||
data: null,
|
||||
|
||||
async render(root, params) {
|
||||
this.userId = params.userId;
|
||||
this.userName = params.name || 'Пользователь';
|
||||
root.innerHTML = `
|
||||
<div class="view">
|
||||
<h1 style="margin:0 0 14px;font-size:22px;">👤 ${escapeHtml(this.userName)}</h1>
|
||||
<div id="up-content">${'<div class="empty-b">⏳ Загружаю…</div>'}</div>
|
||||
</div>
|
||||
`;
|
||||
try {
|
||||
const [user, stats] = await Promise.all([
|
||||
API.UserApi.get(this.userId),
|
||||
API.ReviewApi.stats(this.userId).catch(() => ({})),
|
||||
]);
|
||||
this.data = user.user || user;
|
||||
const u = this.data;
|
||||
const init = ((u.name || u.email || '?')[0] || '?').toUpperCase();
|
||||
const rating = stats.rating_avg ? Number(stats.rating_avg).toFixed(1) : '—';
|
||||
const reviewsCount = stats.rating_count || 0;
|
||||
|
||||
document.getElementById('up-content').innerHTML = `
|
||||
<!-- Аватар и имя -->
|
||||
<div class="card-b" style="display:flex;align-items:center;gap:14px;">
|
||||
<div class="avatar-b lg" style="${u.photo_url ? `background-image:url(${u.photo_url});background-size:cover;color:transparent;` : ''}">${u.photo_url ? '' : init}</div>
|
||||
<div style="flex:1;">
|
||||
<div style="font-weight:700;font-size:18px;">${escapeHtml(u.name || '')}</div>
|
||||
${u.city ? `<div style="font-size:13px;color:var(--text-dim);">📍 ${escapeHtml(u.city)}</div>` : ''}
|
||||
${reviewsCount > 0 ? `<div style="font-size:13px;color:var(--warn);margin-top:4px;">★ ${rating} · ${reviewsCount} ${pluralRu(reviewsCount, 'отзыв', 'отзыва', 'отзывов')}</div>` : ''}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- О себе -->
|
||||
${u.bio ? `
|
||||
<div class="card-b" style="margin-top:12px;">
|
||||
<div style="font-size:12px;font-weight:600;color:var(--text-dim);margin-bottom:6px;">О себе</div>
|
||||
<div style="font-size:14px;line-height:1.4;">${escapeHtml(u.bio)}</div>
|
||||
</div>
|
||||
` : ''}
|
||||
|
||||
<!-- Действия -->
|
||||
<div class="row-list" style="margin-top:14px;">
|
||||
<button class="btn-b" id="up-message">💬 Написать</button>
|
||||
${reviewsCount > 0 ? `<button class="btn-b ghost" id="up-reviews">⭐ Все отзывы</button>` : ''}
|
||||
</div>
|
||||
`;
|
||||
|
||||
const msgBtn = document.getElementById('up-message');
|
||||
if (msgBtn) msgBtn.onclick = async () => {
|
||||
msgBtn.className = 'btn-b load';
|
||||
msgBtn.textContent = '⏳ Открываем…';
|
||||
try {
|
||||
const r = await API.ChatApi.ensureChat(this.userId);
|
||||
const cid = r.chat_id || r.chat?.id;
|
||||
if (!cid) throw new Error('chat not created');
|
||||
App.openChat(cid, this.userId, u.name);
|
||||
} catch (e) {
|
||||
msgBtn.className = 'btn-b err';
|
||||
msgBtn.textContent = '⚠️ ' + (e.message || 'Ошибка');
|
||||
App._toast('Не удалось открыть чат', 'err');
|
||||
setTimeout(() => { msgBtn.className = 'btn-b'; msgBtn.textContent = '💬 Написать'; }, 2000);
|
||||
}
|
||||
};
|
||||
|
||||
const revBtn = document.getElementById('up-reviews');
|
||||
if (revBtn) revBtn.onclick = () => UserReviewsView.render(root, { userId: this.userId, userName: u.name });
|
||||
} catch (e) {
|
||||
document.getElementById('up-content').innerHTML = `<div class="empty-b">⚠️ Не удалось загрузить</div>`;
|
||||
App._toast('Ошибка загрузки профиля', 'err');
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
window.UserProfileView = UserProfileView;
|
||||
|
||||
function pluralRu(n, one, few, many) {
|
||||
const mod10 = n % 10, mod100 = n % 100;
|
||||
if (mod10 === 1 && mod100 !== 11) return one;
|
||||
if (mod10 >= 2 && mod10 <= 4 && (mod100 < 10 || mod100 >= 20)) return few;
|
||||
return many;
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user