feat(dashboard): карточка чтения берёт данные и цвет из «Учебников»
Источник — /api/textbooks (как страница «Учебники»): - учебник в процессе (есть прочитанные §) → «Продолжить чтение» с прогресс-баром и «N из M § прочитано», ссылка на last_para; - иначе первый учебник каталога → «Начать чтение», «M § · новый учебник»; - фон карточки = градиент обложки по t.color (TB_COVER — зеркало .tb-cover из textbooks.html), полная синхронизация цвета. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+55
-45
@@ -3200,27 +3200,30 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ══ HERO: Reading card (continue or start) ══════════════════════ */
|
/* ══ HERO: Reading card — данные и цвет из блока «Учебники» ═══════ */
|
||||||
// Градиент-обложка карточки чтения по предмету (как обложки учебников)
|
// Палитра обложек учебников (зеркало .tb-cover из textbooks.html)
|
||||||
const SUBJ_GRADIENT = {
|
const TB_COVER = {
|
||||||
chemistry:'linear-gradient(135deg,#d9742a 0%,#b3531a 100%)',
|
amber:'linear-gradient(135deg,#b45309 0%,#d97706 60%,#f59e0b 100%)',
|
||||||
physics:'linear-gradient(135deg,#3b6fd4 0%,#26408a 100%)',
|
blue:'linear-gradient(135deg,#1e40af 0%,#2563eb 60%,#3b82f6 100%)',
|
||||||
biology:'linear-gradient(135deg,#2fa86a 0%,#1c6e44 100%)',
|
green:'linear-gradient(135deg,#047857 0%,#059669 60%,#10b981 100%)',
|
||||||
math:'linear-gradient(135deg,#7a5cf0 0%,#4b32b3 100%)',
|
violet:'linear-gradient(135deg,#6d28d9 0%,#7c3aed 60%,#9333ea 100%)',
|
||||||
informatics:'linear-gradient(135deg,#0f9aa8 0%,#0a6470 100%)',
|
pink:'linear-gradient(135deg,#be185d 0%,#db2777 60%,#ec4899 100%)',
|
||||||
english:'linear-gradient(135deg,#e0557f 0%,#a82f57 100%)',
|
indigo:'linear-gradient(135deg,#3730a3 0%,#4f46e5 60%,#818cf8 100%)',
|
||||||
russian:'linear-gradient(135deg,#d64545 0%,#8a2626 100%)',
|
rose:'linear-gradient(135deg,#9f1239 0%,#e11d48 60%,#fb7185 100%)',
|
||||||
belarus:'linear-gradient(135deg,#d64545 0%,#8a2626 100%)',
|
teal:'linear-gradient(135deg,#134e4a 0%,#0d9488 60%,#14b8a6 100%)',
|
||||||
history:'linear-gradient(135deg,#b5862a 0%,#7a571a 100%)',
|
cyan:'linear-gradient(135deg,#164e63 0%,#0891b2 60%,#22d3ee 100%)',
|
||||||
geography:'linear-gradient(135deg,#2f9aa8 0%,#1c6470 100%)',
|
emerald:'linear-gradient(135deg,#064e3b 0%,#059669 60%,#34d399 100%)',
|
||||||
astronomy:'linear-gradient(135deg,#5a4fd4 0%,#2e2680 100%)',
|
'amber-light':'linear-gradient(135deg,#92400e 0%,#d97706 60%,#fbbf24 100%)',
|
||||||
social:'linear-gradient(135deg,#c2682a 0%,#8a4519 100%)',
|
sky:'linear-gradient(135deg,#0c4a6e 0%,#0284c7 60%,#7dd3fc 100%)',
|
||||||
|
red:'linear-gradient(135deg,#7f1d1d 0%,#dc2626 60%,#f87171 100%)',
|
||||||
|
orange:'linear-gradient(135deg,#9a3412 0%,#ea580c 60%,#fb923c 100%)',
|
||||||
|
yellow:'linear-gradient(135deg,#854d0e 0%,#ca8a04 60%,#fde047 100%)',
|
||||||
};
|
};
|
||||||
function _renderReadCard(o) {
|
function _renderReadCard(o) {
|
||||||
const card = document.getElementById('hc-read');
|
const card = document.getElementById('hc-read');
|
||||||
if (!card) return;
|
if (!card) return;
|
||||||
if (o.href) card.href = o.href;
|
if (o.href) card.href = o.href;
|
||||||
if (SUBJ_GRADIENT[o.slug]) card.style.background = SUBJ_GRADIENT[o.slug];
|
if (TB_COVER[o.color]) card.style.background = TB_COVER[o.color];
|
||||||
document.getElementById('hc-read-tag').textContent = o.tag;
|
document.getElementById('hc-read-tag').textContent = o.tag;
|
||||||
document.getElementById('hc-read-title').textContent = o.title;
|
document.getElementById('hc-read-title').textContent = o.title;
|
||||||
document.getElementById('hc-read-sub').textContent = o.sub || '';
|
document.getElementById('hc-read-sub').textContent = o.sub || '';
|
||||||
@@ -3238,36 +3241,43 @@
|
|||||||
async function loadContinueWidget() {
|
async function loadContinueWidget() {
|
||||||
const card = document.getElementById('hc-read');
|
const card = document.getElementById('hc-read');
|
||||||
if (!card) return;
|
if (!card) return;
|
||||||
|
let books;
|
||||||
try {
|
try {
|
||||||
const data = await LS.api('/api/courses/continue');
|
const r = await LS.api('/api/textbooks');
|
||||||
if (data && data.courseId) {
|
books = r && r.textbooks ? r.textbooks : [];
|
||||||
const pct = data.lessonCount > 0 ? Math.round((data.doneCount || 0) / data.lessonCount * 100) : 0;
|
} catch { return; } // нет доступа/ошибка — оставляем дефолт «Учебники»
|
||||||
_renderReadCard({
|
if (!books.length) return;
|
||||||
tag: 'Продолжить чтение',
|
|
||||||
href: `/lesson?course=${data.courseId}&lesson=${data.lessonId}`,
|
// Выбор учебника: тот, что в процессе (есть прочитанные §), приоритет
|
||||||
title: data.courseTitle || 'Продолжить',
|
// последнему открытому; иначе — первый из каталога как рекомендованный.
|
||||||
sub: data.lessonTitle || '',
|
const withProgress = books
|
||||||
meta: `${data.doneCount || 0} / ${data.lessonCount || 0} уроков`,
|
.filter(b => (b.progress && (b.progress.last_para || (b.progress.read || []).length)))
|
||||||
slug: data.subjectSlug, pct, showProg: true,
|
.sort((a, b) => ((b.progress.read || []).length) - ((a.progress.read || []).length));
|
||||||
});
|
const inProgress = withProgress[0];
|
||||||
return;
|
const b = inProgress || books[0];
|
||||||
}
|
const readCount = (b.progress && b.progress.read ? b.progress.read.length : 0);
|
||||||
} catch { /* нет прогресса — покажем рекомендованный учебник */ }
|
const pct = b.para_count ? Math.round(100 * readCount / b.para_count) : 0;
|
||||||
try {
|
const href = (b.progress && b.progress.last_para)
|
||||||
const courses = await LS.api('/api/courses');
|
? `/textbook/${b.slug}#${b.progress.last_para}`
|
||||||
if (Array.isArray(courses) && courses.length) {
|
: `/textbook/${b.slug}`;
|
||||||
const c = courses[0];
|
|
||||||
const cnt = c.lesson_count || 0;
|
if (inProgress) {
|
||||||
_renderReadCard({
|
_renderReadCard({
|
||||||
tag: 'Начать чтение',
|
tag: 'Продолжить чтение', href, color: b.color,
|
||||||
href: `/course?id=${c.id}`,
|
title: b.title,
|
||||||
title: c.title || 'Учебник',
|
sub: b.description || `${b.grade} класс`,
|
||||||
sub: c.description || 'Открой учебник и начни курс.',
|
meta: `${readCount} из ${b.para_count} § прочитано`,
|
||||||
meta: cnt ? `${cnt} § · новый учебник` : 'новый учебник',
|
pct, showProg: true,
|
||||||
slug: c.subject_slug, pct: 0, showProg: false,
|
});
|
||||||
});
|
} else {
|
||||||
}
|
_renderReadCard({
|
||||||
} catch { /* дефолт «Учебники» */ }
|
tag: 'Начать чтение', href, color: b.color,
|
||||||
|
title: b.title,
|
||||||
|
sub: b.description || `${b.grade} класс`,
|
||||||
|
meta: `${b.para_count} § · новый учебник`,
|
||||||
|
pct: 0, showProg: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ══ HERO: Lab of the day (deterministic daily pick) ═════════════ */
|
/* ══ HERO: Lab of the day (deterministic daily pick) ═════════════ */
|
||||||
|
|||||||
Reference in New Issue
Block a user