feat: submit all modal forms without page reload
- add a generic AJAX submitter for forms inside dialogs - keep modal values and show field errors on server validation failure - soft-refresh the page content on success, preserving scroll, search and filters - share toast and re-init hooks for tables, markdown, sections and group page - require passwords and add minlength/match validation on auth forms
This commit is contained in:
@@ -278,7 +278,8 @@
|
||||
<p class="hint">Если студент уже в другой группе — он будет переведён в эту.</p>
|
||||
<button class="btn btn-secondary" type="submit" th:disabled="${#lists.isEmpty(candidates)}">Зачислить существующего</button>
|
||||
</form>
|
||||
<form class="form-stack mt-4" th:action="@{/admin/groups/{gid}/students/create(gid=${group.id})}" method="post">
|
||||
<form class="form-stack mt-4" th:action="@{/admin/groups/{gid}/students/create(gid=${group.id})}" method="post"
|
||||
data-success="Студент создан. Пароль показан ниже.">
|
||||
<input type="hidden" name="_csrf" th:value="${csrfToken}">
|
||||
<div class="row g-3">
|
||||
<div class="col-md-6"><label class="label" for="stNo">Студенческий номер (логин)</label>
|
||||
@@ -346,7 +347,7 @@
|
||||
<div class="dlg-body" id="assign-modal-body"></div>
|
||||
</dialog>
|
||||
|
||||
<script th:src="@{/js/admin-group.js(v='38')}" defer></script>
|
||||
<script th:src="@{/js/admin-group.js(v='39')}" defer></script>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -14,8 +14,8 @@
|
||||
</p>
|
||||
<form class="card form-card form-stack" th:action="@{/change-password}" method="post">
|
||||
<div><label class="label" for="old">Текущий пароль</label><input class="input" id="old" name="old" type="password" autocomplete="current-password" required></div>
|
||||
<div><label class="label" for="new">Новый пароль</label><input class="input" id="new" name="new" type="password" autocomplete="new-password" required><p class="hint">Минимум 8 символов.</p></div>
|
||||
<div><label class="label" for="new2">Повторите новый пароль</label><input class="input" id="new2" name="new2" type="password" autocomplete="new-password" required></div>
|
||||
<div><label class="label" for="new">Новый пароль</label><input class="input" id="new" name="new" type="password" autocomplete="new-password" minlength="8" required><p class="hint">Минимум 8 символов.</p></div>
|
||||
<div><label class="label" for="new2">Повторите новый пароль</label><input class="input" id="new2" name="new2" type="password" autocomplete="new-password" minlength="8" data-match="#new" data-error-match="Пароли не совпадают" required></div>
|
||||
<button class="btn btn-primary" type="submit">Сменить пароль</button>
|
||||
<a th:if="${currentUser != null and !currentUser.mustChangePassword}"
|
||||
class="btn btn-secondary" th:href="${currentUser.admin} ? @{/admin} : @{/student}">Отмена</a>
|
||||
|
||||
@@ -21,14 +21,15 @@
|
||||
<link id="hljs-light" rel="stylesheet" th:href="@{/css/hljs-github.css(v='36')}">
|
||||
<link id="hljs-dark" rel="stylesheet" th:href="@{/css/hljs-github-dark.css(v='36')}" disabled>
|
||||
<script th:src="@{/js/highlight.min.js(v='36')}" defer></script>
|
||||
<script th:src="@{/js/table-scroll.js(v='36')}" defer></script>
|
||||
<script th:src="@{/js/tablekit.js(v='36')}" defer></script>
|
||||
<script th:src="@{/js/table-scroll.js(v='38')}" defer></script>
|
||||
<script th:src="@{/js/tablekit.js(v='38')}" defer></script>
|
||||
<script th:src="@{/js/bootstrap.bundle.min.js}" defer></script>
|
||||
<script th:src="@{/js/theme-init.js(v='36')}"></script>
|
||||
<script th:src="@{/js/app.js(v='36')}" defer></script>
|
||||
<script th:src="@{/js/app.js(v='38')}" defer></script>
|
||||
<script th:src="@{/js/ui-state.js(v='36')}" defer></script>
|
||||
<script th:src="@{/js/form-feedback.js(v='38')}" defer></script>
|
||||
<script th:src="@{/js/md-content.js(v='36')}" defer></script>
|
||||
<script th:src="@{/js/form-feedback.js(v='39')}" defer></script>
|
||||
<script th:src="@{/js/modal-ajax.js(v='1')}" defer></script>
|
||||
<script th:src="@{/js/md-content.js(v='38')}" defer></script>
|
||||
<script th:src="@{/js/submit-guard.js(v='36')}" defer></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@@ -7,9 +7,9 @@
|
||||
<h1 class="page-title">Создание администратора</h1>
|
||||
<p class="text-sm text-muted mb-4">Это разовая настройка. Аккаунт преподавателя создаётся один раз.</p>
|
||||
<form class="card form-card form-stack" th:action="@{/setup}" method="post">
|
||||
<div><label class="label" for="login">Логин</label><input class="input" id="login" name="login" autocomplete="username" th:value="${login}" autofocus required></div>
|
||||
<div><label class="label" for="password">Пароль</label><input class="input" id="password" name="password" type="password" autocomplete="new-password" required><p class="hint">Минимум 8 символов.</p></div>
|
||||
<div><label class="label" for="password2">Повторите пароль</label><input class="input" id="password2" name="password2" type="password" autocomplete="new-password" required></div>
|
||||
<div><label class="label" for="login">Логин</label><input class="input" id="login" name="login" autocomplete="username" th:value="${login}" minlength="3" autofocus required></div>
|
||||
<div><label class="label" for="password">Пароль</label><input class="input" id="password" name="password" type="password" autocomplete="new-password" minlength="8" required><p class="hint">Минимум 8 символов.</p></div>
|
||||
<div><label class="label" for="password2">Повторите пароль</label><input class="input" id="password2" name="password2" type="password" autocomplete="new-password" minlength="8" data-match="#password" data-error-match="Пароли не совпадают" required></div>
|
||||
<button class="btn btn-primary" type="submit">Создать и войти</button>
|
||||
<input type="hidden" name="_csrf" th:value="${csrfToken}">
|
||||
</form>
|
||||
|
||||
Reference in New Issue
Block a user