ui(mobile): полировка расположения редактора и каталога

Только layout/spacing/touch-targets, без изменения цветовой палитры
и типографики.

editor.html
- Шапка теста: «Название» — отдельной строкой, large input; «Описание»
  ниже; «Проходной балл» — компактная пара label+input справа,
  inputmode="numeric".
- AI-панель разбита на 3 группы с подзаголовками: «Создать вопросы»
  (По названию / По текущей сетке), «Улучшить существующее»
  (Проверить / Улучшить), «Импортировать» (загрузка файла).
- Все основные кнопки — min-h-11, на мобиле во всю ширину/в гриде по 2.
- Карточка вопроса: бейдж-номер, кнопки up/down/delete по 40×40,
  textarea и опции — на всю ширину с min-w-0 чтобы не было overflow.
- Опции: чекбокс «Правильный» в 40×40 tap-target, input занимает flex,
  кнопка удаления 40×40.
- Footer переведён на fixed bottom с safe-area-inset-bottom; контент
  получает pb-24, чтобы не уезжал под футер.
- Модалка AI-результата теперь fullscreen на мобиле, sm:rounded-2xl
  на десктопе; шапка/тело/кнопки — отдельными зонами.

list.html
- Заголовок и кнопка «Создать тест» вертикально на мобиле,
  кнопка во всю ширину min-h-11.
- Карточка теста — целиком кликабельная (`<a>` обёртка), grid-cols-1
  по умолчанию, sm:2, lg:3.
- Модалка создания — fullscreen на мобиле с крестиком в шапке,
  safe-area-inset-bottom в футере.

base.html
- Ссылки «Тесты» и «Настройки» теперь видны и на мобиле как иконки
  (40×40 tap-target), подписи появляются с sm: брейкпоинта.
- Имя/роль пользователя — только с md+ (узкий мобильный экран).

Made-with: Cursor
This commit is contained in:
Константин Лебединский
2026-04-27 23:55:39 +05:00
parent 547840d671
commit 2d6d75fb3c
3 changed files with 227 additions and 161 deletions
+43 -30
View File
@@ -2,39 +2,42 @@
{% block title %}Тесты — каталог{% endblock %}
{% block content %}
<section class="rounded-2xl bg-white shadow-sm border border-ink-300/60 p-6">
<div class="flex items-center justify-between gap-4 flex-wrap">
<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-2xl font-semibold">Каталог тестов</h1>
<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 gap-2 px-4 py-2 rounded-lg
bg-brand-600 hover:bg-brand-700 text-white font-medium transition">
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 sm:grid-cols-2 lg:grid-cols-3">
<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 p-4 bg-white">
<div class="flex items-start justify-between gap-2">
<h3 class="font-semibold text-ink-900 line-clamp-2">{{ t.title }}</h3>
<span class="text-xs text-ink-500 shrink-0">v{{ t.version }}</span>
</div>
{% if t.description %}
<p class="mt-1 text-sm text-ink-500 line-clamp-3">{{ t.description }}</p>
{% endif %}
<p class="mt-2 text-xs text-ink-500">Автор: {{ t.author_full_name or '—' }}</p>
<div class="mt-3 flex items-center gap-2">
<a href="{{ url_for('tests.tests_editor_page', test_id=t.id) }}"
class="inline-flex items-center gap-1 text-sm text-brand-700 hover:underline">
<span class="material-symbols-outlined text-base">edit_note</span>
Открыть редактор
</a>
</div>
<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>
@@ -60,16 +63,23 @@
{% endif %}
</section>
<dialog id="dlg-create" class="rounded-2xl p-0 backdrop:bg-ink-900/40 max-w-md w-full">
<form method="dialog" class="bg-white rounded-2xl">
<div class="p-5 border-b border-ink-300/60">
<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
sm:rounded-2xl bg-white backdrop:bg-ink-900/50">
<form method="dialog" class="flex flex-col h-full sm:h-auto bg-white sm:rounded-2xl">
<div class="px-4 sm:px-5 py-3 border-b border-ink-300/60 flex items-center justify-between">
<h2 class="text-lg font-semibold">Новый тест</h2>
<button type="button" id="dlg-cancel-x"
class="p-2 rounded hover:bg-ink-100 min-w-10 min-h-10"
aria-label="Закрыть">
<span class="material-symbols-outlined">close</span>
</button>
</div>
<div class="p-5 space-y-3">
<div class="flex-1 overflow-y-auto px-4 sm:px-5 py-4 space-y-3">
<label class="block">
<span class="text-sm font-medium text-ink-700">Название</span>
<input id="new-test-title" type="text" required maxlength="200"
class="mt-1 w-full rounded-lg border border-ink-300 px-3 py-2
class="mt-1 w-full rounded-lg border border-ink-300 px-3 py-3
focus:border-brand-500 focus:ring-2 focus:ring-brand-500/20" />
</label>
<label class="block">
@@ -79,11 +89,13 @@
focus:border-brand-500 focus:ring-2 focus:ring-brand-500/20"></textarea>
</label>
</div>
<div class="p-4 border-t border-ink-300/60 flex justify-end gap-2 bg-ink-100/40 rounded-b-2xl">
<div class="px-4 sm:px-5 py-3 border-t border-ink-300/60 flex justify-end gap-2
bg-ink-100/40 sm:rounded-b-2xl
pb-[max(env(safe-area-inset-bottom),0.75rem)]">
<button type="button" id="dlg-cancel"
class="px-4 py-2 rounded-lg text-ink-700 hover:bg-ink-100">Отмена</button>
class="px-4 py-2.5 rounded-lg text-ink-700 hover:bg-ink-100 min-h-11">Отмена</button>
<button type="button" id="dlg-submit"
class="px-4 py-2 rounded-lg bg-brand-600 hover:bg-brand-700 text-white">
class="px-4 py-2.5 rounded-lg bg-brand-600 hover:bg-brand-700 text-white min-h-11">
Создать
</button>
</div>
@@ -106,6 +118,7 @@
setTimeout(() => titleEl.focus(), 50);
});
document.getElementById('dlg-cancel').addEventListener('click', () => dlg.close());
document.getElementById('dlg-cancel-x').addEventListener('click', () => dlg.close());
document.getElementById('dlg-submit').addEventListener('click', async () => {
const title = titleEl.value.trim();