Migrate UI to Lucide icons, reorder sidebar, and polish journal/groups UX

- Replace emoji icons with Lucide across admin pages; add vendor/lucide.min.js and renderIcons() helper\n- Reorder sidebar logically (Dashboard, Journal, Students, Groups, Files, Links, Trash + admin sections)\n- Groups: open photo chronology only via the Фото button; covers no longer clickable\n- Journal: show group badge over card photo, compact AI-status icon beside description, and date range in empty-state message\n- Update README (AI worker, Lucide, worker.js)
This commit is contained in:
dev
2026-09-11 14:33:41 +03:00
parent 275ed46dfb
commit ad81adfe71
21 changed files with 128 additions and 63 deletions
+28 -9
View File
@@ -49,10 +49,21 @@ const AI_STATUS_LABELS = {
reverted: 'ИИ: возвращён оригинал',
};
const AI_STATUS_ICONS = {
pending: 'clock',
processing: 'loader',
done: 'circle-check',
skipped: 'skip-forward',
error: 'circle-alert',
reverted: 'undo',
};
function aiBadge(e) {
if (!e || !e.ai_status) return '';
const label = AI_STATUS_LABELS[e.ai_status] || `ИИ: ${e.ai_status}`;
return `<span class="ai-badge ai-badge-${esc(e.ai_status)}" title="${esc(label)}">${esc(label)}</span>`;
const st = e.ai_status;
const label = AI_STATUS_LABELS[st] || `ИИ: ${st}`;
const icon = AI_STATUS_ICONS[st] || 'sparkles';
return `<span class="ai-badge ai-badge-${esc(st)}" title="${esc(label)}" role="img" aria-label="${esc(label)}"><i data-lucide="${icon}"></i></span>`;
}
function findFile(id) {
@@ -82,10 +93,10 @@ function gridEntryHTML(e) {
return `
<div class="card">
${mp ? `<img src="${API}${mp}" data-img="${API}${mp}" alt="">` : ''}
<div class="info">
<div class="name">${esc(e.student_name)}</div>${aiBadge(e)}
<div class="info">
<div class="name">${esc(e.student_name)}</div>
<div class="group">${esc(e.group_name)}</div>
<div class="desc">${esc(e.description)}</div>
<div class="desc">${esc(e.description)}${aiBadge(e)}</div>
${filesHTML(e.files)}
<div class="time">${new Date(e.created_at).toLocaleString('ru')}</div>
</div>
@@ -102,10 +113,10 @@ function cardEntryHTML(e) {
${mp
? `<img src="${API}${mp}" data-img="${API}${mp}" alt="">`
: `<div class="no-photo"><svg viewBox="0 0 240 160" preserveAspectRatio="xMidYMid meet" aria-hidden="true"><circle cx="105" cy="48" r="15"/><path d="M74 140 C74 106 88 92 105 92 C122 92 136 106 136 140 Z"/><circle cx="145" cy="58" r="18"/><path d="M108 144 C108 104 124 88 145 88 C166 88 182 104 182 144 Z"/></svg></div>`}
${e.group_name ? `<span class="group-badge" title="${esc(e.group_name)}">${esc(e.group_name)}</span>` : ''}
<div class="body">
<div class="name">${esc(e.student_name)}</div>${aiBadge(e)}
<div class="group">${esc(e.group_name)}</div>
<div class="desc">${esc(e.description)}</div>
<div class="name">${esc(e.student_name)}</div>
<div class="desc">${esc(e.description)}${aiBadge(e)}</div>
${filesHTML(e.files)}
</div>
<div class="foot">
@@ -122,8 +133,15 @@ function cardEntryHTML(e) {
function renderGrid() {
const grid = document.getElementById('grid');
if (!currentEntries.length) {
const fmt = d => d ? d.split('-').reverse().join('.') : '';
const from = fmt(document.getElementById('dateFrom').value);
const to = fmt(document.getElementById('dateTo').value);
let range = '';
if (from && to) range = ` за ${from} – ${to}`;
else if (from) range = ` с ${from}`;
else if (to) range = ` по ${to}`;
grid.className = 'grid';
grid.innerHTML = '<div class="empty">Ничего не найдено</div>';
grid.innerHTML = `<div class="empty">Ничего не найдено${range}</div>`;
return;
}
if (view === 'cards') {
@@ -133,6 +151,7 @@ function renderGrid() {
grid.className = 'grid';
grid.innerHTML = currentEntries.map(gridEntryHTML).join('');
}
renderIcons();
}
function setView(v) {