diff --git a/frontend/textbooks.html b/frontend/textbooks.html index 6478197..ffaeb92 100644 --- a/frontend/textbooks.html +++ b/frontend/textbooks.html @@ -22,88 +22,121 @@ .tb-title { font-family:'Unbounded',sans-serif; font-size:1.35rem; font-weight:800; letter-spacing:-.02em; } .tb-sub { font-size:.82rem; color:var(--text-2); margin-top:2px; } + /* ── Filter chips ── */ + .tb-filters { + display:flex; gap:6px; flex-wrap:wrap; margin-bottom:16px; align-items:center; + } + .tb-filter-label { + font-size:.7rem; font-weight:700; color:var(--text-3); + text-transform:uppercase; letter-spacing:.06em; margin-right:6px; + } + .tb-chip { + padding:5px 11px; border-radius:99px; + background:var(--surface); border:1.5px solid var(--border); + color:var(--text-2); font-family:'Manrope',sans-serif; + font-size:.78rem; font-weight:700; cursor:pointer; + transition:all .14s; display:inline-flex; align-items:center; gap:5px; + } + .tb-chip:hover { color:var(--text); border-color:var(--text-3); } + .tb-chip.active { + background:var(--violet); color:#fff; border-color:var(--violet); + } + .tb-chip-count { + font-size:.68rem; opacity:.7; font-weight:600; + } + + /* ── Compact grid ── */ .tb-grid { display:grid; - grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); - gap:22px; + grid-template-columns: repeat(auto-fill, minmax(190px, 1fr)); + gap:10px; } .tb-card { + position:relative; background:var(--surface); border:1.5px solid var(--border); - border-radius:18px; overflow:hidden; - transition: border-color .18s, box-shadow .18s, transform .18s; - display:flex; flex-direction:column; + border-radius:12px; overflow:hidden; + display:flex; align-items:stretch; + text-decoration:none; color:inherit; + transition: border-color .14s, transform .14s, box-shadow .14s; + min-height:74px; } .tb-card:hover { - transform: translateY(-3px); - box-shadow: 0 12px 36px rgba(0,0,0,.18); + transform: translateY(-1px); + border-color: var(--text-3); + box-shadow: 0 4px 14px rgba(0,0,0,.08); } - .tb-cover { - height:140px; position:relative; overflow:hidden; - display:flex; align-items:flex-end; padding:18px 22px 14px; + /* Left color strip (cover-marker) */ + .tb-mark { + flex:0 0 46px; + display:flex; flex-direction:column; + align-items:center; justify-content:center; + color:#fff; position:relative; overflow:hidden; } - .tb-cover.amber { background:linear-gradient(135deg, #b45309 0%, #d97706 60%, #f59e0b 100%); } - .tb-cover.blue { background:linear-gradient(135deg, #1e40af 0%, #2563eb 60%, #3b82f6 100%); } - .tb-cover.green { background:linear-gradient(135deg, #047857 0%, #059669 60%, #10b981 100%); } - .tb-cover.violet { background:linear-gradient(135deg, #6d28d9 0%, #7c3aed 60%, #9333ea 100%); } - .tb-cover.pink { background:linear-gradient(135deg, #be185d 0%, #db2777 60%, #ec4899 100%); } - .tb-cover.indigo { background:linear-gradient(135deg, #3730a3 0%, #4f46e5 60%, #818cf8 100%); } - .tb-cover.rose { background:linear-gradient(135deg, #9f1239 0%, #e11d48 60%, #fb7185 100%); } - .tb-cover.teal { background:linear-gradient(135deg, #134e4a 0%, #0d9488 60%, #14b8a6 100%); } - .tb-cover.cyan { background:linear-gradient(135deg, #164e63 0%, #0891b2 60%, #22d3ee 100%); } - .tb-cover.emerald{ background:linear-gradient(135deg, #064e3b 0%, #059669 60%, #34d399 100%); } - .tb-cover.amber-light{ background:linear-gradient(135deg, #92400e 0%, #d97706 60%, #fbbf24 100%); } - - .tb-cover::before { + .tb-mark::before { content: attr(data-watermark); - position:absolute; right:-10px; top:-15%; + position:absolute; left:50%; top:50%; transform:translate(-50%,-50%); font-family:'Unbounded',sans-serif; font-weight:900; - font-size:clamp(3rem, 9vw, 7rem); letter-spacing:-.04em; line-height:1; - color:transparent; -webkit-text-stroke:1.5px rgba(255,255,255,.18); - pointer-events:none; user-select:none; + font-size:2.4rem; letter-spacing:-.04em; line-height:1; + color:rgba(255,255,255,.16); + pointer-events:none; user-select:none; z-index:0; } - .tb-cover-info { - position:relative; z-index:1; color:#fff; - } - .tb-cover-grade { - display:inline-flex; align-items:center; gap:4px; - padding:3px 10px; border-radius:99px; - background:rgba(255,255,255,.18); backdrop-filter:blur(4px); - font-size:.7rem; font-weight:800; text-transform:uppercase; letter-spacing:.08em; - margin-bottom:6px; - } - .tb-cover-title { + .tb-mark-grade { + position:relative; z-index:1; font-family:'Unbounded',sans-serif; font-weight:800; - font-size:1.15rem; letter-spacing:-.01em; + font-size:1.1rem; letter-spacing:-.02em; line-height:1; } + .tb-mark-sub { + position:relative; z-index:1; + font-size:.56rem; font-weight:800; text-transform:uppercase; + letter-spacing:.08em; margin-top:3px; opacity:.85; + } + .tb-mark.amber { background:linear-gradient(160deg, #b45309 0%, #f59e0b 100%); } + .tb-mark.blue { background:linear-gradient(160deg, #1e40af 0%, #3b82f6 100%); } + .tb-mark.green { background:linear-gradient(160deg, #047857 0%, #10b981 100%); } + .tb-mark.violet { background:linear-gradient(160deg, #6d28d9 0%, #9333ea 100%); } + .tb-mark.pink { background:linear-gradient(160deg, #be185d 0%, #ec4899 100%); } + .tb-mark.indigo { background:linear-gradient(160deg, #3730a3 0%, #818cf8 100%); } + .tb-mark.rose { background:linear-gradient(160deg, #9f1239 0%, #fb7185 100%); } + .tb-mark.teal { background:linear-gradient(160deg, #134e4a 0%, #14b8a6 100%); } + .tb-mark.cyan { background:linear-gradient(160deg, #164e63 0%, #22d3ee 100%); } + .tb-mark.emerald{ background:linear-gradient(160deg, #064e3b 0%, #34d399 100%); } + .tb-mark.amber-light{ background:linear-gradient(160deg, #92400e 0%, #fbbf24 100%); } + /* Body */ .tb-body { - padding:16px 20px 18px; flex:1; - display:flex; flex-direction:column; gap:10px; + flex:1; min-width:0; + padding:9px 12px 8px 12px; + display:flex; flex-direction:column; gap:5px; + justify-content:center; } - .tb-author { - font-size:.78rem; color:var(--text-2); font-weight:600; - display:inline-flex; align-items:center; gap:6px; + .tb-title-row { + display:flex; align-items:flex-start; gap:6px; } - .tb-author svg { width:13px; height:13px; opacity:.7; } - .tb-desc { - font-size:.85rem; line-height:1.55; color:var(--text-2); + .tb-name { + font-family:'Manrope',sans-serif; font-weight:700; + font-size:.86rem; line-height:1.25; color:var(--text); + overflow:hidden; display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical; flex:1; } + .tb-meta { + font-size:.68rem; color:var(--text-3); font-weight:600; + display:flex; align-items:center; gap:6px; + } + .tb-meta b { color:var(--text-2); font-weight:700; } + /* Progress strip */ .tb-progress { - margin-top:6px; - padding-top:12px; border-top:1px solid var(--border); + display:flex; align-items:center; gap:6px; + margin-top:auto; } .tb-progress-bar { - height:6px; border-radius:99px; background:var(--border); overflow:hidden; - margin-bottom:7px; + flex:1; height:3px; border-radius:99px; background:var(--border); overflow:hidden; } .tb-progress-fill { - height:100%; border-radius:99px; - transition: width .3s ease; + height:100%; border-radius:99px; transition: width .3s ease; } .tb-progress.amber .tb-progress-fill { background:#d97706; } .tb-progress.blue .tb-progress-fill { background:#2563eb; } @@ -116,42 +149,34 @@ .tb-progress.cyan .tb-progress-fill { background:#0891b2; } .tb-progress.emerald .tb-progress-fill { background:#059669; } .tb-progress-text { - display:flex; justify-content:space-between; align-items:center; - font-size:.74rem; color:var(--text-3); + font-size:.66rem; color:var(--text-3); font-weight:700; + font-variant-numeric: tabular-nums; + min-width:28px; text-align:right; } - .tb-progress-text b { color:var(--text); font-weight:700; } - - .tb-actions { - display:flex; gap:8px; margin-top:12px; - } - .tb-btn { - flex:1; padding:9px 14px; border-radius:10px; - border:1.5px solid var(--border-h); background:transparent; color:var(--text); - font-family:'Manrope',sans-serif; font-size:.85rem; font-weight:700; - cursor:pointer; transition:all .15s; text-decoration:none; - display:inline-flex; align-items:center; justify-content:center; gap:6px; - } - .tb-btn:hover { border-color:var(--text-2); } - .tb-btn.primary { - border-color:transparent; color:#fff; - } - .tb-btn.primary.amber { background:#d97706; } - .tb-btn.primary.blue { background:#2563eb; } - .tb-btn.primary.green { background:#059669; } - .tb-btn.primary.violet { background:#7c3aed; } - .tb-btn.primary.pink { background:#db2777; } - .tb-btn.primary.indigo { background:#4f46e5; } - .tb-btn.primary.rose { background:#e11d48; } - .tb-btn.primary.teal { background:#0d9488; } - .tb-btn.primary.cyan { background:#0891b2; } - .tb-btn.primary.emerald { background:#059669; } - .tb-btn.primary:hover { filter:brightness(1.1); } - .tb-btn svg { width:14px; height:14px; } + /* Teacher assign button — overlay on hover */ .tb-assign-btn { - width:auto; min-width:42px; padding:9px 12px; - flex:0 0 auto; + position:absolute; top:5px; right:5px; + width:24px; height:24px; padding:0; border-radius:6px; + background:rgba(255,255,255,.92); border:1px solid var(--border); + color:var(--text-2); cursor:pointer; + display:flex; align-items:center; justify-content:center; + opacity:0; transition:opacity .14s, color .14s, transform .14s; + z-index:2; } + html.dark .tb-assign-btn { background:rgba(20,20,25,.92); } + .tb-card:hover .tb-assign-btn { opacity:1; } + .tb-assign-btn:hover { color:var(--violet); transform:scale(1.05); } + .tb-assign-btn svg { width:13px; height:13px; } + + /* Continue badge (small "play" overlay on cards in progress) */ + .tb-resume { + position:absolute; bottom:5px; right:5px; + font-size:.6rem; font-weight:800; color:var(--text-3); + text-transform:uppercase; letter-spacing:.04em; + opacity:0; transition:opacity .14s; + } + .tb-card:hover .tb-resume { opacity:1; } .tb-empty { grid-column: 1 / -1; @@ -159,6 +184,12 @@ } .tb-empty svg { width:48px; height:48px; opacity:.5; margin-bottom:14px; stroke:var(--text-3); } + @media (max-width: 600px) { + .tb-grid { grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); gap:8px; } + .tb-name { font-size:.8rem; } + .tb-mark { flex-basis:40px; } + } + /* ── Tabs ── */ .tb-tabs { display:flex; gap:4px; margin-bottom:24px; @@ -363,6 +394,11 @@
+
@@ -452,54 +488,87 @@ } } + let subjectFilter = 'all'; + const SUBJECT_LABELS = { + physics:'Физика', math:'Математика', chemistry:'Химия', biology:'Биология', + russian:'Русский', literature:'Литература', english:'Английский', + history:'История', geography:'География', informatics:'Информатика' + }; + const SUBJECT_WATERMARK = { + physics:'Φ', math:'Σ', chemistry:'Х', biology:'Β', + russian:'Р', literature:'Л', english:'E', + history:'H', geography:'G', informatics:'I' + }; + + window.setSubjectFilter = function(s) { + subjectFilter = s; + document.querySelectorAll('.tb-chip').forEach(c => c.classList.toggle('active', c.dataset.subject === s)); + render(); + }; + + function buildSubjectChips() { + const counts = {}; + textbooks.forEach(t => { counts[t.subject] = (counts[t.subject] || 0) + 1; }); + const subjects = Object.keys(counts).sort(); + if (!subjects.length || subjects.length < 2) { + document.getElementById('tb-filters').style.display = 'none'; + return; + } + document.getElementById('tb-filters').style.display = 'flex'; + document.getElementById('cnt-all').textContent = textbooks.length; + const cont = document.getElementById('tb-subject-chips'); + cont.innerHTML = subjects.map(s => { + const label = SUBJECT_LABELS[s] || s; + return ``; + }).join(''); + } + function render() { const grid = document.getElementById('tb-grid'); if (!textbooks.length) { grid.innerHTML = '
Учебники не добавлены
'; return; } - grid.innerHTML = textbooks.map(t => { + buildSubjectChips(); + const filtered = subjectFilter === 'all' ? textbooks : textbooks.filter(t => t.subject === subjectFilter); + if (!filtered.length) { + grid.innerHTML = '
По этому фильтру ничего нет
'; + return; + } + grid.innerHTML = filtered.map(t => { const readCount = (t.progress?.read || []).length; const pct = t.para_count ? Math.round(100 * readCount / t.para_count) : 0; - const watermark = t.subject === 'chemistry' ? 'Х' : t.subject === 'physics' ? 'Φ' : t.subject === 'math' ? 'Σ' : t.subject === 'biology' ? 'Β' : '§'; + const watermark = SUBJECT_WATERMARK[t.subject] || '§'; const continueHref = t.progress?.last_para ? `/textbook/${t.slug}#${t.progress.last_para}` : `/textbook/${t.slug}`; + const resumeLabel = t.progress?.last_para ? 'Продолжить →' : 'Открыть →'; + const subjLabel = SUBJECT_LABELS[t.subject] || t.subject || ''; return ` -
-
`; + ${isTeacher ? `` : ''} +
${resumeLabel}
+ `; }).join(''); if (window.lucide) lucide.createIcons(); }