diff --git a/frontend/dashboard.html b/frontend/dashboard.html index 1ac6b27..396d170 100644 --- a/frontend/dashboard.html +++ b/frontend/dashboard.html @@ -293,14 +293,14 @@ } .act-tab:hover { color: var(--violet); } .act-tab.active { background: var(--text); color: #fff; } - .act-scale-btns { display: flex; gap: 3px; } + .act-scale-btns { display: inline-flex; gap: 2px; padding: 3px; border-radius: 10px; background: rgba(15,23,42,0.05); } .act-scale-btn { - padding: 2px 8px; border-radius: 6px; border: 1px solid rgba(15,23,42,0.08); - font-family: 'Manrope', sans-serif; font-size: 0.62rem; font-weight: 700; - color: var(--text-3); background: transparent; cursor: pointer; transition: all 0.12s; + padding: 3px 11px; border-radius: 7px; border: none; + font-family: 'Manrope', sans-serif; font-size: 0.64rem; font-weight: 700; + color: var(--text-3); background: transparent; cursor: pointer; transition: all 0.15s; } - .act-scale-btn:hover { border-color: rgba(155,93,229,0.3); color: var(--violet); } - .act-scale-btn.active { background: rgba(155,93,229,0.08); border-color: rgba(155,93,229,0.25); color: var(--violet); } + .act-scale-btn:hover { color: var(--violet); } + .act-scale-btn.active { background: #fff; color: var(--violet); box-shadow: 0 1px 4px rgba(15,23,42,0.1); } .act-pane { display: none; } .act-pane.visible { display: block; } @@ -311,21 +311,32 @@ .hm-weekdays { display: flex; flex-direction: column; gap: 2px; width: 22px; flex-shrink: 0; padding-top: 1px; } .hm-wd { font-size: 0.5rem; font-weight: 700; color: var(--text-3); height: 14px; line-height: 14px; } .mini-heatmap { display: grid; grid-template-rows: repeat(7, 14px); grid-auto-flow: column; grid-auto-columns: 14px; gap: 2px; } - .hm-trend { font-size: 0.7rem; font-weight: 700; color: var(--text-3); } - .hm-trend.up { color: #059652; } - .hm-trend.down { color: #E0335E; } + /* Hero stat row */ + .hm-hero { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; margin-bottom: 13px; } + .hm-hero-num-row { display: flex; align-items: baseline; gap: 7px; flex-wrap: wrap; } + .hm-hero-num { font-family: 'Unbounded', sans-serif; font-size: 1.72rem; font-weight: 800; color: var(--text); line-height: 1; } + .hm-hero-unit { font-size: 0.76rem; font-weight: 700; color: var(--text-3); } + .hm-hero-sub { font-size: 0.72rem; color: var(--text-3); font-weight: 600; margin-top: 6px; } + .hm-hero-sub strong { color: var(--text); font-weight: 800; } + .hm-trend-pill { display: inline-flex; align-items: center; gap: 3px; padding: 3px 9px; border-radius: 99px; + font-size: 0.66rem; font-weight: 800; font-family: 'Manrope', sans-serif; white-space: nowrap; } + .hm-trend-pill svg { width: 12px; height: 12px; stroke-width: 2.6; } + .hm-trend-pill.up { background: rgba(5,150,82,0.1); color: #059652; } + .hm-trend-pill.down { background: rgba(224,51,94,0.1); color: #E0335E; } + .hm-trend-pill.flat { background: rgba(15,23,42,0.05); color: var(--text-3); } .hm-empty { padding: 28px 16px; text-align: center; color: var(--text-3); } .hm-empty-ic { color: var(--violet); opacity: .85; margin-bottom: 8px; } .hm-empty-t { font-weight: 700; color: var(--text); font-size: 0.92rem; margin-bottom: 4px; } .hm-empty-s { font-size: 0.78rem; line-height: 1.5; max-width: 320px; margin: 0 auto 12px; } .hm-empty-cta { display: inline-block; padding: 8px 16px; border-radius: 9px; background: var(--violet); color: #fff; font-weight: 700; font-size: 0.82rem; text-decoration: none; } .mhm-cell { - width: 14px; height: 14px; border-radius: 50%; background: rgba(15,23,42,0.05); - cursor: pointer; transition: transform 0.12s, box-shadow 0.12s; + width: 14px; height: 14px; border-radius: 4px; background: rgba(15,23,42,0.05); + cursor: pointer; transition: transform 0.12s ease, box-shadow 0.12s ease; animation: hmCellIn 0.3s ease both; } - .mhm-cell:hover { transform: scale(1.45); z-index: 2; position: relative; } - .mhm-cell.has-data:hover { box-shadow: 0 0 8px var(--cell-color, rgba(155,93,229,0.5)); } + .mhm-cell.has-data { box-shadow: inset 0 0 0 0.5px rgba(15,23,42,0.06); } + .mhm-cell:hover { transform: scale(1.34); z-index: 2; position: relative; border-radius: 5px; } + .mhm-cell.has-data:hover { box-shadow: 0 3px 12px var(--cell-color, rgba(155,93,229,0.5)); } @keyframes hmCellIn { from { opacity: 0; transform: scale(0.3); } to { opacity: 1; transform: scale(1); } } /* Subject-colored cells */ @@ -335,16 +346,18 @@ .mhm-cell.s-phys { --cell-color: rgba(245,158,11,0.85); } .mhm-cell.s-mix { --cell-color: rgba(155,93,229,0.6); } - /* Heatmap footer stats */ - .hm-footer { - display: flex; align-items: center; gap: 14px; margin-top: 8px; padding-top: 8px; - border-top: 1px solid rgba(15,23,42,0.05); - font-size: 0.68rem; color: var(--text-3); font-weight: 600; flex-wrap: wrap; + /* Heatmap legend */ + .hm-legend-row { + display: flex; align-items: center; flex-wrap: wrap; gap: 6px; + margin-top: 13px; padding-top: 11px; border-top: 1px solid rgba(15,23,42,0.06); } - .hm-footer strong { color: var(--text); font-weight: 800; } - .hm-legend { display: flex; align-items: center; gap: 4px; margin-left: auto; } - .hm-legend-dot { width: 10px; height: 10px; border-radius: 50%; } - .hm-legend-label { font-size: 0.58rem; } + .hm-legend { display: flex; align-items: center; flex-wrap: wrap; gap: 6px; } + .hm-legend-chip { + display: inline-flex; align-items: center; gap: 5px; padding: 2px 9px; + border-radius: 99px; background: rgba(15,23,42,0.04); + font-size: 0.6rem; font-weight: 700; color: var(--text-3); + } + .hm-legend-dot { width: 7px; height: 7px; border-radius: 50%; flex-shrink: 0; } /* Day popup */ .hm-day-popup { @@ -370,11 +383,14 @@ /* Dark mode */ .app-layout.dark .act-tab.active { background: #E8ECF2; color: var(--text); } - .app-layout.dark .act-scale-btn { border-color: rgba(255,255,255,0.08); color: #6B7A8E; } - .app-layout.dark .act-scale-btn.active { background: rgba(155,93,229,0.12); border-color: rgba(155,93,229,0.3); color: var(--violet); } + .app-layout.dark .act-scale-btns { background: rgba(255,255,255,0.05); } + .app-layout.dark .act-scale-btn { color: #6B7A8E; } + .app-layout.dark .act-scale-btn.active { background: rgba(255,255,255,0.1); color: var(--violet); box-shadow: none; } .app-layout.dark .mhm-cell { background: rgba(255,255,255,0.04); } - .app-layout.dark .hm-footer { border-color: rgba(255,255,255,0.06); } - .app-layout.dark .hm-footer strong { color: #E8ECF2; } + .app-layout.dark .mhm-cell.has-data { box-shadow: inset 0 0 0 0.5px rgba(255,255,255,0.07); } + .app-layout.dark .hm-hero-num, .app-layout.dark .hm-hero-sub strong { color: #E8ECF2; } + .app-layout.dark .hm-legend-row { border-color: rgba(255,255,255,0.07); } + .app-layout.dark .hm-legend-chip { background: rgba(255,255,255,0.05); } .app-layout.dark .hm-day-popup { background: #1A1D27; border-color: rgba(255,255,255,0.08); box-shadow: 0 12px 40px rgba(0,0,0,0.4); } .app-layout.dark .hm-day-popup .hdp-date, .app-layout.dark .hm-day-popup .hdp-subj { color: #E8ECF2; } @@ -540,15 +556,15 @@ /* C2: Streak calendar */ .streak-cal { display: grid; grid-template-columns: repeat(7, 1fr); gap: 3px; } .sc-day { - aspect-ratio: 1; border-radius: 6px; display: flex; align-items: center; justify-content: center; + aspect-ratio: 1; border-radius: 8px; display: flex; align-items: center; justify-content: center; font-size: 0.62rem; font-weight: 700; color: var(--text-3); background: rgba(15,23,42,0.03); - transition: transform 0.12s; + transition: transform 0.12s, box-shadow 0.12s; } - .sc-day.today { border: 1.5px solid var(--violet); color: var(--violet); font-weight: 800; } + .sc-day.today { box-shadow: inset 0 0 0 1.5px var(--violet); color: var(--violet); font-weight: 800; } .sc-day.active { background: rgba(155,93,229,0.18); color: #7c3aed; } - .sc-day.active.today { background: var(--violet); color: #fff; } + .sc-day.active.today { background: var(--violet); color: #fff; box-shadow: 0 3px 10px rgba(155,93,229,0.35); } .sc-day.future { opacity: 0.3; } - .sc-day:hover { transform: scale(1.15); } + .sc-day:hover { transform: scale(1.15); box-shadow: 0 4px 12px rgba(15,23,42,0.12); } .sc-day.pulse { animation: scPulse 2s ease-in-out infinite; } @keyframes scPulse { 0%, 100% { box-shadow: 0 0 0 0 rgba(155,93,229,0.3); } @@ -556,7 +572,7 @@ } .sc-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 8px; } .sc-month { font-family: 'Unbounded', sans-serif; font-size: 0.7rem; font-weight: 800; color: var(--text); } - .sc-streak-badge { font-family: 'Unbounded', sans-serif; font-size: 0.7rem; font-weight: 800; color: #F59E0B; display: flex; align-items: center; gap: 4px; } + .sc-streak-badge { font-family: 'Unbounded', sans-serif; font-size: 0.68rem; font-weight: 800; color: #E8890B; display: inline-flex; align-items: center; gap: 4px; padding: 3px 10px; border-radius: 99px; background: rgba(245,158,11,0.13); } .sc-weekdays { display: grid; grid-template-columns: repeat(7, 1fr); gap: 3px; margin-bottom: 3px; } .sc-wd { font-size: 0.56rem; color: var(--text-3); font-weight: 700; text-align: center; text-transform: uppercase; } @@ -1778,13 +1794,6 @@
-
-
- - - -
-
@@ -3526,6 +3535,10 @@ const ACT_ORDER = ['test', 'exam', 'cards', 'lesson', 'live', 'homework']; function _dayTotal(types) { let s = 0; for (const k in (types || {})) s += types[k] || 0; return s; } function _domType(types) { let best = null, bn = -1; for (const k in (types || {})) { if (types[k] > bn) { bn = types[k]; best = k; } } return best; } + function _hexA(h, a) { const n = parseInt(String(h).slice(1), 16); return `rgba(${(n>>16)&255},${(n>>8)&255},${n&255},${a})`; } + const ICN_TREND_UP = ''; + const ICN_TREND_DOWN = ''; + const ICN_TREND_FLAT = ''; /* ══ WIDGET: Activity heatmap (all study types) ══════════════════════ */ async function loadActivityWidget() { @@ -3595,10 +3608,8 @@ let style; if (n && !isFuture) { const color = (ACT_TYPES[_domType(types)] || {}).color || '#9B5DE5'; - const alpha = Math.min(1, 0.32 + Math.log2(n + 1) * 0.22); - const sz = Math.min(14, 8 + Math.min(n, 3) * 2); - const margin = (14 - sz) / 2; - style = ` style="background:${color};opacity:${alpha.toFixed(2)};width:${sz}px;height:${sz}px;margin:${margin}px;animation-delay:${cellIdx * 4}ms"`; + const alpha = Math.min(1, 0.42 + Math.log2(n + 1) * 0.2); + style = ` style="background:${_hexA(color, alpha.toFixed(2))};--cell-color:${_hexA(color, 0.55)};animation-delay:${cellIdx * 4}ms"`; } else if (isFuture) { style = ` style="opacity:0.15;animation-delay:${cellIdx * 4}ms"`; } else { @@ -3609,33 +3620,46 @@ } } - // Footer: total + weekly trend + activity-type legend + // Hero: total + weekly trend pill + scale control const diff = thisWeek - lastWeek; - const trendTxt = diff > 0 ? `+${diff} к прошлой` : diff < 0 ? `${diff} к прошлой` : 'как на прошлой'; - const trendCls = diff > 0 ? 'up' : diff < 0 ? 'down' : ''; - let footerHtml = `${totalAll} занятий за ${weeks} нед.`; - footerHtml += `эта неделя ${thisWeek} ${trendTxt}`; - footerHtml += `
` + - ACT_ORDER.map(k => `${ACT_TYPES[k].label}`).join('') + - `
`; + let trendCls, trendTxt, trendIcon; + if (diff > 0) { trendCls = 'up'; trendTxt = `+${diff}`; trendIcon = ICN_TREND_UP; } + else if (diff < 0) { trendCls = 'down'; trendTxt = `${diff}`; trendIcon = ICN_TREND_DOWN; } + else { trendCls = 'flat'; trendTxt = 'без изменений'; trendIcon = ICN_TREND_FLAT; } + const wkWord = weeks === 26 ? '6 мес.' : `${weeks} нед.`; + const scaleHtml = [[6,'6н'],[12,'12н'],[26,'6м']] + .map(([w,l]) => ``).join(''); + + const heroHtml = + `
` + + `
` + + `${totalAll}` + + `занятий за ${wkWord}` + + `${trendIcon}${trendTxt}` + + `
` + + `
на этой неделе ${thisWeek}, на прошлой ${lastWeek}
` + + `
${scaleHtml}
`; + + const legendHtml = `
` + + ACT_ORDER.map(k => `${ACT_TYPES[k].label}`).join('') + + `
`; host.innerHTML = + heroHtml + `
${monthHtml}
` + `
` + `
${wdNames.map(w => `
${w}
`).join('')}
` + `
${cellsHtml}
` + `
` + - ``; + legendHtml; setTimeout(() => { initHeatmapTooltip(); initHeatmapClick(); }, 50); } /* ══ Heatmap scale switch ══════════════════════════════════════════ */ - function setHmScale(weeks, btn) { + function setHmScale(weeks) { _hmScale = weeks; - document.querySelectorAll('.act-scale-btn').forEach(b => b.classList.remove('active')); - if (btn) btn.classList.add('active'); - renderHeatmap(); + renderHeatmap(); // rebuilds the scale control with the active state } /* ══ Activity tab switch ═══════════════════════════════════════════ */ @@ -3881,7 +3905,13 @@ if (isFuture) cls += ' future'; // Pulse today if no session yet if (isToday && !todayHasSession) cls += ' pulse'; - html += `
${d}
`; + // Shade active (non-today) days by how much was done + let st = ''; + if (isActive && !isToday) { + const a = Math.min(0.42, 0.16 + Math.log2(_dayTotal(_activityDays[key]) + 1) * 0.1); + st = ` style="background:${_hexA('#9B5DE5', a.toFixed(2))}"`; + } + html += `
${d}
`; } html += ''; body.innerHTML = html;