feat: extend linux operations curriculum
This commit is contained in:
59
index.html
59
index.html
@@ -288,6 +288,27 @@
|
||||
color: var(--muted);
|
||||
line-height: 1.7;
|
||||
}
|
||||
.stage-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||
gap: 12px;
|
||||
margin-top: 14px;
|
||||
}
|
||||
.stage-card {
|
||||
padding: 14px;
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 18px;
|
||||
background: rgba(255,255,255,0.18);
|
||||
}
|
||||
.stage-card h4 {
|
||||
margin: 0 0 8px;
|
||||
font-size: 16px;
|
||||
}
|
||||
.stage-card p {
|
||||
margin: 0 0 8px;
|
||||
color: var(--muted);
|
||||
line-height: 1.7;
|
||||
}
|
||||
.lesson-btn.done {
|
||||
border-color: rgba(29, 155, 108, 0.4);
|
||||
background: rgba(29, 155, 108, 0.1);
|
||||
@@ -305,12 +326,14 @@
|
||||
.hero-grid, .detail-grid { grid-template-columns: 1fr; }
|
||||
.mini-stats { grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
||||
.module-summary-grid { grid-template-columns: 1fr; }
|
||||
.stage-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
||||
}
|
||||
@media (max-width: 720px) {
|
||||
.shell { padding: 14px; }
|
||||
.topbar, .hero-head, .search, .terminal-input { flex-direction: column; align-items: stretch; }
|
||||
.stats { grid-template-columns: 1fr; }
|
||||
.mini-stats { grid-template-columns: 1fr; }
|
||||
.stage-grid { grid-template-columns: 1fr; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
@@ -408,6 +431,13 @@
|
||||
<div class="mastery-note" id="masteryNote">Master a lesson after you can explain the command, predict the output, and connect it to a real operations step.</div>
|
||||
</section>
|
||||
|
||||
<section class="detail card">
|
||||
<div class="eyebrow">Learning stages</div>
|
||||
<h2 style="margin: 8px 0 0;">System route from Linux basics to operations habits</h2>
|
||||
<p class="muted">This view helps you study in phases instead of bouncing between unrelated commands.</p>
|
||||
<div class="stage-grid" id="stageGrid"></div>
|
||||
</section>
|
||||
|
||||
<section class="detail card">
|
||||
<div class="eyebrow">Module heatmap</div>
|
||||
<h2 style="margin: 8px 0 0;">See how the learning path is distributed</h2>
|
||||
@@ -567,6 +597,7 @@
|
||||
renderRuntime(state.overview.runtime || {});
|
||||
renderCommandTags(state.overview.commands || []);
|
||||
renderModules();
|
||||
renderStageGrid();
|
||||
renderModuleSummaryGrid();
|
||||
}
|
||||
|
||||
@@ -628,6 +659,34 @@
|
||||
`).join('');
|
||||
}
|
||||
|
||||
function renderStageGrid() {
|
||||
const groups = [
|
||||
{ title: 'Stage 1: Foundations', summary: 'Orientation, listing, file operations, and permissions.', range: [0, 1] },
|
||||
{ title: 'Stage 2: Search & Observation', summary: 'Text search, log preview, process, service, and network inspection.', range: [2, 3] },
|
||||
{ title: 'Stage 3: Incidents', summary: 'Disk, auth, and service-path drills that connect commands into playbooks.', range: [4, 4] },
|
||||
{ title: 'Stage 4: Automation & Platform', summary: 'Shell state, package tools, archives, monitoring, and scheduling.', range: [5, 7] }
|
||||
];
|
||||
const modules = state.overview.modules || [];
|
||||
const target = document.getElementById('stageGrid');
|
||||
target.innerHTML = groups.map((group) => {
|
||||
const selectedModules = modules.slice(group.range[0], group.range[1] + 1);
|
||||
const lessonTotal = selectedModules.reduce((sum, item) => sum + (item.lesson_count || 0), 0);
|
||||
const exerciseTotal = selectedModules.reduce((sum, item) => sum + (item.exercise_count || 0), 0);
|
||||
return `
|
||||
<article class="stage-card">
|
||||
<h4>${escapeHtml(group.title)}</h4>
|
||||
<p>${escapeHtml(group.summary)}</p>
|
||||
<div class="mini-stats">
|
||||
<div class="mini-stat"><span>Modules</span><strong>${selectedModules.length}</strong></div>
|
||||
<div class="mini-stat"><span>Lessons</span><strong>${lessonTotal}</strong></div>
|
||||
<div class="mini-stat"><span>Exercises</span><strong>${exerciseTotal}</strong></div>
|
||||
<div class="mini-stat"><span>Mastered</span><strong>${selectedModules.reduce((sum, item) => sum + (item.lessons || []).filter((lesson) => isMastered(lesson.id)).length, 0)}</strong></div>
|
||||
</div>
|
||||
</article>
|
||||
`;
|
||||
}).join('');
|
||||
}
|
||||
|
||||
async function openLesson(lessonId, focusExerciseId = '') {
|
||||
const response = await fetch('/api/lesson?id=' + encodeURIComponent(lessonId));
|
||||
const payload = await response.json();
|
||||
|
||||
Reference in New Issue
Block a user