Skip to content

Commit 59cf280

Browse files
committed
Collapse completed course modules
1 parent 2848b0a commit 59cf280

2 files changed

Lines changed: 38 additions & 4 deletions

File tree

‎app/static/app.js‎

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
const view = document.querySelector('#view');
22
let dashboard = null;
3+
const MODULE_COLLAPSE_KEY = 'python-path-module-collapse';
34
const sandboxHistory = [];
45
const sandboxSamples = [
56
{ label: 'Вывод текста', code: "print('Привет, Python!')\n", inputs: [] },
@@ -11,6 +12,23 @@ const esc = (value = '') => String(value)
1112
.replaceAll('&', '&amp;').replaceAll('<', '&lt;').replaceAll('>', '&gt;').replaceAll('"', '&quot;').replaceAll("'", '&#039;');
1213
const rich = (value = '') => esc(value).replace(/`([^`]+)`/g, '<code class="inline-code">$1</code>');
1314

15+
function moduleCollapseStates() {
16+
try { return JSON.parse(localStorage.getItem(MODULE_COLLAPSE_KEY) || '{}'); }
17+
catch { return {}; }
18+
}
19+
20+
function isModuleCollapsed(module) {
21+
if (module.completed !== module.total) return false;
22+
const states = moduleCollapseStates();
23+
return states[module.id] ?? true;
24+
}
25+
26+
function saveModuleCollapsed(moduleId, collapsed) {
27+
const states = moduleCollapseStates();
28+
states[moduleId] = collapsed;
29+
localStorage.setItem(MODULE_COLLAPSE_KEY, JSON.stringify(states));
30+
}
31+
1432
async function api(path, options = {}) {
1533
const response = await fetch(path, {
1634
headers: { 'Content-Type': 'application/json', ...(options.headers || {}) },
@@ -76,6 +94,17 @@ function renderDashboard(data) {
7694
${moduleHtml}
7795
<div class="section-heading"><div><h2>Коллекция достижений</h2><p>Маленькие победы поддерживают ритм.</p></div></div>
7896
<section class="badges">${badgeHtml}</section>`;
97+
view.querySelectorAll('[data-module-toggle]').forEach((button) => {
98+
button.addEventListener('click', () => {
99+
const module = button.closest('.module');
100+
const body = module.querySelector('[data-module-body]');
101+
const collapsed = !body.hidden;
102+
body.hidden = collapsed;
103+
button.setAttribute('aria-expanded', String(!collapsed));
104+
button.querySelector('.module-collapse').textContent = collapsed ? 'Развернуть ▾' : 'Свернуть ▴';
105+
saveModuleCollapsed(button.dataset.moduleToggle, collapsed);
106+
});
107+
});
79108
}
80109

81110
function renderModule(module) {
@@ -92,10 +121,15 @@ function renderModule(module) {
92121
const examMarkup = exam.available
93122
? `<a class="button ${exam.passed ? 'ghost' : 'blue'}" href="#/exam/${module.id}">${exam.passed ? `Сдан: ${exam.score} б.` : 'Пройти'}</a>`
94123
: '<button class="button ghost" disabled>🔒 Закрыт</button>';
124+
const collapsed = isModuleCollapsed(module);
125+
const title = `<div><h2>${module.icon} ${esc(module.title)}</h2><p>${esc(module.description)}</p></div><span class="module-progress">${module.completed} из ${module.total}<br>уроков</span>`;
126+
const header = module.completed === module.total
127+
? `<button class="module-title ${module.color} module-toggle" type="button" data-module-toggle="${module.id}" aria-expanded="${String(!collapsed)}">${title}<span class="module-collapse">${collapsed ? 'Развернуть ▾' : 'Свернуть ▴'}</span></button>`
128+
: `<div class="module-title ${module.color}">${title}</div>`;
95129
return `<section class="module card">
96-
<div class="module-title ${module.color}"><div><h2>${module.icon} ${esc(module.title)}</h2><p>${esc(module.description)}</p></div><span class="module-progress">${module.completed} из ${module.total}<br>уроков</span></div>
97-
<div class="lesson-list">${lessons}</div>
98-
<div class="exam-row"><div><strong>🏆 ${module.id === 'realworld' ? 'Финальный экзамен' : 'Мини-экзамен раздела'}</strong><p>${exam.available ? 'Проверь раздел и получи +50 XP.' : 'Откроется после всех уроков раздела.'}</p></div>${examMarkup}</div>
130+
${header}
131+
<div data-module-body ${collapsed ? 'hidden' : ''}><div class="lesson-list">${lessons}</div>
132+
<div class="exam-row"><div><strong>🏆 ${module.id === 'realworld' ? 'Финальный экзамен' : 'Мини-экзамен раздела'}</strong><p>${exam.available ? 'Проверь раздел и получи +50 XP.' : 'Откроется после всех уроков раздела.'}</p></div>${examMarkup}</div></div>
99133
</section>`;
100134
}
101135

‎app/static/styles.css‎

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)