@ -1,444 +1,307 @@ |
|||||
const sqlite = require('better-sqlite3') |
const mysql = require('mysql2/promise') |
||||
const bcrypt = require('bcryptjs') |
const bcrypt = require('bcryptjs') |
||||
|
|
||||
|
const pool = mysql.createPool({ |
||||
|
host: '127.0.0.1', |
||||
|
user: 'root', |
||||
|
password: '', |
||||
|
database: 'university', |
||||
|
waitForConnections: true, |
||||
|
connectionLimit: 10, |
||||
|
queueLimit: 0 |
||||
|
}) |
||||
|
|
||||
// Construct the database path using the detected IP
|
async function createTables() { |
||||
let dbPath = `./base/data.db`; |
const connection = await pool.getConnection() |
||||
|
|
||||
// Connect to SQLite database with the initial path
|
try { |
||||
let database = new sqlite(dbPath); |
// Users table
|
||||
|
await connection.query(` |
||||
|
CREATE TABLE IF NOT EXISTS users ( |
||||
|
id INT AUTO_INCREMENT PRIMARY KEY, |
||||
|
username VARCHAR(200) NOT NULL, |
||||
|
email VARCHAR(250) NOT NULL UNIQUE, |
||||
// Create the users table if it doesn't exist
|
password TEXT NOT NULL, |
||||
const createUserTableQuery = ` |
roles VARCHAR(250) NOT NULL, |
||||
CREATE TABLE IF NOT EXISTS users ( |
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, |
||||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, |
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP |
||||
username VARCHAR(200) NOT NULL, |
) ENGINE=InnoDB; |
||||
email VARCHAR(250) NOT NULL UNIQUE, |
`)
|
||||
password TEXT NOT NULL, |
|
||||
roles VARCHAR(250) NOT NULL, |
// Status table
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, |
await connection.query(` |
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP |
CREATE TABLE IF NOT EXISTS status ( |
||||
); |
id INT AUTO_INCREMENT PRIMARY KEY, |
||||
` |
nom VARCHAR(200) NOT NULL, |
||||
database.prepare(createUserTableQuery).run() |
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, |
||||
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP |
||||
// Insert a default admin user if not exists
|
) ENGINE=InnoDB; |
||||
const insertDefaultUserQuery = ` |
`)
|
||||
INSERT INTO users (username, email, password, roles) |
|
||||
SELECT 'admin', 'admin@example.com', ?, 'admin' |
// Mentions table
|
||||
WHERE NOT EXISTS (SELECT 1 FROM users WHERE username = 'admin'); |
await connection.query(` |
||||
` |
CREATE TABLE IF NOT EXISTS mentions ( |
||||
|
id INT AUTO_INCREMENT PRIMARY KEY, |
||||
// Hash the password '1234' before storing
|
nom VARCHAR(250) NOT NULL, |
||||
const hashedPassword = bcrypt.hashSync('123456789', 10) |
uniter VARCHAR(50) NOT NULL, |
||||
database.prepare(insertDefaultUserQuery).run(hashedPassword) |
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, |
||||
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP |
||||
// create table for note status
|
) ENGINE=InnoDB; |
||||
const createStatusTableQuery = ` |
`)
|
||||
CREATE TABLE IF NOT EXISTS status ( |
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, |
await connection.query(` |
||||
nom VARCHAR(200) NOT NULL, |
CREATE TABLE IF NOT EXISTS niveaus ( |
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, |
id INT AUTO_INCREMENT PRIMARY KEY, |
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP |
nom VARCHAR(50) UNIQUE NOT NULL, |
||||
); |
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, |
||||
` |
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP |
||||
database.prepare(createStatusTableQuery).run() |
) ENGINE=InnoDB; |
||||
|
`)
|
||||
// create table for mention
|
|
||||
const createMentionTableQuery = ` |
await connection.query(` |
||||
CREATE TABLE IF NOT EXISTS mentions ( |
CREATE TABLE IF NOT EXISTS etudiants ( |
||||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, |
id INT AUTO_INCREMENT PRIMARY KEY, |
||||
nom VARCHAR(250) NOT NULL, |
nom VARCHAR(250) DEFAULT NULL, |
||||
uniter VARCHAR(50) NOT NULL, -- Abréviation du nom |
prenom VARCHAR(250) DEFAULT NULL, |
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, |
photos TEXT DEFAULT NULL, |
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP |
date_de_naissances DATE DEFAULT NULL, |
||||
); |
niveau VARCHAR(250) NOT NULL, |
||||
` |
annee_scolaire VARCHAR(20) NOT NULL, |
||||
database.prepare(createMentionTableQuery).run() |
status INT DEFAULT NULL, |
||||
|
mention_id INT NOT NULL, |
||||
// Create the niveau table if it doesn't exist
|
num_inscription TEXT UNIQUE NOT NULL, |
||||
const createNiveauTableQuery = ` |
sexe VARCHAR(20) DEFAULT NULL, |
||||
CREATE TABLE IF NOT EXISTS niveaus ( |
cin VARCHAR(250) DEFAULT NULL, |
||||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, |
date_delivrance TEXT DEFAULT NULL, |
||||
nom VARCHAR(50) NOT NULL, -- Exemple: L1, L2, L3, etc. |
nationalite VARCHAR(250) DEFAULT NULL, |
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, |
annee_bacc TEXT DEFAULT NULL, |
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP |
serie VARCHAR(20) DEFAULT NULL, |
||||
); |
boursier VARCHAR(20) DEFAULT NULL, |
||||
` |
domaine VARCHAR(250) DEFAULT NULL, |
||||
database.prepare(createNiveauTableQuery).run() |
contact VARCHAR(20) DEFAULT NULL, |
||||
|
parcours VARCHAR(250) DEFAULT NULL, |
||||
// Create the etudiants table if it doesn't exist
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, |
||||
const createEtudiantsTableQuery = ` |
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, |
||||
CREATE TABLE IF NOT EXISTS etudiants ( |
FOREIGN KEY (status) REFERENCES status(id), |
||||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, |
FOREIGN KEY (mention_id) REFERENCES mentions(id) |
||||
nom VARCHAR(250) DEFAULT NULL, |
) ENGINE=InnoDB; |
||||
prenom VARCHAR(250) DEFAULT NULL, |
`)
|
||||
photos TEXT DEFAULT NULL, |
|
||||
date_de_naissances DATE DEFAULT NULL, |
await connection.query(` |
||||
niveau VARCHAR(250) NOT NULL, -- Clé étrangère vers niveaus |
CREATE TABLE IF NOT EXISTS matieres ( |
||||
annee_scolaire VARCHAR(20) NOT NULL, |
id INT AUTO_INCREMENT PRIMARY KEY, |
||||
status INTEGER DEFAULT NULL, |
nom VARCHAR(250) UNIQUE NOT NULL, |
||||
mention_id INTEGER NOT NULL, -- Clé étrangère vers mentions |
unite_enseignement VARCHAR(250) NOT NULL, |
||||
num_inscription TEXT NOT NULL, |
credit INT NOT NULL, |
||||
sexe VARCHAR(20) DEFAULT NULL, |
heure INT NOT NULL, |
||||
cin VARCHAR(250) DEFAULT NULL, |
ue VARCHAR(10) NOT NULL, |
||||
date_delivrance DEFAULT NULL, |
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, |
||||
nationalite DATE DEFAULT NULL, |
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP |
||||
annee_bacc DATE DEFAULT NULL, |
) ENGINE=InnoDB; |
||||
serie VARCHAR(20) DEFAULT NULL, |
`)
|
||||
boursier BOOLEAN DEFAULT FALSE, |
|
||||
domaine VARCHAR(250) DEFAULT NULL, |
await connection.query(` |
||||
contact VARCHAR(20) DEFAULT NULL, |
CREATE TABLE IF NOT EXISTS semestres ( |
||||
parcours VARCHAR(250) DEFAULT NULL, |
id INT AUTO_INCREMENT PRIMARY KEY, |
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, |
nom VARCHAR(30) NOT NULL, |
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, |
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, |
||||
FOREIGN KEY (status) REFERENCES status(id), |
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP |
||||
FOREIGN KEY (mention_id) REFERENCES mentions(id) |
) ENGINE=InnoDB; |
||||
); |
`)
|
||||
` |
|
||||
database.prepare(createEtudiantsTableQuery).run() |
await connection.query(` |
||||
|
CREATE TABLE IF NOT EXISTS matiere_mention ( |
||||
// Create the notes table if it doesn't exist
|
id INT AUTO_INCREMENT PRIMARY KEY, |
||||
const createMatiereTableQuery = ` |
matiere_id INT NOT NULL, |
||||
CREATE TABLE IF NOT EXISTS matieres ( |
mention_id INT NOT NULL, |
||||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, |
FOREIGN KEY (matiere_id) REFERENCES matieres(id), |
||||
nom VARCHAR(250) UNIQUE NOT NULL, |
FOREIGN KEY (mention_id) REFERENCES mentions(id) |
||||
unite_enseignement VARCHAR(250) NOT NULL, |
) ENGINE=InnoDB; |
||||
credit INTEGER NOT NULL, |
`)
|
||||
heure INTEGER NOT NULL, |
|
||||
ue VARCHAR(10) NOT NULL, |
await connection.query(` |
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, |
CREATE TABLE IF NOT EXISTS matiere_semestre ( |
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP |
id INT AUTO_INCREMENT PRIMARY KEY, |
||||
); |
matiere_id INT NOT NULL, |
||||
` |
semestre_id INT NOT NULL, |
||||
database.prepare(createMatiereTableQuery).run() |
mention_id INT NOT NULL, |
||||
|
FOREIGN KEY (matiere_id) REFERENCES matieres(id), |
||||
// Create the semestre table if it doesn't exist
|
FOREIGN KEY (semestre_id) REFERENCES semestres(id), |
||||
const createSemestreTableQuery = ` |
FOREIGN KEY (mention_id) REFERENCES mentions(id) |
||||
CREATE TABLE IF NOT EXISTS semestres ( |
) ENGINE=InnoDB; |
||||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, |
`)
|
||||
nom VARCHAR(30) NOT NULL, -- Exemple: S1, S2, S3, etc. |
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, |
await connection.query(` |
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP |
CREATE TABLE IF NOT EXISTS notes ( |
||||
); |
id INT AUTO_INCREMENT PRIMARY KEY, |
||||
` |
etudiant_id INT NOT NULL, |
||||
database.prepare(createSemestreTableQuery).run() |
matiere_id INT NOT NULL, |
||||
|
etudiant_niveau VARCHAR(50) NOT NULL, |
||||
// Create the semestre table if it doesn't exist
|
mention_id INT NOT NULL, |
||||
const createMatiere_mentionTableQuery = ` |
note FLOAT DEFAULT NULL, |
||||
CREATE TABLE IF NOT EXISTS matiere_mention ( |
annee_scolaire VARCHAR(50) NOT NULL, |
||||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, |
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, |
||||
matiere_id INTEGER NOT NULL, -- Clé étrangère vers matieres |
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, |
||||
mention_id INTEGER NOT NULL, -- Clé étrangère vers mentions |
FOREIGN KEY (etudiant_id) REFERENCES etudiants(id), |
||||
FOREIGN KEY (matiere_id) REFERENCES matieres(id), |
FOREIGN KEY (matiere_id) REFERENCES matieres(id), |
||||
FOREIGN KEY (mention_id) REFERENCES mentions(id) |
FOREIGN KEY (mention_id) REFERENCES mentions(id) |
||||
); |
) ENGINE=InnoDB; |
||||
` |
`)
|
||||
database.prepare(createMatiere_mentionTableQuery).run() |
|
||||
|
await connection.query(` |
||||
const createMatiere_semestreTableQuery = ` |
CREATE TABLE IF NOT EXISTS notesrepech ( |
||||
CREATE TABLE IF NOT EXISTS matiere_semestre ( |
id INT AUTO_INCREMENT PRIMARY KEY, |
||||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, |
etudiant_id INT NOT NULL, |
||||
matiere_id INTEGER NOT NULL, -- Clé étrangère vers matieres |
matiere_id INT NOT NULL, |
||||
semestre_id INTEGER NOT NULL, -- Clé étrangère vers semestres |
etudiant_niveau VARCHAR(50) NOT NULL, |
||||
mention_id INTEGER NOT NULL, -- Clé étrangère vers niveaus |
mention_id INT NOT NULL, |
||||
FOREIGN KEY (matiere_id) REFERENCES matieres(id), |
note FLOAT DEFAULT NULL, |
||||
FOREIGN KEY (semestre_id) REFERENCES semestres(id), |
annee_scolaire VARCHAR(50) NOT NULL, |
||||
FOREIGN KEY (mention_id) REFERENCES mentions(id) |
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, |
||||
); |
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, |
||||
` |
FOREIGN KEY (etudiant_id) REFERENCES etudiants(id), |
||||
database.prepare(createMatiere_semestreTableQuery).run() |
FOREIGN KEY (matiere_id) REFERENCES matieres(id), |
||||
|
FOREIGN KEY (mention_id) REFERENCES mentions(id) |
||||
// Create the notes table if it doesn't exist
|
) ENGINE=InnoDB; |
||||
const createNoteTableQuery = ` |
`)
|
||||
CREATE TABLE IF NOT EXISTS notes ( |
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, |
await connection.query(` |
||||
etudiant_id INTEGER NOT NULL, -- Clé étrangère vers etudiants |
CREATE TABLE IF NOT EXISTS notesystems ( |
||||
matiere_id INTEGER NOT NULL, -- Clé étrangère vers matieres |
id INT AUTO_INCREMENT PRIMARY KEY, |
||||
etudiant_niveau VARCHAR(50) NOT NULL, |
admis FLOAT NOT NULL DEFAULT 10, |
||||
mention_id INTEGER NOT NULL, |
redouble FLOAT NOT NULL DEFAULT 9.99, |
||||
note FLOAT DEFAULT NULL, |
renvoyer FLOAT NOT NULL DEFAULT 7.99, |
||||
annee_scolaire VARCHAR(50) NOT NULL, |
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, |
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, |
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP |
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, |
) ENGINE=InnoDB; |
||||
FOREIGN KEY (etudiant_id) REFERENCES etudiants(id), |
`)
|
||||
FOREIGN KEY (matiere_id) REFERENCES matieres(id) |
|
||||
FOREIGN KEY (mention_id) REFERENCES mentions(id) |
await connection.query(` |
||||
); |
CREATE TABLE IF NOT EXISTS anneescolaire ( |
||||
` |
id INT AUTO_INCREMENT PRIMARY KEY, |
||||
database.prepare(createNoteTableQuery).run() |
code VARCHAR(30) NOT NULL, |
||||
|
debut DATE NOT NULL, |
||||
// Create the notes second session table if it doesn't exist
|
fin DATE NOT NULL, |
||||
const createNoteRepechTableQuery = ` |
is_current TINYINT(1) DEFAULT 0, |
||||
CREATE TABLE IF NOT EXISTS notesrepech ( |
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, |
||||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, |
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP |
||||
etudiant_id INTEGER NOT NULL, -- Clé étrangère vers etudiants |
) ENGINE=InnoDB; |
||||
matiere_id INTEGER NOT NULL, -- Clé étrangère vers matieres |
`)
|
||||
etudiant_niveau VARCHAR(50) NOT NULL, |
|
||||
mention_id INTEGER NOT NULL, |
await connection.query(` |
||||
note FLOAT DEFAULT NULL, |
CREATE TABLE IF NOT EXISTS traitmentsystem ( |
||||
annee_scolaire VARCHAR(50) NOT NULL, |
id INT AUTO_INCREMENT PRIMARY KEY, |
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, |
code VARCHAR(30) NOT NULL, |
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, |
debut DATE NOT NULL, |
||||
FOREIGN KEY (etudiant_id) REFERENCES etudiants(id), |
fin DATE NOT NULL, |
||||
FOREIGN KEY (matiere_id) REFERENCES matieres(id) |
is_finished TINYINT(1) DEFAULT 0, |
||||
FOREIGN KEY (mention_id) REFERENCES mentions(id) |
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, |
||||
); |
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP |
||||
` |
) ENGINE=InnoDB; |
||||
database.prepare(createNoteRepechTableQuery).run() |
`)
|
||||
|
|
||||
// create table for note système
|
await connection.query(` |
||||
const createNoteSystemeTableQuery = ` |
CREATE TABLE IF NOT EXISTS nessesaryTable ( |
||||
CREATE TABLE IF NOT EXISTS notesystems ( |
id INT AUTO_INCREMENT PRIMARY KEY, |
||||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, |
uniter_heure INT NOT NULL, |
||||
admis FLOAT NOT NULL DEFAULT 10, |
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, |
||||
redouble FLOAT NOT NULL DEFAULT 9.99, |
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP |
||||
renvoyer FLOAT NOT NULL DEFAULT 7.99, |
) ENGINE=InnoDB; |
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, |
`)
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP |
|
||||
); |
await connection.query(` |
||||
` |
CREATE TABLE IF NOT EXISTS matiereEnseignants ( |
||||
database.prepare(createNoteSystemeTableQuery).run() |
id INT AUTO_INCREMENT PRIMARY KEY, |
||||
|
matiere_id INT NOT NULL, |
||||
// create table année scolaire
|
nom_enseignant VARCHAR(250) NOT NULL, |
||||
const createAnneeScolaireTableQuery = ` |
prenom_enseignant VARCHAR(250) NOT NULL, |
||||
CREATE TABLE IF NOT EXISTS anneescolaire ( |
contact VARCHAR(11) NOT NULL, |
||||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, |
date DATE NOT NULL, |
||||
code VARCHAR(30) NOT NULL, |
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, |
||||
debut DATE NOT NULL, |
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, |
||||
fin DATE NOT NULL, |
FOREIGN KEY (matiere_id) REFERENCES matieres(id) |
||||
is_current INTEGER DEFAULT 0, |
) ENGINE=InnoDB; |
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, |
`)
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP |
|
||||
); |
await connection.query(` |
||||
` |
CREATE TABLE IF NOT EXISTS parcours ( |
||||
database.prepare(createAnneeScolaireTableQuery).run() |
id INT AUTO_INCREMENT PRIMARY KEY, |
||||
|
nom VARCHAR(250) NOT NULL, |
||||
// create traitement systeme
|
uniter VARCHAR(250) NOT NULL, |
||||
const createTraitementSystemQuery = ` |
mention_id INT DEFAULT NULL, |
||||
CREATE TABLE IF NOT EXISTS traitmentsystem ( |
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, |
||||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, |
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP |
||||
code VARCHAR(30) NOT NULL, |
) ENGINE=InnoDB; |
||||
debut DATE NOT NULL, |
`)
|
||||
fin DATE NOT NULL, |
|
||||
is_finished INTEGER DEFAULT 0, |
await connection.query(` |
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, |
CREATE TABLE IF NOT EXISTS parcoursmatiere ( |
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP |
id INT AUTO_INCREMENT PRIMARY KEY, |
||||
); |
matiere_id INT NOT NULL, |
||||
` |
parcour_id INT NOT NULL, |
||||
database.prepare(createTraitementSystemQuery).run() |
FOREIGN KEY (matiere_id) REFERENCES matieres(id), |
||||
|
FOREIGN KEY (parcour_id) REFERENCES parcours(id) |
||||
const createNecessaryParameterTableQuery = ` |
) ENGINE=InnoDB; |
||||
CREATE TABLE IF NOT EXISTS nessesaryTable ( |
`)
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, |
|
||||
uniter_heure INTEGER NOT NULL, |
await connection.query(` |
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, |
CREATE TABLE IF NOT EXISTS trancheecolage ( |
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP |
id INT AUTO_INCREMENT PRIMARY KEY, |
||||
); |
etudiant_id INT NOT NULL, |
||||
` |
tranchename VARCHAR(255) NOT NULL, |
||||
database.prepare(createNecessaryParameterTableQuery).run() |
montant DOUBLE NOT NULL |
||||
|
) ENGINE=InnoDB; |
||||
const createMatiereEnseignantTableQuery = ` |
`)
|
||||
CREATE TABLE IF NOT EXISTS matiereEnseignants ( |
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, |
await connection.query(` |
||||
matiere_id INTEGER NOT NULL, |
CREATE TABLE IF NOT EXISTS ipconfig ( |
||||
nom_enseignant VARCHAR(250) NOT NULL, |
id INT AUTO_INCREMENT PRIMARY KEY, |
||||
prenom_enseignant VARCHAR(250) NOT NULL, |
ipname VARCHAR(255) NOT NULL |
||||
contact VARCHAR(11) NOT NULL, |
) ENGINE=InnoDB; |
||||
date DATE NOT NULL, |
`)
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, |
} finally { |
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, |
connection.release() |
||||
FOREIGN KEY (matiere_id) REFERENCES matieres(id) |
|
||||
); |
|
||||
` |
|
||||
database.prepare(createMatiereEnseignantTableQuery).run() |
|
||||
|
|
||||
const createParcourTableQuery = ` |
|
||||
CREATE TABLE IF NOT EXISTS parcours ( |
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, |
|
||||
nom VARCHAR(250) NOT NULL, |
|
||||
uniter VARCHAR(250) NOT NULL, |
|
||||
mention_id INTEGER DEFAULT NULL, -- Clé étrangère vers mentions |
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, |
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP |
|
||||
); |
|
||||
` |
|
||||
database.prepare(createParcourTableQuery).run() |
|
||||
|
|
||||
const createParcourSemestreTableQuery = ` |
|
||||
CREATE TABLE IF NOT EXISTS parcoursmatiere ( |
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, |
|
||||
matiere_id INTEGER NOT NULL, |
|
||||
parcour_id INTEGER NOT NULL, |
|
||||
FOREIGN KEY (matiere_id) REFERENCES matieres(id), |
|
||||
FOREIGN KEY (parcour_id) REFERENCES parcours(id) |
|
||||
); |
|
||||
` |
|
||||
database.prepare(createParcourSemestreTableQuery).run() |
|
||||
|
|
||||
const createTableEcolageQuery = ` |
|
||||
CREATE TABLE IF NOT EXISTS trancheecolage ( |
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, |
|
||||
etudiant_id INTEGER NOT NULL, |
|
||||
tranchename VARCHAR(255) NOT NULL, |
|
||||
montant DOUBLE NOT NULL |
|
||||
); |
|
||||
` |
|
||||
database.prepare(createTableEcolageQuery).run() |
|
||||
|
|
||||
const createTableStoreIP = ` |
|
||||
CREATE TABLE IF NOT EXISTS ipconfig ( |
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, |
|
||||
ipname VARCHAR(255) NOT NULL |
|
||||
); |
|
||||
`;
|
|
||||
database.prepare(createTableStoreIP).run() |
|
||||
|
|
||||
// -------------------------------------- function pre-excuter --------------------------------------------
|
|
||||
|
|
||||
async function insertStatusesIfNotExist() { |
|
||||
// Préparation des requêtes
|
|
||||
const checkStatusQuery = database.prepare(` |
|
||||
SELECT COUNT(*) AS count FROM status WHERE nom = ?; |
|
||||
`)
|
|
||||
const insertStatusQuery = database.prepare(` |
|
||||
INSERT INTO status (nom) VALUES (?); |
|
||||
`)
|
|
||||
|
|
||||
// Tableau des statuts à vérifier/insérer
|
|
||||
const arrayStatus = ['Nouveau', 'Passant', 'Redoublant', 'Renvoyé', 'Ancien'] |
|
||||
|
|
||||
for (let index = 0; index < arrayStatus.length; index++) { |
|
||||
const statusName = arrayStatus[index] |
|
||||
|
|
||||
// Vérification si le statut existe déjà
|
|
||||
const result = checkStatusQuery.get(statusName) |
|
||||
|
|
||||
// Si le statut n'existe pas, on l'insère
|
|
||||
if (result.count === 0) { |
|
||||
insertStatusQuery.run(statusName) |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
// execute the function
|
|
||||
insertStatusesIfNotExist() |
|
||||
|
|
||||
async function insertDefaultNoteSystemIfNotExist() { |
|
||||
// Préparation de la requête pour vérifier si une entrée existe déjà
|
|
||||
const checkNoteSystemQuery = database.prepare(` |
|
||||
SELECT COUNT(*) AS count FROM notesystems; |
|
||||
`)
|
|
||||
|
|
||||
// Préparation de la requête pour insérer une entrée par défaut
|
|
||||
const insertNoteSystemQuery = database.prepare(` |
|
||||
INSERT INTO notesystems (admis, redouble, renvoyer) |
|
||||
VALUES (?, ?, ?); |
|
||||
`)
|
|
||||
|
|
||||
// Valeurs par défaut à insérer
|
|
||||
const defaultValues = { |
|
||||
admis: 10.0, |
|
||||
redouble: 9.99, |
|
||||
renvoyer: 7.99 |
|
||||
} |
|
||||
|
|
||||
// Vérification si une entrée existe déjà
|
|
||||
const result = checkNoteSystemQuery.get() |
|
||||
|
|
||||
if (result.count === 0) { |
|
||||
// Insérer les valeurs par défaut si aucune entrée n'existe
|
|
||||
insertNoteSystemQuery.run(defaultValues.admis, defaultValues.redouble, defaultValues.renvoyer) |
|
||||
} |
} |
||||
} |
} |
||||
|
|
||||
insertDefaultNoteSystemIfNotExist() |
async function insertDefaultAdmin() { |
||||
|
const conn = await pool.getConnection() |
||||
async function semestreCreate() { |
|
||||
const query = database.prepare('INSERT INTO semestres (nom) VALUES (?)') |
|
||||
// Préparation de la requête pour vérifier si une entrée existe déjà
|
|
||||
const checkSemestreQuery = database.prepare(` |
|
||||
SELECT COUNT(*) AS count FROM semestres; |
|
||||
`)
|
|
||||
|
|
||||
try { |
try { |
||||
let arraySemestre = [ |
const [rows] = await conn.query(`SELECT COUNT(*) as count FROM users WHERE username = ?`, [ |
||||
'S1', |
'admin' |
||||
'S2', |
]) |
||||
'S3', |
if (rows[0].count === 0) { |
||||
'S4', |
const hashedPassword = bcrypt.hashSync('123456789', 10) |
||||
'S5', |
await conn.query( |
||||
'S6', |
` |
||||
'S7', |
INSERT INTO users (username, email, password, roles) |
||||
'S8', |
VALUES (?, ?, ?, ?)`,
|
||||
'S9', |
['admin', 'admin@example.com', hashedPassword, 'admin'] |
||||
'S10', |
) |
||||
'S11', |
|
||||
'S12', |
|
||||
'S13', |
|
||||
'S14', |
|
||||
'S14', |
|
||||
'S16' |
|
||||
] |
|
||||
// Vérification si une entrée existe déjà
|
|
||||
const result = checkSemestreQuery.get() |
|
||||
|
|
||||
if (result.count === 0) { |
|
||||
database.transaction(() => { |
|
||||
for (let index = 0; index < arraySemestre.length; index++) { |
|
||||
query.run(arraySemestre[index]) |
|
||||
} |
|
||||
})() |
|
||||
} |
} |
||||
} catch (error) { |
} finally { |
||||
console.log(error) |
conn.release() |
||||
} |
|
||||
} |
|
||||
|
|
||||
const createNecessaryParameterTable = () => { |
|
||||
// Check if the table is empty
|
|
||||
const rowCount = database.prepare(`SELECT COUNT(*) AS count FROM nessesaryTable`).get().count |
|
||||
|
|
||||
// If the table is empty, insert the default value
|
|
||||
if (rowCount === 0) { |
|
||||
const insertDefaultQuery = ` |
|
||||
INSERT INTO nessesaryTable (uniter_heure) VALUES (15); |
|
||||
` |
|
||||
database.prepare(insertDefaultQuery).run() |
|
||||
} |
} |
||||
} |
} |
||||
|
|
||||
// Call the function when the app runs
|
async function insertStatusesIfNotExist() { |
||||
createNecessaryParameterTable() |
const conn = await pool.getConnection() |
||||
|
try { |
||||
semestreCreate() |
const statuses = ['Nouveau', 'Passant', 'Redoublant', 'Renvoyé', 'Ancien'] |
||||
|
for (let name of statuses) { |
||||
// Function to get the IP from the database
|
const [rows] = await conn.query(`SELECT COUNT(*) as count FROM status WHERE nom = ?`, [name]) |
||||
function getIP() { |
if (rows[0].count === 0) { |
||||
const data = database.prepare("SELECT * FROM ipconfig WHERE id = 1").get(); |
await conn.query(`INSERT INTO status (nom) VALUES (?)`, [name]) |
||||
|
} |
||||
if (data) { |
} |
||||
return data.ipname; |
} finally { |
||||
} else { |
conn.release() |
||||
return null; // Explicitly return `null` if no data is found
|
|
||||
} |
} |
||||
} |
} |
||||
|
|
||||
// Get the new IP from the database
|
|
||||
let newIP = getIP(); |
|
||||
|
|
||||
if (newIP) { |
|
||||
// Construct the database path using the new IP from the database
|
|
||||
dbPath = `\\\\${newIP}\\base\\data.db`; |
|
||||
|
|
||||
// Reconnect to SQLite database with the updated path
|
|
||||
database = new sqlite(dbPath); // Re-initialize database connection with new path
|
|
||||
console.log("now COnnect to the ", dbPath); |
|
||||
} |
|
||||
|
|
||||
module.exports = { |
module.exports = { |
||||
database |
pool, |
||||
|
createTables, |
||||
|
insertDefaultAdmin, |
||||
|
insertStatusesIfNotExist |
||||
} |
} |
||||
|
|||||
@ -1,264 +1,316 @@ |
|||||
const fs = require('fs') |
const fs = require('fs'); |
||||
const path = require('path') |
const path = require('path'); |
||||
const XLSX = require('xlsx') |
const XLSX = require('xlsx'); |
||||
const { getCompressedDefaultImage } = require('../function/GetImageDefaault') |
const { getCompressedDefaultImage } = require('../function/GetImageDefaault'); |
||||
const { parse } = require('csv-parse/sync') |
const { parse } = require('csv-parse/sync'); |
||||
const { pool } = require('../database') |
const { pool } = require('../database'); |
||||
const dayjs = require('dayjs') |
const dayjs = require('dayjs'); |
||||
// const { getStatusMention } = require('../function/Helper')
|
const customParseFormat = require('dayjs/plugin/customParseFormat'); |
||||
const customParseFormat = require('dayjs/plugin/customParseFormat') |
dayjs.extend(customParseFormat); |
||||
// const { log } = require('console')
|
|
||||
// const customParseFormatt = require('dayjs/plu')
|
// ✅ Fonction de correction d'encodage
|
||||
dayjs.extend(customParseFormat) |
function fixEncoding(str) { |
||||
|
if (typeof str !== 'string') return str; |
||||
// Function to convert any date format to 'YYYY-MM-DD'
|
return str |
||||
|
.replace(/├®/g, 'é') |
||||
|
.replace(/├à/g, 'à') |
||||
|
.replace(/├©/g, 'é') |
||||
|
.replace(/├ô/g, 'ô') |
||||
|
.replace(/├ù/g, 'ù') |
||||
|
.replace(/’/g, "'") |
||||
|
.replace(/â€/g, '…') |
||||
|
.replace(/â€/g, '-'); |
||||
|
} |
||||
function convertToISODate(input) { |
function convertToISODate(input) { |
||||
// Try parsing the date with different formats
|
if (!input) return null; |
||||
const formats = [ |
|
||||
'DD/MM/YYYY', |
console.log('🔍 Input original:', input, 'Type:', typeof input); |
||||
'MM/DD/YYYY', |
|
||||
'YYYY-MM-DD', |
// Si input est un objet Date valide
|
||||
'DD-MM-YYYY', |
if (input instanceof Date && !isNaN(input)) { |
||||
'MM-DD-YYYY', |
const result = dayjs(input).format('YYYY-MM-DD'); |
||||
'DD/MM/YY', |
console.log('📅 Date object convertie:', result); |
||||
'MM/DD/YY', |
return result; |
||||
'DD-MMM-YY' |
|
||||
] |
|
||||
const parsedDate = dayjs(input, formats, true) // Strict parsing to ensure formats are matched correctly
|
|
||||
|
|
||||
// If the date is valid, return it in the YYYY-MM-DD format
|
|
||||
if (parsedDate.isValid()) { |
|
||||
return parsedDate.format('YYYY-MM-DD') |
|
||||
} |
} |
||||
|
|
||||
// Handle cases like "Vers 2000"
|
// Si input est un nombre (numéro de série Excel)
|
||||
const versMatch = typeof input === 'string' && input.match(/vers\s*(\d{4})/i); |
if (typeof input === 'number') { |
||||
if (versMatch) { |
// Formule Excel: (numéro - 25569) * 86400 * 1000
|
||||
return `${versMatch[1]}-01-01`; // Return in ISO format
|
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; |
||||
} |
} |
||||
|
|
||||
function excelDateToJSDate(serial) { |
// Si input est une chaîne
|
||||
const utc_days = Math.floor(serial - 25569); // days from Jan 1, 1970
|
if (typeof input === 'string') { |
||||
const utc_value = utc_days * 86400; // seconds in a day
|
const cleanInput = input.trim(); |
||||
return new Date(utc_value * 1000); // JS Date uses milliseconds
|
|
||||
|
// 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; |
||||
|
} |
||||
|
|
||||
|
// 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
|
||||
|
'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; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 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; |
||||
|
} |
||||
} |
} |
||||
|
|
||||
let jsDate = excelDateToJSDate(input); |
console.error('❌ Impossible de convertir:', input); |
||||
|
return null; |
||||
|
} |
||||
|
|
||||
// If the input is not a valid date, return 'Invalid Date'
|
// Fonction utilitaire pour vérifier les années bissextiles
|
||||
return jsDate |
function isLeapYear(year) { |
||||
|
return (year % 4 === 0 && year % 100 !== 0) || (year % 400 === 0); |
||||
} |
} |
||||
|
|
||||
|
// ✅ Mise à jour d'un étudiant existant
|
||||
|
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 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() |
||||
|
]; |
||||
|
|
||||
|
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}`); |
||||
|
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
|
||||
async function insertMultipleEtudiants(etudiants) { |
async function insertMultipleEtudiants(etudiants) { |
||||
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, |
||||
mention_id, num_inscription, sexe, cin, date_delivrance, nationalite, |
mention_id, num_inscription, sexe, cin, date_delivrance, nationalite, |
||||
annee_bacc, serie, boursier, domaine, contact, parcours |
annee_bacc, serie, boursier, domaine, contact, parcours |
||||
) |
) VALUES ? |
||||
VALUES ? |
`;
|
||||
` |
|
||||
|
|
||||
// Prepare values as array of arrays
|
const values = etudiants.map(row => [ |
||||
const values = etudiants.map((row) => [ |
|
||||
row.nom, |
row.nom, |
||||
row.prenom, |
row.prenom, |
||||
getCompressedDefaultImage(), // photos (you can adjust this if needed)
|
getCompressedDefaultImage(), |
||||
convertToISODate(row.date_naissance), |
convertToISODate(row.date_naissance), |
||||
row.niveau, |
row.niveau, |
||||
row.annee_scolaire, |
row.annee_scolaire, |
||||
row.code_redoublement, |
row.code_redoublement, |
||||
row.mention, |
row.mention, |
||||
row.num_inscription, |
row.num_inscription.toString(), |
||||
row.sexe, |
row.sexe, |
||||
row.cin, |
row.cin, |
||||
convertToISODate(row.date_de_delivrance), |
convertToISODate(row.date_de_delivrance), |
||||
row.nationaliter, |
row.nationaliter, |
||||
row.annee_baccalaureat, |
parseInt(row.annee_baccalaureat, 10), |
||||
row.serie, |
row.serie, |
||||
row.boursier, |
row.boursier, |
||||
row.domaine, |
fixEncoding(row.domaine), |
||||
row.contact, |
row.contact, |
||||
null // parcours
|
null |
||||
]) |
]); |
||||
|
|
||||
try { |
try { |
||||
const [result] = await pool.query(sql, [values]) |
const [result] = await pool.query(sql, [values]); |
||||
return { |
return { success: true, affectedRows: result.affectedRows }; |
||||
success: true, |
|
||||
affectedRows: result.affectedRows, |
|
||||
insertId: result.insertId |
|
||||
} |
|
||||
} catch (error) { |
} catch (error) { |
||||
return { success: false, error: error.message } |
console.error('❌ Erreur MySQL :', error.message); |
||||
|
return { success: false, error: error.message }; |
||||
} |
} |
||||
} |
} |
||||
|
|
||||
/** |
// ✅ Import fichier vers base
|
||||
* Function to import data from XLSX or CSV file into SQLite database |
|
||||
* @param {string} filePath - Path to the file (either .xlsx or .csv) |
|
||||
*/ |
|
||||
async function importFileToDatabase(filePath) { |
async function importFileToDatabase(filePath) { |
||||
const fileExtension = path.extname(filePath).toLowerCase() |
const fileExtension = path.extname(filePath).toLowerCase(); |
||||
|
let records; |
||||
|
|
||||
// Determine the file type and parse accordingly
|
|
||||
let records |
|
||||
if (fileExtension === '.xlsx') { |
if (fileExtension === '.xlsx') { |
||||
// Read and parse XLSX file
|
const workbook = XLSX.readFile(filePath); |
||||
const workbook = XLSX.readFile(filePath) |
const worksheet = workbook.Sheets[workbook.SheetNames[0]]; |
||||
const worksheet = workbook.Sheets[workbook.SheetNames[0]] // Assuming data is in the first sheet
|
// raw: true pour garder les valeurs brutes, surtout pour les dates
|
||||
records = XLSX.utils.sheet_to_json(worksheet, { defval: '', raw: false }) |
records = XLSX.utils.sheet_to_json(worksheet, { defval: ''}); |
||||
} else if (fileExtension === '.csv') { |
} else if (fileExtension === '.csv') { |
||||
// Read and parse CSV file
|
const fileContent = fs.readFileSync(filePath, 'utf8'); |
||||
const fileContent = fs.readFileSync(filePath, 'utf8') |
records = parse(fileContent, { columns: true, skip_empty_lines: true }); |
||||
records = parse(fileContent, { |
}else { |
||||
columns: true, |
console.error('Unsupported file format.'); |
||||
skip_empty_lines: true |
return { error: true, message: 'Format de fichier non supporté.' }; |
||||
}) |
|
||||
} else { |
|
||||
console.error('Unsupported file format. Only .xlsx and .csv are allowed.') |
|
||||
return |
|
||||
} |
} |
||||
|
|
||||
// ✅ Count number of data rows
|
console.log(`📄 Nombre de lignes : ${records.length}`); |
||||
const numberOfLines = records.length |
|
||||
console.log(`Number of data rows: ${numberOfLines}`) |
|
||||
|
|
||||
try { |
// Vérifier champs obligatoires
|
||||
let error = true |
const requiredFields = [ |
||||
let message = '' |
'nom', 'date_naissance', 'niveau', 'annee_scolaire', |
||||
|
'mention', 'num_inscription', 'nationaliter', 'sexe', |
||||
// Vérifier les données en une seule boucle
|
'annee_baccalaureat', 'serie', 'code_redoublement', |
||||
let oldNum = '' |
'boursier', 'domaine' |
||||
for (const row of records) { |
]; |
||||
if ( |
|
||||
!row.nom || |
for (const [i, row] of records.entries()) { |
||||
// !row.prenom ||
|
for (const field of requiredFields) { |
||||
!row.date_naissance || |
if (!row[field]) { |
||||
!row.niveau || |
const msg = `Le champ '${field}' est manquant à la ligne ${i + 2}`; |
||||
!row.annee_scolaire || |
console.error(msg); |
||||
!row.mention || |
return { error: true, message: msg }; |
||||
!row.num_inscription || |
|
||||
!row.nationaliter || |
|
||||
!row.sexe || |
|
||||
// !row.cin ||
|
|
||||
// !row.date_de_delivrance ||
|
|
||||
!row.annee_baccalaureat || |
|
||||
!row.serie || |
|
||||
!row.code_redoublement || |
|
||||
!row.boursier || |
|
||||
!row.domaine |
|
||||
// ||
|
|
||||
// !row.contact
|
|
||||
) { |
|
||||
if (!row.nom) { |
|
||||
message = "Le champ 'nom' est inconnu" |
|
||||
} |
|
||||
// else if (!row.prenom) {
|
|
||||
// message = "Le champ 'prenom' est inconnu"
|
|
||||
// }
|
|
||||
else if (!row.date_naissance) { |
|
||||
message = "Le champ 'date_naissance' est inconnu" |
|
||||
} else if (!row.niveau) { |
|
||||
message = "Le champ 'niveau' est inconnu" |
|
||||
} else if (!row.annee_scolaire) { |
|
||||
message = "Le champ 'annee_scolaire' est inconnu" |
|
||||
} else if (!row.mention) { |
|
||||
message = "Le champ 'mention' est inconnu" |
|
||||
} else if (!row.num_inscription) { |
|
||||
message = "Le champ 'num_inscription' est inconnu" |
|
||||
} else if (!row.nationaliter) { |
|
||||
message = "Le champ 'nationaliter' est inconnu" |
|
||||
} else if (!row.sexe) { |
|
||||
message = "Le champ 'sexe' est inconnu" |
|
||||
} |
|
||||
// else if (!row.cin) {
|
|
||||
// message = "Le champ 'cin' est inconnu"
|
|
||||
// } else if (!row.date_de_delivrance) {
|
|
||||
// message = "Le champ 'date_de_delivrance' est inconnu"
|
|
||||
// }
|
|
||||
else if (!row.annee_baccalaureat) { |
|
||||
message = "Le champ 'annee_baccalaureat' est inconnu" |
|
||||
} else if (!row.serie) { |
|
||||
message = "Le champ 'serie' est inconnu" |
|
||||
} else if (!row.code_redoublement) { |
|
||||
message = "Le champ 'code_redoublement' est inconnu" |
|
||||
} else if (!row.boursier) { |
|
||||
message = "Le champ 'boursier' est inconnu" |
|
||||
} else if (!row.domaine) { |
|
||||
message = "Le champ 'domaine' est inconnu" |
|
||||
} |
|
||||
// else if (!row.contact) {
|
|
||||
// message = "Le champ 'contact' est inconnu"
|
|
||||
// }
|
|
||||
error = false |
|
||||
break |
|
||||
} |
} |
||||
} |
} |
||||
|
} |
||||
|
|
||||
const query = 'SELECT * FROM mentions' |
const [mentionRows] = await pool.query('SELECT * FROM mentions'); |
||||
const [rows] = await pool.query(query) |
const [statusRows] = await pool.query('SELECT * FROM status'); |
||||
const MentionList = rows |
|
||||
console.log(MentionList) |
|
||||
|
|
||||
if (error !== false) { |
|
||||
let newReccord = [] |
|
||||
// Utiliser transaction pour éviter une latence si l'insertion dépasse 100
|
|
||||
for (const row of records) { |
|
||||
// Convert row.mention to uppercase and compare with ListMention.nom and ListMention.uniter (also converted to uppercase)
|
|
||||
const matchedMention = MentionList.find( |
|
||||
(mention) => |
|
||||
mention.nom.toUpperCase() === row.mention.toUpperCase() || |
|
||||
mention.uniter.toUpperCase() === row.mention.toUpperCase() |
|
||||
) |
|
||||
|
|
||||
// If a match is found, update row.mention with ListMention.id
|
|
||||
if (matchedMention) { |
|
||||
row.mention = matchedMention.id |
|
||||
} |
|
||||
|
|
||||
const query = 'SELECT * FROM status' |
const etudiantsToInsert = []; |
||||
|
const doublons = []; |
||||
let [rows] = await pool.query(query) |
console.log(records); |
||||
let response = rows |
|
||||
let statutCode |
|
||||
for (let index = 0; index < response.length; index++) { |
|
||||
let nom = response[index].nom |
|
||||
let nomLower = nom.toLowerCase() // Correct method
|
|
||||
let find1 = row.code_redoublement.slice(0, 1) |
|
||||
let find2 = row.code_redoublement.slice(0, 3) |
|
||||
|
|
||||
if (nomLower.slice(0, 1) == find1.toLowerCase()) { |
|
||||
statutCode = response[index].id |
|
||||
} else if (nomLower.slice(0, 3) == find2.toLowerCase()) { |
|
||||
statutCode = response[index].id |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
row.code_redoublement = statutCode |
for (const row of records) { |
||||
row.num_inscription = row.num_inscription.toString() |
// Mapping mention
|
||||
try { |
console.log('Avant conversion date_naissance:', row.date_naissance); |
||||
let compare = row.num_inscription |
row.date_naissance = convertToISODate(row.date_naissance); |
||||
if (compare == oldNum) { |
console.log('Après conversion date_naissance:', row.date_naissance); |
||||
row.num_inscription = String(row.num_inscription) |
const matchedMention = mentionRows.find( |
||||
} |
m => m.nom.toUpperCase() === row.mention.toUpperCase() || |
||||
console.log(row.code_redoublement) |
m.uniter.toUpperCase() === row.mention.toUpperCase() |
||||
newReccord.push(row) |
); |
||||
oldNum = compare |
if (matchedMention) row.mention = matchedMention.id; |
||||
} catch (error) { |
|
||||
console.log(error) |
// 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()) |
||||
|
); |
||||
|
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] |
||||
|
); |
||||
|
|
||||
|
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}` }; |
||||
} |
} |
||||
console.log(insertMultipleEtudiants(newReccord)) |
continue; |
||||
} |
} |
||||
|
|
||||
return { error, message } |
etudiantsToInsert.push(row); |
||||
} catch (error) { |
|
||||
console.error('Error inserting record:', error) |
|
||||
return { error: 'error' } |
|
||||
} |
} |
||||
} |
|
||||
|
|
||||
module.exports = { |
console.log(etudiantsToInsert); |
||||
importFileToDatabase |
|
||||
|
// 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.`; |
||||
|
return { error: false, message: msg }; |
||||
} |
} |
||||
|
|
||||
|
module.exports = { importFileToDatabase }; |
||||
|
|||||
|
Before Width: | Height: | Size: 35 KiB |
|
Before Width: | Height: | Size: 43 KiB |
@ -1,912 +0,0 @@ |
|||||
import { app, shell, BrowserWindow, ipcMain, Tray, Menu } from 'electron' |
|
||||
import { join } from 'path' |
|
||||
const path = require('path') |
|
||||
import { electronApp, optimizer, is } from '@electron-toolkit/utils' |
|
||||
import icon from '../../resources/logo.ico?asset' // Your tray icon file
|
|
||||
const database = require('../../database/database2') |
|
||||
|
|
||||
database |
|
||||
.createTables() |
|
||||
.then(() => database.insertDefaultAdmin()) |
|
||||
.then(() => database.insertStatusesIfNotExist()) |
|
||||
.catch(console.error) |
|
||||
|
|
||||
const { createConfigIp, updateIPConfig } = require('../../database/Models/IpConfig') |
|
||||
const { importFileToDatabase } = require('../../database/import/Etudiants') |
|
||||
const { loginUser, forgotPassword, insertUser, updateUser } = require('../../database/Models/Users') |
|
||||
const { |
|
||||
insertEtudiant, |
|
||||
getSingleEtudiant, |
|
||||
FilterDataByNiveau, |
|
||||
updateEtudiant, |
|
||||
changePDP, |
|
||||
updateParcours, |
|
||||
createTranche, |
|
||||
getTranche, |
|
||||
updateTranche, |
|
||||
deleteTranche, |
|
||||
getSingleTranche |
|
||||
} = require('../../database/Models/Etudiants') |
|
||||
const { |
|
||||
insertNiveau, |
|
||||
updateNiveau, |
|
||||
getSingleNiveau, |
|
||||
deleteNiveau |
|
||||
} = require('../../database/Models/Niveau') |
|
||||
const { |
|
||||
insertNote, |
|
||||
getNote, |
|
||||
updateNote, |
|
||||
showMoyen, |
|
||||
getMatiereAndNote, |
|
||||
getNotesWithRepechToDisplay |
|
||||
} = require('../../database/Models/Notes') |
|
||||
const { |
|
||||
createMatiere, |
|
||||
getSingleMatiere, |
|
||||
updateMatiere, |
|
||||
displayMatiereFromForm, |
|
||||
deleteMatiere, |
|
||||
asygnationToMention, |
|
||||
getMentionMatiere, |
|
||||
getMentionMatiereChecked, |
|
||||
getSemestreMatiere, |
|
||||
insertUpdateMentionSemestre, |
|
||||
insertNewProf, |
|
||||
getSIngleProf, |
|
||||
updateProf |
|
||||
} = require('../../database/Models/Matieres') |
|
||||
const { importFileToDatabaseMatiere } = require('../../database/import/Matieres') |
|
||||
const { importNiveau } = require('../../database/import/Niveau') |
|
||||
const { updateSysteme } = require('../../database/Models/NoteSysrem') |
|
||||
const { |
|
||||
createAnneeScolaire, |
|
||||
deleteAnneeScolaire, |
|
||||
getSingleAnneScolaire, |
|
||||
updateAnneeScolaire, |
|
||||
setCurrent |
|
||||
} = require('../../database/Models/AnneeScolaire') |
|
||||
const { |
|
||||
createMention, |
|
||||
deleteMention, |
|
||||
getSingleMention, |
|
||||
updateMention |
|
||||
} = require('../../database/Models/Mentions') |
|
||||
const { |
|
||||
getNoteRepech, |
|
||||
updateNoteRepech, |
|
||||
showMoyenRepech |
|
||||
} = require('../../database/Models/NoteRepechage') |
|
||||
const { |
|
||||
updateCurrentYears, |
|
||||
updateStudents, |
|
||||
updateNessesaryTable |
|
||||
} = require('../../database/function/System') |
|
||||
const { autoUpdater } = require('electron-updater') |
|
||||
const { URL } = require('../../database/api/Config') |
|
||||
const { |
|
||||
insertParcour, |
|
||||
getSingleParcours, |
|
||||
deletes, |
|
||||
updateparcour, |
|
||||
parcourMatiere, |
|
||||
extractFiche, |
|
||||
getParcourMatiere |
|
||||
} = require('../../database/Models/Parcours') |
|
||||
|
|
||||
// Declare mainWindow and tray in the global scope
|
|
||||
let mainWindow |
|
||||
let tray = null |
|
||||
updateCurrentYears() |
|
||||
updateStudents() |
|
||||
|
|
||||
autoUpdater.setFeedURL({ |
|
||||
provider: 'generic', |
|
||||
url: `${URL}/latest` // Ensure this points to the folder containing latest.yml
|
|
||||
}) |
|
||||
|
|
||||
function createWindow() { |
|
||||
// Create the browser window.
|
|
||||
mainWindow = new BrowserWindow({ |
|
||||
width: 1375, |
|
||||
minWidth: 1375, |
|
||||
height: 740, |
|
||||
minHeight: 740, |
|
||||
show: false, |
|
||||
autoHideMenuBar: true, |
|
||||
fullscreen: false, |
|
||||
icon: path.join(__dirname, 'resources', 'logo.ico'), // Path to your icon,
|
|
||||
...(process.platform === 'linux' ? { icon } : {}), |
|
||||
webPreferences: { |
|
||||
preload: join(__dirname, '../preload/index.js'), |
|
||||
nodeIntegration: true, |
|
||||
contextIsolation: true, |
|
||||
sandbox: false |
|
||||
} |
|
||||
}) |
|
||||
|
|
||||
// Désactiver les raccourcis clavier
|
|
||||
mainWindow.webContents.on('before-input-event', (event, input) => { |
|
||||
if (input.control || input.meta || input.alt || input.key === 'F11') { |
|
||||
event.preventDefault() |
|
||||
} |
|
||||
}) |
|
||||
|
|
||||
mainWindow.on('ready-to-show', () => { |
|
||||
mainWindow.maximize() // Maximiser la fenêtre
|
|
||||
mainWindow.show() |
|
||||
}) |
|
||||
|
|
||||
mainWindow.webContents.setWindowOpenHandler((details) => { |
|
||||
shell.openExternal(details.url) |
|
||||
return { action: 'deny' } |
|
||||
}) |
|
||||
|
|
||||
// Load the appropriate URL based on environment
|
|
||||
if (is.dev && process.env['ELECTRON_RENDERER_URL']) { |
|
||||
mainWindow.loadURL(process.env['ELECTRON_RENDERER_URL']) |
|
||||
} else { |
|
||||
mainWindow.loadFile(join(__dirname, '../renderer/index.html')) |
|
||||
} |
|
||||
|
|
||||
// Handle window close (hide instead of closing)
|
|
||||
mainWindow.on('close', (event) => { |
|
||||
if (!app.isQuiting) { |
|
||||
event.preventDefault() |
|
||||
mainWindow.hide() // Minimize to tray instead of closing
|
|
||||
} else { |
|
||||
// Destroy the tray when quitting
|
|
||||
if (tray) tray.destroy() |
|
||||
} |
|
||||
}) |
|
||||
} |
|
||||
|
|
||||
// Function to create the system tray
|
|
||||
function createTray() { |
|
||||
const iconPath = icon // Use your icon path here
|
|
||||
tray = new Tray(iconPath) |
|
||||
|
|
||||
// Create a context menu for the tray
|
|
||||
const contextMenu = Menu.buildFromTemplate([ |
|
||||
{ |
|
||||
label: 'Ouvrir', |
|
||||
click: () => { |
|
||||
mainWindow.show() |
|
||||
mainWindow.webContents.send('navigateToRoute', '#/') // Send the route as a string
|
|
||||
} |
|
||||
}, |
|
||||
{ |
|
||||
label: 'A Propos', |
|
||||
click: () => { |
|
||||
mainWindow.show() |
|
||||
mainWindow.webContents.send('navigateToRoute', '#/apropos') // Send the route as a string
|
|
||||
} |
|
||||
}, |
|
||||
{ |
|
||||
label: 'Quit', |
|
||||
click: () => { |
|
||||
// Clear localStorage in the renderer process
|
|
||||
mainWindow.webContents |
|
||||
.executeJavaScript('localStorage.removeItem("ACCESS_TOKEN");') |
|
||||
.then(() => { |
|
||||
console.log('localStorage cleared.') |
|
||||
// Ensure the app quits entirely
|
|
||||
if (tray) { |
|
||||
app.quit() |
|
||||
tray.destroy() |
|
||||
// if (app.quit()) {
|
|
||||
// tray.destroy()
|
|
||||
// }
|
|
||||
} // Quit the app
|
|
||||
}) |
|
||||
.catch((err) => { |
|
||||
console.error('Error clearing localStorage:', err) |
|
||||
// Quit the app even if clearing fails
|
|
||||
if (tray) { |
|
||||
app.quit() |
|
||||
tray.destroy() |
|
||||
// if (app.quit()) {
|
|
||||
// tray.destroy()
|
|
||||
// }
|
|
||||
} |
|
||||
}) |
|
||||
} |
|
||||
} |
|
||||
]) |
|
||||
|
|
||||
tray.setToolTip('My Electron App') |
|
||||
tray.setContextMenu(contextMenu) |
|
||||
|
|
||||
// Show the app when the tray icon is clicked
|
|
||||
tray.on('click', () => { |
|
||||
mainWindow.show() |
|
||||
}) |
|
||||
} |
|
||||
|
|
||||
app.whenReady().then(() => { |
|
||||
electronApp.setAppUserModelId('com.electron') |
|
||||
autoUpdater.checkForUpdatesAndNotify() |
|
||||
|
|
||||
app.on('browser-window-created', (_, window) => { |
|
||||
optimizer.watchWindowShortcuts(window) |
|
||||
}) |
|
||||
|
|
||||
createWindow() |
|
||||
createTray() // Create the tray icon
|
|
||||
|
|
||||
app.on('activate', function () { |
|
||||
if (BrowserWindow.getAllWindows().length === 0) createWindow() |
|
||||
}) |
|
||||
}) |
|
||||
|
|
||||
// When an update is available
|
|
||||
autoUpdater.on('update-available', () => { |
|
||||
dialog.showMessageBox({ |
|
||||
type: 'info', |
|
||||
title: 'Mise à jour disponible', |
|
||||
message: 'Une nouvelle version est disponible. Téléchargement en cours...' |
|
||||
}) |
|
||||
}) |
|
||||
|
|
||||
// When the update is downloaded
|
|
||||
autoUpdater.on('update-downloaded', (info) => { |
|
||||
dialog |
|
||||
.showMessageBox({ |
|
||||
type: 'info', |
|
||||
title: 'Mise à jour prête', |
|
||||
message: `La version ${info.version} a été téléchargée. Redémarrer maintenant ?`, |
|
||||
buttons: ['Redémarrer', 'Plus tard'] |
|
||||
}) |
|
||||
.then((result) => { |
|
||||
if (result.response === 0) { |
|
||||
autoUpdater.quitAndInstall() |
|
||||
} |
|
||||
}) |
|
||||
}) |
|
||||
|
|
||||
// If an error occurs
|
|
||||
autoUpdater.on('error', (error) => { |
|
||||
dialog.showErrorBox('Update Error', error == null ? 'Unknown' : error.message) |
|
||||
}) |
|
||||
|
|
||||
// Quit the app when all windows are closed, except on macOS
|
|
||||
app.on('window-all-closed', () => { |
|
||||
if (process.platform !== 'darwin') { |
|
||||
app.quit() |
|
||||
} |
|
||||
}) |
|
||||
|
|
||||
// In this file you can include the rest of your app"s specific main process
|
|
||||
// code. You can also put them in separate files and require them here.
|
|
||||
|
|
||||
// Event for handling login
|
|
||||
ipcMain.handle('login', async (event, credentials) => { |
|
||||
const { username, password } = credentials |
|
||||
|
|
||||
const users = await loginUser(username, password) |
|
||||
|
|
||||
if (users) { |
|
||||
return { success: true, user: users } |
|
||||
} else { |
|
||||
return { success: false } |
|
||||
} |
|
||||
}) |
|
||||
|
|
||||
// Event for handling insert other user
|
|
||||
ipcMain.handle('insertUser', async (event, credentials) => { |
|
||||
const { username, email, password, roles } = credentials |
|
||||
|
|
||||
const users = await insertUser(username, email, password, roles) |
|
||||
|
|
||||
return users |
|
||||
}) |
|
||||
|
|
||||
// event for handlign forgot password
|
|
||||
ipcMain.handle('forgotPassword', async (event, credentials) => { |
|
||||
const { email, password, passwordConfirmation } = credentials |
|
||||
|
|
||||
const updated = await forgotPassword(email, password, passwordConfirmation) |
|
||||
|
|
||||
if (updated) { |
|
||||
return updated |
|
||||
} |
|
||||
}) |
|
||||
|
|
||||
// event for updating users
|
|
||||
ipcMain.handle('updateUsers', async (event, credentials) => { |
|
||||
const { username, newUsername, email, newEmail, passwordVerif, password, id } = credentials |
|
||||
|
|
||||
const update = await updateUser(newUsername, newEmail, password, id) |
|
||||
|
|
||||
return update |
|
||||
}) |
|
||||
|
|
||||
// event for quit app
|
|
||||
ipcMain.handle('quit', async () => { |
|
||||
app.quit() |
|
||||
}) |
|
||||
|
|
||||
// event for minimizing the app
|
|
||||
ipcMain.handle('minimize', async () => { |
|
||||
if (mainWindow) { |
|
||||
mainWindow.minimize() |
|
||||
} |
|
||||
}) |
|
||||
|
|
||||
// event for insert etudiants
|
|
||||
ipcMain.handle('insertEtudiant', async (event, credentials) => { |
|
||||
const { |
|
||||
nom, |
|
||||
prenom, |
|
||||
photos, |
|
||||
date_de_naissances, |
|
||||
niveau, |
|
||||
annee_scolaire, |
|
||||
status, |
|
||||
num_inscription, |
|
||||
mention_id, |
|
||||
sexe, |
|
||||
nationaliter, |
|
||||
cin, |
|
||||
date_delivrence, |
|
||||
annee_bacc, |
|
||||
serie, |
|
||||
boursier, |
|
||||
domaine, |
|
||||
contact, |
|
||||
parcours |
|
||||
} = credentials |
|
||||
|
|
||||
const insert = await insertEtudiant( |
|
||||
nom, |
|
||||
prenom, |
|
||||
photos, |
|
||||
date_de_naissances, |
|
||||
niveau, |
|
||||
annee_scolaire, |
|
||||
status, |
|
||||
num_inscription, |
|
||||
mention_id, |
|
||||
sexe, |
|
||||
nationaliter, |
|
||||
cin, |
|
||||
date_delivrence, |
|
||||
annee_bacc, |
|
||||
serie, |
|
||||
boursier, |
|
||||
domaine, |
|
||||
contact, |
|
||||
parcours |
|
||||
) |
|
||||
|
|
||||
return insert |
|
||||
}) |
|
||||
|
|
||||
// event for fetching single
|
|
||||
ipcMain.handle('getByNiveau', async (event, credentials) => { |
|
||||
const { niveau } = credentials |
|
||||
|
|
||||
const getSingle = await FilterDataByNiveau(niveau) |
|
||||
|
|
||||
return getSingle |
|
||||
}) |
|
||||
|
|
||||
// event for fetching single
|
|
||||
ipcMain.handle('single', async (event, credentials) => { |
|
||||
const { id } = credentials |
|
||||
|
|
||||
const getSingle = await getSingleEtudiant(id) |
|
||||
|
|
||||
return getSingle |
|
||||
}) |
|
||||
|
|
||||
// event for inserting niveau
|
|
||||
ipcMain.handle('insertNiveau', async (event, credentials) => { |
|
||||
const { nom } = credentials |
|
||||
|
|
||||
const insert = await insertNiveau(nom) |
|
||||
|
|
||||
return insert |
|
||||
}) |
|
||||
|
|
||||
// event for updating etudiants
|
|
||||
ipcMain.handle('updateETudiants', async (event, credentials) => { |
|
||||
const { |
|
||||
nom, |
|
||||
prenom, |
|
||||
photos, |
|
||||
date_de_naissances, |
|
||||
niveau, |
|
||||
annee_scolaire, |
|
||||
status, |
|
||||
mention_id, |
|
||||
num_inscription, |
|
||||
id, |
|
||||
sexe, |
|
||||
nationalite, |
|
||||
cin, |
|
||||
date_delivrance, |
|
||||
annee_bacc, |
|
||||
serie, |
|
||||
boursier, |
|
||||
domaine, |
|
||||
contact, |
|
||||
parcours |
|
||||
} = credentials |
|
||||
|
|
||||
const updating = await updateEtudiant( |
|
||||
nom, |
|
||||
prenom, |
|
||||
photos, |
|
||||
date_de_naissances, |
|
||||
niveau, |
|
||||
annee_scolaire, |
|
||||
status, |
|
||||
mention_id, |
|
||||
num_inscription, |
|
||||
id, |
|
||||
sexe, |
|
||||
nationalite, |
|
||||
cin, |
|
||||
date_delivrance, |
|
||||
annee_bacc, |
|
||||
serie, |
|
||||
boursier, |
|
||||
domaine, |
|
||||
contact, |
|
||||
parcours |
|
||||
) |
|
||||
|
|
||||
return updating |
|
||||
}) |
|
||||
|
|
||||
// event for updating etudiants pdp
|
|
||||
ipcMain.handle('updateETudiantsPDP', async (event, credentials) => { |
|
||||
const { pdp, id } = credentials |
|
||||
|
|
||||
const updating = await changePDP(pdp, id) |
|
||||
|
|
||||
return updating |
|
||||
}) |
|
||||
|
|
||||
// event for adding notes
|
|
||||
ipcMain.handle('insertNote', async (event, credentials) => { |
|
||||
const { etudiant_id, etudiant_niveau, mention_id, formData, annee_scolaire } = credentials |
|
||||
|
|
||||
const insert = await insertNote( |
|
||||
etudiant_id, |
|
||||
etudiant_niveau, |
|
||||
mention_id, |
|
||||
formData, |
|
||||
annee_scolaire |
|
||||
) |
|
||||
|
|
||||
return insert |
|
||||
}) |
|
||||
|
|
||||
// event for get single note
|
|
||||
ipcMain.handle('getSingleNote', async (event, credentials) => { |
|
||||
const { id, niveau, mention_id } = credentials |
|
||||
|
|
||||
const get = await getNote(id, niveau, mention_id) |
|
||||
|
|
||||
return get |
|
||||
}) |
|
||||
|
|
||||
// event for get single note repech
|
|
||||
ipcMain.handle('getNotesRepech', async (event, credentials) => { |
|
||||
const { id, niveau, mention_id } = credentials |
|
||||
|
|
||||
const get = await getNoteRepech(id, niveau, mention_id) |
|
||||
|
|
||||
return get |
|
||||
}) |
|
||||
|
|
||||
// event for updating note
|
|
||||
ipcMain.handle('updatetNote', async (event, credentials) => { |
|
||||
const { formData, niveau, id, mention_id, annee_scolaire } = credentials |
|
||||
|
|
||||
const update = await updateNote(formData, niveau, id, mention_id, annee_scolaire) |
|
||||
|
|
||||
return update |
|
||||
}) |
|
||||
|
|
||||
// event for updating note repech
|
|
||||
ipcMain.handle('updatetNoteRepech', async (event, credentials) => { |
|
||||
const { formData2, niveau, id } = credentials |
|
||||
|
|
||||
const update = await updateNoteRepech(formData2, niveau, id) |
|
||||
|
|
||||
return update |
|
||||
}) |
|
||||
|
|
||||
// event to get single matiere
|
|
||||
ipcMain.handle('getMatiereByID', async (event, credentials) => { |
|
||||
const { id } = credentials |
|
||||
|
|
||||
const matiere = await getSingleMatiere(id) |
|
||||
|
|
||||
return matiere |
|
||||
}) |
|
||||
|
|
||||
// event for updating matiere
|
|
||||
ipcMain.handle('updateMatiere', async (event, credentials) => { |
|
||||
const { nom, credit, uniter, ue, id } = credentials |
|
||||
|
|
||||
const update = await updateMatiere(nom, id, credit, uniter, ue) |
|
||||
|
|
||||
return update |
|
||||
}) |
|
||||
// event for importExcel
|
|
||||
ipcMain.handle('importexcel', async (event, credentials) => { |
|
||||
const files = credentials |
|
||||
console.log(files) |
|
||||
const insert = await importFileToDatabase(files) |
|
||||
|
|
||||
return insert |
|
||||
}) |
|
||||
|
|
||||
// event for udatign a single niveau
|
|
||||
ipcMain.handle('updateSingleNiveau', async (event, credentials) => { |
|
||||
const { nom, id } = credentials |
|
||||
|
|
||||
const update = updateNiveau(nom, id) |
|
||||
|
|
||||
return update |
|
||||
}) |
|
||||
|
|
||||
// event to get single niveau
|
|
||||
ipcMain.handle('singleNiveau', async (event, credentials) => { |
|
||||
const { id } = credentials |
|
||||
|
|
||||
const update = getSingleNiveau(id) |
|
||||
|
|
||||
return update |
|
||||
}) |
|
||||
|
|
||||
// event for creating matiere
|
|
||||
ipcMain.handle('createMatiere', async (event, credentials) => { |
|
||||
const { nom, credit, uniter, ue } = credentials |
|
||||
|
|
||||
const create = createMatiere(nom, credit, uniter, ue) |
|
||||
|
|
||||
return create |
|
||||
}) |
|
||||
|
|
||||
// event for import excel matiere
|
|
||||
ipcMain.handle('importExcelMatiere', async (event, credentials) => { |
|
||||
const files = credentials |
|
||||
console.log(files) |
|
||||
const insert = await importFileToDatabaseMatiere(files) |
|
||||
|
|
||||
return insert |
|
||||
}) |
|
||||
|
|
||||
// event for import excel niveau
|
|
||||
ipcMain.handle('importNiveau', async (event, credentials) => { |
|
||||
const files = credentials |
|
||||
console.log(files) |
|
||||
const insert = await importNiveau(files) |
|
||||
|
|
||||
return insert |
|
||||
}) |
|
||||
|
|
||||
// event for updating note systeme
|
|
||||
ipcMain.handle('updateNoteSysteme', async (event, credentials) => { |
|
||||
const { id, admis, redouble, renvoyer } = credentials |
|
||||
|
|
||||
const update = updateSysteme(id, admis, redouble, renvoyer) |
|
||||
return update |
|
||||
}) |
|
||||
|
|
||||
// event for updating note systeme
|
|
||||
ipcMain.handle('createAnneeScolaire', async (event, credentials) => { |
|
||||
const { code, debut, fin } = credentials |
|
||||
|
|
||||
const create = createAnneeScolaire(code, debut, fin) |
|
||||
return create |
|
||||
}) |
|
||||
|
|
||||
ipcMain.handle('getMoyene', async (event, credentials) => { |
|
||||
const { niveau, scolaire } = credentials |
|
||||
console.log('index.js', niveau, scolaire) |
|
||||
|
|
||||
const create = showMoyen(niveau, scolaire) |
|
||||
return create |
|
||||
}) |
|
||||
|
|
||||
ipcMain.handle('getMoyenneRepech', async (event, credentials) => { |
|
||||
const { niveau, scolaire } = credentials |
|
||||
console.log('index.js', niveau, scolaire) |
|
||||
|
|
||||
const create = showMoyenRepech(niveau, scolaire) |
|
||||
return create |
|
||||
}) |
|
||||
|
|
||||
ipcMain.handle('noteMatiere', async (event, credentials) => { |
|
||||
const { id, niveau, annee_scolaire } = credentials |
|
||||
|
|
||||
const get = getMatiereAndNote(id, niveau, annee_scolaire) |
|
||||
return get |
|
||||
}) |
|
||||
|
|
||||
ipcMain.handle('displayMatiereFromForm', async (event, credentials) => { |
|
||||
const { niveau, mention_id, parcours } = credentials |
|
||||
|
|
||||
const get = displayMatiereFromForm(niveau, mention_id, parcours) |
|
||||
return get |
|
||||
}) |
|
||||
|
|
||||
ipcMain.handle('createMention', async (event, credentials) => { |
|
||||
const { nom, uniter } = credentials |
|
||||
|
|
||||
const get = createMention(nom, uniter) |
|
||||
return get |
|
||||
}) |
|
||||
|
|
||||
ipcMain.handle('getSingleMention', async (event, credentials) => { |
|
||||
const { id } = credentials |
|
||||
|
|
||||
const get = getSingleMention(id) |
|
||||
return get |
|
||||
}) |
|
||||
|
|
||||
ipcMain.handle('updateMention', async (event, credentials) => { |
|
||||
const { nom, uniter, id } = credentials |
|
||||
|
|
||||
const get = updateMention(nom, uniter, id) |
|
||||
return get |
|
||||
}) |
|
||||
|
|
||||
ipcMain.handle('deleteMention', async (event, credentials) => { |
|
||||
const { id } = credentials |
|
||||
|
|
||||
const get = deleteMention(id) |
|
||||
return get |
|
||||
}) |
|
||||
|
|
||||
ipcMain.handle('deleteNiveaus', async (event, credentials) => { |
|
||||
const { id } = credentials |
|
||||
|
|
||||
const get = deleteNiveau(id) |
|
||||
return get |
|
||||
}) |
|
||||
|
|
||||
ipcMain.handle('deleteMatiere', async (event, credentials) => { |
|
||||
const { id } = credentials |
|
||||
|
|
||||
const get = deleteMatiere(id) |
|
||||
return get |
|
||||
}) |
|
||||
|
|
||||
ipcMain.handle('asign', async (event, credentials) => { |
|
||||
const { formData, id } = credentials |
|
||||
// console.log(formData, id);
|
|
||||
const get = asygnationToMention(formData, id) |
|
||||
|
|
||||
return get |
|
||||
}) |
|
||||
|
|
||||
ipcMain.handle('asignSemestre', async (event, credentials) => { |
|
||||
const { id } = credentials |
|
||||
|
|
||||
const get = getMentionMatiereChecked(id) |
|
||||
|
|
||||
return get |
|
||||
}) |
|
||||
|
|
||||
ipcMain.handle('getAsign', async (event, credentials) => { |
|
||||
const { id } = credentials |
|
||||
// console.log(formData, id);
|
|
||||
const get = getMentionMatiere(id) |
|
||||
|
|
||||
return get |
|
||||
}) |
|
||||
|
|
||||
ipcMain.handle('deleteAnneeScolaire', async (event, credentials) => { |
|
||||
const { id } = credentials |
|
||||
// console.log(formData, id);
|
|
||||
const get = deleteAnneeScolaire(id) |
|
||||
|
|
||||
return get |
|
||||
}) |
|
||||
|
|
||||
ipcMain.handle('getSemestreMatiere', async (event, credentials) => { |
|
||||
const { id } = credentials |
|
||||
// console.log(formData, id);
|
|
||||
const get = getSemestreMatiere(id) |
|
||||
|
|
||||
return get |
|
||||
}) |
|
||||
|
|
||||
ipcMain.handle('insertUpdateMentionSemestre', async (event, credentials) => { |
|
||||
const { id, selectedSemestres } = credentials |
|
||||
// console.log(formData, id);
|
|
||||
const get = insertUpdateMentionSemestre(id, selectedSemestres) |
|
||||
|
|
||||
return get |
|
||||
}) |
|
||||
|
|
||||
ipcMain.handle('getSingleAnneeScolaire', async (event, credentials) => { |
|
||||
const { id } = credentials |
|
||||
// console.log(formData, id);
|
|
||||
const get = getSingleAnneScolaire(id) |
|
||||
|
|
||||
return get |
|
||||
}) |
|
||||
|
|
||||
ipcMain.handle('updateAnneeScolaire', async (event, credentials) => { |
|
||||
const { code, debut, fin, id } = credentials |
|
||||
// console.log(formData, id);
|
|
||||
const get = updateAnneeScolaire(id, code, debut, fin) |
|
||||
|
|
||||
return get |
|
||||
}) |
|
||||
|
|
||||
ipcMain.handle('setCurrent', async (event, credentials) => { |
|
||||
const { id } = credentials |
|
||||
// console.log(formData, id);
|
|
||||
const get = setCurrent(id) |
|
||||
|
|
||||
return get |
|
||||
}) |
|
||||
|
|
||||
ipcMain.handle('noteRelerer', async (event, credentials) => { |
|
||||
const { id, anneescolaire, niveau } = credentials |
|
||||
// console.log(formData, id);
|
|
||||
const get = getNotesWithRepechToDisplay(id, anneescolaire, niveau) |
|
||||
|
|
||||
return get |
|
||||
}) |
|
||||
|
|
||||
ipcMain.handle('updateNessesary', async (event, credentials) => { |
|
||||
const { id, multiplicateur } = credentials |
|
||||
// console.log(formData, id);
|
|
||||
const get = updateNessesaryTable(id, multiplicateur) |
|
||||
|
|
||||
return get |
|
||||
}) |
|
||||
|
|
||||
ipcMain.handle('insertProf', async (event, credentials) => { |
|
||||
const { nom_enseignant, prenom_enseignant, contact, date, matiere_id } = credentials |
|
||||
// console.log(formData, id);
|
|
||||
const get = insertNewProf(matiere_id, nom_enseignant, prenom_enseignant, contact, date) |
|
||||
|
|
||||
return get |
|
||||
}) |
|
||||
|
|
||||
ipcMain.handle('insertParcours', async (event, credentials) => { |
|
||||
const { nom, uniter, mention_id } = credentials |
|
||||
// console.log(formData, id);
|
|
||||
const get = insertParcour(nom, uniter, mention_id) |
|
||||
|
|
||||
return get |
|
||||
}) |
|
||||
|
|
||||
ipcMain.handle('getSingleParcours', async (event, credentials) => { |
|
||||
const { id } = credentials |
|
||||
// console.log(formData, id);
|
|
||||
const get = getSingleParcours(id) |
|
||||
|
|
||||
return get |
|
||||
}) |
|
||||
|
|
||||
ipcMain.handle('deleteParcours', async (event, credentials) => { |
|
||||
const { id } = credentials |
|
||||
// console.log(formData, id);
|
|
||||
const get = deletes(id) |
|
||||
|
|
||||
return get |
|
||||
}) |
|
||||
|
|
||||
ipcMain.handle('updateParcours', async (event, credentials) => { |
|
||||
const { nom, uniter, mention_id, id } = credentials |
|
||||
// console.log(formData, id);
|
|
||||
const get = updateparcour(id, nom, uniter, mention_id) |
|
||||
|
|
||||
return get |
|
||||
}) |
|
||||
|
|
||||
ipcMain.handle('parcourMatiere', async (event, credentials) => { |
|
||||
const { matiere_id, parcour_id } = credentials |
|
||||
// console.log(formData, id);
|
|
||||
const get = parcourMatiere(matiere_id, parcour_id) |
|
||||
|
|
||||
return get |
|
||||
}) |
|
||||
|
|
||||
ipcMain.handle('getSingleProf', async (event, credentials) => { |
|
||||
const { id } = credentials |
|
||||
// console.log(formData, id);
|
|
||||
const get = getSIngleProf(id) |
|
||||
|
|
||||
return get |
|
||||
}) |
|
||||
|
|
||||
ipcMain.handle('updateProf', async (event, credentials) => { |
|
||||
const { nom_enseignant, prenom_enseignant, contact, date, matiere_id } = credentials |
|
||||
// console.log(formData, id);
|
|
||||
const get = updateProf(matiere_id, nom_enseignant, prenom_enseignant, contact, date) |
|
||||
|
|
||||
return get |
|
||||
}) |
|
||||
|
|
||||
ipcMain.handle('extractFiches', async (event, credentials) => { |
|
||||
const { matiere_id } = credentials |
|
||||
// console.log(formData, id);
|
|
||||
const get = extractFiche(matiere_id) |
|
||||
|
|
||||
return get |
|
||||
}) |
|
||||
|
|
||||
ipcMain.handle('getParcourMatiere', async (event, credentials) => { |
|
||||
const { matiere_id } = credentials |
|
||||
// console.log(formData, id);
|
|
||||
const get = getParcourMatiere(matiere_id) |
|
||||
|
|
||||
return get |
|
||||
}) |
|
||||
|
|
||||
ipcMain.handle('changeParcours', async (event, credentials) => { |
|
||||
const { parcours, user_id } = credentials |
|
||||
// console.log(formData, id);
|
|
||||
const get = updateParcours(parcours, user_id) |
|
||||
|
|
||||
return get |
|
||||
}) |
|
||||
|
|
||||
ipcMain.handle('createTranche', async (event, credentials) => { |
|
||||
const { etudiant_id, tranchename, montant } = credentials |
|
||||
// console.log(formData, id);
|
|
||||
const get = createTranche(etudiant_id, tranchename, montant) |
|
||||
|
|
||||
return get |
|
||||
}) |
|
||||
|
|
||||
ipcMain.handle('getTranche', async (event, credentials) => { |
|
||||
const { id } = credentials |
|
||||
// console.log(formData, id);
|
|
||||
const get = getTranche(id) |
|
||||
|
|
||||
return get |
|
||||
}) |
|
||||
|
|
||||
ipcMain.handle('updateTranche', async (event, credentials) => { |
|
||||
const { id, tranchename, montant } = credentials |
|
||||
// console.log(formData, id);
|
|
||||
const get = updateTranche(id, tranchename, montant) |
|
||||
|
|
||||
return get |
|
||||
}) |
|
||||
|
|
||||
ipcMain.handle('deleteTranche', async (event, credentials) => { |
|
||||
const { id } = credentials |
|
||||
console.log(id) |
|
||||
const get = deleteTranche(id) |
|
||||
|
|
||||
return get |
|
||||
}) |
|
||||
|
|
||||
ipcMain.handle('getSingleTranche', async (event, credentials) => { |
|
||||
const { id } = credentials |
|
||||
// console.log(formData, id);
|
|
||||
const get = getSingleTranche(id) |
|
||||
|
|
||||
return get |
|
||||
}) |
|
||||
|
|
||||
ipcMain.handle('createIPConfig', async (event, credentials) => { |
|
||||
const { ipname } = credentials |
|
||||
// console.log(formData, id);
|
|
||||
const get = createConfigIp(ipname) |
|
||||
|
|
||||
return get |
|
||||
}) |
|
||||
|
|
||||
ipcMain.handle('updateIPConfig', async (event, credentials) => { |
|
||||
const { id, ipname } = credentials |
|
||||
// console.log(formData, id);
|
|
||||
const get = updateIPConfig(id, ipname) |
|
||||
|
|
||||
return get |
|
||||
}) |
|
||||
@ -1,895 +0,0 @@ |
|||||
import { app, shell, BrowserWindow, ipcMain, Tray, Menu } from 'electron' |
|
||||
import { join } from 'path' |
|
||||
const path = require('path') |
|
||||
import { electronApp, optimizer, is } from '@electron-toolkit/utils' |
|
||||
import icon from '../../resources/logo.ico?asset' // Your tray icon file
|
|
||||
const database = require('../../database/database') |
|
||||
|
|
||||
database |
|
||||
.createTables() |
|
||||
.then(() => database.insertDefaultAdmin()) |
|
||||
.then(() => database.insertStatusesIfNotExist()) |
|
||||
.catch(console.error) |
|
||||
|
|
||||
const { loginUsers, insertUser, updateUser } = require('../../database/Models/Users') |
|
||||
const { createConfigIp, updateIPConfig } = require('../../database/Models/IpConfig') |
|
||||
const { importFileToDatabase } = require('../../database/import/Etudiants') |
|
||||
const { |
|
||||
insertEtudiant, |
|
||||
getSingleEtudiant, |
|
||||
FilterDataByNiveau, |
|
||||
updateEtudiant, |
|
||||
changePDP, |
|
||||
updateParcours, |
|
||||
createTranche, |
|
||||
getTranche, |
|
||||
updateTranche, |
|
||||
deleteTranche, |
|
||||
getSingleTranche |
|
||||
} = require('../../database/Models/Etudiants') |
|
||||
const { |
|
||||
insertNiveau, |
|
||||
updateNiveau, |
|
||||
getSingleNiveau, |
|
||||
deleteNiveau |
|
||||
} = require('../../database/Models/Niveau') |
|
||||
const { |
|
||||
insertNote, |
|
||||
getNote, |
|
||||
updateNote, |
|
||||
showMoyen, |
|
||||
getMatiereAndNote, |
|
||||
getNotesWithRepechToDisplay |
|
||||
} = require('../../database/Models/Notes') |
|
||||
const { |
|
||||
createMatiere, |
|
||||
getSingleMatiere, |
|
||||
updateMatiere, |
|
||||
displayMatiereFromForm, |
|
||||
deleteMatiere, |
|
||||
asygnationToMention, |
|
||||
getMentionMatiere, |
|
||||
getMentionMatiereChecked, |
|
||||
getSemestreMatiere, |
|
||||
insertUpdateMentionSemestre, |
|
||||
insertNewProf, |
|
||||
getSIngleProf, |
|
||||
updateProf |
|
||||
} = require('../../database/Models/Matieres') |
|
||||
const { importFileToDatabaseMatiere } = require('../../database/import/Matieres') |
|
||||
const { importNiveau } = require('../../database/import/Niveau') |
|
||||
const { updateSysteme } = require('../../database/Models/NoteSysrem') |
|
||||
const { |
|
||||
createAnneeScolaire, |
|
||||
deleteAnneeScolaire, |
|
||||
getSingleAnneScolaire, |
|
||||
updateAnneeScolaire, |
|
||||
setCurrent |
|
||||
} = require('../../database/Models/AnneeScolaire') |
|
||||
const { |
|
||||
createMention, |
|
||||
deleteMention, |
|
||||
getSingleMention, |
|
||||
updateMention |
|
||||
} = require('../../database/Models/Mentions') |
|
||||
const { |
|
||||
getNoteRepech, |
|
||||
updateNoteRepech, |
|
||||
showMoyenRepech |
|
||||
} = require('../../database/Models/NoteRepechage') |
|
||||
const { |
|
||||
updateCurrentYears, |
|
||||
updateStudents, |
|
||||
updateNessesaryTable |
|
||||
} = require('../../database/function/System') |
|
||||
const { autoUpdater } = require('electron-updater') |
|
||||
const { URL } = require('../../database/api/Config') |
|
||||
const { |
|
||||
insertParcour, |
|
||||
getSingleParcours, |
|
||||
deletes, |
|
||||
updateparcour, |
|
||||
parcourMatiere, |
|
||||
extractFiche, |
|
||||
getParcourMatiere |
|
||||
} = require('../../database/Models/Parcours') |
|
||||
|
|
||||
// Declare mainWindow and tray in the global scope
|
|
||||
let mainWindow |
|
||||
let tray = null |
|
||||
updateCurrentYears() |
|
||||
updateStudents() |
|
||||
|
|
||||
autoUpdater.setFeedURL({ |
|
||||
provider: 'generic', |
|
||||
url: `${URL}/latest` // Ensure this points to the folder containing latest.yml
|
|
||||
}) |
|
||||
|
|
||||
function createWindow() { |
|
||||
// Create the browser window.
|
|
||||
mainWindow = new BrowserWindow({ |
|
||||
width: 1375, |
|
||||
minWidth: 1375, |
|
||||
height: 740, |
|
||||
minHeight: 740, |
|
||||
show: false, |
|
||||
autoHideMenuBar: true, |
|
||||
fullscreen: false, |
|
||||
icon: path.join(__dirname, 'resources', 'logo.ico'), // Path to your icon,
|
|
||||
...(process.platform === 'linux' ? { icon } : {}), |
|
||||
webPreferences: { |
|
||||
preload: join(__dirname, '../preload/index.js'), |
|
||||
nodeIntegration: true, |
|
||||
contextIsolation: true, |
|
||||
sandbox: false |
|
||||
} |
|
||||
}) |
|
||||
|
|
||||
// Désactiver les raccourcis clavier
|
|
||||
mainWindow.webContents.on('before-input-event', (event, input) => { |
|
||||
if (input.control || input.meta || input.alt || input.key === 'F11') { |
|
||||
event.preventDefault() |
|
||||
} |
|
||||
}) |
|
||||
|
|
||||
mainWindow.on('ready-to-show', () => { |
|
||||
mainWindow.maximize() // Maximiser la fenêtre
|
|
||||
mainWindow.show() |
|
||||
}) |
|
||||
|
|
||||
mainWindow.webContents.setWindowOpenHandler((details) => { |
|
||||
shell.openExternal(details.url) |
|
||||
return { action: 'deny' } |
|
||||
}) |
|
||||
|
|
||||
// Load the appropriate URL based on environment
|
|
||||
if (is.dev && process.env['ELECTRON_RENDERER_URL']) { |
|
||||
mainWindow.loadURL(process.env['ELECTRON_RENDERER_URL']) |
|
||||
} else { |
|
||||
mainWindow.loadFile(join(__dirname, '../renderer/index.html')) |
|
||||
} |
|
||||
|
|
||||
// Handle window close (hide instead of closing)
|
|
||||
mainWindow.on('close', (event) => { |
|
||||
if (!app.isQuiting) { |
|
||||
event.preventDefault() |
|
||||
mainWindow.hide() // Minimize to tray instead of closing
|
|
||||
} else { |
|
||||
// Destroy the tray when quitting
|
|
||||
if (tray) tray.destroy() |
|
||||
} |
|
||||
}) |
|
||||
} |
|
||||
|
|
||||
// Function to create the system tray
|
|
||||
function createTray() { |
|
||||
const iconPath = icon // Use your icon path here
|
|
||||
tray = new Tray(iconPath) |
|
||||
|
|
||||
// Create a context menu for the tray
|
|
||||
const contextMenu = Menu.buildFromTemplate([ |
|
||||
{ |
|
||||
label: 'Ouvrir', |
|
||||
click: () => { |
|
||||
mainWindow.show() |
|
||||
mainWindow.webContents.send('navigateToRoute', '#/') // Send the route as a string
|
|
||||
} |
|
||||
}, |
|
||||
{ |
|
||||
label: 'A Propos', |
|
||||
click: () => { |
|
||||
mainWindow.show() |
|
||||
mainWindow.webContents.send('navigateToRoute', '#/apropos') // Send the route as a string
|
|
||||
} |
|
||||
}, |
|
||||
{ |
|
||||
label: 'Quit', |
|
||||
click: () => { |
|
||||
// Clear localStorage in the renderer process
|
|
||||
mainWindow.webContents |
|
||||
.executeJavaScript('localStorage.removeItem("ACCESS_TOKEN");') |
|
||||
.then(() => { |
|
||||
console.log('localStorage cleared.') |
|
||||
// Ensure the app quits entirely
|
|
||||
if (tray) { |
|
||||
app.quit() |
|
||||
tray.destroy() |
|
||||
// if (app.quit()) {
|
|
||||
// tray.destroy()
|
|
||||
// }
|
|
||||
} // Quit the app
|
|
||||
}) |
|
||||
.catch((err) => { |
|
||||
console.error('Error clearing localStorage:', err) |
|
||||
// Quit the app even if clearing fails
|
|
||||
if (tray) { |
|
||||
app.quit() |
|
||||
tray.destroy() |
|
||||
// if (app.quit()) {
|
|
||||
// tray.destroy()
|
|
||||
// }
|
|
||||
} |
|
||||
}) |
|
||||
} |
|
||||
} |
|
||||
]) |
|
||||
|
|
||||
tray.setToolTip('My Electron App') |
|
||||
tray.setContextMenu(contextMenu) |
|
||||
|
|
||||
// Show the app when the tray icon is clicked
|
|
||||
tray.on('click', () => { |
|
||||
mainWindow.show() |
|
||||
}) |
|
||||
} |
|
||||
|
|
||||
app.whenReady().then(() => { |
|
||||
electronApp.setAppUserModelId('com.electron') |
|
||||
autoUpdater.checkForUpdatesAndNotify() |
|
||||
|
|
||||
app.on('browser-window-created', (_, window) => { |
|
||||
optimizer.watchWindowShortcuts(window) |
|
||||
}) |
|
||||
|
|
||||
createWindow() |
|
||||
createTray() // Create the tray icon
|
|
||||
|
|
||||
app.on('activate', function () { |
|
||||
if (BrowserWindow.getAllWindows().length === 0) createWindow() |
|
||||
}) |
|
||||
}) |
|
||||
|
|
||||
// When an update is available
|
|
||||
autoUpdater.on('update-available', () => { |
|
||||
dialog.showMessageBox({ |
|
||||
type: 'info', |
|
||||
title: 'Mise à jour disponible', |
|
||||
message: 'Une nouvelle version est disponible. Téléchargement en cours...' |
|
||||
}) |
|
||||
}) |
|
||||
|
|
||||
// When the update is downloaded
|
|
||||
autoUpdater.on('update-downloaded', (info) => { |
|
||||
dialog |
|
||||
.showMessageBox({ |
|
||||
type: 'info', |
|
||||
title: 'Mise à jour prête', |
|
||||
message: `La version ${info.version} a été téléchargée. Redémarrer maintenant ?`, |
|
||||
buttons: ['Redémarrer', 'Plus tard'] |
|
||||
}) |
|
||||
.then((result) => { |
|
||||
if (result.response === 0) { |
|
||||
autoUpdater.quitAndInstall() |
|
||||
} |
|
||||
}) |
|
||||
}) |
|
||||
|
|
||||
// If an error occurs
|
|
||||
autoUpdater.on('error', (error) => { |
|
||||
dialog.showErrorBox('Update Error', error == null ? 'Unknown' : error.message) |
|
||||
}) |
|
||||
|
|
||||
// Quit the app when all windows are closed, except on macOS
|
|
||||
app.on('window-all-closed', () => { |
|
||||
if (process.platform !== 'darwin') { |
|
||||
app.quit() |
|
||||
} |
|
||||
}) |
|
||||
|
|
||||
// In this file you can include the rest of your app"s specific main process
|
|
||||
// code. You can also put them in separate files and require them here.
|
|
||||
|
|
||||
// event for quit app
|
|
||||
ipcMain.handle('quit', async () => { |
|
||||
app.quit() |
|
||||
}) |
|
||||
|
|
||||
// event for minimizing the app
|
|
||||
ipcMain.handle('minimize', async () => { |
|
||||
if (mainWindow) { |
|
||||
mainWindow.minimize() |
|
||||
} |
|
||||
}) |
|
||||
|
|
||||
ipcMain.handle('login', async (event, credentials) => { |
|
||||
const { username, password } = credentials |
|
||||
|
|
||||
// Pass username and password to loginUsers
|
|
||||
let response = await loginUsers(username, password) |
|
||||
|
|
||||
return response |
|
||||
}) |
|
||||
|
|
||||
ipcMain.handle('insertUser', async (event, credentials) => { |
|
||||
const { username, email, password, roles } = credentials |
|
||||
|
|
||||
const users = await insertUser(username, email, password, roles) |
|
||||
|
|
||||
return users |
|
||||
}) |
|
||||
|
|
||||
ipcMain.handle('updateUsers', async (event, credentials) => { |
|
||||
const { username, email, password, id } = credentials |
|
||||
|
|
||||
const update = await updateUser(username, email, password, id) |
|
||||
|
|
||||
return update |
|
||||
}) |
|
||||
|
|
||||
// event for insert etudiants
|
|
||||
ipcMain.handle('insertEtudiant', async (event, credentials) => { |
|
||||
const { |
|
||||
nom, |
|
||||
prenom, |
|
||||
photos, |
|
||||
date_de_naissances, |
|
||||
niveau, |
|
||||
annee_scolaire, |
|
||||
status, |
|
||||
num_inscription, |
|
||||
mention_id, |
|
||||
sexe, |
|
||||
nationaliter, |
|
||||
cin, |
|
||||
date_delivrence, |
|
||||
annee_bacc, |
|
||||
serie, |
|
||||
boursier, |
|
||||
domaine, |
|
||||
contact, |
|
||||
parcours |
|
||||
} = credentials |
|
||||
|
|
||||
const insert = await insertEtudiant( |
|
||||
nom, |
|
||||
prenom, |
|
||||
photos, |
|
||||
date_de_naissances, |
|
||||
niveau, |
|
||||
annee_scolaire, |
|
||||
status, |
|
||||
num_inscription, |
|
||||
mention_id, |
|
||||
sexe, |
|
||||
nationaliter, |
|
||||
cin, |
|
||||
date_delivrence, |
|
||||
annee_bacc, |
|
||||
serie, |
|
||||
boursier, |
|
||||
domaine, |
|
||||
contact, |
|
||||
parcours |
|
||||
) |
|
||||
|
|
||||
return insert |
|
||||
}) |
|
||||
|
|
||||
// event for fetching single
|
|
||||
ipcMain.handle('getByNiveau', async (event, credentials) => { |
|
||||
const { niveau } = credentials |
|
||||
|
|
||||
const getSingle = await FilterDataByNiveau(niveau) |
|
||||
|
|
||||
return getSingle |
|
||||
}) |
|
||||
|
|
||||
// event for fetching single
|
|
||||
ipcMain.handle('single', async (event, credentials) => { |
|
||||
const { id } = credentials |
|
||||
|
|
||||
const getSingle = await getSingleEtudiant(id) |
|
||||
|
|
||||
return getSingle |
|
||||
}) |
|
||||
|
|
||||
// event for inserting niveau
|
|
||||
ipcMain.handle('insertNiveau', async (event, credentials) => { |
|
||||
const { nom } = credentials |
|
||||
|
|
||||
const insert = await insertNiveau(nom) |
|
||||
|
|
||||
return insert |
|
||||
}) |
|
||||
|
|
||||
// event for updating etudiants
|
|
||||
ipcMain.handle('updateETudiants', async (event, credentials) => { |
|
||||
const { |
|
||||
nom, |
|
||||
prenom, |
|
||||
photos, |
|
||||
date_de_naissances, |
|
||||
niveau, |
|
||||
annee_scolaire, |
|
||||
status, |
|
||||
mention_id, |
|
||||
num_inscription, |
|
||||
id, |
|
||||
sexe, |
|
||||
nationalite, |
|
||||
cin, |
|
||||
date_delivrance, |
|
||||
annee_bacc, |
|
||||
serie, |
|
||||
boursier, |
|
||||
domaine, |
|
||||
contact, |
|
||||
parcours |
|
||||
} = credentials |
|
||||
|
|
||||
const updating = await updateEtudiant( |
|
||||
nom, |
|
||||
prenom, |
|
||||
photos, |
|
||||
date_de_naissances, |
|
||||
niveau, |
|
||||
annee_scolaire, |
|
||||
status, |
|
||||
mention_id, |
|
||||
num_inscription, |
|
||||
id, |
|
||||
sexe, |
|
||||
nationalite, |
|
||||
cin, |
|
||||
date_delivrance, |
|
||||
annee_bacc, |
|
||||
serie, |
|
||||
boursier, |
|
||||
domaine, |
|
||||
contact, |
|
||||
parcours |
|
||||
) |
|
||||
|
|
||||
return updating |
|
||||
}) |
|
||||
|
|
||||
// event for updating etudiants pdp
|
|
||||
ipcMain.handle('updateETudiantsPDP', async (event, credentials) => { |
|
||||
const { pdp, id } = credentials |
|
||||
|
|
||||
const updating = await changePDP(pdp, id) |
|
||||
|
|
||||
return updating |
|
||||
}) |
|
||||
|
|
||||
// event for adding notes
|
|
||||
ipcMain.handle('insertNote', async (event, credentials) => { |
|
||||
const { etudiant_id, etudiant_niveau, mention_id, formData, annee_scolaire } = credentials |
|
||||
|
|
||||
const insert = await insertNote( |
|
||||
etudiant_id, |
|
||||
etudiant_niveau, |
|
||||
mention_id, |
|
||||
formData, |
|
||||
annee_scolaire |
|
||||
) |
|
||||
|
|
||||
return insert |
|
||||
}) |
|
||||
|
|
||||
// event for get single note
|
|
||||
ipcMain.handle('getSingleNote', async (event, credentials) => { |
|
||||
const { id, niveau, mention_id } = credentials |
|
||||
|
|
||||
const get = await getNote(id, niveau, mention_id) |
|
||||
|
|
||||
return get |
|
||||
}) |
|
||||
|
|
||||
// event for get single note repech
|
|
||||
ipcMain.handle('getNotesRepech', async (event, credentials) => { |
|
||||
const { id, niveau, mention_id } = credentials |
|
||||
|
|
||||
const get = await getNoteRepech(id, niveau, mention_id) |
|
||||
|
|
||||
return get |
|
||||
}) |
|
||||
|
|
||||
// event for updating note
|
|
||||
ipcMain.handle('updatetNote', async (event, credentials) => { |
|
||||
const { formData, niveau, id, mention_id, annee_scolaire } = credentials |
|
||||
|
|
||||
const update = await updateNote(formData, niveau, id, mention_id, annee_scolaire) |
|
||||
|
|
||||
return update |
|
||||
}) |
|
||||
|
|
||||
// event for updating note repech
|
|
||||
ipcMain.handle('updatetNoteRepech', async (event, credentials) => { |
|
||||
const { formData2, niveau, id } = credentials |
|
||||
|
|
||||
const update = await updateNoteRepech(formData2, niveau, id) |
|
||||
|
|
||||
return update |
|
||||
}) |
|
||||
|
|
||||
// event to get single matiere
|
|
||||
ipcMain.handle('getMatiereByID', async (event, credentials) => { |
|
||||
const { id } = credentials |
|
||||
|
|
||||
const matiere = await getSingleMatiere(id) |
|
||||
|
|
||||
return matiere |
|
||||
}) |
|
||||
|
|
||||
// // event for updating matiere
|
|
||||
ipcMain.handle('updateMatiere', async (event, credentials) => { |
|
||||
const { nom, credit, uniter, ue, id } = credentials |
|
||||
|
|
||||
const update = await updateMatiere(nom, id, credit, uniter, ue) |
|
||||
|
|
||||
return update |
|
||||
}) |
|
||||
// event for importExcel
|
|
||||
ipcMain.handle('importexcel', async (event, credentials) => { |
|
||||
const files = credentials |
|
||||
console.log(files) |
|
||||
const insert = await importFileToDatabase(files) |
|
||||
|
|
||||
return insert |
|
||||
}) |
|
||||
|
|
||||
// event for udatign a single niveau
|
|
||||
ipcMain.handle('updateSingleNiveau', async (event, credentials) => { |
|
||||
const { nom, id } = credentials |
|
||||
|
|
||||
const update = updateNiveau(nom, id) |
|
||||
|
|
||||
return update |
|
||||
}) |
|
||||
|
|
||||
// event to get single niveau
|
|
||||
ipcMain.handle('singleNiveau', async (event, credentials) => { |
|
||||
const { id } = credentials |
|
||||
|
|
||||
const update = getSingleNiveau(id) |
|
||||
|
|
||||
return update |
|
||||
}) |
|
||||
|
|
||||
// event for creating matiere
|
|
||||
ipcMain.handle('createMatiere', async (event, credentials) => { |
|
||||
const { nom, credit, uniter, ue } = credentials |
|
||||
|
|
||||
const create = createMatiere(nom, credit, uniter, ue) |
|
||||
|
|
||||
return create |
|
||||
}) |
|
||||
|
|
||||
// // event for import excel matiere
|
|
||||
ipcMain.handle('importExcelMatiere', async (event, credentials) => { |
|
||||
const files = credentials |
|
||||
console.log(files) |
|
||||
const insert = await importFileToDatabaseMatiere(files) |
|
||||
|
|
||||
return insert |
|
||||
}) |
|
||||
|
|
||||
// event for import excel niveau
|
|
||||
ipcMain.handle('importNiveau', async (event, credentials) => { |
|
||||
const files = credentials |
|
||||
console.log(files) |
|
||||
const insert = await importNiveau(files) |
|
||||
|
|
||||
return insert |
|
||||
}) |
|
||||
|
|
||||
// event for updating note systeme
|
|
||||
ipcMain.handle('updateNoteSysteme', async (event, credentials) => { |
|
||||
const { id, admis, redouble, renvoyer } = credentials |
|
||||
|
|
||||
const update = updateSysteme(id, admis, redouble, renvoyer) |
|
||||
return update |
|
||||
}) |
|
||||
|
|
||||
// event for updating note systeme
|
|
||||
ipcMain.handle('createAnneeScolaire', async (event, credentials) => { |
|
||||
const { code, debut, fin } = credentials |
|
||||
|
|
||||
const create = createAnneeScolaire(code, debut, fin) |
|
||||
return create |
|
||||
}) |
|
||||
|
|
||||
ipcMain.handle('getMoyene', async (event, credentials) => { |
|
||||
const { niveau, scolaire } = credentials |
|
||||
console.log('index.js', niveau, scolaire) |
|
||||
|
|
||||
const create = showMoyen(niveau, scolaire) |
|
||||
return create |
|
||||
}) |
|
||||
|
|
||||
ipcMain.handle('getMoyenneRepech', async (event, credentials) => { |
|
||||
const { niveau, scolaire } = credentials |
|
||||
console.log('index.js', niveau, scolaire) |
|
||||
|
|
||||
const create = showMoyenRepech(niveau, scolaire) |
|
||||
return create |
|
||||
}) |
|
||||
|
|
||||
ipcMain.handle('noteMatiere', async (event, credentials) => { |
|
||||
const { id, niveau, annee_scolaire } = credentials |
|
||||
|
|
||||
const get = getMatiereAndNote(id, niveau, annee_scolaire) |
|
||||
return get |
|
||||
}) |
|
||||
|
|
||||
ipcMain.handle('displayMatiereFromForm', async (event, credentials) => { |
|
||||
const { niveau, mention_id, parcours } = credentials |
|
||||
|
|
||||
const get = displayMatiereFromForm(niveau, mention_id, parcours) |
|
||||
return get |
|
||||
}) |
|
||||
|
|
||||
ipcMain.handle('createMention', async (event, credentials) => { |
|
||||
const { nom, uniter } = credentials |
|
||||
|
|
||||
const get = createMention(nom, uniter) |
|
||||
return get |
|
||||
}) |
|
||||
|
|
||||
ipcMain.handle('getSingleMention', async (event, credentials) => { |
|
||||
const { id } = credentials |
|
||||
|
|
||||
const get = getSingleMention(id) |
|
||||
return get |
|
||||
}) |
|
||||
|
|
||||
ipcMain.handle('updateMention', async (event, credentials) => { |
|
||||
const { nom, uniter, id } = credentials |
|
||||
|
|
||||
const get = updateMention(nom, uniter, id) |
|
||||
return get |
|
||||
}) |
|
||||
|
|
||||
ipcMain.handle('deleteMention', async (event, credentials) => { |
|
||||
const { id } = credentials |
|
||||
|
|
||||
const get = deleteMention(id) |
|
||||
return get |
|
||||
}) |
|
||||
|
|
||||
ipcMain.handle('deleteNiveaus', async (event, credentials) => { |
|
||||
const { id } = credentials |
|
||||
|
|
||||
const get = deleteNiveau(id) |
|
||||
return get |
|
||||
}) |
|
||||
|
|
||||
ipcMain.handle('deleteMatiere', async (event, credentials) => { |
|
||||
const { id } = credentials |
|
||||
|
|
||||
const get = deleteMatiere(id) |
|
||||
return get |
|
||||
}) |
|
||||
|
|
||||
ipcMain.handle('asign', async (event, credentials) => { |
|
||||
const { formData, id } = credentials |
|
||||
// console.log(formData, id);
|
|
||||
const get = asygnationToMention(formData, id) |
|
||||
|
|
||||
return get |
|
||||
}) |
|
||||
|
|
||||
ipcMain.handle('asignSemestre', async (event, credentials) => { |
|
||||
const { id } = credentials |
|
||||
|
|
||||
const get = getMentionMatiereChecked(id) |
|
||||
|
|
||||
return get |
|
||||
}) |
|
||||
|
|
||||
ipcMain.handle('getAsign', async (event, credentials) => { |
|
||||
const { id } = credentials |
|
||||
// console.log(formData, id);
|
|
||||
const get = getMentionMatiere(id) |
|
||||
|
|
||||
return get |
|
||||
}) |
|
||||
|
|
||||
ipcMain.handle('deleteAnneeScolaire', async (event, credentials) => { |
|
||||
const { id } = credentials |
|
||||
// console.log(formData, id);
|
|
||||
const get = deleteAnneeScolaire(id) |
|
||||
|
|
||||
return get |
|
||||
}) |
|
||||
|
|
||||
ipcMain.handle('getSemestreMatiere', async (event, credentials) => { |
|
||||
const { id } = credentials |
|
||||
// console.log(formData, id);
|
|
||||
const get = getSemestreMatiere(id) |
|
||||
|
|
||||
return get |
|
||||
}) |
|
||||
|
|
||||
ipcMain.handle('insertUpdateMentionSemestre', async (event, credentials) => { |
|
||||
const { id, selectedSemestres } = credentials |
|
||||
// console.log(formData, id);
|
|
||||
const get = insertUpdateMentionSemestre(id, selectedSemestres) |
|
||||
|
|
||||
return get |
|
||||
}) |
|
||||
|
|
||||
ipcMain.handle('getSingleAnneeScolaire', async (event, credentials) => { |
|
||||
const { id } = credentials |
|
||||
// console.log(formData, id);
|
|
||||
const get = getSingleAnneScolaire(id) |
|
||||
|
|
||||
return get |
|
||||
}) |
|
||||
|
|
||||
ipcMain.handle('updateAnneeScolaire', async (event, credentials) => { |
|
||||
const { code, debut, fin, id } = credentials |
|
||||
// console.log(formData, id);
|
|
||||
const get = updateAnneeScolaire(id, code, debut, fin) |
|
||||
|
|
||||
return get |
|
||||
}) |
|
||||
|
|
||||
ipcMain.handle('setCurrent', async (event, credentials) => { |
|
||||
const { id } = credentials |
|
||||
// console.log(formData, id);
|
|
||||
const get = setCurrent(id) |
|
||||
|
|
||||
return get |
|
||||
}) |
|
||||
|
|
||||
ipcMain.handle('noteRelerer', async (event, credentials) => { |
|
||||
const { id, anneescolaire, niveau } = credentials |
|
||||
// console.log(formData, id);
|
|
||||
const get = getNotesWithRepechToDisplay(id, anneescolaire, niveau) |
|
||||
|
|
||||
return get |
|
||||
}) |
|
||||
|
|
||||
ipcMain.handle('updateNessesary', async (event, credentials) => { |
|
||||
const { id, multiplicateur } = credentials |
|
||||
// console.log(formData, id);
|
|
||||
const get = updateNessesaryTable(id, multiplicateur) |
|
||||
|
|
||||
return get |
|
||||
}) |
|
||||
|
|
||||
ipcMain.handle('insertProf', async (event, credentials) => { |
|
||||
const { nom_enseignant, prenom_enseignant, contact, date, matiere_id } = credentials |
|
||||
// console.log(formData, id);
|
|
||||
const get = insertNewProf(matiere_id, nom_enseignant, prenom_enseignant, contact, date) |
|
||||
|
|
||||
return get |
|
||||
}) |
|
||||
|
|
||||
ipcMain.handle('insertParcours', async (event, credentials) => { |
|
||||
const { nom, uniter, mention_id } = credentials |
|
||||
// console.log(formData, id);
|
|
||||
const get = insertParcour(nom, uniter, mention_id) |
|
||||
|
|
||||
return get |
|
||||
}) |
|
||||
|
|
||||
ipcMain.handle('getSingleParcours', async (event, credentials) => { |
|
||||
const { id } = credentials |
|
||||
// console.log(formData, id);
|
|
||||
const get = getSingleParcours(id) |
|
||||
|
|
||||
return get |
|
||||
}) |
|
||||
|
|
||||
ipcMain.handle('deleteParcours', async (event, credentials) => { |
|
||||
const { id } = credentials |
|
||||
// console.log(formData, id);
|
|
||||
const get = deletes(id) |
|
||||
|
|
||||
return get |
|
||||
}) |
|
||||
|
|
||||
ipcMain.handle('updateParcours', async (event, credentials) => { |
|
||||
const { nom, uniter, mention_id, id } = credentials |
|
||||
// console.log(formData, id);
|
|
||||
const get = updateparcour(id, nom, uniter, mention_id) |
|
||||
|
|
||||
return get |
|
||||
}) |
|
||||
|
|
||||
ipcMain.handle('parcourMatiere', async (event, credentials) => { |
|
||||
const { matiere_id, parcour_id } = credentials |
|
||||
// console.log(formData, id);
|
|
||||
const get = parcourMatiere(matiere_id, parcour_id) |
|
||||
|
|
||||
return get |
|
||||
}) |
|
||||
|
|
||||
ipcMain.handle('getSingleProf', async (event, credentials) => { |
|
||||
const { id } = credentials |
|
||||
// console.log(formData, id);
|
|
||||
const get = getSIngleProf(id) |
|
||||
|
|
||||
return get |
|
||||
}) |
|
||||
|
|
||||
ipcMain.handle('updateProf', async (event, credentials) => { |
|
||||
const { nom_enseignant, prenom_enseignant, contact, date, matiere_id } = credentials |
|
||||
// console.log(formData, id);
|
|
||||
const get = updateProf(matiere_id, nom_enseignant, prenom_enseignant, contact, date) |
|
||||
|
|
||||
return get |
|
||||
}) |
|
||||
|
|
||||
ipcMain.handle('extractFiches', async (event, credentials) => { |
|
||||
const { matiere_id } = credentials |
|
||||
// console.log(formData, id);
|
|
||||
const get = extractFiche(matiere_id) |
|
||||
|
|
||||
return get |
|
||||
}) |
|
||||
|
|
||||
ipcMain.handle('getParcourMatiere', async (event, credentials) => { |
|
||||
const { matiere_id } = credentials |
|
||||
// console.log(formData, id);
|
|
||||
const get = getParcourMatiere(matiere_id) |
|
||||
|
|
||||
return get |
|
||||
}) |
|
||||
|
|
||||
ipcMain.handle('changeParcours', async (event, credentials) => { |
|
||||
const { parcours, user_id } = credentials |
|
||||
// console.log(formData, id);
|
|
||||
const get = updateParcours(parcours, user_id) |
|
||||
|
|
||||
return get |
|
||||
}) |
|
||||
|
|
||||
ipcMain.handle('createTranche', async (event, credentials) => { |
|
||||
const { etudiant_id, tranchename, montant } = credentials |
|
||||
// console.log(formData, id);
|
|
||||
const get = createTranche(etudiant_id, tranchename, montant) |
|
||||
|
|
||||
return get |
|
||||
}) |
|
||||
|
|
||||
ipcMain.handle('getTranche', async (event, credentials) => { |
|
||||
const { id } = credentials |
|
||||
// console.log(formData, id);
|
|
||||
const get = getTranche(id) |
|
||||
|
|
||||
return get |
|
||||
}) |
|
||||
|
|
||||
ipcMain.handle('updateTranche', async (event, credentials) => { |
|
||||
const { id, tranchename, montant } = credentials |
|
||||
// console.log(formData, id);
|
|
||||
const get = updateTranche(id, tranchename, montant) |
|
||||
|
|
||||
return get |
|
||||
}) |
|
||||
|
|
||||
ipcMain.handle('deleteTranche', async (event, credentials) => { |
|
||||
const { id } = credentials |
|
||||
console.log(id) |
|
||||
const get = deleteTranche(id) |
|
||||
|
|
||||
return get |
|
||||
}) |
|
||||
|
|
||||
ipcMain.handle('getSingleTranche', async (event, credentials) => { |
|
||||
const { id } = credentials |
|
||||
// console.log(formData, id);
|
|
||||
const get = getSingleTranche(id) |
|
||||
|
|
||||
return get |
|
||||
}) |
|
||||
|
|
||||
ipcMain.handle('createIPConfig', async (event, credentials) => { |
|
||||
const { ipname } = credentials |
|
||||
// console.log(formData, id);
|
|
||||
const get = createConfigIp(ipname) |
|
||||
|
|
||||
return get |
|
||||
}) |
|
||||
|
|
||||
ipcMain.handle('updateIPConfig', async (event, credentials) => { |
|
||||
const { id, ipname } = credentials |
|
||||
// console.log(formData, id);
|
|
||||
const get = updateIPConfig(id, ipname) |
|
||||
|
|
||||
return get |
|
||||
}) |
|
||||
@ -1,135 +0,0 @@ |
|||||
import { app, shell, BrowserWindow, ipcMain } from 'electron' |
|
||||
import { join } from 'path' |
|
||||
import { electronApp, optimizer, is } from '@electron-toolkit/utils' |
|
||||
import icon from '../../resources/icon.png?asset' |
|
||||
const { loginUser, forgotPassword } = require('../../database/Models/Users') |
|
||||
|
|
||||
let splashWindow |
|
||||
let mainWindow |
|
||||
|
|
||||
// Create splash window
|
|
||||
function createSplashScreen() { |
|
||||
splashWindow = new BrowserWindow({ |
|
||||
width: 400, |
|
||||
height: 300, |
|
||||
frame: false, |
|
||||
alwaysOnTop: true, |
|
||||
transparent: true, |
|
||||
resizable: false, |
|
||||
webPreferences: { |
|
||||
nodeIntegration: true, |
|
||||
contextIsolation: false |
|
||||
} |
|
||||
}) |
|
||||
|
|
||||
splashWindow.loadFile(join(__dirname, '../renderer/splash.html')) |
|
||||
|
|
||||
splashWindow.on('closed', () => { |
|
||||
splashWindow = null |
|
||||
}) |
|
||||
} |
|
||||
|
|
||||
// Create main application window
|
|
||||
function createWindow() { |
|
||||
mainWindow = new BrowserWindow({ |
|
||||
width: 1000, |
|
||||
minWidth: 1000, |
|
||||
height: 670, |
|
||||
minHeight: 670, |
|
||||
show: false, |
|
||||
autoHideMenuBar: true, |
|
||||
fullscreen: true, |
|
||||
...(process.platform === 'linux' ? { icon } : {}), |
|
||||
webPreferences: { |
|
||||
preload: join(__dirname, '../preload/index.js'), |
|
||||
nodeIntegration: true, |
|
||||
contextIsolation: true, |
|
||||
sandbox: false |
|
||||
} |
|
||||
}) |
|
||||
|
|
||||
mainWindow.on('ready-to-show', () => { |
|
||||
if (splashWindow) { |
|
||||
splashWindow.close() // Close splash screen when main window is ready
|
|
||||
} |
|
||||
mainWindow.show() |
|
||||
}) |
|
||||
|
|
||||
mainWindow.webContents.setWindowOpenHandler((details) => { |
|
||||
shell.openExternal(details.url) |
|
||||
return { action: 'deny' } |
|
||||
}) |
|
||||
|
|
||||
// Load the initial content
|
|
||||
if (is.dev && process.env['ELECTRON_RENDERER_URL']) { |
|
||||
mainWindow.loadURL(process.env['ELECTRON_RENDERER_URL']) |
|
||||
} else { |
|
||||
mainWindow.loadFile(join(__dirname, '../renderer/index.html')) |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
// Function to load new content and show the splash screen
|
|
||||
function loadNewContent(contentPath) { |
|
||||
createSplashScreen() // Show splash screen before loading new content
|
|
||||
|
|
||||
mainWindow.loadFile(contentPath).then(() => { |
|
||||
if (splashWindow) { |
|
||||
splashWindow.close() // Close splash screen after loading content
|
|
||||
} |
|
||||
mainWindow.show() // Show main window
|
|
||||
}) |
|
||||
} |
|
||||
|
|
||||
// This method will be called when Electron has finished initialization
|
|
||||
app.whenReady().then(() => { |
|
||||
// Set app user model id for Windows
|
|
||||
electronApp.setAppUserModelId('com.electron') |
|
||||
|
|
||||
// Default open or close DevTools by F12 in development
|
|
||||
app.on('browser-window-created', (_, window) => { |
|
||||
optimizer.watchWindowShortcuts(window) |
|
||||
}) |
|
||||
|
|
||||
// Create initial main window
|
|
||||
createWindow() |
|
||||
|
|
||||
// IPC for loading new content
|
|
||||
ipcMain.on('load-content', (event, contentPath) => { |
|
||||
loadNewContent(contentPath) |
|
||||
}) |
|
||||
|
|
||||
app.on('activate', function () { |
|
||||
if (BrowserWindow.getAllWindows().length === 0) createWindow() |
|
||||
}) |
|
||||
}) |
|
||||
|
|
||||
// Quit when all windows are closed, except on macOS
|
|
||||
app.on('window-all-closed', () => { |
|
||||
if (process.platform !== 'darwin') { |
|
||||
app.quit() |
|
||||
} |
|
||||
}) |
|
||||
|
|
||||
// Event for handling login
|
|
||||
ipcMain.handle('login', async (event, credentials) => { |
|
||||
const { username, password } = credentials |
|
||||
|
|
||||
const users = await loginUser(username, password) |
|
||||
|
|
||||
if (users) { |
|
||||
return { success: true, user: users } |
|
||||
} else { |
|
||||
return { success: false } |
|
||||
} |
|
||||
}) |
|
||||
|
|
||||
// Event for handling forgot password
|
|
||||
ipcMain.handle('forgotPassword', async (event, credentials) => { |
|
||||
const { email, password, passwordConfirmation } = credentials |
|
||||
|
|
||||
const updated = await forgotPassword(email, password, passwordConfirmation) |
|
||||
|
|
||||
if (updated) { |
|
||||
return updated |
|
||||
} |
|
||||
}) |
|
||||
@ -1,210 +0,0 @@ |
|||||
import { contextBridge, ipcRenderer } from 'electron' |
|
||||
import { electronAPI } from '@electron-toolkit/preload' |
|
||||
const { getNessesarytable } = require('../../database/function/System') |
|
||||
const { getNiveau } = require('../../database/Models/Niveau') |
|
||||
const { getAllUsers } = require('../../database/Models/Users') |
|
||||
const { getAllEtudiants, getDataToDashboard } = require('../../database/Models/Etudiants') |
|
||||
const { verifyEtudiantIfHeHasNotes, blockShowMoyene } = require('../../database/Models/Notes') |
|
||||
|
|
||||
const { synchronizeData } = require('../../database/api/SyncronisationDataUsers') |
|
||||
const { synchronizeDataEtudiants } = require('../../database/api/SyncronisationDataEtudiants') |
|
||||
const { synchronizeDataNotes } = require('../../database/api/CheckUpdateNote') |
|
||||
const { getMatiere, getSemestre, getEnseignants } = require('../../database/Models/Matieres') |
|
||||
const { getSysteme } = require('../../database/Models/NoteSysrem') |
|
||||
const { getStatus } = require('../../database/Models/Status') |
|
||||
const { getAnneeScolaire, getInterval } = require('../../database/Models/AnneeScolaire') |
|
||||
const { getMentions } = require('../../database/Models/Mentions') |
|
||||
const { getAll } = require('../../database/api/Get') |
|
||||
const { getParcours } = require('../../database/Models/Parcours') |
|
||||
const { getIPConfig } = require('../../database/Models/IpConfig') |
|
||||
|
|
||||
// Custom APIs for renderer
|
|
||||
const api = {} |
|
||||
|
|
||||
// Use `contextBridge` APIs to expose Electron APIs to
|
|
||||
// renderer only if context isolation is enabled, otherwise
|
|
||||
// just add to the DOM global.
|
|
||||
if (process.contextIsolated) { |
|
||||
try { |
|
||||
contextBridge.exposeInMainWorld('electron', electronAPI) |
|
||||
contextBridge.exposeInMainWorld('api', api) |
|
||||
|
|
||||
/** |
|
||||
* contextBridge for Tray |
|
||||
*/ |
|
||||
contextBridge.exposeInMainWorld('Tray', { |
|
||||
onNavigate: (callback) => { |
|
||||
ipcRenderer.on('navigateToRoute', (event, route) => { |
|
||||
callback(route) // Pass the route to the renderer callback
|
|
||||
}) |
|
||||
} |
|
||||
}) |
|
||||
|
|
||||
/** |
|
||||
* contextBridge for users |
|
||||
*/ |
|
||||
contextBridge.exposeInMainWorld('allUser', { |
|
||||
users: () => getAllUsers(), |
|
||||
login: (credentials) => ipcRenderer.invoke('login', credentials), |
|
||||
insertUsers: (credentials) => ipcRenderer.invoke('insertUser', credentials), |
|
||||
forgotPassword: (credentials) => ipcRenderer.invoke('forgotPassword', credentials), |
|
||||
quit: () => ipcRenderer.invoke('quit'), |
|
||||
minimize: () => ipcRenderer.invoke('minimize'), |
|
||||
updateUsers: (credentials) => ipcRenderer.invoke('updateUsers', credentials) |
|
||||
}) |
|
||||
|
|
||||
contextBridge.exposeInMainWorld('syncro', { |
|
||||
getall: () => getAll() |
|
||||
}) |
|
||||
|
|
||||
// syncronisation des donner
|
|
||||
window.addEventListener('online', async () => { |
|
||||
if (navigator.onLine) { |
|
||||
// synchronizeData()
|
|
||||
// synchronizeDataEtudiants()
|
|
||||
// synchronizeDataNotes()
|
|
||||
await getAll() |
|
||||
} |
|
||||
}) |
|
||||
// send data
|
|
||||
getAll() |
|
||||
|
|
||||
/** |
|
||||
* contextBridge for etudiants |
|
||||
*/ |
|
||||
contextBridge.exposeInMainWorld('etudiants', { |
|
||||
insertEtudiant: (credentials) => ipcRenderer.invoke('insertEtudiant', credentials), |
|
||||
getEtudiants: () => getAllEtudiants(), |
|
||||
FilterDataByNiveau: (credential) => ipcRenderer.invoke('getByNiveau', credential), |
|
||||
getSingle: (credential) => ipcRenderer.invoke('single', credential), |
|
||||
updateEtudiants: (credentials) => ipcRenderer.invoke('updateETudiants', credentials), |
|
||||
getDataToDashboards: () => getDataToDashboard(), |
|
||||
updateEtudiantsPDP: (credentials) => ipcRenderer.invoke('updateETudiantsPDP', credentials), |
|
||||
importExcel: (credentials) => ipcRenderer.invoke('importexcel', credentials), |
|
||||
changeParcours: (credentials) => ipcRenderer.invoke('changeParcours', credentials), |
|
||||
createTranche: (credentials) => ipcRenderer.invoke('createTranche', credentials), |
|
||||
getTranche: (credentials) => ipcRenderer.invoke('getTranche', credentials), |
|
||||
updateTranche: (credentials) => ipcRenderer.invoke('updateTranche', credentials), |
|
||||
deleteTranche: (credentials) => ipcRenderer.invoke('deleteTranche', credentials), |
|
||||
getSingleTranche: (credentials) => ipcRenderer.invoke('getSingleTranche', credentials) |
|
||||
}) |
|
||||
|
|
||||
/** |
|
||||
* cobtextBridge for niveaus |
|
||||
*/ |
|
||||
contextBridge.exposeInMainWorld('niveaus', { |
|
||||
getNiveau: () => getNiveau(), |
|
||||
getSingleNiveau: (credential) => ipcRenderer.invoke('singleNiveau', credential), |
|
||||
insertNiveau: (credentials) => ipcRenderer.invoke('insertNiveau', credentials), |
|
||||
updateSingleNiveau: (credentials) => ipcRenderer.invoke('updateSingleNiveau', credentials), |
|
||||
importNiveau: (credentials) => ipcRenderer.invoke('importNiveau', credentials), |
|
||||
deleteNiveaus: (credentials) => ipcRenderer.invoke('deleteNiveaus', credentials) |
|
||||
}) |
|
||||
|
|
||||
/** |
|
||||
* contextBridge for notes |
|
||||
*/ |
|
||||
contextBridge.exposeInMainWorld('notes', { |
|
||||
getNotes: (credentials) => ipcRenderer.invoke('getSingleNote', credentials), |
|
||||
insertNote: (credentials) => ipcRenderer.invoke('insertNote', credentials), |
|
||||
updateNote: (credentials) => ipcRenderer.invoke('updatetNote', credentials), |
|
||||
getMoyenne: (credentials) => ipcRenderer.invoke('getMoyene', credentials), |
|
||||
noteMatiere: (credentials) => ipcRenderer.invoke('noteMatiere', credentials), |
|
||||
noteRelerer: (credentials) => ipcRenderer.invoke('noteRelerer', credentials), |
|
||||
getMoyenneVerify: () => verifyEtudiantIfHeHasNotes(), |
|
||||
getblockNote: () => blockShowMoyene() |
|
||||
}) |
|
||||
|
|
||||
/** |
|
||||
* contextbridge for note repechage |
|
||||
*/ |
|
||||
contextBridge.exposeInMainWorld('noteRepech', { |
|
||||
getNotesRepech: (credentials) => ipcRenderer.invoke('getNotesRepech', credentials), |
|
||||
updateNoteRepech: (credentials) => ipcRenderer.invoke('updatetNoteRepech', credentials), |
|
||||
getMoyenneRepech: (credentials) => ipcRenderer.invoke('getMoyenneRepech', credentials) |
|
||||
}) |
|
||||
|
|
||||
/** |
|
||||
* contextBridge for matieres |
|
||||
*/ |
|
||||
contextBridge.exposeInMainWorld('matieres', { |
|
||||
getMatiere: () => getMatiere(), |
|
||||
createMatiere: (credentials) => ipcRenderer.invoke('createMatiere', credentials), |
|
||||
getMatiereByID: (credentials) => ipcRenderer.invoke('getMatiereByID', credentials), |
|
||||
updateMatiere: (credentials) => ipcRenderer.invoke('updateMatiere', credentials), |
|
||||
importExcel: (credentials) => ipcRenderer.invoke('importExcelMatiere', credentials), |
|
||||
displayMatiereFromForm: (credentials) => |
|
||||
ipcRenderer.invoke('displayMatiereFromForm', credentials), |
|
||||
deleteMatiere: (credentials) => ipcRenderer.invoke('deleteMatiere', credentials), |
|
||||
asign: (credentials) => ipcRenderer.invoke('asign', credentials), |
|
||||
getAsign: (credentials) => ipcRenderer.invoke('getAsign', credentials), |
|
||||
asignSemestre: (credentials) => ipcRenderer.invoke('asignSemestre', credentials), |
|
||||
getSemestreMatiere: (credentials) => ipcRenderer.invoke('getSemestreMatiere', credentials), |
|
||||
getSemestre: () => getSemestre(), |
|
||||
getNessesary: () => getNessesarytable(), |
|
||||
getENseignant: () => getEnseignants(), |
|
||||
insertUpdateMentionSemestre: (credentials) => |
|
||||
ipcRenderer.invoke('insertUpdateMentionSemestre', credentials), |
|
||||
updateNessesary: (credentials) => ipcRenderer.invoke('updateNessesary', credentials), |
|
||||
insertProf: (credentials) => ipcRenderer.invoke('insertProf', credentials), |
|
||||
getSingleProf: (credentials) => ipcRenderer.invoke('getSingleProf', credentials), |
|
||||
updateProf: (credentials) => ipcRenderer.invoke('updateProf', credentials) |
|
||||
}) |
|
||||
|
|
||||
/** |
|
||||
* contextBridge for note systeme |
|
||||
*/ |
|
||||
contextBridge.exposeInMainWorld('notesysteme', { |
|
||||
getSyteme: () => getSysteme(), |
|
||||
updateNoteSysteme: (credentials) => ipcRenderer.invoke('updateNoteSysteme', credentials), |
|
||||
insertParcours: (credentials) => ipcRenderer.invoke('insertParcours', credentials), |
|
||||
getSingleParcours: (credentials) => ipcRenderer.invoke('getSingleParcours', credentials), |
|
||||
deleteParcours: (credentials) => ipcRenderer.invoke('deleteParcours', credentials), |
|
||||
updateParcours: (credentials) => ipcRenderer.invoke('updateParcours', credentials), |
|
||||
parcourMatiere: (credentials) => ipcRenderer.invoke('parcourMatiere', credentials), |
|
||||
getParcours: () => getParcours(), |
|
||||
extractFiches: (credentials) => ipcRenderer.invoke('extractFiches', credentials), |
|
||||
getParcourMatiere: (credentials) => ipcRenderer.invoke('getParcourMatiere', credentials), |
|
||||
createIPConfig: (credentials) => ipcRenderer.invoke('createIPConfig', credentials), |
|
||||
getIPConfig: () => getIPConfig(), |
|
||||
updateIPConfig: (credentials) => ipcRenderer.invoke('updateIPConfig', credentials) |
|
||||
}) |
|
||||
|
|
||||
/** |
|
||||
* contextbridge for status |
|
||||
*/ |
|
||||
contextBridge.exposeInMainWorld('statuss', { |
|
||||
getStatus: () => getStatus() |
|
||||
}) |
|
||||
|
|
||||
/** |
|
||||
* contextbridge for annee scolaire |
|
||||
*/ |
|
||||
contextBridge.exposeInMainWorld('anneescolaire', { |
|
||||
getAnneeScolaire: () => getAnneeScolaire(), |
|
||||
getInterval: () => getInterval(), |
|
||||
createAnneeScolaire: (credentials) => ipcRenderer.invoke('createAnneeScolaire', credentials), |
|
||||
deleteAnneeScolaire: (credentials) => ipcRenderer.invoke('deleteAnneeScolaire', credentials), |
|
||||
getSingleAnneeScolaire: (credentials) => |
|
||||
ipcRenderer.invoke('getSingleAnneeScolaire', credentials), |
|
||||
updateAnneeScolaire: (credentials) => ipcRenderer.invoke('updateAnneeScolaire', credentials), |
|
||||
setCurrent: (credentials) => ipcRenderer.invoke('setCurrent', credentials) |
|
||||
}) |
|
||||
|
|
||||
/** |
|
||||
* contextbridge for mention |
|
||||
*/ |
|
||||
contextBridge.exposeInMainWorld('mention', { |
|
||||
createMention: (credentials) => ipcRenderer.invoke('createMention', credentials), |
|
||||
getMention: () => getMentions(), |
|
||||
getSingleMention: (credentials) => ipcRenderer.invoke('getSingleMention', credentials), |
|
||||
updateMention: (credentials) => ipcRenderer.invoke('updateMention', credentials), |
|
||||
deleteMention: (credentials) => ipcRenderer.invoke('deleteMention', credentials) |
|
||||
}) |
|
||||
} catch (error) { |
|
||||
console.error(error) |
|
||||
} |
|
||||
} else { |
|
||||
window.electron = electronAPI |
|
||||
window.api = api |
|
||||
} |
|
||||
@ -1,206 +0,0 @@ |
|||||
import { contextBridge, ipcRenderer } from 'electron' |
|
||||
import { electronAPI } from '@electron-toolkit/preload' |
|
||||
const { getNessesarytable } = require('../../database/function/System') |
|
||||
const { getNiveau } = require('../../database/Models/Niveau') |
|
||||
const { getAllUsers } = require('../../database/Models/Users') |
|
||||
const { getAllEtudiants, getDataToDashboard } = require('../../database/Models/Etudiants') |
|
||||
const { verifyEtudiantIfHeHasNotes, blockShowMoyene } = require('../../database/Models/Notes') |
|
||||
const { getMatiere, getSemestre, getEnseignants } = require('../../database/Models/Matieres') |
|
||||
const { getSysteme } = require('../../database/Models/NoteSysrem') |
|
||||
const { getStatus } = require('../../database/Models/Status') |
|
||||
const { getAnneeScolaire, getInterval } = require('../../database/Models/AnneeScolaire') |
|
||||
const { getMentions } = require('../../database/Models/Mentions') |
|
||||
const { getAll } = require('../../database/api/Get') |
|
||||
const { getParcours } = require('../../database/Models/Parcours') |
|
||||
const { getIPConfig } = require('../../database/Models/IpConfig') |
|
||||
|
|
||||
// Custom APIs for renderer
|
|
||||
const api = {} |
|
||||
|
|
||||
// Use `contextBridge` APIs to expose Electron APIs to
|
|
||||
// renderer only if context isolation is enabled, otherwise
|
|
||||
// just add to the DOM global.
|
|
||||
if (process.contextIsolated) { |
|
||||
try { |
|
||||
contextBridge.exposeInMainWorld('electron', electronAPI) |
|
||||
contextBridge.exposeInMainWorld('api', api) |
|
||||
|
|
||||
/** |
|
||||
* contextBridge for Tray |
|
||||
*/ |
|
||||
contextBridge.exposeInMainWorld('Tray', { |
|
||||
onNavigate: (callback) => { |
|
||||
ipcRenderer.on('navigateToRoute', (event, route) => { |
|
||||
callback(route) // Pass the route to the renderer callback
|
|
||||
}) |
|
||||
} |
|
||||
}) |
|
||||
|
|
||||
/** |
|
||||
* contextBridge for users |
|
||||
*/ |
|
||||
contextBridge.exposeInMainWorld('allUser', { |
|
||||
users: () => getAllUsers(), |
|
||||
login: (credentials) => ipcRenderer.invoke('login', credentials), |
|
||||
insertUsers: (credentials) => ipcRenderer.invoke('insertUser', credentials), |
|
||||
forgotPassword: (credentials) => ipcRenderer.invoke('forgotPassword', credentials), |
|
||||
quit: () => ipcRenderer.invoke('quit'), |
|
||||
minimize: () => ipcRenderer.invoke('minimize'), |
|
||||
updateUsers: (credentials) => ipcRenderer.invoke('updateUsers', credentials) |
|
||||
}) |
|
||||
|
|
||||
contextBridge.exposeInMainWorld('syncro', { |
|
||||
getall: () => getAll() |
|
||||
}) |
|
||||
|
|
||||
// syncronisation des donner
|
|
||||
window.addEventListener('online', async () => { |
|
||||
if (navigator.onLine) { |
|
||||
// synchronizeData()
|
|
||||
// synchronizeDataEtudiants()
|
|
||||
// synchronizeDataNotes()
|
|
||||
await getAll() |
|
||||
} |
|
||||
}) |
|
||||
// send data
|
|
||||
getAll() |
|
||||
|
|
||||
/** |
|
||||
* contextBridge for etudiants |
|
||||
*/ |
|
||||
contextBridge.exposeInMainWorld('etudiants', { |
|
||||
insertEtudiant: (credentials) => ipcRenderer.invoke('insertEtudiant', credentials), |
|
||||
getEtudiants: () => getAllEtudiants(), |
|
||||
FilterDataByNiveau: (credential) => ipcRenderer.invoke('getByNiveau', credential), |
|
||||
getSingle: (credential) => ipcRenderer.invoke('single', credential), |
|
||||
updateEtudiants: (credentials) => ipcRenderer.invoke('updateETudiants', credentials), |
|
||||
getDataToDashboards: () => getDataToDashboard(), |
|
||||
updateEtudiantsPDP: (credentials) => ipcRenderer.invoke('updateETudiantsPDP', credentials), |
|
||||
importExcel: (credentials) => ipcRenderer.invoke('importexcel', credentials), |
|
||||
changeParcours: (credentials) => ipcRenderer.invoke('changeParcours', credentials), |
|
||||
createTranche: (credentials) => ipcRenderer.invoke('createTranche', credentials), |
|
||||
getTranche: (credentials) => ipcRenderer.invoke('getTranche', credentials), |
|
||||
updateTranche: (credentials) => ipcRenderer.invoke('updateTranche', credentials), |
|
||||
deleteTranche: (credentials) => ipcRenderer.invoke('deleteTranche', credentials), |
|
||||
getSingleTranche: (credentials) => ipcRenderer.invoke('getSingleTranche', credentials) |
|
||||
}) |
|
||||
|
|
||||
/** |
|
||||
* cobtextBridge for niveaus |
|
||||
*/ |
|
||||
contextBridge.exposeInMainWorld('niveaus', { |
|
||||
getNiveau: () => getNiveau(), |
|
||||
getSingleNiveau: (credential) => ipcRenderer.invoke('singleNiveau', credential), |
|
||||
insertNiveau: (credentials) => ipcRenderer.invoke('insertNiveau', credentials), |
|
||||
updateSingleNiveau: (credentials) => ipcRenderer.invoke('updateSingleNiveau', credentials), |
|
||||
importNiveau: (credentials) => ipcRenderer.invoke('importNiveau', credentials), |
|
||||
deleteNiveaus: (credentials) => ipcRenderer.invoke('deleteNiveaus', credentials) |
|
||||
}) |
|
||||
|
|
||||
/** |
|
||||
* contextBridge for notes |
|
||||
*/ |
|
||||
contextBridge.exposeInMainWorld('notes', { |
|
||||
getNotes: (credentials) => ipcRenderer.invoke('getSingleNote', credentials), |
|
||||
insertNote: (credentials) => ipcRenderer.invoke('insertNote', credentials), |
|
||||
updateNote: (credentials) => ipcRenderer.invoke('updatetNote', credentials), |
|
||||
getMoyenne: (credentials) => ipcRenderer.invoke('getMoyene', credentials), |
|
||||
noteMatiere: (credentials) => ipcRenderer.invoke('noteMatiere', credentials), |
|
||||
noteRelerer: (credentials) => ipcRenderer.invoke('noteRelerer', credentials), |
|
||||
getMoyenneVerify: () => verifyEtudiantIfHeHasNotes(), |
|
||||
getblockNote: () => blockShowMoyene() |
|
||||
}) |
|
||||
|
|
||||
/** |
|
||||
* contextbridge for note repechage |
|
||||
*/ |
|
||||
contextBridge.exposeInMainWorld('noteRepech', { |
|
||||
getNotesRepech: (credentials) => ipcRenderer.invoke('getNotesRepech', credentials), |
|
||||
updateNoteRepech: (credentials) => ipcRenderer.invoke('updatetNoteRepech', credentials), |
|
||||
getMoyenneRepech: (credentials) => ipcRenderer.invoke('getMoyenneRepech', credentials) |
|
||||
}) |
|
||||
|
|
||||
/** |
|
||||
* contextBridge for matieres |
|
||||
*/ |
|
||||
contextBridge.exposeInMainWorld('matieres', { |
|
||||
getMatiere: () => getMatiere(), |
|
||||
createMatiere: (credentials) => ipcRenderer.invoke('createMatiere', credentials), |
|
||||
getMatiereByID: (credentials) => ipcRenderer.invoke('getMatiereByID', credentials), |
|
||||
updateMatiere: (credentials) => ipcRenderer.invoke('updateMatiere', credentials), |
|
||||
importExcel: (credentials) => ipcRenderer.invoke('importExcelMatiere', credentials), |
|
||||
displayMatiereFromForm: (credentials) => |
|
||||
ipcRenderer.invoke('displayMatiereFromForm', credentials), |
|
||||
deleteMatiere: (credentials) => ipcRenderer.invoke('deleteMatiere', credentials), |
|
||||
asign: (credentials) => ipcRenderer.invoke('asign', credentials), |
|
||||
getAsign: (credentials) => ipcRenderer.invoke('getAsign', credentials), |
|
||||
asignSemestre: (credentials) => ipcRenderer.invoke('asignSemestre', credentials), |
|
||||
getSemestreMatiere: (credentials) => ipcRenderer.invoke('getSemestreMatiere', credentials), |
|
||||
getSemestre: () => getSemestre(), |
|
||||
getNessesary: () => getNessesarytable(), |
|
||||
getENseignant: () => getEnseignants(), |
|
||||
insertUpdateMentionSemestre: (credentials) => |
|
||||
ipcRenderer.invoke('insertUpdateMentionSemestre', credentials), |
|
||||
updateNessesary: (credentials) => ipcRenderer.invoke('updateNessesary', credentials), |
|
||||
insertProf: (credentials) => ipcRenderer.invoke('insertProf', credentials), |
|
||||
getSingleProf: (credentials) => ipcRenderer.invoke('getSingleProf', credentials), |
|
||||
updateProf: (credentials) => ipcRenderer.invoke('updateProf', credentials) |
|
||||
}) |
|
||||
|
|
||||
/** |
|
||||
* contextBridge for note systeme |
|
||||
*/ |
|
||||
contextBridge.exposeInMainWorld('notesysteme', { |
|
||||
getSyteme: () => getSysteme(), |
|
||||
updateNoteSysteme: (credentials) => ipcRenderer.invoke('updateNoteSysteme', credentials), |
|
||||
insertParcours: (credentials) => ipcRenderer.invoke('insertParcours', credentials), |
|
||||
getSingleParcours: (credentials) => ipcRenderer.invoke('getSingleParcours', credentials), |
|
||||
deleteParcours: (credentials) => ipcRenderer.invoke('deleteParcours', credentials), |
|
||||
updateParcours: (credentials) => ipcRenderer.invoke('updateParcours', credentials), |
|
||||
parcourMatiere: (credentials) => ipcRenderer.invoke('parcourMatiere', credentials), |
|
||||
getParcours: () => getParcours(), |
|
||||
extractFiches: (credentials) => ipcRenderer.invoke('extractFiches', credentials), |
|
||||
getParcourMatiere: (credentials) => ipcRenderer.invoke('getParcourMatiere', credentials), |
|
||||
createIPConfig: (credentials) => ipcRenderer.invoke('createIPConfig', credentials), |
|
||||
getIPConfig: () => getIPConfig(), |
|
||||
updateIPConfig: (credentials) => ipcRenderer.invoke('updateIPConfig', credentials) |
|
||||
}) |
|
||||
|
|
||||
/** |
|
||||
* contextbridge for status |
|
||||
*/ |
|
||||
contextBridge.exposeInMainWorld('statuss', { |
|
||||
getStatus: () => getStatus() |
|
||||
}) |
|
||||
|
|
||||
/** |
|
||||
* contextbridge for annee scolaire |
|
||||
*/ |
|
||||
contextBridge.exposeInMainWorld('anneescolaire', { |
|
||||
getAnneeScolaire: () => getAnneeScolaire(), |
|
||||
getInterval: () => getInterval(), |
|
||||
createAnneeScolaire: (credentials) => ipcRenderer.invoke('createAnneeScolaire', credentials), |
|
||||
deleteAnneeScolaire: (credentials) => ipcRenderer.invoke('deleteAnneeScolaire', credentials), |
|
||||
getSingleAnneeScolaire: (credentials) => |
|
||||
ipcRenderer.invoke('getSingleAnneeScolaire', credentials), |
|
||||
updateAnneeScolaire: (credentials) => ipcRenderer.invoke('updateAnneeScolaire', credentials), |
|
||||
setCurrent: (credentials) => ipcRenderer.invoke('setCurrent', credentials) |
|
||||
}) |
|
||||
|
|
||||
/** |
|
||||
* contextbridge for mention |
|
||||
*/ |
|
||||
contextBridge.exposeInMainWorld('mention', { |
|
||||
createMention: (credentials) => ipcRenderer.invoke('createMention', credentials), |
|
||||
getMention: () => getMentions(), |
|
||||
getSingleMention: (credentials) => ipcRenderer.invoke('getSingleMention', credentials), |
|
||||
updateMention: (credentials) => ipcRenderer.invoke('updateMention', credentials), |
|
||||
deleteMention: (credentials) => ipcRenderer.invoke('deleteMention', credentials) |
|
||||
}) |
|
||||
} catch (error) { |
|
||||
console.error(error) |
|
||||
} |
|
||||
} else { |
|
||||
window.electron = electronAPI |
|
||||
window.api = api |
|
||||
} |
|
||||
@ -1,18 +0,0 @@ |
|||||
<!doctype html> |
|
||||
<html> |
|
||||
<head> |
|
||||
<meta charset="UTF-8" /> |
|
||||
<title>Université de Toamasina</title> |
|
||||
<link rel="shortcut icon" href="src/assets/logo.ico" type="image/x-icon" /> |
|
||||
<!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP --> |
|
||||
<meta |
|
||||
http-equiv="Content-Security-Policy" |
|
||||
content="default-src 'self'; connect-src 'self' https://api.polytechnique.c4m.mg; script-src 'self' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data:" |
|
||||
/> |
|
||||
</head> |
|
||||
|
|
||||
<body> |
|
||||
<div id="root"></div> |
|
||||
<script type="module" src="/src/main.jsx"></script> |
|
||||
</body> |
|
||||
</html> |
|
||||
@ -1,63 +0,0 @@ |
|||||
import React, { useEffect, useState } from 'react' |
|
||||
import { RouterProvider } from 'react-router-dom' |
|
||||
import Router from './Routes/Routes' |
|
||||
import { AuthContextProvider, useAuthContext } from './contexts/AuthContext' |
|
||||
import { ClipLoader } from 'react-spinners' // Import the loader |
|
||||
import preloader from './assets/preloader.jpg' |
|
||||
import { DataProvider } from './contexts/MoyenneDeClasseContext' |
|
||||
|
|
||||
const App = () => { |
|
||||
const [loading, setLoading] = useState(true) |
|
||||
const { setToken } = useAuthContext() |
|
||||
|
|
||||
// Simulate loading (e.g., fetching some initial data or assets) |
|
||||
useEffect(() => { |
|
||||
const timer = setTimeout(() => { |
|
||||
setLoading(false) // Set loading to false after the simulated loading time |
|
||||
}, 3000) // 3 seconds delay to simulate loading |
|
||||
|
|
||||
return () => clearTimeout(timer) // Cleanup the timer |
|
||||
}, []) |
|
||||
|
|
||||
// Show Preloader while loading, else show your content |
|
||||
if (loading) { |
|
||||
return ( |
|
||||
<div |
|
||||
className="preloader-container" |
|
||||
style={{ |
|
||||
display: 'flex', |
|
||||
justifyContent: 'center', |
|
||||
alignItems: 'center', |
|
||||
height: '100vh', |
|
||||
backgroundImage: `url("${preloader}")`, |
|
||||
backgroundSize: 'cover', |
|
||||
backgroundPosition: 'center', |
|
||||
minHeight: '100vh' |
|
||||
}} |
|
||||
> |
|
||||
<ClipLoader color="blue" loading={loading} size={50} /> |
|
||||
</div> |
|
||||
) |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* condition usign with the Tray icon on the bottom |
|
||||
*/ |
|
||||
if (window.Tray && typeof window.Tray.onNavigate === 'function') { |
|
||||
window.Tray.onNavigate((route) => { |
|
||||
if (route) { |
|
||||
window.location.hash = route // Navigate by updating the hash |
|
||||
} |
|
||||
}) |
|
||||
} |
|
||||
|
|
||||
return ( |
|
||||
<AuthContextProvider> |
|
||||
<DataProvider> |
|
||||
<RouterProvider router={Router} /> |
|
||||
</DataProvider> |
|
||||
</AuthContextProvider> |
|
||||
) |
|
||||
} |
|
||||
|
|
||||
export default App |
|
||||
@ -1,207 +0,0 @@ |
|||||
import { createHashRouter } from 'react-router-dom' |
|
||||
import DefaultLayout from '../layouts/DefaultLayout' |
|
||||
import Login from '../components/Login' |
|
||||
import LoginLayout from '../layouts/LoginLayout' |
|
||||
import NotFound from '../components/NotFound' |
|
||||
import ForgotPassword from '../components/ForgotPassword' |
|
||||
import Home from '../components/Home' |
|
||||
import Student from '../components/Student' |
|
||||
import Notes from '../components/Notes' |
|
||||
import AddStudent from '../components/AddStudent' |
|
||||
import SingleEtudiant from '../components/SingleEtudiant' |
|
||||
import AddNotes from '../components/AddNotes' |
|
||||
import Apropos from '../components/Apropos' |
|
||||
import Niveau from '../components/Niveau' |
|
||||
import AddNiveau from '../components/AddNiveau' |
|
||||
import Admin from '../components/Addadmin' |
|
||||
import Setting from '../components/Param' |
|
||||
import SingleNotes from '../components/SingleNotes' |
|
||||
import ExportEtudiants from '../components/ExportEtudiants' |
|
||||
import SingleNiveau from '../components/singleNiveau' |
|
||||
import Matieres from '../components/Matieres' |
|
||||
import AddMatiere from '../components/AddMatiere' |
|
||||
import ImportMatiere from '../components/ImportMatiere' |
|
||||
import SingleMatiere from '../components/SingleMatiere' |
|
||||
import ImportNiveau from '../components/ImportNiveau' |
|
||||
import SystemeNote from '../components/SystemeNote' |
|
||||
import AnneeScolaire from '../components/AnneeScolaire' |
|
||||
import AddAnneeScolaire from '../components/AddAnneeScolaire' |
|
||||
import Noteclasse from '../components/Noteclasse' |
|
||||
import TesteDatagrid from '../components/TesteDatagrid' |
|
||||
import Manuel from '../components/Manuel' |
|
||||
import Mentions from '../components/Mentions' |
|
||||
import AddMention from '../components/AddMention' |
|
||||
import SinleMention from '../components/SinleMention' |
|
||||
import AssignMatiereToMention from '../components/AssignMatiereToMention' |
|
||||
import AssingMatiereToSemestre from '../components/AssingMatiereToSemestre' |
|
||||
import SingleAnneeScolaire from '../components/SingleAnneeScolaire' |
|
||||
import Parcours from '../components/Parcours' |
|
||||
import ModalExportFichr from '../components/ModalExportFichr' |
|
||||
import Resultat from '../components/Resultat' |
|
||||
import TrancheEcolage from '../components/TrancheEcolage' |
|
||||
|
|
||||
// Use createHashRouter instead of createBrowserRouter because the desktop app is in local machine |
|
||||
const Router = createHashRouter([ |
|
||||
{ |
|
||||
path: '/', |
|
||||
element: <DefaultLayout />, |
|
||||
children: [ |
|
||||
{ |
|
||||
path: '/', // This will now be accessed as #/ |
|
||||
element: <Home /> |
|
||||
}, |
|
||||
{ |
|
||||
path: '/student', |
|
||||
element: <Student /> |
|
||||
}, |
|
||||
{ |
|
||||
path: '/notes', |
|
||||
element: <Notes /> |
|
||||
}, |
|
||||
{ |
|
||||
path: '/addstudent', |
|
||||
element: <AddStudent /> |
|
||||
}, |
|
||||
{ |
|
||||
path: '/single/:id', |
|
||||
element: <SingleEtudiant /> |
|
||||
}, |
|
||||
{ |
|
||||
path: '/addnotes/:id/:niveau/:mention_id/:parcours', |
|
||||
element: <AddNotes /> |
|
||||
}, |
|
||||
{ |
|
||||
path: '/anneescolaire/:id', |
|
||||
element: <SingleAnneeScolaire /> |
|
||||
}, |
|
||||
{ |
|
||||
path: '/asignmatiere/:id', |
|
||||
element: <AssignMatiereToMention /> |
|
||||
}, |
|
||||
{ |
|
||||
path: '/asignmatieresemestre/:id', |
|
||||
element: <AssingMatiereToSemestre /> |
|
||||
}, |
|
||||
{ |
|
||||
path: '/manual', |
|
||||
element: <Manuel /> |
|
||||
}, |
|
||||
{ |
|
||||
path: '/mention', |
|
||||
element: <Mentions /> |
|
||||
}, |
|
||||
{ |
|
||||
path: '/addmention', |
|
||||
element: <AddMention /> |
|
||||
}, |
|
||||
{ |
|
||||
path: '/singlemention/:id', |
|
||||
element: <SinleMention /> |
|
||||
}, |
|
||||
{ |
|
||||
path: '/apropos', |
|
||||
element: <Apropos /> |
|
||||
}, |
|
||||
{ |
|
||||
path: '/niveau', |
|
||||
element: <Niveau /> |
|
||||
}, |
|
||||
{ |
|
||||
path: '/addniveau', |
|
||||
element: <AddNiveau /> |
|
||||
}, |
|
||||
{ |
|
||||
path: '/para', |
|
||||
element: <Setting /> |
|
||||
}, |
|
||||
{ |
|
||||
path: '/admin', |
|
||||
element: <Admin /> |
|
||||
}, |
|
||||
{ |
|
||||
path: '/single/notes/:id/:niveau/:scolaire', |
|
||||
element: <SingleNotes /> |
|
||||
}, |
|
||||
{ |
|
||||
path: '/exportetudiant', |
|
||||
element: <ExportEtudiants /> |
|
||||
}, |
|
||||
{ |
|
||||
path: '/single/niveau/:id', |
|
||||
element: <SingleNiveau /> |
|
||||
}, |
|
||||
{ |
|
||||
path: '/matiere', |
|
||||
element: <Matieres /> |
|
||||
}, |
|
||||
{ |
|
||||
path: '/addmatiere', |
|
||||
element: <AddMatiere /> |
|
||||
}, |
|
||||
{ |
|
||||
path: '/addmatiere/import', |
|
||||
element: <ImportMatiere /> |
|
||||
}, |
|
||||
{ |
|
||||
path: '/singlematiere/:id', |
|
||||
element: <SingleMatiere /> |
|
||||
}, |
|
||||
{ |
|
||||
path: '/importniveau', |
|
||||
element: <ImportNiveau /> |
|
||||
}, |
|
||||
{ |
|
||||
path: '/systemenote', |
|
||||
element: <SystemeNote /> |
|
||||
}, |
|
||||
{ |
|
||||
path: '/anneescolaire', |
|
||||
element: <AnneeScolaire /> |
|
||||
}, |
|
||||
{ |
|
||||
path: '/addanneescolaire', |
|
||||
element: <AddAnneeScolaire /> |
|
||||
}, |
|
||||
{ |
|
||||
path: '/noteclass/:niveau/:scolaire', |
|
||||
element: <Noteclasse /> |
|
||||
}, |
|
||||
{ |
|
||||
path: '/parcours', |
|
||||
element: <Parcours /> |
|
||||
}, |
|
||||
{ |
|
||||
path: '/fiche/:matiere_id/:nom', |
|
||||
element: <ModalExportFichr /> |
|
||||
}, |
|
||||
{ |
|
||||
path: '/resultat/:niveau/:scolaire', |
|
||||
element: <Resultat /> |
|
||||
}, |
|
||||
{ |
|
||||
path: '/tranche/:id', |
|
||||
element: <TrancheEcolage /> |
|
||||
} |
|
||||
] |
|
||||
}, |
|
||||
{ |
|
||||
path: '/', |
|
||||
element: <LoginLayout />, |
|
||||
children: [ |
|
||||
{ |
|
||||
path: '/login', |
|
||||
element: <Login /> |
|
||||
}, |
|
||||
{ |
|
||||
path: '/forgotpassword', |
|
||||
element: <ForgotPassword /> |
|
||||
} |
|
||||
] |
|
||||
}, |
|
||||
{ |
|
||||
path: '/*', |
|
||||
element: <NotFound /> |
|
||||
} |
|
||||
]) |
|
||||
|
|
||||
export default Router |
|
||||
@ -1,12 +0,0 @@ |
|||||
.blockTitle h1 { |
|
||||
font-size: 10px; |
|
||||
font-weight: bold; |
|
||||
color: white; |
|
||||
display: flex; |
|
||||
align-items: center; |
|
||||
gap: 10px; |
|
||||
} |
|
||||
.boxEtudiantsCard { |
|
||||
width: 10%; |
|
||||
padding: 1%; |
|
||||
} |
|
||||
@ -1,15 +0,0 @@ |
|||||
.blockTitle h1 { |
|
||||
font-size: 20px; |
|
||||
font-weight: bold; |
|
||||
color: white; |
|
||||
display: flex; |
|
||||
align-items: center; |
|
||||
gap: 10px; |
|
||||
} |
|
||||
.boxEtudiantsCard { |
|
||||
width: 100%; |
|
||||
padding: 1%; |
|
||||
display: flex; |
|
||||
align-items: center; |
|
||||
justify-content: center; |
|
||||
} |
|
||||
@ -1,17 +0,0 @@ |
|||||
.h1style { |
|
||||
text-transform: uppercase; |
|
||||
font-weight: 900; |
|
||||
/* 6636af4a 6636af ffae01 */ |
|
||||
border-left: 10px solid #ffff; |
|
||||
padding-left: 10px; |
|
||||
margin-bottom: 30px; |
|
||||
background: linear-gradient(to right, #ffaf01b4, transparent); |
|
||||
color: white; |
|
||||
width: 100%; |
|
||||
padding-left: 45px; |
|
||||
font-size: 25px; |
|
||||
} |
|
||||
.mainHome { |
|
||||
padding: 1% 0 0 0; |
|
||||
width: 100%; |
|
||||
} |
|
||||
|
Before Width: | Height: | Size: 8.9 KiB |
@ -1,86 +0,0 @@ |
|||||
body { |
|
||||
margin: 0; |
|
||||
padding: 0; |
|
||||
} |
|
||||
.container { |
|
||||
display: flex; |
|
||||
align-items: center; |
|
||||
justify-content: center; |
|
||||
height: 100vh; |
|
||||
gap: 20px; |
|
||||
} |
|
||||
.cart { |
|
||||
width: 33%; |
|
||||
/* height: 35%; */ |
|
||||
border: solid 1px rgba(0, 0, 0, 0.315); |
|
||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.8); /* Adds a soft shadow */ |
|
||||
/* border-radius: 10px; */ |
|
||||
padding: 1px; |
|
||||
background-color: #ffded4; |
|
||||
position: relative; |
|
||||
} |
|
||||
.cart-footer { |
|
||||
position: absolute; |
|
||||
height: 40px; |
|
||||
width: 100%; |
|
||||
bottom: 0; |
|
||||
left: 0; |
|
||||
border-bottom-left-radius: 10px; |
|
||||
border-bottom-right-radius: 10px; |
|
||||
background-color: #ff5a27; |
|
||||
} |
|
||||
.title { |
|
||||
margin: 0; |
|
||||
padding: 0; |
|
||||
text-align: center; |
|
||||
text-transform: uppercase; |
|
||||
z-index: 1; |
|
||||
} |
|
||||
.title h1, |
|
||||
p { |
|
||||
margin: 0; |
|
||||
font-size: 15px; |
|
||||
} |
|
||||
.content { |
|
||||
z-index: 1; |
|
||||
height: 70%; |
|
||||
display: flex; |
|
||||
align-items: center; |
|
||||
justify-content: space-between; |
|
||||
} |
|
||||
.cart_photos { |
|
||||
width: 30%; |
|
||||
text-align: center; |
|
||||
} |
|
||||
.cart_photos img { |
|
||||
border-radius: 50px; |
|
||||
border: solid 2px #ff5a27; |
|
||||
width: 95px; |
|
||||
height: 100px; |
|
||||
object-fit: cover; |
|
||||
} |
|
||||
.cart_info { |
|
||||
width: 60%; |
|
||||
padding-left: 1%; |
|
||||
} |
|
||||
.cart_info p { |
|
||||
font-size: 16px; |
|
||||
} |
|
||||
.qrContent { |
|
||||
display: flex; |
|
||||
align-items: center; |
|
||||
justify-content: space-around; |
|
||||
padding: 2%; |
|
||||
} |
|
||||
.gauche h1, |
|
||||
.droite h1 { |
|
||||
font-size: 17px; |
|
||||
text-align: center; |
|
||||
} |
|
||||
.gauche img, |
|
||||
.droite img { |
|
||||
width: 150px; |
|
||||
} |
|
||||
.droite .qrcodeDroite { |
|
||||
width: 150px; |
|
||||
} |
|
||||
@ -1,4 +0,0 @@ |
|||||
.display { |
|
||||
background-color: rgba(255, 255, 255, 0.9); |
|
||||
/* margin-bottom: 2%; */ |
|
||||
} |
|
||||
@ -1,20 +0,0 @@ |
|||||
html, |
|
||||
body { |
|
||||
height: 100%; |
|
||||
margin: 0; |
|
||||
padding: 0; |
|
||||
} |
|
||||
|
|
||||
/* DefaultLayout.css */ |
|
||||
.default_layout { |
|
||||
/* height: 100%; Use full viewport height */ |
|
||||
min-height: 100vh; |
|
||||
overflow-y: auto; |
|
||||
background: url('../assets/background2.jpg') no-repeat center/cover; |
|
||||
/* background-color: #aad4e571; */ |
|
||||
} |
|
||||
.outlet { |
|
||||
padding-left: 55px; |
|
||||
margin-top: 0; |
|
||||
height: 100%; |
|
||||
} |
|
||||
@ -1,78 +0,0 @@ |
|||||
.header { |
|
||||
width: 100%; |
|
||||
display: flex; |
|
||||
align-items: start; |
|
||||
justify-content: space-between; |
|
||||
} |
|
||||
.headerCenter { |
|
||||
text-align: center; |
|
||||
color: black; |
|
||||
line-height: 15px; |
|
||||
width: 60%; |
|
||||
} |
|
||||
.headerCenter h5 { |
|
||||
text-transform: uppercase; |
|
||||
line-height: 12px; |
|
||||
font-size: 14px; |
|
||||
} |
|
||||
.headerCenter p { |
|
||||
font-style: italic; |
|
||||
font-weight: 400; |
|
||||
font-size: 14px; |
|
||||
} |
|
||||
.transition { |
|
||||
border: solid 1px gray; |
|
||||
width: 100%; |
|
||||
padding: 1% 2%; |
|
||||
font-weight: bold; |
|
||||
} |
|
||||
.transition h6 { |
|
||||
font-size: 12px; |
|
||||
} |
|
||||
|
|
||||
/* content */ |
|
||||
.content { |
|
||||
text-align: center; |
|
||||
} |
|
||||
.contentHeader { |
|
||||
color: black; |
|
||||
flex-direction: column; |
|
||||
display: flex; |
|
||||
align-items: center; |
|
||||
justify-content: center; |
|
||||
font-size: 13px; |
|
||||
} |
|
||||
.contentHeader h1 { |
|
||||
font-size: 28px; |
|
||||
} |
|
||||
.contentHeaderList { |
|
||||
font-weight: bold; |
|
||||
font-size: 13px; |
|
||||
} |
|
||||
.contentHeaderList p { |
|
||||
font-size: 13px; |
|
||||
display: flex; |
|
||||
gap: 30px; |
|
||||
} |
|
||||
.ContentTable { |
|
||||
display: flex; |
|
||||
align-items: center; |
|
||||
justify-content: center; |
|
||||
} |
|
||||
.ContentTable table { |
|
||||
border-color: black !important; |
|
||||
font-size: 13px; |
|
||||
margin: 0; |
|
||||
} |
|
||||
.contentFooter { |
|
||||
display: flex; |
|
||||
align-items: end; |
|
||||
flex-direction: column; |
|
||||
margin-top: 5px; |
|
||||
} |
|
||||
.signature { |
|
||||
width: 50%; |
|
||||
display: flex; |
|
||||
flex-direction: column; |
|
||||
gap: 30px; |
|
||||
} |
|
||||
@ -1,78 +0,0 @@ |
|||||
.boxEtudiantsCard { |
|
||||
width: 100%; |
|
||||
padding: 1%; |
|
||||
/* margin-top: 12%; */ |
|
||||
} |
|
||||
.cards { |
|
||||
/* width: 30%; */ |
|
||||
justify-content: space-between; |
|
||||
align-items: center; |
|
||||
padding: 0 1%; |
|
||||
} |
|
||||
.nomEtudiants { |
|
||||
font-weight: bold; |
|
||||
} |
|
||||
.header { |
|
||||
color: white; |
|
||||
/* backdrop-filter: blur(5px); */ |
|
||||
/* position: fixed; */ |
|
||||
width: 100%; |
|
||||
z-index: 9; |
|
||||
} |
|
||||
.container { |
|
||||
display: flex; |
|
||||
align-items: center; |
|
||||
justify-content: center; |
|
||||
padding: 1% 2%; |
|
||||
background: linear-gradient(to left, #ffaf01b4, transparent); |
|
||||
} |
|
||||
.blockTitle { |
|
||||
display: flex; |
|
||||
align-items: center; |
|
||||
justify-content: space-between; |
|
||||
padding: 0 3%; |
|
||||
} |
|
||||
.blockTitle h1 { |
|
||||
font-size: 20px; |
|
||||
font-weight: bold; |
|
||||
/* text-transform: uppercase; */ |
|
||||
color: white; |
|
||||
} |
|
||||
.blockTitle button { |
|
||||
font-size: 12px; |
|
||||
display: inline-flex; |
|
||||
gap: 10px; |
|
||||
} |
|
||||
.boxImg { |
|
||||
padding: 2%; |
|
||||
display: flex; |
|
||||
align-items: center; |
|
||||
justify-content: center; |
|
||||
} |
|
||||
.imagePDP { |
|
||||
object-fit: cover; |
|
||||
border: solid 2px rgb(156, 39, 176); |
|
||||
} |
|
||||
.mainHome { |
|
||||
border: solid 1px red; |
|
||||
} |
|
||||
.select:hover { |
|
||||
border-color: rgb(156, 39, 176) !important; |
|
||||
} |
|
||||
.details { |
|
||||
background-color: rgba(128, 128, 128, 0.4); |
|
||||
border-radius: 8px; |
|
||||
padding: 3% 1% 3% 4%; |
|
||||
text-align: left; |
|
||||
} |
|
||||
.details span { |
|
||||
display: block; |
|
||||
line-height: 25px; |
|
||||
} |
|
||||
.cardOverflow { |
|
||||
height: 450px; |
|
||||
transition: 1s ease; |
|
||||
} |
|
||||
.cardOverflow:hover { |
|
||||
transform: scale(1.03); |
|
||||
} |
|
||||
@ -1,20 +0,0 @@ |
|||||
/* .container{ |
|
||||
background: url(bg2.jpg) no-repeat fixed center/cover; |
|
||||
background-color: #08000e; |
|
||||
} */ |
|
||||
.cards { |
|
||||
background-color: #06000aa4 !important; |
|
||||
color: white !important; |
|
||||
box-shadow: 0px 4px 10px rgba(255, 255, 255, 0.2) !important; /* Light white shadow */ |
|
||||
border: solid 1px rgba(245, 245, 245, 0.692); |
|
||||
} |
|
||||
.formulaireLogin { |
|
||||
color: white !important; |
|
||||
} |
|
||||
.formulaireLogin label { |
|
||||
color: white; |
|
||||
font-size: 17px; |
|
||||
} |
|
||||
.formulaireLogin input { |
|
||||
color: white; |
|
||||
} |
|
||||
@ -1,25 +0,0 @@ |
|||||
.navnar { |
|
||||
color: rgba(0, 0, 0, 0.7); |
|
||||
background-color: #2d2d2d; |
|
||||
display: flex; |
|
||||
align-items: center; |
|
||||
justify-content: space-between; |
|
||||
padding: 2px 15px; |
|
||||
position: fixed; |
|
||||
width: 100%; |
|
||||
top: 0; |
|
||||
border-bottom: solid 1px white; |
|
||||
color: white; |
|
||||
z-index: 99999; |
|
||||
} |
|
||||
.gauche { |
|
||||
width: 30%; |
|
||||
display: flex; |
|
||||
align-items: center; |
|
||||
} |
|
||||
.droite { |
|
||||
width: 4%; |
|
||||
display: flex; |
|
||||
align-items: center; |
|
||||
justify-content: space-between; |
|
||||
} |
|
||||
@ -1,51 +0,0 @@ |
|||||
.navbar { |
|
||||
background-color: #2d2d2d; |
|
||||
border-top: solid 1px white; |
|
||||
color: white; |
|
||||
width: 50px; |
|
||||
/* top: 28px; */ |
|
||||
position: fixed; |
|
||||
height: 100%; |
|
||||
/* bottom: 0; */ |
|
||||
display: flex; |
|
||||
align-items: center; |
|
||||
justify-content: space-between; |
|
||||
flex-direction: column; |
|
||||
padding: 0 0 5% 0; |
|
||||
margin: 0; |
|
||||
border-right: solid 1px white; |
|
||||
z-index: 99; |
|
||||
} |
|
||||
.liste1, |
|
||||
.liste2 { |
|
||||
list-style: none; |
|
||||
padding: 0; |
|
||||
margin: 0; |
|
||||
display: flex; |
|
||||
flex-direction: column; |
|
||||
gap: 20px; |
|
||||
} |
|
||||
.liste1 li, |
|
||||
.liste2 li { |
|
||||
font-size: 25px; |
|
||||
cursor: pointer; |
|
||||
} |
|
||||
.nav_link { |
|
||||
color: white; |
|
||||
position: relative; |
|
||||
transition: 0.8s ease; |
|
||||
} |
|
||||
.icon_label { |
|
||||
position: absolute; |
|
||||
top: 0; /* Below the icon */ |
|
||||
left: 60px; |
|
||||
transform: translateX(-50%); |
|
||||
background-color: black; |
|
||||
color: white; |
|
||||
padding: 5px; |
|
||||
border-radius: 4px; |
|
||||
white-space: nowrap; /* Keep the label on one line */ |
|
||||
font-size: 12px; |
|
||||
margin-top: 5px; /* Slight gap between icon and label */ |
|
||||
z-index: 1; |
|
||||
} |
|
||||
|
Before Width: | Height: | Size: 7.4 KiB |
|
Before Width: | Height: | Size: 290 KiB |
|
Before Width: | Height: | Size: 14 MiB |
@ -1,97 +0,0 @@ |
|||||
/* :root { |
|
||||
--ev-c-white: #ffffff; |
|
||||
--ev-c-white-soft: #f8f8f8; |
|
||||
--ev-c-white-mute: #f2f2f2; |
|
||||
|
|
||||
--ev-c-black: #1b1b1f; |
|
||||
--ev-c-black-soft: #222222; |
|
||||
--ev-c-black-mute: #282828; |
|
||||
|
|
||||
--ev-c-gray-1: #515c67; |
|
||||
--ev-c-gray-2: #414853; |
|
||||
--ev-c-gray-3: #32363f; |
|
||||
|
|
||||
--ev-c-text-1: rgba(255, 255, 245, 0.86); |
|
||||
--ev-c-text-2: rgba(235, 235, 245, 0.6); |
|
||||
--ev-c-text-3: rgba(235, 235, 245, 0.38); |
|
||||
|
|
||||
--ev-button-alt-border: transparent; |
|
||||
--ev-button-alt-text: var(--ev-c-text-1); |
|
||||
--ev-button-alt-bg: var(--ev-c-gray-3); |
|
||||
--ev-button-alt-hover-border: transparent; |
|
||||
--ev-button-alt-hover-text: var(--ev-c-text-1); |
|
||||
--ev-button-alt-hover-bg: var(--ev-c-gray-2); |
|
||||
} |
|
||||
|
|
||||
:root { |
|
||||
--color-background: var(--ev-c-black); |
|
||||
--color-background-soft: var(--ev-c-black-soft); |
|
||||
--color-background-mute: var(--ev-c-black-mute); |
|
||||
|
|
||||
--color-text: var(--ev-c-text-1); |
|
||||
} |
|
||||
|
|
||||
*, |
|
||||
*::before, |
|
||||
*::after { |
|
||||
box-sizing: border-box; |
|
||||
margin: 0; |
|
||||
font-weight: normal; |
|
||||
} |
|
||||
|
|
||||
ul { |
|
||||
list-style: none; |
|
||||
} |
|
||||
|
|
||||
body { |
|
||||
min-height: 100vh; |
|
||||
color: var(--color-text); |
|
||||
background: var(--color-background); |
|
||||
line-height: 1.6; |
|
||||
font-family: |
|
||||
Inter, |
|
||||
-apple-system, |
|
||||
BlinkMacSystemFont, |
|
||||
'Segoe UI', |
|
||||
Roboto, |
|
||||
Oxygen, |
|
||||
Ubuntu, |
|
||||
Cantarell, |
|
||||
'Fira Sans', |
|
||||
'Droid Sans', |
|
||||
'Helvetica Neue', |
|
||||
sans-serif; |
|
||||
text-rendering: optimizeLegibility; |
|
||||
-webkit-font-smoothing: antialiased; |
|
||||
-moz-osx-font-smoothing: grayscale; |
|
||||
} */ |
|
||||
.form-control:focus { |
|
||||
outline: none; |
|
||||
box-shadow: none; |
|
||||
border-color: initial; /* Optional: restore the default border color */ |
|
||||
} |
|
||||
table { |
|
||||
border-collapse: collapse; /* Ensure there is no space between table cells */ |
|
||||
width: 100%; /* Adjust width as needed */ |
|
||||
margin: 0; |
|
||||
padding: 0; |
|
||||
} |
|
||||
|
|
||||
td, |
|
||||
th { |
|
||||
padding: 0; /* Remove padding from table cells */ |
|
||||
margin: 0; /* Ensure no margin inside cells */ |
|
||||
border: 1px solid black; /* Optional: Add border if needed */ |
|
||||
text-align: center; /* Center text horizontally */ |
|
||||
vertical-align: middle; /* Center text vertically */ |
|
||||
} |
|
||||
|
|
||||
tr { |
|
||||
margin: 0; |
|
||||
padding: 0; |
|
||||
} |
|
||||
|
|
||||
tbody { |
|
||||
margin: 0; |
|
||||
padding: 0; |
|
||||
} |
|
||||
|
Before Width: | Height: | Size: 1.4 MiB |
|
Before Width: | Height: | Size: 7.9 MiB |
|
Before Width: | Height: | Size: 2.6 MiB |
|
Before Width: | Height: | Size: 24 MiB |
|
Before Width: | Height: | Size: 5.7 KiB |
|
Before Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 863 B |
|
Before Width: | Height: | Size: 156 KiB |
|
Before Width: | Height: | Size: 69 KiB |
|
Before Width: | Height: | Size: 7.2 KiB |
|
Before Width: | Height: | Size: 43 KiB |
|
Before Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 9.8 KiB |
|
Before Width: | Height: | Size: 5.8 KiB |
@ -1,163 +0,0 @@ |
|||||
@import './base.css'; |
|
||||
|
|
||||
body { |
|
||||
display: flex; |
|
||||
align-items: center; |
|
||||
justify-content: center; |
|
||||
overflow: hidden; |
|
||||
background-image: url('./wavy-lines.svg'); |
|
||||
background-size: cover; |
|
||||
user-select: none; |
|
||||
} |
|
||||
|
|
||||
code { |
|
||||
font-weight: 600; |
|
||||
padding: 3px 5px; |
|
||||
border-radius: 2px; |
|
||||
background-color: var(--color-background-mute); |
|
||||
font-family: |
|
||||
ui-monospace, |
|
||||
SFMono-Regular, |
|
||||
SF Mono, |
|
||||
Menlo, |
|
||||
Consolas, |
|
||||
Liberation Mono, |
|
||||
monospace; |
|
||||
font-size: 85%; |
|
||||
} |
|
||||
|
|
||||
#root { |
|
||||
display: flex; |
|
||||
align-items: center; |
|
||||
justify-content: center; |
|
||||
flex-direction: column; |
|
||||
margin-bottom: 80px; |
|
||||
} |
|
||||
|
|
||||
.logo { |
|
||||
margin-bottom: 20px; |
|
||||
-webkit-user-drag: none; |
|
||||
height: 128px; |
|
||||
width: 128px; |
|
||||
will-change: filter; |
|
||||
transition: filter 300ms; |
|
||||
} |
|
||||
|
|
||||
.logo:hover { |
|
||||
filter: drop-shadow(0 0 1.2em #6988e6aa); |
|
||||
} |
|
||||
|
|
||||
.creator { |
|
||||
font-size: 14px; |
|
||||
line-height: 16px; |
|
||||
color: var(--ev-c-text-2); |
|
||||
font-weight: 600; |
|
||||
margin-bottom: 10px; |
|
||||
} |
|
||||
|
|
||||
.text { |
|
||||
font-size: 28px; |
|
||||
color: var(--ev-c-text-1); |
|
||||
font-weight: 700; |
|
||||
line-height: 32px; |
|
||||
text-align: center; |
|
||||
margin: 0 10px; |
|
||||
padding: 16px 0; |
|
||||
} |
|
||||
|
|
||||
.tip { |
|
||||
font-size: 16px; |
|
||||
line-height: 24px; |
|
||||
color: var(--ev-c-text-2); |
|
||||
font-weight: 600; |
|
||||
} |
|
||||
|
|
||||
.react { |
|
||||
background: -webkit-linear-gradient(315deg, #087ea4 55%, #7c93ee); |
|
||||
background-clip: text; |
|
||||
-webkit-background-clip: text; |
|
||||
-webkit-text-fill-color: transparent; |
|
||||
font-weight: 700; |
|
||||
} |
|
||||
|
|
||||
.actions { |
|
||||
display: flex; |
|
||||
padding-top: 32px; |
|
||||
margin: -6px; |
|
||||
flex-wrap: wrap; |
|
||||
justify-content: flex-start; |
|
||||
} |
|
||||
|
|
||||
.action { |
|
||||
flex-shrink: 0; |
|
||||
padding: 6px; |
|
||||
} |
|
||||
|
|
||||
.action a { |
|
||||
cursor: pointer; |
|
||||
text-decoration: none; |
|
||||
display: inline-block; |
|
||||
border: 1px solid transparent; |
|
||||
text-align: center; |
|
||||
font-weight: 600; |
|
||||
white-space: nowrap; |
|
||||
border-radius: 20px; |
|
||||
padding: 0 20px; |
|
||||
line-height: 38px; |
|
||||
font-size: 14px; |
|
||||
border-color: var(--ev-button-alt-border); |
|
||||
color: var(--ev-button-alt-text); |
|
||||
background-color: var(--ev-button-alt-bg); |
|
||||
} |
|
||||
|
|
||||
.action a:hover { |
|
||||
border-color: var(--ev-button-alt-hover-border); |
|
||||
color: var(--ev-button-alt-hover-text); |
|
||||
background-color: var(--ev-button-alt-hover-bg); |
|
||||
} |
|
||||
|
|
||||
.versions { |
|
||||
position: absolute; |
|
||||
bottom: 30px; |
|
||||
margin: 0 auto; |
|
||||
padding: 15px 0; |
|
||||
font-family: 'Menlo', 'Lucida Console', monospace; |
|
||||
display: inline-flex; |
|
||||
overflow: hidden; |
|
||||
align-items: center; |
|
||||
border-radius: 22px; |
|
||||
background-color: #202127; |
|
||||
backdrop-filter: blur(24px); |
|
||||
} |
|
||||
|
|
||||
.versions li { |
|
||||
display: block; |
|
||||
float: left; |
|
||||
border-right: 1px solid var(--ev-c-gray-1); |
|
||||
padding: 0 20px; |
|
||||
font-size: 14px; |
|
||||
line-height: 14px; |
|
||||
opacity: 0.8; |
|
||||
&:last-child { |
|
||||
border: none; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
@media (max-width: 720px) { |
|
||||
.text { |
|
||||
font-size: 20px; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
@media (max-width: 620px) { |
|
||||
.versions { |
|
||||
display: none; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
@media (max-width: 350px) { |
|
||||
.tip, |
|
||||
.actions { |
|
||||
display: none; |
|
||||
} |
|
||||
} |
|
||||
|
Before Width: | Height: | Size: 5.3 KiB |
|
Before Width: | Height: | Size: 454 KiB |
|
Before Width: | Height: | Size: 871 B |
|
Before Width: | Height: | Size: 96 KiB |
|
Before Width: | Height: | Size: 174 KiB |
|
Before Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 1.7 KiB |
@ -1,283 +0,0 @@ |
|||||
import React, { useRef, useState } from 'react' |
|
||||
import classe from '../assets/AllStyleComponents.module.css' |
|
||||
import classeHome from '../assets/Home.module.css' |
|
||||
import Paper from '@mui/material/Paper' |
|
||||
import { Link, useNavigate } from 'react-router-dom' |
|
||||
import { IoMdReturnRight } from 'react-icons/io' |
|
||||
import { Modal, Box, Typography, Button, InputAdornment, TextField, Grid } from '@mui/material' |
|
||||
import { FaCalendarAlt, FaCalendarPlus } from 'react-icons/fa' |
|
||||
import validationAnneeScolaire from './validation/ValidationAddAnneeScolaire' |
|
||||
import svgError from '../assets/error.svg' |
|
||||
import svgSuccess from '../assets/success.svg' |
|
||||
|
|
||||
const AddAnneeScolaire = () => { |
|
||||
const [formData, setFormData] = useState({ |
|
||||
code: '', |
|
||||
debut: '', |
|
||||
fin: '' |
|
||||
}) |
|
||||
|
|
||||
const debutRef = useRef() |
|
||||
const codeRef = useRef() |
|
||||
const finRef = useRef() |
|
||||
|
|
||||
/** |
|
||||
* function to set the data in state |
|
||||
* @param {*} e |
|
||||
*/ |
|
||||
const handleInputChange = (e) => { |
|
||||
const { name, value } = e.target |
|
||||
setFormData((prevData) => ({ |
|
||||
...prevData, |
|
||||
[name]: value |
|
||||
})) |
|
||||
} |
|
||||
|
|
||||
const formSubmit = async (e) => { |
|
||||
e.preventDefault() |
|
||||
let isValid = validationAnneeScolaire(codeRef.current, debutRef.current, finRef.current) |
|
||||
|
|
||||
if (isValid) { |
|
||||
let response = await window.anneescolaire.createAnneeScolaire(formData) |
|
||||
console.log(response) |
|
||||
if (response.success) { |
|
||||
setStatus(200) |
|
||||
setOpen(true) |
|
||||
} else { |
|
||||
setStatus(400) |
|
||||
setOpen(true) |
|
||||
} |
|
||||
} else { |
|
||||
setStatus(400) |
|
||||
setOpen(true) |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
const [status, setStatus] = useState(200) |
|
||||
|
|
||||
/** |
|
||||
* hook to open modal |
|
||||
*/ |
|
||||
const [open, setOpen] = useState(false) |
|
||||
|
|
||||
/** |
|
||||
* function to close modal |
|
||||
*/ |
|
||||
const handleClose = () => { |
|
||||
setOpen(false) |
|
||||
window.history.back() |
|
||||
} |
|
||||
const handleClose2 = () => { |
|
||||
setOpen(false) |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* function to return the view Modal |
|
||||
* |
|
||||
* @returns {JSX} |
|
||||
*/ |
|
||||
const modals = () => ( |
|
||||
<Modal |
|
||||
open={open} |
|
||||
onClose={status === 200 ? handleClose : handleClose} |
|
||||
aria-labelledby="modal-title" |
|
||||
aria-describedby="modal-description" |
|
||||
> |
|
||||
<Box |
|
||||
sx={{ |
|
||||
position: 'absolute', |
|
||||
top: '50%', |
|
||||
left: '50%', |
|
||||
transform: 'translate(-50%, -50%)', |
|
||||
width: 450, |
|
||||
bgcolor: 'background.paper', |
|
||||
boxShadow: 24, |
|
||||
p: 4 |
|
||||
}} |
|
||||
> |
|
||||
{status === 200 ? ( |
|
||||
<Typography style={{ display: 'flex', flexDirection: 'column', alignItems: 'center' }}> |
|
||||
<img src={svgSuccess} alt="" width={50} height={50} />{' '} |
|
||||
<span>Année universitaire insérer avec succes</span> |
|
||||
</Typography> |
|
||||
) : ( |
|
||||
<Typography style={{ display: 'flex', flexDirection: 'column', alignItems: 'center' }}> |
|
||||
<img src={svgError} alt="" width={50} height={50} />{' '} |
|
||||
<span>Erreur, veuillez réessayer</span> |
|
||||
</Typography> |
|
||||
)} |
|
||||
<Box |
|
||||
sx={{ |
|
||||
marginTop: '2%', |
|
||||
display: 'flex', |
|
||||
gap: '20px', |
|
||||
alignItems: 'end', |
|
||||
justifyContent: 'flex-end' |
|
||||
}} |
|
||||
> |
|
||||
{status === 200 ? ( |
|
||||
<Button onClick={handleClose} color="warning" variant="contained"> |
|
||||
OK |
|
||||
</Button> |
|
||||
) : ( |
|
||||
<Button onClick={handleClose2} color="warning" variant="contained"> |
|
||||
OK |
|
||||
</Button> |
|
||||
)} |
|
||||
</Box> |
|
||||
</Box> |
|
||||
</Modal> |
|
||||
) |
|
||||
|
|
||||
return ( |
|
||||
<div className={classe.mainHome}> |
|
||||
{modals()} |
|
||||
<div className={classeHome.header}> |
|
||||
<div className={classe.h1style}> |
|
||||
<div className={classeHome.blockTitle}> |
|
||||
<h1 style={{ display: 'flex', alignItems: 'center', gap: '10px' }}> |
|
||||
<FaCalendarPlus /> Ajout Année universitaire |
|
||||
</h1> |
|
||||
<Link to={'#'} onClick={() => window.history.back()}> |
|
||||
<Button color="warning" variant="contained"> |
|
||||
<IoMdReturnRight style={{ fontSize: '20px' }} /> |
|
||||
</Button> |
|
||||
</Link> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
|
|
||||
<div className={classeHome.boxEtudiantsCard}> |
|
||||
<Paper |
|
||||
sx={{ |
|
||||
width: '100%', |
|
||||
height: '100%', // Auto height to make the grid responsive |
|
||||
minHeight: 500, // Ensures a minimum height |
|
||||
display: 'flex', |
|
||||
flexDirection: 'column', |
|
||||
alignItems: 'center', |
|
||||
justifyContent: 'center' |
|
||||
}} |
|
||||
> |
|
||||
<form |
|
||||
onSubmit={formSubmit} |
|
||||
style={{ |
|
||||
width: '50%', |
|
||||
padding: '1%', |
|
||||
height: '20%', |
|
||||
border: 'solid 2px orange', |
|
||||
borderRadius: '10px' |
|
||||
}} |
|
||||
> |
|
||||
<h4 style={{ textAlign: 'center', padding: '0 0 3% 0' }}> |
|
||||
Creation de nouvelle année universitaire |
|
||||
</h4> |
|
||||
<Grid container spacing={2}> |
|
||||
<Grid item xs={12} sm={6}> |
|
||||
<TextField |
|
||||
label={'Année universitaire'} |
|
||||
name={'code'} |
|
||||
placeholder="2024-2025" |
|
||||
color="warning" |
|
||||
fullWidth |
|
||||
value={formData.code} |
|
||||
InputProps={{ |
|
||||
startAdornment: ( |
|
||||
<InputAdornment position="start"> |
|
||||
<FaCalendarAlt /> |
|
||||
</InputAdornment> |
|
||||
) |
|
||||
}} |
|
||||
onChange={handleInputChange} |
|
||||
inputRef={codeRef} |
|
||||
className="inputAddNote" |
|
||||
sx={{ |
|
||||
'& .MuiOutlinedInput-root': { |
|
||||
'&:hover fieldset': { |
|
||||
borderColor: '#ff9800' // Set the border color on hover |
|
||||
} |
|
||||
}, |
|
||||
'& .MuiInputBase-input::placeholder': { |
|
||||
fontSize: '14px' // Set the placeholder font size |
|
||||
} |
|
||||
}} |
|
||||
/> |
|
||||
</Grid> |
|
||||
<Grid item xs={12} sm={6}> |
|
||||
<TextField |
|
||||
label={'Date de début'} |
|
||||
name={'debut'} |
|
||||
color="warning" |
|
||||
onChange={handleInputChange} |
|
||||
inputRef={debutRef} |
|
||||
value={formData.debut} |
|
||||
fullWidth |
|
||||
type="date" |
|
||||
InputProps={{ |
|
||||
startAdornment: ( |
|
||||
<InputAdornment position="start"> |
|
||||
<FaCalendarAlt /> |
|
||||
</InputAdornment> |
|
||||
) |
|
||||
}} |
|
||||
className="inputAddNote" |
|
||||
sx={{ |
|
||||
'& .MuiOutlinedInput-root': { |
|
||||
'&:hover fieldset': { |
|
||||
borderColor: '#ff9800' // Set the border color on hover |
|
||||
} |
|
||||
}, |
|
||||
'& .MuiInputBase-input::placeholder': { |
|
||||
fontSize: '11px' // Set the placeholder font size |
|
||||
} |
|
||||
}} |
|
||||
/> |
|
||||
</Grid> |
|
||||
<Grid item xs={12} sm={6}> |
|
||||
<TextField |
|
||||
label={'Date de fin'} |
|
||||
name={'fin'} |
|
||||
color="warning" |
|
||||
fullWidth |
|
||||
type="date" |
|
||||
InputProps={{ |
|
||||
startAdornment: ( |
|
||||
<InputAdornment position="start"> |
|
||||
<FaCalendarAlt /> |
|
||||
</InputAdornment> |
|
||||
) |
|
||||
}} |
|
||||
onChange={handleInputChange} |
|
||||
inputRef={finRef} |
|
||||
value={formData.fin} |
|
||||
className="inputAddNote" |
|
||||
sx={{ |
|
||||
'& .MuiOutlinedInput-root': { |
|
||||
'&:hover fieldset': { |
|
||||
borderColor: '#ff9800' // Set the border color on hover |
|
||||
} |
|
||||
}, |
|
||||
'& .MuiInputBase-input::placeholder': { |
|
||||
fontSize: '11px' // Set the placeholder font size |
|
||||
} |
|
||||
}} |
|
||||
/> |
|
||||
</Grid> |
|
||||
<Grid |
|
||||
item |
|
||||
xs={12} |
|
||||
style={{ display: 'flex', gap: '30px', justifyContent: 'flex-end' }} |
|
||||
> |
|
||||
<Button type="submit" color="warning" variant="contained"> |
|
||||
Enregister |
|
||||
</Button> |
|
||||
</Grid> |
|
||||
</Grid> |
|
||||
</form> |
|
||||
</Paper> |
|
||||
</div> |
|
||||
</div> |
|
||||
) |
|
||||
} |
|
||||
|
|
||||
export default AddAnneeScolaire |
|
||||
@ -1,413 +0,0 @@ |
|||||
import React, { useEffect, useRef, useState } from 'react' |
|
||||
import classe from '../assets/AllStyleComponents.module.css' |
|
||||
import classeAdd from '../assets/AddStudent.module.css' |
|
||||
import classeHome from '../assets/Home.module.css' |
|
||||
import { Link, useNavigate } from 'react-router-dom' |
|
||||
import { IoMdReturnRight } from 'react-icons/io' |
|
||||
import { |
|
||||
Box, |
|
||||
Button, |
|
||||
InputAdornment, |
|
||||
Typography, |
|
||||
Modal, |
|
||||
TextField, |
|
||||
Grid, |
|
||||
Autocomplete |
|
||||
} from '@mui/material' |
|
||||
import { BsBookmarkPlusFill } from 'react-icons/bs' |
|
||||
import { FaBook, FaClipboardList, FaClock, FaFileExcel } from 'react-icons/fa' |
|
||||
import validationMatiereAdd from './validation/ValidationMatiereAdd' |
|
||||
import svgSuccess from '../assets/success.svg' |
|
||||
import ChangeCapitalize from './function/ChangeCapitalizeLetter' |
|
||||
import { MdOutlineNumbers } from 'react-icons/md' |
|
||||
import { IoBookmark } from 'react-icons/io5' |
|
||||
import ChangeCapital from './function/ChangeCapitalLetter' |
|
||||
import { CgPathUnite } from 'react-icons/cg' |
|
||||
|
|
||||
const AddMatiere = () => { |
|
||||
const [formData, setFormData] = useState({ |
|
||||
nom: '', |
|
||||
credit: '', |
|
||||
uniter: '', |
|
||||
ue: '' |
|
||||
}) |
|
||||
|
|
||||
const navigate = useNavigate() |
|
||||
|
|
||||
const [matieres, setMatieres] = useState([]) |
|
||||
const [mention, setMention] = useState([]) |
|
||||
|
|
||||
useEffect(() => { |
|
||||
window.matieres.getMatiere().then((response) => { |
|
||||
setMatieres(response) |
|
||||
}) |
|
||||
|
|
||||
window.mention.getMention().then((response) => { |
|
||||
setMention(Array.isArray(response) ? response : []) |
|
||||
}) |
|
||||
}, []) |
|
||||
|
|
||||
/** |
|
||||
* hook to open modal |
|
||||
*/ |
|
||||
const [open, setOpen] = useState(false) |
|
||||
|
|
||||
/** |
|
||||
* function to close modal |
|
||||
*/ |
|
||||
const handleClose = () => { |
|
||||
setOpen(false) |
|
||||
navigate('/matiere') |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* function to set the data in state |
|
||||
* @param {*} e |
|
||||
*/ |
|
||||
const handleInputChange = (e) => { |
|
||||
const { name, value } = e.target |
|
||||
setFormData((prevData) => ({ |
|
||||
...prevData, |
|
||||
[name]: value |
|
||||
})) |
|
||||
} |
|
||||
|
|
||||
const nomRef = useRef() |
|
||||
const errorRef = useRef() |
|
||||
const creditRef = useRef() |
|
||||
const ueRef = useRef() |
|
||||
const uniterRef = useRef() |
|
||||
|
|
||||
const formSubmit = async (e) => { |
|
||||
e.preventDefault() |
|
||||
|
|
||||
let arrayMatiere = [] |
|
||||
|
|
||||
matieres.map((matiere) => { |
|
||||
arrayMatiere.push(matiere.nom) |
|
||||
}) |
|
||||
|
|
||||
let valid = validationMatiereAdd( |
|
||||
nomRef.current, |
|
||||
errorRef.current, |
|
||||
arrayMatiere, |
|
||||
creditRef.current |
|
||||
) |
|
||||
console.log(formData, valid) |
|
||||
if (valid) { |
|
||||
let response = await window.matieres.createMatiere(formData) |
|
||||
console.log(response) |
|
||||
if (response.success) { |
|
||||
setOpen(true) |
|
||||
} |
|
||||
|
|
||||
if (response.code) { |
|
||||
errorRef.current.textContent = `${formData.nom} existe déjà` |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* function to return the view Modal |
|
||||
* |
|
||||
* @returns {JSX} |
|
||||
*/ |
|
||||
const modals = () => ( |
|
||||
<Modal |
|
||||
open={open} |
|
||||
onClose={handleClose} |
|
||||
aria-labelledby="modal-title" |
|
||||
aria-describedby="modal-description" |
|
||||
> |
|
||||
<Box |
|
||||
sx={{ |
|
||||
position: 'absolute', |
|
||||
top: '50%', |
|
||||
left: '50%', |
|
||||
transform: 'translate(-50%, -50%)', |
|
||||
width: 450, |
|
||||
bgcolor: 'background.paper', |
|
||||
boxShadow: 24, |
|
||||
p: 4 |
|
||||
}} |
|
||||
> |
|
||||
<Typography style={{ display: 'flex', flexDirection: 'column', alignItems: 'center' }}> |
|
||||
<img src={svgSuccess} alt="" width={50} height={50} />{' '} |
|
||||
<span>Matière insérer avec succes</span> |
|
||||
</Typography> |
|
||||
<Box |
|
||||
sx={{ |
|
||||
marginTop: '2%', |
|
||||
display: 'flex', |
|
||||
gap: '20px', |
|
||||
alignItems: 'end', |
|
||||
justifyContent: 'flex-end' |
|
||||
}} |
|
||||
> |
|
||||
<Button onClick={handleClose} color="warning" variant="contained"> |
|
||||
OK |
|
||||
</Button> |
|
||||
</Box> |
|
||||
</Box> |
|
||||
</Modal> |
|
||||
) |
|
||||
|
|
||||
return ( |
|
||||
<div className={classe.mainHome}> |
|
||||
{modals()} |
|
||||
<div className={classeAdd.header}> |
|
||||
<div className={classe.h1style}> |
|
||||
<div className={classeHome.blockTitle}> |
|
||||
<h1 style={{ display: 'flex', alignItems: 'center', gap: '10px' }}> |
|
||||
<BsBookmarkPlusFill /> |
|
||||
Ajout Matière |
|
||||
</h1> |
|
||||
<Link to={'/matiere'}> |
|
||||
<Button color="warning" variant="contained"> |
|
||||
<IoMdReturnRight style={{ fontSize: '20px' }} /> |
|
||||
</Button> |
|
||||
</Link> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
|
|
||||
{/* displaying the formulaire */} |
|
||||
<div className={classeHome.boxEtudiantsCard}> |
|
||||
<Box |
|
||||
sx={{ |
|
||||
position: 'absolute', |
|
||||
top: '55%', |
|
||||
left: '50%', |
|
||||
transform: 'translate(-50%, -50%)', |
|
||||
width: 700, |
|
||||
borderRadius: '2%', |
|
||||
bgcolor: 'background.paper', |
|
||||
boxShadow: 24, |
|
||||
p: 4 |
|
||||
}} |
|
||||
> |
|
||||
<div style={{ textAlign: 'right', fontWeight: 'bold' }}> |
|
||||
<Link to={'/addmatiere/import'} style={{ textDecoration: 'none', color: 'orange' }}> |
|
||||
Importer un fichier excel <FaFileExcel /> |
|
||||
</Link> |
|
||||
</div> |
|
||||
<h3 style={{ textAlign: 'center', paddingBottom: '15px', textDecoration: 'underline' }}> |
|
||||
Ajout d'un Matière |
|
||||
</h3> |
|
||||
<Box |
|
||||
sx={{ |
|
||||
marginTop: '2%', |
|
||||
width: '100%' |
|
||||
}} |
|
||||
> |
|
||||
<form onSubmit={formSubmit}> |
|
||||
<Grid container spacing={2}> |
|
||||
<Grid item xs={12} sm={6}> |
|
||||
<TextField |
|
||||
label="Nom" |
|
||||
name="nom" |
|
||||
color="warning" |
|
||||
fullWidth |
|
||||
placeholder="Nom du matière" |
|
||||
value={formData.nom} |
|
||||
onChange={handleInputChange} |
|
||||
onInput={() => ChangeCapitalize(nomRef)} |
|
||||
InputProps={{ |
|
||||
startAdornment: ( |
|
||||
<InputAdornment position="start"> |
|
||||
<FaBook /> |
|
||||
</InputAdornment> |
|
||||
) |
|
||||
}} |
|
||||
inputRef={nomRef} |
|
||||
sx={{ |
|
||||
marginBottom: '5px', |
|
||||
'& .MuiOutlinedInput-root': { |
|
||||
'&:hover fieldset': { |
|
||||
borderColor: '#ff9800' // Set the border color on hover |
|
||||
} |
|
||||
} |
|
||||
}} |
|
||||
/> |
|
||||
</Grid> |
|
||||
<Grid item xs={12} sm={6}> |
|
||||
<TextField |
|
||||
label="Crédit" |
|
||||
name="credit" |
|
||||
color="warning" |
|
||||
fullWidth |
|
||||
placeholder="Crédit pour la matiere" |
|
||||
type="number" |
|
||||
value={formData.credit} |
|
||||
onChange={handleInputChange} |
|
||||
InputProps={{ |
|
||||
startAdornment: ( |
|
||||
<InputAdornment position="start"> |
|
||||
<MdOutlineNumbers /> |
|
||||
</InputAdornment> |
|
||||
) |
|
||||
}} |
|
||||
inputProps={{ min: 1 }} |
|
||||
inputRef={creditRef} |
|
||||
sx={{ |
|
||||
marginBottom: '5px', |
|
||||
'& .MuiOutlinedInput-root': { |
|
||||
'&:hover fieldset': { |
|
||||
borderColor: '#ff9800' // Set the border color on hover |
|
||||
} |
|
||||
} |
|
||||
}} |
|
||||
/> |
|
||||
</Grid> |
|
||||
{/* <Grid item xs={12} sm={6}> |
|
||||
<TextField |
|
||||
label="Semestre" |
|
||||
name="semestre" |
|
||||
color='warning' |
|
||||
fullWidth |
|
||||
placeholder='exemple S1, S2, S3, S4' |
|
||||
type='text' |
|
||||
value={formData.semestre} |
|
||||
onChange={handleInputChange} |
|
||||
InputProps={{ |
|
||||
startAdornment: ( |
|
||||
<InputAdornment position="start"> |
|
||||
<CiCalendar /> |
|
||||
</InputAdornment> |
|
||||
), |
|
||||
}} |
|
||||
inputProps={{ min: 1 }} |
|
||||
inputRef={semestreRef} |
|
||||
sx={{ |
|
||||
marginBottom:"5px", |
|
||||
'& .MuiOutlinedInput-root': { |
|
||||
'&:hover fieldset': { |
|
||||
borderColor: '#ff9800', // Set the border color on hover |
|
||||
}, |
|
||||
}, |
|
||||
}} |
|
||||
/> |
|
||||
</Grid> */} |
|
||||
<Grid item xs={12} sm={6}> |
|
||||
<TextField |
|
||||
label="Unité d'enseignement" |
|
||||
name="uniter" |
|
||||
color="warning" |
|
||||
fullWidth |
|
||||
placeholder="Le matiere sera dans quelle unité d'enseignement" |
|
||||
value={formData.uniter} |
|
||||
onChange={handleInputChange} |
|
||||
onInput={() => ChangeCapital(uniterRef)} |
|
||||
InputProps={{ |
|
||||
startAdornment: ( |
|
||||
<InputAdornment position="start"> |
|
||||
<IoBookmark /> |
|
||||
</InputAdornment> |
|
||||
) |
|
||||
}} |
|
||||
inputProps={{ min: 1 }} |
|
||||
inputRef={uniterRef} |
|
||||
sx={{ |
|
||||
marginBottom: '5px', |
|
||||
'& .MuiOutlinedInput-root': { |
|
||||
'&:hover fieldset': { |
|
||||
borderColor: '#ff9800' // Set the border color on hover |
|
||||
} |
|
||||
} |
|
||||
}} |
|
||||
/> |
|
||||
</Grid> |
|
||||
<Grid item xs={12} sm={6}> |
|
||||
<TextField |
|
||||
label="UE" |
|
||||
name="ue" |
|
||||
color="warning" |
|
||||
fullWidth |
|
||||
placeholder="TI2" |
|
||||
value={formData.ue} |
|
||||
onInput={() => ChangeCapital(ueRef)} |
|
||||
onChange={handleInputChange} |
|
||||
type="text" |
|
||||
InputProps={{ |
|
||||
startAdornment: ( |
|
||||
<InputAdornment position="start"> |
|
||||
<CgPathUnite /> |
|
||||
</InputAdornment> |
|
||||
) |
|
||||
}} |
|
||||
inputProps={{ min: 1 }} |
|
||||
inputRef={ueRef} |
|
||||
sx={{ |
|
||||
marginBottom: '5px', |
|
||||
'& .MuiOutlinedInput-root': { |
|
||||
'&:hover fieldset': { |
|
||||
borderColor: '#ff9800' // Set the border color on hover |
|
||||
} |
|
||||
} |
|
||||
}} |
|
||||
/> |
|
||||
</Grid> |
|
||||
{/* <Grid item xs={12} sm={6}> |
|
||||
<Autocomplete |
|
||||
multiple |
|
||||
options={mention} // Options array for Autocomplete |
|
||||
getOptionLabel={(option) => option.nom || ""} // Safely access `nom` |
|
||||
value={formData.mention_id.map(id => mention.find(item => item.id === id)) || []} // Bind selected values to form data |
|
||||
onChange={(event, newValue) => { |
|
||||
setFormData((prevData) => ({ |
|
||||
...prevData, |
|
||||
mention_id: newValue.map((item) => item.id), // Store only the IDs of selected mentions |
|
||||
})); |
|
||||
}} |
|
||||
isOptionEqualToValue={(option, value) => option.id === value.id} // Ensure correct matching of options |
|
||||
renderInput={(params) => ( |
|
||||
<TextField |
|
||||
{...params} |
|
||||
label="Mention" |
|
||||
color="warning" |
|
||||
placeholder="Sélectionnez les mentions" |
|
||||
InputProps={{ |
|
||||
...params.InputProps, |
|
||||
startAdornment: ( |
|
||||
<> |
|
||||
<InputAdornment position="start"> |
|
||||
<FaClipboardList /> |
|
||||
</InputAdornment> |
|
||||
{params.InputProps.startAdornment} |
|
||||
</> |
|
||||
), |
|
||||
}} |
|
||||
sx={{ |
|
||||
marginBottom: "5px", |
|
||||
'& .MuiOutlinedInput-root': { |
|
||||
'&:hover fieldset': { |
|
||||
borderColor: '#ff9800', // Set border color on hover |
|
||||
}, |
|
||||
}, |
|
||||
}} |
|
||||
/> |
|
||||
)} |
|
||||
/> |
|
||||
|
|
||||
</Grid> */} |
|
||||
</Grid> |
|
||||
<span style={{ color: 'red', marginBottom: '15px' }} ref={errorRef}></span> |
|
||||
<br /> |
|
||||
<Grid |
|
||||
item |
|
||||
xs={12} |
|
||||
style={{ display: 'flex', gap: '30px', justifyContent: 'flex-end' }} |
|
||||
> |
|
||||
<Button type="submit" color="warning" variant="contained"> |
|
||||
Enregister |
|
||||
</Button> |
|
||||
</Grid> |
|
||||
</form> |
|
||||
</Box> |
|
||||
</Box> |
|
||||
</div> |
|
||||
</div> |
|
||||
) |
|
||||
} |
|
||||
|
|
||||
export default AddMatiere |
|
||||
@ -1,257 +0,0 @@ |
|||||
import React, { useEffect, useRef, useState } from 'react' |
|
||||
import classe from '../assets/AllStyleComponents.module.css' |
|
||||
import classeAdd from '../assets/AddStudent.module.css' |
|
||||
import classeHome from '../assets/Home.module.css' |
|
||||
import { Link, useNavigate } from 'react-router-dom' |
|
||||
import { IoMdReturnRight } from 'react-icons/io' |
|
||||
import { Box, Button, InputAdornment, Typography, Modal, TextField, Grid } from '@mui/material' |
|
||||
import { BsBookmarkPlusFill } from 'react-icons/bs' |
|
||||
import { FaClipboardList } from 'react-icons/fa6' |
|
||||
import ChangeCapital from './function/ChangeCapitalLetter' |
|
||||
import { IoBookmark } from 'react-icons/io5' |
|
||||
import svgSuccess from '../assets/success.svg' |
|
||||
|
|
||||
const AddMention = () => { |
|
||||
const [formData, setFormData] = useState({ |
|
||||
nom: '', |
|
||||
uniter: '' |
|
||||
}) |
|
||||
|
|
||||
const [errors, setErrors] = useState({ |
|
||||
nom: false, |
|
||||
uniter: false |
|
||||
}) |
|
||||
|
|
||||
const navigate = useNavigate() |
|
||||
|
|
||||
/** |
|
||||
* function to set the data in state |
|
||||
* @param {*} e |
|
||||
*/ |
|
||||
const handleInputChange = (e) => { |
|
||||
const { name, value } = e.target |
|
||||
setFormData({ |
|
||||
...formData, |
|
||||
[name]: value |
|
||||
}) |
|
||||
setErrors({ |
|
||||
...errors, |
|
||||
[name]: false // Reset the error when user starts typing |
|
||||
}) |
|
||||
} |
|
||||
|
|
||||
const formSubmit = async (e) => { |
|
||||
e.preventDefault() |
|
||||
const newErrors = {} |
|
||||
let hasError = false |
|
||||
|
|
||||
// Check for empty fields |
|
||||
Object.keys(formData).forEach((key) => { |
|
||||
if (!formData[key].trim()) { |
|
||||
newErrors[key] = true // Set error for empty fields |
|
||||
hasError = true |
|
||||
} |
|
||||
}) |
|
||||
|
|
||||
setErrors(newErrors) |
|
||||
|
|
||||
if (!hasError) { |
|
||||
try { |
|
||||
let response = await window.mention.createMention(formData) |
|
||||
|
|
||||
if (response.success) { |
|
||||
setOpen(true) |
|
||||
} |
|
||||
} catch (error) { |
|
||||
console.log(error) |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* hook to open modal |
|
||||
*/ |
|
||||
const [open, setOpen] = useState(false) |
|
||||
|
|
||||
/** |
|
||||
* function to close modal |
|
||||
*/ |
|
||||
const handleClose = () => { |
|
||||
setOpen(false) |
|
||||
navigate('/mention') |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* function to return the view Modal |
|
||||
* |
|
||||
* @returns {JSX} |
|
||||
*/ |
|
||||
const modals = () => ( |
|
||||
<Modal |
|
||||
open={open} |
|
||||
onClose={handleClose} |
|
||||
aria-labelledby="modal-title" |
|
||||
aria-describedby="modal-description" |
|
||||
> |
|
||||
<Box |
|
||||
sx={{ |
|
||||
position: 'absolute', |
|
||||
top: '50%', |
|
||||
left: '50%', |
|
||||
transform: 'translate(-50%, -50%)', |
|
||||
width: 450, |
|
||||
bgcolor: 'background.paper', |
|
||||
boxShadow: 24, |
|
||||
p: 4 |
|
||||
}} |
|
||||
> |
|
||||
<Typography style={{ display: 'flex', flexDirection: 'column', alignItems: 'center' }}> |
|
||||
<img src={svgSuccess} alt="" width={50} height={50} />{' '} |
|
||||
<span>Mention insérer avec succes</span> |
|
||||
</Typography> |
|
||||
<Box |
|
||||
sx={{ |
|
||||
marginTop: '2%', |
|
||||
display: 'flex', |
|
||||
gap: '20px', |
|
||||
alignItems: 'end', |
|
||||
justifyContent: 'flex-end' |
|
||||
}} |
|
||||
> |
|
||||
<Button onClick={handleClose} color="warning" variant="contained"> |
|
||||
OK |
|
||||
</Button> |
|
||||
</Box> |
|
||||
</Box> |
|
||||
</Modal> |
|
||||
) |
|
||||
|
|
||||
// Helper function to get helperText dynamically |
|
||||
const getHelperText = (field) => (errors[field] ? 'Ce champ est requis.' : '') |
|
||||
|
|
||||
const nomRef = useRef() |
|
||||
const uniterRef = useRef() |
|
||||
return ( |
|
||||
<div className={classe.mainHome}> |
|
||||
{modals()} |
|
||||
<div className={classeAdd.header}> |
|
||||
<div className={classe.h1style}> |
|
||||
<div className={classeHome.blockTitle}> |
|
||||
<h1 style={{ display: 'flex', alignItems: 'center', gap: '10px' }}> |
|
||||
<BsBookmarkPlusFill /> |
|
||||
Ajout Mention |
|
||||
</h1> |
|
||||
<Link to={'/mention'}> |
|
||||
<Button color="warning" variant="contained"> |
|
||||
<IoMdReturnRight style={{ fontSize: '20px' }} /> |
|
||||
</Button> |
|
||||
</Link> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
|
|
||||
{/* displaying the formulaire */} |
|
||||
<div className={classeHome.boxEtudiantsCard}> |
|
||||
<Box |
|
||||
sx={{ |
|
||||
position: 'absolute', |
|
||||
top: '55%', |
|
||||
left: '50%', |
|
||||
transform: 'translate(-50%, -50%)', |
|
||||
width: 700, |
|
||||
borderRadius: '2%', |
|
||||
bgcolor: 'background.paper', |
|
||||
boxShadow: 24, |
|
||||
p: 4 |
|
||||
}} |
|
||||
> |
|
||||
<h3 style={{ textAlign: 'center', paddingBottom: '15px', textDecoration: 'underline' }}> |
|
||||
Ajout d'un Mention |
|
||||
</h3> |
|
||||
<Box |
|
||||
sx={{ |
|
||||
marginTop: '2%', |
|
||||
width: '100%' |
|
||||
}} |
|
||||
> |
|
||||
<form onSubmit={formSubmit}> |
|
||||
<Grid container spacing={2}> |
|
||||
<Grid item xs={12} sm={6}> |
|
||||
<TextField |
|
||||
label="Nom" |
|
||||
error={errors.nom} |
|
||||
name="nom" |
|
||||
color="warning" |
|
||||
fullWidth |
|
||||
placeholder="GENIE DES MINES" |
|
||||
value={formData.nom} |
|
||||
onChange={handleInputChange} |
|
||||
onInput={() => ChangeCapital(nomRef)} |
|
||||
InputProps={{ |
|
||||
startAdornment: ( |
|
||||
<InputAdornment position="start"> |
|
||||
<FaClipboardList /> |
|
||||
</InputAdornment> |
|
||||
) |
|
||||
}} |
|
||||
helperText={getHelperText('nom')} |
|
||||
inputRef={nomRef} |
|
||||
sx={{ |
|
||||
marginBottom: '5px', |
|
||||
'& .MuiOutlinedInput-root': { |
|
||||
'&:hover fieldset': { |
|
||||
borderColor: '#ff9800' // Set the border color on hover |
|
||||
} |
|
||||
} |
|
||||
}} |
|
||||
/> |
|
||||
</Grid> |
|
||||
<Grid item xs={12} sm={6}> |
|
||||
<TextField |
|
||||
label="Unité" |
|
||||
error={errors.uniter} |
|
||||
helperText={getHelperText('uniter')} |
|
||||
name="uniter" |
|
||||
color="warning" |
|
||||
fullWidth |
|
||||
placeholder="GM" |
|
||||
value={formData.uniter} |
|
||||
onChange={handleInputChange} |
|
||||
onInput={() => ChangeCapital(uniterRef)} |
|
||||
InputProps={{ |
|
||||
startAdornment: ( |
|
||||
<InputAdornment position="start"> |
|
||||
<IoBookmark /> |
|
||||
</InputAdornment> |
|
||||
) |
|
||||
}} |
|
||||
inputRef={uniterRef} |
|
||||
sx={{ |
|
||||
marginBottom: '5px', |
|
||||
'& .MuiOutlinedInput-root': { |
|
||||
'&:hover fieldset': { |
|
||||
borderColor: '#ff9800' // Set the border color on hover |
|
||||
} |
|
||||
} |
|
||||
}} |
|
||||
/> |
|
||||
</Grid> |
|
||||
<Grid |
|
||||
item |
|
||||
xs={12} |
|
||||
style={{ display: 'flex', gap: '30px', justifyContent: 'flex-end' }} |
|
||||
> |
|
||||
<Button type="submit" color="warning" variant="contained"> |
|
||||
Enregister |
|
||||
</Button> |
|
||||
</Grid> |
|
||||
</Grid> |
|
||||
</form> |
|
||||
</Box> |
|
||||
</Box> |
|
||||
</div> |
|
||||
</div> |
|
||||
) |
|
||||
} |
|
||||
|
|
||||
export default AddMention |
|
||||
@ -1,214 +0,0 @@ |
|||||
import React, { useEffect, useRef, useState } from 'react' |
|
||||
import classe from '../assets/AllStyleComponents.module.css' |
|
||||
import classeAdd from '../assets/AddStudent.module.css' |
|
||||
import { Link, useNavigate } from 'react-router-dom' |
|
||||
import { IoMdPersonAdd, IoMdReturnRight } from 'react-icons/io' |
|
||||
import { FaFileExcel, FaUser } from 'react-icons/fa' |
|
||||
import { Box, Button, InputAdornment, TextField, Grid, Modal, Typography } from '@mui/material' |
|
||||
import classeHome from '../assets/Home.module.css' |
|
||||
import validationNote from './validation/AddNiveau' |
|
||||
import svgSuccess from '../assets/success.svg' |
|
||||
|
|
||||
const AddNiveau = () => { |
|
||||
const navigate = useNavigate() |
|
||||
/** |
|
||||
* hook for storing data in the input |
|
||||
*/ |
|
||||
const [formData, setFormData] = useState({ |
|
||||
nom: '' |
|
||||
}) |
|
||||
const [niveau, setNiveau] = useState([]) |
|
||||
/** |
|
||||
* hook to open modal |
|
||||
*/ |
|
||||
const [open, setOpen] = useState(false) |
|
||||
|
|
||||
/** |
|
||||
* function to close modal |
|
||||
*/ |
|
||||
const handleClose = () => { |
|
||||
setOpen(false) |
|
||||
navigate('/niveau') |
|
||||
} |
|
||||
|
|
||||
useEffect(() => { |
|
||||
window.niveaus.getNiveau().then((response) => { |
|
||||
setNiveau(response) |
|
||||
}) |
|
||||
}, []) |
|
||||
|
|
||||
/** |
|
||||
* function to set the data in state |
|
||||
* @param {*} e |
|
||||
*/ |
|
||||
const handleInputChange = (e) => { |
|
||||
const { name, value } = e.target |
|
||||
setFormData((prevData) => ({ |
|
||||
...prevData, |
|
||||
[name]: value |
|
||||
})) |
|
||||
} |
|
||||
|
|
||||
const nomRef = useRef() |
|
||||
const nomError = useRef() |
|
||||
|
|
||||
/** |
|
||||
* function to return the view Modal |
|
||||
* |
|
||||
* @returns {JSX} |
|
||||
*/ |
|
||||
const modals = () => ( |
|
||||
<Modal |
|
||||
open={open} |
|
||||
onClose={handleClose} |
|
||||
aria-labelledby="modal-title" |
|
||||
aria-describedby="modal-description" |
|
||||
> |
|
||||
<Box |
|
||||
sx={{ |
|
||||
position: 'absolute', |
|
||||
top: '50%', |
|
||||
left: '50%', |
|
||||
transform: 'translate(-50%, -50%)', |
|
||||
width: 450, |
|
||||
bgcolor: 'background.paper', |
|
||||
boxShadow: 24, |
|
||||
p: 4 |
|
||||
}} |
|
||||
> |
|
||||
<Typography style={{ display: 'flex', alignItems: 'center', flexDirection: 'column' }}> |
|
||||
<img src={svgSuccess} alt="" width={50} height={50} />{' '} |
|
||||
<span>Insertion a été effectuée avec succès</span> |
|
||||
</Typography> |
|
||||
<Box |
|
||||
sx={{ |
|
||||
marginTop: '2%', |
|
||||
display: 'flex', |
|
||||
gap: '20px', |
|
||||
alignItems: 'end', |
|
||||
justifyContent: 'flex-end' |
|
||||
}} |
|
||||
> |
|
||||
<Button onClick={handleClose} color="warning" variant="contained"> |
|
||||
OK |
|
||||
</Button> |
|
||||
</Box> |
|
||||
</Box> |
|
||||
</Modal> |
|
||||
) |
|
||||
|
|
||||
const formSubmit = async (e) => { |
|
||||
e.preventDefault() |
|
||||
let niveauNom = [] |
|
||||
niveau.map((niv) => { |
|
||||
niveauNom.push(niv.nom) |
|
||||
}) |
|
||||
let validation = validationNote(nomRef.current, nomError.current, niveauNom) |
|
||||
|
|
||||
if (validation) { |
|
||||
let response = await window.niveaus.insertNiveau(formData) |
|
||||
|
|
||||
let responses = JSON.parse(response) |
|
||||
if (responses.changes == 1) { |
|
||||
setOpen(true) |
|
||||
setFormData({ |
|
||||
nom: '' |
|
||||
}) |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
return ( |
|
||||
<div className={classe.mainHome}> |
|
||||
{modals()} |
|
||||
<div className={classeAdd.header}> |
|
||||
<div className={classe.h1style}> |
|
||||
<div className={classeHome.blockTitle}> |
|
||||
<h1 style={{ display: 'flex', alignItems: 'center', gap: '10px' }}> |
|
||||
<IoMdPersonAdd /> |
|
||||
Ajout niveau |
|
||||
</h1> |
|
||||
<Link to={'/niveau'}> |
|
||||
<Button color="warning" variant="contained"> |
|
||||
<IoMdReturnRight style={{ fontSize: '20px' }} /> |
|
||||
</Button> |
|
||||
</Link> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
|
|
||||
<div className={classeAdd.boxEtudiantsCard}> |
|
||||
<Box |
|
||||
sx={{ |
|
||||
position: 'absolute', |
|
||||
top: '50%', |
|
||||
left: '50%', |
|
||||
transform: 'translate(-50%, -50%)', |
|
||||
width: 700, |
|
||||
borderRadius: '2%', |
|
||||
bgcolor: 'background.paper', |
|
||||
boxShadow: 24, |
|
||||
p: 4 |
|
||||
}} |
|
||||
> |
|
||||
<h3 style={{ textAlign: 'center', paddingBottom: '15px', textDecoration: 'underline' }}> |
|
||||
Ajout d'un niveau |
|
||||
</h3> |
|
||||
<div style={{ textAlign: 'right', fontWeight: 'bold' }}> |
|
||||
<Link to={'/importniveau'} style={{ textDecoration: 'none', color: 'orange' }}> |
|
||||
Importer un fichier excel <FaFileExcel /> |
|
||||
</Link> |
|
||||
</div> |
|
||||
<Box |
|
||||
sx={{ |
|
||||
marginTop: '2%', |
|
||||
width: '100%' |
|
||||
}} |
|
||||
> |
|
||||
<form onSubmit={formSubmit}> |
|
||||
<TextField |
|
||||
label="Nom" |
|
||||
name="nom" |
|
||||
color="warning" |
|
||||
fullWidth |
|
||||
placeholder="L1 ou L2 ou L3 ou M1 ou M2 ou D1 ou D2 ou D3" |
|
||||
value={formData.nom} |
|
||||
onChange={handleInputChange} |
|
||||
InputProps={{ |
|
||||
startAdornment: ( |
|
||||
<InputAdornment position="start"> |
|
||||
<FaUser /> |
|
||||
</InputAdornment> |
|
||||
) |
|
||||
}} |
|
||||
inputRef={nomRef} |
|
||||
sx={{ |
|
||||
marginBottom: '5px', |
|
||||
'& .MuiOutlinedInput-root': { |
|
||||
'&:hover fieldset': { |
|
||||
borderColor: '#ff9800' // Set the border color on hover |
|
||||
} |
|
||||
} |
|
||||
}} |
|
||||
/> |
|
||||
<span style={{ color: 'red', marginBottom: '15px' }} ref={nomError}></span> |
|
||||
<br /> |
|
||||
{/* Submit Button */} |
|
||||
<Grid |
|
||||
item |
|
||||
xs={12} |
|
||||
style={{ display: 'flex', gap: '30px', justifyContent: 'flex-end' }} |
|
||||
> |
|
||||
<Button type="submit" color="warning" variant="contained"> |
|
||||
Enregister |
|
||||
</Button> |
|
||||
</Grid> |
|
||||
</form> |
|
||||
</Box> |
|
||||
</Box> |
|
||||
</div> |
|
||||
</div> |
|
||||
) |
|
||||
} |
|
||||
|
|
||||
export default AddNiveau |
|
||||
@ -1,312 +0,0 @@ |
|||||
import React, { useEffect, useRef, useState } from 'react' |
|
||||
import classe from '../assets/AllStyleComponents.module.css' |
|
||||
import classeAdd from '../assets/AddNotes.module.css' |
|
||||
import classeHome from '../assets/Home.module.css' |
|
||||
import { Modal, Box, Typography, Button, InputAdornment, TextField, Grid } from '@mui/material' |
|
||||
import { CgNotes } from 'react-icons/cg' |
|
||||
import { IoMdReturnRight } from 'react-icons/io' |
|
||||
import { Link, useParams, useNavigate } from 'react-router-dom' |
|
||||
import svgSuccess from '../assets/success.svg' |
|
||||
import svgError from '../assets/error.svg' |
|
||||
import validateAddNote from './validation/AddNote' |
|
||||
import ModalUpdateParcoursEtudiant from './ModalUpdateParcoursEtudiant' |
|
||||
|
|
||||
const AddNotes = () => { |
|
||||
const { id, niveau, mention_id, parcours } = useParams() |
|
||||
const [matieres, setMatieres] = useState([]) |
|
||||
const [formData, setFormData] = useState({}) // Initialize with an empty object |
|
||||
const [etudiants, setEtudiants] = useState([]) |
|
||||
const navigate = useNavigate() |
|
||||
const [openModal1, setOpenModal1] = useState(false) |
|
||||
const oncloseModal1 = () => { |
|
||||
setOpenModal1(false) |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* Fetching the matieres |
|
||||
*/ |
|
||||
useEffect(() => { |
|
||||
window.matieres.displayMatiereFromForm({ niveau, mention_id, parcours }).then((response) => { |
|
||||
setMatieres(response) |
|
||||
}) |
|
||||
|
|
||||
window.etudiants.getSingle({ id }).then((response) => { |
|
||||
setEtudiants(response) |
|
||||
}) |
|
||||
|
|
||||
if (parcours == 'Pas de parcours' && niveau != 'L1') { |
|
||||
setOpenModal1(true) |
|
||||
} |
|
||||
}, []) |
|
||||
|
|
||||
const [isSubmitted, setIsSubmitted] = useState(false) |
|
||||
const [parcoursChange, setParcourChanges] = useState('') |
|
||||
const handleFormSubmit = (status, parcours) => { |
|
||||
setIsSubmitted(status) |
|
||||
setParcourChanges(parcours) |
|
||||
} |
|
||||
|
|
||||
useEffect(() => { |
|
||||
if (isSubmitted) { |
|
||||
let parcours = parcoursChange |
|
||||
|
|
||||
window.matieres.displayMatiereFromForm({ niveau, mention_id, parcours }).then((response) => { |
|
||||
setMatieres(response) |
|
||||
}) |
|
||||
setIsSubmitted(false) |
|
||||
} |
|
||||
}, [isSubmitted]) |
|
||||
|
|
||||
let niveauEtudiant = etudiants.niveau |
|
||||
let AnneeScolaireEtudiant = etudiants.annee_scolaire |
|
||||
|
|
||||
/** |
|
||||
* Update formData whenever matieres change |
|
||||
*/ |
|
||||
useEffect(() => { |
|
||||
const initialFormData = matieres.reduce((acc, mat) => { |
|
||||
acc[mat.id] = '' // Initialize each key with an empty string |
|
||||
return acc |
|
||||
}, {}) |
|
||||
setFormData(initialFormData) |
|
||||
}, [matieres]) // Dependency array ensures this runs whenever `matieres` is updated |
|
||||
|
|
||||
const [disabled, setDisabled] = useState(false) |
|
||||
|
|
||||
/** |
|
||||
* Handle form submission |
|
||||
*/ |
|
||||
const handleSubmit = async (e) => { |
|
||||
e.preventDefault() |
|
||||
const etudiant_id = id |
|
||||
const etudiant_niveau = niveauEtudiant |
|
||||
let valid = validateAddNote() |
|
||||
let annee_scolaire = AnneeScolaireEtudiant |
|
||||
let mention_id = etudiants.mention_id |
|
||||
|
|
||||
if (valid) { |
|
||||
let response = await window.notes.insertNote({ |
|
||||
etudiant_id, |
|
||||
etudiant_niveau, |
|
||||
mention_id, |
|
||||
formData, |
|
||||
annee_scolaire |
|
||||
}) |
|
||||
console.log(response) |
|
||||
if (response.success) { |
|
||||
setOpen(true) |
|
||||
setStatut(200) |
|
||||
setDisabled(true) |
|
||||
const resetFormData = matieres.reduce((acc, mat) => { |
|
||||
acc[mat.id] = '' // Reset each field to an empty string |
|
||||
return acc |
|
||||
}, {}) |
|
||||
setFormData(resetFormData) |
|
||||
} |
|
||||
} else { |
|
||||
setOpen(true) |
|
||||
setStatut(400) |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
const [statut, setStatut] = useState(200) |
|
||||
|
|
||||
/** |
|
||||
* hook to open modal |
|
||||
*/ |
|
||||
const [open, setOpen] = useState(false) |
|
||||
|
|
||||
/** |
|
||||
* function to close modal |
|
||||
*/ |
|
||||
const handleClose = () => { |
|
||||
setOpen(false) |
|
||||
} |
|
||||
|
|
||||
const handleClose2 = () => { |
|
||||
navigate('/notes') |
|
||||
setOpen(false) |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* function to return the view Modal |
|
||||
* |
|
||||
* @returns {JSX} |
|
||||
*/ |
|
||||
const modals = () => ( |
|
||||
<Modal |
|
||||
open={open} |
|
||||
onClose={handleClose} |
|
||||
aria-labelledby="modal-title" |
|
||||
aria-describedby="modal-description" |
|
||||
> |
|
||||
<Box |
|
||||
sx={{ |
|
||||
position: 'absolute', |
|
||||
top: '50%', |
|
||||
left: '50%', |
|
||||
transform: 'translate(-50%, -50%)', |
|
||||
width: 450, |
|
||||
bgcolor: 'background.paper', |
|
||||
boxShadow: 24, |
|
||||
p: 4 |
|
||||
}} |
|
||||
> |
|
||||
{statut === 200 ? ( |
|
||||
<Typography |
|
||||
style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: '10px' }} |
|
||||
> |
|
||||
<img src={svgSuccess} alt="" width={70} height={70} />{' '} |
|
||||
<span> |
|
||||
Note de {etudiants.nom} {etudiants.prenom} en {etudiants.niveau} a été inserer avec |
|
||||
succès |
|
||||
</span> |
|
||||
</Typography> |
|
||||
) : ( |
|
||||
<Typography |
|
||||
style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: '10px' }} |
|
||||
> |
|
||||
<img src={svgError} alt="" width={70} height={70} />{' '} |
|
||||
<span>Inserer au moin un champ</span> |
|
||||
</Typography> |
|
||||
)} |
|
||||
<Box |
|
||||
sx={{ |
|
||||
marginTop: '2%', |
|
||||
display: 'flex', |
|
||||
gap: '20px', |
|
||||
alignItems: 'end', |
|
||||
justifyContent: 'flex-end' |
|
||||
}} |
|
||||
> |
|
||||
{statut == 200 ? ( |
|
||||
<Button onClick={handleClose2} color="warning" variant="contained"> |
|
||||
OK |
|
||||
</Button> |
|
||||
) : ( |
|
||||
<Button onClick={handleClose} color="warning" variant="contained"> |
|
||||
OK |
|
||||
</Button> |
|
||||
)} |
|
||||
</Box> |
|
||||
</Box> |
|
||||
</Modal> |
|
||||
) |
|
||||
|
|
||||
return ( |
|
||||
<div className={classe.mainHome}> |
|
||||
{modals()} |
|
||||
<ModalUpdateParcoursEtudiant |
|
||||
onClose={oncloseModal1} |
|
||||
open={openModal1} |
|
||||
user_id={etudiants.id} |
|
||||
onSubmit={handleFormSubmit} |
|
||||
/> |
|
||||
<div className={classeAdd.header}> |
|
||||
<div className={classe.h1style}> |
|
||||
<div className={classeHome.blockTitle}> |
|
||||
<h1 style={{ display: 'flex', alignItems: 'center', gap: '10px' }}> |
|
||||
<CgNotes /> |
|
||||
Ajout note |
|
||||
</h1> |
|
||||
<Link to={'/student'}> |
|
||||
<Button color="warning" variant="contained"> |
|
||||
<IoMdReturnRight style={{ fontSize: '20px' }} /> |
|
||||
</Button> |
|
||||
</Link> |
|
||||
</div> |
|
||||
</div> |
|
||||
|
|
||||
{/* displaying */} |
|
||||
<div className={classeHome.boxEtudiantsCard}> |
|
||||
<Box |
|
||||
sx={{ |
|
||||
position: 'absolute', |
|
||||
top: '55%', |
|
||||
left: '50%', |
|
||||
transform: 'translate(-50%, -50%)', |
|
||||
width: 700, |
|
||||
borderRadius: '2%', |
|
||||
bgcolor: 'background.paper', |
|
||||
boxShadow: 24, |
|
||||
p: 4 |
|
||||
}} |
|
||||
> |
|
||||
<Box |
|
||||
sx={{ |
|
||||
marginTop: '2%', |
|
||||
display: 'flex', |
|
||||
width: '100%', |
|
||||
gap: '20px', |
|
||||
alignItems: 'start', |
|
||||
justifyContent: 'center' |
|
||||
}} |
|
||||
> |
|
||||
<form onSubmit={handleSubmit} style={{ width: '100%' }}> |
|
||||
<h4 style={{ textAlign: 'center', marginBottom: '3%' }}> |
|
||||
Ajout des notes :{' '} |
|
||||
<span style={{ color: '#ff9800' }}> |
|
||||
{etudiants.nom} {etudiants.prenom} en {etudiants.niveau} |
|
||||
</span> |
|
||||
</h4> |
|
||||
|
|
||||
{/* map the all matiere to the form */} |
|
||||
<Grid container spacing={2}> |
|
||||
{matieres.map((mat) => ( |
|
||||
<Grid item xs={12} sm={3} key={mat.nom}> |
|
||||
<TextField |
|
||||
label={mat.nom} |
|
||||
name={mat.id} |
|
||||
placeholder="point séparateur" |
|
||||
color="warning" |
|
||||
fullWidth |
|
||||
value={formData[mat.id] || ''} // Access the correct value from formData |
|
||||
onChange={ |
|
||||
(e) => setFormData({ ...formData, [mat.id]: e.target.value }) // Update the specific key |
|
||||
} |
|
||||
InputProps={{ |
|
||||
startAdornment: ( |
|
||||
<InputAdornment position="start"> |
|
||||
<CgNotes /> |
|
||||
</InputAdornment> |
|
||||
) |
|
||||
}} |
|
||||
className="inputAddNote" |
|
||||
sx={{ |
|
||||
'& .MuiOutlinedInput-root': { |
|
||||
'&:hover fieldset': { |
|
||||
borderColor: '#ff9800' // Set the border color on hover |
|
||||
} |
|
||||
}, |
|
||||
'& .MuiInputBase-input::placeholder': { |
|
||||
fontSize: '11px' // Set the placeholder font size |
|
||||
} |
|
||||
}} |
|
||||
/> |
|
||||
</Grid> |
|
||||
))} |
|
||||
</Grid> |
|
||||
<Grid |
|
||||
item |
|
||||
xs={12} |
|
||||
style={{ |
|
||||
display: 'flex', |
|
||||
gap: '30px', |
|
||||
justifyContent: 'flex-end', |
|
||||
marginTop: '1%' |
|
||||
}} |
|
||||
> |
|
||||
<Button type="submit" color="warning" variant="contained" disabled={disabled}> |
|
||||
Enregister |
|
||||
</Button> |
|
||||
</Grid> |
|
||||
</form> |
|
||||
</Box> |
|
||||
</Box> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
) |
|
||||
} |
|
||||
|
|
||||
export default AddNotes |
|
||||
@ -1,162 +0,0 @@ |
|||||
import React, { useEffect, useRef, useState } from 'react' |
|
||||
import { |
|
||||
Dialog, |
|
||||
DialogActions, |
|
||||
DialogContent, |
|
||||
DialogTitle, |
|
||||
TextField, |
|
||||
Button, |
|
||||
Autocomplete, |
|
||||
InputAdornment, |
|
||||
Box, |
|
||||
Grid |
|
||||
} from '@mui/material' |
|
||||
import { MdRule } from 'react-icons/md' |
|
||||
import { FaClipboardList } from 'react-icons/fa' |
|
||||
|
|
||||
const AddParcours = ({ open, onClose, onSubmitSuccess }) => { |
|
||||
const [formData, setFormData] = useState({ |
|
||||
nom: '', |
|
||||
uniter: '', |
|
||||
mention_id: null |
|
||||
}) |
|
||||
|
|
||||
const [mention, setMention] = useState([]) |
|
||||
|
|
||||
useEffect(() => { |
|
||||
window.mention.getMention().then((response) => { |
|
||||
setMention(response) |
|
||||
}) |
|
||||
}, []) |
|
||||
|
|
||||
const handleChange = (e) => { |
|
||||
const { name, value } = e.target |
|
||||
setFormData({ ...formData, [name]: value }) |
|
||||
} |
|
||||
|
|
||||
const handleSubmit = async (e) => { |
|
||||
e.preventDefault() |
|
||||
let response = await window.notesysteme.insertParcours(formData) |
|
||||
console.log(response) |
|
||||
if (response.success) { |
|
||||
onSubmitSuccess(true) |
|
||||
onClose() // Close the modal after submission |
|
||||
setFormData({ |
|
||||
nom: '', |
|
||||
uniter: '', |
|
||||
mention_id: null |
|
||||
}) |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
return ( |
|
||||
<Dialog open={open} onClose={onClose}> |
|
||||
<form action="" onSubmit={handleSubmit}> |
|
||||
<DialogTitle>Information sur le parcour</DialogTitle> |
|
||||
<DialogContent> |
|
||||
<Box sx={{ flexGrow: 1 }}> |
|
||||
<Grid container spacing={2}> |
|
||||
<Grid item xs={12} sm={6}> |
|
||||
<TextField |
|
||||
autoFocus |
|
||||
margin="normal" |
|
||||
required |
|
||||
name="nom" |
|
||||
label="Nom du parcours" |
|
||||
type="text" |
|
||||
fullWidth |
|
||||
placeholder="Nom du parcours" |
|
||||
variant="outlined" |
|
||||
value={formData.nom} |
|
||||
color="warning" |
|
||||
onChange={handleChange} |
|
||||
InputProps={{ |
|
||||
startAdornment: ( |
|
||||
<InputAdornment position="start"> |
|
||||
<MdRule /> |
|
||||
</InputAdornment> |
|
||||
) |
|
||||
}} |
|
||||
/> |
|
||||
</Grid> |
|
||||
<Grid item xs={12} sm={6}> |
|
||||
<TextField |
|
||||
autoFocus |
|
||||
margin="normal" |
|
||||
required |
|
||||
name="uniter" |
|
||||
label="Uniter parcours" |
|
||||
type="text" |
|
||||
fullWidth |
|
||||
placeholder="GPESB" |
|
||||
variant="outlined" |
|
||||
value={formData.uniter} |
|
||||
color="warning" |
|
||||
onChange={handleChange} |
|
||||
InputProps={{ |
|
||||
startAdornment: ( |
|
||||
<InputAdornment position="start"> |
|
||||
<MdRule /> |
|
||||
</InputAdornment> |
|
||||
) |
|
||||
}} |
|
||||
/> |
|
||||
</Grid> |
|
||||
<Grid item xs={12} sm={12}> |
|
||||
<Autocomplete |
|
||||
options={mention} // Options array for Autocomplete |
|
||||
getOptionLabel={(option) => option.nom || ''} // Safely access `nom` |
|
||||
value={mention.find((item) => item.id === formData.mention_id) || null} // Bind selected value to form data |
|
||||
onChange={(event, newValue) => { |
|
||||
setFormData((prevData) => ({ |
|
||||
...prevData, |
|
||||
mention_id: newValue ? newValue.id : null // Store the ID of the selected mention |
|
||||
})) |
|
||||
}} |
|
||||
isOptionEqualToValue={(option, value) => option.id === value.id} // Ensure correct matching of options |
|
||||
renderInput={(params) => ( |
|
||||
<TextField |
|
||||
{...params} |
|
||||
label="Mention" |
|
||||
color="warning" |
|
||||
placeholder="Sélectionnez une mention" |
|
||||
InputProps={{ |
|
||||
...params.InputProps, |
|
||||
startAdornment: ( |
|
||||
<> |
|
||||
<InputAdornment position="start"> |
|
||||
<FaClipboardList /> |
|
||||
</InputAdornment> |
|
||||
{params.InputProps.startAdornment} |
|
||||
</> |
|
||||
) |
|
||||
}} |
|
||||
sx={{ |
|
||||
marginBottom: '5px', |
|
||||
'& .MuiOutlinedInput-root': { |
|
||||
'&:hover fieldset': { |
|
||||
borderColor: '#ff9800' // Set border color on hover |
|
||||
} |
|
||||
} |
|
||||
}} |
|
||||
/> |
|
||||
)} |
|
||||
/> |
|
||||
</Grid> |
|
||||
</Grid> |
|
||||
</Box> |
|
||||
</DialogContent> |
|
||||
<DialogActions> |
|
||||
<Button onClick={onClose} color="error"> |
|
||||
Annuler |
|
||||
</Button> |
|
||||
<Button type="submit" color="warning"> |
|
||||
Soumettre |
|
||||
</Button> |
|
||||
</DialogActions> |
|
||||
</form> |
|
||||
</Dialog> |
|
||||
) |
|
||||
} |
|
||||
|
|
||||
export default AddParcours |
|
||||
@ -1,336 +0,0 @@ |
|||||
import React, { useRef, useState } from 'react' |
|
||||
import classe from '../assets/AllStyleComponents.module.css' |
|
||||
import img from '../assets/admin.png' |
|
||||
import classeHome from '../assets/Home.module.css' |
|
||||
import { Box, Button, InputAdornment, TextField, Grid, Typography, Modal } from '@mui/material' |
|
||||
import classeAdd from '../assets/AddStudent.module.css' |
|
||||
import validationAddAdmin from './validation/AddAdmin' |
|
||||
import svgSuccess from '../assets/success.svg' |
|
||||
import svgError from '../assets/error.svg' |
|
||||
import { FaEnvelope, FaLock, FaUser } from 'react-icons/fa' |
|
||||
|
|
||||
const Admin = () => { |
|
||||
const [formData, setFormData] = useState({ |
|
||||
username: '', |
|
||||
email: '', |
|
||||
password: '', |
|
||||
roles: 'Enseignant' |
|
||||
}) |
|
||||
|
|
||||
const usernameRef = useRef() |
|
||||
const emailRef = useRef() |
|
||||
const passwordRef = useRef() |
|
||||
const rolesRef = useRef() |
|
||||
const errorUsername = useRef() |
|
||||
const errorEmail = useRef() |
|
||||
const errorPassword = useRef() |
|
||||
|
|
||||
/** |
|
||||
* function to set the data in state |
|
||||
* @param {*} e |
|
||||
*/ |
|
||||
const handleInputChange = (e) => { |
|
||||
const { name, value } = e.target |
|
||||
setFormData((prevData) => ({ |
|
||||
...prevData, |
|
||||
[name]: value |
|
||||
})) |
|
||||
} |
|
||||
|
|
||||
const handleSubmit = async (e) => { |
|
||||
e.preventDefault() |
|
||||
// Handle form submission logic |
|
||||
const valid = validationAddAdmin( |
|
||||
usernameRef.current, |
|
||||
emailRef.current, |
|
||||
passwordRef.current, |
|
||||
errorUsername.current, |
|
||||
errorEmail.current, |
|
||||
errorPassword.current |
|
||||
) |
|
||||
console.log(formData) |
|
||||
console.log(valid) |
|
||||
|
|
||||
if (valid) { |
|
||||
const response = await window.allUser.insertUsers(formData) |
|
||||
console.log(response) |
|
||||
if (response.success) { |
|
||||
setOpen(true) |
|
||||
setFormData({ |
|
||||
username: '', |
|
||||
email: '', |
|
||||
password: '', |
|
||||
roles: 'Enseignant' |
|
||||
}) |
|
||||
} |
|
||||
|
|
||||
if (response.code) { |
|
||||
setCode(422) |
|
||||
setOpen(true) |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* hook to open modal |
|
||||
*/ |
|
||||
const [open, setOpen] = useState(false) |
|
||||
const [code, setCode] = useState(200) |
|
||||
|
|
||||
/** |
|
||||
* function to close modal |
|
||||
*/ |
|
||||
const handleClose = () => setOpen(false) |
|
||||
|
|
||||
/** |
|
||||
* function to return the view Modal |
|
||||
* |
|
||||
* @returns {JSX} |
|
||||
*/ |
|
||||
const modals = () => ( |
|
||||
<Modal |
|
||||
open={open} |
|
||||
onClose={handleClose} |
|
||||
aria-labelledby="modal-title" |
|
||||
aria-describedby="modal-description" |
|
||||
> |
|
||||
<Box |
|
||||
sx={{ |
|
||||
position: 'absolute', |
|
||||
top: '50%', |
|
||||
left: '50%', |
|
||||
transform: 'translate(-50%, -50%)', |
|
||||
width: 450, |
|
||||
bgcolor: 'background.paper', |
|
||||
boxShadow: 24, |
|
||||
p: 4 |
|
||||
}} |
|
||||
> |
|
||||
{code == 422 ? ( |
|
||||
<Typography |
|
||||
style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: '10px' }} |
|
||||
> |
|
||||
<img src={svgError} alt="" width={50} height={50} />{' '} |
|
||||
<span style={{ marginLeft: '10px' }}>Email déjà pris</span> |
|
||||
</Typography> |
|
||||
) : ( |
|
||||
<Typography |
|
||||
style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: '10px' }} |
|
||||
> |
|
||||
<img src={svgSuccess} alt="" width={50} height={50} />{' '} |
|
||||
<span>Insertion a été effectuée avec succès</span> |
|
||||
</Typography> |
|
||||
)} |
|
||||
<Box |
|
||||
sx={{ |
|
||||
marginTop: '2%', |
|
||||
display: 'flex', |
|
||||
gap: '20px', |
|
||||
alignItems: 'end', |
|
||||
justifyContent: 'flex-end' |
|
||||
}} |
|
||||
> |
|
||||
<Button onClick={handleClose} color="warning" variant="contained"> |
|
||||
OK |
|
||||
</Button> |
|
||||
</Box> |
|
||||
</Box> |
|
||||
</Modal> |
|
||||
) |
|
||||
|
|
||||
const sendData = async () => { |
|
||||
await window.syncro.getall() |
|
||||
} |
|
||||
|
|
||||
return ( |
|
||||
<div className={classe.mainHome}> |
|
||||
{modals()} |
|
||||
<div className={classeHome.header}> |
|
||||
<div className={classe.h1style}> |
|
||||
<div className={classeHome.blockTitle}> |
|
||||
<h1>Ajout d'admin</h1> |
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: '10px' }}> |
|
||||
<Button color="warning" variant="contained"> |
|
||||
Recevoir une mise à jour au server |
|
||||
</Button> |
|
||||
<Button color="warning" variant="contained" onClick={sendData}> |
|
||||
Envoyer une mise à jour au server |
|
||||
</Button> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
{/* contenu */} |
|
||||
<div className={classeHome.contenaire}> |
|
||||
<div className={classeAdd.boxEtudiantsCard}> |
|
||||
<Box |
|
||||
sx={{ |
|
||||
position: 'absolute', |
|
||||
top: '50%', |
|
||||
left: '50%', |
|
||||
transform: 'translate(-50%, -50%)', |
|
||||
width: 700, |
|
||||
borderRadius: '3%', |
|
||||
bgcolor: 'background.paper', |
|
||||
boxShadow: 24, |
|
||||
p: 4 |
|
||||
}} |
|
||||
> |
|
||||
<Box |
|
||||
sx={{ |
|
||||
marginTop: '5%', |
|
||||
display: 'flex', |
|
||||
gap: '10px', |
|
||||
alignItems: 'start', |
|
||||
flexDirection: 'column', |
|
||||
fontSize: 16, |
|
||||
fontFamily: 'sans-serif', |
|
||||
justifyContent: 'flex-end' |
|
||||
}} |
|
||||
> |
|
||||
<span style={{ display: 'flex', marginLeft: '40%' }}> |
|
||||
<img src={img} alt="" height={150} width={150} /> |
|
||||
</span> |
|
||||
|
|
||||
<form onSubmit={handleSubmit} style={{ marginTop: '5%' }}> |
|
||||
<Grid container spacing={2}> |
|
||||
{/* matieres Algebre */} |
|
||||
<Grid item xs={12} sm={6}> |
|
||||
<TextField |
|
||||
label="username" |
|
||||
name="username" |
|
||||
color="warning" |
|
||||
fullWidth |
|
||||
value={formData.username} |
|
||||
onChange={handleInputChange} |
|
||||
InputProps={{ |
|
||||
startAdornment: ( |
|
||||
<InputAdornment position="start"> |
|
||||
<FaUser /> |
|
||||
</InputAdornment> |
|
||||
) |
|
||||
}} |
|
||||
inputRef={usernameRef} |
|
||||
sx={{ |
|
||||
'& .MuiOutlinedInput-root': { |
|
||||
'&:hover fieldset': { |
|
||||
borderColor: '#ff9800' // Set the border color on hover |
|
||||
} |
|
||||
} |
|
||||
}} |
|
||||
/> |
|
||||
{/* error username */} |
|
||||
<span |
|
||||
className="text-danger" |
|
||||
ref={errorUsername} |
|
||||
style={{ fontSize: '13px' }} |
|
||||
></span> |
|
||||
</Grid> |
|
||||
{/* matieres Analyse */} |
|
||||
<Grid item xs={12} sm={6}> |
|
||||
<TextField |
|
||||
label="email" |
|
||||
name="email" |
|
||||
fullWidth |
|
||||
color="warning" |
|
||||
value={formData.email} |
|
||||
onChange={handleInputChange} |
|
||||
InputProps={{ |
|
||||
startAdornment: ( |
|
||||
<InputAdornment position="start"> |
|
||||
<FaEnvelope /> |
|
||||
</InputAdornment> |
|
||||
) |
|
||||
}} |
|
||||
inputRef={emailRef} |
|
||||
sx={{ |
|
||||
'& .MuiOutlinedInput-root': { |
|
||||
'&:hover fieldset': { |
|
||||
borderColor: '#ff9800' // Set the border color on hover |
|
||||
} |
|
||||
} |
|
||||
}} |
|
||||
/> |
|
||||
{/* error email */} |
|
||||
<span |
|
||||
className="text-danger" |
|
||||
style={{ fontSize: '13px' }} |
|
||||
ref={errorEmail} |
|
||||
></span> |
|
||||
</Grid> |
|
||||
{/* matieres Mecanique general */} |
|
||||
<Grid item xs={12} sm={6}> |
|
||||
<TextField |
|
||||
label="password" |
|
||||
name="password" |
|
||||
fullWidth |
|
||||
color="warning" |
|
||||
value={formData.password} |
|
||||
onChange={handleInputChange} |
|
||||
InputProps={{ |
|
||||
startAdornment: ( |
|
||||
<InputAdornment position="start"> |
|
||||
<FaLock /> |
|
||||
</InputAdornment> |
|
||||
) |
|
||||
}} |
|
||||
inputRef={passwordRef} |
|
||||
sx={{ |
|
||||
'& .MuiOutlinedInput-root': { |
|
||||
'&:hover fieldset': { |
|
||||
borderColor: '#ff9800' // Set the border color on hover |
|
||||
} |
|
||||
} |
|
||||
}} |
|
||||
/> |
|
||||
{/* error password */} |
|
||||
<span |
|
||||
className="text-danger" |
|
||||
ref={errorPassword} |
|
||||
style={{ fontSize: '13px' }} |
|
||||
></span> |
|
||||
</Grid> |
|
||||
{/* Matieres Resistance Materiaux */} |
|
||||
<Grid item xs={12} sm={6}> |
|
||||
<TextField |
|
||||
label="roles" |
|
||||
name="roles" |
|
||||
fullWidth |
|
||||
color="warning" |
|
||||
value={formData.roles} |
|
||||
onChange={handleInputChange} |
|
||||
disabled |
|
||||
InputProps={{ |
|
||||
startAdornment: <InputAdornment position="start"></InputAdornment> |
|
||||
}} |
|
||||
inputRef={rolesRef} |
|
||||
sx={{ |
|
||||
'& .MuiOutlinedInput-root': { |
|
||||
'&:hover fieldset': { |
|
||||
borderColor: '#ff9800' // Set the border color on hover |
|
||||
} |
|
||||
} |
|
||||
}} |
|
||||
/> |
|
||||
</Grid> |
|
||||
{/* Submit Button */} |
|
||||
<Grid |
|
||||
item |
|
||||
xs={12} |
|
||||
style={{ display: 'flex', gap: '30px', justifyContent: 'flex-end' }} |
|
||||
> |
|
||||
<Button type="submit" color="warning" variant="contained"> |
|
||||
Enregister |
|
||||
</Button> |
|
||||
</Grid> |
|
||||
</Grid> |
|
||||
</form> |
|
||||
</Box> |
|
||||
</Box> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
) |
|
||||
} |
|
||||
|
|
||||
export default Admin |
|
||||
@ -1,112 +0,0 @@ |
|||||
import React, { useState } from 'react' |
|
||||
import { |
|
||||
Dialog, |
|
||||
DialogActions, |
|
||||
DialogContent, |
|
||||
DialogTitle, |
|
||||
TextField, |
|
||||
Button, |
|
||||
Autocomplete, |
|
||||
InputAdornment, |
|
||||
Box, |
|
||||
Grid |
|
||||
} from '@mui/material' |
|
||||
import { MdLabelImportantOutline } from 'react-icons/md' |
|
||||
|
|
||||
const AjoutTranche = ({ open, onClose, onSubmitSuccess, id }) => { |
|
||||
const [formData, setFormData] = useState({ |
|
||||
etudiant_id: id, |
|
||||
tranchename: '', |
|
||||
montant: '' |
|
||||
}) |
|
||||
|
|
||||
const handleChange = (e) => { |
|
||||
const { name, value } = e.target |
|
||||
setFormData({ ...formData, [name]: value }) |
|
||||
} |
|
||||
|
|
||||
const handleSubmit = async (e) => { |
|
||||
e.preventDefault() |
|
||||
let response = await window.etudiants.createTranche(formData) |
|
||||
|
|
||||
if (response.success) { |
|
||||
onClose() |
|
||||
onSubmitSuccess(true) |
|
||||
setFormData({ |
|
||||
etudiant_id: id, |
|
||||
tranchename: '', |
|
||||
montant: '' |
|
||||
}) |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
return ( |
|
||||
<Dialog open={open} onClose={onClose}> |
|
||||
<form action="" onSubmit={handleSubmit}> |
|
||||
<DialogTitle>Ajout tranche</DialogTitle> |
|
||||
<DialogContent> |
|
||||
<Box sx={{ flexGrow: 1 }}> |
|
||||
<Grid container spacing={2}> |
|
||||
<Grid item xs={12} sm={6}> |
|
||||
<TextField |
|
||||
autoFocus |
|
||||
margin="normal" |
|
||||
required |
|
||||
name="tranchename" |
|
||||
label="Désignation" |
|
||||
type="text" |
|
||||
fullWidth |
|
||||
placeholder="Tranche 1" |
|
||||
variant="outlined" |
|
||||
value={formData.tranchename} |
|
||||
color="warning" |
|
||||
onChange={handleChange} |
|
||||
InputProps={{ |
|
||||
startAdornment: ( |
|
||||
<InputAdornment position="start"> |
|
||||
<MdLabelImportantOutline /> |
|
||||
</InputAdornment> |
|
||||
) |
|
||||
}} |
|
||||
/> |
|
||||
</Grid> |
|
||||
<Grid item xs={12} sm={6}> |
|
||||
<TextField |
|
||||
autoFocus |
|
||||
margin="normal" |
|
||||
required |
|
||||
name="montant" |
|
||||
label="Montant" |
|
||||
type="number" |
|
||||
fullWidth |
|
||||
placeholder="Montant" |
|
||||
variant="outlined" |
|
||||
value={formData.montant} |
|
||||
color="warning" |
|
||||
onChange={handleChange} |
|
||||
InputProps={{ |
|
||||
startAdornment: ( |
|
||||
<InputAdornment position="start"> |
|
||||
<MdLabelImportantOutline /> |
|
||||
</InputAdornment> |
|
||||
) |
|
||||
}} |
|
||||
/> |
|
||||
</Grid> |
|
||||
</Grid> |
|
||||
</Box> |
|
||||
</DialogContent> |
|
||||
<DialogActions> |
|
||||
<Button onClick={onClose} color="error"> |
|
||||
Annuler |
|
||||
</Button> |
|
||||
<Button type="submit" color="warning"> |
|
||||
Soumettre |
|
||||
</Button> |
|
||||
</DialogActions> |
|
||||
</form> |
|
||||
</Dialog> |
|
||||
) |
|
||||
} |
|
||||
|
|
||||
export default AjoutTranche |
|
||||
@ -1,319 +0,0 @@ |
|||||
import React, { useEffect, useState } from 'react' |
|
||||
import classe from '../assets/AllStyleComponents.module.css' |
|
||||
import classeHome from '../assets/Home.module.css' |
|
||||
import Paper from '@mui/material/Paper' |
|
||||
import { Modal, Box, Typography, Button, InputAdornment, TextField, Grid } from '@mui/material' |
|
||||
import { FaCalendarAlt, FaCheck, FaCheckCircle, FaSquare } from 'react-icons/fa' |
|
||||
import { DataGrid, GridToolbar, GridToolbarFilterButton } from '@mui/x-data-grid' |
|
||||
import { createTheme, ThemeProvider } from '@mui/material/styles' |
|
||||
import { frFR } from '@mui/x-data-grid/locales' |
|
||||
import { FaCalendarPlus } from 'react-icons/fa' |
|
||||
import { Link } from 'react-router-dom' |
|
||||
import { FaPenToSquare, FaTrash } from 'react-icons/fa6' |
|
||||
import { Tooltip } from 'react-tooltip' |
|
||||
import dayjs from 'dayjs' |
|
||||
import warning from '../assets/warning.svg' |
|
||||
import success from '../assets/success.svg' |
|
||||
import { BsCalendar2Date } from 'react-icons/bs' |
|
||||
|
|
||||
const AnneeScolaire = () => { |
|
||||
const theme = createTheme({ |
|
||||
components: { |
|
||||
MuiIconButton: { |
|
||||
styleOverrides: { |
|
||||
root: { |
|
||||
color: 'gray' // Change the color of toolbar icons |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
MuiButton: { |
|
||||
styleOverrides: { |
|
||||
root: { |
|
||||
color: '#121212' // Change the color of toolbar icons |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
}) |
|
||||
|
|
||||
const [anneeScolaire, setAnneeScolaire] = useState([]) |
|
||||
|
|
||||
useEffect(() => { |
|
||||
window.anneescolaire.getAnneeScolaire().then((response) => { |
|
||||
setAnneeScolaire(response) |
|
||||
}) |
|
||||
}, []) |
|
||||
|
|
||||
const [isDeleted, setIsDeleted] = useState(false) |
|
||||
const [ids, setIds] = useState(0) |
|
||||
|
|
||||
const column = [ |
|
||||
{ field: 'code', headerName: 'Année universitaire', width: 130 }, |
|
||||
{ field: 'debut', headerName: 'Date de début', width: 130 }, |
|
||||
{ field: 'fin', headerName: 'Date de fin', width: 130 }, |
|
||||
{ |
|
||||
field: 'action', |
|
||||
headerName: 'Action', |
|
||||
flex: 1, |
|
||||
renderCell: (params) => ( |
|
||||
<div style={{ display: 'flex', gap: '10px' }}> |
|
||||
<Link to={`/anneescolaire/${params.value}`}> |
|
||||
<Button color="warning" variant="contained"> |
|
||||
<FaPenToSquare |
|
||||
style={{ fontSize: '20px', color: 'white' }} |
|
||||
className={`update${params.value}`} |
|
||||
/> |
|
||||
<Tooltip |
|
||||
anchorSelect={`.update${params.value}`} |
|
||||
style={{ fontSize: '15px', zIndex: 22 }} |
|
||||
place="top" |
|
||||
> |
|
||||
Modifier |
|
||||
</Tooltip> |
|
||||
</Button> |
|
||||
</Link> |
|
||||
<Link |
|
||||
to={`#`} |
|
||||
onClick={() => { |
|
||||
setIds(params.row.id) |
|
||||
setOpen(true) |
|
||||
}} |
|
||||
> |
|
||||
<Button color="error" variant="contained"> |
|
||||
<FaTrash |
|
||||
style={{ fontSize: '20px', color: 'white' }} |
|
||||
className={`delete${params.value}`} |
|
||||
/> |
|
||||
<Tooltip |
|
||||
anchorSelect={`.delete${params.value}`} |
|
||||
style={{ fontSize: '15px', zIndex: 22 }} |
|
||||
place="top" |
|
||||
> |
|
||||
Supprimer |
|
||||
</Tooltip> |
|
||||
</Button> |
|
||||
</Link> |
|
||||
</div> |
|
||||
) |
|
||||
}, |
|
||||
{ |
|
||||
field: 'current', |
|
||||
headerName: 'Année en cours', |
|
||||
flex: 1, |
|
||||
renderCell: (params) => ( |
|
||||
<div style={{ display: 'flex', gap: '10px' }}> |
|
||||
<Link to={`#`}> |
|
||||
<Button color="success" variant="contained"> |
|
||||
{params.value.current == 1 ? ( |
|
||||
<FaCheck |
|
||||
style={{ fontSize: '20px', color: 'white' }} |
|
||||
className={`courrants${params.value.id}`} |
|
||||
/> |
|
||||
) : ( |
|
||||
<FaSquare |
|
||||
style={{ fontSize: '20px', color: 'white' }} |
|
||||
className={`courrant${params.value.id}`} |
|
||||
onClick={() => setCurrent(params.value.id)} |
|
||||
/> |
|
||||
)} |
|
||||
</Button> |
|
||||
<Tooltip |
|
||||
anchorSelect={`.courrants${params.value.id}`} |
|
||||
style={{ fontSize: '15px', zIndex: 22 }} |
|
||||
place="top" |
|
||||
> |
|
||||
Année en cours |
|
||||
</Tooltip> |
|
||||
</Link> |
|
||||
</div> |
|
||||
) |
|
||||
} |
|
||||
] |
|
||||
|
|
||||
const paginationModel = { page: 0, pageSize: 5 } |
|
||||
|
|
||||
let data = anneeScolaire.map((annee) => ({ |
|
||||
id: annee.id, |
|
||||
code: annee.code, |
|
||||
debut: dayjs(annee.debut).format('DD-MM-YYYY'), |
|
||||
fin: dayjs(annee.fin).format('DD-MM-YYYY'), |
|
||||
action: annee.id, |
|
||||
current: { current: annee.is_current, id: annee.id } |
|
||||
})) |
|
||||
|
|
||||
const setCurrent = async (id) => { |
|
||||
// let response = await window.anneescolaire.setCurrent({id}); |
|
||||
// console.log(response); |
|
||||
// if (response.changes) { |
|
||||
// window.anneescolaire.getAnneeScolaire().then((response) => { |
|
||||
// setAnneeScolaire(response); |
|
||||
// }); |
|
||||
// } |
|
||||
} |
|
||||
|
|
||||
const deleteButton = async (id) => { |
|
||||
let response = await window.anneescolaire.deleteAnneeScolaire({ id }) |
|
||||
if (response.success) { |
|
||||
const updatedAnneeScolaire = anneeScolaire.filter((anneeScolaire) => anneeScolaire.id !== id) |
|
||||
setAnneeScolaire(updatedAnneeScolaire) |
|
||||
setIsDeleted(true) |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
const CustomToolbar = () => { |
|
||||
return ( |
|
||||
<div> |
|
||||
<GridToolbarFilterButton /> |
|
||||
</div> |
|
||||
) |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* hook to open modal |
|
||||
*/ |
|
||||
const [open, setOpen] = useState(false) |
|
||||
|
|
||||
/** |
|
||||
* function to close modal |
|
||||
*/ |
|
||||
const handleClose = () => { |
|
||||
setOpen(false) |
|
||||
setIsDeleted(false) |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* function to return the view Modal |
|
||||
* |
|
||||
* @returns {JSX} |
|
||||
*/ |
|
||||
const modals = () => ( |
|
||||
<Modal |
|
||||
open={open} |
|
||||
onClose={handleClose} |
|
||||
aria-labelledby="modal-title" |
|
||||
aria-describedby="modal-description" |
|
||||
> |
|
||||
<Box |
|
||||
sx={{ |
|
||||
position: 'absolute', |
|
||||
top: '50%', |
|
||||
left: '50%', |
|
||||
transform: 'translate(-50%, -50%)', |
|
||||
width: 450, |
|
||||
bgcolor: 'background.paper', |
|
||||
boxShadow: 24, |
|
||||
p: 4 |
|
||||
}} |
|
||||
> |
|
||||
{isDeleted ? ( |
|
||||
<Typography |
|
||||
style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: '10px' }} |
|
||||
> |
|
||||
<img src={success} alt="" width={50} height={50} /> <span>Suprimer avec succèss</span> |
|
||||
</Typography> |
|
||||
) : ( |
|
||||
<Typography |
|
||||
style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: '10px' }} |
|
||||
> |
|
||||
<img src={warning} alt="" width={50} height={50} />{' '} |
|
||||
<span>Voulez vous supprimer cette année ?</span> |
|
||||
</Typography> |
|
||||
)} |
|
||||
<Box |
|
||||
sx={{ |
|
||||
marginTop: '2%', |
|
||||
display: 'flex', |
|
||||
gap: '20px', |
|
||||
alignItems: 'end', |
|
||||
justifyContent: 'flex-end' |
|
||||
}} |
|
||||
> |
|
||||
{isDeleted ? ( |
|
||||
<Button onClick={handleClose} color="warning" variant="contained"> |
|
||||
OK |
|
||||
</Button> |
|
||||
) : ( |
|
||||
<div> |
|
||||
<Button |
|
||||
onClick={() => deleteButton(ids)} |
|
||||
sx={{ mr: 1 }} |
|
||||
color="warning" |
|
||||
variant="contained" |
|
||||
> |
|
||||
Oui |
|
||||
</Button> |
|
||||
<Button onClick={handleClose} color="warning" variant="contained"> |
|
||||
Non |
|
||||
</Button> |
|
||||
</div> |
|
||||
)} |
|
||||
</Box> |
|
||||
</Box> |
|
||||
</Modal> |
|
||||
) |
|
||||
|
|
||||
return ( |
|
||||
<div className={classe.mainHome}> |
|
||||
{modals()} |
|
||||
<div className={classeHome.header}> |
|
||||
<div className={classe.h1style}> |
|
||||
<div className={classeHome.blockTitle}> |
|
||||
<h1 style={{ display: 'flex', alignItems: 'center', gap: '10px' }}> |
|
||||
<BsCalendar2Date /> Année Universitaire |
|
||||
</h1> |
|
||||
<Link to={'/addanneescolaire'}> |
|
||||
<Button color="warning" variant="contained"> |
|
||||
<FaCalendarPlus style={{ fontSize: '20px' }} /> Ajouter |
|
||||
</Button> |
|
||||
</Link> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
|
|
||||
<div className={classeHome.boxEtudiantsCard}> |
|
||||
<Paper |
|
||||
sx={{ |
|
||||
width: '100%', |
|
||||
height: '100%', // Auto height to make the grid responsive |
|
||||
minHeight: 500, // Ensures a minimum height |
|
||||
display: 'flex', |
|
||||
flexDirection: 'column' |
|
||||
}} |
|
||||
> |
|
||||
<ThemeProvider theme={theme}> |
|
||||
<div |
|
||||
style={{ |
|
||||
display: 'flex', |
|
||||
flexDirection: 'column', // Stacks content vertically on smaller screens, |
|
||||
width: '100%' |
|
||||
}} |
|
||||
> |
|
||||
<DataGrid |
|
||||
rows={data} |
|
||||
columns={column} |
|
||||
initialState={{ pagination: { paginationModel } }} |
|
||||
pageSizeOptions={[5, 10]} |
|
||||
sx={{ |
|
||||
border: 0, |
|
||||
width: 'auto', // Ensures the DataGrid takes full width |
|
||||
height: '50%', // Ensures it grows to fit content |
|
||||
minHeight: 400, // Minimum height for the DataGrid |
|
||||
display: 'flex', |
|
||||
justifyContent: 'center', |
|
||||
'@media (max-width: 600px)': { |
|
||||
width: '100%', // 100% width on small screens |
|
||||
height: 'auto' // Allow height to grow with content |
|
||||
} |
|
||||
}} |
|
||||
slots={{ toolbar: CustomToolbar }} |
|
||||
localeText={frFR.components.MuiDataGrid.defaultProps.localeText} |
|
||||
/> |
|
||||
</div> |
|
||||
</ThemeProvider> |
|
||||
</Paper> |
|
||||
</div> |
|
||||
</div> |
|
||||
) |
|
||||
} |
|
||||
|
|
||||
export default AnneeScolaire |
|
||||
@ -1,68 +0,0 @@ |
|||||
import React from 'react' |
|
||||
import classe from '../assets/AllStyleComponents.module.css' |
|
||||
import classeHome from '../assets/Home.module.css' |
|
||||
import logo from '../assets/logo or.png' |
|
||||
import { Box } from '@mui/material' |
|
||||
import classeAdd from '../assets/AddStudent.module.css' |
|
||||
|
|
||||
const Apropos = () => { |
|
||||
return ( |
|
||||
<div className={classe.mainHome}> |
|
||||
<div className={classeHome.header}> |
|
||||
<div className={classe.h1style}> |
|
||||
<div className={classeHome.blockTitle}> |
|
||||
<h1>A propos</h1> |
|
||||
|
|
||||
{/* contenu */} |
|
||||
<div className={classeHome.contenaire}> |
|
||||
<div className={classeAdd.boxEtudiantsCard}> |
|
||||
<Box |
|
||||
sx={{ |
|
||||
position: 'absolute', |
|
||||
top: '50%', |
|
||||
left: '50%', |
|
||||
transform: 'translate(-50%, -50%)', |
|
||||
width: 700, |
|
||||
borderRadius: '3%', |
|
||||
bgcolor: 'background.paper', |
|
||||
boxShadow: 24, |
|
||||
p: 4 |
|
||||
}} |
|
||||
> |
|
||||
<Box |
|
||||
sx={{ |
|
||||
marginTop: '5%', |
|
||||
display: 'flex', |
|
||||
gap: '10px', |
|
||||
alignItems: 'start', |
|
||||
flexDirection: 'column', |
|
||||
fontSize: 16, |
|
||||
fontFamily: 'sans-serif', |
|
||||
justifyContent: 'flex-end' |
|
||||
}} |
|
||||
> |
|
||||
<span style={{ display: 'flex', marginLeft: '30%' }}> |
|
||||
<img src={logo} alt="" height={250} width={250} /> |
|
||||
</span> |
|
||||
|
|
||||
<p style={{ color: 'black' }}> |
|
||||
Nom du Logiciel: CUniversity <br /> |
|
||||
<br /> Description : logiciel de gestion d'universiter <br /> <br /> |
|
||||
Createur: CPAY COMPANY FOR MADAGASCAR <br /> |
|
||||
<br /> Licence: A vie <br /> |
|
||||
<br /> Contact: 0348415301 |
|
||||
<br /> <br /> |
|
||||
E-mail: director@c4m.mg |
|
||||
</p> |
|
||||
</Box> |
|
||||
</Box> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
) |
|
||||
} |
|
||||
|
|
||||
export default Apropos |
|
||||
@ -1,203 +0,0 @@ |
|||||
import React, { useEffect, useState } from 'react' |
|
||||
import { Link, useParams } from 'react-router-dom' |
|
||||
import { FaPlus } from 'react-icons/fa' |
|
||||
import { Button, Grid, Paper, Checkbox, Modal, Typography, Box } from '@mui/material' |
|
||||
import { IoMdReturnRight } from 'react-icons/io' |
|
||||
import classe from '../assets/AllStyleComponents.module.css' |
|
||||
import classeAdd from '../assets/AddStudent.module.css' |
|
||||
import classeHome from '../assets/Home.module.css' |
|
||||
import svgSuccess from '../assets/success.svg' |
|
||||
|
|
||||
const AssignMatiereToMention = () => { |
|
||||
let { id } = useParams() |
|
||||
|
|
||||
const [mention, setMention] = useState([]) |
|
||||
const [matiere, setMatiere] = useState({}) |
|
||||
const [matiereMention, setMatiereMention] = useState([]) |
|
||||
const [formData, setFormData] = useState({}) |
|
||||
|
|
||||
// Fetch data on component mount |
|
||||
useEffect(() => { |
|
||||
window.matieres.getAsign({ id }).then((response) => { |
|
||||
setMatiereMention(response) // Set matiereMention |
|
||||
}) |
|
||||
window.mention.getMention().then((response) => { |
|
||||
setMention(response) // Set mention |
|
||||
}) |
|
||||
window.matieres.getMatiereByID({ id }).then((response) => { |
|
||||
setMatiere(response) // Set matiere |
|
||||
}) |
|
||||
}, [id]) |
|
||||
|
|
||||
// Initialize formData based on mentions and matiereMention |
|
||||
useEffect(() => { |
|
||||
if (mention.length && matiereMention.length) { |
|
||||
const initialFormData = mention.reduce((acc, mens) => { |
|
||||
// Check if the current mention ID exists in matiereMention |
|
||||
const isChecked = matiereMention.some((item) => item.mention_id === mens.id) |
|
||||
acc[mens.id] = isChecked // Set true if matched, false otherwise |
|
||||
return acc |
|
||||
}, {}) |
|
||||
setFormData(initialFormData) // Update formData |
|
||||
} |
|
||||
}, [mention, matiereMention]) |
|
||||
|
|
||||
/** |
|
||||
* Handle form submission |
|
||||
*/ |
|
||||
const formSubmit = async (e) => { |
|
||||
e.preventDefault() |
|
||||
|
|
||||
let response = await window.matieres.asign({ formData, id }) |
|
||||
console.log(response) |
|
||||
if (response.success) { |
|
||||
setOpen(true) |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* Handle checkbox change |
|
||||
*/ |
|
||||
const handleCheckboxChange = (id) => { |
|
||||
setFormData((prevData) => ({ |
|
||||
...prevData, |
|
||||
[id]: !prevData[id] // Toggle the checkbox value |
|
||||
})) |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* hook to open modal |
|
||||
*/ |
|
||||
const [open, setOpen] = useState(false) |
|
||||
|
|
||||
/** |
|
||||
* function to close modal |
|
||||
*/ |
|
||||
const handleClose = () => { |
|
||||
setOpen(false) |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* function to return the view Modal |
|
||||
* |
|
||||
* @returns {JSX} |
|
||||
*/ |
|
||||
const modals = () => ( |
|
||||
<Modal |
|
||||
open={open} |
|
||||
onClose={handleClose} |
|
||||
aria-labelledby="modal-title" |
|
||||
aria-describedby="modal-description" |
|
||||
> |
|
||||
<Box |
|
||||
sx={{ |
|
||||
position: 'absolute', |
|
||||
top: '50%', |
|
||||
left: '50%', |
|
||||
transform: 'translate(-50%, -50%)', |
|
||||
width: 450, |
|
||||
bgcolor: 'background.paper', |
|
||||
boxShadow: 24, |
|
||||
p: 4 |
|
||||
}} |
|
||||
> |
|
||||
<Typography style={{ display: 'flex', alignItems: 'center', flexDirection: 'column' }}> |
|
||||
<img src={svgSuccess} alt="" width={50} height={50} />{' '} |
|
||||
<span>Changemet effectuée avec succès</span> |
|
||||
</Typography> |
|
||||
<Box |
|
||||
sx={{ |
|
||||
marginTop: '2%', |
|
||||
display: 'flex', |
|
||||
gap: '20px', |
|
||||
alignItems: 'end', |
|
||||
justifyContent: 'flex-end' |
|
||||
}} |
|
||||
> |
|
||||
<Button onClick={handleClose} color="warning" variant="contained"> |
|
||||
OK |
|
||||
</Button> |
|
||||
</Box> |
|
||||
</Box> |
|
||||
</Modal> |
|
||||
) |
|
||||
|
|
||||
return ( |
|
||||
<div className={classe.mainHome}> |
|
||||
{modals()} |
|
||||
<div className={classeAdd.header}> |
|
||||
<div className={classe.h1style}> |
|
||||
<div className={classeHome.blockTitle}> |
|
||||
<h1 style={{ display: 'flex', alignItems: 'center', gap: '10px' }}> |
|
||||
<FaPlus /> Asignation |
|
||||
</h1> |
|
||||
<Link to={'#'} onClick={() => window.history.back()}> |
|
||||
<Button color="warning" variant="contained"> |
|
||||
<IoMdReturnRight style={{ fontSize: '20px' }} /> |
|
||||
</Button> |
|
||||
</Link> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
|
|
||||
<Paper |
|
||||
sx={{ |
|
||||
height: 'auto', |
|
||||
minHeight: 500, |
|
||||
display: 'flex', |
|
||||
width: '100%' |
|
||||
}} |
|
||||
> |
|
||||
<form style={{ width: '100%', padding: '2%' }} onSubmit={formSubmit}> |
|
||||
<h5 style={{ textAlign: 'center', textDecoration: 'underline' }}> |
|
||||
Choisissez les mentions qui utilisent {matiere?.nom || 'la matière'} |
|
||||
</h5> |
|
||||
<Grid container spacing={2}> |
|
||||
{mention.map((mens) => ( |
|
||||
<Grid |
|
||||
item |
|
||||
xs={12} |
|
||||
sm={3} |
|
||||
sx={{ |
|
||||
display: 'flex', |
|
||||
justifyContent: 'space-between', |
|
||||
alignItems: 'center', |
|
||||
padding: 5 |
|
||||
}} |
|
||||
key={mens.id} |
|
||||
> |
|
||||
<span> |
|
||||
<b> |
|
||||
<i>{mens.nom}</i> |
|
||||
</b> |
|
||||
</span> |
|
||||
<span> |
|
||||
<Checkbox |
|
||||
checked={formData[mens.id] || false} // Bind checkbox to formData |
|
||||
onChange={() => handleCheckboxChange(mens.id)} // Handle change |
|
||||
color="success" |
|
||||
/> |
|
||||
</span> |
|
||||
</Grid> |
|
||||
))} |
|
||||
</Grid> |
|
||||
<Grid |
|
||||
item |
|
||||
xs={12} |
|
||||
style={{ |
|
||||
display: 'flex', |
|
||||
gap: '30px', |
|
||||
justifyContent: 'flex-end' |
|
||||
}} |
|
||||
> |
|
||||
<Button type="submit" color="warning" variant="contained"> |
|
||||
Enregistrer |
|
||||
</Button> |
|
||||
</Grid> |
|
||||
</form> |
|
||||
</Paper> |
|
||||
</div> |
|
||||
) |
|
||||
} |
|
||||
|
|
||||
export default AssignMatiereToMention |
|
||||
@ -1,259 +0,0 @@ |
|||||
import React, { useEffect, useState } from 'react' |
|
||||
import classe from '../assets/AllStyleComponents.module.css' |
|
||||
import classeAdd from '../assets/AddStudent.module.css' |
|
||||
import classeHome from '../assets/Home.module.css' |
|
||||
import { Link, useParams } from 'react-router-dom' |
|
||||
import { Button, Grid, Paper, Modal, Typography, Box } from '@mui/material' |
|
||||
import { IoMdReturnRight } from 'react-icons/io' |
|
||||
import { FaPlus } from 'react-icons/fa' |
|
||||
import OutlinedInput from '@mui/material/OutlinedInput' |
|
||||
import InputLabel from '@mui/material/InputLabel' |
|
||||
import MenuItem from '@mui/material/MenuItem' |
|
||||
import FormControl from '@mui/material/FormControl' |
|
||||
import Select from '@mui/material/Select' |
|
||||
import svgSuccess from '../assets/success.svg' |
|
||||
|
|
||||
const AssingMatiereToSemestre = () => { |
|
||||
let { id } = useParams() |
|
||||
|
|
||||
const [mentions, setMentions] = useState([]) |
|
||||
const [matiereSemestre, setMatiereSemestre] = useState([]) |
|
||||
const [matiere, setMatiere] = useState({}) |
|
||||
const [semestre, setSemestre] = useState([]) |
|
||||
|
|
||||
useEffect(() => { |
|
||||
window.matieres.asignSemestre({ id }).then((response) => { |
|
||||
setMentions(response) |
|
||||
}) |
|
||||
|
|
||||
window.matieres.getMatiereByID({ id }).then((response) => { |
|
||||
setMatiere(response) // Set matiere |
|
||||
}) |
|
||||
|
|
||||
window.matieres.getSemestreMatiere({ id }).then((response) => { |
|
||||
setMatiereSemestre(response) // Set matiere |
|
||||
}) |
|
||||
|
|
||||
window.matieres.getSemestre().then((response) => { |
|
||||
setSemestre(response) // Set matiere |
|
||||
}) |
|
||||
}, [id]) |
|
||||
|
|
||||
// State to manage selected semestres for each mention |
|
||||
const [selectedSemestres, setSelectedSemestres] = useState({}) |
|
||||
|
|
||||
// Populate the initial state for selectedSemestres |
|
||||
useEffect(() => { |
|
||||
const initialSelectedSemestres = {} |
|
||||
matiereSemestre.forEach((item) => { |
|
||||
if (!initialSelectedSemestres[item.mention_id]) { |
|
||||
initialSelectedSemestres[item.mention_id] = [] |
|
||||
} |
|
||||
initialSelectedSemestres[item.mention_id].push(item.semestre_id) |
|
||||
}) |
|
||||
setSelectedSemestres(initialSelectedSemestres) |
|
||||
}, [matiereSemestre]) |
|
||||
|
|
||||
// Handle change in Select |
|
||||
const handleChange = (id) => (event) => { |
|
||||
const { |
|
||||
target: { value } |
|
||||
} = event |
|
||||
|
|
||||
// Update state for the specific mention ID |
|
||||
setSelectedSemestres((prevState) => ({ |
|
||||
...prevState, |
|
||||
[id]: typeof value === 'string' ? value.split(',') : value |
|
||||
})) |
|
||||
} |
|
||||
|
|
||||
const formSubmit = async (e) => { |
|
||||
e.preventDefault() |
|
||||
|
|
||||
let response = await window.matieres.insertUpdateMentionSemestre({ id, selectedSemestres }) |
|
||||
|
|
||||
console.log(response) |
|
||||
if (response.success) { |
|
||||
setOpen(true) |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* hook to open modal |
|
||||
*/ |
|
||||
const [open, setOpen] = useState(false) |
|
||||
|
|
||||
/** |
|
||||
* function to close modal |
|
||||
*/ |
|
||||
const handleClose = () => { |
|
||||
setOpen(false) |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* function to return the view Modal |
|
||||
* |
|
||||
* @returns {JSX} |
|
||||
*/ |
|
||||
const modals = () => ( |
|
||||
<Modal |
|
||||
open={open} |
|
||||
onClose={handleClose} |
|
||||
aria-labelledby="modal-title" |
|
||||
aria-describedby="modal-description" |
|
||||
> |
|
||||
<Box |
|
||||
sx={{ |
|
||||
position: 'absolute', |
|
||||
top: '50%', |
|
||||
left: '50%', |
|
||||
transform: 'translate(-50%, -50%)', |
|
||||
width: 450, |
|
||||
bgcolor: 'background.paper', |
|
||||
boxShadow: 24, |
|
||||
p: 4 |
|
||||
}} |
|
||||
> |
|
||||
<Typography style={{ display: 'flex', alignItems: 'center', flexDirection: 'column' }}> |
|
||||
<img src={svgSuccess} alt="" width={50} height={50} />{' '} |
|
||||
<span>Changemet effectuée avec succès</span> |
|
||||
</Typography> |
|
||||
<Box |
|
||||
sx={{ |
|
||||
marginTop: '2%', |
|
||||
display: 'flex', |
|
||||
gap: '20px', |
|
||||
alignItems: 'end', |
|
||||
justifyContent: 'flex-end' |
|
||||
}} |
|
||||
> |
|
||||
<Button onClick={handleClose} color="warning" variant="contained"> |
|
||||
OK |
|
||||
</Button> |
|
||||
</Box> |
|
||||
</Box> |
|
||||
</Modal> |
|
||||
) |
|
||||
|
|
||||
console.log(matiereSemestre) |
|
||||
|
|
||||
// Step 1: Preprocess matiereSemestre to create a mapping |
|
||||
const preprocessSemestres = () => { |
|
||||
const mapping = {} |
|
||||
matiereSemestre.forEach((item) => { |
|
||||
if (item.matiere_id === id) { |
|
||||
// Filter by matiere_id if necessary |
|
||||
if (!mapping[item.mention_id]) { |
|
||||
mapping[item.mention_id] = [] |
|
||||
} |
|
||||
mapping[item.mention_id].push(item.semestre_id) |
|
||||
} |
|
||||
}) |
|
||||
return mapping |
|
||||
} |
|
||||
|
|
||||
return ( |
|
||||
<div className={classe.mainHome}> |
|
||||
{modals()} |
|
||||
<div className={classeAdd.header}> |
|
||||
<div className={classe.h1style}> |
|
||||
<div className={classeHome.blockTitle}> |
|
||||
<h1 style={{ display: 'flex', alignItems: 'center', gap: '10px' }}> |
|
||||
<FaPlus /> Asignation Semestre |
|
||||
</h1> |
|
||||
<Link to={'/matiere'}> |
|
||||
<Button color="warning" variant="contained"> |
|
||||
<IoMdReturnRight style={{ fontSize: '20px' }} /> |
|
||||
</Button> |
|
||||
</Link> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
<Paper |
|
||||
sx={{ |
|
||||
height: 'auto', |
|
||||
minHeight: 500, |
|
||||
display: 'flex', |
|
||||
width: '100%' |
|
||||
}} |
|
||||
> |
|
||||
<form style={{ width: '100%', padding: '2%' }} onSubmit={formSubmit}> |
|
||||
<h5 style={{ textAlign: 'center', textDecoration: 'underline' }}> |
|
||||
Choisissez les semestre dans la quelle on enseigne {matiere?.nom || 'la matière'} |
|
||||
</h5> |
|
||||
<Grid container spacing={2}> |
|
||||
{mentions.map((mens) => ( |
|
||||
<Grid |
|
||||
item |
|
||||
xs={12} |
|
||||
sm={4} |
|
||||
sx={{ |
|
||||
display: 'flex', |
|
||||
justifyContent: 'space-between', |
|
||||
alignItems: 'center', |
|
||||
padding: 5 |
|
||||
}} |
|
||||
key={mens.id} |
|
||||
> |
|
||||
<div> |
|
||||
<div> |
|
||||
<b> |
|
||||
<i>{mens.nom}</i> |
|
||||
</b> |
|
||||
</div> |
|
||||
<div> |
|
||||
<FormControl sx={{ m: 1, width: 300 }}> |
|
||||
<InputLabel id={`semestre-label-${mens.id}`} color="warning"> |
|
||||
Semestre |
|
||||
</InputLabel> |
|
||||
<Select |
|
||||
labelId={`semestre-label-${mens.id}`} |
|
||||
id={`semestre-select-${mens.id}`} |
|
||||
multiple |
|
||||
color="warning" |
|
||||
size="small" |
|
||||
required |
|
||||
value={selectedSemestres[mens.id] || []} // Pre-select semestres for this mention |
|
||||
onChange={handleChange(mens.id)} // Pass mention ID to handler |
|
||||
input={<OutlinedInput label="Semestre" />} |
|
||||
MenuProps={{ |
|
||||
PaperProps: { |
|
||||
style: { |
|
||||
maxHeight: 200, // Limit dropdown height |
|
||||
width: 250 // Adjust dropdown width if needed |
|
||||
} |
|
||||
} |
|
||||
}} |
|
||||
> |
|
||||
{semestre.map((sem) => ( |
|
||||
<MenuItem key={sem.id} value={sem.id}> |
|
||||
{sem.nom} |
|
||||
</MenuItem> |
|
||||
))} |
|
||||
</Select> |
|
||||
</FormControl> |
|
||||
</div> |
|
||||
</div> |
|
||||
</Grid> |
|
||||
))} |
|
||||
</Grid> |
|
||||
<Grid |
|
||||
item |
|
||||
xs={12} |
|
||||
style={{ |
|
||||
display: 'flex', |
|
||||
gap: '30px', |
|
||||
justifyContent: 'flex-end' |
|
||||
}} |
|
||||
> |
|
||||
<Button type="submit" color="warning" variant="contained"> |
|
||||
Enregistrer |
|
||||
</Button> |
|
||||
</Grid> |
|
||||
</form> |
|
||||
</Paper> |
|
||||
</div> |
|
||||
) |
|
||||
} |
|
||||
|
|
||||
export default AssingMatiereToSemestre |
|
||||
@ -1,30 +0,0 @@ |
|||||
import React from 'react' |
|
||||
import { |
|
||||
GridToolbarContainer, |
|
||||
GridToolbarFilterButton, |
|
||||
GridToolbarColumnsButton, |
|
||||
GridToolbarExport |
|
||||
} from '@mui/x-data-grid' |
|
||||
import { Button } from '@mui/material' |
|
||||
import { FaCloudUploadAlt } from 'react-icons/fa' |
|
||||
|
|
||||
const CustomBar = ({ onImport }) => { |
|
||||
return ( |
|
||||
<GridToolbarContainer> |
|
||||
<GridToolbarColumnsButton /> |
|
||||
<GridToolbarFilterButton /> |
|
||||
{/* Custom import button */} |
|
||||
<Button |
|
||||
variant="contained" |
|
||||
color="inherit" |
|
||||
startIcon={<FaCloudUploadAlt />} |
|
||||
onClick={onImport} |
|
||||
> |
|
||||
Importer |
|
||||
</Button> |
|
||||
<GridToolbarExport /> |
|
||||
</GridToolbarContainer> |
|
||||
) |
|
||||
} |
|
||||
|
|
||||
export default CustomBar |
|
||||
@ -1,52 +0,0 @@ |
|||||
import React, { useEffect, useState } from 'react' |
|
||||
import { |
|
||||
Dialog, |
|
||||
DialogActions, |
|
||||
DialogContent, |
|
||||
DialogTitle, |
|
||||
Button, |
|
||||
Box, |
|
||||
Typography |
|
||||
} from '@mui/material' |
|
||||
|
|
||||
const DeleteTranche = ({ open, onClose, id, onSubmitSuccess }) => { |
|
||||
const [idDelete, setIdDelete] = useState(null) |
|
||||
|
|
||||
useEffect(() => { |
|
||||
setIdDelete(id) |
|
||||
}, [id]) |
|
||||
|
|
||||
console.log(idDelete) |
|
||||
|
|
||||
const deleteOption = async () => { |
|
||||
if (idDelete !== null) { |
|
||||
const id = idDelete |
|
||||
let response = await window.etudiants.deleteTranche({ id }) |
|
||||
if (response.success) { |
|
||||
onSubmitSuccess(true) |
|
||||
onClose() |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
return ( |
|
||||
<Dialog open={open} onClose={onClose}> |
|
||||
<DialogTitle>Suppression</DialogTitle> |
|
||||
<DialogContent> |
|
||||
<Box sx={{ flexGrow: 1, width: '300px' }}> |
|
||||
<Typography>Voulez vous Supprimer ?</Typography> |
|
||||
</Box> |
|
||||
</DialogContent> |
|
||||
<DialogActions> |
|
||||
<Button onClick={onClose} color="error"> |
|
||||
Annuler |
|
||||
</Button> |
|
||||
<Button onClick={deleteOption} color="warning"> |
|
||||
Soumettre |
|
||||
</Button> |
|
||||
</DialogActions> |
|
||||
</Dialog> |
|
||||
) |
|
||||
} |
|
||||
|
|
||||
export default DeleteTranche |
|
||||
@ -1,517 +0,0 @@ |
|||||
import React, { useEffect, useState } from 'react' |
|
||||
import classe from '../assets/AllStyleComponents.module.css' |
|
||||
import classeAdd from '../assets/AddStudent.module.css' |
|
||||
import classeHome from '../assets/Home.module.css' |
|
||||
import { IoMdReturnRight } from 'react-icons/io' |
|
||||
import { Link } from 'react-router-dom' |
|
||||
import { FaFileExcel, FaCloudUploadAlt, FaCloudDownloadAlt } from 'react-icons/fa' |
|
||||
import { Box, Button, Typography, ThemeProvider, Modal } from '@mui/material' |
|
||||
import { styled, createTheme } from '@mui/material/styles' |
|
||||
import * as XLSX from 'xlsx' |
|
||||
import Papa from 'papaparse' |
|
||||
import { DataGrid, GridToolbarContainer, GridToolbarColumnsButton } from '@mui/x-data-grid' |
|
||||
import { frFR } from '@mui/x-data-grid/locales' |
|
||||
import dayjs from 'dayjs' |
|
||||
import CustomBar from './CustomBar' |
|
||||
import svgSuccess from '../assets/success.svg' |
|
||||
import svgError from '../assets/error.svg' |
|
||||
import { MenuItem, Select, FormControl, InputLabel } from '@mui/material' |
|
||||
|
|
||||
const ExportEtudiants = () => { |
|
||||
const [tableData, setTableData] = useState([]) |
|
||||
const [error, setError] = useState('') |
|
||||
const [isInserted, setIsinserted] = useState(true) |
|
||||
const [message, setMessage] = useState('') |
|
||||
|
|
||||
const theme = createTheme({ |
|
||||
components: { |
|
||||
MuiIconButton: { |
|
||||
styleOverrides: { |
|
||||
root: { |
|
||||
color: 'gray' // Toolbar icons color |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
MuiButton: { |
|
||||
styleOverrides: { |
|
||||
root: { |
|
||||
color: '#121212' // Button text color |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
}) |
|
||||
|
|
||||
const [header, setHeader] = useState([]) |
|
||||
let field = [] |
|
||||
const [dynamicColumns, setColumns] = useState([]) |
|
||||
|
|
||||
for (let index = 0; index < header.length; index++) { |
|
||||
field.push(header[index].toLowerCase().replace(/\s+/g, '_')) |
|
||||
} |
|
||||
|
|
||||
useEffect(() => { |
|
||||
setColumns( |
|
||||
header.map((col, index) => ({ |
|
||||
field: field[index], // Converts the header text to field names (e.g., "Nom" -> "nom") |
|
||||
headerName: col, // Display the header as is |
|
||||
width: 150 // Adjust the width as needed |
|
||||
})) |
|
||||
) |
|
||||
}, [header]) |
|
||||
|
|
||||
const paginationModel = { page: 0, pageSize: 5 } |
|
||||
|
|
||||
// Assuming `tableData` is an array where each entry corresponds to a student's data |
|
||||
const dataRow = tableData.map((etudiant, index) => { |
|
||||
// Dynamically create an object with fields from the header and data from `etudiant` |
|
||||
let row = { id: index + 1 } // Unique ID for each row |
|
||||
|
|
||||
field.forEach((fieldName, idx) => { |
|
||||
if (fieldName === 'date_de_naissance') { |
|
||||
row[fieldName] = dayjs(etudiant[idx]).format('DD-MM-YYYY') // Format date |
|
||||
} else { |
|
||||
row[fieldName] = etudiant[idx] // Assign value to field dynamically |
|
||||
} |
|
||||
}) |
|
||||
|
|
||||
return row |
|
||||
}) |
|
||||
|
|
||||
const VisuallyHiddenInput = styled('input')({ |
|
||||
clip: 'rect(0 0 0 0)', |
|
||||
clipPath: 'inset(50%)', |
|
||||
height: 1, |
|
||||
overflow: 'hidden', |
|
||||
position: 'absolute', |
|
||||
bottom: 0, |
|
||||
left: 0, |
|
||||
whiteSpace: 'nowrap', |
|
||||
width: 1 |
|
||||
}) |
|
||||
|
|
||||
const [files, setFiles] = useState() |
|
||||
|
|
||||
const handleFileChange = (event) => { |
|
||||
const file = event.target.files[0] |
|
||||
setFiles(event.target.files[0]) |
|
||||
if (!file) { |
|
||||
setError('No file selected') |
|
||||
return |
|
||||
} |
|
||||
|
|
||||
const fileExtension = file.name.split('.').pop().toLowerCase() |
|
||||
|
|
||||
if (fileExtension === 'xlsx') { |
|
||||
const reader = new FileReader() |
|
||||
reader.onload = (e) => { |
|
||||
const data = new Uint8Array(e.target.result) |
|
||||
const workbook = XLSX.read(data, { type: 'array' }) |
|
||||
|
|
||||
// Extract data from all sheets and combine |
|
||||
const allData = [] |
|
||||
workbook.SheetNames.forEach((sheetName) => { |
|
||||
const worksheet = workbook.Sheets[sheetName] |
|
||||
const rows = XLSX.utils.sheet_to_json(worksheet, { header: 1 }) |
|
||||
setHeader(rows[0]) // keep the header |
|
||||
allData.push(...rows.slice(1)) // Skip header row for each sheet |
|
||||
}) |
|
||||
setTableData(allData) |
|
||||
} |
|
||||
reader.readAsArrayBuffer(file) |
|
||||
} else if (fileExtension === 'csv') { |
|
||||
const reader = new FileReader() |
|
||||
reader.onload = (e) => { |
|
||||
Papa.parse(e.target.result, { |
|
||||
complete: (results) => { |
|
||||
setHeader(results.data[0]) // keep the header |
|
||||
console.log(results.data) |
|
||||
setTableData(results.data.slice(1)) // Skip header row |
|
||||
}, |
|
||||
header: false |
|
||||
}) |
|
||||
} |
|
||||
reader.readAsText(file) |
|
||||
} else { |
|
||||
setError('Unsupported file format. Please upload .xlsx or .csv file.') |
|
||||
setTableData([]) |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* fonction qui envoye dans le back |
|
||||
*/ |
|
||||
const handleImport = async () => { |
|
||||
// Code to handle file import (can open a file dialog or call handleFileChange) |
|
||||
|
|
||||
let response = await window.etudiants.importExcel(files.path) |
|
||||
|
|
||||
console.log(response) |
|
||||
|
|
||||
if (response.message) { |
|
||||
setMessage(response.message) |
|
||||
} |
|
||||
|
|
||||
if (response.error) { |
|
||||
setIsinserted(true) |
|
||||
setOpen(true) |
|
||||
setTableData([]) |
|
||||
} else { |
|
||||
setIsinserted(false) |
|
||||
setOpen(true) |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* hook to open modal |
|
||||
*/ |
|
||||
const [open, setOpen] = useState(false) |
|
||||
|
|
||||
/** |
|
||||
* function to close modal |
|
||||
*/ |
|
||||
const handleClose = () => setOpen(false) |
|
||||
|
|
||||
/** |
|
||||
* function to return the view Modal |
|
||||
* |
|
||||
* @returns {JSX} |
|
||||
*/ |
|
||||
const modals = () => ( |
|
||||
<Modal |
|
||||
open={open} |
|
||||
onClose={handleClose} |
|
||||
aria-labelledby="modal-title" |
|
||||
aria-describedby="modal-description" |
|
||||
> |
|
||||
<Box |
|
||||
sx={{ |
|
||||
position: 'absolute', |
|
||||
top: '50%', |
|
||||
left: '50%', |
|
||||
transform: 'translate(-50%, -50%)', |
|
||||
width: 450, |
|
||||
bgcolor: 'background.paper', |
|
||||
boxShadow: 24, |
|
||||
p: 4 |
|
||||
}} |
|
||||
> |
|
||||
{isInserted ? ( |
|
||||
<Typography |
|
||||
style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: '10px' }} |
|
||||
> |
|
||||
<img src={svgSuccess} alt="" width={50} height={50} />{' '} |
|
||||
<span>Importation a été effectuée avec succès</span> |
|
||||
</Typography> |
|
||||
) : ( |
|
||||
<Typography |
|
||||
style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: '10px' }} |
|
||||
> |
|
||||
<img src={svgError} alt="" width={50} height={50} />{' '} |
|
||||
<span> |
|
||||
L'importation n'a pas été effectuée |
|
||||
<br /> |
|
||||
{message} |
|
||||
</span> |
|
||||
</Typography> |
|
||||
)} |
|
||||
<Box |
|
||||
sx={{ |
|
||||
marginTop: '2%', |
|
||||
display: 'flex', |
|
||||
gap: '20px', |
|
||||
alignItems: 'end', |
|
||||
justifyContent: 'flex-end' |
|
||||
}} |
|
||||
> |
|
||||
<Button onClick={handleClose} color="warning" variant="contained"> |
|
||||
OK |
|
||||
</Button> |
|
||||
</Box> |
|
||||
</Box> |
|
||||
</Modal> |
|
||||
) |
|
||||
|
|
||||
const exemplaireFileExcel = [ |
|
||||
{ |
|
||||
nom: 'nom2', |
|
||||
prenom: 'prenom2', |
|
||||
niveau: 'L1', |
|
||||
date_naissance: 'jj/mm/AAAA', |
|
||||
annee_scolaire: '2024-2025', |
|
||||
mention: 'INFO', |
|
||||
num_inscription: 'azertyuiop', |
|
||||
sexe: 'F', |
|
||||
cin: '1234567890987', |
|
||||
date_de_delivrance: 'JJ/MM/AAAA', |
|
||||
nationaliter: 'Malagasy', |
|
||||
annee_baccalaureat: 'AAAA', |
|
||||
serie: 'D', |
|
||||
code_redoublement: 'N si nouveau, P si passant, R si redoublant RM si renvoyer, A si ancient', |
|
||||
boursier: 'Non', |
|
||||
domaine: "(S) Science de l'ingénieur", |
|
||||
contact: '0387205654' |
|
||||
}, |
|
||||
{ |
|
||||
nom: 'nom1', |
|
||||
prenom: 'prenom2', |
|
||||
niveau: 'L2', |
|
||||
date_naissance: 'jj/mm/AAAA', |
|
||||
annee_scolaire: '2024-2025', |
|
||||
mention: 'Informatique', |
|
||||
num_inscription: 'azertyuiop', |
|
||||
sexe: 'M', |
|
||||
cin: '1234567890987', |
|
||||
date_de_delivrance: 'JJ/MM/AAAA', |
|
||||
nationaliter: 'Malagasy', |
|
||||
annee_baccalaureat: 'AAAA', |
|
||||
serie: 'D', |
|
||||
code_redoublement: 'N si nouveau, P si passant, R si redoublant RM si renvoyer, A si ancient', |
|
||||
boursier: 'Non', |
|
||||
domaine: "(S) Science de l'ingénieur", |
|
||||
contact: '0387205654' |
|
||||
} |
|
||||
] |
|
||||
|
|
||||
const convertToExcel = () => { |
|
||||
// convert json to sheet |
|
||||
const worksheet = XLSX.utils.json_to_sheet(exemplaireFileExcel) |
|
||||
|
|
||||
// Create a new workbook and append the worksheet |
|
||||
const workbook = XLSX.utils.book_new() |
|
||||
XLSX.utils.book_append_sheet(workbook, worksheet, 'Sheet1') |
|
||||
|
|
||||
// Write the workbook to a Blob and create a download link |
|
||||
const excelBuffer = XLSX.write(workbook, { bookType: 'xlsx', type: 'array' }) |
|
||||
const data = new Blob([excelBuffer], { type: 'application/octet-stream' }) |
|
||||
const url = URL.createObjectURL(data) |
|
||||
|
|
||||
// Trigger a download |
|
||||
const link = document.createElement('a') |
|
||||
link.href = url |
|
||||
link.download = 'exemplaire_etudiant.xlsx' |
|
||||
link.click() |
|
||||
|
|
||||
// Clean up |
|
||||
URL.revokeObjectURL(url) |
|
||||
} |
|
||||
|
|
||||
const handleColumnVisibilityChange = (model) => { |
|
||||
// Get the currently visible columns |
|
||||
const visibleColumns = dynamicColumns.filter((column) => model[column.field] !== false) |
|
||||
|
|
||||
// Create a new Excel file with visible columns |
|
||||
createExcelFile(visibleColumns) |
|
||||
} |
|
||||
|
|
||||
const createExcelFile = (columns) => { |
|
||||
// Extract and set the header |
|
||||
const header = columns.reduce((acc, col) => { |
|
||||
acc[col.field] = col.headerName || col.field // Use headerName or field as default |
|
||||
return acc |
|
||||
}, {}) |
|
||||
|
|
||||
// Map the data rows to match the extracted headers |
|
||||
const worksheetData = dataRow.map((row) => { |
|
||||
const filteredRow = {} |
|
||||
columns.forEach((col) => { |
|
||||
const headerName = header[col.field] |
|
||||
filteredRow[headerName] = row[col.field] |
|
||||
}) |
|
||||
return filteredRow |
|
||||
}) |
|
||||
|
|
||||
// Create a worksheet from the data |
|
||||
const ws = XLSX.utils.json_to_sheet(worksheetData) |
|
||||
|
|
||||
// Create a workbook and add the worksheet to it |
|
||||
const wb = XLSX.utils.book_new() |
|
||||
XLSX.utils.book_append_sheet(wb, ws, 'Sheet1') |
|
||||
|
|
||||
// Generate the Excel file as binary data |
|
||||
const excelFile = XLSX.write(wb, { bookType: 'xlsx', type: 'array' }) |
|
||||
|
|
||||
// Create a Blob for the Excel data |
|
||||
const blob = new Blob([excelFile], { |
|
||||
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' |
|
||||
}) |
|
||||
|
|
||||
// Create a link element to trigger the download |
|
||||
const url = URL.createObjectURL(blob) |
|
||||
const link = document.createElement('a') |
|
||||
link.href = url |
|
||||
link.download = 'original-file.xlsx' // Original file name or any desired name |
|
||||
document.body.appendChild(link) |
|
||||
link.click() |
|
||||
document.body.removeChild(link) |
|
||||
URL.revokeObjectURL(url) // Clean up |
|
||||
} |
|
||||
|
|
||||
// Handle header name change for a specific field and auto-export to Excel |
|
||||
const handleHeaderChange = (field, newHeaderName) => { |
|
||||
setColumns((prevColumns) => |
|
||||
prevColumns.map((col) => (col.field === field ? { ...col, headerName: newHeaderName } : col)) |
|
||||
) |
|
||||
} |
|
||||
|
|
||||
return ( |
|
||||
<div className={classe.mainHome}> |
|
||||
{modals()} |
|
||||
<div className={classeAdd.header}> |
|
||||
<div className={classe.h1style}> |
|
||||
<div className={classeHome.blockTitle}> |
|
||||
<h1 style={{ display: 'flex', alignItems: 'center', gap: '10px' }}> |
|
||||
<FaFileExcel /> Export |
|
||||
</h1> |
|
||||
<Link to={'/addstudent'}> |
|
||||
<Button color="warning" variant="contained"> |
|
||||
<IoMdReturnRight style={{ fontSize: '20px' }} /> |
|
||||
</Button> |
|
||||
</Link> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
|
|
||||
<Box |
|
||||
sx={{ |
|
||||
// position: 'absolute', |
|
||||
// top: '52%', |
|
||||
// left: '52%', |
|
||||
// transform: 'translate(-50%, -50%)', |
|
||||
width: '99.5%', |
|
||||
borderRadius: '2%', |
|
||||
bgcolor: 'background.paper', |
|
||||
boxShadow: 24, |
|
||||
p: 4 |
|
||||
}} |
|
||||
> |
|
||||
<Box sx={{ mt: 2, display: 'flex', alignItems: 'center', flexDirection: 'column' }}> |
|
||||
<div style={{ display: 'flex', gap: '20px', marginBottom: '20px' }}> |
|
||||
<Button |
|
||||
component="label" |
|
||||
variant="contained" |
|
||||
color="warning" |
|
||||
startIcon={<FaCloudUploadAlt />} |
|
||||
> |
|
||||
Charger un fichier excel |
|
||||
<VisuallyHiddenInput type="file" onChange={handleFileChange} accept=".xlsx, .csv" /> |
|
||||
</Button> |
|
||||
<Button |
|
||||
component="label" |
|
||||
variant="contained" |
|
||||
color="error" |
|
||||
startIcon={<FaCloudDownloadAlt />} |
|
||||
onClick={convertToExcel} |
|
||||
> |
|
||||
Télécharger le modèle d'import |
|
||||
</Button> |
|
||||
</div> |
|
||||
{error && ( |
|
||||
<Typography color="error" variant="body2"> |
|
||||
{error} |
|
||||
</Typography> |
|
||||
)} |
|
||||
{tableData.length > 0 && ( |
|
||||
<ThemeProvider theme={theme}> |
|
||||
<div |
|
||||
style={{ |
|
||||
display: 'flex', |
|
||||
alignItems: 'center', |
|
||||
justifyContent: 'space-between', |
|
||||
flexWrap: 'wrap' |
|
||||
}} |
|
||||
> |
|
||||
{/* Dropdowns for each column */} |
|
||||
{dynamicColumns.map((col) => ( |
|
||||
<FormControl |
|
||||
key={col.field} |
|
||||
sx={{ |
|
||||
m: 1, |
|
||||
width: '100%', |
|
||||
'& .MuiOutlinedInput-root': { |
|
||||
'&:hover fieldset': { |
|
||||
borderColor: '#ff9800' // Set the border color on hover |
|
||||
} |
|
||||
} |
|
||||
}} |
|
||||
size="small" |
|
||||
color="warning" |
|
||||
variant="outlined" |
|
||||
style={{ margin: 10, width: 200 }} |
|
||||
> |
|
||||
<InputLabel id={col.headerName}>{col.headerName}</InputLabel> |
|
||||
<Select |
|
||||
labelId={col.headerName} |
|
||||
value={col.headerName} |
|
||||
label={col.headerName} |
|
||||
color="warning" |
|
||||
name={col.headerName} |
|
||||
onChange={(e) => handleHeaderChange(col.field, e.target.value)} |
|
||||
> |
|
||||
<MenuItem value={col.headerName}>{col.headerName}</MenuItem> |
|
||||
<MenuItem value={'nom'}>nom</MenuItem> |
|
||||
<MenuItem value={'prenom'}>prenom</MenuItem> |
|
||||
<MenuItem value={`niveau`}>niveau</MenuItem> |
|
||||
<MenuItem value={`date_naissance`}>date de naissance</MenuItem> |
|
||||
<MenuItem value={`annee_scolaire`}>année scolaire</MenuItem> |
|
||||
<MenuItem value={`mention`}>mention</MenuItem> |
|
||||
<MenuItem value={`num_inscription`}>numéro d'inscription</MenuItem> |
|
||||
<MenuItem value={`nationalite`}>Nationaliter</MenuItem> |
|
||||
<MenuItem value={`sexe`}>Sexe</MenuItem> |
|
||||
<MenuItem value={`cin`}>CIN</MenuItem> |
|
||||
<MenuItem value={`date_de_livraison`}>Date de livraison</MenuItem> |
|
||||
<MenuItem value={`annee_baccalaureat`}>Année du baccalaureat</MenuItem> |
|
||||
<MenuItem value={`serie`}>Série</MenuItem> |
|
||||
<MenuItem value={`code_redoublement`}>Code redoublement</MenuItem> |
|
||||
<MenuItem value={`boursier`}>Boursier</MenuItem> |
|
||||
<MenuItem value={`domaine`}>Domaine</MenuItem> |
|
||||
<MenuItem value={`contact`}>Contact</MenuItem> |
|
||||
<MenuItem value={`parcours`}>Parcours</MenuItem> |
|
||||
</Select> |
|
||||
</FormControl> |
|
||||
))} |
|
||||
</div> |
|
||||
<div |
|
||||
style={{ |
|
||||
display: 'flex', |
|
||||
alignItems: 'center', |
|
||||
justifyContent: 'center', |
|
||||
flexDirection: 'column', |
|
||||
width: '100%' |
|
||||
}} |
|
||||
> |
|
||||
<DataGrid |
|
||||
rows={dataRow} |
|
||||
columns={dynamicColumns} |
|
||||
initialState={{ pagination: { paginationModel } }} |
|
||||
onColumnVisibilityModelChange={handleColumnVisibilityChange} // Listen for column visibility changes |
|
||||
pageSizeOptions={[5, 10]} |
|
||||
disableRowSelectionOnClick |
|
||||
sx={{ |
|
||||
width: '100%', |
|
||||
height: '100%', |
|
||||
minHeight: 400, |
|
||||
maxWidth: '100%', // Prevents grid from exceeding the container width |
|
||||
'@media (max-width: 600px)': { |
|
||||
width: '100%', |
|
||||
height: 'auto', // Adjust height for smaller screens |
|
||||
fontSize: '0.8rem' // Smaller font size for better readability on small screens |
|
||||
}, |
|
||||
'@media (max-width: 960px)': { |
|
||||
fontSize: '1rem' // Adjust font size for medium screens |
|
||||
} |
|
||||
}} |
|
||||
slots={{ toolbar: CustomBar }} |
|
||||
slotProps={{ toolbar: { onImport: handleImport } }} |
|
||||
localeText={frFR.components.MuiDataGrid.defaultProps.localeText} |
|
||||
/> |
|
||||
</div> |
|
||||
</ThemeProvider> |
|
||||
)} |
|
||||
</Box> |
|
||||
</Box> |
|
||||
</div> |
|
||||
) |
|
||||
} |
|
||||
|
|
||||
export default ExportEtudiants |
|
||||
@ -1,247 +0,0 @@ |
|||||
import React, { useRef, useState } from 'react' |
|
||||
import classe from '../assets/Login.module.css' |
|
||||
import { FaEnvelope, FaLock, FaLockOpen } from 'react-icons/fa' |
|
||||
import { Link } from 'react-router-dom' |
|
||||
import { |
|
||||
Modal, |
|
||||
Box, |
|
||||
Container, |
|
||||
Grid, |
|
||||
Card, |
|
||||
Typography, |
|
||||
TextField, |
|
||||
Button, |
|
||||
InputAdornment |
|
||||
} from '@mui/material' |
|
||||
import { validationForgotPassword } from './validation/ForgotPassword' |
|
||||
|
|
||||
const ForgotPassword = () => { |
|
||||
/** |
|
||||
* hook to store data from the input field |
|
||||
*/ |
|
||||
const [email, setEmail] = useState() |
|
||||
const [password, setPassword] = useState() |
|
||||
const [passwordConfirmation, setPasswordConfirmation] = useState() |
|
||||
|
|
||||
/** |
|
||||
* hook to open modal and set the sattus and message |
|
||||
*/ |
|
||||
const [open, setOpen] = useState(false) |
|
||||
const [message, setMessage] = useState('') |
|
||||
const [status, setStatus] = useState(null) |
|
||||
|
|
||||
const handleClose = () => setOpen(false) |
|
||||
|
|
||||
/** |
|
||||
* ref for our email and password, confirm password input and the error span |
|
||||
*/ |
|
||||
const emailRef = useRef() |
|
||||
const passwordRef = useRef() |
|
||||
const passwordConfirmationRef = useRef() |
|
||||
const emailError = useRef() |
|
||||
const passwordError = useRef() |
|
||||
const passwordConfirmationError = useRef() |
|
||||
|
|
||||
/** |
|
||||
* function to return the view Modal |
|
||||
* |
|
||||
* @returns {JSX} |
|
||||
*/ |
|
||||
const modals = () => ( |
|
||||
<Modal |
|
||||
open={open} |
|
||||
onClose={handleClose} |
|
||||
aria-labelledby="modal-title" |
|
||||
aria-describedby="modal-description" |
|
||||
> |
|
||||
<Box |
|
||||
sx={{ |
|
||||
position: 'absolute', |
|
||||
top: '50%', |
|
||||
left: '50%', |
|
||||
transform: 'translate(-50%, -50%)', |
|
||||
width: 400, |
|
||||
bgcolor: 'background.paper', |
|
||||
boxShadow: 24, |
|
||||
p: 4 |
|
||||
}} |
|
||||
> |
|
||||
<Typography id="modal-title" variant="h6" component="h2"> |
|
||||
{status === 200 ? 'Success' : 'Error'} |
|
||||
</Typography> |
|
||||
<Typography id="modal-description" sx={{ mt: 2 }}> |
|
||||
{message} |
|
||||
</Typography> |
|
||||
<Button onClick={handleClose}>Close</Button> |
|
||||
</Box> |
|
||||
</Modal> |
|
||||
) |
|
||||
|
|
||||
/** |
|
||||
* function to send the email verification |
|
||||
* to change the password and redirect the user to login |
|
||||
* |
|
||||
* @param {any} e |
|
||||
*/ |
|
||||
const verification = async (e) => { |
|
||||
e.preventDefault() |
|
||||
|
|
||||
let validation = validationForgotPassword( |
|
||||
emailRef.current, |
|
||||
passwordRef.current, |
|
||||
passwordConfirmationRef.current, |
|
||||
emailError.current, |
|
||||
passwordError.current, |
|
||||
passwordConfirmationError.current |
|
||||
) |
|
||||
console.log(validation) |
|
||||
if (validation === true) { |
|
||||
const response = await window.allUser.forgotPassword({ |
|
||||
email, |
|
||||
password, |
|
||||
passwordConfirmation |
|
||||
}) |
|
||||
|
|
||||
if (response.status === 200) { |
|
||||
setMessage(response.message) |
|
||||
setStatus(200) |
|
||||
} else { |
|
||||
setMessage(response.message) |
|
||||
setStatus(response.status) |
|
||||
} |
|
||||
setOpen(true) |
|
||||
console.log(response) |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
return ( |
|
||||
<Container |
|
||||
maxWidth={false} |
|
||||
sx={{ minHeight: '100vh', display: 'flex', alignItems: 'center', justifyContent: 'center' }} |
|
||||
className={classe.container} |
|
||||
> |
|
||||
{modals()} |
|
||||
<Grid container justifyContent="center"> |
|
||||
<Grid item xs={12} md={6} lg={4}> |
|
||||
<Card className={`p-4 shadow ${classe.cards}`} sx={{ padding: 4, boxShadow: 3 }}> |
|
||||
<Typography variant="h5" align="center" gutterBottom> |
|
||||
Changer de mot de passe |
|
||||
</Typography> |
|
||||
<form onSubmit={verification} className={classe.formulaireLogin}> |
|
||||
<TextField |
|
||||
label="Email" |
|
||||
variant="outlined" |
|
||||
type="text" |
|
||||
color="secondary" |
|
||||
className={classe.input} |
|
||||
fullWidth |
|
||||
margin="normal" |
|
||||
InputProps={{ |
|
||||
startAdornment: ( |
|
||||
<InputAdornment position="start"> |
|
||||
<FaEnvelope style={{ color: 'white' }} /> |
|
||||
</InputAdornment> |
|
||||
) |
|
||||
}} |
|
||||
sx={{ |
|
||||
'& .MuiOutlinedInput-root': { |
|
||||
'& fieldset': { |
|
||||
borderColor: 'white' // Set the border color when not focused |
|
||||
}, |
|
||||
'&:hover fieldset': { |
|
||||
borderColor: 'rgb(156, 39, 176)' // Set the border color on hover |
|
||||
} |
|
||||
} |
|
||||
}} |
|
||||
placeholder="Entrer email" |
|
||||
onChange={(e) => setEmail(e.target.value)} |
|
||||
inputRef={emailRef} |
|
||||
/> |
|
||||
<span ref={emailError} className="text-danger"></span> |
|
||||
|
|
||||
<TextField |
|
||||
label="Nouveau Mot de passe" |
|
||||
variant="outlined" |
|
||||
type="password" |
|
||||
className={classe.input} |
|
||||
color="secondary" |
|
||||
fullWidth |
|
||||
margin="normal" |
|
||||
InputProps={{ |
|
||||
startAdornment: ( |
|
||||
<InputAdornment position="start"> |
|
||||
<FaLock style={{ color: 'white' }} /> |
|
||||
</InputAdornment> |
|
||||
) |
|
||||
}} |
|
||||
sx={{ |
|
||||
'& .MuiOutlinedInput-root': { |
|
||||
'& fieldset': { |
|
||||
borderColor: 'white' // Set the border color when not focused |
|
||||
}, |
|
||||
'&:hover fieldset': { |
|
||||
borderColor: 'rgb(156, 39, 176)' // Set the border color on hover |
|
||||
} |
|
||||
} |
|
||||
}} |
|
||||
placeholder="Mot de passe" |
|
||||
onChange={(e) => setPassword(e.target.value)} |
|
||||
inputRef={passwordRef} |
|
||||
/> |
|
||||
<span ref={passwordError} className="text-danger"></span> |
|
||||
|
|
||||
<TextField |
|
||||
label="Confirmation Mot de passe" |
|
||||
variant="outlined" |
|
||||
type="password" |
|
||||
color="secondary" |
|
||||
fullWidth |
|
||||
margin="normal" |
|
||||
InputProps={{ |
|
||||
startAdornment: ( |
|
||||
<InputAdornment position="start"> |
|
||||
<FaLockOpen style={{ color: 'white' }} /> |
|
||||
</InputAdornment> |
|
||||
) |
|
||||
}} |
|
||||
sx={{ |
|
||||
'& .MuiOutlinedInput-root': { |
|
||||
'& fieldset': { |
|
||||
borderColor: 'white' // Set the border color when not focused |
|
||||
}, |
|
||||
'&:hover fieldset': { |
|
||||
borderColor: 'rgb(156, 39, 176)' // Set the border color on hover |
|
||||
} |
|
||||
} |
|
||||
}} |
|
||||
className={classe.input} |
|
||||
placeholder="Confirmation Mot de passe" |
|
||||
onChange={(e) => setPasswordConfirmation(e.target.value)} |
|
||||
inputRef={passwordConfirmationRef} |
|
||||
/> |
|
||||
<span ref={passwordConfirmationError} className="text-danger"></span> |
|
||||
|
|
||||
<Button |
|
||||
variant="contained" |
|
||||
color="secondary" |
|
||||
type="submit" |
|
||||
fullWidth |
|
||||
sx={{ marginTop: 2 }} |
|
||||
> |
|
||||
Modifier |
|
||||
</Button> |
|
||||
|
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between', marginTop: '1rem' }}> |
|
||||
<Link to="/login" style={{ color: '#FFFFFF' }}> |
|
||||
Se connecter |
|
||||
</Link> |
|
||||
</div> |
|
||||
</form> |
|
||||
</Card> |
|
||||
</Grid> |
|
||||
</Grid> |
|
||||
</Container> |
|
||||
) |
|
||||
} |
|
||||
|
|
||||
export default ForgotPassword |
|
||||
@ -1,194 +0,0 @@ |
|||||
import React, { useEffect, useState } from 'react' |
|
||||
import classe from '../assets/AllStyleComponents.module.css' |
|
||||
import { Bar } from 'react-chartjs-2' |
|
||||
import { |
|
||||
Chart as ChartJS, |
|
||||
CategoryScale, |
|
||||
LinearScale, |
|
||||
BarElement, |
|
||||
Title, |
|
||||
Tooltip, |
|
||||
Legend |
|
||||
} from 'chart.js' |
|
||||
import classeHome from '../assets/Home.module.css' |
|
||||
import dclass from '../assets/Dashboard.module.css' |
|
||||
import dayjs from 'dayjs' |
|
||||
import MenuItem from '@mui/material/MenuItem' |
|
||||
import FormControl from '@mui/material/FormControl' |
|
||||
import Select from '@mui/material/Select' |
|
||||
import InputLabel from '@mui/material/InputLabel' |
|
||||
|
|
||||
ChartJS.register(CategoryScale, LinearScale, BarElement, Title, Tooltip, Legend) |
|
||||
|
|
||||
const Home = () => { |
|
||||
const [etudiants, setEtudiants] = useState([]) |
|
||||
const [niveau, setNiveau] = useState([]) |
|
||||
const [annee_scolaire, setAnnee_scolaire] = useState([]) |
|
||||
const [originalEtudiants, setOriginalEtudiants] = useState([]) |
|
||||
// Get the current year |
|
||||
const currentYear = dayjs().year() |
|
||||
|
|
||||
useEffect(() => { |
|
||||
// Fetch data and update state |
|
||||
window.etudiants.getDataToDashboards().then((response) => { |
|
||||
setEtudiants(response.etudiants) |
|
||||
setOriginalEtudiants(response.etudiants) |
|
||||
setNiveau(response.niveau) |
|
||||
setAnnee_scolaire(response.anne_scolaire) |
|
||||
}) |
|
||||
}, []) |
|
||||
|
|
||||
// filter all data |
|
||||
|
|
||||
// ordre des colones de filtre |
|
||||
const desiredOrder = ['L1', 'L2', 'L3', 'M1', 'M2', 'D1', 'D2', 'D3'] |
|
||||
let allNiveau = niveau.map((item) => item.nom) |
|
||||
allNiveau.sort((a, b) => desiredOrder.indexOf(a) - desiredOrder.indexOf(b)) |
|
||||
|
|
||||
const studentGrades = {} |
|
||||
|
|
||||
// Loop through allNiveau and set the number of students for each key |
|
||||
allNiveau.forEach((niveauNom) => { |
|
||||
// Filter etudiants based on the matching niveau |
|
||||
const studentCount = etudiants.filter((etudiant) => etudiant.niveau === niveauNom).length |
|
||||
|
|
||||
// Assign the student count as the value for the corresponding key in studentGrades |
|
||||
studentGrades[niveauNom] = studentCount |
|
||||
}) |
|
||||
const studentCounts = Object.values(studentGrades) |
|
||||
|
|
||||
// Find the maximum value using Math.max |
|
||||
const maxStudentCount = Math.max(...studentCounts) |
|
||||
|
|
||||
const FilterAnneeScolaire = (e) => { |
|
||||
let annee_scolaire = e.target.value |
|
||||
const filteredEtudiants = originalEtudiants.filter( |
|
||||
(etudiant) => etudiant.annee_scolaire === annee_scolaire |
|
||||
) |
|
||||
setEtudiants(filteredEtudiants) |
|
||||
if (annee_scolaire == 'general') { |
|
||||
setEtudiants(originalEtudiants) |
|
||||
} |
|
||||
} |
|
||||
// end filter all data |
|
||||
|
|
||||
// Calculate the number of classes |
|
||||
const numberOfClasses = Object.keys(studentGrades).length |
|
||||
|
|
||||
// Data for the Bar chart |
|
||||
const data = { |
|
||||
labels: Object.keys(studentGrades), // Class levels |
|
||||
datasets: [ |
|
||||
{ |
|
||||
label: 'Nombre des étudiants', |
|
||||
data: Object.values(studentGrades), // Student counts |
|
||||
backgroundColor: [ |
|
||||
'#FF6384', |
|
||||
'#36A2EB', |
|
||||
'#FFCE56', |
|
||||
'#4BC0C0', |
|
||||
'#9966FF', |
|
||||
'#FF9F40', |
|
||||
'#C9CBCF', |
|
||||
'#00A36C' |
|
||||
], // Colors for each bar |
|
||||
borderColor: [ |
|
||||
'#FF6384', |
|
||||
'#36A2EB', |
|
||||
'#FFCE56', |
|
||||
'#4BC0C0', |
|
||||
'#9966FF', |
|
||||
'#FF9F40', |
|
||||
'#C9CBCF', |
|
||||
'#00A36C' |
|
||||
], |
|
||||
borderWidth: 1 |
|
||||
} |
|
||||
] |
|
||||
} |
|
||||
|
|
||||
// Chart options |
|
||||
const options = { |
|
||||
responsive: true, |
|
||||
plugins: { |
|
||||
legend: { |
|
||||
position: 'top' |
|
||||
}, |
|
||||
title: { |
|
||||
display: true, |
|
||||
text: `Nombre des niveau (Total : ${numberOfClasses})` |
|
||||
} |
|
||||
}, |
|
||||
scales: { |
|
||||
y: { |
|
||||
beginAtZero: true, |
|
||||
max: maxStudentCount // Set max value for the Y axis |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
return ( |
|
||||
<div className={classe.mainHome}> |
|
||||
<div className={classeHome.header}> |
|
||||
<div className={classe.h1style}> |
|
||||
<div className={classeHome.blockTitle}> |
|
||||
<h1>Dashboard</h1> |
|
||||
<FormControl |
|
||||
sx={{ |
|
||||
m: 1, |
|
||||
width: '30%', |
|
||||
'& .MuiOutlinedInput-root': { |
|
||||
'&:hover fieldset': { |
|
||||
borderColor: '#ff9800' // Set the border color on hover |
|
||||
} |
|
||||
} |
|
||||
}} |
|
||||
size="small" |
|
||||
variant="outlined" |
|
||||
> |
|
||||
<InputLabel |
|
||||
id="demo-select-small-label" |
|
||||
sx={{ color: 'black', fontSize: '15px', textTransform: 'capitalize' }} |
|
||||
color="warning" |
|
||||
> |
|
||||
Année Scolaire |
|
||||
</InputLabel> |
|
||||
<Select |
|
||||
labelId="demo-select-small-label" |
|
||||
id="demo-select-small" |
|
||||
label="Année Scolaire" |
|
||||
color="warning" |
|
||||
defaultValue={'general'} |
|
||||
onChange={FilterAnneeScolaire} |
|
||||
sx={{ |
|
||||
background: 'white', |
|
||||
textTransform: 'capitalize', |
|
||||
display: 'flex', |
|
||||
alignItems: 'center', // Align icon and text vertically |
|
||||
'& .MuiSelect-icon': { |
|
||||
marginLeft: 'auto' // Keep the dropdown arrow to the right |
|
||||
} |
|
||||
}} |
|
||||
> |
|
||||
<MenuItem value="general" selected> |
|
||||
<em>Géneral</em> |
|
||||
</MenuItem> |
|
||||
{annee_scolaire.map((niveau) => ( |
|
||||
<MenuItem value={niveau.annee_scolaire} key={niveau.annee_scolaire}> |
|
||||
{niveau.annee_scolaire} |
|
||||
</MenuItem> |
|
||||
))} |
|
||||
</Select> |
|
||||
</FormControl> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
{/* display each bars */} |
|
||||
<div className={dclass.display}> |
|
||||
<Bar data={data} options={options} /> |
|
||||
</div> |
|
||||
</div> |
|
||||
) |
|
||||
} |
|
||||
|
|
||||
export default Home |
|
||||
@ -1,489 +0,0 @@ |
|||||
import React, { useEffect, useState } from 'react' |
|
||||
import classe from '../assets/AllStyleComponents.module.css' |
|
||||
import classeAdd from '../assets/AddStudent.module.css' |
|
||||
import classeHome from '../assets/Home.module.css' |
|
||||
import { Box, Button, Typography, ThemeProvider, Modal } from '@mui/material' |
|
||||
import { Link } from 'react-router-dom' |
|
||||
import { FaCloudDownloadAlt, FaCloudUploadAlt, FaFileExcel } from 'react-icons/fa' |
|
||||
import { IoMdReturnRight } from 'react-icons/io' |
|
||||
import { styled, createTheme } from '@mui/material/styles' |
|
||||
import * as XLSX from 'xlsx' |
|
||||
import Papa from 'papaparse' |
|
||||
import { DataGrid, GridToolbarContainer, GridToolbarColumnsButton } from '@mui/x-data-grid' |
|
||||
import { frFR } from '@mui/x-data-grid/locales' |
|
||||
import CustomBar from './CustomBar' |
|
||||
import dayjs from 'dayjs' |
|
||||
import svgSuccess from '../assets/success.svg' |
|
||||
import svgError from '../assets/error.svg' |
|
||||
import { MenuItem, Select, FormControl, InputLabel } from '@mui/material' |
|
||||
|
|
||||
const ImportMatiere = () => { |
|
||||
const VisuallyHiddenInput = styled('input')({ |
|
||||
clip: 'rect(0 0 0 0)', |
|
||||
clipPath: 'inset(50%)', |
|
||||
height: 1, |
|
||||
overflow: 'hidden', |
|
||||
position: 'absolute', |
|
||||
bottom: 0, |
|
||||
left: 0, |
|
||||
whiteSpace: 'nowrap', |
|
||||
width: 1 |
|
||||
}) |
|
||||
|
|
||||
const [error, setError] = useState('') |
|
||||
const [isInserted, setIsinserted] = useState(true) |
|
||||
const [message, setMessage] = useState('') |
|
||||
const [tableData, setTableData] = useState([]) |
|
||||
const [files, setFiles] = useState() |
|
||||
const [header, setHeader] = useState([]) |
|
||||
let field = [] |
|
||||
const [dynamicColumns, setColumns] = useState([]) |
|
||||
|
|
||||
for (let index = 0; index < header.length; index++) { |
|
||||
field.push(header[index].toLowerCase().replace(/\s+/g, '_')) |
|
||||
} |
|
||||
|
|
||||
useEffect(() => { |
|
||||
setColumns( |
|
||||
header.map((col, index) => ({ |
|
||||
field: field[index], // Converts the header text to field names (e.g., "Nom" -> "nom") |
|
||||
headerName: col, // Display the header as is |
|
||||
width: 150 // Adjust the width as needed |
|
||||
})) |
|
||||
) |
|
||||
}, [header]) |
|
||||
|
|
||||
const theme = createTheme({ |
|
||||
components: { |
|
||||
MuiIconButton: { |
|
||||
styleOverrides: { |
|
||||
root: { |
|
||||
color: 'gray' // Toolbar icons color |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
MuiButton: { |
|
||||
styleOverrides: { |
|
||||
root: { |
|
||||
color: '#121212' // Button text color |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
}) |
|
||||
|
|
||||
const paginationModel = { page: 0, pageSize: 5 } |
|
||||
|
|
||||
// Assuming `tableData` is an array where each entry corresponds to a student's data |
|
||||
const dataRow = tableData.map((etudiant, index) => { |
|
||||
// Dynamically create an object with fields from the header and data from `etudiant` |
|
||||
let row = { id: index + 1 } // Unique ID for each row |
|
||||
|
|
||||
field.forEach((fieldName, idx) => { |
|
||||
if (fieldName === 'date_de_naissance') { |
|
||||
row[fieldName] = dayjs(etudiant[idx]).format('DD-MM-YYYY') // Format date |
|
||||
} else { |
|
||||
row[fieldName] = etudiant[idx] // Assign value to field dynamically |
|
||||
} |
|
||||
}) |
|
||||
|
|
||||
return row |
|
||||
}) |
|
||||
|
|
||||
const handleFileChange = (event) => { |
|
||||
const file = event.target.files[0] |
|
||||
setFiles(event.target.files[0]) |
|
||||
if (!file) { |
|
||||
setError('No file selected') |
|
||||
return |
|
||||
} |
|
||||
|
|
||||
const fileExtension = file.name.split('.').pop().toLowerCase() |
|
||||
|
|
||||
if (fileExtension === 'xlsx') { |
|
||||
const reader = new FileReader() |
|
||||
reader.onload = (e) => { |
|
||||
const data = new Uint8Array(e.target.result) |
|
||||
const workbook = XLSX.read(data, { type: 'array' }) |
|
||||
|
|
||||
// Extract data from all sheets and combine |
|
||||
const allData = [] |
|
||||
workbook.SheetNames.forEach((sheetName) => { |
|
||||
const worksheet = workbook.Sheets[sheetName] |
|
||||
const rows = XLSX.utils.sheet_to_json(worksheet, { header: 1 }) |
|
||||
setHeader(rows[0]) // keep the header |
|
||||
allData.push(...rows.slice(1)) // Skip header row for each sheet |
|
||||
}) |
|
||||
setTableData(allData) |
|
||||
} |
|
||||
reader.readAsArrayBuffer(file) |
|
||||
} else if (fileExtension === 'csv') { |
|
||||
const reader = new FileReader() |
|
||||
reader.onload = (e) => { |
|
||||
Papa.parse(e.target.result, { |
|
||||
complete: (results) => { |
|
||||
setHeader(results.data[0]) // keep the header |
|
||||
setTableData(results.data.slice(1)) // Skip header row |
|
||||
}, |
|
||||
header: false |
|
||||
}) |
|
||||
} |
|
||||
reader.readAsText(file) |
|
||||
} else { |
|
||||
setError('Unsupported file format. Please upload .xlsx or .csv file.') |
|
||||
setTableData([]) |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
const exemplaireFileExcel = [ |
|
||||
{ |
|
||||
nom: 'matiere 1', |
|
||||
credit: 5, |
|
||||
uniter: 'MUI', |
|
||||
heure: 39 |
|
||||
}, |
|
||||
{ |
|
||||
nom: 'matiere 2', |
|
||||
credit: 5, |
|
||||
uniter: 'MUI', |
|
||||
heure: 39 |
|
||||
}, |
|
||||
{ |
|
||||
nom: 'matiere 3', |
|
||||
credit: 5, |
|
||||
uniter: 'MUI', |
|
||||
heure: 39 |
|
||||
}, |
|
||||
{ |
|
||||
nom: 'matiere 4', |
|
||||
credit: 5, |
|
||||
uniter: 'MUI', |
|
||||
heure: 39 |
|
||||
}, |
|
||||
{ |
|
||||
nom: 'matiere 5', |
|
||||
credit: 5, |
|
||||
uniter: 'MUI', |
|
||||
heure: 39 |
|
||||
} |
|
||||
] |
|
||||
|
|
||||
const convertToExcel = () => { |
|
||||
// convert json to sheet |
|
||||
const worksheet = XLSX.utils.json_to_sheet(exemplaireFileExcel) |
|
||||
|
|
||||
// Create a new workbook and append the worksheet |
|
||||
const workbook = XLSX.utils.book_new() |
|
||||
XLSX.utils.book_append_sheet(workbook, worksheet, 'Sheet1') |
|
||||
|
|
||||
// Write the workbook to a Blob and create a download link |
|
||||
const excelBuffer = XLSX.write(workbook, { bookType: 'xlsx', type: 'array' }) |
|
||||
const data = new Blob([excelBuffer], { type: 'application/octet-stream' }) |
|
||||
const url = URL.createObjectURL(data) |
|
||||
|
|
||||
// Trigger a download |
|
||||
const link = document.createElement('a') |
|
||||
link.href = url |
|
||||
link.download = 'exemplaier_matiere.xlsx' |
|
||||
link.click() |
|
||||
|
|
||||
// Clean up |
|
||||
URL.revokeObjectURL(url) |
|
||||
} |
|
||||
|
|
||||
const handleColumnVisibilityChange = (model) => { |
|
||||
// Get the currently visible columns |
|
||||
const visibleColumns = dynamicColumns.filter((column) => model[column.field] !== false) |
|
||||
|
|
||||
// Create a new Excel file with visible columns |
|
||||
createExcelFile(visibleColumns) |
|
||||
} |
|
||||
|
|
||||
const createExcelFile = (columns) => { |
|
||||
// Extract and set the header |
|
||||
const header = columns.reduce((acc, col) => { |
|
||||
acc[col.field] = col.headerName || col.field // Use headerName or field as default |
|
||||
return acc |
|
||||
}, {}) |
|
||||
|
|
||||
// Map the data rows to match the extracted headers |
|
||||
const worksheetData = dataRow.map((row) => { |
|
||||
const filteredRow = {} |
|
||||
columns.forEach((col) => { |
|
||||
const headerName = header[col.field] |
|
||||
filteredRow[headerName] = row[col.field] |
|
||||
}) |
|
||||
return filteredRow |
|
||||
}) |
|
||||
|
|
||||
// Create a worksheet from the data |
|
||||
const ws = XLSX.utils.json_to_sheet(worksheetData) |
|
||||
|
|
||||
// Create a workbook and add the worksheet to it |
|
||||
const wb = XLSX.utils.book_new() |
|
||||
XLSX.utils.book_append_sheet(wb, ws, 'Sheet1') |
|
||||
|
|
||||
// Generate the Excel file as binary data |
|
||||
const excelFile = XLSX.write(wb, { bookType: 'xlsx', type: 'array' }) |
|
||||
|
|
||||
// Create a Blob for the Excel data |
|
||||
const blob = new Blob([excelFile], { |
|
||||
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' |
|
||||
}) |
|
||||
|
|
||||
// Create a link element to trigger the download |
|
||||
const url = URL.createObjectURL(blob) |
|
||||
const link = document.createElement('a') |
|
||||
link.href = url |
|
||||
link.download = 'original-file.xlsx' // Original file name or any desired name |
|
||||
document.body.appendChild(link) |
|
||||
link.click() |
|
||||
document.body.removeChild(link) |
|
||||
URL.revokeObjectURL(url) // Clean up |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* fonction qui envoye dans le back |
|
||||
*/ |
|
||||
const handleImport = async () => { |
|
||||
// Code to handle file import (can open a file dialog or call handleFileChange) |
|
||||
|
|
||||
let response = await window.matieres.importExcel(files.path) |
|
||||
|
|
||||
console.log(response) |
|
||||
|
|
||||
if (response.message) { |
|
||||
setMessage(response.message) |
|
||||
} |
|
||||
|
|
||||
if (response.error) { |
|
||||
setIsinserted(true) |
|
||||
setOpen(true) |
|
||||
setTableData([]) |
|
||||
} else { |
|
||||
setIsinserted(false) |
|
||||
setOpen(true) |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* hook to open modal |
|
||||
*/ |
|
||||
const [open, setOpen] = useState(false) |
|
||||
|
|
||||
/** |
|
||||
* function to close modal |
|
||||
*/ |
|
||||
const handleClose = () => setOpen(false) |
|
||||
|
|
||||
/** |
|
||||
* function to return the view Modal |
|
||||
* |
|
||||
* @returns {JSX} |
|
||||
*/ |
|
||||
const modals = () => ( |
|
||||
<Modal |
|
||||
open={open} |
|
||||
onClose={handleClose} |
|
||||
aria-labelledby="modal-title" |
|
||||
aria-describedby="modal-description" |
|
||||
> |
|
||||
<Box |
|
||||
sx={{ |
|
||||
position: 'absolute', |
|
||||
top: '50%', |
|
||||
left: '50%', |
|
||||
transform: 'translate(-50%, -50%)', |
|
||||
width: 450, |
|
||||
bgcolor: 'background.paper', |
|
||||
boxShadow: 24, |
|
||||
p: 4 |
|
||||
}} |
|
||||
> |
|
||||
{isInserted ? ( |
|
||||
<Typography |
|
||||
style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: '10px' }} |
|
||||
> |
|
||||
<img src={svgSuccess} alt="" width={50} height={50} />{' '} |
|
||||
<span>Importation a été effectuée avec succès</span> |
|
||||
</Typography> |
|
||||
) : ( |
|
||||
<Typography |
|
||||
style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: '10px' }} |
|
||||
> |
|
||||
<img src={svgError} alt="" width={50} height={50} />{' '} |
|
||||
<span> |
|
||||
L'importation n'a pas été effectuée |
|
||||
<br /> |
|
||||
{message} |
|
||||
</span> |
|
||||
</Typography> |
|
||||
)} |
|
||||
<Box |
|
||||
sx={{ |
|
||||
marginTop: '2%', |
|
||||
display: 'flex', |
|
||||
gap: '20px', |
|
||||
alignItems: 'end', |
|
||||
justifyContent: 'flex-end' |
|
||||
}} |
|
||||
> |
|
||||
<Button onClick={handleClose} color="warning" variant="contained"> |
|
||||
OK |
|
||||
</Button> |
|
||||
</Box> |
|
||||
</Box> |
|
||||
</Modal> |
|
||||
) |
|
||||
|
|
||||
// Handle header name change for a specific field and auto-export to Excel |
|
||||
const handleHeaderChange = (field, newHeaderName) => { |
|
||||
setColumns((prevColumns) => |
|
||||
prevColumns.map((col) => (col.field === field ? { ...col, headerName: newHeaderName } : col)) |
|
||||
) |
|
||||
} |
|
||||
|
|
||||
return ( |
|
||||
<div className={classe.mainHome}> |
|
||||
{modals()} |
|
||||
<div className={classeAdd.header}> |
|
||||
<div className={classe.h1style}> |
|
||||
<div className={classeHome.blockTitle}> |
|
||||
<h1 style={{ display: 'flex', alignItems: 'center', gap: '10px' }}> |
|
||||
<FaFileExcel /> |
|
||||
Importation de données |
|
||||
</h1> |
|
||||
<Link to={'/addmatiere'}> |
|
||||
<Button color="warning" variant="contained"> |
|
||||
<IoMdReturnRight style={{ fontSize: '20px' }} /> |
|
||||
</Button> |
|
||||
</Link> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
|
|
||||
{/* displaying */} |
|
||||
<Box |
|
||||
sx={{ |
|
||||
position: 'absolute', |
|
||||
top: '52%', |
|
||||
left: '52%', |
|
||||
transform: 'translate(-50%, -50%)', |
|
||||
width: '95%', |
|
||||
borderRadius: '2%', |
|
||||
bgcolor: 'background.paper', |
|
||||
boxShadow: 24, |
|
||||
p: 4 |
|
||||
}} |
|
||||
> |
|
||||
<Box sx={{ mt: 2, display: 'flex', alignItems: 'center', flexDirection: 'column' }}> |
|
||||
<div style={{ display: 'flex', gap: '20px', marginBottom: '20px' }}> |
|
||||
<Button |
|
||||
component="label" |
|
||||
variant="contained" |
|
||||
color="warning" |
|
||||
startIcon={<FaCloudUploadAlt />} |
|
||||
> |
|
||||
Charger un fichier excel |
|
||||
<VisuallyHiddenInput type="file" onChange={handleFileChange} accept=".xlsx, .csv" /> |
|
||||
</Button> |
|
||||
<Button |
|
||||
component="label" |
|
||||
variant="contained" |
|
||||
color="error" |
|
||||
startIcon={<FaCloudDownloadAlt />} |
|
||||
onClick={convertToExcel} |
|
||||
> |
|
||||
Télécharger le modèle d'import |
|
||||
</Button> |
|
||||
</div> |
|
||||
{error && ( |
|
||||
<Typography color="error" variant="body2"> |
|
||||
{error} |
|
||||
</Typography> |
|
||||
)} |
|
||||
{tableData.length > 0 && ( |
|
||||
<ThemeProvider theme={theme}> |
|
||||
<div |
|
||||
style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }} |
|
||||
> |
|
||||
{/* Dropdowns for each column */} |
|
||||
{dynamicColumns.map((col) => ( |
|
||||
<FormControl |
|
||||
key={col.field} |
|
||||
sx={{ |
|
||||
m: 1, |
|
||||
width: '100%', |
|
||||
'& .MuiOutlinedInput-root': { |
|
||||
'&:hover fieldset': { |
|
||||
borderColor: '#ff9800' // Set the border color on hover |
|
||||
} |
|
||||
} |
|
||||
}} |
|
||||
size="small" |
|
||||
color="warning" |
|
||||
variant="outlined" |
|
||||
style={{ margin: 10, width: 200 }} |
|
||||
> |
|
||||
<InputLabel id={col.headerName}>{col.headerName}</InputLabel> |
|
||||
<Select |
|
||||
labelId={col.headerName} |
|
||||
value={col.headerName} |
|
||||
label={col.headerName} |
|
||||
color="warning" |
|
||||
name={col.headerName} |
|
||||
onChange={(e) => handleHeaderChange(col.field, e.target.value)} |
|
||||
> |
|
||||
<MenuItem value={col.headerName}>{col.headerName}</MenuItem> |
|
||||
<MenuItem value={'nom'}>nom</MenuItem> |
|
||||
<MenuItem value={'credit'}>credit</MenuItem> |
|
||||
<MenuItem value={`uniter`}>uniter d'enseignement</MenuItem> |
|
||||
<MenuItem value={`ue`}>UE</MenuItem> |
|
||||
</Select> |
|
||||
</FormControl> |
|
||||
))} |
|
||||
</div> |
|
||||
<div |
|
||||
style={{ |
|
||||
display: 'flex', |
|
||||
alignItems: 'center', |
|
||||
justifyContent: 'center', |
|
||||
flexDirection: 'column', |
|
||||
width: '100%' |
|
||||
}} |
|
||||
> |
|
||||
<DataGrid |
|
||||
rows={dataRow} |
|
||||
columns={dynamicColumns} |
|
||||
initialState={{ pagination: { paginationModel } }} |
|
||||
onColumnVisibilityModelChange={handleColumnVisibilityChange} // Listen for column visibility changes |
|
||||
pageSizeOptions={[5, 10]} |
|
||||
disableRowSelectionOnClick |
|
||||
sx={{ |
|
||||
width: '100%', |
|
||||
height: '100%', |
|
||||
minHeight: 400, |
|
||||
maxWidth: '100%', // Prevents grid from exceeding the container width |
|
||||
'@media (max-width: 600px)': { |
|
||||
width: '100%', |
|
||||
height: 'auto', // Adjust height for smaller screens |
|
||||
fontSize: '0.8rem' // Smaller font size for better readability on small screens |
|
||||
}, |
|
||||
'@media (max-width: 960px)': { |
|
||||
fontSize: '1rem' // Adjust font size for medium screens |
|
||||
} |
|
||||
}} |
|
||||
slots={{ toolbar: CustomBar }} |
|
||||
slotProps={{ toolbar: { onImport: handleImport } }} |
|
||||
localeText={frFR.components.MuiDataGrid.defaultProps.localeText} |
|
||||
/> |
|
||||
</div> |
|
||||
</ThemeProvider> |
|
||||
)} |
|
||||
</Box> |
|
||||
</Box> |
|
||||
</div> |
|
||||
) |
|
||||
} |
|
||||
|
|
||||
export default ImportMatiere |
|
||||
@ -1,397 +0,0 @@ |
|||||
import React, { useState } from 'react' |
|
||||
import classe from '../assets/AllStyleComponents.module.css' |
|
||||
import classeAdd from '../assets/AddStudent.module.css' |
|
||||
import classeHome from '../assets/Home.module.css' |
|
||||
import { Box, Button, Typography, ThemeProvider, Modal } from '@mui/material' |
|
||||
import { Link } from 'react-router-dom' |
|
||||
import { FaCloudDownloadAlt, FaCloudUploadAlt, FaFileExcel } from 'react-icons/fa' |
|
||||
import { IoMdReturnRight } from 'react-icons/io' |
|
||||
import { styled, createTheme } from '@mui/material/styles' |
|
||||
import * as XLSX from 'xlsx' |
|
||||
import Papa from 'papaparse' |
|
||||
import { DataGrid, GridToolbarContainer, GridToolbarColumnsButton } from '@mui/x-data-grid' |
|
||||
import { frFR } from '@mui/x-data-grid/locales' |
|
||||
import CustomBar from './CustomBar' |
|
||||
import dayjs from 'dayjs' |
|
||||
import svgSuccess from '../assets/success.svg' |
|
||||
|
|
||||
const ImportNiveau = () => { |
|
||||
const VisuallyHiddenInput = styled('input')({ |
|
||||
clip: 'rect(0 0 0 0)', |
|
||||
clipPath: 'inset(50%)', |
|
||||
height: 1, |
|
||||
overflow: 'hidden', |
|
||||
position: 'absolute', |
|
||||
bottom: 0, |
|
||||
left: 0, |
|
||||
whiteSpace: 'nowrap', |
|
||||
width: 1 |
|
||||
}) |
|
||||
|
|
||||
const [error, setError] = useState('') |
|
||||
const [tableData, setTableData] = useState([]) |
|
||||
const [files, setFiles] = useState() |
|
||||
const [header, setHeader] = useState([]) |
|
||||
let field = [] |
|
||||
|
|
||||
for (let index = 0; index < header.length; index++) { |
|
||||
field.push(header[index].toLowerCase().replace(/\s+/g, '_')) |
|
||||
} |
|
||||
|
|
||||
const dynamicColumns = header.map((col, index) => ({ |
|
||||
field: col.toLowerCase().replace(/\s+/g, '_'), // Converts the header text to field names (e.g., "Nom" -> "nom") |
|
||||
headerName: col, // Display the header as is |
|
||||
width: 150 // Adjust the width as needed |
|
||||
})) |
|
||||
|
|
||||
const theme = createTheme({ |
|
||||
components: { |
|
||||
MuiIconButton: { |
|
||||
styleOverrides: { |
|
||||
root: { |
|
||||
color: 'gray' // Toolbar icons color |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
MuiButton: { |
|
||||
styleOverrides: { |
|
||||
root: { |
|
||||
color: '#121212' // Button text color |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
}) |
|
||||
|
|
||||
const paginationModel = { page: 0, pageSize: 5 } |
|
||||
|
|
||||
// Assuming `tableData` is an array where each entry corresponds to a student's data |
|
||||
const dataRow = tableData.map((etudiant, index) => { |
|
||||
// Dynamically create an object with fields from the header and data from `etudiant` |
|
||||
let row = { id: index + 1 } // Unique ID for each row |
|
||||
|
|
||||
field.forEach((fieldName, idx) => { |
|
||||
if (fieldName === 'date_de_naissance') { |
|
||||
row[fieldName] = dayjs(etudiant[idx]).format('DD-MM-YYYY') // Format date |
|
||||
} else { |
|
||||
row[fieldName] = etudiant[idx] // Assign value to field dynamically |
|
||||
} |
|
||||
}) |
|
||||
|
|
||||
return row |
|
||||
}) |
|
||||
|
|
||||
const handleFileChange = (event) => { |
|
||||
const file = event.target.files[0] |
|
||||
setFiles(event.target.files[0]) |
|
||||
if (!file) { |
|
||||
setError('No file selected') |
|
||||
return |
|
||||
} |
|
||||
|
|
||||
const fileExtension = file.name.split('.').pop().toLowerCase() |
|
||||
|
|
||||
if (fileExtension === 'xlsx') { |
|
||||
const reader = new FileReader() |
|
||||
reader.onload = (e) => { |
|
||||
const data = new Uint8Array(e.target.result) |
|
||||
const workbook = XLSX.read(data, { type: 'array' }) |
|
||||
|
|
||||
// Extract data from all sheets and combine |
|
||||
const allData = [] |
|
||||
workbook.SheetNames.forEach((sheetName) => { |
|
||||
const worksheet = workbook.Sheets[sheetName] |
|
||||
const rows = XLSX.utils.sheet_to_json(worksheet, { header: 1 }) |
|
||||
setHeader(rows[0]) // keep the header |
|
||||
allData.push(...rows.slice(1)) // Skip header row for each sheet |
|
||||
}) |
|
||||
setTableData(allData) |
|
||||
} |
|
||||
reader.readAsArrayBuffer(file) |
|
||||
} else if (fileExtension === 'csv') { |
|
||||
const reader = new FileReader() |
|
||||
reader.onload = (e) => { |
|
||||
Papa.parse(e.target.result, { |
|
||||
complete: (results) => { |
|
||||
setHeader(results.data[0]) // keep the header |
|
||||
setTableData(results.data.slice(1)) // Skip header row |
|
||||
}, |
|
||||
header: false |
|
||||
}) |
|
||||
} |
|
||||
reader.readAsText(file) |
|
||||
} else { |
|
||||
setError('Unsupported file format. Please upload .xlsx or .csv file.') |
|
||||
setTableData([]) |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
const exemplaireFileExcel = [ |
|
||||
{ |
|
||||
Nom: 'L1' |
|
||||
}, |
|
||||
{ |
|
||||
Nom: 'L2' |
|
||||
}, |
|
||||
{ |
|
||||
Nom: 'L3' |
|
||||
}, |
|
||||
{ |
|
||||
Nom: 'M1' |
|
||||
}, |
|
||||
{ |
|
||||
Nom: 'M2' |
|
||||
} |
|
||||
] |
|
||||
|
|
||||
const convertToExcel = () => { |
|
||||
// convert json to sheet |
|
||||
const worksheet = XLSX.utils.json_to_sheet(exemplaireFileExcel) |
|
||||
|
|
||||
// Create a new workbook and append the worksheet |
|
||||
const workbook = XLSX.utils.book_new() |
|
||||
XLSX.utils.book_append_sheet(workbook, worksheet, 'Sheet1') |
|
||||
|
|
||||
// Write the workbook to a Blob and create a download link |
|
||||
const excelBuffer = XLSX.write(workbook, { bookType: 'xlsx', type: 'array' }) |
|
||||
const data = new Blob([excelBuffer], { type: 'application/octet-stream' }) |
|
||||
const url = URL.createObjectURL(data) |
|
||||
|
|
||||
// Trigger a download |
|
||||
const link = document.createElement('a') |
|
||||
link.href = url |
|
||||
link.download = 'exemplaire_niveau.xlsx' |
|
||||
link.click() |
|
||||
|
|
||||
// Clean up |
|
||||
URL.revokeObjectURL(url) |
|
||||
} |
|
||||
|
|
||||
const handleColumnVisibilityChange = (model) => { |
|
||||
// Get the currently visible columns |
|
||||
const visibleColumns = dynamicColumns.filter((column) => model[column.field] !== false) |
|
||||
|
|
||||
// Create a new Excel file with visible columns |
|
||||
createExcelFile(visibleColumns) |
|
||||
} |
|
||||
|
|
||||
const createExcelFile = (columns) => { |
|
||||
// Extract and set the header |
|
||||
const header = columns.reduce((acc, col) => { |
|
||||
acc[col.field] = col.headerName || col.field // Use headerName or field as default |
|
||||
return acc |
|
||||
}, {}) |
|
||||
|
|
||||
// Map the data rows to match the extracted headers |
|
||||
const worksheetData = dataRow.map((row) => { |
|
||||
const filteredRow = {} |
|
||||
columns.forEach((col) => { |
|
||||
const headerName = header[col.field] |
|
||||
filteredRow[headerName] = row[col.field] |
|
||||
}) |
|
||||
return filteredRow |
|
||||
}) |
|
||||
|
|
||||
// Create a worksheet from the data |
|
||||
const ws = XLSX.utils.json_to_sheet(worksheetData) |
|
||||
|
|
||||
// Create a workbook and add the worksheet to it |
|
||||
const wb = XLSX.utils.book_new() |
|
||||
XLSX.utils.book_append_sheet(wb, ws, 'Sheet1') |
|
||||
|
|
||||
// Generate the Excel file as binary data |
|
||||
const excelFile = XLSX.write(wb, { bookType: 'xlsx', type: 'array' }) |
|
||||
|
|
||||
// Create a Blob for the Excel data |
|
||||
const blob = new Blob([excelFile], { |
|
||||
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' |
|
||||
}) |
|
||||
|
|
||||
// Create a link element to trigger the download |
|
||||
const url = URL.createObjectURL(blob) |
|
||||
const link = document.createElement('a') |
|
||||
link.href = url |
|
||||
link.download = 'original-file.xlsx' // Original file name or any desired name |
|
||||
document.body.appendChild(link) |
|
||||
link.click() |
|
||||
document.body.removeChild(link) |
|
||||
URL.revokeObjectURL(url) // Clean up |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* fonction qui envoye dans le back |
|
||||
*/ |
|
||||
const handleImport = async () => { |
|
||||
// Code to handle file import (can open a file dialog or call handleFileChange) |
|
||||
|
|
||||
let response = await window.niveaus.importNiveau(files.path) |
|
||||
|
|
||||
console.log(response) |
|
||||
if (response.success) { |
|
||||
setOpen(true) |
|
||||
setTableData([]) |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* hook to open modal |
|
||||
*/ |
|
||||
const [open, setOpen] = useState(false) |
|
||||
|
|
||||
/** |
|
||||
* function to close modal |
|
||||
*/ |
|
||||
const handleClose = () => setOpen(false) |
|
||||
|
|
||||
/** |
|
||||
* function to return the view Modal |
|
||||
* |
|
||||
* @returns {JSX} |
|
||||
*/ |
|
||||
const modals = () => ( |
|
||||
<Modal |
|
||||
open={open} |
|
||||
onClose={handleClose} |
|
||||
aria-labelledby="modal-title" |
|
||||
aria-describedby="modal-description" |
|
||||
> |
|
||||
<Box |
|
||||
sx={{ |
|
||||
position: 'absolute', |
|
||||
top: '50%', |
|
||||
left: '50%', |
|
||||
transform: 'translate(-50%, -50%)', |
|
||||
width: 450, |
|
||||
bgcolor: 'background.paper', |
|
||||
boxShadow: 24, |
|
||||
p: 4 |
|
||||
}} |
|
||||
> |
|
||||
<Typography |
|
||||
style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: '10px' }} |
|
||||
> |
|
||||
<img src={svgSuccess} alt="" width={50} height={50} />{' '} |
|
||||
<span>Importation a été effectuée avec succès</span> |
|
||||
</Typography> |
|
||||
<Box |
|
||||
sx={{ |
|
||||
marginTop: '2%', |
|
||||
display: 'flex', |
|
||||
gap: '20px', |
|
||||
alignItems: 'end', |
|
||||
justifyContent: 'flex-end' |
|
||||
}} |
|
||||
> |
|
||||
<Button onClick={handleClose} color="warning" variant="contained"> |
|
||||
OK |
|
||||
</Button> |
|
||||
</Box> |
|
||||
</Box> |
|
||||
</Modal> |
|
||||
) |
|
||||
|
|
||||
return ( |
|
||||
<div className={classe.mainHome}> |
|
||||
{modals()} |
|
||||
<div className={classeAdd.header}> |
|
||||
<div className={classe.h1style}> |
|
||||
<div className={classeHome.blockTitle}> |
|
||||
<h1 style={{ display: 'flex', alignItems: 'center', gap: '10px' }}> |
|
||||
<FaFileExcel /> |
|
||||
Importation de données |
|
||||
</h1> |
|
||||
<Link to={'/addniveau'}> |
|
||||
<Button color="warning" variant="contained"> |
|
||||
<IoMdReturnRight style={{ fontSize: '20px' }} /> |
|
||||
</Button> |
|
||||
</Link> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
|
|
||||
{/* displaying */} |
|
||||
<Box |
|
||||
sx={{ |
|
||||
position: 'absolute', |
|
||||
top: '52%', |
|
||||
left: '52%', |
|
||||
transform: 'translate(-50%, -50%)', |
|
||||
width: '95%', |
|
||||
borderRadius: '2%', |
|
||||
bgcolor: 'background.paper', |
|
||||
boxShadow: 24, |
|
||||
p: 4 |
|
||||
}} |
|
||||
> |
|
||||
<Box sx={{ mt: 2, display: 'flex', alignItems: 'center', flexDirection: 'column' }}> |
|
||||
<div style={{ display: 'flex', gap: '20px', marginBottom: '20px' }}> |
|
||||
<Button |
|
||||
component="label" |
|
||||
variant="contained" |
|
||||
color="warning" |
|
||||
startIcon={<FaCloudUploadAlt />} |
|
||||
> |
|
||||
Charger un fichier excel |
|
||||
<VisuallyHiddenInput type="file" onChange={handleFileChange} accept=".xlsx, .csv" /> |
|
||||
</Button> |
|
||||
<Button |
|
||||
component="label" |
|
||||
variant="contained" |
|
||||
color="error" |
|
||||
startIcon={<FaCloudDownloadAlt />} |
|
||||
onClick={convertToExcel} |
|
||||
> |
|
||||
Télécharger le modèle d'import |
|
||||
</Button> |
|
||||
</div> |
|
||||
{error && ( |
|
||||
<Typography color="error" variant="body2"> |
|
||||
{error} |
|
||||
</Typography> |
|
||||
)} |
|
||||
{tableData.length > 0 && ( |
|
||||
<ThemeProvider theme={theme}> |
|
||||
<div |
|
||||
style={{ |
|
||||
display: 'flex', |
|
||||
alignItems: 'center', |
|
||||
justifyContent: 'center', |
|
||||
flexDirection: 'column', |
|
||||
width: '100%' |
|
||||
}} |
|
||||
> |
|
||||
<DataGrid |
|
||||
rows={dataRow} |
|
||||
columns={dynamicColumns} |
|
||||
initialState={{ pagination: { paginationModel } }} |
|
||||
onColumnVisibilityModelChange={handleColumnVisibilityChange} // Listen for column visibility changes |
|
||||
pageSizeOptions={[5, 10]} |
|
||||
disableRowSelectionOnClick |
|
||||
sx={{ |
|
||||
width: '100%', |
|
||||
height: '100%', |
|
||||
minHeight: 400, |
|
||||
maxWidth: '100%', // Prevents grid from exceeding the container width |
|
||||
'@media (max-width: 600px)': { |
|
||||
width: '100%', |
|
||||
height: 'auto', // Adjust height for smaller screens |
|
||||
fontSize: '0.8rem' // Smaller font size for better readability on small screens |
|
||||
}, |
|
||||
'@media (max-width: 960px)': { |
|
||||
fontSize: '1rem' // Adjust font size for medium screens |
|
||||
} |
|
||||
}} |
|
||||
slots={{ toolbar: CustomBar }} |
|
||||
slotProps={{ toolbar: { onImport: handleImport } }} |
|
||||
localeText={frFR.components.MuiDataGrid.defaultProps.localeText} |
|
||||
/> |
|
||||
</div> |
|
||||
</ThemeProvider> |
|
||||
)} |
|
||||
</Box> |
|
||||
</Box> |
|
||||
</div> |
|
||||
) |
|
||||
} |
|
||||
|
|
||||
export default ImportNiveau |
|
||||
@ -1,133 +0,0 @@ |
|||||
import React, { useEffect, useState } from 'react' |
|
||||
import { |
|
||||
Dialog, |
|
||||
DialogActions, |
|
||||
DialogContent, |
|
||||
DialogTitle, |
|
||||
TextField, |
|
||||
Button, |
|
||||
InputAdornment, |
|
||||
Box, |
|
||||
Grid, |
|
||||
FormControl, |
|
||||
InputLabel, |
|
||||
Select, |
|
||||
OutlinedInput, |
|
||||
MenuItem |
|
||||
} from '@mui/material' |
|
||||
|
|
||||
|
|
||||
const IpConfig = ({ open, onClose }) => { |
|
||||
|
|
||||
const [localIp, setLocalIp] = useState(""); |
|
||||
const [formData, setFormData] = useState({ |
|
||||
ipname: "", |
|
||||
id: '', |
|
||||
}) |
|
||||
const [ip, setIp] = useState({}); |
|
||||
|
|
||||
useEffect(() => { |
|
||||
|
|
||||
window.notesysteme.getIPConfig().then((response) => { |
|
||||
setIp(response) |
|
||||
}) |
|
||||
|
|
||||
const getLocalIP = async () => { |
|
||||
const pc = new RTCPeerConnection(); |
|
||||
pc.createDataChannel(""); // Create a data channel |
|
||||
pc.createOffer().then((offer) => pc.setLocalDescription(offer)); |
|
||||
|
|
||||
pc.onicecandidate = (event) => { |
|
||||
if (event && event.candidate) { |
|
||||
const ipRegex = /([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})/; |
|
||||
const match = ipRegex.exec(event.candidate.candidate); |
|
||||
if (match) setLocalIp(match[1]); |
|
||||
pc.close(); |
|
||||
} |
|
||||
}; |
|
||||
}; |
|
||||
|
|
||||
getLocalIP(); |
|
||||
}, []); |
|
||||
|
|
||||
useEffect(() => { |
|
||||
if (ip) { |
|
||||
setFormData((prevData) => ({ |
|
||||
...prevData, |
|
||||
ipname: ip.ipname, |
|
||||
id: ip.id |
|
||||
})) |
|
||||
} |
|
||||
|
|
||||
}, [ip]); |
|
||||
|
|
||||
const handleChange = (e) => { |
|
||||
const { name, value } = e.target |
|
||||
setFormData({ ...formData, [name]: value }) |
|
||||
} |
|
||||
|
|
||||
const formSubmit = async (e) => { |
|
||||
e.preventDefault() |
|
||||
|
|
||||
if (ip == undefined) { |
|
||||
let response = await window.notesysteme.createIPConfig(formData) |
|
||||
console.log('running the creation: ', response); |
|
||||
if (response.changes) { |
|
||||
window.notesysteme.getIPConfig().then((response) => { |
|
||||
setIp(response) |
|
||||
}) |
|
||||
onClose() |
|
||||
} |
|
||||
} else { |
|
||||
let response = await window.notesysteme.updateIPConfig(formData) |
|
||||
console.log('running the update: ', response); |
|
||||
if (response.changes) { |
|
||||
window.notesysteme.getIPConfig().then((response) => { |
|
||||
setIp(response) |
|
||||
}) |
|
||||
onClose() |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
} |
|
||||
|
|
||||
return ( |
|
||||
<Dialog open={open} onClose={onClose}> |
|
||||
<form action="" onSubmit={formSubmit}> |
|
||||
<DialogTitle>Configuration de l'adresse IP</DialogTitle> |
|
||||
<div style={{textAlign:"end"}}>Votre Local IP: {localIp || "Detecting..."}</div> |
|
||||
<DialogContent> |
|
||||
<Box sx={{ flexGrow: 1 }}> |
|
||||
<Grid container spacing={2}> |
|
||||
<Grid item xs={12} sm={12}> |
|
||||
<TextField |
|
||||
autoFocus |
|
||||
margin="dense" |
|
||||
name="ipname" |
|
||||
required |
|
||||
label="IP du PC serveur" |
|
||||
type="text" |
|
||||
fullWidth |
|
||||
variant="outlined" |
|
||||
value={formData.ipname} |
|
||||
color="warning" |
|
||||
onChange={handleChange} |
|
||||
/> |
|
||||
</Grid> |
|
||||
</Grid> |
|
||||
</Box> |
|
||||
</DialogContent> |
|
||||
<DialogActions> |
|
||||
<Button onClick={onClose} color="warning"> |
|
||||
Annuler |
|
||||
</Button> |
|
||||
<Button type="submit" color="warning"> |
|
||||
Soumettre |
|
||||
</Button> |
|
||||
</DialogActions> |
|
||||
</form> |
|
||||
</Dialog> |
|
||||
) |
|
||||
} |
|
||||
|
|
||||
export default IpConfig |
|
||||
@ -1,157 +0,0 @@ |
|||||
import { useRef, useState } from 'react' |
|
||||
// import { Container, Row, Col, Form, Button, Card, InputGroup } from 'react-bootstrap'; |
|
||||
import { Container, Grid, Card, Typography, TextField, Button, InputAdornment } from '@mui/material' |
|
||||
import { FaUserCircle, FaLock, FaUser } from 'react-icons/fa' |
|
||||
import classe from '../assets/Login.module.css' |
|
||||
import { useAuthContext } from '../contexts/AuthContext' |
|
||||
import { Link } from 'react-router-dom' |
|
||||
import { ValidationLogin, invalidCredential } from './validation/Login' |
|
||||
|
|
||||
const Login = () => { |
|
||||
/** |
|
||||
* token from the AuthContext in the context folder |
|
||||
*/ |
|
||||
const { setToken } = useAuthContext() |
|
||||
|
|
||||
/** |
|
||||
* hook to store our data from the input element |
|
||||
*/ |
|
||||
const [username, setUsername] = useState() |
|
||||
const [password, setPassword] = useState() |
|
||||
|
|
||||
/** |
|
||||
* ref for our username and password input and the error span |
|
||||
*/ |
|
||||
const userNameRef = useRef() |
|
||||
const passwordRef = useRef() |
|
||||
const userNameError = useRef() |
|
||||
const passwordError = useRef() |
|
||||
|
|
||||
/** |
|
||||
* function to send the data to the IPCRender |
|
||||
* and make login |
|
||||
* |
|
||||
* @param {any} e |
|
||||
*/ |
|
||||
const formLogin = async (e) => { |
|
||||
e.preventDefault() |
|
||||
|
|
||||
let validate = ValidationLogin( |
|
||||
userNameRef.current, |
|
||||
passwordRef.current, |
|
||||
userNameError.current, |
|
||||
passwordError.current |
|
||||
) |
|
||||
|
|
||||
if (validate) { |
|
||||
const response = await window.allUser.login({ username, password }) |
|
||||
|
|
||||
if (response.success) { |
|
||||
// Redirect to main window |
|
||||
setToken(JSON.stringify(response.user)) |
|
||||
} else { |
|
||||
invalidCredential(userNameError.current, response.error) |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
return ( |
|
||||
<Container |
|
||||
maxWidth={false} |
|
||||
sx={{ minHeight: '100vh', display: 'flex', alignItems: 'center', justifyContent: 'center' }} |
|
||||
className={classe.container} |
|
||||
> |
|
||||
<Grid container justifyContent="center"> |
|
||||
<Grid item xs={12} md={6} lg={4}> |
|
||||
<Card className={`p-4 shadow ` + classe.cards} sx={{ padding: 4, boxShadow: 3 }}> |
|
||||
<div style={{ textAlign: 'center', marginBottom: '1rem' }}> |
|
||||
<FaUserCircle size={60} color="dark" className="text-light" /> |
|
||||
</div> |
|
||||
<Typography variant="h5" align="center" className="text-light" gutterBottom> |
|
||||
Université de Toamasina |
|
||||
</Typography> |
|
||||
<form onSubmit={formLogin} className={classe.formulaireLogin}> |
|
||||
<TextField |
|
||||
label="Nom d'utilisateur" |
|
||||
color="secondary" |
|
||||
variant="outlined" |
|
||||
fullWidth |
|
||||
placeholder="Nom d'utilisateur" |
|
||||
margin="normal" |
|
||||
InputProps={{ |
|
||||
startAdornment: ( |
|
||||
<InputAdornment position="start"> |
|
||||
<FaUser style={{ color: 'white' }} /> |
|
||||
</InputAdornment> |
|
||||
) |
|
||||
}} |
|
||||
className={classe.input} |
|
||||
onChange={(e) => setUsername(e.target.value)} |
|
||||
inputRef={userNameRef} |
|
||||
sx={{ |
|
||||
'& .MuiOutlinedInput-root': { |
|
||||
'& fieldset': { |
|
||||
borderColor: 'white' // Set the border color when not focused |
|
||||
}, |
|
||||
'&:hover fieldset': { |
|
||||
borderColor: 'rgb(156, 39, 176)' // Set the border color on hover |
|
||||
} |
|
||||
} |
|
||||
}} |
|
||||
/> |
|
||||
<span className="text-danger" ref={userNameError}></span> |
|
||||
<TextField |
|
||||
label="Mot de passe" |
|
||||
color="secondary" |
|
||||
variant="outlined" |
|
||||
type="password" |
|
||||
fullWidth |
|
||||
placeholder="Mot de passe" |
|
||||
margin="normal" |
|
||||
className={classe.input} |
|
||||
InputProps={{ |
|
||||
startAdornment: ( |
|
||||
<InputAdornment position="start"> |
|
||||
<FaLock style={{ color: 'white' }} /> |
|
||||
</InputAdornment> |
|
||||
) |
|
||||
}} |
|
||||
onChange={(e) => setPassword(e.target.value)} |
|
||||
inputRef={passwordRef} |
|
||||
sx={{ |
|
||||
'& .MuiOutlinedInput-root': { |
|
||||
'& fieldset': { |
|
||||
borderColor: 'white' // Set the border color when not focused |
|
||||
}, |
|
||||
'&:hover fieldset': { |
|
||||
borderColor: 'rgb(156, 39, 176)' // Set the border color on hover |
|
||||
} |
|
||||
} |
|
||||
}} |
|
||||
/> |
|
||||
<span ref={passwordError} className="text-danger"></span> |
|
||||
|
|
||||
<Button |
|
||||
variant="contained" |
|
||||
color="secondary" |
|
||||
type="submit" |
|
||||
fullWidth |
|
||||
sx={{ marginTop: 2 }} |
|
||||
> |
|
||||
Se connecter |
|
||||
</Button> |
|
||||
|
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between', marginTop: '1rem' }}> |
|
||||
<Link to="/forgotpassword" className="text-light"> |
|
||||
Mot de passe oublié ? |
|
||||
</Link> |
|
||||
</div> |
|
||||
</form> |
|
||||
</Card> |
|
||||
</Grid> |
|
||||
</Grid> |
|
||||
</Container> |
|
||||
) |
|
||||
} |
|
||||
|
|
||||
export default Login |
|
||||