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