Files
offlineu/templates/select_course.html
T
2026-08-20 13:47:51 -04:00

154 lines
7.1 KiB
HTML

<!DOCTYPE html>
<html>
<head>
<title>OfflineU - Select Course</title>
<style>
body { font-family: Arial, sans-serif; margin: 40px; color: #222; }
.container { max-width: 900px; margin: 0 auto; }
.tabs { margin-bottom: 20px; }
.tabs button {
padding: 8px 16px; margin-right: 8px; cursor: pointer;
border: 1px solid #ccc; background: #f5f5f5; border-radius: 4px;
}
.tabs button.active { background: #4CAF50; color: white; border-color: #4CAF50; }
.group { margin: 18px 0; }
.group-title {
font-size: 1.1em; font-weight: bold; margin-bottom: 6px;
padding-bottom: 4px; border-bottom: 2px solid #4CAF50; color: #2e7d32;
}
.course-card {
padding: 10px 14px; border: 1px solid #ddd; margin: 5px 0;
cursor: pointer; border-radius: 4px; background: #e8f5e8;
display: flex; justify-content: space-between; align-items: center;
}
.course-card:hover { background-color: #d7ecd7; }
.course-card .meta { color: #666; font-size: 0.85em; }
.directory { padding: 10px; border: 1px solid #ddd; margin: 5px 0; cursor: pointer; border-radius: 4px; }
.directory:hover { background-color: #f0f0f0; }
.course-candidate { background-color: #e8f5e8; }
.empty-state, .error-state {
padding: 16px; border: 1px dashed #ccc; border-radius: 4px;
color: #666; margin-top: 10px;
}
.error-state { border-color: #e57373; color: #b71c1c; background: #fff5f5; }
.path-bar { font-size: 0.9em; color: #666; margin-bottom: 10px; }
code { background: #f0f0f0; padding: 2px 5px; border-radius: 3px; }
</style>
</head>
<body>
<div class="container">
<h1>OfflineU - Course Selection</h1>
<div class="tabs">
<button id="tab-library" class="active" onclick="showTab('library')">📚 Library</button>
<button id="tab-filesystem" onclick="showTab('filesystem')">🗂️ Browse Filesystem</button>
</div>
<div id="library-view">
<div class="path-bar" id="library-path-bar"></div>
<div id="library-groups"></div>
</div>
<div id="filesystem-view" style="display:none;">
<div id="browser"></div>
</div>
<script>
function showTab(tab) {
document.getElementById('tab-library').classList.toggle('active', tab === 'library');
document.getElementById('tab-filesystem').classList.toggle('active', tab === 'filesystem');
document.getElementById('library-view').style.display = tab === 'library' ? '' : 'none';
document.getElementById('filesystem-view').style.display = tab === 'filesystem' ? '' : 'none';
if (tab === 'filesystem' && !document.getElementById('browser').dataset.loaded) {
document.getElementById('browser').dataset.loaded = '1';
loadDirectories();
}
}
// ---- Library view: grouped, click-to-load, no typed paths ----
function loadLibrary() {
fetch('/library')
.then(r => r.json())
.then(data => {
document.getElementById('library-path-bar').innerHTML =
`Scanning: <code>${data.library_path}</code>`;
const container = document.getElementById('library-groups');
const groupNames = Object.keys(data.groups).sort((a, b) => a.localeCompare(b));
if (data.errors && data.errors.length && groupNames.length === 0) {
container.innerHTML = `<div class="error-state">
Couldn't load the library at <code>${data.library_path}</code>.<br>
${data.errors.map(e => e).join('<br>')}<br><br>
Make sure that path is mounted into the container, or use
"Browse Filesystem" to pick a course manually.
</div>`;
return;
}
if (groupNames.length === 0) {
container.innerHTML = `<div class="empty-state">
No courses found under <code>${data.library_path}</code>.
Drop course folders in there, or use "Browse Filesystem" instead.
</div>`;
return;
}
container.innerHTML = groupNames.map(group => `
<div class="group">
<div class="group-title">📁 ${group}</div>
${data.groups[group].map(course => `
<div class="course-card" onclick="loadCourse('${course.path.replace(/'/g, "\\'")}')">
<span>🎓 ${course.name}</span>
<span class="meta">${course.media_files} media file${course.media_files === 1 ? '' : 's'}</span>
</div>
`).join('')}
</div>
`).join('');
});
}
// ---- Filesystem view: original full-path browser (fallback) ----
function loadDirectories(path = '') {
fetch(`/browse?path=${encodeURIComponent(path)}`)
.then(r => r.json())
.then(data => {
const browser = document.getElementById('browser');
browser.innerHTML = `
<h3>Current: ${data.current_path}</h3>
${data.parent_path ? `<div class="directory" onclick="loadDirectories('${data.parent_path}')">📁 .. (Parent)</div>` : ''}
${data.directories.map(dir => `
<div class="directory ${dir.is_course_candidate ? 'course-candidate' : ''}"
onclick="${dir.is_course_candidate ? `loadCourse('${dir.path}')` : `loadDirectories('${dir.path}')`}">
📁 ${dir.name} ${dir.media_files > 0 ? `(${dir.media_files} media files)` : ''}
</div>
`).join('')}
`;
});
}
function loadCourse(path) {
fetch('/load_course', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({course_path: path})
})
.then(r => r.json())
.then(data => {
if (data.success) {
location.reload();
} else {
alert('Error: ' + data.error);
}
});
}
loadLibrary();
</script>
</div>
</body>
</html>