Files
TestingWebApp/flask_app/app/templates/tests/editor.html
T

295 lines
16 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 {% if ui_variant == 'legacy' %}test-detail-page test-detail-page--with-fixed-actions{% endif %}"
data-test-id="{{ test_id }}"
data-initial='{{ content | tojson | safe }}'>
<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>
<textarea id="test-title" maxlength="200" rows="1" placeholder="Название теста"
class="hero-brick__title font-headline"></textarea>
<textarea id="test-description" rows="2" placeholder="Краткое описание (необязательно)"
class="hero-brick__desc"></textarea>
<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>
<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="btn btn-ghost" type="button" style="min-height:43px;">
Сгенерировать тест (ИИ)
</button>
</div>
</div>
<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">
<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-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
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>
</button>
</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 {% if ui_variant == 'legacy' %}max-w-2xl{% else %}max-w-6xl{% endif %} px-4 py-3
flex items-center justify-between gap-3">
<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 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 btn btn-primary">
<span class="material-symbols-outlined text-base">save</span>
Сохранить
</button>
</div>
</div>
<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 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
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>
<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>
<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>
</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 btn btn-ghost btn--sm">
<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 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 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 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 question-option-remove"
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 %}