From 548b1dd59c031561d2f164789750eba6b745d111 Mon Sep 17 00:00:00 2001 From: kalugin66 Date: Fri, 3 Jul 2026 12:02:46 +0500 Subject: [PATCH] =?UTF-8?q?=D0=9F=D1=80=D0=B5=D0=B4=D0=BC=D0=B5=D1=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/style.css | 27 +++++++++++++++++++++++++++ src/routes/api.js | 31 +++++++++++++++++++++++++++++++ views/index.ejs | 34 ++++++++++++++++++++++++++++++++++ 3 files changed, 92 insertions(+) diff --git a/public/style.css b/public/style.css index e0c8117..f4256b2 100644 --- a/public/style.css +++ b/public/style.css @@ -39,9 +39,36 @@ h2 { margin: 15px 0 8px; font-size: 16px; } max-width: 300px; } .input-sm { max-width: 120px; } +.input-xs { max-width: 80px; } + +#subject-select { max-width: 220px; } .search-bar { margin-bottom: 15px; } +.filter-extras { + margin-bottom: 12px; + padding: 8px 12px; + background: #fafafa; + border: 1px solid #e0e0e0; + border-radius: 4px; +} +.filter-extras .form-row { + margin-bottom: 0; + display: flex; + align-items: center; + gap: 6px; + flex-wrap: wrap; +} +.filter-extras .form-row label { + min-width: auto; + font-weight: 500; + font-size: 13px; + margin-right: 2px; +} +.filter-extras .form-row label:not(:first-child) { + margin-left: 10px; +} + .filter-bar { display: flex; gap: 12px; diff --git a/src/routes/api.js b/src/routes/api.js index 7be0c78..8011e55 100644 --- a/src/routes/api.js +++ b/src/routes/api.js @@ -98,9 +98,19 @@ router.post('/import', upload.single('xlsfile'), (req, res) => { } }); +router.get('/subjects', (req, res) => { + const rows = dbAll('SELECT code, name FROM subjects ORDER BY is_mandatory DESC, name'); + res.json(rows); +}); + router.get('/students', (req, res) => { const search = req.query.search || ''; const filter = req.query.filter || ''; + const subjectCode = req.query.subject_code || ''; + const scoreMin = req.query.score_min; + const scoreMax = req.query.score_max; + const gradeMin = req.query.grade_min; + const gradeMax = req.query.grade_max; const baseQuery = ` SELECT s.*, @@ -125,6 +135,27 @@ router.get('/students', (req, res) => { params.push(like, like, like, like); } + if (subjectCode) { + whereClauses.push('er.subject_code = ?'); + params.push(subjectCode); + } + if (scoreMin !== undefined && scoreMin !== '') { + whereClauses.push('er.primary_score >= ?'); + params.push(parseInt(scoreMin, 10)); + } + if (scoreMax !== undefined && scoreMax !== '') { + whereClauses.push('er.primary_score <= ?'); + params.push(parseInt(scoreMax, 10)); + } + if (gradeMin !== undefined && gradeMin !== '') { + whereClauses.push('er.grade >= ?'); + params.push(parseInt(gradeMin, 10)); + } + if (gradeMax !== undefined && gradeMax !== '') { + whereClauses.push('er.grade <= ?'); + params.push(parseInt(gradeMax, 10)); + } + let havingClause = ''; if (filter === 'gia' || filter === 'oge') { havingClause = 'HAVING elective_count > 0'; diff --git a/views/index.ejs b/views/index.ejs index 023fc13..316cf92 100644 --- a/views/index.ejs +++ b/views/index.ejs @@ -72,6 +72,21 @@ +
+
+ + + + + + + + +
+
+ @@ -125,9 +140,19 @@ async function loadStudents() { const search = document.getElementById('search').value; const filter = document.querySelector('input[name="filter"]:checked').value; + const subjectCode = document.getElementById('subject-select').value; + const scoreMin = document.getElementById('score-min').value; + const scoreMax = document.getElementById('score-max').value; + const gradeMin = document.getElementById('grade-min').value; + const gradeMax = document.getElementById('grade-max').value; const params = new URLSearchParams(); if (search) params.set('search', search); if (filter && filter !== 'all') params.set('filter', filter); + if (subjectCode) params.set('subject_code', subjectCode); + if (scoreMin) params.set('score_min', scoreMin); + if (scoreMax) params.set('score_max', scoreMax); + if (gradeMin) params.set('grade_min', gradeMin); + if (gradeMax) params.set('grade_max', gradeMax); const url = '/api/students' + (params.toString() ? '?' + params.toString() : ''); const res = await fetch(url); allStudents = await res.json(); @@ -145,6 +170,14 @@ parSel.innerHTML = parallels.map(p => ``).join(''); } + async function loadSubjects() { + const res = await fetch('/api/subjects'); + const subjects = await res.json(); + const sel = document.getElementById('subject-select'); + sel.innerHTML = '' + + subjects.map(s => ``).join(''); + } + function toggleBatchMode() { const scope = document.querySelector('input[name="batch-scope"]:checked').value; document.getElementById('class-select').disabled = scope !== 'class'; @@ -236,6 +269,7 @@ loadStudents(); loadClasses(); + loadSubjects();