Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions classroom/assets/css/main.css
Original file line number Diff line number Diff line change
Expand Up @@ -937,6 +937,12 @@ button {



/* Skip rendering of off-screen rows — browser handles it natively, no JS needed */
#body-table-teach tr {
content-visibility: auto;
contain-intrinsic-size: 0 44px;
}

#body-table-teach .propic {
width: 36px;
height: 36px;
Expand Down
37 changes: 36 additions & 1 deletion classroom/assets/js/main/ClassroomManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,14 @@ class ClassroomManager {
classroom.classroom.name = `${classroom.classroom.name} | ${classroom.classroom.groupe}`;
}
}
// Preserve already lazy-loaded students so they are not wiped
const oldClasses = container._myClasses || [];
for (let newEntry of response) {
const existing = oldClasses.find(c => c.classroom.id === newEntry.classroom.id);
if (existing && existing.students.length > 0) {
newEntry.students = existing.students;
}
}
container._myClasses = response;
Main.getClassroomManager().countClassroomAndStudents(response);
onEnd();
Expand All @@ -310,11 +318,38 @@ class ClassroomManager {
};
data.forEach((classroom) => {
teacherData.classrooms++;
teacherData.students += classroom.students.length;
// Use server-provided studentCount (students array is lazy-loaded)
teacherData.students += classroom.studentCount ?? classroom.students.length;
});
UserManager.getUser().teacherData = teacherData;
}

/**
* Load full student data for one classroom (lazy, called on click).
* Stores result in _myClasses so subsequent clicks don't re-fetch.
*/
getClassroomStudents(classroomId) {
return new Promise((resolve, reject) => {
$.ajax({
type: 'POST',
dataType: 'JSON',
url: '/routing/Routing.php?controller=classroom&action=get_classroom_students',
data: { classroomId: classroomId },
success: (response) => {
if (response.error) {
console.error('getClassroomStudents error:', response.error);
resolve([]);
return;
}
const entry = this._myClasses.find(c => c.classroom.id == classroomId);
if (entry) entry.students = response.students;
resolve(response.students);
},
error: (e) => { console.error(e); reject(e); }
});
});
}

/**
* Get teachers from the classroom
* Access with Main.getClassroomManager()._myTeachers
Expand Down
4 changes: 2 additions & 2 deletions classroom/assets/js/scripts/buttons.js
Original file line number Diff line number Diff line change
Expand Up @@ -1077,7 +1077,7 @@ function classroomsDisplay() {
let classes = Main.getClassroomManager()._myClasses;
if (classes.length) {
classes.forEach(classroom => {
$('.list-classes').append(classeItem(classroom.classroom, classroom.students.length, classroom.students));
$('.list-classes').append(classeItem(classroom.classroom, classroom.studentCount ?? classroom.students.length, classroom.students, classroom.pendingCorrections, classroom.activitiesCount));
});
} else {
$('.list-classes').append(noContentDiv).localize();
Expand All @@ -1088,7 +1088,7 @@ function classroomsDisplay() {
let classes = Main.getClassroomManager()._myClasses;
if (classes.length) {
classes.forEach(classroom => {
$('.list-classes').append(classeItem(classroom.classroom, classroom.students.length, classroom.students));
$('.list-classes').append(classeItem(classroom.classroom, classroom.studentCount ?? classroom.students.length, classroom.students, classroom.pendingCorrections, classroom.activitiesCount));
});
} else {
$('.list-classes').append(noContentDiv).localize();
Expand Down
8 changes: 5 additions & 3 deletions classroom/assets/js/scripts/classroomDisplay.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,11 +78,13 @@ $('body').on('change', '#is-anonymised', function () {
});

function anonymizeStudents() {
$('.username').each(function (index,el) {
$('.username').each(function (index, el) {
// Use absolute student index embedded during virtual scroll rendering
const absIdx = parseInt($(el).closest('tr').attr('data-student-idx') ?? index);
$(el).children().children('img').attr('src', _PATH + 'assets/media/alphabet/E.png')
$(el).children().children('img').attr('alt', 'Photo de profil anonymisée - Étudiant ' + (index + 1))
$(el).children().children('img').attr('alt', 'Photo de profil anonymisée - Étudiant ' + (absIdx + 1))
$(el).children().children('img').attr('anonymized', 'true')
$(el).children().children('.user-cell-username').text(i18next.t('classroom.activities.anoStudent') + " " + index)
$(el).children().children('.user-cell-username').text(i18next.t('classroom.activities.anoStudent') + " " + absIdx)
$(el).children().children('.user-cell-username').attr('title', '')
})
}
Expand Down
6 changes: 3 additions & 3 deletions classroom/assets/js/scripts/dashboardActivities.js
Original file line number Diff line number Diff line change
Expand Up @@ -342,7 +342,7 @@ function teacherFolder(folder, displayStyle) {
}


function classeItem(classe, nbStudents, students) {
function classeItem(classe, nbStudents, students, overridePendingCorrections = null, overrideActivitiesCount = null) {
function maxLength(array) {
let count = 0
for (let i = 0; i < array.length; i++) {
Expand All @@ -353,8 +353,8 @@ function classeItem(classe, nbStudents, students) {
return count
}

let maxAct = maxLength(students)
let remainingCorrections = getRemainingCorrections(students);
let maxAct = overrideActivitiesCount !== null ? overrideActivitiesCount : maxLength(students);
let remainingCorrections = overridePendingCorrections !== null ? overridePendingCorrections : getRemainingCorrections(students);

let remainingCorrectionsSpanElt = remainingCorrections ? `<span class="results-correcting c-text-secondary position-absolute bottom-0 end-0 m-2" role="status" aria-label="${remainingCorrections} corrections en attente"><i class="fas fa-pen" aria-hidden="true"></i> ${remainingCorrections}</span>` : '';
let html = `<div class="class-item" role="listitem">
Expand Down
16 changes: 14 additions & 2 deletions classroom/assets/js/scripts/displayPanel.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,10 @@ DisplayPanel.prototype.classroom_dashboard_profil_panel_teacher = function () {
})

getIntelFromClasses();
const correctionsCount = getRemainingCorrections(Main.getClassroomManager()._myClasses.flatMap(c => c.students));
// pendingCorrections is pre-computed server-side in the lightweight get_by_user response
const correctionsCount = Main.getClassroomManager()._myClasses.reduce(
(sum, c) => sum + (c.pendingCorrections ?? getRemainingCorrections(c.students ?? [])), 0
);
const correctionsElement = $('.tocorrect-activities');
correctionsElement.html(correctionsCount);

Expand Down Expand Up @@ -468,7 +471,16 @@ function updateUIWithStudents(link) {

updateClassroomLinkUI(ClassroomSettings.classroom);
updateClassroomLockUI(classroomData.classroom.isBlocked);
displayStudentsInClassroom(classroomData.students, link);

if (classroomData.students && classroomData.students.length > 0) {
// Already loaded (cached from a previous click)
displayStudentsInClassroom(classroomData.students, link);
} else {
// Lazy-load: first click on this classroom
Main.getClassroomManager().getClassroomStudents(classroomData.classroom.id).then(students => {
displayStudentsInClassroom(students, link);
});
}
}

DisplayPanel.prototype.classroom_dashboard_new_activity_panel3 = function (ref) {
Expand Down
75 changes: 41 additions & 34 deletions classroom/assets/js/scripts/manageClassroom.js
Original file line number Diff line number Diff line change
Expand Up @@ -1080,7 +1080,8 @@ function displayStudentsInClassroom(students, link=false) {
// get the current classroom index of activities
let arrayIndexesActivities = listIndexesActivities(students);

students.forEach(element => {
const studentRows = [];
students.forEach((element, elementIdx) => {
// reorder the current student activities to fit to the classroom index of activities
let arrayActivities = reorderActivities([...element.activities, ...element.courses], arrayIndexesActivities);
let pseudo = element.user.pseudo;
Expand Down Expand Up @@ -1370,10 +1371,22 @@ function displayStudentsInClassroom(students, link=false) {
}
// end of the current table row
html += '</tr>';
$('#body-table-teach').append(html).localize();
$('[data-bs-toggle="tooltip"]').tooltip()
studentRows.push(html);
});


// Preload unique letter images (max 26 requests); browser cache serves subsequent rows
new Set(students.map(s => getFirstLetterOfPseudo(s.user.pseudo))).forEach(letter => {
const _img = new Image();
_img.src = `${_PATH}assets/media/alphabet/${letter}.png`;
});

// Render all students directly into the table
const tbody = document.getElementById('body-table-teach');
const tmpTbody = document.createElement('tbody');
tmpTbody.innerHTML = studentRows.join('');
Array.from(tmpTbody.children).forEach(el => tbody.appendChild(el));
$(tbody).find('[data-bs-toggle="tooltip"]').tooltip();
$(tbody).localize();
appendAddStudentButton();
// get classroom settings from localstorage
let settings = getClassroomDisplaySettings(link);
Expand Down Expand Up @@ -1418,18 +1431,19 @@ function displayStudentsInClassroom(students, link=false) {
// add four empty divs for monochrome styling
$('#body-table-teach .bilan-cell').html(`<div class="monochrome-grade-div"></div><div class="monochrome-grade-div"></div><div class="monochrome-grade-div"></div><div class="monochrome-grade-div"></div>`);

$('#classroom-panel-table-container table .dropdown').on('show.bs.dropdown', (event) => {
let classroomTable = event.target.closest('table');
classroomTable.classList.add('dropdowns-opened');
$(classroomTable).find('tr').addClass('non-dropdown');
event.target.closest('tr').classList.remove('non-dropdown');
});

$('#classroom-panel-table-container table .dropdown').on('hidden.bs.dropdown', (event) => {
let classroomTable = event.target.closest('table');
classroomTable.classList.remove('dropdowns-opened');
$(classroomTable).find('tr').removeClass('non-dropdown');
});
// Delegated event binding so lazy-loaded rows also receive these handlers
$('#classroom-panel-table-container table').off('show.bs.dropdown.lazyrows hidden.bs.dropdown.lazyrows')
.on('show.bs.dropdown.lazyrows', '.dropdown', (event) => {
let classroomTable = event.target.closest('table');
classroomTable.classList.add('dropdowns-opened');
$(classroomTable).find('tr').addClass('non-dropdown');
event.target.closest('tr').classList.remove('non-dropdown');
})
.on('hidden.bs.dropdown.lazyrows', '.dropdown', (event) => {
let classroomTable = event.target.closest('table');
classroomTable.classList.remove('dropdowns-opened');
$(classroomTable).find('tr').removeClass('non-dropdown');
});

// Plugin hook: post-render callback (no-op without plugin)
if (typeof window.onDashboardRendered === 'function') {
Expand Down Expand Up @@ -1994,27 +2008,20 @@ class DashboardAutoRefresh {
refresh() {
if($_GET('panel') == 'classroom-table-panel-teacher' && $_GET('option')){
this.isRefreshing = true;
let previousClassroomState, newClassroomState;
if (getClassroomInListByLink($_GET('option'))[0]) {
previousClassroomState = {
data: JSON.stringify(getClassroomInListByLink($_GET('option'))[0].students),
link: $_GET('option')
};
}
Main.getClassroomManager().getClasses(Main.getClassroomManager()).then(() => {
if ($_GET('option') == previousClassroomState.link) {
if (getClassroomInListByLink($_GET('option'))[0]) {
newClassroomState = JSON.stringify(getClassroomInListByLink($_GET('option'))[0].students);
}
// Only refresh the classroom if it has changed
if (previousClassroomState.data != newClassroomState){
if (getClassroomInListByLink($_GET('option'))[0]) {
let students = getClassroomInListByLink($_GET('option'))[0].students;
const link = $_GET('option');
const entry = getClassroomInListByLink(link)?.[0];
if (entry) {
const previousStudentsJson = JSON.stringify(entry.students);
Main.getClassroomManager().getClassroomStudents(entry.classroom.id).then((students) => {
if ($_GET('option') == link) {
const newStudentsJson = JSON.stringify(students);
// Only refresh the classroom display if data has changed
if (previousStudentsJson != newStudentsJson) {
displayStudentsInClassroom(students);
}
}
}
});
});
}
setTimeout(() => { this.refresh() }, this.refreshInterval);
} else {
this.isRefreshing = false;
Expand Down
15 changes: 10 additions & 5 deletions classroom/assets/js/scripts/teacherActivities.js
Original file line number Diff line number Diff line change
Expand Up @@ -380,13 +380,18 @@ function listStudentsToAttribute(ref = null) {
if (classes.length == 0) {
$('#attribute-activity-to-students-close').after(NO_CLASS)
$('#attribute-activity-to-students-close').hide()

} else {
classes.forEach(element => {
$('#list-student-attribute-modal').append(classeList(element, ref))
const pending = classes
.filter(c => c.students.length === 0)
.map(c => Main.getClassroomManager().getClassroomStudents(c.classroom.id));

Promise.all(pending).then(() => {
classes.forEach(element => {
$('#list-student-attribute-modal').append(classeList(element, ref))
});
$('.no-classes').remove()
$('#attribute-activity-to-students-close').show()
});
$('.no-classes').remove()
$('#attribute-activity-to-students-close').show()
}
}

Expand Down
4 changes: 2 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@
},
"require-dev": {
"phpunit/phpunit": "8.0.0",
"vittascience/vtuser": "1.3.5",
"vittascience/vtuser": "1.3.6",
"vittascience/vtinterfaces": "1.5.8",
"vittascience/vtclassroom": "1.4.3",
"vittascience/vtclassroom": "dev-arz-optimisation",
"vittascience/vtlearn": "1.4.6",
"vittascience/vutils": "1.3.1",
"symfony/var-dumper": "^5.3"
Expand Down