Table columns: dropdown/text filters per column

- tablekit builds a filter row under the headers for every th with data-filter;
  distinct cell values become a select (label from data-filter-label), or a
  text substring filter with data-filter="text"
- filters combine with the global table search; placeholder rows (colspan) are
  not filtered away; filter row follows the lab-focus column hiding
- enabled on: matrix lab columns (by variant), defense table (ЛР, status with
  readable values), students (Основа, Пароль), lab assignment lists (Уровень,
  Источник, Сдача), labs list (Статус)
This commit is contained in:
2026-09-15 22:43:48 +03:00
parent b212eed546
commit aa1fb5c791
6 changed files with 150 additions and 36 deletions

View File

@@ -50,7 +50,7 @@
<thead>
<tr>
<th class="matrix-sticky th-sortable is-asc" title="ФИО — по возрастанию">Студент</th>
<th th:each="lab : ${labs}" class="text-center lab-col" th:attr="data-lab=${lab.id}">
<th th:each="lab : ${labs}" class="text-center lab-col" data-filter th:attr="data-lab=${lab.id}">
<div class="lab-col-head">
<div class="lab-head-row">
<a th:href="@{/admin/labs/{id}(id=${lab.id})}" th:text="|ЛР ${lab.number}|">ЛР</a>
@@ -75,7 +75,8 @@
<div class="font-medium" th:text="${row.fullName}">—</div>
<div class="text-sm text-muted font-mono" th:text="${row.studentNo}">—</div>
</td>
<td th:each="cell, iterStat : ${row.cells}" class="text-center cell-lab" th:attr="data-lab=${cell.labId},data-sort=${cell.variantNumber}">
<td th:each="cell, iterStat : ${row.cells}" class="text-center cell-lab"
th:attr="data-lab=${cell.labId},data-sort=${cell.variantNumber},data-filter-label=${cell.variantNumber != null ? 'вариант ' + cell.variantNumber : 'без варианта'}">
<div class="cell-chips">
<span class="cell-chip"
th:title="(${cell.variantNumber} ? 'Вариант ' + ${cell.variantNumber} : 'Вариант') + ' — ' + (${cell.variantManual} ? 'закреплён вручную (раздача его не тронет)' : 'выдан автоматически')">
@@ -121,7 +122,7 @@
</div>
<div class="card card-flush mb-8">
<table id="defense-table" class="tablekit">
<thead><tr><th class="th-sortable is-asc" title="ФИО — по возрастанию">Студент</th><th>ЛР</th><th>Задание</th><th>Статус</th><th class="text-right" data-nosort>Действия</th></tr></thead>
<thead><tr><th class="th-sortable is-asc" title="ФИО — по возрастанию">Студент</th><th data-filter>ЛР</th><th>Задание</th><th data-filter>Статус</th><th class="text-right" data-nosort>Действия</th></tr></thead>
<tbody>
<tr th:each="d : ${defenseRows}" th:attr="data-lab=${d.labId}">
<td>
@@ -130,7 +131,7 @@
</td>
<td class="font-mono" th:text="|ЛР ${d.labNumber}|">—</td>
<td class="text-sm text-muted" th:text="${#strings.abbreviate(d.assignmentMd, 60)}">—</td>
<td>
<td th:attr="data-filter-value=${d.status == 'waiting' ? (d.code != null ? 'код выдан' : 'ждёт кода') : (d.status == 'started' ? 'пишет' : (d.status == 'ready' ? 'готов' : d.status))}">
<span class="badge badge-warn" th:if="${d.status == 'waiting' and d.code == null}">ждёт кода</span>
<span class="badge badge-warn" th:if="${d.status == 'waiting' and d.code != null}">код выдан</span>
<span class="badge badge-warn" th:if="${d.status == 'started'}">пишет (<span th:text="${d.secondsLeft}"></span> c)</span>
@@ -173,7 +174,7 @@
</div>
<div class="card card-flush">
<table id="students-table" class="tablekit">
<thead><tr><th data-nosort>№</th><th class="th-sortable is-asc" title="ФИО — по возрастанию">ФИО</th><th>Логин</th><th>Основа</th><th>Варианты</th><th>Пароль</th><th class="text-right" data-nosort>Действия</th></tr></thead>
<thead><tr><th data-nosort>№</th><th class="th-sortable is-asc" title="ФИО — по возрастанию">ФИО</th><th>Логин</th><th data-filter>Основа</th><th>Варианты</th><th data-filter>Пароль</th><th class="text-right" data-nosort>Действия</th></tr></thead>
<tbody>
<tr th:each="row, iter : ${studentGrades}">
<td class="text-muted" th:text="${iter.count}">1</td>