ДИЗАЙН-СИСТЕМА в app.css: - --primary #FF6B35, --success #3dd68c, --danger #ff5c5c - --bg #0f0f12, --bg-elev #1a1a22, --bg-input #121218 - Компоненты: .card-b, .row-b, .field-b, .btn-b, .switch-b, .chip, .avatar-b, .bubble-b, .composer-b, .toast-b, .stars-b, .pulse-b, .empty-b ПЕРЕПИСАНЫ на единый стиль: Settings: - Карточка с аватаром пользователя - Card-row для юр.документов (📄 / 🔒 / ⚠️) - 'Выйти' — danger btn-b с state machine (idle→load→ok) Profile: - Карточка аватара + кнопка 'Загрузить фото' через multipart /api/v1/me/photo - Поля: field-b (ctrl + hint) - Чипы в chip-list, новый стиль с .on - Счётчик bio символов - 'Сохранить' со state machine + toast Review (создание): - Звёзды в stars-b с center hint - textarea с counter - switch-b 'Анонимно' (вместо checkbox) - Submit со state machine + toast 'Спасибо за отзыв!' UserReviews: - Средний рейтинг + звёзды - Список карточек с отзывами Chats: - Список с avatar-b + last message + время + badge непрочитанных - Пустое состояние с эмодзи - formatChatTime() — 'сейчас' / '5м' / '3ч' / '2д' Chat (один): - Header с аватаром и кнопками 👤 ⭐ - composer-b: 📎 + input + ➤ (sticky bottom) - bubble-b .in / .out с .ticks.read - Send со state + toast - sendPhoto() для фоток в чате App.js _toast(msg, kind): - Единый toast-b с классами ok / err Всё в одном стиле. CSS variables, padding 16, radius 14, no disabled кнопок.
64 lines
3.2 KiB
JavaScript
64 lines
3.2 KiB
JavaScript
// Chats list — единый стиль BuhApp
|
||
const ChatsView = {
|
||
async render(root) {
|
||
root.innerHTML = `
|
||
<div class="view">
|
||
<h1 style="margin:0 0 14px;font-size:22px;">💬 Чаты</h1>
|
||
<div id="chats-list" class="empty-b">⏳ Загрузка…</div>
|
||
</div>
|
||
`;
|
||
try {
|
||
const r = await API.ChatApi.listChats();
|
||
const list = document.getElementById('chats-list');
|
||
if (!r.chats || r.chats.length === 0) {
|
||
list.innerHTML = '<div class="empty-b"><div class="em">💬</div>Пока нет чатов<br><small>Открой карту и напиши кому-нибудь!</small></div>';
|
||
return;
|
||
}
|
||
list.className = 'row-list';
|
||
list.innerHTML = '';
|
||
r.chats.forEach((item) => {
|
||
const row = document.createElement('div');
|
||
row.className = 'card-b row-list-item';
|
||
row.style.padding = '12px';
|
||
row.style.cursor = 'pointer';
|
||
row.style.display = 'flex';
|
||
row.style.gap = '12px';
|
||
row.style.alignItems = 'center';
|
||
const init = (item.other_name || '?')[0].toUpperCase();
|
||
const lastText = item.last_message ? escapeHtml(item.last_message) : '<em style="color:var(--text-mute);">Нет сообщений</em>';
|
||
row.innerHTML = `
|
||
<div class="avatar-b" style="${item.other_photo ? `background-image:url(${item.other_photo});background-size:cover;color:transparent;` : ''}">${item.other_photo ? '' : init}</div>
|
||
<div style="flex:1;min-width:0;">
|
||
<div style="display:flex;justify-content:space-between;align-items:center;">
|
||
<div style="font-weight:600;font-size:15px;">${escapeHtml(item.other_name || 'Без имени')}</div>
|
||
${item.last_ts ? `<div style="font-size:11px;color:var(--text-mute);">${formatChatTime(item.last_ts)}</div>` : ''}
|
||
</div>
|
||
<div style="font-size:13px;color:var(--text-dim);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;margin-top:2px;">${lastText}</div>
|
||
</div>
|
||
${item.unread_count > 0 ? `<div style="background:var(--primary);color:#fff;font-size:11px;font-weight:700;min-width:20px;height:20px;padding:0 6px;border-radius:10px;display:flex;align-items:center;justify-content:center;">${item.unread_count}</div>` : ''}
|
||
`;
|
||
row.onclick = () => App.openChat(item.chat.id, item.other_id, item.other_name);
|
||
list.appendChild(row);
|
||
});
|
||
} catch (e) {
|
||
document.getElementById('chats-list').innerHTML = '<div class="empty-b"><div class="em">⚠️</div>Ошибка загрузки<br><small>Попробуй позже</small></div>';
|
||
App._toast('Не удалось загрузить чаты', 'err');
|
||
}
|
||
},
|
||
};
|
||
|
||
function formatChatTime(ts) {
|
||
try {
|
||
const d = new Date(ts);
|
||
const now = new Date();
|
||
const diff = (now - d) / 1000;
|
||
if (diff < 60) return 'сейчас';
|
||
if (diff < 3600) return Math.floor(diff / 60) + 'м';
|
||
if (diff < 86400) return Math.floor(diff / 3600) + 'ч';
|
||
if (diff < 604800) return Math.floor(diff / 86400) + 'д';
|
||
return d.toLocaleDateString('ru-RU', { day: 'numeric', month: 'short' });
|
||
} catch { return ''; }
|
||
}
|
||
|
||
window.ChatsView = ChatsView;
|