161 lines
6.5 KiB
Plaintext
161 lines
6.5 KiB
Plaintext
<!DOCTYPE html>
|
||
<html lang="ru">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title><%= student.last_name %> <%= student.first_name %> — Справки</title>
|
||
<link rel="stylesheet" href="/style.css">
|
||
</head>
|
||
<body>
|
||
<div class="container">
|
||
<nav class="nav">
|
||
<a href="/" class="btn">← К списку</a>
|
||
<span class="user-bar"><%= user ? user.name : '' %></span>
|
||
<button id="logout-btn" class="btn btn-logout">Выйти</button>
|
||
</nav>
|
||
|
||
<h1><%= student.last_name %> <%= student.first_name %> <%= student.patronymic %></h1>
|
||
<p>Класс: <strong><%= student.class %></strong></p>
|
||
|
||
<div class="card">
|
||
<h2>Обязательные предметы</h2>
|
||
<form id="mandatory-form" class="mandatory-form">
|
||
<div class="form-row">
|
||
<label>Русский язык</label>
|
||
<input type="number" id="rus-score" class="input input-sm" placeholder="Первичный балл" value="<%= rusResult ? rusResult.primary_score || '' : '' %>">
|
||
<input type="number" id="rus-grade" class="input input-sm" placeholder="Оценка (2-5)" min="2" max="5" value="<%= rusResult ? rusResult.grade || '' : '' %>">
|
||
</div>
|
||
<div class="form-row">
|
||
<label>Математика</label>
|
||
<input type="number" id="math-score" class="input input-sm" placeholder="Первичный балл" value="<%= mathResult ? mathResult.primary_score || '' : '' %>">
|
||
<input type="number" id="math-grade" class="input input-sm" placeholder="Оценка (2-5)" min="2" max="5" value="<%= mathResult ? mathResult.grade || '' : '' %>">
|
||
</div>
|
||
<button type="submit" class="btn btn-green">Сохранить</button>
|
||
<span id="mandatory-msg" class="msg"></span>
|
||
</form>
|
||
</div>
|
||
|
||
<% if (electiveResults.length > 0) { %>
|
||
<div class="card">
|
||
<h2>Предметы по выбору (из протокола)</h2>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Предмет</th>
|
||
<th>Первичный балл</th>
|
||
<th>Оценка</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<% electiveResults.forEach(r => { %>
|
||
<tr>
|
||
<td><%= r.subject_name %></td>
|
||
<td><%= r.primary_score !== null ? r.primary_score : '—' %></td>
|
||
<td><strong><%= r.grade !== null ? r.grade : '—' %></strong></td>
|
||
</tr>
|
||
<% }) %>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
<% } %>
|
||
|
||
<div class="card">
|
||
<h2>Сформировать справку</h2>
|
||
<form id="cert-form">
|
||
<div class="form-row">
|
||
<label>
|
||
<input type="radio" name="cert-type" value="gia" checked onchange="toggleElectives()">
|
||
ГИА (ОГЭ) — 4 предмета, с первичными баллами
|
||
</label>
|
||
</div>
|
||
<div class="form-row">
|
||
<label>
|
||
<input type="radio" name="cert-type" value="gve" onchange="toggleElectives()">
|
||
ГВЭ — 2 предмета, только отметки
|
||
</label>
|
||
</div>
|
||
|
||
<div id="electives-block" style="margin-top:10px">
|
||
<p>Выберите 1–2 предмета для справки ГИА:</p>
|
||
<% electiveResults.forEach(r => { %>
|
||
<label class="checkbox-label">
|
||
<input type="checkbox" name="elective" value="<%= r.subject_code %>" class="elective-cb">
|
||
<%= r.subject_name %>
|
||
</label>
|
||
<% }) %>
|
||
<span id="elective-msg" class="msg"></span>
|
||
</div>
|
||
|
||
<button type="submit" class="btn btn-blue" style="margin-top:15px">Сформировать и распечатать</button>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
|
||
<script>
|
||
window.__CSRF_TOKEN = '<%= csrfToken %>';
|
||
|
||
const _origFetch = window.fetch.bind(window);
|
||
window.fetch = function(input, init = {}) {
|
||
init.credentials = 'include';
|
||
const method = (init.method || 'GET').toUpperCase();
|
||
if (['POST','PUT','PATCH','DELETE'].includes(method)) {
|
||
if (!init.headers) init.headers = {};
|
||
init.headers['X-CSRF-Token'] = window.__CSRF_TOKEN || '';
|
||
}
|
||
return _origFetch(input, init);
|
||
};
|
||
|
||
document.getElementById('logout-btn').addEventListener('click', async () => {
|
||
await fetch('/api/logout', { method: 'POST' });
|
||
window.location.href = '/login';
|
||
});
|
||
|
||
document.getElementById('mandatory-form').addEventListener('submit', async (e) => {
|
||
e.preventDefault();
|
||
const score = v => { const n = parseInt(v,10); return isNaN(n)?null:n; };
|
||
const res = await fetch('/api/students/<%= student.id %>/mandatory', {
|
||
method: 'PUT',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({
|
||
rus_score: score(document.getElementById('rus-score').value),
|
||
rus_grade: score(document.getElementById('rus-grade').value),
|
||
math_score: score(document.getElementById('math-score').value),
|
||
math_grade: score(document.getElementById('math-grade').value)
|
||
})
|
||
});
|
||
const msg = document.getElementById('mandatory-msg');
|
||
if (res.ok) { msg.textContent = 'Сохранено'; msg.className = 'msg msg-ok'; }
|
||
else { msg.textContent = 'Ошибка'; msg.className = 'msg msg-err'; }
|
||
});
|
||
|
||
document.querySelectorAll('.elective-cb').forEach(cb => {
|
||
cb.addEventListener('change', () => {
|
||
const checked = document.querySelectorAll('.elective-cb:checked');
|
||
if (checked.length > 2) cb.checked = false;
|
||
});
|
||
});
|
||
|
||
function toggleElectives() {
|
||
const type = document.querySelector('input[name="cert-type"]:checked').value;
|
||
document.getElementById('electives-block').style.display = type === 'gia' ? 'block' : 'none';
|
||
}
|
||
|
||
document.getElementById('cert-form').addEventListener('submit', (e) => {
|
||
e.preventDefault();
|
||
const type = document.querySelector('input[name="cert-type"]:checked').value;
|
||
const electives = [...document.querySelectorAll('.elective-cb:checked')].map(cb => cb.value);
|
||
const msg = document.getElementById('elective-msg');
|
||
|
||
if (type === 'gia' && electives.length < 1) {
|
||
msg.textContent = 'Выберите хотя бы 1 предмет для ГИА';
|
||
msg.className = 'msg msg-err';
|
||
return;
|
||
}
|
||
msg.textContent = '';
|
||
const url = `/certificate/<%= student.id %>?type=${type}&subjects=${electives.join(',')}`;
|
||
window.open(url, '_blank');
|
||
});
|
||
</script>
|
||
</body>
|
||
</html>
|