Дорабоки интерфейса системы тестирования. Раздел 1 Шапка+Верхний brick
This commit is contained in:
@@ -2,66 +2,122 @@
|
||||
{% block title %}Тесты — каталог{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<section class="rounded-2xl bg-white shadow-sm border border-ink-300/60 p-4 sm:p-6">
|
||||
<div class="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-3">
|
||||
<div>
|
||||
<h1 class="text-xl sm:text-2xl font-semibold">Каталог тестов</h1>
|
||||
<p class="mt-1 text-sm text-ink-500">Активные тесты, к которым у вас есть доступ.</p>
|
||||
{% if ui_variant == 'legacy' %}
|
||||
<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">
|
||||
Создать
|
||||
</button>
|
||||
</div>
|
||||
<button id="btn-create-test"
|
||||
class="inline-flex items-center justify-center gap-2 px-4 py-3 rounded-lg
|
||||
bg-brand-600 hover:bg-brand-700 text-white font-medium transition
|
||||
min-h-11 w-full sm:w-auto">
|
||||
<span class="material-symbols-outlined text-base">add</span>
|
||||
Создать тест
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{% if visible %}
|
||||
<ul class="mt-5 grid gap-3 grid-cols-1 sm:grid-cols-2 lg:grid-cols-3">
|
||||
{% for t in visible %}
|
||||
<li class="rounded-xl border border-ink-300/60 hover:border-brand-300 hover:shadow-sm transition bg-white">
|
||||
<a href="{{ url_for('tests.tests_editor_page', test_id=t.id) }}"
|
||||
class="block p-4 active:bg-ink-100/40">
|
||||
<div class="flex items-start justify-between gap-2">
|
||||
<h3 class="font-semibold text-ink-900 line-clamp-2 min-w-0">{{ t.title }}</h3>
|
||||
<span class="text-xs text-ink-500 shrink-0 mt-0.5">v{{ t.version }}</span>
|
||||
{% if visible %}
|
||||
<ul class="list-stack" aria-label="Тесты в общем списке">
|
||||
{% for t in visible %}
|
||||
<li class="list-row list-row--split">
|
||||
<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>
|
||||
<span class="list-row__meta">
|
||||
{{ t.author_full_name or '—' }}
|
||||
<span class="list-row__meta-tail"> · v{{ t.version }}</span>
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
{% if t.description %}
|
||||
<p class="mt-1 text-sm text-ink-500 line-clamp-3">{{ t.description }}</p>
|
||||
{% endif %}
|
||||
<div class="mt-3 flex items-center justify-between gap-2 text-xs text-ink-500">
|
||||
<span class="truncate">Автор: {{ t.author_full_name or '—' }}</span>
|
||||
<span class="inline-flex items-center gap-1 text-brand-700">
|
||||
<span class="material-symbols-outlined text-sm">edit_note</span>
|
||||
Открыть
|
||||
</span>
|
||||
<div class="list-row__side">
|
||||
<button type="button" class="btn btn-ghost btn-start-pass" data-test-id="{{ t.id }}">Пройти</button>
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% else %}
|
||||
<p class="mt-5 text-ink-500 text-sm">Доступных тестов пока нет.</p>
|
||||
{% endif %}
|
||||
|
||||
{% if hidden %}
|
||||
<details class="mt-6 rounded-xl border border-ink-300/60 bg-ink-100/50 p-4">
|
||||
<summary class="cursor-pointer font-medium text-ink-700">
|
||||
Скрытые вами цепочки ({{ hidden|length }})
|
||||
</summary>
|
||||
<ul class="mt-3 space-y-2">
|
||||
{% for t in hidden %}
|
||||
<li class="flex items-center justify-between gap-2 text-sm">
|
||||
<span>{{ t.title }} <span class="text-ink-500">· v{{ t.version }}</span></span>
|
||||
<a href="{{ url_for('tests.tests_editor_page', test_id=t.id) }}"
|
||||
class="text-brand-700 hover:underline">Открыть</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</details>
|
||||
{% endif %}
|
||||
</section>
|
||||
{% else %}
|
||||
<p class="text-muted">Нет тестов</p>
|
||||
{% endif %}
|
||||
|
||||
{% if hidden %}
|
||||
<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">
|
||||
<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>
|
||||
<span class="list-row__meta">
|
||||
{{ t.author_full_name or '—' }}
|
||||
<span class="list-row__meta-tail"> · v{{ t.version }} · скрыт</span>
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="list-row__side">
|
||||
<button type="button" class="btn btn-ghost btn-start-pass" data-test-id="{{ t.id }}">Пройти</button>
|
||||
</div>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
</section>
|
||||
{% else %}
|
||||
<section class="rounded-2xl bg-white shadow-sm border border-ink-300/60 p-4 sm:p-6">
|
||||
<div class="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-3">
|
||||
<div>
|
||||
<h1 class="text-xl sm:text-2xl font-semibold">Каталог тестов</h1>
|
||||
<p class="mt-1 text-sm text-ink-500">Активные тесты, к которым у вас есть доступ.</p>
|
||||
</div>
|
||||
<button id="btn-create-test"
|
||||
class="inline-flex items-center justify-center gap-2 px-4 py-3 rounded-lg
|
||||
bg-brand-600 hover:bg-brand-700 text-white font-medium transition
|
||||
min-h-11 w-full sm:w-auto">
|
||||
<span class="material-symbols-outlined text-base">add</span>
|
||||
Создать тест
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{% if visible %}
|
||||
<ul class="mt-5 grid gap-3 grid-cols-1 sm:grid-cols-2 lg:grid-cols-3">
|
||||
{% for t in visible %}
|
||||
<li class="rounded-xl border border-ink-300/60 hover:border-brand-300 hover:shadow-sm transition bg-white">
|
||||
<a href="{{ url_for('tests.tests_editor_page', test_id=t.id) }}"
|
||||
class="block p-4 active:bg-ink-100/40">
|
||||
<div class="flex items-start justify-between gap-2">
|
||||
<h3 class="font-semibold text-ink-900 line-clamp-2 min-w-0">{{ t.title }}</h3>
|
||||
<span class="text-xs text-ink-500 shrink-0 mt-0.5">v{{ t.version }}</span>
|
||||
</div>
|
||||
{% if t.description %}
|
||||
<p class="mt-1 text-sm text-ink-500 line-clamp-3">{{ t.description }}</p>
|
||||
{% endif %}
|
||||
<div class="mt-3 flex items-center justify-between gap-2 text-xs text-ink-500">
|
||||
<span class="truncate">Автор: {{ t.author_full_name or '—' }}</span>
|
||||
<span class="inline-flex items-center gap-1 text-brand-700">
|
||||
<span class="material-symbols-outlined text-sm">edit_note</span>
|
||||
Открыть
|
||||
</span>
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% else %}
|
||||
<p class="mt-5 text-ink-500 text-sm">Доступных тестов пока нет.</p>
|
||||
{% endif %}
|
||||
|
||||
{% if hidden %}
|
||||
<details class="mt-6 rounded-xl border border-ink-300/60 bg-ink-100/50 p-4">
|
||||
<summary class="cursor-pointer font-medium text-ink-700">
|
||||
Скрытые вами цепочки ({{ hidden|length }})
|
||||
</summary>
|
||||
<ul class="mt-3 space-y-2">
|
||||
{% for t in hidden %}
|
||||
<li class="flex items-center justify-between gap-2 text-sm">
|
||||
<span>{{ t.title }} <span class="text-ink-500">· v{{ t.version }}</span></span>
|
||||
<a href="{{ url_for('tests.tests_editor_page', test_id=t.id) }}"
|
||||
class="text-brand-700 hover:underline">Открыть</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</details>
|
||||
{% endif %}
|
||||
</section>
|
||||
{% endif %}
|
||||
|
||||
<dialog id="dlg-create"
|
||||
class="m-0 p-0 w-full h-full sm:h-auto sm:max-w-md sm:w-full sm:m-auto
|
||||
@@ -136,6 +192,36 @@
|
||||
alert(e.message || 'Не удалось создать тест.');
|
||||
}
|
||||
});
|
||||
|
||||
const passButtons = Array.from(document.querySelectorAll('.btn-start-pass'));
|
||||
passButtons.forEach((btn) => {
|
||||
btn.addEventListener('click', async () => {
|
||||
const testId = btn.dataset.testId;
|
||||
if (!testId) return;
|
||||
btn.disabled = true;
|
||||
const oldText = btn.textContent;
|
||||
btn.textContent = '…';
|
||||
try {
|
||||
const r = await fetch(`/api/tests/${testId}/attempts/start`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({}),
|
||||
});
|
||||
let data = {};
|
||||
try { data = await r.json(); } catch (_) {}
|
||||
if (!r.ok || !data.attempt || !data.attempt.id) {
|
||||
// В Flask legacy контуре пока может отсутствовать отдельная UI-страница попытки.
|
||||
// Тогда ведём в карточку теста, чтобы пользователь не попадал на not_found.
|
||||
window.location.href = `/tests/${testId}/edit`;
|
||||
return;
|
||||
}
|
||||
window.location.href = `/tests/${testId}/attempt/${data.attempt.id}`;
|
||||
} catch (e) {
|
||||
window.location.href = `/tests/${testId}/edit`;
|
||||
return;
|
||||
}
|
||||
});
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
Reference in New Issue
Block a user