feat(dashboard): карточка чтения наполняется данными (фон+инфо учебника)

Раньше при отсутствии начатого курса карточка оставалась статичной
заглушкой («Учебники»). Теперь:
- при прогрессе — «Продолжить чтение»: курс, урок, прогресс-бар, %;
- иначе — рекомендованный учебник из /api/courses: название, описание,
  число параграфов;
- фон-градиент карточки по предмету (SUBJ_GRADIENT, как обложки).
Синтаксис всех инлайн-скриптов проверен (0 ошибок).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Maxim Dolgolyov
2026-05-31 12:18:40 +03:00
parent eaba6b7389
commit 4fed35fec8
+62 -12
View File
@@ -3201,23 +3201,73 @@
}
/* ══ HERO: Reading card (continue or start) ══════════════════════ */
// Градиент-обложка карточки чтения по предмету (как обложки учебников)
const SUBJ_GRADIENT = {
chemistry:'linear-gradient(135deg,#d9742a 0%,#b3531a 100%)',
physics:'linear-gradient(135deg,#3b6fd4 0%,#26408a 100%)',
biology:'linear-gradient(135deg,#2fa86a 0%,#1c6e44 100%)',
math:'linear-gradient(135deg,#7a5cf0 0%,#4b32b3 100%)',
informatics:'linear-gradient(135deg,#0f9aa8 0%,#0a6470 100%)',
english:'linear-gradient(135deg,#e0557f 0%,#a82f57 100%)',
russian:'linear-gradient(135deg,#d64545 0%,#8a2626 100%)',
belarus:'linear-gradient(135deg,#d64545 0%,#8a2626 100%)',
history:'linear-gradient(135deg,#b5862a 0%,#7a571a 100%)',
geography:'linear-gradient(135deg,#2f9aa8 0%,#1c6470 100%)',
astronomy:'linear-gradient(135deg,#5a4fd4 0%,#2e2680 100%)',
social:'linear-gradient(135deg,#c2682a 0%,#8a4519 100%)',
};
function _renderReadCard(o) {
const card = document.getElementById('hc-read');
if (!card) return;
if (o.href) card.href = o.href;
if (SUBJ_GRADIENT[o.slug]) card.style.background = SUBJ_GRADIENT[o.slug];
document.getElementById('hc-read-tag').textContent = o.tag;
document.getElementById('hc-read-title').textContent = o.title;
document.getElementById('hc-read-sub').textContent = o.sub || '';
document.getElementById('hc-read-meta').textContent = o.meta || '';
const pw = document.getElementById('hc-read-prog-wrap');
const pe = document.getElementById('hc-read-pct');
if (o.showProg) {
if (pw) { pw.style.display = ''; document.getElementById('hc-read-prog').style.width = (o.pct || 0) + '%'; }
if (pe) { pe.style.display = ''; pe.textContent = (o.pct || 0) + '%'; }
} else {
if (pw) pw.style.display = 'none';
if (pe) pe.style.display = 'none';
}
}
async function loadContinueWidget() {
const card = document.getElementById('hc-read');
if (!card) return;
try {
const data = await LS.api('/api/courses/continue');
if (!data || !data.courseId) return; // оставляем дефолт «Начать чтение → Учебники»
const pct = data.lessonCount > 0 ? Math.round((data.doneCount || 0) / data.lessonCount * 100) : 0;
card.href = `/lesson?course=${data.courseId}&lesson=${data.lessonId}`;
document.getElementById('hc-read-tag').textContent = 'Продолжить чтение';
document.getElementById('hc-read-title').textContent = data.courseTitle || 'Продолжить';
document.getElementById('hc-read-sub').textContent = data.lessonTitle || '';
document.getElementById('hc-read-meta').textContent = `${data.doneCount || 0} / ${data.lessonCount || 0} уроков`;
const progWrap = document.getElementById('hc-read-prog-wrap');
if (progWrap) { progWrap.style.display = ''; document.getElementById('hc-read-prog').style.width = pct + '%'; }
const pctEl = document.getElementById('hc-read-pct');
if (pctEl) { pctEl.style.display = ''; pctEl.textContent = pct + '%'; }
} catch { /* нет курса в процессе — карточка остаётся в дефолте */ }
if (data && data.courseId) {
const pct = data.lessonCount > 0 ? Math.round((data.doneCount || 0) / data.lessonCount * 100) : 0;
_renderReadCard({
tag: 'Продолжить чтение',
href: `/lesson?course=${data.courseId}&lesson=${data.lessonId}`,
title: data.courseTitle || 'Продолжить',
sub: data.lessonTitle || '',
meta: `${data.doneCount || 0} / ${data.lessonCount || 0} уроков`,
slug: data.subjectSlug, pct, showProg: true,
});
return;
}
} catch { /* нет прогресса — покажем рекомендованный учебник */ }
try {
const courses = await LS.api('/api/courses');
if (Array.isArray(courses) && courses.length) {
const c = courses[0];
const cnt = c.lesson_count || 0;
_renderReadCard({
tag: 'Начать чтение',
href: `/course?id=${c.id}`,
title: c.title || 'Учебник',
sub: c.description || 'Открой учебник и начни курс.',
meta: cnt ? `${cnt} § · новый учебник` : 'новый учебник',
slug: c.subject_slug, pct: 0, showProg: false,
});
}
} catch { /* дефолт «Учебники» */ }
}
/* ══ HERO: Lab of the day (deterministic daily pick) ═════════════ */