@ -1,444 +1,307 @@ |
|||
const sqlite = require('better-sqlite3') |
|||
const mysql = require('mysql2/promise') |
|||
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
|
|||
let dbPath = `./base/data.db`; |
|||
async function createTables() { |
|||
const connection = await pool.getConnection() |
|||
|
|||
// Connect to SQLite database with the initial path
|
|||
let database = new sqlite(dbPath); |
|||
|
|||
|
|||
|
|||
|
|||
|
|||
// Create the users table if it doesn't exist
|
|||
const createUserTableQuery = ` |
|||
try { |
|||
// Users table
|
|||
await connection.query(` |
|||
CREATE TABLE IF NOT EXISTS users ( |
|||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, |
|||
id INT AUTO_INCREMENT PRIMARY KEY, |
|||
username VARCHAR(200) NOT NULL, |
|||
email VARCHAR(250) NOT NULL UNIQUE, |
|||
password TEXT NOT NULL, |
|||
roles VARCHAR(250) NOT NULL, |
|||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, |
|||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP |
|||
); |
|||
` |
|||
database.prepare(createUserTableQuery).run() |
|||
|
|||
// Insert a default admin user if not exists
|
|||
const insertDefaultUserQuery = ` |
|||
INSERT INTO users (username, email, password, roles) |
|||
SELECT 'admin', 'admin@example.com', ?, 'admin' |
|||
WHERE NOT EXISTS (SELECT 1 FROM users WHERE username = 'admin'); |
|||
` |
|||
|
|||
// Hash the password '1234' before storing
|
|||
const hashedPassword = bcrypt.hashSync('123456789', 10) |
|||
database.prepare(insertDefaultUserQuery).run(hashedPassword) |
|||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP |
|||
) ENGINE=InnoDB; |
|||
`)
|
|||
|
|||
// create table for note status
|
|||
const createStatusTableQuery = ` |
|||
// Status table
|
|||
await connection.query(` |
|||
CREATE TABLE IF NOT EXISTS status ( |
|||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, |
|||
id INT AUTO_INCREMENT PRIMARY KEY, |
|||
nom VARCHAR(200) NOT NULL, |
|||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, |
|||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP |
|||
); |
|||
` |
|||
database.prepare(createStatusTableQuery).run() |
|||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP |
|||
) ENGINE=InnoDB; |
|||
`)
|
|||
|
|||
// create table for mention
|
|||
const createMentionTableQuery = ` |
|||
// Mentions table
|
|||
await connection.query(` |
|||
CREATE TABLE IF NOT EXISTS mentions ( |
|||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, |
|||
id INT AUTO_INCREMENT PRIMARY KEY, |
|||
nom VARCHAR(250) NOT NULL, |
|||
uniter VARCHAR(50) NOT NULL, -- Abréviation du nom |
|||
uniter VARCHAR(50) NOT NULL, |
|||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, |
|||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP |
|||
); |
|||
` |
|||
database.prepare(createMentionTableQuery).run() |
|||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP |
|||
) ENGINE=InnoDB; |
|||
`)
|
|||
|
|||
// Create the niveau table if it doesn't exist
|
|||
const createNiveauTableQuery = ` |
|||
await connection.query(` |
|||
CREATE TABLE IF NOT EXISTS niveaus ( |
|||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, |
|||
nom VARCHAR(50) NOT NULL, -- Exemple: L1, L2, L3, etc. |
|||
id INT AUTO_INCREMENT PRIMARY KEY, |
|||
nom VARCHAR(50) UNIQUE NOT NULL, |
|||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, |
|||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP |
|||
); |
|||
` |
|||
database.prepare(createNiveauTableQuery).run() |
|||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP |
|||
) ENGINE=InnoDB; |
|||
`)
|
|||
|
|||
// Create the etudiants table if it doesn't exist
|
|||
const createEtudiantsTableQuery = ` |
|||
await connection.query(` |
|||
CREATE TABLE IF NOT EXISTS etudiants ( |
|||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, |
|||
id INT AUTO_INCREMENT PRIMARY KEY, |
|||
nom VARCHAR(250) DEFAULT NULL, |
|||
prenom VARCHAR(250) DEFAULT NULL, |
|||
photos TEXT DEFAULT NULL, |
|||
date_de_naissances DATE DEFAULT NULL, |
|||
niveau VARCHAR(250) NOT NULL, -- Clé étrangère vers niveaus |
|||
niveau VARCHAR(250) NOT NULL, |
|||
annee_scolaire VARCHAR(20) NOT NULL, |
|||
status INTEGER DEFAULT NULL, |
|||
mention_id INTEGER NOT NULL, -- Clé étrangère vers mentions |
|||
num_inscription TEXT NOT NULL, |
|||
status INT DEFAULT NULL, |
|||
mention_id INT NOT NULL, |
|||
num_inscription TEXT UNIQUE NOT NULL, |
|||
sexe VARCHAR(20) DEFAULT NULL, |
|||
cin VARCHAR(250) DEFAULT NULL, |
|||
date_delivrance DEFAULT NULL, |
|||
nationalite DATE DEFAULT NULL, |
|||
annee_bacc DATE DEFAULT NULL, |
|||
date_delivrance TEXT DEFAULT NULL, |
|||
nationalite VARCHAR(250) DEFAULT NULL, |
|||
annee_bacc TEXT DEFAULT NULL, |
|||
serie VARCHAR(20) DEFAULT NULL, |
|||
boursier BOOLEAN DEFAULT FALSE, |
|||
boursier VARCHAR(20) DEFAULT NULL, |
|||
domaine VARCHAR(250) DEFAULT NULL, |
|||
contact VARCHAR(20) DEFAULT NULL, |
|||
parcours VARCHAR(250) DEFAULT NULL, |
|||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, |
|||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, |
|||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, |
|||
FOREIGN KEY (status) REFERENCES status(id), |
|||
FOREIGN KEY (mention_id) REFERENCES mentions(id) |
|||
); |
|||
` |
|||
database.prepare(createEtudiantsTableQuery).run() |
|||
) ENGINE=InnoDB; |
|||
`)
|
|||
|
|||
// Create the notes table if it doesn't exist
|
|||
const createMatiereTableQuery = ` |
|||
await connection.query(` |
|||
CREATE TABLE IF NOT EXISTS matieres ( |
|||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, |
|||
id INT AUTO_INCREMENT PRIMARY KEY, |
|||
nom VARCHAR(250) UNIQUE NOT NULL, |
|||
unite_enseignement VARCHAR(250) NOT NULL, |
|||
credit INTEGER NOT NULL, |
|||
heure INTEGER NOT NULL, |
|||
credit INT NOT NULL, |
|||
heure INT NOT NULL, |
|||
ue VARCHAR(10) NOT NULL, |
|||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, |
|||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP |
|||
); |
|||
` |
|||
database.prepare(createMatiereTableQuery).run() |
|||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP |
|||
) ENGINE=InnoDB; |
|||
`)
|
|||
|
|||
// Create the semestre table if it doesn't exist
|
|||
const createSemestreTableQuery = ` |
|||
await connection.query(` |
|||
CREATE TABLE IF NOT EXISTS semestres ( |
|||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, |
|||
nom VARCHAR(30) NOT NULL, -- Exemple: S1, S2, S3, etc. |
|||
id INT AUTO_INCREMENT PRIMARY KEY, |
|||
nom VARCHAR(30) NOT NULL, |
|||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, |
|||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP |
|||
); |
|||
` |
|||
database.prepare(createSemestreTableQuery).run() |
|||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP |
|||
) ENGINE=InnoDB; |
|||
`)
|
|||
|
|||
// Create the semestre table if it doesn't exist
|
|||
const createMatiere_mentionTableQuery = ` |
|||
await connection.query(` |
|||
CREATE TABLE IF NOT EXISTS matiere_mention ( |
|||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, |
|||
matiere_id INTEGER NOT NULL, -- Clé étrangère vers matieres |
|||
mention_id INTEGER NOT NULL, -- Clé étrangère vers mentions |
|||
id INT AUTO_INCREMENT PRIMARY KEY, |
|||
matiere_id INT NOT NULL, |
|||
mention_id INT NOT NULL, |
|||
FOREIGN KEY (matiere_id) REFERENCES matieres(id), |
|||
FOREIGN KEY (mention_id) REFERENCES mentions(id) |
|||
); |
|||
` |
|||
database.prepare(createMatiere_mentionTableQuery).run() |
|||
) ENGINE=InnoDB; |
|||
`)
|
|||
|
|||
const createMatiere_semestreTableQuery = ` |
|||
await connection.query(` |
|||
CREATE TABLE IF NOT EXISTS matiere_semestre ( |
|||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, |
|||
matiere_id INTEGER NOT NULL, -- Clé étrangère vers matieres |
|||
semestre_id INTEGER NOT NULL, -- Clé étrangère vers semestres |
|||
mention_id INTEGER NOT NULL, -- Clé étrangère vers niveaus |
|||
id INT AUTO_INCREMENT PRIMARY KEY, |
|||
matiere_id INT NOT NULL, |
|||
semestre_id INT NOT NULL, |
|||
mention_id INT NOT NULL, |
|||
FOREIGN KEY (matiere_id) REFERENCES matieres(id), |
|||
FOREIGN KEY (semestre_id) REFERENCES semestres(id), |
|||
FOREIGN KEY (mention_id) REFERENCES mentions(id) |
|||
); |
|||
` |
|||
database.prepare(createMatiere_semestreTableQuery).run() |
|||
) ENGINE=InnoDB; |
|||
`)
|
|||
|
|||
// Create the notes table if it doesn't exist
|
|||
const createNoteTableQuery = ` |
|||
await connection.query(` |
|||
CREATE TABLE IF NOT EXISTS notes ( |
|||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, |
|||
etudiant_id INTEGER NOT NULL, -- Clé étrangère vers etudiants |
|||
matiere_id INTEGER NOT NULL, -- Clé étrangère vers matieres |
|||
id INT AUTO_INCREMENT PRIMARY KEY, |
|||
etudiant_id INT NOT NULL, |
|||
matiere_id INT NOT NULL, |
|||
etudiant_niveau VARCHAR(50) NOT NULL, |
|||
mention_id INTEGER NOT NULL, |
|||
mention_id INT NOT NULL, |
|||
note FLOAT DEFAULT NULL, |
|||
annee_scolaire VARCHAR(50) NOT NULL, |
|||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, |
|||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, |
|||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, |
|||
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) |
|||
); |
|||
` |
|||
database.prepare(createNoteTableQuery).run() |
|||
) ENGINE=InnoDB; |
|||
`)
|
|||
|
|||
// Create the notes second session table if it doesn't exist
|
|||
const createNoteRepechTableQuery = ` |
|||
await connection.query(` |
|||
CREATE TABLE IF NOT EXISTS notesrepech ( |
|||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, |
|||
etudiant_id INTEGER NOT NULL, -- Clé étrangère vers etudiants |
|||
matiere_id INTEGER NOT NULL, -- Clé étrangère vers matieres |
|||
id INT AUTO_INCREMENT PRIMARY KEY, |
|||
etudiant_id INT NOT NULL, |
|||
matiere_id INT NOT NULL, |
|||
etudiant_niveau VARCHAR(50) NOT NULL, |
|||
mention_id INTEGER NOT NULL, |
|||
mention_id INT NOT NULL, |
|||
note FLOAT DEFAULT NULL, |
|||
annee_scolaire VARCHAR(50) NOT NULL, |
|||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, |
|||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, |
|||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, |
|||
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) |
|||
); |
|||
` |
|||
database.prepare(createNoteRepechTableQuery).run() |
|||
) ENGINE=InnoDB; |
|||
`)
|
|||
|
|||
// create table for note système
|
|||
const createNoteSystemeTableQuery = ` |
|||
await connection.query(` |
|||
CREATE TABLE IF NOT EXISTS notesystems ( |
|||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, |
|||
id INT AUTO_INCREMENT PRIMARY KEY, |
|||
admis FLOAT NOT NULL DEFAULT 10, |
|||
redouble FLOAT NOT NULL DEFAULT 9.99, |
|||
renvoyer FLOAT NOT NULL DEFAULT 7.99, |
|||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, |
|||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP |
|||
); |
|||
` |
|||
database.prepare(createNoteSystemeTableQuery).run() |
|||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP |
|||
) ENGINE=InnoDB; |
|||
`)
|
|||
|
|||
// create table année scolaire
|
|||
const createAnneeScolaireTableQuery = ` |
|||
await connection.query(` |
|||
CREATE TABLE IF NOT EXISTS anneescolaire ( |
|||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, |
|||
id INT AUTO_INCREMENT PRIMARY KEY, |
|||
code VARCHAR(30) NOT NULL, |
|||
debut DATE NOT NULL, |
|||
fin DATE NOT NULL, |
|||
is_current INTEGER DEFAULT 0, |
|||
is_current TINYINT(1) DEFAULT 0, |
|||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, |
|||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP |
|||
); |
|||
` |
|||
database.prepare(createAnneeScolaireTableQuery).run() |
|||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP |
|||
) ENGINE=InnoDB; |
|||
`)
|
|||
|
|||
// create traitement systeme
|
|||
const createTraitementSystemQuery = ` |
|||
await connection.query(` |
|||
CREATE TABLE IF NOT EXISTS traitmentsystem ( |
|||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, |
|||
id INT AUTO_INCREMENT PRIMARY KEY, |
|||
code VARCHAR(30) NOT NULL, |
|||
debut DATE NOT NULL, |
|||
fin DATE NOT NULL, |
|||
is_finished INTEGER DEFAULT 0, |
|||
is_finished TINYINT(1) DEFAULT 0, |
|||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, |
|||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP |
|||
); |
|||
` |
|||
database.prepare(createTraitementSystemQuery).run() |
|||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP |
|||
) ENGINE=InnoDB; |
|||
`)
|
|||
|
|||
const createNecessaryParameterTableQuery = ` |
|||
await connection.query(` |
|||
CREATE TABLE IF NOT EXISTS nessesaryTable ( |
|||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, |
|||
uniter_heure INTEGER NOT NULL, |
|||
id INT AUTO_INCREMENT PRIMARY KEY, |
|||
uniter_heure INT NOT NULL, |
|||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, |
|||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP |
|||
); |
|||
` |
|||
database.prepare(createNecessaryParameterTableQuery).run() |
|||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP |
|||
) ENGINE=InnoDB; |
|||
`)
|
|||
|
|||
const createMatiereEnseignantTableQuery = ` |
|||
await connection.query(` |
|||
CREATE TABLE IF NOT EXISTS matiereEnseignants ( |
|||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, |
|||
matiere_id INTEGER NOT NULL, |
|||
id INT AUTO_INCREMENT PRIMARY KEY, |
|||
matiere_id INT NOT NULL, |
|||
nom_enseignant VARCHAR(250) NOT NULL, |
|||
prenom_enseignant VARCHAR(250) NOT NULL, |
|||
contact VARCHAR(11) NOT NULL, |
|||
date DATE NOT NULL, |
|||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, |
|||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, |
|||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, |
|||
FOREIGN KEY (matiere_id) REFERENCES matieres(id) |
|||
); |
|||
` |
|||
database.prepare(createMatiereEnseignantTableQuery).run() |
|||
) ENGINE=InnoDB; |
|||
`)
|
|||
|
|||
const createParcourTableQuery = ` |
|||
await connection.query(` |
|||
CREATE TABLE IF NOT EXISTS parcours ( |
|||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, |
|||
id INT AUTO_INCREMENT PRIMARY KEY, |
|||
nom VARCHAR(250) NOT NULL, |
|||
uniter VARCHAR(250) NOT NULL, |
|||
mention_id INTEGER DEFAULT NULL, -- Clé étrangère vers mentions |
|||
mention_id INT DEFAULT NULL, |
|||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, |
|||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP |
|||
); |
|||
` |
|||
database.prepare(createParcourTableQuery).run() |
|||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP |
|||
) ENGINE=InnoDB; |
|||
`)
|
|||
|
|||
const createParcourSemestreTableQuery = ` |
|||
await connection.query(` |
|||
CREATE TABLE IF NOT EXISTS parcoursmatiere ( |
|||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, |
|||
matiere_id INTEGER NOT NULL, |
|||
parcour_id INTEGER NOT NULL, |
|||
id INT AUTO_INCREMENT PRIMARY KEY, |
|||
matiere_id INT NOT NULL, |
|||
parcour_id INT NOT NULL, |
|||
FOREIGN KEY (matiere_id) REFERENCES matieres(id), |
|||
FOREIGN KEY (parcour_id) REFERENCES parcours(id) |
|||
); |
|||
` |
|||
database.prepare(createParcourSemestreTableQuery).run() |
|||
) ENGINE=InnoDB; |
|||
`)
|
|||
|
|||
const createTableEcolageQuery = ` |
|||
await connection.query(` |
|||
CREATE TABLE IF NOT EXISTS trancheecolage ( |
|||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, |
|||
etudiant_id INTEGER NOT NULL, |
|||
id INT AUTO_INCREMENT PRIMARY KEY, |
|||
etudiant_id INT NOT NULL, |
|||
tranchename VARCHAR(255) NOT NULL, |
|||
montant DOUBLE NOT NULL |
|||
); |
|||
` |
|||
database.prepare(createTableEcolageQuery).run() |
|||
) ENGINE=InnoDB; |
|||
`)
|
|||
|
|||
const createTableStoreIP = ` |
|||
await connection.query(` |
|||
CREATE TABLE IF NOT EXISTS ipconfig ( |
|||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, |
|||
id INT AUTO_INCREMENT PRIMARY KEY, |
|||
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 (?); |
|||
) ENGINE=InnoDB; |
|||
`)
|
|||
|
|||
// 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) |
|||
} finally { |
|||
connection.release() |
|||
} |
|||
} |
|||
|
|||
insertDefaultNoteSystemIfNotExist() |
|||
|
|||
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; |
|||
`)
|
|||
|
|||
async function insertDefaultAdmin() { |
|||
const conn = await pool.getConnection() |
|||
try { |
|||
let arraySemestre = [ |
|||
'S1', |
|||
'S2', |
|||
'S3', |
|||
'S4', |
|||
'S5', |
|||
'S6', |
|||
'S7', |
|||
'S8', |
|||
'S9', |
|||
'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]) |
|||
} |
|||
})() |
|||
const [rows] = await conn.query(`SELECT COUNT(*) as count FROM users WHERE username = ?`, [ |
|||
'admin' |
|||
]) |
|||
if (rows[0].count === 0) { |
|||
const hashedPassword = bcrypt.hashSync('123456789', 10) |
|||
await conn.query( |
|||
` |
|||
INSERT INTO users (username, email, password, roles) |
|||
VALUES (?, ?, ?, ?)`,
|
|||
['admin', 'admin@example.com', hashedPassword, 'admin'] |
|||
) |
|||
} |
|||
} catch (error) { |
|||
console.log(error) |
|||
} finally { |
|||
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() |
|||
async function insertStatusesIfNotExist() { |
|||
const conn = await pool.getConnection() |
|||
try { |
|||
const statuses = ['Nouveau', 'Passant', 'Redoublant', 'Renvoyé', 'Ancien'] |
|||
for (let name of statuses) { |
|||
const [rows] = await conn.query(`SELECT COUNT(*) as count FROM status WHERE nom = ?`, [name]) |
|||
if (rows[0].count === 0) { |
|||
await conn.query(`INSERT INTO status (nom) VALUES (?)`, [name]) |
|||
} |
|||
} |
|||
|
|||
// Call the function when the app runs
|
|||
createNecessaryParameterTable() |
|||
|
|||
semestreCreate() |
|||
|
|||
// Function to get the IP from the database
|
|||
function getIP() { |
|||
const data = database.prepare("SELECT * FROM ipconfig WHERE id = 1").get(); |
|||
|
|||
if (data) { |
|||
return data.ipname; |
|||
} else { |
|||
return null; // Explicitly return `null` if no data is found
|
|||
} |
|||
} finally { |
|||
conn.release() |
|||
} |
|||
|
|||
// 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 = { |
|||
database |
|||
pool, |
|||
createTables, |
|||
insertDefaultAdmin, |
|||
insertStatusesIfNotExist |
|||
} |
|||
|
|||
@ -1,264 +1,316 @@ |
|||
const fs = require('fs') |
|||
const path = require('path') |
|||
const XLSX = require('xlsx') |
|||
const { getCompressedDefaultImage } = require('../function/GetImageDefaault') |
|||
const { parse } = require('csv-parse/sync') |
|||
const { pool } = require('../database') |
|||
const dayjs = require('dayjs') |
|||
// const { getStatusMention } = require('../function/Helper')
|
|||
const customParseFormat = require('dayjs/plugin/customParseFormat') |
|||
// const { log } = require('console')
|
|||
// const customParseFormatt = require('dayjs/plu')
|
|||
dayjs.extend(customParseFormat) |
|||
|
|||
// Function to convert any date format to 'YYYY-MM-DD'
|
|||
const fs = require('fs'); |
|||
const path = require('path'); |
|||
const XLSX = require('xlsx'); |
|||
const { getCompressedDefaultImage } = require('../function/GetImageDefaault'); |
|||
const { parse } = require('csv-parse/sync'); |
|||
const { pool } = require('../database'); |
|||
const dayjs = require('dayjs'); |
|||
const customParseFormat = require('dayjs/plugin/customParseFormat'); |
|||
dayjs.extend(customParseFormat); |
|||
|
|||
// ✅ Fonction de correction d'encodage
|
|||
function fixEncoding(str) { |
|||
if (typeof str !== 'string') return str; |
|||
return str |
|||
.replace(/├®/g, 'é') |
|||
.replace(/├à/g, 'à') |
|||
.replace(/├©/g, 'é') |
|||
.replace(/├ô/g, 'ô') |
|||
.replace(/├ù/g, 'ù') |
|||
.replace(/’/g, "'") |
|||
.replace(/â€/g, '…') |
|||
.replace(/â€/g, '-'); |
|||
} |
|||
function convertToISODate(input) { |
|||
// Try parsing the date with different formats
|
|||
if (!input) return null; |
|||
|
|||
console.log('🔍 Input original:', input, 'Type:', typeof input); |
|||
|
|||
// Si input est un objet Date valide
|
|||
if (input instanceof Date && !isNaN(input)) { |
|||
const result = dayjs(input).format('YYYY-MM-DD'); |
|||
console.log('📅 Date object convertie:', result); |
|||
return result; |
|||
} |
|||
|
|||
// Si input est un nombre (numéro de série Excel)
|
|||
if (typeof input === 'number') { |
|||
// Formule Excel: (numéro - 25569) * 86400 * 1000
|
|||
const excelDate = new Date((input - 25569) * 86400 * 1000); |
|||
const result = dayjs(excelDate).format('YYYY-MM-DD'); |
|||
console.log('📊 Numéro Excel', input, 'converti en:', result); |
|||
return result; |
|||
} |
|||
|
|||
// Si input est une chaîne
|
|||
if (typeof input === 'string') { |
|||
const cleanInput = input.trim(); |
|||
|
|||
// Cas spécial "vers YYYY"
|
|||
const versMatch = cleanInput.match(/vers\s*(\d{4})/i); |
|||
if (versMatch) { |
|||
const result = `${versMatch[1]}-01-01`; |
|||
console.log('📝 "Vers" détecté:', result); |
|||
return result; |
|||
} |
|||
|
|||
// Formats à tester dans l'ordre de priorité
|
|||
const formats = [ |
|||
'DD/MM/YYYY', |
|||
'MM/DD/YYYY', |
|||
'YYYY-MM-DD', |
|||
'DD-MM-YYYY', |
|||
'MM-DD-YYYY', |
|||
'DD/MM/YY', |
|||
'MM/DD/YY', |
|||
'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
|
|||
'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()) { |
|||
return parsedDate.format('YYYY-MM-DD') |
|||
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
|
|||
} |
|||
} |
|||
|
|||
// Handle cases like "Vers 2000"
|
|||
const versMatch = typeof input === 'string' && input.match(/vers\s*(\d{4})/i); |
|||
if (versMatch) { |
|||
return `${versMatch[1]}-01-01`; // Return in ISO format
|
|||
return result; |
|||
} |
|||
} |
|||
|
|||
function excelDateToJSDate(serial) { |
|||
const utc_days = Math.floor(serial - 25569); // days from Jan 1, 1970
|
|||
const utc_value = utc_days * 86400; // seconds in a day
|
|||
return new Date(utc_value * 1000); // JS Date uses milliseconds
|
|||
// 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'
|
|||
return jsDate |
|||
// Fonction utilitaire pour vérifier les années bissextiles
|
|||
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) { |
|||
const sql = ` |
|||
INSERT INTO etudiants ( |
|||
nom, prenom, photos, date_de_naissances, niveau, annee_scolaire, status, |
|||
mention_id, num_inscription, sexe, cin, date_delivrance, nationalite, |
|||
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.prenom, |
|||
getCompressedDefaultImage(), // photos (you can adjust this if needed)
|
|||
getCompressedDefaultImage(), |
|||
convertToISODate(row.date_naissance), |
|||
row.niveau, |
|||
row.annee_scolaire, |
|||
row.code_redoublement, |
|||
row.mention, |
|||
row.num_inscription, |
|||
row.num_inscription.toString(), |
|||
row.sexe, |
|||
row.cin, |
|||
convertToISODate(row.date_de_delivrance), |
|||
row.nationaliter, |
|||
row.annee_baccalaureat, |
|||
parseInt(row.annee_baccalaureat, 10), |
|||
row.serie, |
|||
row.boursier, |
|||
row.domaine, |
|||
fixEncoding(row.domaine), |
|||
row.contact, |
|||
null // parcours
|
|||
]) |
|||
null |
|||
]); |
|||
|
|||
try { |
|||
const [result] = await pool.query(sql, [values]) |
|||
return { |
|||
success: true, |
|||
affectedRows: result.affectedRows, |
|||
insertId: result.insertId |
|||
} |
|||
const [result] = await pool.query(sql, [values]); |
|||
return { success: true, affectedRows: result.affectedRows }; |
|||
} catch (error) { |
|||
return { success: false, error: error.message } |
|||
console.error('❌ Erreur MySQL :', error.message); |
|||
return { success: false, error: error.message }; |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* Function to import data from XLSX or CSV file into SQLite database |
|||
* @param {string} filePath - Path to the file (either .xlsx or .csv) |
|||
*/ |
|||
// ✅ Import fichier vers base
|
|||
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') { |
|||
// Read and parse XLSX file
|
|||
const workbook = XLSX.readFile(filePath) |
|||
const worksheet = workbook.Sheets[workbook.SheetNames[0]] // Assuming data is in the first sheet
|
|||
records = XLSX.utils.sheet_to_json(worksheet, { defval: '', raw: false }) |
|||
const workbook = XLSX.readFile(filePath); |
|||
const worksheet = workbook.Sheets[workbook.SheetNames[0]]; |
|||
// raw: true pour garder les valeurs brutes, surtout pour les dates
|
|||
records = XLSX.utils.sheet_to_json(worksheet, { defval: ''}); |
|||
} else if (fileExtension === '.csv') { |
|||
// Read and parse CSV file
|
|||
const fileContent = fs.readFileSync(filePath, 'utf8') |
|||
records = parse(fileContent, { |
|||
columns: true, |
|||
skip_empty_lines: true |
|||
}) |
|||
const fileContent = fs.readFileSync(filePath, 'utf8'); |
|||
records = parse(fileContent, { columns: true, skip_empty_lines: true }); |
|||
}else { |
|||
console.error('Unsupported file format. Only .xlsx and .csv are allowed.') |
|||
return |
|||
console.error('Unsupported file format.'); |
|||
return { error: true, message: 'Format de fichier non supporté.' }; |
|||
} |
|||
|
|||
// ✅ Count number of data rows
|
|||
const numberOfLines = records.length |
|||
console.log(`Number of data rows: ${numberOfLines}`) |
|||
console.log(`📄 Nombre de lignes : ${records.length}`); |
|||
|
|||
try { |
|||
let error = true |
|||
let message = '' |
|||
// Vérifier champs obligatoires
|
|||
const requiredFields = [ |
|||
'nom', 'date_naissance', 'niveau', 'annee_scolaire', |
|||
'mention', 'num_inscription', 'nationaliter', 'sexe', |
|||
'annee_baccalaureat', 'serie', 'code_redoublement', |
|||
'boursier', 'domaine' |
|||
]; |
|||
|
|||
// Vérifier les données en une seule boucle
|
|||
let oldNum = '' |
|||
for (const row of records) { |
|||
if ( |
|||
!row.nom || |
|||
// !row.prenom ||
|
|||
!row.date_naissance || |
|||
!row.niveau || |
|||
!row.annee_scolaire || |
|||
!row.mention || |
|||
!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" |
|||
for (const [i, row] of records.entries()) { |
|||
for (const field of requiredFields) { |
|||
if (!row[field]) { |
|||
const msg = `Le champ '${field}' est manquant à la ligne ${i + 2}`; |
|||
console.error(msg); |
|||
return { error: true, message: msg }; |
|||
} |
|||
// 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 [rows] = await pool.query(query) |
|||
const MentionList = rows |
|||
console.log(MentionList) |
|||
const [mentionRows] = await pool.query('SELECT * FROM mentions'); |
|||
const [statusRows] = await pool.query('SELECT * FROM status'); |
|||
|
|||
const etudiantsToInsert = []; |
|||
const doublons = []; |
|||
console.log(records); |
|||
|
|||
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 |
|||
} |
|||
// Mapping mention
|
|||
console.log('Avant conversion date_naissance:', row.date_naissance); |
|||
row.date_naissance = convertToISODate(row.date_naissance); |
|||
console.log('Après conversion date_naissance:', row.date_naissance); |
|||
const matchedMention = mentionRows.find( |
|||
m => m.nom.toUpperCase() === row.mention.toUpperCase() || |
|||
m.uniter.toUpperCase() === row.mention.toUpperCase() |
|||
); |
|||
if (matchedMention) row.mention = matchedMention.id; |
|||
|
|||
const query = 'SELECT * FROM status' |
|||
|
|||
let [rows] = await pool.query(query) |
|||
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 |
|||
} |
|||
// 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; |
|||
|
|||
row.code_redoublement = statutCode |
|||
row.num_inscription = row.num_inscription.toString() |
|||
try { |
|||
let compare = row.num_inscription |
|||
if (compare == oldNum) { |
|||
row.num_inscription = String(row.num_inscription) |
|||
} |
|||
console.log(row.code_redoublement) |
|||
newReccord.push(row) |
|||
oldNum = compare |
|||
} catch (error) { |
|||
console.log(error) |
|||
// 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}` }; |
|||
} |
|||
continue; |
|||
} |
|||
console.log(insertMultipleEtudiants(newReccord)) |
|||
|
|||
etudiantsToInsert.push(row); |
|||
} |
|||
|
|||
return { error, message } |
|||
} catch (error) { |
|||
console.error('Error inserting record:', error) |
|||
return { error: 'error' } |
|||
console.log(etudiantsToInsert); |
|||
|
|||
// Insertion des nouveaux
|
|||
let insertResult = { success: true, affectedRows: 0 }; |
|||
if (etudiantsToInsert.length > 0) { |
|||
insertResult = await insertMultipleEtudiants(etudiantsToInsert); |
|||
if (!insertResult.success) { |
|||
return { error: true, message: `Erreur lors de l'insertion : ${insertResult.error}` }; |
|||
} |
|||
} |
|||
|
|||
module.exports = { |
|||
importFileToDatabase |
|||
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 |
|||
@ -1,98 +0,0 @@ |
|||
import React, { useState } from 'react' |
|||
import { Box, Button, Typography, Grid, Paper } from '@mui/material' |
|||
import { GrManual } from 'react-icons/gr' |
|||
import { Document, Page } from 'react-pdf/dist/esm/entry.webpack5' |
|||
import { FaAngleDoubleLeft, FaAngleDoubleRight } from 'react-icons/fa' |
|||
import pdfUrl from '../assets/manuel.pdf' |
|||
import classe from '../assets/AllStyleComponents.module.css' |
|||
import classeAdd from '../assets/AddStudent.module.css' |
|||
import classeHome from '../assets/Home.module.css' |
|||
|
|||
const Manuel = () => { |
|||
const [numPages, setNumPages] = useState(null) // Use correct name |
|||
const [pageNumber, setPageNumber] = useState(1) |
|||
|
|||
// Fix the onDocumentSuccess function |
|||
function onDocumentSuccess({ numPages }) { |
|||
setNumPages(numPages) // Use the correct property |
|||
} |
|||
|
|||
const prevPage = () => { |
|||
if (pageNumber > 1) { |
|||
setPageNumber(pageNumber - 1) |
|||
} |
|||
} |
|||
|
|||
const nextPage = () => { |
|||
if (pageNumber < numPages) { |
|||
setPageNumber(pageNumber + 1) |
|||
} |
|||
} |
|||
|
|||
return ( |
|||
<div className={classe.mainHome}> |
|||
<div className={classeAdd.header}> |
|||
<div className={classe.h1style}> |
|||
<div className={classeHome.blockTitle}> |
|||
<h1 style={{ display: 'flex', alignItems: 'center', gap: '10px' }}> |
|||
<GrManual /> Manuel d'utilisation |
|||
</h1> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<Paper |
|||
sx={{ |
|||
height: 'auto', |
|||
minHeight: 500, |
|||
display: 'flex', |
|||
alignItems: 'center', |
|||
justifyContent: 'center', |
|||
flexDirection: 'column', |
|||
width: '100%' |
|||
}} |
|||
> |
|||
<Grid sx={{ display: 'flex', gap: '20px', padding: '1%' }}> |
|||
<Button color="warning" variant="contained" onClick={prevPage}> |
|||
<FaAngleDoubleLeft /> |
|||
</Button> |
|||
<Button color="warning" variant="contained" onClick={nextPage}> |
|||
<FaAngleDoubleRight /> |
|||
</Button> |
|||
</Grid> |
|||
|
|||
<Typography variant="h6"> |
|||
Page {pageNumber} sur {numPages} |
|||
</Typography> |
|||
|
|||
<div |
|||
style={{ |
|||
display: 'flex', |
|||
alignItems: 'center', |
|||
justifyContent: 'center', |
|||
width: '100%' |
|||
}} |
|||
> |
|||
<Document |
|||
file={pdfUrl} |
|||
onLoadSuccess={onDocumentSuccess} |
|||
loading={<div>Chargement du Manuel...</div>} |
|||
error={<div>Une erreur s'est produite lors du chargement du Manuel.</div>} |
|||
> |
|||
<Page pageNumber={pageNumber} width={1200} /> |
|||
</Document> |
|||
</div> |
|||
|
|||
<Grid sx={{ display: 'flex', gap: '20px', padding: '1%' }}> |
|||
<Button color="warning" variant="contained" onClick={prevPage}> |
|||
<FaAngleDoubleLeft /> |
|||
</Button> |
|||
<Button color="warning" variant="contained" onClick={nextPage}> |
|||
<FaAngleDoubleRight /> |
|||
</Button> |
|||
</Grid> |
|||
</Paper> |
|||
</div> |
|||
) |
|||
} |
|||
|
|||
export default Manuel |
|||