Files
TestingWebApp/flask_app/app/templates/tests/editor.html
T
Константин Лебединский 2d6d75fb3c 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
2026-04-27 23:55:39 +05:00

241 lines
13 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{% extends "base.html" %}
{% block title %}{{ content.test.title }} — редактор{% endblock %}
{% 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 }}'>
{# ── 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>
{# ── 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>
{# Группа 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>Загрузить документ (PDF, DOCX, TXT, MD)</span>
<input id="ai-import-file" type="file" accept=".pdf,.docx,.txt,.md" class="hidden" />
</label>
<p class="mt-1.5 text-xs text-ink-500">
До 16 МБ. AI извлечёт текст и предложит черновик теста.
</p>
</div>
<p id="ai-status" class="mt-3 text-sm text-ink-500 min-h-[1.25rem]"></p>
</section>
{# ── 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-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>
{# ── 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 class="truncate">Цепочка активна</span>
</label>
<div class="flex items-center gap-2 shrink-0">
<a href="{{ url_for('tests.tests_list_page') }}"
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.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>
</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-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-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-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-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>
{# Тип ответа + 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 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>
<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>
{# ── Шаблон варианта ────────────────────────────────────────────── #}
<template id="tpl-option">
<li class="flex items-center gap-2 opt-item">
{# Чекбокс «Правильный» — обёрнут в большой 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 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 %}
<script src="{{ url_for('static', filename='js/editor.js') }}"></script>
{% endblock %}