diff --git a/backend/src/controllers/assignmentController.js b/backend/src/controllers/assignmentController.js index b635729..1e6df80 100644 --- a/backend/src/controllers/assignmentController.js +++ b/backend/src/controllers/assignmentController.js @@ -2,6 +2,7 @@ const db = require('../db/db'); const { pushNotif } = require('../utils/notifications'); const { stripTags } = require('../utils/sanitize'); const { SESSION_MODES } = require('../constants'); +const AssignmentUtils = require('../../../frontend/js/assignment-utils.js'); // единый источник: тип/«сдано» const VALID_ASSIGN_MODES = SESSION_MODES; @@ -344,14 +345,6 @@ function myAssignments(req, res) { res.json(assignmentRowsForUser(req.user.id)); } -/* Тип задания — те же признаки, что на /homework (asgnType). */ -function _assignTypeOf(r) { - if (r.textbook_id) return 'textbook'; - if (r.file_id) return 'file'; - if (r.is_homework && (r.count == null || r.count <= 1) && (!r.subject_slug || r.subject_slug === 'other')) return 'upload'; - return 'test'; -} - /* ── GET /api/classes/:id/outstanding ── что «висит» у каждого ученика класса ── Учитель/админ видят по каждому ученику его НЕзакрытые задания (классовые + личные от этого учителя) со статусом: не начато / в процессе / на доработке / просрочено. */ @@ -388,23 +381,16 @@ function classOutstanding(req, res) { ); const pending = []; for (const r of rows) { - const type = _assignTypeOf(r); + const t = AssignmentUtils.type(r); + const st = (t === 'upload' || t === 'file') ? subMap.get(r.id + '_' + m.id) : null; + // Учительская семантика: любая сдача не на доработке = не долг (default opts). + if (AssignmentUtils.isDone(r, st ? { status: st } : null)) continue; const overdue = r.deadline && new Date(r.deadline).getTime() < now; - let done = false, status = overdue ? 'overdue' : 'not_started'; - if (type === 'textbook') { - done = !!(r.textbook_all_read || r.completed_at); - } else if (type === 'upload' || type === 'file') { - const st = subMap.get(r.id + '_' + m.id); - if (st && st !== 'revision') done = true; // сдал — на проверке/принято - else if (st === 'revision') status = 'revision'; // вернули на доработку - } else { // test - const maxAtt = r.max_attempts || 0, used = r.attempts_used || 0; - if (maxAtt > 0 && used >= maxAtt) done = true; - else if (r.session_status === 'completed' && r.mode !== 'repeat') done = true; - else if (r.session_status === 'in_progress') status = 'in_progress'; - } - if (!done) pending.push({ - assignment_id: r.id, title: r.title, type, deadline: r.deadline, + let status = overdue ? 'overdue' : 'not_started'; + if (st === 'revision') status = 'revision'; // вернули на доработку + else if (t === 'test' && r.session_status === 'in_progress') status = 'in_progress'; + pending.push({ + assignment_id: r.id, title: r.title, type: t, deadline: r.deadline, status, is_homework: r.is_homework ? 1 : 0, scope: r.class_id === cidNum ? 'class' : 'direct', }); diff --git a/frontend/dashboard.html b/frontend/dashboard.html index 5797f9a..3a85a0a 100644 --- a/frontend/dashboard.html +++ b/frontend/dashboard.html @@ -1889,6 +1889,7 @@ + @@ -2381,15 +2382,8 @@ body.classList.toggle('collapsed'); } - /* ── Urgency sort score (lower = shown first) ── */ - function urgencyScore(a) { - if (a.session_status === 'in_progress') return -4; // in progress top - const dlMs = a.deadline ? new Date(a.deadline) - Date.now() : Infinity; - if (dlMs < 0) return -3; // overdue - if (dlMs < 24 * 3600 * 1000) return -2; // urgent <24h - if (dlMs < Infinity) return dlMs; // sorted by deadline - return 1e12; // no deadline last - } + /* ── Urgency sort score (lower = shown first) — общий модуль ── */ + function urgencyScore(a) { return AssignmentUtils.urgencyScore(a); } /* ── Is assignment urgent for teacher (within 48h) ── */ function isTeacherUrgent(a) { @@ -2457,7 +2451,7 @@ } /* ── Upload-only homework (no test, no file) ── */ - if (a.is_homework && !a.file_id && !a.session_id && a.count <= 1 && (!a.subject_slug || a.subject_slug === 'other')) { + if (AssignmentUtils.type(a) === 'upload') { const over = a.deadline && new Date(a.deadline) < new Date(); const sub = _mySubmissions.get(a.id); const metaParts = [classStr, dl ? `до ${dl}` : null, @@ -2694,18 +2688,22 @@ reIcons(); return; } - // Classify + // Classify (active/overdue/done) — тип и «сдано» из общего модуля AssignmentUtils. function classify(a) { - const maxAtt = a.max_attempts || 0; - const usedAtt = a.attempts_used ?? 0; - if (a.textbook_id) { - if (a.completed_at || a.textbook_all_read) return 'done'; + const t = AssignmentUtils.type(a); + if (t === 'textbook') { + if (AssignmentUtils.isDone(a)) return 'done'; if (a.deadline && new Date(a.deadline) < now) return 'overdue'; return 'active'; } - if (maxAtt > 0 && usedAtt >= maxAtt) return 'done'; - if (a.session_status === 'completed' && a.mode !== 'repeat') return 'done'; - if (!a.file_id && a.deadline && new Date(a.deadline) < now && a.session_status !== 'in_progress') return 'overdue'; + if (t === 'test') { + if (AssignmentUtils.isDone(a)) return 'done'; + if (a.deadline && new Date(a.deadline) < now && a.session_status !== 'in_progress') return 'overdue'; + return 'active'; + } + // upload / file: «сдано» по сабмишену здесь не считаем (как и раньше — статус + // показывает чип сдачи в карточке); upload просрочивается по дедлайну, file — всегда активен. + if (t === 'upload' && a.deadline && new Date(a.deadline) < now) return 'overdue'; return 'active'; } diff --git a/frontend/homework.html b/frontend/homework.html index 2f0578c..507e987 100644 --- a/frontend/homework.html +++ b/frontend/homework.html @@ -258,6 +258,7 @@ +