push 18112025
This commit is contained in:
parent
226d8b18f4
commit
bd0999dce6
@ -62,7 +62,7 @@ async function insertEtudiant(
|
||||
* @returns JSON
|
||||
*/
|
||||
async function getAllEtudiants() {
|
||||
const query = database.prepare('SELECT * FROM etudiants ORDER BY annee_scolaire DESC')
|
||||
const query = database.prepare('SELECT e.*, m.uniter AS mentionUnite, m.nom As nomMention FROM etudiants e JOIN mentions m ON e.mention_id = m.id ORDER BY annee_scolaire DESC')
|
||||
try {
|
||||
let response = await query.all()
|
||||
|
||||
|
||||
@ -65,7 +65,7 @@ async function insertEtudiant(
|
||||
* @returns JSON
|
||||
*/
|
||||
async function getAllEtudiants() {
|
||||
const sql = 'SELECT * FROM etudiants ORDER BY annee_scolaire DESC'
|
||||
const sql = 'SELECT e.*, m.uniter AS mentionUnite, m.nom As nomMention FROM etudiants e JOIN mentions m ON e.mention_id = m.id ORDER BY annee_scolaire DESC'
|
||||
try {
|
||||
let [rows] = await pool.query(sql)
|
||||
|
||||
@ -83,7 +83,7 @@ async function getAllEtudiants() {
|
||||
* @returns Promise
|
||||
*/
|
||||
async function getSingleEtudiant(id) {
|
||||
const sql = 'SELECT e.*, m.uniter AS mentionUnite FROM etudiants e JOIN mentions m ON e.mention_id = m.id WHERE e.id = ?'
|
||||
const sql = 'SELECT e.*, m.uniter AS mentionUnite, m.nom As nomMention FROM etudiants e JOIN mentions m ON e.mention_id = m.id WHERE e.id = ?'
|
||||
|
||||
try {
|
||||
const [rows] = await pool.query(sql, [id])
|
||||
@ -100,7 +100,7 @@ async function getSingleEtudiant(id) {
|
||||
* @returns JSON
|
||||
*/
|
||||
async function FilterDataByNiveau(niveau) {
|
||||
const sql = 'SELECT * FROM etudiants WHERE niveau = ? ORDER BY annee_scolaire DESC'
|
||||
const sql = 'SELECT e.*, m.uniter AS mentionUnite, m.nom As nomMention FROM etudiants e JOIN mentions m ON e.mention_id = m.id WHERE niveau = ? ORDER BY annee_scolaire DESC'
|
||||
try {
|
||||
let [rows] = await pool.query(sql, [niveau])
|
||||
|
||||
|
||||
@ -1,14 +1,14 @@
|
||||
const mysql = require('mysql2/promise')
|
||||
const bcrypt = require('bcryptjs')
|
||||
|
||||
const pool = mysql.createPool({
|
||||
host: '192.168.200.200',
|
||||
user: 'root',
|
||||
password: 'stephane1313',
|
||||
database: 'university',
|
||||
waitForConnections: true,
|
||||
connectionLimit: 10,
|
||||
queueLimit: 0
|
||||
const pool = mysql.createPool({
|
||||
host: '192.168.200.200',
|
||||
user: 'root',
|
||||
password: 'stephane1313',
|
||||
database: 'university',
|
||||
waitForConnections: true,
|
||||
connectionLimit: 10,
|
||||
queueLimit: 0
|
||||
})
|
||||
|
||||
async function createTables() {
|
||||
|
||||
@ -8,7 +8,7 @@ const dayjs = require('dayjs');
|
||||
const customParseFormat = require('dayjs/plugin/customParseFormat');
|
||||
dayjs.extend(customParseFormat);
|
||||
|
||||
// ✅ Fonction de correction d'encodage
|
||||
// ---------- Fonctions utilitaires ----------
|
||||
function fixEncoding(str) {
|
||||
if (typeof str !== 'string') return str;
|
||||
return str
|
||||
@ -21,156 +21,105 @@ function fixEncoding(str) {
|
||||
.replace(/â€/g, '…')
|
||||
.replace(/â€/g, '-');
|
||||
}
|
||||
|
||||
function convertToISODate(input) {
|
||||
if (!input) return null;
|
||||
|
||||
console.log('🔍 Input original:', input, 'Type:', typeof input);
|
||||
|
||||
// Si input est un objet Date valide
|
||||
if (input instanceof Date && !isNaN(input)) {
|
||||
const result = dayjs(input).format('YYYY-MM-DD');
|
||||
console.log('📅 Date object convertie:', result);
|
||||
return result;
|
||||
return dayjs(input).format('YYYY-MM-DD');
|
||||
}
|
||||
|
||||
// Si input est un nombre (numéro de série Excel)
|
||||
if (typeof input === 'number') {
|
||||
// Formule Excel: (numéro - 25569) * 86400 * 1000
|
||||
const excelDate = new Date((input - 25569) * 86400 * 1000);
|
||||
const result = dayjs(excelDate).format('YYYY-MM-DD');
|
||||
console.log('📊 Numéro Excel', input, 'converti en:', result);
|
||||
return result;
|
||||
return dayjs(new Date((input - 25569) * 86400 * 1000)).format('YYYY-MM-DD');
|
||||
}
|
||||
|
||||
// Si input est une chaîne
|
||||
if (typeof input === 'string') {
|
||||
const cleanInput = input.trim();
|
||||
|
||||
// Cas spécial "vers YYYY"
|
||||
const versMatch = cleanInput.match(/vers\s*(\d{4})/i);
|
||||
if (versMatch) {
|
||||
const result = `${versMatch[1]}-01-01`;
|
||||
console.log('📝 "Vers" détecté:', result);
|
||||
return result;
|
||||
}
|
||||
if (versMatch) return `${versMatch[1]}-01-01`;
|
||||
|
||||
// Formats à tester dans l'ordre de priorité
|
||||
const formats = [
|
||||
'DD/MM/YYYY', 'D/M/YYYY', // Format français prioritaire
|
||||
'YYYY-MM-DD', // Format ISO
|
||||
'DD-MM-YYYY', 'D-M-YYYY', // Format français avec tirets
|
||||
'MM/DD/YYYY', 'M/D/YYYY', // Format américain
|
||||
'MM-DD-YYYY', 'M-D-YYYY', // Format américain avec tirets
|
||||
'DD/MM/YY', 'D/M/YY', // Années courtes
|
||||
'DD/MM/YYYY', 'D/M/YYYY',
|
||||
'YYYY-MM-DD',
|
||||
'DD-MM-YYYY', 'D-M-YYYY',
|
||||
'MM/DD/YYYY', 'M/D/YYYY',
|
||||
'MM-DD-YYYY', 'M-D-YYYY',
|
||||
'DD/MM/YY', 'D/M/YY',
|
||||
'MM/DD/YY', 'M/D/YY',
|
||||
'DD-MM-YY', 'D-M-YY',
|
||||
'MM-DD-YY', 'M-D-YY'
|
||||
];
|
||||
|
||||
// Test avec parsing strict pour éviter les interprétations erronées
|
||||
for (const format of formats) {
|
||||
const parsedDate = dayjs(cleanInput, format, true); // true = strict parsing
|
||||
if (parsedDate.isValid()) {
|
||||
const result = parsedDate.format('YYYY-MM-DD');
|
||||
console.log(`✅ Format "${format}" réussi:`, cleanInput, '->', result);
|
||||
|
||||
// Vérification supplémentaire pour les dates invalides comme 29/02 en année non-bissextile
|
||||
if (format.includes('DD/MM') || format.includes('D/M')) {
|
||||
const day = parsedDate.date();
|
||||
const month = parsedDate.month() + 1; // dayjs month is 0-indexed
|
||||
const year = parsedDate.year();
|
||||
|
||||
// Vérifier si c'est le 29 février d'une année non-bissextile
|
||||
if (month === 2 && day === 29 && !isLeapYear(year)) {
|
||||
console.warn('⚠️ Date invalide détectée: 29 février en année non-bissextile');
|
||||
return null; // ou retourner une date par défaut
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
for (const fmt of formats) {
|
||||
const parsed = dayjs(cleanInput, fmt, true);
|
||||
if (parsed.isValid()) return parsed.format('YYYY-MM-DD');
|
||||
}
|
||||
|
||||
// Si aucun format strict ne fonctionne, essayer le parsing libre en dernier recours
|
||||
const freeParseDate = dayjs(cleanInput);
|
||||
if (freeParseDate.isValid()) {
|
||||
const result = freeParseDate.format('YYYY-MM-DD');
|
||||
console.log('🆓 Parsing libre réussi:', cleanInput, '->', result);
|
||||
return result;
|
||||
}
|
||||
const freeParse = dayjs(cleanInput);
|
||||
if (freeParse.isValid()) return freeParse.format('YYYY-MM-DD');
|
||||
}
|
||||
|
||||
console.error('❌ Impossible de convertir:', input);
|
||||
return null;
|
||||
}
|
||||
|
||||
// Fonction utilitaire pour vérifier les années bissextiles
|
||||
// Vérifie année bissextile
|
||||
function isLeapYear(year) {
|
||||
return (year % 4 === 0 && year % 100 !== 0) || (year % 400 === 0);
|
||||
}
|
||||
|
||||
// ✅ Mise à jour d'un étudiant existant
|
||||
// ---------- UPDATE étudiant ----------
|
||||
async function updateEtudiant(row) {
|
||||
const sql = `
|
||||
UPDATE etudiants SET
|
||||
nom = ?,
|
||||
prenom = ?,
|
||||
photos = ?,
|
||||
date_de_naissances = ?,
|
||||
niveau = ?,
|
||||
annee_scolaire = ?,
|
||||
status = ?,
|
||||
mention_id = ?,
|
||||
num_inscription = ?,
|
||||
sexe = ?,
|
||||
date_delivrance = ?,
|
||||
nationalite = ?,
|
||||
annee_bacc = ?,
|
||||
serie = ?,
|
||||
boursier = ?,
|
||||
domaine = ?,
|
||||
contact = ?,
|
||||
parcours = ?
|
||||
WHERE cin = ? OR (LOWER(nom) = ? AND LOWER(prenom) = ?)
|
||||
`;
|
||||
const fields = [];
|
||||
const params = [];
|
||||
|
||||
const params = [
|
||||
row.nom,
|
||||
row.prenom,
|
||||
getCompressedDefaultImage(),
|
||||
convertToISODate(row.date_naissance),
|
||||
row.niveau,
|
||||
row.annee_scolaire,
|
||||
row.code_redoublement,
|
||||
row.mention,
|
||||
row.num_inscription.toString(),
|
||||
row.sexe,
|
||||
convertToISODate(row.date_de_delivrance),
|
||||
row.nationaliter,
|
||||
parseInt(row.annee_baccalaureat, 10),
|
||||
row.serie,
|
||||
row.boursier,
|
||||
fixEncoding(row.domaine),
|
||||
row.contact,
|
||||
null,
|
||||
row.cin,
|
||||
row.nom.toLowerCase().trim(),
|
||||
row.prenom.toLowerCase().trim()
|
||||
];
|
||||
function addFieldIfValue(field, value) {
|
||||
if (value !== undefined && value !== null && value !== '') {
|
||||
fields.push(`${field} = ?`);
|
||||
params.push(value);
|
||||
}
|
||||
}
|
||||
|
||||
addFieldIfValue('nom', row.nom);
|
||||
addFieldIfValue('prenom', row.prenom);
|
||||
addFieldIfValue('date_de_naissances', convertToISODate(row.date_naissance));
|
||||
addFieldIfValue('niveau', row.niveau);
|
||||
addFieldIfValue('annee_scolaire', row.annee_scolaire);
|
||||
addFieldIfValue('status', row.code_redoublement);
|
||||
addFieldIfValue('mention_id', row.mention);
|
||||
addFieldIfValue('num_inscription', row.num_inscription?.toString());
|
||||
addFieldIfValue('sexe', row.sexe);
|
||||
addFieldIfValue('date_delivrance', convertToISODate(row.date_de_delivrance));
|
||||
addFieldIfValue('nationalite', row.nationaliter);
|
||||
addFieldIfValue('annee_bacc', parseInt(row.annee_baccalaureat, 10));
|
||||
addFieldIfValue('serie', row.serie);
|
||||
addFieldIfValue('boursier', row.boursier);
|
||||
addFieldIfValue('domaine', fixEncoding(row.domaine));
|
||||
addFieldIfValue('contact', row.contact);
|
||||
|
||||
if (fields.length === 0) return { success: false, error: 'Aucun champ valide à mettre à jour' };
|
||||
|
||||
let sql, whereParams;
|
||||
|
||||
if (row.cin && row.cin.toString().trim() !== '') {
|
||||
sql = `UPDATE etudiants SET ${fields.join(', ')} WHERE cin = ?`;
|
||||
whereParams = [row.cin];
|
||||
} else {
|
||||
sql = `UPDATE etudiants SET ${fields.join(', ')} WHERE LOWER(TRIM(nom)) = ? AND LOWER(TRIM(prenom)) = ?`;
|
||||
whereParams = [row.nom.toLowerCase().trim(), row.prenom.toLowerCase().trim()];
|
||||
}
|
||||
|
||||
try {
|
||||
const [result] = await pool.query(sql, params);
|
||||
console.log(`Update effectué pour CIN ${row.cin} ou nom ${row.nom} ${row.prenom}, affectedRows=${result.affectedRows}`);
|
||||
const [result] = await pool.query(sql, [...params, ...whereParams]);
|
||||
return { success: true, affectedRows: result.affectedRows };
|
||||
} catch (error) {
|
||||
console.error('❌ Erreur MySQL update :', error.message);
|
||||
return { success: false, error: error.message };
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ✅ Insertion réelle multiple
|
||||
// ---------- INSERT multiple étudiants ----------
|
||||
async function insertMultipleEtudiants(etudiants) {
|
||||
if (!etudiants || etudiants.length === 0) return { success: true, affectedRows: 0 };
|
||||
|
||||
const sql = `
|
||||
INSERT INTO etudiants (
|
||||
nom, prenom, photos, date_de_naissances, niveau, annee_scolaire, status,
|
||||
@ -190,7 +139,7 @@ async function insertMultipleEtudiants(etudiants) {
|
||||
row.mention,
|
||||
row.num_inscription.toString(),
|
||||
row.sexe,
|
||||
row.cin,
|
||||
row.cin || null,
|
||||
convertToISODate(row.date_de_delivrance),
|
||||
row.nationaliter,
|
||||
parseInt(row.annee_baccalaureat, 10),
|
||||
@ -205,31 +154,26 @@ async function insertMultipleEtudiants(etudiants) {
|
||||
const [result] = await pool.query(sql, [values]);
|
||||
return { success: true, affectedRows: result.affectedRows };
|
||||
} catch (error) {
|
||||
console.error('❌ Erreur MySQL :', error.message);
|
||||
return { success: false, error: error.message };
|
||||
}
|
||||
}
|
||||
|
||||
// ✅ Import fichier vers base
|
||||
// ---------- IMPORT fichier ----------
|
||||
async function importFileToDatabase(filePath) {
|
||||
const fileExtension = path.extname(filePath).toLowerCase();
|
||||
let records;
|
||||
const ext = path.extname(filePath).toLowerCase();
|
||||
|
||||
if (fileExtension === '.xlsx') {
|
||||
if (ext === '.xlsx') {
|
||||
const workbook = XLSX.readFile(filePath);
|
||||
const worksheet = workbook.Sheets[workbook.SheetNames[0]];
|
||||
// raw: true pour garder les valeurs brutes, surtout pour les dates
|
||||
records = XLSX.utils.sheet_to_json(worksheet, { defval: ''});
|
||||
} else if (fileExtension === '.csv') {
|
||||
const fileContent = fs.readFileSync(filePath, 'utf8');
|
||||
records = parse(fileContent, { columns: true, skip_empty_lines: true });
|
||||
}else {
|
||||
console.error('Unsupported file format.');
|
||||
return { error: true, message: 'Format de fichier non supporté.' };
|
||||
records = XLSX.utils.sheet_to_json(worksheet, { defval: '' });
|
||||
} else if (ext === '.csv') {
|
||||
const content = fs.readFileSync(filePath, 'utf8');
|
||||
records = parse(content, { columns: true, skip_empty_lines: true });
|
||||
} else {
|
||||
return { error: true, message: 'Format de fichier non supporté' };
|
||||
}
|
||||
|
||||
console.log(`📄 Nombre de lignes : ${records.length}`);
|
||||
|
||||
// Vérifier champs obligatoires
|
||||
const requiredFields = [
|
||||
'nom', 'date_naissance', 'niveau', 'annee_scolaire',
|
||||
@ -239,12 +183,8 @@ async function importFileToDatabase(filePath) {
|
||||
];
|
||||
|
||||
for (const [i, row] of records.entries()) {
|
||||
for (const field of requiredFields) {
|
||||
if (!row[field]) {
|
||||
const msg = `Le champ '${field}' est manquant à la ligne ${i + 2}`;
|
||||
console.error(msg);
|
||||
return { error: true, message: msg };
|
||||
}
|
||||
for (const f of requiredFields) {
|
||||
if (!row[f]) return { error: true, message: `Le champ '${f}' est manquant à la ligne ${i + 2}` };
|
||||
}
|
||||
}
|
||||
|
||||
@ -253,64 +193,48 @@ async function importFileToDatabase(filePath) {
|
||||
|
||||
const etudiantsToInsert = [];
|
||||
const doublons = [];
|
||||
console.log(records);
|
||||
|
||||
for (const row of records) {
|
||||
// Mapping mention
|
||||
console.log('Avant conversion date_naissance:', row.date_naissance);
|
||||
row.date_naissance = convertToISODate(row.date_naissance);
|
||||
console.log('Après conversion date_naissance:', row.date_naissance);
|
||||
|
||||
// Mapping mention
|
||||
const matchedMention = mentionRows.find(
|
||||
m => m.nom.toUpperCase() === row.mention.toUpperCase() ||
|
||||
m.uniter.toUpperCase() === row.mention.toUpperCase()
|
||||
m => m.nom.toUpperCase() === row.mention.toUpperCase() || m.uniter.toUpperCase() === row.mention.toUpperCase()
|
||||
);
|
||||
if (matchedMention) row.mention = matchedMention.id;
|
||||
|
||||
// Gestion code_redoublement -> status id
|
||||
if (row.code_redoublement) {
|
||||
row.code_redoublement = row.code_redoublement.trim().substring(0, 1);
|
||||
} else {
|
||||
row.code_redoublement = 'N';
|
||||
}
|
||||
const statusMatch = statusRows.find(
|
||||
s => s.nom.toLowerCase().startsWith(row.code_redoublement.toLowerCase())
|
||||
);
|
||||
// Mapping status
|
||||
row.code_redoublement = (row.code_redoublement ? row.code_redoublement.trim().substring(0, 1) : 'N');
|
||||
const statusMatch = statusRows.find(s => s.nom.toLowerCase().startsWith(row.code_redoublement.toLowerCase()));
|
||||
if (statusMatch) row.code_redoublement = statusMatch.id;
|
||||
|
||||
// Vérification doublons (extraction complet)
|
||||
const nomComplet = (row.nom + ' ' + row.prenom).toLowerCase().trim();
|
||||
|
||||
const [existing] = await pool.query(
|
||||
'SELECT * FROM etudiants WHERE LOWER(CONCAT(nom, " ", prenom)) = ? OR cin = ?',
|
||||
[nomComplet, row.cin]
|
||||
);
|
||||
// Détection doublons (ignorer CIN vide)
|
||||
let existing;
|
||||
if (row.cin && row.cin.toString().trim() !== '') {
|
||||
[existing] = await pool.query('SELECT * FROM etudiants WHERE cin = ?', [row.cin]);
|
||||
} else {
|
||||
[existing] = await pool.query(
|
||||
'SELECT * FROM etudiants WHERE LOWER(TRIM(nom)) = ? AND LOWER(TRIM(prenom)) = ?',
|
||||
[row.nom.toLowerCase().trim(), row.prenom.toLowerCase().trim()]
|
||||
);
|
||||
}
|
||||
|
||||
if (existing.length > 0) {
|
||||
doublons.push({ nom: row.nom, prenom: row.prenom, cin: row.cin });
|
||||
// Mise à jour
|
||||
const updateResult = await updateEtudiant(row);
|
||||
if (!updateResult.success) {
|
||||
return { error: true, message: `Erreur lors de la mise à jour de ${row.nom} ${row.prenom} : ${updateResult.error}` };
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
etudiantsToInsert.push(row);
|
||||
}
|
||||
|
||||
console.log(etudiantsToInsert);
|
||||
|
||||
// Insertion des nouveaux
|
||||
let insertResult = { success: true, affectedRows: 0 };
|
||||
if (etudiantsToInsert.length > 0) {
|
||||
insertResult = await insertMultipleEtudiants(etudiantsToInsert);
|
||||
if (!insertResult.success) {
|
||||
return { error: true, message: `Erreur lors de l'insertion : ${insertResult.error}` };
|
||||
if (!updateResult.success) return { error: true, message: `Erreur update ${row.nom} ${row.prenom}: ${updateResult.error}` };
|
||||
} else {
|
||||
etudiantsToInsert.push(row);
|
||||
}
|
||||
}
|
||||
|
||||
let msg = `Importation réussie. ${etudiantsToInsert.length} nouvel(le)(s) étudiant(s) inséré(s). ${doublons.length} étudiant(s) mis à jour.`;
|
||||
return { error: false, message: msg };
|
||||
console.log('✅ Nouveaux à insérer :', etudiantsToInsert.map(e => e.nom + ' ' + e.prenom));
|
||||
console.log('🔄 Étudiants mis à jour :', doublons.map(e => e.nom + ' ' + e.prenom));
|
||||
|
||||
const insertResult = await insertMultipleEtudiants(etudiantsToInsert);
|
||||
if (!insertResult.success) return { error: true, message: insertResult.error };
|
||||
|
||||
return { error: false, message: `Importation réussie. ${etudiantsToInsert.length} nouvel(le)(s) inséré(s), ${doublons.length} mis à jour.` };
|
||||
}
|
||||
|
||||
module.exports = { importFileToDatabase };
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 9.8 KiB After Width: | Height: | Size: 39 KiB |
@ -10,6 +10,7 @@ import svgSuccess from '../assets/success.svg'
|
||||
import svgError from '../assets/error.svg'
|
||||
import validateAddNote from './validation/AddNote'
|
||||
import ModalUpdateParcoursEtudiant from './ModalUpdateParcoursEtudiant'
|
||||
import { useLocation } from 'react-router-dom'
|
||||
|
||||
const AddNotes = () => {
|
||||
const { id, niveau, mention_id, parcours } = useParams()
|
||||
@ -22,6 +23,9 @@ const AddNotes = () => {
|
||||
setOpenModal1(false)
|
||||
}
|
||||
|
||||
const location = useLocation()
|
||||
const previousFilter = location.state?.selectedNiveau
|
||||
|
||||
/**
|
||||
* Fetching the matieres
|
||||
*/
|
||||
@ -83,7 +87,7 @@ const AddNotes = () => {
|
||||
let valid = validateAddNote()
|
||||
let annee_scolaire = AnneeScolaireEtudiant
|
||||
let mention_id = etudiants.mention_id
|
||||
|
||||
|
||||
if (valid) {
|
||||
let response = await window.notes.insertNote({
|
||||
etudiant_id,
|
||||
@ -92,21 +96,33 @@ const AddNotes = () => {
|
||||
formData,
|
||||
annee_scolaire
|
||||
})
|
||||
|
||||
if (response.success) {
|
||||
// ✅ Ici on sauvegarde le filtre avant d'ouvrir le modal
|
||||
if (previousFilter) {
|
||||
localStorage.setItem('selectedNiveau', previousFilter)
|
||||
}
|
||||
|
||||
setOpen(true)
|
||||
setStatut(200)
|
||||
setDisabled(true)
|
||||
|
||||
// Reset du formulaire
|
||||
const resetFormData = matieres.reduce((acc, mat) => {
|
||||
acc[mat.id] = '' // Reset each field to an empty string
|
||||
acc[mat.id] = ''
|
||||
return acc
|
||||
}, {})
|
||||
setFormData(resetFormData)
|
||||
} else {
|
||||
setOpen(true)
|
||||
setStatut(400)
|
||||
}
|
||||
} else {
|
||||
setOpen(true)
|
||||
setStatut(400)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const [statut, setStatut] = useState(200)
|
||||
|
||||
@ -123,9 +139,10 @@ const AddNotes = () => {
|
||||
}
|
||||
|
||||
const handleClose2 = () => {
|
||||
navigate('/notes')
|
||||
navigate('/student', { state: { selectedNiveau: previousFilter } })
|
||||
setOpen(false)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* function to return the view Modal
|
||||
|
||||
@ -192,10 +192,10 @@ const ReleverNotes = ({ id, anneescolaire, niveau, sessionType = 'ensemble', ref
|
||||
{Object.entries(groupedDataBySemestre).map(([semestre, matieres]) => {
|
||||
// Group by unite_enseignement inside each semestre
|
||||
const groupedByUnite = matieres.reduce((acc, matiere) => {
|
||||
if (!acc[matiere.unite_enseignement]) {
|
||||
acc[matiere.unite_enseignement] = []
|
||||
if (!acc[matiere.ue]) {
|
||||
acc[matiere.ue] = []
|
||||
}
|
||||
acc[matiere.unite_enseignement].push(matiere)
|
||||
acc[matiere.ue].push(matiere)
|
||||
return acc
|
||||
}, {})
|
||||
|
||||
@ -218,7 +218,6 @@ const ReleverNotes = ({ id, anneescolaire, niveau, sessionType = 'ensemble', ref
|
||||
paddingTop: '8px',
|
||||
borderRight: 'solid 1px black',
|
||||
borderBottom: 'solid 1px black',
|
||||
background: '#bdbcbc',
|
||||
borderLeft: 'solid 1px black'
|
||||
}}
|
||||
>
|
||||
@ -255,6 +254,24 @@ const ReleverNotes = ({ id, anneescolaire, niveau, sessionType = 'ensemble', ref
|
||||
<td style={{ borderRight: 'solid 1px black', borderTop: 'solid 1px black' }}>
|
||||
{matiere.note}
|
||||
</td>
|
||||
{/* Moyenne UE pour session normale */}
|
||||
{matiereIndex === 0 && (
|
||||
<td
|
||||
rowSpan={matieres.length}
|
||||
style={{
|
||||
fontWeight: 'bold',
|
||||
textAlign: 'center',
|
||||
borderRight: 'solid 1px black',
|
||||
borderTop: 'solid 1px black'
|
||||
}}
|
||||
className="moyenneUENormale"
|
||||
>
|
||||
{(
|
||||
matieres.reduce((total, matiere) => total + matiere.note, 0) /
|
||||
matieres.length
|
||||
).toFixed(2)}
|
||||
</td>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
|
||||
@ -266,6 +283,24 @@ const ReleverNotes = ({ id, anneescolaire, niveau, sessionType = 'ensemble', ref
|
||||
<td style={{ borderRight: 'solid 1px black', borderTop: 'solid 1px black' }}>
|
||||
{matiere.noterepech}
|
||||
</td>
|
||||
{/* Moyenne UE pour session rattrapage */}
|
||||
{matiereIndex === 0 && (
|
||||
<td
|
||||
rowSpan={matieres.length}
|
||||
style={{
|
||||
fontWeight: 'bold',
|
||||
textAlign: 'center',
|
||||
borderRight: 'solid 1px black',
|
||||
borderTop: 'solid 1px black'
|
||||
}}
|
||||
className="moyenneUERattrapage"
|
||||
>
|
||||
{(
|
||||
matieres.reduce((total, matiere) => total + matiere.noterepech, 0) /
|
||||
matieres.length
|
||||
).toFixed(2)}
|
||||
</td>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
|
||||
@ -289,7 +324,7 @@ const ReleverNotes = ({ id, anneescolaire, niveau, sessionType = 'ensemble', ref
|
||||
matieres.reduce((total, matiere) => total + matiere.noterepech, 0) /
|
||||
matieres.length
|
||||
).toFixed(2),
|
||||
sessionType // MODIFICATION: Passer le sessionType
|
||||
sessionType
|
||||
)}
|
||||
</td>
|
||||
)}
|
||||
@ -298,7 +333,7 @@ const ReleverNotes = ({ id, anneescolaire, niveau, sessionType = 'ensemble', ref
|
||||
|
||||
{/* Add Total Row for 'unite_enseignement' */}
|
||||
<tr
|
||||
style={{ background: '#bdbcbc', border: 'none', borderLeft: 'solid 1px black' }}
|
||||
style={{ border: 'none', borderLeft: 'solid 1px black' }}
|
||||
>
|
||||
<td
|
||||
colSpan={2}
|
||||
@ -311,7 +346,7 @@ const ReleverNotes = ({ id, anneescolaire, niveau, sessionType = 'ensemble', ref
|
||||
borderTop: 'solid 1px black'
|
||||
}}
|
||||
>
|
||||
Total de Credit et Moyenne des Notes
|
||||
Total de Credit
|
||||
</td>
|
||||
|
||||
{sessionType !== 'rattrapage' && (
|
||||
@ -327,19 +362,14 @@ const ReleverNotes = ({ id, anneescolaire, niveau, sessionType = 'ensemble', ref
|
||||
{matieres.reduce((total, matiere) => total + matiere.credit, 0)}
|
||||
</td>
|
||||
<td
|
||||
colSpan={2}
|
||||
style={{
|
||||
textAlign: 'center',
|
||||
fontWeight: 'bold',
|
||||
borderRight: 'solid 1px black',
|
||||
borderTop: 'solid 1px black'
|
||||
}}
|
||||
className="moyenneNotes"
|
||||
>
|
||||
{(
|
||||
matieres.reduce((total, matiere) => total + matiere.note, 0) /
|
||||
matieres.length
|
||||
).toFixed(2)}
|
||||
</td>
|
||||
></td>
|
||||
</>
|
||||
)}
|
||||
|
||||
@ -356,19 +386,14 @@ const ReleverNotes = ({ id, anneescolaire, niveau, sessionType = 'ensemble', ref
|
||||
{matieres.reduce((total, matiere) => total + matiere.credit, 0)}
|
||||
</td>
|
||||
<td
|
||||
colSpan={2}
|
||||
style={{
|
||||
textAlign: 'center',
|
||||
fontWeight: 'bold',
|
||||
borderRight: 'solid 1px black',
|
||||
borderTop: 'solid 1px black'
|
||||
}}
|
||||
className="moyenneNotesRattrapage"
|
||||
>
|
||||
{(
|
||||
matieres.reduce((total, matiere) => total + matiere.noterepech, 0) /
|
||||
matieres.length
|
||||
).toFixed(2)}
|
||||
</td>
|
||||
></td>
|
||||
</>
|
||||
)}
|
||||
|
||||
@ -390,20 +415,20 @@ const ReleverNotes = ({ id, anneescolaire, niveau, sessionType = 'ensemble', ref
|
||||
)
|
||||
}
|
||||
|
||||
// MODIFICATION: Fonction totalNotes mise à jour pour tenir compte du sessionType
|
||||
// MODIFICATION: Fonction totalNotes mise à jour pour utiliser les nouvelles classes
|
||||
const totalNotes = () => {
|
||||
let totalNotes = document.querySelectorAll('.moyenneNotes')
|
||||
let totalNotesRepech = document.querySelectorAll('.moyenneNotesRattrapage')
|
||||
let totalNotesNormale = document.querySelectorAll('.moyenneUENormale')
|
||||
let totalNotesRattrapage = document.querySelectorAll('.moyenneUERattrapage')
|
||||
|
||||
let TotalNoteNumber = 0
|
||||
let TotalNoteNumberRepech = 0
|
||||
|
||||
totalNotes.forEach((notes) => {
|
||||
TotalNoteNumber += Number(notes.textContent) / totalNotes.length
|
||||
totalNotesNormale.forEach((notes) => {
|
||||
TotalNoteNumber += Number(notes.textContent) / totalNotesNormale.length
|
||||
})
|
||||
|
||||
totalNotesRepech.forEach((notes) => {
|
||||
TotalNoteNumberRepech += Number(notes.textContent) / totalNotesRepech.length
|
||||
totalNotesRattrapage.forEach((notes) => {
|
||||
TotalNoteNumberRepech += Number(notes.textContent) / totalNotesRattrapage.length
|
||||
})
|
||||
|
||||
// Retourner la note selon le type de session
|
||||
@ -440,12 +465,32 @@ const ReleverNotes = ({ id, anneescolaire, niveau, sessionType = 'ensemble', ref
|
||||
ref={Telever}
|
||||
>
|
||||
<div style={{ width: '80%' }}>
|
||||
<div
|
||||
style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}
|
||||
>
|
||||
<img src={logoRelerev1} alt="image en tete" width={70} />
|
||||
<img src={logoRelerev2} alt="image en tete" width={70} />
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
textAlign: 'center'
|
||||
}}
|
||||
>
|
||||
<img src={logoRelerev2} alt="logo gauche" width={90} />
|
||||
|
||||
<div style={{ flex: 1, margin: '0 20px' }}>
|
||||
<h5 style={{ margin: 0, fontWeight: 'bold', textTransform: 'uppercase',fontSize: '16px' }}>
|
||||
REPOBLIKAN'I MADAGASIKARA
|
||||
</h5>
|
||||
<p style={{ margin: 0, fontStyle: 'italic',fontSize: '11px' }}>Fitiavana – Tanindrazana – Fandrosoana</p>
|
||||
<p style={{ margin: 0, fontWeight: 'bold',fontSize: '11px' }}>
|
||||
MINISTÈRE DE L'ENSEIGNEMENT SUPÉRIEUR <br />
|
||||
ET DE LA RECHERCHE SCIENTIFIQUE
|
||||
</p>
|
||||
<p style={{ margin: 0, fontWeight: 'bold',fontSize: '16px' }}>UNIVERSITÉ DE TOAMASINA</p>
|
||||
<p style={{ margin: 0, fontWeight: 'bold',fontSize: '16px' }}>ÉCOLE SUPÉRIEURE POLYTECHNIQUE</p>
|
||||
</div>
|
||||
|
||||
<img src={logoRelerev1} alt="logo droite" width={90} />
|
||||
</div>
|
||||
|
||||
<hr style={{ margin: 0, border: 'solid 1px black' }} />
|
||||
<h4 style={{ textTransform: 'uppercase', textAlign: 'center', marginBottom: 0 }}>
|
||||
Releve de notes
|
||||
@ -489,7 +534,7 @@ const ReleverNotes = ({ id, anneescolaire, niveau, sessionType = 'ensemble', ref
|
||||
{/* droite gauche */}
|
||||
<div style={{ width: '30%' }}>
|
||||
<span>
|
||||
<b>Annee U</b>
|
||||
<b>Annee Sco</b>
|
||||
</span>
|
||||
<br />
|
||||
<span>
|
||||
@ -517,9 +562,8 @@ const ReleverNotes = ({ id, anneescolaire, niveau, sessionType = 'ensemble', ref
|
||||
<tr style={{ borderTop: 'solid 1px black', textAlign: 'center' }}>
|
||||
<th colSpan={3}></th>
|
||||
<th
|
||||
colSpan={sessionType === 'ensemble' ? 4 : 2}
|
||||
colSpan={sessionType === 'ensemble' ? 6 : 3}
|
||||
style={{
|
||||
background: '#bdbcbc',
|
||||
borderLeft: 'solid 1px black',
|
||||
borderRight: 'solid 1px black'
|
||||
}}
|
||||
@ -535,8 +579,8 @@ const ReleverNotes = ({ id, anneescolaire, niveau, sessionType = 'ensemble', ref
|
||||
|
||||
{sessionType !== 'rattrapage' && (
|
||||
<th
|
||||
colSpan={shouldShowCredits() ? 2 : 1}
|
||||
style={{ background: '#bdbcbc', borderLeft: 'solid 1px black' }}
|
||||
colSpan={3}
|
||||
style={{ borderLeft: 'solid 1px black' }}
|
||||
>
|
||||
Normale
|
||||
</th>
|
||||
@ -544,8 +588,8 @@ const ReleverNotes = ({ id, anneescolaire, niveau, sessionType = 'ensemble', ref
|
||||
|
||||
{sessionType !== 'normale' && (
|
||||
<th
|
||||
colSpan={shouldShowCredits() ? 2 : 1}
|
||||
style={{ background: '#bdbcbc', borderLeft: 'solid 1px black' }}
|
||||
colSpan={3}
|
||||
style={{ borderLeft: 'solid 1px black' }}
|
||||
>
|
||||
Rattrapage
|
||||
</th>
|
||||
@ -558,20 +602,21 @@ const ReleverNotes = ({ id, anneescolaire, niveau, sessionType = 'ensemble', ref
|
||||
<tr
|
||||
style={{
|
||||
borderTop: 'solid 1px black',
|
||||
background: '#bdbcbc',
|
||||
textAlign: 'center'
|
||||
textAlign: 'center',
|
||||
padding:'20px',
|
||||
}}
|
||||
>
|
||||
<th style={{ borderLeft: 'solid 1px black', borderBottom: 'solid 1px black' }}>
|
||||
<th style={{padding: '1%', borderLeft: 'solid 1px black', borderBottom: 'solid 1px black' }}>
|
||||
semestre
|
||||
</th>
|
||||
<th style={{ borderLeft: 'solid 1px black' }}>UE</th>
|
||||
<th style={{ borderLeft: 'solid 1px black' }}>EC</th>
|
||||
<th style={{ borderLeft: 'solid 1px black' }}>Unités <br /> d'Enseignement <br />(UE) </th>
|
||||
<th style={{ borderLeft: 'solid 1px black' }}>Éléments <br /> constitutifs <br />(EC)</th>
|
||||
|
||||
{sessionType !== 'rattrapage' && (
|
||||
<>
|
||||
<th style={{ borderLeft: 'solid 1px black', padding: '0 5px' }}>crédit</th>
|
||||
<th style={{ borderLeft: 'solid 1px black', padding: '0 5px' }}>Notes</th>
|
||||
<th style={{ borderLeft: 'solid 1px black', padding: '0 5px' }}>Moyenne</th>
|
||||
</>
|
||||
)}
|
||||
|
||||
@ -579,6 +624,7 @@ const ReleverNotes = ({ id, anneescolaire, niveau, sessionType = 'ensemble', ref
|
||||
<>
|
||||
<th style={{ borderLeft: 'solid 1px black', padding: '0 5px' }}>crédit</th>
|
||||
<th style={{ borderLeft: 'solid 1px black', padding: '0 5px' }}>Notes</th>
|
||||
<th style={{ borderLeft: 'solid 1px black', padding: '0 5px' }}>Moyenne</th>
|
||||
</>
|
||||
)}
|
||||
|
||||
@ -602,7 +648,7 @@ const ReleverNotes = ({ id, anneescolaire, niveau, sessionType = 'ensemble', ref
|
||||
</td>
|
||||
<td style={{ borderRight: 'solid 1px black' }}>{note.toFixed(2)}</td>
|
||||
<td style={{ borderRight: 'solid 1px black' }}>/20</td>
|
||||
<td colSpan={sessionType === 'ensemble' ? 3 : 2}></td>
|
||||
<td colSpan={sessionType === 'ensemble' ? 5 : 3}></td>
|
||||
</tr>
|
||||
<tr style={{ border: 'solid 1px black' }}>
|
||||
<td
|
||||
@ -616,7 +662,7 @@ const ReleverNotes = ({ id, anneescolaire, niveau, sessionType = 'ensemble', ref
|
||||
Mention:{' '}
|
||||
<span style={{ marginLeft: '3%' }}>{getmentionAfterNotes(note)}</span>
|
||||
</td>
|
||||
<td colSpan={sessionType === 'ensemble' ? 4 : 3} style={{ textAlign: 'left', paddingLeft: '1%' }}>
|
||||
<td colSpan={sessionType === 'ensemble' ? 6 : 4} style={{ textAlign: 'left', paddingLeft: '1%' }}>
|
||||
Décision du Jury:{' '}
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@ -27,6 +27,7 @@ import { MdVerified } from 'react-icons/md'
|
||||
import warning from '../assets/warning.svg'
|
||||
import success from '../assets/success.svg'
|
||||
import { useAuthContext } from '../contexts/AuthContext' // Import du contexte d'authentification
|
||||
import { useLocation } from 'react-router-dom'
|
||||
|
||||
const Student = () => {
|
||||
// Récupération de l'utilisateur connecté
|
||||
@ -59,22 +60,56 @@ const Student = () => {
|
||||
|
||||
const [status, setStatus] = useState([])
|
||||
const [mention, setMention] = useState([])
|
||||
const [filterModel, setFilterModel] = useState({ items: [] })
|
||||
const [sortModel, setSortModel] = useState([])
|
||||
const location = useLocation()
|
||||
const savedFilter = localStorage.getItem('selectedNiveau') || ''
|
||||
const initialFilter = location.state?.selectedNiveau || savedFilter
|
||||
|
||||
const [selectedNiveau, setSelectedNiveau] = useState(initialFilter)
|
||||
|
||||
useEffect(() => {
|
||||
if (initialFilter) {
|
||||
setSelectedNiveau(initialFilter)
|
||||
FilterData({ target: { value: initialFilter } }) // applique le filtre initial
|
||||
}
|
||||
}, [initialFilter])
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* hook for displaying the students
|
||||
*/
|
||||
const [allEtudiants, setAllEtudiants] = useState([])
|
||||
const [etudiants, setEtudiants] = useState([])
|
||||
const [notes, setNotes] = useState([])
|
||||
|
||||
useEffect(() => {
|
||||
window.etudiants.getEtudiants().then((response) => {
|
||||
setEtudiants(response)
|
||||
setAllEtudiants(response)
|
||||
|
||||
if (selectedNiveau && selectedNiveau !== '') {
|
||||
setEtudiants(response.filter(e => e.niveau === selectedNiveau))
|
||||
} else {
|
||||
setEtudiants(response)
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
window.notes.getMoyenneVerify().then((response) => {
|
||||
setNotes(response)
|
||||
})
|
||||
}, [selectedNiveau])
|
||||
|
||||
useEffect(() => {
|
||||
const savedFilters = localStorage.getItem('datagridFilters')
|
||||
const savedSort = localStorage.getItem('datagridSort')
|
||||
const savedPagination = localStorage.getItem('datagridPagination')
|
||||
|
||||
if (savedFilters) setFilterModel(JSON.parse(savedFilters))
|
||||
if (savedSort) setSortModel(JSON.parse(savedSort))
|
||||
if (savedPagination) setPaginationModel(JSON.parse(savedPagination))
|
||||
}, [])
|
||||
|
||||
|
||||
const [niveaus, setNiveau] = useState([])
|
||||
|
||||
@ -222,15 +257,21 @@ const Student = () => {
|
||||
</Link>
|
||||
) : (
|
||||
<Link
|
||||
to={`/addnotes/${params.value}/${params.row.niveau}/${params.row.mention_id}/${params.row.parcour}`}
|
||||
>
|
||||
<Button color="warning" variant="contained" className={`note${params.value}`}>
|
||||
<CgNotes style={{ fontSize: '20px', color: 'white' }} />
|
||||
</Button>
|
||||
<Tooltip anchorSelect={`.note${params.value}`} className="custom-tooltip" place="top">
|
||||
Ajouter un notes à cet étudiant
|
||||
</Tooltip>
|
||||
</Link>
|
||||
to={`/addnotes/${params.value}/${params.row.niveau}/${params.row.mention_id}/${params.row.parcour}`}
|
||||
state={{ selectedNiveau: selectedNiveau }} // <-- on envoie le filtre
|
||||
>
|
||||
<Button color="warning" variant="contained" className={`note${params.value}`}>
|
||||
<CgNotes style={{ fontSize: '20px', color: 'white' }} />
|
||||
</Button>
|
||||
<Tooltip
|
||||
anchorSelect={`.note${params.value}`}
|
||||
className="custom-tooltip"
|
||||
place="top"
|
||||
>
|
||||
Ajouter un notes à cet étudiant
|
||||
</Tooltip>
|
||||
</Link>
|
||||
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
@ -277,6 +318,7 @@ const Student = () => {
|
||||
{ field: 'status', headerName: 'Status', width: 140 },
|
||||
{ field: 'num_inscription', headerName: "Numéro d'inscription", width: 160 },
|
||||
{ field: 'parcour', headerName: 'Parcours', width: 150 },
|
||||
{ field: 'nomMention', headerName: 'Mention', width: 150 },
|
||||
{
|
||||
field: 'photos',
|
||||
headerName: 'Image',
|
||||
@ -501,6 +543,7 @@ const Student = () => {
|
||||
contact: etudiant.contact,
|
||||
mention_id: etudiant.mention_id,
|
||||
mentionUnite: etudiant.mentionUnite,
|
||||
nomMention: etudiant.nomMention,
|
||||
action: etudiant.id // Ensure this is a valid URL for the image
|
||||
}))
|
||||
|
||||
@ -518,21 +561,20 @@ const Student = () => {
|
||||
/**
|
||||
* ✅ Fonction de filtrage avec reset de pagination
|
||||
*/
|
||||
const FilterData = async (e) => {
|
||||
let niveau = e.target.value
|
||||
if (niveau !== '') {
|
||||
let data = await window.etudiants.FilterDataByNiveau({ niveau })
|
||||
setEtudiants(data)
|
||||
// Reset vers la première page après filtrage
|
||||
setPaginationModel(prev => ({ ...prev, page: 0 }))
|
||||
const FilterData = (e) => {
|
||||
const niveau = e.target.value
|
||||
setSelectedNiveau(niveau)
|
||||
|
||||
if (niveau === '') {
|
||||
setEtudiants(allEtudiants)
|
||||
} else {
|
||||
window.etudiants.getEtudiants().then((response) => {
|
||||
setEtudiants(response)
|
||||
// Reset vers la première page
|
||||
setPaginationModel(prev => ({ ...prev, page: 0 }))
|
||||
})
|
||||
const filtered = allEtudiants.filter(student => student.niveau === niveau)
|
||||
setEtudiants(filtered)
|
||||
}
|
||||
|
||||
setPaginationModel(prev => ({ ...prev, page: 0 }))
|
||||
}
|
||||
|
||||
|
||||
const [openModal, setOpenModal] = useState(false)
|
||||
const [openModal2, setOpenModal2] = useState(false)
|
||||
@ -645,36 +687,31 @@ const Student = () => {
|
||||
Niveau
|
||||
</InputLabel>
|
||||
<Select
|
||||
labelId="demo-select-small-label"
|
||||
id="demo-select-small"
|
||||
label="Niveau"
|
||||
color="warning"
|
||||
name="niveau"
|
||||
defaultValue={''}
|
||||
onChange={FilterData}
|
||||
startAdornment={
|
||||
<InputAdornment position="start">
|
||||
<FaGraduationCap />
|
||||
</InputAdornment>
|
||||
}
|
||||
sx={{
|
||||
background: 'white',
|
||||
display: 'flex',
|
||||
alignItems: 'center', // Align icon and text vertically
|
||||
'& .MuiSelect-icon': {
|
||||
marginLeft: 'auto' // Keep the dropdown arrow to the right
|
||||
}
|
||||
}}
|
||||
>
|
||||
<MenuItem value="">
|
||||
<em>Tous les étudiants</em>
|
||||
</MenuItem>
|
||||
{niveaus.map((niveau) => (
|
||||
<MenuItem value={niveau.nom} key={niveau.id}>
|
||||
{niveau.nom}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
labelId="demo-select-small-label"
|
||||
id="demo-select-small"
|
||||
label="Niveau"
|
||||
color="warning"
|
||||
name="niveau"
|
||||
value={selectedNiveau} // ✅ ici
|
||||
onChange={FilterData} // ✅ ici
|
||||
startAdornment={
|
||||
<InputAdornment position="start">
|
||||
<FaGraduationCap />
|
||||
</InputAdornment>
|
||||
}
|
||||
sx={{ background: 'white', display: 'flex', alignItems: 'center', '& .MuiSelect-icon': { marginLeft: 'auto' } }}
|
||||
>
|
||||
<MenuItem value="">
|
||||
<em>Tous les étudiants</em>
|
||||
</MenuItem>
|
||||
{niveaus.map((niveau) => (
|
||||
<MenuItem value={niveau.nom} key={niveau.id}>
|
||||
{niveau.nom}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
|
||||
|
||||
</FormControl>
|
||||
</div>
|
||||
</div>
|
||||
@ -700,11 +737,23 @@ const Student = () => {
|
||||
}}
|
||||
>
|
||||
<DataGrid
|
||||
rows={dataRow}
|
||||
columns={columns}
|
||||
pageSizeOptions={pageSizeOptions}
|
||||
paginationModel={paginationModel} // ✅ Utilise l'état complet
|
||||
onPaginationModelChange={handlePaginationModelChange} // ✅ Gère page ET pageSize
|
||||
rows={dataRow}
|
||||
columns={columns}
|
||||
filterModel={filterModel} // ✅ Restaure les filtres
|
||||
onFilterModelChange={(newModel) => {
|
||||
setFilterModel(newModel)
|
||||
localStorage.setItem('datagridFilters', JSON.stringify(newModel))
|
||||
}}
|
||||
sortModel={sortModel} // ✅ Restaure le tri
|
||||
onSortModelChange={(newModel) => {
|
||||
setSortModel(newModel)
|
||||
localStorage.setItem('datagridSort', JSON.stringify(newModel))
|
||||
}}
|
||||
paginationModel={paginationModel}
|
||||
onPaginationModelChange={(newModel) => {
|
||||
handlePaginationModelChange(newModel)
|
||||
localStorage.setItem('datagridPagination', JSON.stringify(newModel))
|
||||
}}
|
||||
sx={{
|
||||
border: 0,
|
||||
width: 'auto', // Ensures the DataGrid takes full width
|
||||
|
||||
Loading…
Reference in New Issue
Block a user