You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

169 lines
7.0 KiB

{% extends "base.html" %}
{% block title %}Назначения — Тестирование персонала{% endblock %}
{% block content %}
<div class="space-y-4 sm:space-y-5 pb-10">
<div class="flex items-center justify-between gap-3">
<h1 class="text-xl font-semibold text-ink-900">Назначения</h1>
<a href="{{ url_for('main.index') }}" class="link-back text-sm">← Главная</a>
</div>
{# Выбор теста #}
<div class="cabinet-brick">
<h2 class="font-semibold text-sm text-ink-700 mb-3">Тест</h2>
<select id="assign-test-select" class="form-input">
<option value="">— Выберите тест —</option>
{% for t in tests %}
<option value="{{ t.id }}">{{ t.title }}</option>
{% endfor %}
</select>
</div>
{# Панель назначения (скрыта пока не выбран тест) #}
<div id="assign-panel" class="cabinet-brick hidden">
<h2 class="font-semibold text-sm text-ink-700 mb-3">Кому выдать</h2>
<p class="test-detail-hint">Список с учётом поиска и фильтров; можно отметить всех на экране.</p>
<div class="assign-toolbar mt-3">
<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 mt-3"></div>
<div class="inline-actions mt-3">
<button id="assign-submit" class="btn btn-primary" type="button">Назначить выбранным</button>
<span id="assign-status" class="muted"></span>
</div>
</div>
</div>
{% endblock %}
{% block scripts %}
<script>
(function () {
const $ = (s) => document.querySelector(s);
const testSelect = $('#assign-test-select');
const assignPanel = $('#assign-panel');
const assignSearchEl = $('#assign-search');
const assignDeptEl = $('#assign-dept');
const assignClinicEl = $('#assign-clinic');
const assignListEl = $('#assign-list');
const assignSelectAllBtn = $('#assign-select-all');
const assignSubmitBtn = $('#assign-submit');
const assignStatusEl = $('#assign-status');
let currentTestId = null;
let assignPeople = [];
let assignSelected = new Set();
function escHtml(s) {
return String(s || '').replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;');
}
function renderAssignList() {
assignListEl.innerHTML = '';
assignPeople.forEach((p) => {
const row = document.createElement('label');
row.className = `assign-row${assignSelected.has(String(p.staffId)) ? ' assign-row--selected' : ''}`;
row.innerHTML = `
<input type="checkbox" ${assignSelected.has(String(p.staffId)) ? 'checked' : ''} />
<span class="assign-row__text">
<span class="assign-row__fio">${escHtml(p.fio || '—')}</span>
${p.webLogin ? `<span class="assign-row__login">${escHtml(p.webLogin)}</span>` : ''}
<span class="assign-row__meta">${escHtml(p.department || '—')}</span>
</span>`;
const cb = row.querySelector('input');
cb.addEventListener('change', () => {
const k = String(p.staffId);
if (cb.checked) assignSelected.add(k); else assignSelected.delete(k);
row.classList.toggle('assign-row--selected', cb.checked);
});
assignListEl.appendChild(row);
});
if (!assignPeople.length) assignListEl.innerHTML = '<p class="muted" style="padding:.75rem;">Никого не найдено.</p>';
}
async function loadDirectory() {
if (!currentTestId) return;
assignStatusEl.textContent = 'Загружаем…';
try {
const params = new URLSearchParams();
if (assignSearchEl.value.trim()) params.set('q', assignSearchEl.value.trim());
if (assignDeptEl.value && assignDeptEl.value !== '__all__') params.set('department', assignDeptEl.value);
params.set('clinic', assignClinicEl.value || 'all');
const r = await fetch(`/api/auth/dev/assignment-directory?${params.toString()}`);
const data = await r.json();
if (!r.ok) throw new Error(data.error || 'Не удалось загрузить сотрудников');
assignPeople = data.people || [];
const depts = data.departments || [];
if (assignDeptEl.options.length <= 1) {
depts.forEach((d) => {
const o = document.createElement('option');
o.value = d; o.textContent = d;
assignDeptEl.appendChild(o);
});
}
assignSelected = new Set();
renderAssignList();
assignStatusEl.textContent = '';
} catch (e) {
assignStatusEl.textContent = e.message || 'Ошибка загрузки';
}
}
testSelect.addEventListener('change', () => {
currentTestId = testSelect.value || null;
if (currentTestId) {
assignPanel.classList.remove('hidden');
// Сброс фильтров
assignDeptEl.innerHTML = '<option value="__all__">Все отделы</option>';
assignSearchEl.value = '';
loadDirectory();
} else {
assignPanel.classList.add('hidden');
}
});
let t = null;
assignSearchEl.addEventListener('input', () => { clearTimeout(t); t = setTimeout(loadDirectory, 350); });
assignDeptEl.addEventListener('change', loadDirectory);
assignClinicEl.addEventListener('change', loadDirectory);
assignSelectAllBtn.addEventListener('click', () => {
assignPeople.forEach((p) => assignSelected.add(String(p.staffId)));
renderAssignList();
});
assignSubmitBtn.addEventListener('click', async () => {
if (!currentTestId) return;
const selectedRows = assignPeople.filter((p) => assignSelected.has(String(p.staffId)));
if (!selectedRows.length) { assignStatusEl.textContent = 'Никто не выбран'; return; }
assignStatusEl.textContent = 'Назначаем…';
try {
const payload = selectedRows.map((p) => ({
staffId: p.staffId,
webLogin: p.webLogin || null,
fio: p.fio || null,
department: p.department || null,
}));
const r = await fetch(`/api/tests/${currentTestId}/assign`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ targets: payload }),
});
const data = await r.json();
if (!r.ok) throw new Error(data.error || 'Ошибка назначения');
assignStatusEl.textContent = `Назначено: ${data.count ?? selectedRows.length}`;
} catch (e) {
assignStatusEl.textContent = e.message || 'Ошибка назначения';
}
});
})();
</script>
{% endblock %}