No inline JS/styles in templates: app.js delegation (data-action/data-modal/data-confirm/data-auto-submit), B3 confirm/alert dialogs, theme-init file
This commit is contained in:
@@ -33,8 +33,7 @@
|
||||
th:attr="data-refresh=${refreshUrl}">
|
||||
<input type="hidden" name="_csrf" th:value="${csrfToken}">
|
||||
<input type="hidden" name="studentId" th:value="${row.studentId}">
|
||||
<select class="input" name="labLevelId" th:disabled="${#lists.isEmpty(lab.levels)}"
|
||||
onchange="this.closest('form').requestSubmit()">
|
||||
<select class="input" name="labLevelId" th:disabled="${#lists.isEmpty(lab.levels)}" data-auto-submit>
|
||||
<option th:each="lv : ${lab.levels}" th:value="${lv.id}"
|
||||
th:selected="${lv.id == row.levelId}" th:text="${lv.title}">Уровень</option>
|
||||
</select>
|
||||
@@ -46,7 +45,7 @@
|
||||
th:attr="data-refresh=${refreshUrl}">
|
||||
<input type="hidden" name="_csrf" th:value="${csrfToken}">
|
||||
<input type="hidden" name="studentId" th:value="${row.studentId}">
|
||||
<select class="input" name="variantId" onchange="confirmAssignVariant(this)">
|
||||
<select class="input js-variant-select" name="variantId">
|
||||
<option value="">— авто —</option>
|
||||
<optgroup th:each="lv : ${lab.levels}" th:label="${lv.title}">
|
||||
<option th:each="v : ${lv.variants}" th:value="${v.id}"
|
||||
|
||||
@@ -19,14 +19,14 @@
|
||||
<a class="btn btn-secondary" th:href="@{/admin/groups/{id}/submissions.zip(id=${group.id})}"
|
||||
title="ZIP со всеми последними решениями группы по всем лабораторным">Скачать работы</a>
|
||||
<a class="btn btn-secondary" th:href="@{/admin/groups/{id}/credentials(id=${group.id}, format='xlsx')}">Скачать пароли</a>
|
||||
<button class="btn" type="button" onclick="openModal('modal-add-student')">+ Студент</button>
|
||||
<button class="btn" type="button" data-modal="modal-add-student">+ Студент</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="toolbar">
|
||||
<input class="input table-search w-260" data-target="group-table"
|
||||
placeholder="Поиск по студенту…" aria-label="Поиск по студенту">
|
||||
<select class="input" id="lab-focus" onchange="focusLab()">
|
||||
<select class="input" id="lab-focus">
|
||||
<option value="">все лабораторные</option>
|
||||
<option th:each="lab : ${labs}" th:value="${lab.id}" th:text="|ЛР ${lab.number}|">—</option>
|
||||
</select>
|
||||
@@ -47,8 +47,7 @@
|
||||
<span class="lab-col-title" th:text="${lab.title}">—</span>
|
||||
<div class="lab-head-actions">
|
||||
<button class="mini-btn" type="button"
|
||||
th:attr="data-url=@{/admin/groups/{id}/assign-modal(id=${group.id}, labId=${lab.id})},data-lab-name=|ЛР ${lab.number}|"
|
||||
onclick="openLabAssign(this)"
|
||||
th:attr="data-url=@{/admin/groups/{id}/assign-modal(id=${group.id}, labId=${lab.id})},data-lab-name=|ЛР ${lab.number}|" data-action="assign"
|
||||
title="Выдача вариантов: уровни, варианты, автокраспределение по группе">
|
||||
<svg class="cell-ico" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M4 6h16M4 12h16M4 18h16"/></svg>
|
||||
</button>
|
||||
@@ -89,16 +88,14 @@
|
||||
<div class="cell-main">
|
||||
<button class="variant-btn" type="button"
|
||||
th:classappend="${cell.variantManual} ? 'manual' : ''"
|
||||
th:attr="data-student=${row.studentId},data-student-name=${row.fullName},data-lab=${cell.labId},data-lab-name='ЛР ' + ${cell.labNumber},data-variant=${cell.variantId}"
|
||||
onclick="openVariant(this)"
|
||||
th:attr="data-student=${row.studentId},data-student-name=${row.fullName},data-lab=${cell.labId},data-lab-name='ЛР ' + ${cell.labNumber},data-variant=${cell.variantId}" data-action="variant"
|
||||
title="Назначить вариант вручную или выдать случайный">
|
||||
<span th:if="${cell.variantNumber}" th:text="'В-' + ${cell.variantNumber}">В-1</span>
|
||||
<span th:if="${cell.variantNumber == null}" class="cell-none">выдать</span>
|
||||
</button>
|
||||
<button class="score-link" type="button"
|
||||
th:classappend="${cell.score == null} ? 'none' : ''"
|
||||
th:attr="data-student=${row.studentId},data-student-name=${row.fullName},data-lab=${cell.labId},data-lab-name='ЛР ' + ${cell.labNumber},data-score=${cell.score},data-comments=${row.commentsJson},data-ci=${iterStat.index}"
|
||||
onclick="openGradeEditor(this)"
|
||||
th:attr="data-student=${row.studentId},data-student-name=${row.fullName},data-lab=${cell.labId},data-lab-name='ЛР ' + ${cell.labNumber},data-score=${cell.score},data-comments=${row.commentsJson},data-ci=${iterStat.index}" data-action="grade"
|
||||
th:title="${cell.score} ? 'Оценка ' + ${cell.score} + ' — изменить' : 'Оценки нет — выставить балл и комментарии'">
|
||||
<span th:text="${cell.score} ?: '—'">—</span>
|
||||
</button>
|
||||
@@ -113,8 +110,7 @@
|
||||
<span th:unless="${cell.submissionId}" class="sub-dim">не сдано</span>
|
||||
<button class="cell-btn st js-defense-cell" type="button"
|
||||
th:classappend="${cell.defenseStatus == null} ? 'dim' : (${cell.defenseStatus == 'ready'} ? 'ok' : 'warn')"
|
||||
th:attr="data-student=${row.studentId},data-student-name=${row.fullName},data-lab=${cell.labId},data-lab-name='ЛР ' + ${cell.labNumber},data-md=${cell.defenseAssignmentMd},data-session=${cell.defenseSessionId},data-status=${cell.defenseStatus},data-seconds=${cell.defenseSecondsLeft},data-code=${cell.defenseCode},data-reason=${cell.defenseReadyReason}"
|
||||
onclick="openDefense(this)"
|
||||
th:attr="data-student=${row.studentId},data-student-name=${row.fullName},data-lab=${cell.labId},data-lab-name='ЛР ' + ${cell.labNumber},data-md=${cell.defenseAssignmentMd},data-session=${cell.defenseSessionId},data-status=${cell.defenseStatus},data-seconds=${cell.defenseSecondsLeft},data-code=${cell.defenseCode},data-reason=${cell.defenseReadyReason}" data-action="defense"
|
||||
th:title="${cell.defenseStatus == null} ? 'Защита не назначена — нажмите, чтобы задать задание' : 'Защита: задание и действия'">
|
||||
<span class="cell-dim">защита:</span>
|
||||
<span class="js-def-label"
|
||||
@@ -132,15 +128,13 @@
|
||||
</td>
|
||||
<td class="text-right actions-cell">
|
||||
<div class="actions-col">
|
||||
<form th:action="@{/admin/groups/{gid}/students/{sid}/reset-password(gid=${group.id}, sid=${row.studentId})}" method="post"
|
||||
onsubmit="return confirm('Сбросить пароль студенту? Текущий пароль перестанет работать, новый покажется один раз.')">
|
||||
<form th:action="@{/admin/groups/{gid}/students/{sid}/reset-password(gid=${group.id}, sid=${row.studentId})}" method="post" data-confirm="Сбросить пароль студенту? Текущий пароль перестанет работать, новый покажется один раз.">
|
||||
<input type="hidden" name="_csrf" th:value="${csrfToken}">
|
||||
<button class="btn btn-secondary btn-sm" type="submit"
|
||||
title="Сгенерировать новый пароль (покажется один раз, потребуется смена при входе)">Сбросить</button>
|
||||
</form>
|
||||
<form th:action="@{/admin/groups/{gid}/students/{sid}/unenroll(gid=${group.id}, sid=${row.studentId})}"
|
||||
method="post"
|
||||
onsubmit="return confirm('Отчислить этого студента из группы? Аккаунт, сдача и оценки сохранятся; студент сможет войти и будет без группы.')">
|
||||
method="post" data-confirm="Отчислить этого студента из группы? Аккаунт, сдача и оценки сохранятся; студент сможет войти и будет без группы.">
|
||||
<input type="hidden" name="_csrf" th:value="${csrfToken}">
|
||||
<button class="btn btn-sm btn-danger" type="submit">Отчислить</button>
|
||||
</form>
|
||||
@@ -157,7 +151,7 @@
|
||||
<dialog id="modal-grade" class="modal modal-lg">
|
||||
<div class="modal-header">
|
||||
<h2 class="modal-title" id="grade-title">Оценка и комментарии</h2>
|
||||
<button class="modal-close" type="button" aria-label="Закрыть" onclick="closeModal('modal-grade')">✕</button>
|
||||
<button class="modal-close" type="button" aria-label="Закрыть">✕</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form class="form-stack" id="grade-form" th:action="@{/admin/groups/{id}/grades(id=${group.id})}" method="post">
|
||||
@@ -170,7 +164,7 @@
|
||||
<input class="input" id="grade-score" name="score" type="number" min="0" max="5" step="0.1"></div>
|
||||
<div id="cs-rows"></div>
|
||||
<div class="flex gap-2">
|
||||
<button class="btn btn-secondary" type="button" onclick="csRow('','neutral')">+ комментарий</button>
|
||||
<button class="btn btn-secondary" type="button" data-action="comment-add">+ комментарий</button>
|
||||
<button class="btn btn-primary" type="submit">Сохранить</button>
|
||||
</div>
|
||||
</form>
|
||||
@@ -182,7 +176,7 @@
|
||||
<dialog id="modal-variant" class="modal modal-lg">
|
||||
<div class="modal-header">
|
||||
<h2 class="modal-title">Назначить вариант</h2>
|
||||
<button class="modal-close" type="button" aria-label="Закрыть" onclick="closeModal('modal-variant')">✕</button>
|
||||
<button class="modal-close" type="button" aria-label="Закрыть">✕</button>
|
||||
</div>
|
||||
<div class="modal-body form-stack">
|
||||
<p class="text-sm text-muted" id="variant-who">—</p>
|
||||
@@ -218,7 +212,7 @@
|
||||
<dialog id="modal-defense" class="modal">
|
||||
<div class="modal-header">
|
||||
<h2 class="modal-title">Индивидуальное задание</h2>
|
||||
<button class="modal-close" type="button" aria-label="Закрыть" onclick="closeModal('modal-defense')">✕</button>
|
||||
<button class="modal-close" type="button" aria-label="Закрыть">✕</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form class="form-stack" id="defense-form" th:action="@{/admin/defenses/save}" method="post">
|
||||
@@ -247,8 +241,7 @@
|
||||
<input type="hidden" name="groupId" th:value="${group.id}">
|
||||
<button class="btn btn-secondary" type="submit">Выдать код</button>
|
||||
</form>
|
||||
<form class="inline-form" id="defense-reset-form" method="post"
|
||||
onsubmit="return confirm('Открыть защиту заново для нового (дополнительного) задания?')">
|
||||
<form class="inline-form" id="defense-reset-form" method="post" data-confirm="Открыть защиту заново для нового (дополнительного) задания?">
|
||||
<input type="hidden" name="_csrf" th:value="${csrfToken}">
|
||||
<input type="hidden" name="labId" id="defense-reset-lab">
|
||||
<input type="hidden" name="groupId" th:value="${group.id}">
|
||||
@@ -267,7 +260,7 @@
|
||||
<dialog id="modal-add-student" class="modal">
|
||||
<div class="modal-header">
|
||||
<h2 class="modal-title">Добавить студента</h2>
|
||||
<button class="modal-close" type="button" aria-label="Закрыть" onclick="closeModal('modal-add-student')">✕</button>
|
||||
<button class="modal-close" type="button" aria-label="Закрыть">✕</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form class="form-stack" id="enroll-form" method="post"
|
||||
@@ -346,7 +339,7 @@
|
||||
<dialog id="modal-lab-assign" class="modal modal-lg">
|
||||
<div class="modal-header">
|
||||
<h2 class="modal-title" id="assign-modal-title">Выдача вариантов</h2>
|
||||
<button class="modal-close" type="button" aria-label="Закрыть" onclick="closeModal('modal-lab-assign')">✕</button>
|
||||
<button class="modal-close" type="button" aria-label="Закрыть">✕</button>
|
||||
</div>
|
||||
<div class="modal-body" id="assign-modal-body"></div>
|
||||
</dialog>
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
<div class="flex gap-2">
|
||||
<input class="input table-search" data-target="groups-table"
|
||||
placeholder="Поиск по названию…" aria-label="Поиск по группам">
|
||||
<button class="btn btn-secondary" type="button" onclick="openModal('modal-import-group')">Импорт</button>
|
||||
<button class="btn btn-primary" type="button" onclick="openModal('modal-create-group')">+ Новая группа</button>
|
||||
<button class="btn btn-secondary" type="button" data-modal="modal-import-group">Импорт</button>
|
||||
<button class="btn btn-primary" type="button" data-modal="modal-create-group">+ Новая группа</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
<dialog id="modal-create-group" class="modal">
|
||||
<div class="modal-header">
|
||||
<h2 class="modal-title">Новая группа</h2>
|
||||
<button class="modal-close" type="button" aria-label="Закрыть" onclick="closeModal('modal-create-group')">✕</button>
|
||||
<button class="modal-close" type="button" aria-label="Закрыть">✕</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form class="form-stack" th:action="@{/admin/groups}" method="post">
|
||||
@@ -45,7 +45,7 @@
|
||||
<dialog id="modal-import-group" class="modal">
|
||||
<div class="modal-header">
|
||||
<h2 class="modal-title">Импорт студентов</h2>
|
||||
<button class="modal-close" type="button" aria-label="Закрыть" onclick="closeModal('modal-import-group')">✕</button>
|
||||
<button class="modal-close" type="button" aria-label="Закрыть">✕</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form class="form-stack" th:action="@{/admin/groups/import}" method="post" enctype="multipart/form-data">
|
||||
|
||||
@@ -46,8 +46,7 @@
|
||||
</td>
|
||||
<td class="text-right">
|
||||
<button class="btn btn-danger" type="submit" name="action"
|
||||
th:value="|delete-level-${level.id}|"
|
||||
onclick="return confirm('Удалить уровень? Несохранённые правки остальных полей формы будут потеряны. Варианты на уровне должны быть пустыми.')">
|
||||
th:value="|delete-level-${level.id}|" data-confirm="Удалить уровень? Несохранённые правки остальных полей формы будут потеряны. Варианты на уровне должны быть пустыми.">
|
||||
удалить</button>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -82,8 +81,7 @@
|
||||
<div><label class="label">Условие (markdown)</label>
|
||||
<textarea class="input" name="variantStatements" rows="6" th:text="${v.statementMd}"></textarea></div>
|
||||
<button class="btn btn-danger" type="submit" name="action"
|
||||
th:value="|delete-variant-${v.id}|"
|
||||
onclick="return confirm('Удалить вариант? Несохранённые правки остальных полей формы будут потеряны.')">Удалить вариант</button>
|
||||
th:value="|delete-variant-${v.id}|" data-confirm="Удалить вариант? Несохранённые правки остальных полей формы будут потеряны.">Удалить вариант</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -11,8 +11,7 @@
|
||||
Статус: <span class="badge"
|
||||
th:classappend="${lab.status == 'published'} ? 'badge-ok' : 'badge-warn'"
|
||||
th:text="${lab.status == 'published'} ? 'Опубликована' : (${lab.status == 'archived'} ? 'В архиве' : 'Черновик')">draft</span>
|
||||
<form class="inline-form" th:action="@{/admin/labs/{id}/toggle-status(id=${lab.id})}" method="post"
|
||||
onsubmit="return confirm('Сменить статус лабораторной? Опубликованную увидят студенты, черновик — перестанут.')">
|
||||
<form class="inline-form" th:action="@{/admin/labs/{id}/toggle-status(id=${lab.id})}" method="post" data-confirm="Сменить статус лабораторной? Опубликованную увидят студенты, черновик — перестанут.">
|
||||
<input type="hidden" name="_csrf" th:value="${csrfToken}">
|
||||
<button class="btn btn-secondary" type="submit"
|
||||
th:text="${lab.status == 'published'} ? 'Скрыть' : 'Опубликовать'">Опубликовать</button>
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
<div class="flex gap-2">
|
||||
<input class="input table-search" data-target="labs-table"
|
||||
placeholder="Поиск по названию…" aria-label="Поиск по лабораторным">
|
||||
<button class="btn btn-secondary" type="button" onclick="openModal('modal-import-lab')">Импорт</button>
|
||||
<button class="btn btn-primary" type="button" onclick="openModal('modal-create-lab')">+ Создать</button>
|
||||
<button class="btn btn-secondary" type="button" data-modal="modal-import-lab">Импорт</button>
|
||||
<button class="btn btn-primary" type="button" data-modal="modal-create-lab">+ Создать</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -27,8 +27,7 @@
|
||||
th:text="${lab.status == 'published'} ? 'Опубликована' : (${lab.status == 'archived'} ? 'В архиве' : 'Черновик')">—</span>
|
||||
</td>
|
||||
<td class="text-center">
|
||||
<form th:action="@{/admin/labs/{id}/toggle-status(id=${lab.id})}" method="post"
|
||||
onsubmit="return confirm('Сменить статус лабораторной? Опубликованную увидят студенты, черновик — перестанут.')">
|
||||
<form th:action="@{/admin/labs/{id}/toggle-status(id=${lab.id})}" method="post" data-confirm="Сменить статус лабораторной? Опубликованную увидят студенты, черновик — перестанут.">
|
||||
<input type="hidden" name="_csrf" th:value="${csrfToken}">
|
||||
<input type="hidden" name="back" th:value="@{/admin/labs}">
|
||||
<button class="btn"
|
||||
@@ -37,8 +36,7 @@
|
||||
th:text="${lab.status == 'published'} ? 'Скрыть' : 'Опубликовать'">—</button>
|
||||
</form>
|
||||
</td>
|
||||
<td class="text-center"><form th:action="@{/admin/labs/{id}/delete(id=${lab.id})}" method="post"
|
||||
onsubmit="return confirm('Удалить лабораторную вместе с вариантами и назначениями? Действие необратимо.')"><input type="hidden" name="_csrf" th:value="${csrfToken}"><button class="btn btn-danger" type="submit">Удалить</button></form></td>
|
||||
<td class="text-center"><form th:action="@{/admin/labs/{id}/delete(id=${lab.id})}" method="post" data-confirm="Удалить лабораторную вместе с вариантами и назначениями? Действие необратимо."><input type="hidden" name="_csrf" th:value="${csrfToken}"><button class="btn btn-danger" type="submit">Удалить</button></form></td>
|
||||
</tr>
|
||||
<tr th:if="${#lists.isEmpty(labs)}"><td colspan="6" class="empty">Лабораторных пока нет.</td></tr>
|
||||
</tbody>
|
||||
@@ -49,7 +47,7 @@
|
||||
<dialog id="modal-import-lab" class="modal">
|
||||
<div class="modal-header">
|
||||
<h2 class="modal-title">Импорт лабораторной</h2>
|
||||
<button class="modal-close" type="button" aria-label="Закрыть" onclick="closeModal('modal-import-lab')">✕</button>
|
||||
<button class="modal-close" type="button" aria-label="Закрыть">✕</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form class="form-stack" th:action="@{/admin/labs/import}" method="post" enctype="multipart/form-data">
|
||||
@@ -72,7 +70,7 @@
|
||||
<dialog id="modal-create-lab" class="modal">
|
||||
<div class="modal-header">
|
||||
<h2 class="modal-title">Новая лабораторная</h2>
|
||||
<button class="modal-close" type="button" aria-label="Закрыть" onclick="closeModal('modal-create-lab')">✕</button>
|
||||
<button class="modal-close" type="button" aria-label="Закрыть">✕</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form class="form-stack" th:action="@{/admin/labs/create}" method="post">
|
||||
|
||||
@@ -30,8 +30,7 @@
|
||||
<td class="font-mono" th:text="${u.studentNo}">—</td>
|
||||
<td><a th:if="${u.groupId}" th:href="@{/admin/groups/{id}(id=${u.groupId})}" th:text="${u.groupName}">—</a><span th:unless="${u.groupId}" class="text-muted">без группы</span></td>
|
||||
<td class="text-right">
|
||||
<form class="inline-form" th:action="@{/admin/users/{id}/delete(id=${u.id})}" method="post"
|
||||
onsubmit="return confirm('Удалить пользователя безвозвратно? Будут удалены аккаунт, группа, оценки с комментариями, сдачи с файлами и задания защит.');">
|
||||
<form class="inline-form" th:action="@{/admin/users/{id}/delete(id=${u.id})}" method="post" data-confirm="Удалить пользователя безвозвратно? Будут удалены аккаунт, группа, оценки с комментариями, сдачи с файлами и задания защит.">
|
||||
<input type="hidden" name="_csrf" th:value="${csrfToken}">
|
||||
<input type="hidden" name="q" th:value="${query}">
|
||||
<button class="btn btn-danger" type="submit">Удалить</button>
|
||||
|
||||
Reference in New Issue
Block a user