Дорабоки интерфейса системы тестирования. Раздел 1 Шапка+Верхний brick

This commit is contained in:
Константин Лебединский
2026-04-29 14:55:43 +05:00
parent 1c4dacbc85
commit eff3fda5b0
34 changed files with 3339 additions and 576 deletions
+147 -93
View File
@@ -3,88 +3,86 @@
{% block content %}
<div id="editor-root"
class="space-y-4 sm:space-y-5 pb-24"
class="space-y-4 sm:space-y-5 pb-24 {% if ui_variant == 'legacy' %}test-detail-page test-detail-page--with-fixed-actions{% endif %}"
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>
<section class="cabinet-brick cabinet-brick--hero hero-brick">
<div class="hero-brick__nav">
<a href="{{ url_for('tests.tests_list_page') }}" class="link-back">← к списку</a>
<span class="hero-brick__meta">
<span>Автор: <b id="intro-author">Вы</b></span>
<span class="hero-brick__sep">·</span>
<span>Обновлён: <span id="intro-updated"></span></span>
<span class="hero-brick__sep">·</span>
<span>Версия <span id="intro-version"></span></span>
</span>
</div>
<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>
<textarea id="test-title" maxlength="200" rows="1" placeholder="Название теста"
class="hero-brick__title font-headline"></textarea>
<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>
<textarea id="test-description" rows="2" placeholder="Краткое описание (необязательно)"
class="hero-brick__desc"></textarea>
<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>
<div class="hero-brick__chips">
<label class="hero-brick__chip">
<span>Порог зачёта</span>
<input id="test-threshold" type="number" min="0" max="100" step="1" inputmode="numeric" />
<span>%</span>
</label>
<span class="hero-brick__chip hero-brick__chip--readonly">
Вопросов: <b id="q-count">0</b>
</span>
<label class="hero-brick__chip">
<input id="chain-active" type="checkbox" />
<span>Активна в каталоге</span>
</label>
</div>
<div id="intro-fork-banner" class="callout callout--warning" data-fork-risk="{{ '1' if content.test.hasForkRisk else '0' }}" style="margin-top:0.85rem; display:none;">
При сохранении будет создана новая версия теста.
</div>
</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>
<details class="cabinet-disclosure cabinet-brick" open>
<summary class="cabinet-disclosure__summary">
<span class="cabinet-disclosure__summary-text">
<span class="cabinet-disclosure__summary-title font-headline">Вопросы</span>
<span class="cabinet-disclosure__summary-sub">Тексты, варианты и при необходимости загрузка из файла</span>
</span>
</summary>
<div class="cabinet-disclosure__body">
<section class="rounded-2xl bg-brand-50/60 border border-brand-100 p-4 sm:p-5 test-detail-ai-panel">
<div class="question-editor-block question-editor-block--first">
<h3 class="test-detail-subsection__title" style="margin-top:0;">Генерация сетки вопросов (ИИ)</h3>
<label class="block">
<span class="form-label">Тема</span>
<input id="ai-topic" type="text" class="form-input" placeholder="Например: Введение про LLM" />
</label>
<div class="mt-3 flex flex-wrap items-end gap-3">
<label class="block">
<span class="form-label">Вопросов</span>
<input id="ai-q-count" type="number" min="1" max="30" step="1" value="7"
class="form-input" style="width:90px;" />
</label>
<label class="block">
<span class="form-label">Вариантов</span>
<input id="ai-o-count" type="number" min="2" max="8" step="1" value="3"
class="form-input" style="width:90px;" />
</label>
<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>
По текущей сетке
class="btn btn-ghost" type="button" style="min-height:43px;">
Сгенерировать тест (ИИ)
</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>
<div class="question-editor-block test-detail-subsection test-detail-subsection--import">
<h3 class="test-detail-subsection__title">Документ в вопросы</h3>
<p class="muted test-detail-hint" style="margin-top:0;">
PDF, Word или текст — вставьте в черновик вопросов.
</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">
@@ -103,10 +101,11 @@
{# ── 3. Вопросы ─────────────────────────────────────────────── #}
<section>
<div class="flex items-center justify-between gap-2 px-1">
<h2 class="font-semibold">Вопросы (<span id="q-count">0</span>)</h2>
<h2 class="font-semibold">Вопросы (<span id="q-count-mirror">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">
bg-white border border-ink-300/60 hover:border-brand-300 text-sm min-h-10
btn btn-ghost btn--sm question-editor__add-question">
<span class="material-symbols-outlined text-base">add</span>
<span class="hidden sm:inline">Добавить вопрос</span>
<span class="sm:hidden">Добавить</span>
@@ -114,37 +113,91 @@
</div>
<ol id="questions" class="mt-3 space-y-3"></ol>
</section>
</div>
</details>
<details class="cabinet-disclosure cabinet-brick" open>
<summary class="cabinet-disclosure__summary">
<span class="cabinet-disclosure__summary-text">
<span class="cabinet-disclosure__summary-title font-headline">История</span>
<span class="cabinet-disclosure__summary-sub">Версии теста и кто проходил</span>
</span>
</summary>
<div class="cabinet-disclosure__body">
<div class="test-detail-subsection test-detail-subsection--tight">
<h3 class="test-detail-subsection__title">Версии</h3>
<ul id="versions-list" class="version-card-list"></ul>
</div>
<div class="test-detail-subsection">
<h3 class="test-detail-subsection__title">Прохождения</h3>
<ul id="attempts-list" class="attempts-card-list"></ul>
</div>
</div>
</details>
<details class="cabinet-disclosure cabinet-brick" open>
<summary class="cabinet-disclosure__summary">
<span class="cabinet-disclosure__summary-text">
<span class="cabinet-disclosure__summary-title font-headline">Показ в каталоге</span>
<span class="cabinet-disclosure__summary-sub">Видимость в списке и выдача сотрудникам</span>
</span>
</summary>
<div class="cabinet-disclosure__body">
<div class="test-detail-subsection test-detail-subsection--tight">
<h3 class="test-detail-subsection__title">Видимость</h3>
<p class="test-detail-hint">Скрытые тесты в общем списке не показываются; ссылку на тест по-прежнему можно открыть.</p>
<div class="publication-visibility__actions">
<button id="btn-toggle-visibility" class="btn btn-ghost" type="button">Скрыть из списка</button>
</div>
</div>
<div class="test-detail-subsection">
<h3 class="test-detail-subsection__title">Кому выдать</h3>
<p class="test-detail-hint">Список с учётом поиска и фильтров; можно отметить всех на экране.</p>
<div class="assign-toolbar">
<input id="assign-search" class="form-input assign-toolbar__search" type="text" placeholder="Поиск: ФИО, логин" />
<select id="assign-dept" class="form-input"><option value="__all__">Все отделы</option></select>
<select id="assign-clinic" class="form-input">
<option value="all">Все</option>
<option value="with">С учёткой в модуле</option>
<option value="without">Без учётки (создадим при назначении)</option>
</select>
<button id="assign-select-all" class="btn btn-ghost btn--sm" type="button">Выбрать всех</button>
</div>
<div id="assign-list" class="assign-list"></div>
<div class="inline-actions" style="margin-top:0.75rem;">
<button id="assign-submit" class="btn btn-primary" type="button">Назначить выбранных</button>
<span id="assign-status" class="muted"></span>
</div>
</div>
</div>
</details>
</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
<div class="mx-auto {% if ui_variant == 'legacy' %}max-w-2xl{% else %}max-w-6xl{% endif %} 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>
<span class="text-xs text-ink-500 truncate">Активность цепочки и поля теста — в шапке.</span>
<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">
class="hidden sm:inline-flex px-3 py-2 rounded-lg text-ink-700 hover:bg-ink-100 text-sm btn btn-ghost">
К каталогу
</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">
bg-brand-600 hover:bg-brand-700 text-white font-medium min-h-11 btn btn-primary">
<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>
<p id="save-status" class="mx-auto {% if ui_variant == 'legacy' %}max-w-2xl{% else %}max-w-6xl{% endif %} 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">
<li class="rounded-xl bg-white border border-ink-300/60 p-3 sm:p-4 q-item question-editor-block">
{# Шапка карточки вопроса: номер слева, кнопки справа. #}
<div class="flex items-center justify-between gap-2">
<span class="inline-flex items-center px-2 py-0.5 rounded-md
@@ -165,27 +218,28 @@
</div>
</div>
<textarea class="q-text mt-3 w-full rounded-lg border border-ink-300 px-3 py-2
<div class="question-editor-block__header">
<h4 class="question-editor-block__title q-num">Вопрос #</h4>
<button class="q-ai btn btn-ghost btn--sm question-editor-block__ai-btn">
Сгенерировать вопрос (ИИ)
</button>
</div>
<textarea class="q-text mt-2 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">
text-sm text-brand-700 hover:bg-brand-50 min-h-10 btn btn-ghost btn--sm">
<span class="material-symbols-outlined text-base">add</span>
Добавить вариант
</button>
@@ -194,19 +248,19 @@
{# ── Шаблон варианта ────────────────────────────────────────────── #}
<template id="tpl-option">
<li class="flex items-center gap-2 opt-item">
<li class="flex items-center gap-2 opt-item question-option-row">
{# Чекбокс «Правильный» — обёрнут в большой 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" />
class="opt-correct w-5 h-5 rounded border-ink-300 text-brand-600 focus:ring-brand-500 question-option-row__mark" />
</label>
<input type="text"
class="opt-text flex-1 min-w-0 rounded-lg border border-ink-300 px-3 py-2
class="opt-text flex-1 min-w-0 rounded-lg border border-ink-300 px-3 py-2 question-option-row__text
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"
rounded hover:bg-red-50 text-red-600 question-option-remove"
title="Удалить" aria-label="Удалить вариант">
<span class="material-symbols-outlined text-base">close</span>
</button>