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:
@@ -57,39 +57,43 @@
|
||||
<span class="material-symbols-outlined text-brand-600">quiz</span>
|
||||
<span>Тестирование</span>
|
||||
</a>
|
||||
<nav class="flex items-center gap-2 text-sm">
|
||||
<nav class="flex items-center gap-1 sm:gap-2 text-sm">
|
||||
{% if current_user %}
|
||||
<a href="{{ url_for('tests.tests_list_page') }}"
|
||||
class="hidden sm:inline-flex items-center gap-1 px-2 py-1 rounded-lg
|
||||
text-ink-700 hover:bg-ink-100">
|
||||
class="inline-flex items-center justify-center gap-1
|
||||
min-w-10 min-h-10 px-2 sm:px-3 rounded-lg
|
||||
text-ink-700 hover:bg-ink-100"
|
||||
title="Каталог тестов" aria-label="Каталог тестов">
|
||||
<span class="material-symbols-outlined text-base">list_alt</span>
|
||||
Тесты
|
||||
<span class="hidden sm:inline">Тесты</span>
|
||||
</a>
|
||||
<a href="{{ url_for('settings.settings_page') }}"
|
||||
class="hidden sm:inline-flex items-center gap-1 px-2 py-1 rounded-lg
|
||||
text-ink-700 hover:bg-ink-100">
|
||||
class="inline-flex items-center justify-center gap-1
|
||||
min-w-10 min-h-10 px-2 sm:px-3 rounded-lg
|
||||
text-ink-700 hover:bg-ink-100"
|
||||
title="Настройки" aria-label="Настройки">
|
||||
<span class="material-symbols-outlined text-base">settings</span>
|
||||
Настройки
|
||||
<span class="hidden sm:inline">Настройки</span>
|
||||
</a>
|
||||
{% endif %}
|
||||
{% if current_user %}
|
||||
<span class="hidden sm:inline text-ink-500">
|
||||
<span class="hidden md:inline text-ink-500">
|
||||
{{ current_user.full_name or current_user.login }}
|
||||
<span class="text-ink-300">·</span>
|
||||
<span class="text-brand-700">{{ current_user.role }}</span>
|
||||
</span>
|
||||
<form method="post" action="{{ url_for('auth.logout') }}" class="inline">
|
||||
<button type="submit"
|
||||
class="inline-flex items-center gap-1 px-2 py-1 rounded-lg
|
||||
text-ink-700 hover:bg-ink-100 transition">
|
||||
class="inline-flex items-center justify-center gap-1
|
||||
min-w-10 min-h-10 px-2 sm:px-3 rounded-lg
|
||||
text-ink-700 hover:bg-ink-100 transition"
|
||||
title="Выйти" aria-label="Выйти">
|
||||
<span class="material-symbols-outlined text-base">logout</span>
|
||||
<span class="hidden sm:inline">Выйти</span>
|
||||
</button>
|
||||
</form>
|
||||
{% else %}
|
||||
<a href="{{ url_for('auth.login_page') }}"
|
||||
class="inline-flex items-center gap-1 px-2 py-1 rounded-lg
|
||||
text-brand-700 hover:bg-brand-50 transition">
|
||||
class="inline-flex items-center gap-1 px-3 py-2 rounded-lg
|
||||
text-brand-700 hover:bg-brand-50 transition min-h-10">
|
||||
<span class="material-symbols-outlined text-base">login</span>
|
||||
Войти
|
||||
</a>
|
||||
|
||||
@@ -3,187 +3,236 @@
|
||||
|
||||
{% block content %}
|
||||
<div id="editor-root"
|
||||
class="space-y-4 sm:space-y-5 pb-24"
|
||||
data-test-id="{{ test_id }}"
|
||||
data-initial='{{ content | tojson | safe }}'>
|
||||
<!-- Шапка: название/описание/проходной балл -->
|
||||
<section class="rounded-2xl bg-white shadow-sm border border-ink-300/60 p-5">
|
||||
<div class="flex items-start justify-between gap-3 flex-wrap">
|
||||
<div class="flex-1 min-w-[260px]">
|
||||
<label class="block">
|
||||
<span class="text-xs font-medium text-ink-500 uppercase">Название</span>
|
||||
<input id="test-title" type="text" maxlength="200"
|
||||
class="mt-1 w-full rounded-lg border border-ink-300 px-3 py-2 text-lg font-semibold
|
||||
focus:border-brand-500 focus:ring-2 focus:ring-brand-500/20" />
|
||||
</label>
|
||||
<label class="block mt-3">
|
||||
<span class="text-xs font-medium text-ink-500 uppercase">Описание</span>
|
||||
<textarea id="test-description" rows="2"
|
||||
class="mt-1 w-full rounded-lg border border-ink-300 px-3 py-2
|
||||
focus:border-brand-500 focus:ring-2 focus:ring-brand-500/20"></textarea>
|
||||
</label>
|
||||
</div>
|
||||
<div class="w-44">
|
||||
<label class="block">
|
||||
<span class="text-xs font-medium text-ink-500 uppercase">Проходной балл, %</span>
|
||||
<input id="test-threshold" type="number" min="0" max="100" step="1"
|
||||
class="mt-1 w-full rounded-lg border border-ink-300 px-3 py-2
|
||||
focus:border-brand-500 focus:ring-2 focus:ring-brand-500/20" />
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{# ── 1. Шапка теста ─────────────────────────────────────────── #}
|
||||
<section class="rounded-2xl bg-white shadow-sm border border-ink-300/60 p-4 sm:p-5">
|
||||
<h2 class="text-xs font-medium text-ink-500 uppercase tracking-wide">Тест</h2>
|
||||
|
||||
<label class="mt-2 block">
|
||||
<span class="sr-only">Название</span>
|
||||
<input id="test-title" type="text" maxlength="200" placeholder="Название теста"
|
||||
class="w-full rounded-lg border border-ink-300 px-3 py-3 text-lg font-semibold
|
||||
focus:border-brand-500 focus:ring-2 focus:ring-brand-500/20" />
|
||||
</label>
|
||||
|
||||
<label class="mt-3 block">
|
||||
<span class="text-xs font-medium text-ink-500">Описание</span>
|
||||
<textarea id="test-description" rows="2" placeholder="Краткое описание (необязательно)"
|
||||
class="mt-1 w-full rounded-lg border border-ink-300 px-3 py-2
|
||||
focus:border-brand-500 focus:ring-2 focus:ring-brand-500/20"></textarea>
|
||||
</label>
|
||||
|
||||
<label class="mt-3 flex items-center justify-between gap-3">
|
||||
<span class="text-xs font-medium text-ink-500">Проходной балл, %</span>
|
||||
<input id="test-threshold" type="number" min="0" max="100" step="1"
|
||||
inputmode="numeric"
|
||||
class="w-24 text-right rounded-lg border border-ink-300 px-3 py-2
|
||||
focus:border-brand-500 focus:ring-2 focus:ring-brand-500/20" />
|
||||
</label>
|
||||
</section>
|
||||
|
||||
<!-- AI-панель -->
|
||||
<section class="mt-4 rounded-2xl bg-brand-50/60 border border-brand-100 p-4">
|
||||
{# ── 2. AI-помощник ─────────────────────────────────────────── #}
|
||||
<section class="rounded-2xl bg-brand-50/60 border border-brand-100 p-4 sm:p-5">
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="material-symbols-outlined text-brand-600">auto_awesome</span>
|
||||
<h2 class="font-semibold text-brand-700">AI-помощник</h2>
|
||||
</div>
|
||||
<p class="mt-1 text-sm text-ink-700">
|
||||
Сгенерировать вопросы по текущей сетке (число вопросов и вариантов берётся из таблицы ниже).
|
||||
</p>
|
||||
<div class="mt-3 flex flex-wrap gap-2 items-center">
|
||||
<button id="ai-generate-test"
|
||||
class="inline-flex items-center gap-2 px-3 py-2 rounded-lg
|
||||
bg-brand-600 hover:bg-brand-700 text-white text-sm">
|
||||
<span class="material-symbols-outlined text-base">stars</span>
|
||||
Сгенерировать по сетке
|
||||
</button>
|
||||
<button id="ai-generate-by-title"
|
||||
class="inline-flex items-center gap-2 px-3 py-2 rounded-lg
|
||||
bg-white border border-brand-300/60 text-brand-700 hover:bg-brand-50 text-sm">
|
||||
<span class="material-symbols-outlined text-base">edit_note</span>
|
||||
Сгенерировать по названию
|
||||
</button>
|
||||
<button id="ai-check"
|
||||
class="inline-flex items-center gap-2 px-3 py-2 rounded-lg
|
||||
bg-white border border-ink-300/60 hover:border-brand-300 text-sm">
|
||||
<span class="material-symbols-outlined text-base">fact_check</span>
|
||||
Проверить тест
|
||||
</button>
|
||||
<button id="ai-improve"
|
||||
class="inline-flex items-center gap-2 px-3 py-2 rounded-lg
|
||||
bg-white border border-ink-300/60 hover:border-brand-300 text-sm">
|
||||
<span class="material-symbols-outlined text-base">tune</span>
|
||||
Улучшить тест
|
||||
</button>
|
||||
<label class="inline-flex items-center gap-2 px-3 py-2 rounded-lg
|
||||
bg-white border border-ink-300/60 hover:border-brand-300 text-sm cursor-pointer">
|
||||
|
||||
{# Группа A — генерация. Главные действия. На sm+ — в одну строку. #}
|
||||
<div class="mt-3">
|
||||
<p class="text-xs font-medium text-ink-500 uppercase tracking-wide">Создать вопросы</p>
|
||||
<div class="mt-2 grid grid-cols-1 sm:grid-cols-2 gap-2">
|
||||
<button id="ai-generate-by-title"
|
||||
class="inline-flex items-center justify-center gap-2 px-3 py-3 rounded-lg
|
||||
bg-brand-600 hover:bg-brand-700 text-white text-sm font-medium min-h-11">
|
||||
<span class="material-symbols-outlined text-base">edit_note</span>
|
||||
По названию
|
||||
</button>
|
||||
<button id="ai-generate-test"
|
||||
class="inline-flex items-center justify-center gap-2 px-3 py-3 rounded-lg
|
||||
bg-white border border-brand-300/60 text-brand-700 hover:bg-brand-50
|
||||
text-sm font-medium min-h-11">
|
||||
<span class="material-symbols-outlined text-base">stars</span>
|
||||
По текущей сетке
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{# Группа B — анализ существующего. #}
|
||||
<div class="mt-4">
|
||||
<p class="text-xs font-medium text-ink-500 uppercase tracking-wide">Улучшить существующее</p>
|
||||
<div class="mt-2 grid grid-cols-2 gap-2">
|
||||
<button id="ai-check"
|
||||
class="inline-flex items-center justify-center gap-2 px-3 py-3 rounded-lg
|
||||
bg-white border border-ink-300/60 hover:border-brand-300
|
||||
text-sm min-h-11">
|
||||
<span class="material-symbols-outlined text-base">fact_check</span>
|
||||
Проверить
|
||||
</button>
|
||||
<button id="ai-improve"
|
||||
class="inline-flex items-center justify-center gap-2 px-3 py-3 rounded-lg
|
||||
bg-white border border-ink-300/60 hover:border-brand-300
|
||||
text-sm min-h-11">
|
||||
<span class="material-symbols-outlined text-base">tune</span>
|
||||
Улучшить
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{# Группа C — импорт. #}
|
||||
<div class="mt-4">
|
||||
<p class="text-xs font-medium text-ink-500 uppercase tracking-wide">Импортировать</p>
|
||||
<label class="mt-2 inline-flex w-full items-center justify-center gap-2 px-3 py-3
|
||||
rounded-lg bg-white border border-ink-300/60 hover:border-brand-300
|
||||
text-sm cursor-pointer min-h-11">
|
||||
<span class="material-symbols-outlined text-base text-brand-600">upload_file</span>
|
||||
<span>Импорт документа</span>
|
||||
<span>Загрузить документ (PDF, DOCX, TXT, MD)</span>
|
||||
<input id="ai-import-file" type="file" accept=".pdf,.docx,.txt,.md" class="hidden" />
|
||||
</label>
|
||||
<span id="ai-status" class="text-sm text-ink-500"></span>
|
||||
<p class="mt-1.5 text-xs text-ink-500">
|
||||
До 16 МБ. AI извлечёт текст и предложит черновик теста.
|
||||
</p>
|
||||
</div>
|
||||
<p class="mt-2 text-xs text-ink-500">
|
||||
Поддерживаются PDF, DOCX, TXT, MD (до 16 МБ). AI извлечёт текст и предложит черновик теста.
|
||||
</p>
|
||||
|
||||
<p id="ai-status" class="mt-3 text-sm text-ink-500 min-h-[1.25rem]"></p>
|
||||
</section>
|
||||
|
||||
<!-- Список вопросов -->
|
||||
<section class="mt-4">
|
||||
{# ── 3. Вопросы ─────────────────────────────────────────────── #}
|
||||
<section>
|
||||
<div class="flex items-center justify-between gap-2 px-1">
|
||||
<h2 class="font-semibold">Вопросы (<span id="q-count">0</span>)</h2>
|
||||
<button id="add-question"
|
||||
class="inline-flex items-center gap-1 px-3 py-1.5 rounded-lg
|
||||
bg-white border border-ink-300/60 hover:border-brand-300 text-sm">
|
||||
class="inline-flex items-center gap-1 px-3 py-2 rounded-lg
|
||||
bg-white border border-ink-300/60 hover:border-brand-300 text-sm min-h-10">
|
||||
<span class="material-symbols-outlined text-base">add</span>
|
||||
Добавить вопрос
|
||||
<span class="hidden sm:inline">Добавить вопрос</span>
|
||||
<span class="sm:hidden">Добавить</span>
|
||||
</button>
|
||||
</div>
|
||||
<ol id="questions" class="mt-3 space-y-3"></ol>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<!-- Footer: сохранение / активность цепочки -->
|
||||
<section class="sticky bottom-0 z-20 mt-6 -mx-4 px-4 py-3
|
||||
bg-white/90 backdrop-blur border-t border-ink-300/60
|
||||
flex items-center justify-between gap-2 flex-wrap">
|
||||
<label class="inline-flex items-center gap-2 text-sm">
|
||||
{# ── Sticky-footer: «Цепочка активна» + «Сохранить» ────────────── #}
|
||||
<div class="fixed bottom-0 inset-x-0 z-30 bg-white/95 backdrop-blur border-t border-ink-300/60
|
||||
pb-[env(safe-area-inset-bottom)]">
|
||||
<div class="mx-auto max-w-6xl px-4 py-3
|
||||
flex items-center justify-between gap-3">
|
||||
<label class="inline-flex items-center gap-2 text-sm min-w-0">
|
||||
<input id="chain-active" type="checkbox"
|
||||
class="rounded border-ink-300 text-brand-600 focus:ring-brand-500" />
|
||||
<span>Цепочка активна (виден в каталоге)</span>
|
||||
<span class="truncate">Цепочка активна</span>
|
||||
</label>
|
||||
<div class="flex items-center gap-2">
|
||||
<span id="save-status" class="text-sm text-ink-500"></span>
|
||||
<div class="flex items-center gap-2 shrink-0">
|
||||
<a href="{{ url_for('tests.tests_list_page') }}"
|
||||
class="px-3 py-2 rounded-lg text-ink-700 hover:bg-ink-100 text-sm">К каталогу</a>
|
||||
class="hidden sm:inline-flex px-3 py-2 rounded-lg text-ink-700 hover:bg-ink-100 text-sm">
|
||||
К каталогу
|
||||
</a>
|
||||
<button id="save-draft"
|
||||
class="inline-flex items-center gap-2 px-4 py-2 rounded-lg
|
||||
bg-brand-600 hover:bg-brand-700 text-white">
|
||||
class="inline-flex items-center gap-2 px-4 py-2.5 rounded-lg
|
||||
bg-brand-600 hover:bg-brand-700 text-white font-medium min-h-11">
|
||||
<span class="material-symbols-outlined text-base">save</span>
|
||||
Сохранить
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
<p id="save-status" class="mx-auto max-w-6xl px-4 pb-2 text-xs text-ink-500"></p>
|
||||
</div>
|
||||
|
||||
<!-- Шаблон вопроса -->
|
||||
{# ── Шаблон вопроса ─────────────────────────────────────────────── #}
|
||||
<template id="tpl-question">
|
||||
<li class="rounded-xl bg-white border border-ink-300/60 p-4 q-item">
|
||||
<div class="flex items-start justify-between gap-2">
|
||||
<span class="text-xs uppercase tracking-wide text-ink-500 q-num">Вопрос #</span>
|
||||
<div class="flex items-center gap-1">
|
||||
<button class="q-up p-1 rounded hover:bg-ink-100" title="Выше">
|
||||
<li class="rounded-xl bg-white border border-ink-300/60 p-3 sm:p-4 q-item">
|
||||
{# Шапка карточки вопроса: номер слева, кнопки справа. #}
|
||||
<div class="flex items-center justify-between gap-2">
|
||||
<span class="inline-flex items-center px-2 py-0.5 rounded-md
|
||||
bg-brand-50 text-brand-700 text-xs font-medium q-num">Вопрос #</span>
|
||||
<div class="flex items-center gap-0.5">
|
||||
<button class="q-up p-2 rounded hover:bg-ink-100 min-w-10 min-h-10"
|
||||
title="Выше" aria-label="Поднять выше">
|
||||
<span class="material-symbols-outlined text-base">arrow_upward</span>
|
||||
</button>
|
||||
<button class="q-down p-1 rounded hover:bg-ink-100" title="Ниже">
|
||||
<button class="q-down p-2 rounded hover:bg-ink-100 min-w-10 min-h-10"
|
||||
title="Ниже" aria-label="Опустить ниже">
|
||||
<span class="material-symbols-outlined text-base">arrow_downward</span>
|
||||
</button>
|
||||
<button class="q-delete p-1 rounded hover:bg-red-50 text-red-600" title="Удалить">
|
||||
<button class="q-delete p-2 rounded hover:bg-red-50 text-red-600 min-w-10 min-h-10"
|
||||
title="Удалить" aria-label="Удалить вопрос">
|
||||
<span class="material-symbols-outlined text-base">delete</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<textarea class="q-text mt-2 w-full rounded-lg border border-ink-300 px-3 py-2
|
||||
|
||||
<textarea class="q-text mt-3 w-full rounded-lg border border-ink-300 px-3 py-2
|
||||
focus:border-brand-500 focus:ring-2 focus:ring-brand-500/20"
|
||||
rows="2" placeholder="Формулировка вопроса"></textarea>
|
||||
|
||||
<div class="mt-2 flex items-center justify-between gap-2 flex-wrap text-sm">
|
||||
<label class="inline-flex items-center gap-2">
|
||||
<input type="checkbox" class="q-multi rounded border-ink-300 text-brand-600 focus:ring-brand-500" />
|
||||
{# Тип ответа + AI — две полные строки на мобиле, в строку на sm+. #}
|
||||
<div class="mt-3 flex flex-col sm:flex-row sm:items-center sm:justify-between gap-2 text-sm">
|
||||
<label class="inline-flex items-center gap-2 min-h-9">
|
||||
<input type="checkbox"
|
||||
class="q-multi rounded border-ink-300 text-brand-600 focus:ring-brand-500" />
|
||||
<span>Несколько правильных ответов</span>
|
||||
</label>
|
||||
<button class="q-ai inline-flex items-center gap-1 px-2 py-1 rounded-lg
|
||||
bg-brand-50 hover:bg-brand-100 text-brand-700">
|
||||
<button class="q-ai inline-flex items-center justify-center gap-1 px-2.5 py-2 rounded-lg
|
||||
bg-brand-50 hover:bg-brand-100 text-brand-700 text-sm min-h-10">
|
||||
<span class="material-symbols-outlined text-base">auto_awesome</span>
|
||||
AI: вопрос/переформулировать
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<ul class="q-options mt-3 space-y-2"></ul>
|
||||
<div class="mt-2">
|
||||
<button class="q-add-option inline-flex items-center gap-1 text-sm text-brand-700 hover:underline">
|
||||
<span class="material-symbols-outlined text-base">add</span> Добавить вариант
|
||||
</button>
|
||||
</div>
|
||||
<button class="q-add-option mt-2 inline-flex items-center gap-1 px-2 py-2 rounded
|
||||
text-sm text-brand-700 hover:bg-brand-50 min-h-10">
|
||||
<span class="material-symbols-outlined text-base">add</span>
|
||||
Добавить вариант
|
||||
</button>
|
||||
</li>
|
||||
</template>
|
||||
|
||||
<!-- Модалка результата AI-проверки/улучшения -->
|
||||
<dialog id="ai-modal" class="rounded-2xl p-0 max-w-3xl w-full backdrop:bg-black/40">
|
||||
<div class="p-5">
|
||||
<div class="flex items-center justify-between gap-3">
|
||||
<h3 id="ai-modal-title" class="text-lg font-semibold">AI</h3>
|
||||
<button id="ai-modal-close" class="p-1 rounded hover:bg-ink-100">
|
||||
<span class="material-symbols-outlined">close</span>
|
||||
</button>
|
||||
</div>
|
||||
<div id="ai-modal-body" class="mt-3 max-h-[70vh] overflow-y-auto"></div>
|
||||
<div id="ai-modal-actions" class="mt-4 flex items-center justify-end gap-2"></div>
|
||||
</div>
|
||||
</dialog>
|
||||
|
||||
{# ── Шаблон варианта ────────────────────────────────────────────── #}
|
||||
<template id="tpl-option">
|
||||
<li class="flex items-center gap-2 opt-item">
|
||||
<input type="checkbox" class="opt-correct rounded border-ink-300 text-brand-600 focus:ring-brand-500" />
|
||||
<input type="text" class="opt-text flex-1 rounded-lg border border-ink-300 px-3 py-1.5
|
||||
focus:border-brand-500 focus:ring-2 focus:ring-brand-500/20"
|
||||
{# Чекбокс «Правильный» — обёрнут в большой tap-target. #}
|
||||
<label class="inline-flex items-center justify-center w-10 h-10 shrink-0 cursor-pointer
|
||||
rounded hover:bg-ink-100" title="Правильный ответ">
|
||||
<input type="checkbox"
|
||||
class="opt-correct w-5 h-5 rounded border-ink-300 text-brand-600 focus:ring-brand-500" />
|
||||
</label>
|
||||
<input type="text"
|
||||
class="opt-text flex-1 min-w-0 rounded-lg border border-ink-300 px-3 py-2
|
||||
focus:border-brand-500 focus:ring-2 focus:ring-brand-500/20"
|
||||
placeholder="Вариант ответа" />
|
||||
<button class="opt-delete p-1 rounded hover:bg-red-50 text-red-600" title="Удалить">
|
||||
<button class="opt-delete shrink-0 w-10 h-10 inline-flex items-center justify-center
|
||||
rounded hover:bg-red-50 text-red-600"
|
||||
title="Удалить" aria-label="Удалить вариант">
|
||||
<span class="material-symbols-outlined text-base">close</span>
|
||||
</button>
|
||||
</li>
|
||||
</template>
|
||||
|
||||
{# ── Модалка результата AI-проверки/улучшения (fullscreen на мобиле) ── #}
|
||||
<dialog id="ai-modal"
|
||||
class="m-0 p-0 w-full h-full sm:h-auto sm:max-w-3xl sm:w-full sm:max-h-[90vh]
|
||||
sm:rounded-2xl sm:m-auto bg-white backdrop:bg-black/50">
|
||||
<div class="flex flex-col h-full sm:max-h-[90vh]">
|
||||
<div class="flex items-center justify-between gap-3 px-4 sm:px-5 py-3 border-b border-ink-300/60">
|
||||
<h3 id="ai-modal-title" class="text-lg font-semibold truncate">AI</h3>
|
||||
<button id="ai-modal-close"
|
||||
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 id="ai-modal-body" class="flex-1 overflow-y-auto px-4 sm:px-5 py-4"></div>
|
||||
<div id="ai-modal-actions"
|
||||
class="px-4 sm:px-5 py-3 border-t border-ink-300/60
|
||||
flex items-center justify-end gap-2 flex-wrap
|
||||
pb-[max(env(safe-area-inset-bottom),0.75rem)]"></div>
|
||||
</div>
|
||||
</dialog>
|
||||
{% endblock %}
|
||||
|
||||
{% block scripts %}
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user