diff --git a/frontend/src/pages/CustomQuizPage.css b/frontend/src/pages/CustomQuizPage.css index e50d731..63cd3be 100644 --- a/frontend/src/pages/CustomQuizPage.css +++ b/frontend/src/pages/CustomQuizPage.css @@ -10,3 +10,25 @@ .custom-test-settings input, .custom-test-settings select { width: 100%; padding: 10px; border: 1px solid var(--border); border-radius: 6px; background: var(--input-bg); color: var(--text); } .custom-test form > button { margin: 8px 8px 0 0; } .custom-test [role=alert] { color: var(--wrong-fg); } +.custom-test-layout { display: grid; grid-template-columns: 300px 1fr; gap: 16px; align-items: start; border: none; background: none; box-shadow: none; padding: 0; } +.custom-test-filters { padding: 14px; } +.custom-test-filters h2 { margin: 0 0 4px; font-size: 1rem; } +.custom-test-filters p { color: var(--text-muted); font-size: .78rem; margin: 0 0 10px; } +.custom-test-filters-toggle { display: none; } +.custom-test-tree { list-style: none; margin: 8px 0; padding: 0; max-height: 60vh; overflow-y: auto; } +.custom-test-tree ul { list-style: none; margin: 0 0 0 16px; padding: 0; } +.custom-test-tree li { margin: 2px 0; } +.custom-test-tree label { display: flex; gap: 6px; align-items: baseline; font-size: .84rem; cursor: pointer; } +.custom-test-tree summary { cursor: pointer; list-style: none; } +.custom-test-tree summary::-webkit-details-marker { display: none; } +.custom-test-branch::before { content: '▸ '; font-size: .7rem; color: var(--text-muted); } +details[open] > .custom-test-branch::before { content: '▾ '; } +.custom-test-main { display: flex; flex-direction: column; gap: 10px; } +.custom-test-settings { display: flex; flex-direction: column; gap: 10px; padding: 16px; } +.custom-test-settings label { display: flex; flex-direction: column; gap: 4px; font-size: .84rem; } +@media (max-width: 760px) { + .custom-test-layout { grid-template-columns: 1fr; } + .custom-test-filters-toggle { display: inline-block; margin-bottom: 8px; } + .custom-test-filters-body { display: none; } + .custom-test-filters-body.open { display: block; } +} diff --git a/frontend/src/pages/CustomQuizPage.jsx b/frontend/src/pages/CustomQuizPage.jsx index 92168ab..22a9b72 100644 --- a/frontend/src/pages/CustomQuizPage.jsx +++ b/frontend/src/pages/CustomQuizPage.jsx @@ -67,52 +67,90 @@ export default function CustomQuizPage() { } finally { setSubmitting(false) } } + const [catSearch, setCatSearch] = useState('') + const [filtersOpen, setFiltersOpen] = useState(true) + const childrenOf = {} + for (const cat of categories) { + ;(childrenOf[cat.parent_id || 0] ||= []).push(cat) + } + const visibleCategories = categories.filter(cat => + [cat.name, ...(cat.breadcrumbs || []).map(b => b.name)].join(' ').toLowerCase().includes(catSearch.toLowerCase())) + const descendantSelected = (cat) => { + const ids = [] + const walk = (id) => { for (const child of childrenOf[id] || []) { ids.push(child.id); walk(child.id) } } + walk(cat.id) + return ids.some(id => categoryIds.includes(id)) + } + const renderTree = (parentId) => { + const branch = (childrenOf[parentId] || []).filter(cat => visibleCategories.includes(cat)) + if (!branch.length) return null + return
Choose questions from your bank, {user?.name || 'learner'}.
-