testingwebapp fixes, weeek tasks 2948-2958

This commit is contained in:
Константин Лебединский
2026-05-04 21:29:23 +05:00
parent 0229bc250b
commit 1ea83aa6b4
9 changed files with 1035 additions and 214 deletions
+81 -4
View File
@@ -19,16 +19,37 @@
{% block content %}
<section class="legacy-list-shell">
<h1 class="font-headline legacy-list-title">Тесты</h1>
<div class="legacy-list-toolbar">
<button id="btn-create-test" class="btn btn-ghost" type="button">
<div class="legacy-list-toolbar legacy-list-toolbar--wrap flex flex-wrap items-center gap-3">
<div class="flex flex-col sm:flex-row flex-1 min-w-0 gap-2 sm:gap-3">
<label class="flex flex-col gap-1 flex-1 min-w-[12rem] max-w-md">
<span class="text-xs font-medium text-ink-600">Поиск по названию</span>
<input id="catalog-search" type="search" autocomplete="off" placeholder="Начните вводить…"
class="rounded-lg border border-ink-300 px-3 py-2 text-sm w-full
focus:border-brand-500 focus:ring-2 focus:ring-brand-500/20" />
</label>
<label class="flex flex-col gap-1 w-full sm:w-52 shrink-0">
<span class="text-xs font-medium text-ink-600">Автор</span>
<select id="catalog-author"
class="rounded-lg border border-ink-300 px-3 py-2 text-sm w-full bg-white
focus:border-brand-500 focus:ring-2 focus:ring-brand-500/20">
<option value="">Все авторы</option>
</select>
</label>
</div>
<button id="btn-create-test" class="btn btn-ghost shrink-0" type="button">
Создать
</button>
</div>
<p id="catalog-filter-empty" class="text-sm text-ink-500 mt-2 hidden" role="status"></p>
{% if visible %}
<ul class="list-stack" aria-label="Тесты в общем списке">
{% for t in visible %}
<li class="list-row list-row--split">
<li class="list-row list-row--split list-row--catalog"
data-catalog-row
data-title-lower="{{ (t.title or '')|lower }}"
data-author-id="{{ t.created_by or '' }}"
data-author-name="{{ (t.author_full_name or '—')|e }}">
<div class="list-row__main">
<a href="{{ url_for('tests.tests_editor_page', test_id=t.id) }}" class="list-row__link">
<span class="list-row__title">{{ t.title }}</span>
@@ -55,7 +76,11 @@
<h2 class="font-headline legacy-list-subtitle">Скрытые вами из списка</h2>
<ul class="list-stack" aria-label="Скрытые тесты автора">
{% for t in hidden %}
<li class="list-row list-row--split list-row--hidden">
<li class="list-row list-row--split list-row--hidden list-row--catalog"
data-catalog-row
data-title-lower="{{ (t.title or '')|lower }}"
data-author-id="{{ t.created_by or '' }}"
data-author-name="{{ (t.author_full_name or '—')|e }}">
<div class="list-row__main">
<a href="{{ url_for('tests.tests_editor_page', test_id=t.id) }}" class="list-row__link">
<span class="list-row__title">{{ t.title }}</span>
@@ -124,6 +149,58 @@ class="m-0 p-0 w-full sm:w-full sm:max-w-md
const dlg = document.getElementById('dlg-create');
const titleEl = document.getElementById('new-test-title');
const descEl = document.getElementById('new-test-desc');
const catalogSearch = document.getElementById('catalog-search');
const catalogAuthor = document.getElementById('catalog-author');
const catalogEmpty = document.getElementById('catalog-filter-empty');
(function initCatalogFilter() {
if (!catalogSearch || !catalogAuthor) return;
const rows = Array.from(document.querySelectorAll('[data-catalog-row]'));
const byAuthor = new Map();
rows.forEach((row) => {
const id = (row.dataset.authorId || '').trim();
const name = (row.dataset.authorName || '').trim() || '—';
if (id && !byAuthor.has(id)) byAuthor.set(id, name);
});
const sorted = Array.from(byAuthor.entries()).sort((a, b) => a[1].localeCompare(b[1], 'ru'));
sorted.forEach(([id, name]) => {
const opt = document.createElement('option');
opt.value = id;
opt.textContent = name;
catalogAuthor.appendChild(opt);
});
function applyFilter() {
const q = (catalogSearch.value || '').trim().toLowerCase();
const author = (catalogAuthor.value || '').trim();
let visibleCount = 0;
rows.forEach((row) => {
const title = row.dataset.titleLower || '';
const aid = (row.dataset.authorId || '').trim();
const matchQ = !q || title.includes(q);
const matchA = !author || aid === author;
const show = matchQ && matchA;
row.style.display = show ? '' : 'none';
if (show) visibleCount += 1;
});
if (catalogEmpty) {
if (rows.length && visibleCount === 0) {
catalogEmpty.textContent = 'Ничего не найдено — измените запрос или фильтр.';
catalogEmpty.classList.remove('hidden');
} else {
catalogEmpty.textContent = '';
catalogEmpty.classList.add('hidden');
}
}
}
let t = null;
catalogSearch.addEventListener('input', () => {
clearTimeout(t);
t = setTimeout(applyFilter, 120);
});
catalogAuthor.addEventListener('change', applyFilter);
})();
document.getElementById('btn-create-test').addEventListener('click', () => {
titleEl.value = '';