Browse Source

push 13-08-2025

andrymodeste 4 months ago
parent
commit
bd0e34d9ad
  1. 8
      database/Models/AnneeScolaire.js
  2. 13
      database/Models/Etudiants.backup.js
  3. 49
      database/Models/Etudiants.js
  4. 32
      database/Models/Matieres.backup.js
  5. 55
      database/Models/Matieres.js
  6. 8
      database/Models/Mentions.js
  7. 7
      database/Models/Niveau.js
  8. 4
      database/Models/NoteSysrem.js
  9. 10
      database/Models/Parcours.js
  10. 487
      database/database.backup.js
  11. 2
      database/database.js
  12. 7
      database/database2.js
  13. 2
      database/function/DownloadReleverNote.js
  14. 4
      database/function/System.js
  15. 452
      database/import/Etudiants.js
  16. BIN
      resources/icon.png
  17. BIN
      resources/logo.ico
  18. 912
      src/main/backup.js
  19. 895
      src/main/index.js
  20. 135
      src/main/test.js
  21. 210
      src/preload/index.backup.js
  22. 206
      src/preload/index.js
  23. 18
      src/renderer/index.html
  24. 63
      src/renderer/src/App.jsx
  25. 207
      src/renderer/src/Routes/Routes.jsx
  26. 12
      src/renderer/src/assets/AddNotes.module.css
  27. 15
      src/renderer/src/assets/AddStudent.module.css
  28. 17
      src/renderer/src/assets/AllStyleComponents.module.css
  29. BIN
      src/renderer/src/assets/Capture.PNG
  30. 86
      src/renderer/src/assets/CarteStudent.module.css
  31. 4
      src/renderer/src/assets/Dashboard.module.css
  32. 20
      src/renderer/src/assets/DefaultLayout.module.css
  33. 78
      src/renderer/src/assets/ExportReleverNote.module.css
  34. 78
      src/renderer/src/assets/Home.module.css
  35. 20
      src/renderer/src/assets/Login.module.css
  36. 25
      src/renderer/src/assets/Navbar.module.css
  37. 51
      src/renderer/src/assets/Sidenav.module.css
  38. BIN
      src/renderer/src/assets/Template carte arriere.pdf
  39. BIN
      src/renderer/src/assets/Template carte frontal.pdf
  40. BIN
      src/renderer/src/assets/admin.png
  41. BIN
      src/renderer/src/assets/arriere.pdf
  42. BIN
      src/renderer/src/assets/autorisationstage.pdf
  43. BIN
      src/renderer/src/assets/background.jpg
  44. BIN
      src/renderer/src/assets/background2.jpg
  45. 97
      src/renderer/src/assets/base.css
  46. BIN
      src/renderer/src/assets/bg1-min.jpg
  47. BIN
      src/renderer/src/assets/bg1.jpg
  48. BIN
      src/renderer/src/assets/bg2-min.jpg
  49. BIN
      src/renderer/src/assets/bg2.jpg
  50. BIN
      src/renderer/src/assets/business_card_template_001.pdf
  51. BIN
      src/renderer/src/assets/business_card_template_001_form.pdf
  52. BIN
      src/renderer/src/assets/business_card_template_002.pdf
  53. BIN
      src/renderer/src/assets/carte_etudiant.odg
  54. BIN
      src/renderer/src/assets/carte_etudiant.pdf
  55. BIN
      src/renderer/src/assets/certificat scolariter.pdf
  56. 10
      src/renderer/src/assets/electron.svg
  57. BIN
      src/renderer/src/assets/enteterelever.png
  58. 52
      src/renderer/src/assets/error.svg
  59. BIN
      src/renderer/src/assets/exemplecarte.png
  60. BIN
      src/renderer/src/assets/logo or.png
  61. BIN
      src/renderer/src/assets/logo.PNG
  62. BIN
      src/renderer/src/assets/logo.ico
  63. BIN
      src/renderer/src/assets/logo.webp
  64. BIN
      src/renderer/src/assets/logo_uni-removebg-preview.webp
  65. BIN
      src/renderer/src/assets/logorelever.png
  66. BIN
      src/renderer/src/assets/logorelever2.png
  67. 163
      src/renderer/src/assets/main.css
  68. BIN
      src/renderer/src/assets/manuel.pdf
  69. BIN
      src/renderer/src/assets/para.png
  70. BIN
      src/renderer/src/assets/preloader.jpg
  71. BIN
      src/renderer/src/assets/recepice.pdf
  72. BIN
      src/renderer/src/assets/releves.pdf
  73. 52
      src/renderer/src/assets/success.svg
  74. BIN
      src/renderer/src/assets/template.png
  75. BIN
      src/renderer/src/assets/template2.png
  76. BIN
      src/renderer/src/assets/teste.pdf
  77. 65
      src/renderer/src/assets/warning.svg
  78. 25
      src/renderer/src/assets/wavy-lines.svg
  79. 283
      src/renderer/src/components/AddAnneeScolaire.jsx
  80. 413
      src/renderer/src/components/AddMatiere.jsx
  81. 257
      src/renderer/src/components/AddMention.jsx
  82. 214
      src/renderer/src/components/AddNiveau.jsx
  83. 312
      src/renderer/src/components/AddNotes.jsx
  84. 162
      src/renderer/src/components/AddParcours.jsx
  85. 1070
      src/renderer/src/components/AddStudent.jsx
  86. 336
      src/renderer/src/components/Addadmin.jsx
  87. 112
      src/renderer/src/components/AjoutTranche.jsx
  88. 319
      src/renderer/src/components/AnneeScolaire.jsx
  89. 68
      src/renderer/src/components/Apropos.jsx
  90. 203
      src/renderer/src/components/AssignMatiereToMention.jsx
  91. 259
      src/renderer/src/components/AssingMatiereToSemestre.jsx
  92. 30
      src/renderer/src/components/CustomBar.jsx
  93. 52
      src/renderer/src/components/DeleteTranche.jsx
  94. 517
      src/renderer/src/components/ExportEtudiants.jsx
  95. 247
      src/renderer/src/components/ForgotPassword.jsx
  96. 194
      src/renderer/src/components/Home.jsx
  97. 489
      src/renderer/src/components/ImportMatiere.jsx
  98. 397
      src/renderer/src/components/ImportNiveau.jsx
  99. 133
      src/renderer/src/components/IpConfig.jsx
  100. 157
      src/renderer/src/components/Login.jsx

8
database/Models/AnneeScolaire.js

@ -71,13 +71,13 @@ async function deleteAnneeScolaire(id) {
if (result.affectedRows === 0) {
return {
success: false,
message: 'Année universitaire non trouvé.'
message: 'Année Scolaire non trouvé.'
}
}
return {
success: true,
message: 'Année universitaire supprimé avec succès.'
message: 'Année Scolaire supprimé avec succès.'
}
} catch (error) {
return { success: false, error: 'Erreur veullez réeseyer' }
@ -93,13 +93,13 @@ async function updateAnneeScolaire(id, code, debut, fin) {
if (result.affectedRows === 0) {
return {
success: false,
message: 'Année universitaire non trouvé ou aucune modification effectuée.'
message: 'Année Scolaire non trouvé ou aucune modification effectuée.'
}
}
return {
success: true,
message: 'Année universitaire mis à jour avec succès.'
message: 'Année Scolaire mis à jour avec succès.'
}
} catch (error) {
return { success: false, error: 'Erreur veullez réeseyer' }

13
database/Models/Etudiants.backup.js

@ -280,6 +280,18 @@ async function deleteTranche(id) {
}
}
async function deleteEtudiant(id) {
const query = database.prepare('DELETE FROM etudiants WHERE id = ?')
try {
let response = query.run(id)
return response
} catch (error) {
return error
}
}
async function getSingleTranche(id) {
try {
return await database.prepare('SELECT * FROM trancheecolage WHERE id = ?').get(id)
@ -301,5 +313,6 @@ module.exports = {
getTranche,
updateTranche,
deleteTranche,
deleteEtudiant,
getSingleTranche
}

49
database/Models/Etudiants.js

@ -176,13 +176,13 @@ async function updateEtudiant(
if (result.affectedRows === 0) {
return {
success: false,
message: 'Année universitaire non trouvé.'
message: 'Année Scolaire non trouvé.'
}
}
return {
success: true,
message: 'Année universitaire supprimé avec succès.'
message: 'Année Scolaire supprimé avec succès.'
}
} catch (error) {
return error
@ -222,13 +222,13 @@ async function changePDP(photos, id) {
if (result.affectedRows === 0) {
return {
success: false,
message: 'Année universitaire non trouvé.'
message: 'Année Scolaire non trouvé.'
}
}
return {
success: true,
message: 'Année universitaire supprimé avec succès.'
message: 'Année Scolaire supprimé avec succès.'
}
} catch (error) {
return error
@ -244,13 +244,13 @@ async function updateParcours(parcours, id) {
if (result.affectedRows === 0) {
return {
success: false,
message: 'Année universitaire non trouvé.'
message: 'Année Scolaire non trouvé.'
}
}
return {
success: true,
message: 'Année universitaire supprimé avec succès.'
message: 'Année Scolaire supprimé avec succès.'
}
} catch (error) {
return error
@ -289,19 +289,21 @@ async function updateTranche(id, tranchename, montant) {
try {
const [result] = await pool.query(sql, [tranchename, montant, id])
console.log('resultat tranche:',result);
if (result.affectedRows === 0) {
return {
success: false,
message: 'Année universitaire non trouvé.'
message: 'Année Scolaire non trouvé.'
}
}
return {
success: true,
message: 'Année universitaire supprimé avec succès.'
message: 'Année Scolaire supprimé avec succès.'
}
} catch (error) {
console.log('resultat error:',error);
return error
}
}
@ -315,19 +317,45 @@ async function deleteTranche(id) {
if (result.affectedRows === 0) {
return {
success: false,
message: 'Année universitaire non trouvé.'
message: 'Année Scolaire non trouvé.'
}
}
return {
success: true,
message: 'Année universitaire supprimé avec succès.'
message: 'Année Scolaire supprimé avec succès.'
}
} catch (error) {
return error
}
}
async function deleteEtudiant(id) {
console.log("id: ", id);
const sql = 'DELETE FROM etudiants WHERE id = ?';
try {
let [result] = await pool.query(sql, [id]);
console.log("Résultat DELETE:", result);
if (result.affectedRows === 0) {
return {
success: false,
message: 'Etudiant non trouvée.'
};
}
return {
success: true,
message: 'Matière supprimée avec succès.'
};
} catch (error) {
console.log("err: ",+ error)
return { success: false, error: 'Erreur, veuillez réessayer: ' + error };
}
}
async function getSingleTranche(id) {
const sql = 'SELECT * FROM trancheecolage WHERE id = ?'
try {
@ -351,5 +379,6 @@ module.exports = {
getTranche,
updateTranche,
deleteTranche,
deleteEtudiant,
getSingleTranche
}

32
database/Models/Matieres.backup.js

@ -29,7 +29,7 @@ async function createMatiere(nom, credit, uniter, ue) {
* @returns Promise
*/
async function getMatiere() {
const query = database.prepare('SELECT * FROM matieres ORDER BY id DESC')
const query = database.prepare('SELECT m.*, n.nom AS niveau_nom FROM matieres m LEFT JOIN niveaus n ON m.niveau_id = n.id ORDER BY m.id DESC')
try {
let response = await query.all()
@ -141,6 +141,35 @@ async function updateMatiere(nom, id, credit, uniter, ue) {
}
}
async function updateMatiereNiveau(formData) {
console.log('formdata:', formData);
const { niveau_id, id } = formData;
const sql = 'UPDATE matieres SET niveau_id = ? WHERE id = ?';
try {
const [result] = await pool.query(sql, [niveau_id, id]);
if (result.affectedRows === 0) {
return {
success: false,
message: 'Matière non trouvée ou aucune modification effectuée.'
};
}
return {
success: true,
message: 'Niveau mis à jour avec succès.'
};
} catch (error) {
console.error(error);
return {
success: false,
error: 'Erreur lors de la mise à jour du niveau : ' + error.message
};
}
}
async function deleteMatiere(id) {
const query = database.prepare('DELETE FROM matieres WHERE id = ?')
@ -340,6 +369,7 @@ module.exports = {
getMatiere,
getSingleMatiere,
updateMatiere,
updateMatiereNiveau,
displayMatiereFromForm,
deleteMatiere,
asygnationToMention,

55
database/Models/Matieres.js

@ -31,7 +31,7 @@ async function createMatiere(nom, credit, uniter, ue) {
* @returns Promise
*/
async function getMatiere() {
const sql = 'SELECT * FROM matieres ORDER BY id DESC'
const sql = 'SELECT m.*, n.nom AS niveau_nom FROM matieres m LEFT JOIN niveaus n ON m.niveau_id = n.id ORDER BY m.id DESC'
try {
let [rows] = await pool.query(sql)
@ -149,41 +149,71 @@ async function updateMatiere(nom, id, credit, uniter, ue) {
if (result.affectedRows === 0) {
return {
success: false,
message: 'Année universitaire non trouvé ou aucune modification effectuée.'
message: 'Année Scolaire non trouvé ou aucune modification effectuée.'
}
}
return {
success: true,
message: 'Année universitaire mis à jour avec succès.'
message: 'Année Scolaire mis à jour avec succès.'
}
} catch (error) {
return { success: false, error: 'Erreur veullez réeseyer' + error }
}
}
async function updateMatiereNiveau(formData) {
const { niveau_id, id } = formData; // ici id sera bien défini
const sql = 'UPDATE matieres SET niveau_id = ? WHERE id = ?';
try {
const [result] = await pool.query(sql, [niveau_id, id]);
if (result.affectedRows === 0) {
return {
success: false,
message: `Matière avec id ${id} non trouvée ou niveau déjà attribué.`
};
}
return {
success: true,
message: `Niveau de la matière ${id} mis à jour avec succès.`
};
} catch (error) {
return {
success: false,
error: 'Erreur lors de la mise à jour : ' + error.message
};
}
}
async function deleteMatiere(id) {
const sql = 'DELETE FROM matieres WHERE id = ?'
console.log("id: ", id);
const sql = 'DELETE FROM matieres WHERE id = ?';
try {
let [result] = await pool.query(sql, [id])
let [result] = await pool.query(sql, [id]);
console.log("Résultat DELETE:", result);
if (result.affectedRows === 0) {
return {
success: false,
message: 'Année universitaire non trouvé.'
}
message: 'Matière non trouvée.'
};
}
return {
success: true,
message: 'Année universitaire supprimé avec succès.'
}
message: 'Matière supprimée avec succès.'
};
} catch (error) {
return { success: false, error: 'Erreur veullez réeseyer' + error }
console.log("err: ",+ error)
return { success: false, error: 'Erreur, veuillez réessayer: ' + error };
}
}
/**
* Assign mentions to a matiere
* @param {Object} formData - keys = mention_ids, values = true/false
@ -444,13 +474,13 @@ async function updateProf(matiere_id, nom, prenom, contact, date) {
if (result.affectedRows === 0) {
return {
success: false,
message: 'Année universitaire non trouvé.'
message: 'Année Scolaire non trouvé.'
}
}
return {
success: true,
message: 'Année universitaire supprimé avec succès.'
message: 'Année Scolaire supprimé avec succès.'
}
} catch (error) {
console.error(error)
@ -464,6 +494,7 @@ module.exports = {
getMatiere,
getSingleMatiere,
updateMatiere,
updateMatiereNiveau,
displayMatiereFromForm,
deleteMatiere,
asygnationToMention,

8
database/Models/Mentions.js

@ -24,13 +24,13 @@ async function deleteMention(id) {
if (result.affectedRows === 0) {
return {
success: false,
message: 'Année universitaire non trouvé.'
message: 'Année Scolaire non trouvé.'
}
}
return {
success: true,
message: 'Année universitaire supprimé avec succès.'
message: 'Année Scolaire supprimé avec succès.'
}
} catch (error) {
return { success: false, error: 'Erreur veullez réeseyer' + error }
@ -70,13 +70,13 @@ async function updateMention(nom, uniter, id) {
if (result.affectedRows === 0) {
return {
success: false,
message: 'Année universitaire non trouvé ou aucune modification effectuée.'
message: 'Année Scolaire non trouvé ou aucune modification effectuée.'
}
}
return {
success: true,
message: 'Année universitaire mis à jour avec succès.'
message: 'Année Scolaire mis à jour avec succès.'
}
} catch (error) {
return { success: false, error: 'Erreur veullez réeseyer' + error }

7
database/Models/Niveau.js

@ -89,17 +89,18 @@ async function updateNiveau(nom, id) {
* function to get all niveau
*/
async function getNiveau() {
const sql = 'SELECT * FROM niveaus'
const sql = 'SELECT niveaus.*,niveaus.id AS niveau_id FROM niveaus'
try {
let [rows] = await pool.query(sql)
console.log("maka niveau: ", rows);
return rows
} catch (error) {
return { success: false, error: 'Erreur veullez réeseyer' }
}
}
async function deleteNiveau(id) {
const sql = 'DELETE FROM niveaus WHERE id = ?'
@ -130,5 +131,5 @@ module.exports = {
insertNiveau,
getSingleNiveau,
updateNiveau,
deleteNiveau
deleteNiveau,
}

4
database/Models/NoteSysrem.js

@ -33,13 +33,13 @@ async function updateSysteme(id, admis, redouble, renvoyer) {
if (result.affectedRows === 0) {
return {
success: false,
message: 'Année universitaire non trouvé.'
message: 'Année Scolaire non trouvé.'
}
}
return {
success: true,
message: 'Année universitaire supprimé avec succès.'
message: 'Année Scolaire supprimé avec succès.'
}
} catch (error) {
return error

10
database/Models/Parcours.js

@ -30,7 +30,7 @@ async function getParcourMatiere(id) {
}
async function getParcours() {
const sql = 'SELECT * FROM parcours ORDER BY id DESC'
const sql = 'SELECT parcours.*, mentions.nom AS mention_nom FROM parcours LEFT JOIN mentions ON parcours.mention_id = mentions.id ORDER BY parcours.id DESC'
try {
let [rows] = await pool.query(sql)
@ -62,13 +62,13 @@ async function deletes(id) {
if (result.affectedRows === 0) {
return {
success: false,
message: 'Année universitaire non trouvé.'
message: 'Année Scolaire non trouvé.'
}
}
return {
success: true,
message: 'Année universitaire supprimé avec succès.'
message: 'Année Scolaire supprimé avec succès.'
}
} catch (error) {
return error
@ -84,13 +84,13 @@ async function updateparcour(id, nom, uniter, mention_id) {
if (result.affectedRows === 0) {
return {
success: false,
message: 'Année universitaire non trouvé ou aucune modification effectuée.'
message: 'Année Scolaire non trouvé ou aucune modification effectuée.'
}
}
return {
success: true,
message: 'Année universitaire mis à jour avec succès.'
message: 'Année Scolaire mis à jour avec succès.'
}
} catch (error) {
return error

487
database/database.backup.js

@ -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 = ?;
) ENGINE=InnoDB;
`)
const insertStatusQuery = database.prepare(`
INSERT INTO status (nom) VALUES (?);
`)
// Tableau des statuts à vérifier/insérer
const arrayStatus = ['Nouveau', 'Passant', 'Redoublant', 'Renvoyé', 'Ancien']
for (let index = 0; index < arrayStatus.length; index++) {
const statusName = arrayStatus[index]
// Vérification si le statut existe déjà
const result = checkStatusQuery.get(statusName)
// Si le statut n'existe pas, on l'insère
if (result.count === 0) {
insertStatusQuery.run(statusName)
}
} finally {
connection.release()
}
}
// 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
async function insertDefaultAdmin() {
const conn = await pool.getConnection()
try {
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']
)
}
// 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 {
conn.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 insertStatusesIfNotExist() {
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 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])
}
})()
}
} 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()
}
}
// 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
}
}
// 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
}

2
database/database.js

@ -1,7 +1,7 @@
const mysql = require('mysql2/promise')
const bcrypt = require('bcryptjs')
const pool = mysql.createPool({
const pool = mysql.createPool({
host: '127.0.0.1',
user: 'root',
password: '',

7
database/database2.js

@ -6,11 +6,12 @@ const pool = mysql.createPool({
user: 'root',
password: '',
database: 'university',
waitForConnections: true,
connectionLimit: 10,
queueLimit: 0
waitForConnections: true,
connectionLimit: 10,
queueLimit: 0
})
async function createTables() {
const connection = await pool.getConnection()

2
database/function/DownloadReleverNote.js

@ -14,7 +14,7 @@ async function modifyPDF(filepath) {
const firstPage = pages[0]
// Replace text based on your data object
const data = { f1: 'Nom', f2: 'Prénom', f3: 23 }
const data = { f1: 'Nom', f2: 'Prenom', f3: 23 }
// Example of replacing placeholders
firstPage.drawText(data.f1, { x: 120, y: 700, size: 12 })

4
database/function/System.js

@ -287,13 +287,13 @@ async function updateNessesaryTable(id, multiplicateur) {
if (result.affectedRows === 0) {
return {
success: false,
message: 'Année universitaire non trouvé.'
message: 'Année Scolaire non trouvé.'
}
}
return {
success: true,
message: 'Année universitaire supprimé avec succès.'
message: 'Année Scolaire supprimé avec succès.'
}
} catch (error) {
return error

452
database/import/Etudiants.js

@ -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;
}
}
console.error('❌ Impossible de convertir:', input);
return null;
}
// Fonction utilitaire pour vérifier les années bissextiles
function isLeapYear(year) {
return (year % 4 === 0 && year % 100 !== 0) || (year % 400 === 0);
}
let jsDate = excelDateToJSDate(input);
// ✅ 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()
];
// If the input is not a valid date, return 'Invalid Date'
return jsDate
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
})
} else {
console.error('Unsupported file format. Only .xlsx and .csv are allowed.')
return
const fileContent = fs.readFileSync(filePath, 'utf8');
records = parse(fileContent, { columns: true, skip_empty_lines: true });
}else {
console.error('Unsupported file format.');
return { error: true, message: 'Format de fichier non supporté.' };
}
// ✅ 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;
// Gestion code_redoublement -> status id
if (row.code_redoublement) {
row.code_redoublement = row.code_redoublement.trim().substring(0, 1);
} else {
row.code_redoublement = 'N';
}
const statusMatch = statusRows.find(
s => s.nom.toLowerCase().startsWith(row.code_redoublement.toLowerCase())
);
if (statusMatch) row.code_redoublement = statusMatch.id;
// Vérification doublons (extraction complet)
const nomComplet = (row.nom + ' ' + row.prenom).toLowerCase().trim();
const 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
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;
}
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)
etudiantsToInsert.push(row);
}
console.log(etudiantsToInsert);
// Insertion des nouveaux
let insertResult = { success: true, affectedRows: 0 };
if (etudiantsToInsert.length > 0) {
insertResult = await insertMultipleEtudiants(etudiantsToInsert);
if (!insertResult.success) {
return { error: true, message: `Erreur lors de l'insertion : ${insertResult.error}` };
}
console.log(insertMultipleEtudiants(newReccord))
}
return { error, message }
} catch (error) {
console.error('Error inserting record:', error)
return { error: 'error' }
}
let msg = `Importation réussie. ${etudiantsToInsert.length} nouvel(le)(s) étudiant(s) inséré(s). ${doublons.length} étudiant(s) mis à jour.`;
return { error: false, message: msg };
}
module.exports = {
importFileToDatabase
}
module.exports = { importFileToDatabase };

BIN
resources/icon.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

BIN
resources/logo.ico

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

912
src/main/backup.js

@ -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
})

895
src/main/index.js

@ -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
})

135
src/main/test.js

@ -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
}
})

210
src/preload/index.backup.js

@ -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
}

206
src/preload/index.js

@ -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
}

18
src/renderer/index.html

@ -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>

63
src/renderer/src/App.jsx

@ -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

207
src/renderer/src/Routes/Routes.jsx

@ -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

12
src/renderer/src/assets/AddNotes.module.css

@ -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%;
}

15
src/renderer/src/assets/AddStudent.module.css

@ -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;
}

17
src/renderer/src/assets/AllStyleComponents.module.css

@ -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%;
}

BIN
src/renderer/src/assets/Capture.PNG

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.9 KiB

86
src/renderer/src/assets/CarteStudent.module.css

@ -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;
}

4
src/renderer/src/assets/Dashboard.module.css

@ -1,4 +0,0 @@
.display {
background-color: rgba(255, 255, 255, 0.9);
/* margin-bottom: 2%; */
}

20
src/renderer/src/assets/DefaultLayout.module.css

@ -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%;
}

78
src/renderer/src/assets/ExportReleverNote.module.css

@ -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;
}

78
src/renderer/src/assets/Home.module.css

@ -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);
}

20
src/renderer/src/assets/Login.module.css

@ -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;
}

25
src/renderer/src/assets/Navbar.module.css

@ -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;
}

51
src/renderer/src/assets/Sidenav.module.css

@ -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;
}

BIN
src/renderer/src/assets/Template carte arriere.pdf

Binary file not shown.

BIN
src/renderer/src/assets/Template carte frontal.pdf

Binary file not shown.

BIN
src/renderer/src/assets/admin.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.4 KiB

BIN
src/renderer/src/assets/arriere.pdf

Binary file not shown.

BIN
src/renderer/src/assets/autorisationstage.pdf

Binary file not shown.

BIN
src/renderer/src/assets/background.jpg

Binary file not shown.

Before

Width:  |  Height:  |  Size: 290 KiB

BIN
src/renderer/src/assets/background2.jpg

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 MiB

97
src/renderer/src/assets/base.css

@ -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;
}

BIN
src/renderer/src/assets/bg1-min.jpg

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 MiB

BIN
src/renderer/src/assets/bg1.jpg

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.9 MiB

BIN
src/renderer/src/assets/bg2-min.jpg

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 MiB

BIN
src/renderer/src/assets/bg2.jpg

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 MiB

BIN
src/renderer/src/assets/business_card_template_001.pdf

Binary file not shown.

BIN
src/renderer/src/assets/business_card_template_001_form.pdf

Binary file not shown.

BIN
src/renderer/src/assets/business_card_template_002.pdf

Binary file not shown.

BIN
src/renderer/src/assets/carte_etudiant.odg

Binary file not shown.

BIN
src/renderer/src/assets/carte_etudiant.pdf

Binary file not shown.

BIN
src/renderer/src/assets/certificat scolariter.pdf

Binary file not shown.

10
src/renderer/src/assets/electron.svg

@ -1,10 +0,0 @@
<svg viewBox="0 0 128 128" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="64" cy="64" r="64" fill="#2F3242"/>
<ellipse cx="63.9835" cy="23.2036" rx="4.48794" ry="4.495" stroke="#A2ECFB" stroke-width="3.6" stroke-linecap="round"/>
<path d="M51.3954 39.5028C52.3733 39.6812 53.3108 39.033 53.4892 38.055C53.6676 37.0771 53.0194 36.1396 52.0414 35.9612L51.3954 39.5028ZM28.6153 43.5751L30.1748 44.4741L30.1748 44.4741L28.6153 43.5751ZM28.9393 60.9358C29.4332 61.7985 30.5329 62.0976 31.3957 61.6037C32.2585 61.1098 32.5575 60.0101 32.0636 59.1473L28.9393 60.9358ZM37.6935 66.7457C37.025 66.01 35.8866 65.9554 35.1508 66.6239C34.415 67.2924 34.3605 68.4308 35.029 69.1666L37.6935 66.7457ZM53.7489 81.7014L52.8478 83.2597L53.7489 81.7014ZM96.9206 89.515C97.7416 88.9544 97.9526 87.8344 97.3919 87.0135C96.8313 86.1925 95.7113 85.9815 94.8904 86.5422L96.9206 89.515ZM52.0414 35.9612C46.4712 34.9451 41.2848 34.8966 36.9738 35.9376C32.6548 36.9806 29.0841 39.1576 27.0559 42.6762L30.1748 44.4741C31.5693 42.0549 34.1448 40.3243 37.8188 39.4371C41.5009 38.5479 46.1547 38.5468 51.3954 39.5028L52.0414 35.9612ZM27.0559 42.6762C24.043 47.9029 25.2781 54.5399 28.9393 60.9358L32.0636 59.1473C28.6579 53.1977 28.1088 48.0581 30.1748 44.4741L27.0559 42.6762ZM35.029 69.1666C39.6385 74.24 45.7158 79.1355 52.8478 83.2597L54.6499 80.1432C47.8081 76.1868 42.0298 71.5185 37.6935 66.7457L35.029 69.1666ZM52.8478 83.2597C61.344 88.1726 70.0465 91.2445 77.7351 92.3608C85.359 93.4677 92.2744 92.6881 96.9206 89.515L94.8904 86.5422C91.3255 88.9767 85.4902 89.849 78.2524 88.7982C71.0793 87.7567 62.809 84.8612 54.6499 80.1432L52.8478 83.2597ZM105.359 84.9077C105.359 81.4337 102.546 78.6127 99.071 78.6127V82.2127C100.553 82.2127 101.759 83.4166 101.759 84.9077H105.359ZM99.071 78.6127C95.5956 78.6127 92.7831 81.4337 92.7831 84.9077H96.3831C96.3831 83.4166 97.5892 82.2127 99.071 82.2127V78.6127ZM92.7831 84.9077C92.7831 88.3817 95.5956 91.2027 99.071 91.2027V87.6027C97.5892 87.6027 96.3831 86.3988 96.3831 84.9077H92.7831ZM99.071 91.2027C102.546 91.2027 105.359 88.3817 105.359 84.9077H101.759C101.759 86.3988 100.553 87.6027 99.071 87.6027V91.2027Z" fill="#A2ECFB"/>
<path d="M91.4873 65.382C90.8456 66.1412 90.9409 67.2769 91.7002 67.9186C92.4594 68.5603 93.5951 68.465 94.2368 67.7058L91.4873 65.382ZM99.3169 43.6354L97.7574 44.5344L99.3169 43.6354ZM84.507 35.2412C83.513 35.2282 82.6967 36.0236 82.6838 37.0176C82.6708 38.0116 83.4661 38.8279 84.4602 38.8409L84.507 35.2412ZM74.9407 39.8801C75.9127 39.6716 76.5315 38.7145 76.323 37.7425C76.1144 36.7706 75.1573 36.1517 74.1854 36.3603L74.9407 39.8801ZM53.7836 46.3728L54.6847 47.931L53.7836 46.3728ZM25.5491 80.9047C25.6932 81.8883 26.6074 82.5688 27.5911 82.4247C28.5747 82.2806 29.2552 81.3664 29.1111 80.3828L25.5491 80.9047ZM94.2368 67.7058C97.8838 63.3907 100.505 58.927 101.752 54.678C103.001 50.4213 102.9 46.2472 100.876 42.7365L97.7574 44.5344C99.1494 46.9491 99.3603 50.0419 98.2974 53.6644C97.2323 57.2945 94.9184 61.3223 91.4873 65.382L94.2368 67.7058ZM100.876 42.7365C97.9119 37.5938 91.7082 35.335 84.507 35.2412L84.4602 38.8409C91.1328 38.9278 95.7262 41.0106 97.7574 44.5344L100.876 42.7365ZM74.1854 36.3603C67.4362 37.8086 60.0878 40.648 52.8826 44.8146L54.6847 47.931C61.5972 43.9338 68.5948 41.2419 74.9407 39.8801L74.1854 36.3603ZM52.8826 44.8146C44.1366 49.872 36.9669 56.0954 32.1491 62.3927C27.3774 68.63 24.7148 75.2115 25.5491 80.9047L29.1111 80.3828C28.4839 76.1026 30.4747 70.5062 35.0084 64.5802C39.496 58.7143 46.2839 52.7889 54.6847 47.931L52.8826 44.8146Z" fill="#A2ECFB"/>
<path d="M49.0825 87.2295C48.7478 86.2934 47.7176 85.8059 46.7816 86.1406C45.8455 86.4753 45.358 87.5055 45.6927 88.4416L49.0825 87.2295ZM78.5635 96.4256C79.075 95.5732 78.7988 94.4675 77.9464 93.9559C77.0941 93.4443 75.9884 93.7205 75.4768 94.5729L78.5635 96.4256ZM79.5703 85.1795C79.2738 86.1284 79.8027 87.1379 80.7516 87.4344C81.7004 87.7308 82.71 87.2019 83.0064 86.2531L79.5703 85.1795ZM84.3832 64.0673H82.5832H84.3832ZM69.156 22.5301C68.2477 22.1261 67.1838 22.535 66.7799 23.4433C66.3759 24.3517 66.7848 25.4155 67.6931 25.8194L69.156 22.5301ZM45.6927 88.4416C47.5994 93.7741 50.1496 98.2905 53.2032 101.505C56.2623 104.724 59.9279 106.731 63.9835 106.731V103.131C61.1984 103.131 58.4165 101.765 55.8131 99.0249C53.2042 96.279 50.8768 92.2477 49.0825 87.2295L45.6927 88.4416ZM63.9835 106.731C69.8694 106.731 74.8921 102.542 78.5635 96.4256L75.4768 94.5729C72.0781 100.235 68.0122 103.131 63.9835 103.131V106.731ZM83.0064 86.2531C85.0269 79.7864 86.1832 72.1831 86.1832 64.0673H82.5832C82.5832 71.8536 81.4723 79.0919 79.5703 85.1795L83.0064 86.2531ZM86.1832 64.0673C86.1832 54.1144 84.4439 44.922 81.4961 37.6502C78.5748 30.4436 74.3436 24.8371 69.156 22.5301L67.6931 25.8194C71.6364 27.5731 75.3846 32.1564 78.1598 39.0026C80.9086 45.7836 82.5832 54.507 82.5832 64.0673H86.1832Z" fill="#A2ECFB"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M103.559 84.9077C103.559 82.4252 101.55 80.4127 99.071 80.4127C96.5924 80.4127 94.5831 82.4252 94.5831 84.9077C94.5831 87.3902 96.5924 89.4027 99.071 89.4027C101.55 89.4027 103.559 87.3902 103.559 84.9077V84.9077Z" stroke="#A2ECFB" stroke-width="3.6" stroke-linecap="round"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M28.8143 89.4027C31.2929 89.4027 33.3023 87.3902 33.3023 84.9077C33.3023 82.4252 31.2929 80.4127 28.8143 80.4127C26.3357 80.4127 24.3264 82.4252 24.3264 84.9077C24.3264 87.3902 26.3357 89.4027 28.8143 89.4027V89.4027V89.4027Z" stroke="#A2ECFB" stroke-width="3.6" stroke-linecap="round"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M64.8501 68.0857C62.6341 68.5652 60.451 67.1547 59.9713 64.9353C59.4934 62.7159 60.9007 60.5293 63.1167 60.0489C65.3326 59.5693 67.5157 60.9798 67.9954 63.1992C68.4742 65.4186 67.066 67.6052 64.8501 68.0857Z" fill="#A2ECFB"/>
</svg>

Before

Width:  |  Height:  |  Size: 5.7 KiB

BIN
src/renderer/src/assets/enteterelever.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

52
src/renderer/src/assets/error.svg

@ -1,52 +0,0 @@
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 52 52"
width="100"
height="100"
>
<circle
cx="26"
cy="26"
r="25"
fill="none"
stroke="#f44336"
stroke-width="2"
/>
<path
fill="none"
stroke="#f44336"
stroke-width="5"
stroke-linecap="round"
stroke-linejoin="round"
stroke-dasharray="40"
stroke-dashoffset="40"
d="M16 16l20 20M16 36l20-20"
id="error-cross"
/>
<style>
@keyframes drawError {
0% {
stroke-dashoffset: 40;
}
50% {
stroke-dashoffset: 0;
}
100% {
stroke-dashoffset: 0;
}
}
#error-cross {
animation: drawError 1s ease forwards infinite;
}
@keyframes pulse {
0%, 100% {
transform: scale(1);
}
50% {
transform: scale(1.1);
}
}
</style>
</svg>

Before

Width:  |  Height:  |  Size: 863 B

BIN
src/renderer/src/assets/exemplecarte.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 156 KiB

BIN
src/renderer/src/assets/logo or.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 69 KiB

BIN
src/renderer/src/assets/logo.PNG

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.2 KiB

BIN
src/renderer/src/assets/logo.ico

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

BIN
src/renderer/src/assets/logo.webp

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

BIN
src/renderer/src/assets/logo_uni-removebg-preview.webp

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

BIN
src/renderer/src/assets/logorelever.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.8 KiB

BIN
src/renderer/src/assets/logorelever2.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.8 KiB

163
src/renderer/src/assets/main.css

@ -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;
}
}

BIN
src/renderer/src/assets/manuel.pdf

Binary file not shown.

BIN
src/renderer/src/assets/para.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.3 KiB

BIN
src/renderer/src/assets/preloader.jpg

Binary file not shown.

Before

Width:  |  Height:  |  Size: 454 KiB

BIN
src/renderer/src/assets/recepice.pdf

Binary file not shown.

BIN
src/renderer/src/assets/releves.pdf

Binary file not shown.

52
src/renderer/src/assets/success.svg

@ -1,52 +0,0 @@
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 52 52"
width="100"
height="100"
>
<circle
cx="26"
cy="26"
r="25"
fill="none"
stroke="#4caf50"
stroke-width="2"
/>
<path
fill="none"
stroke="#4caf50"
stroke-width="5"
stroke-linecap="round"
stroke-linejoin="round"
stroke-dasharray="48"
stroke-dashoffset="48"
d="M14 27l7 7 17-17"
id="success-checkmark"
/>
<style>
@keyframes drawSuccess {
0% {
stroke-dashoffset: 48;
}
50% {
stroke-dashoffset: 0;
}
100% {
stroke-dashoffset: 0;
}
}
#success-checkmark {
animation: drawSuccess 1s ease forwards infinite;
}
@keyframes pulse {
0%, 100% {
transform: scale(1);
}
50% {
transform: scale(1.1);
}
}
</style>
</svg>

Before

Width:  |  Height:  |  Size: 871 B

BIN
src/renderer/src/assets/template.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 96 KiB

BIN
src/renderer/src/assets/template2.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 174 KiB

BIN
src/renderer/src/assets/teste.pdf

Binary file not shown.

65
src/renderer/src/assets/warning.svg

@ -1,65 +0,0 @@
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 52 52"
width="100"
height="100"
>
<circle
cx="26"
cy="26"
r="25"
fill="none"
stroke="#ffa500"
stroke-width="2"
class="pulse-circle"
/>
<!-- Ligne du point d'exclamation -->
<line
x1="26"
y1="15"
x2="26"
y2="32"
stroke="#ffa500"
stroke-width="5"
stroke-linecap="round"
id="exclamation-line"
/>
<!-- Point du point d'exclamation -->
<circle
cx="26"
cy="38"
r="2.5"
fill="#ffa500"
id="exclamation-dot"
/>
<style>
@keyframes drawExclamation {
0% {
stroke-dashoffset: 17;
}
100% {
stroke-dashoffset: 0;
}
}
#exclamation-line {
stroke-dasharray: 17;
stroke-dashoffset: 17;
animation: drawExclamation 0.5s ease forwards;
}
.pulse-circle {
animation: pulse 2s infinite;
transform-origin: center;
}
@keyframes pulse {
0%, 100% {
transform: scale(1);
}
50% {
transform: scale(1.1);
}
}
</style>
</svg>

Before

Width:  |  Height:  |  Size: 1.1 KiB

25
src/renderer/src/assets/wavy-lines.svg

@ -1,25 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1422 800" opacity="0.3">
<defs>
<linearGradient x1="50%" y1="0%" x2="50%" y2="100%" id="oooscillate-grad">
<stop stop-color="hsl(206, 75%, 49%)" stop-opacity="1" offset="0%"></stop>
<stop stop-color="hsl(331, 90%, 56%)" stop-opacity="1" offset="100%"></stop>
</linearGradient>
</defs>
<g stroke-width="1" stroke="url(#oooscillate-grad)" fill="none" stroke-linecap="round">
<path d="M 0 448 Q 355.5 -100 711 400 Q 1066.5 900 1422 448" opacity="0.05"></path>
<path d="M 0 420 Q 355.5 -100 711 400 Q 1066.5 900 1422 420" opacity="0.11"></path>
<path d="M 0 392 Q 352.5 -100 711 400 Q 1066.5 900 1422 392" opacity="0.18"></path>
<path d="M 0 364 Q 355.5 -100 711 400 Q 1066.5 900 1422 364" opacity="0.24"></path>
<path d="M 0 336 Q 355.5 -100 711 400 Q 1066.5 900 1422 336" opacity="0.30"></path>
<path d="M 0 308 Q 355.5 -100 711 400 Q 1066.5 900 1422 308" opacity="0.37"></path>
<path d="M 0 280 Q 355.5 -100 711 400 Q 1066.5 900 1422 280" opacity="0.43"></path>
<path d="M 0 252 Q 355.5 -100 711 400 Q 1066.5 900 1422 252" opacity="0.49"></path>
<path d="M 0 224 Q 355.5 -100 711 400 Q 1066.5 900 1422 224" opacity="0.56"></path>
<path d="M 0 196 Q 355.5 -100 711 400 Q 1066.5 900 1422 196" opacity="0.62"></path>
<path d="M 0 168 Q 355.5 -100 711 400 Q 1066.5 900 1422 168" opacity="0.68"></path>
<path d="M 0 140 Q 355.5 -100 711 400 Q 1066.5 900 1422 140" opacity="0.75"></path>
<path d="M 0 112 Q 355.5 -100 711 400 Q 1066.5 900 1422 112" opacity="0.81"></path>
<path d="M 0 84 Q 355.5 -100 711 400 Q 1066.5 900 1422 84" opacity="0.87"></path>
<path d="M 0 56 Q 355.5 -100 711 400 Q 1066.5 900 1422 56" opacity="0.94"></path>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.7 KiB

283
src/renderer/src/components/AddAnneeScolaire.jsx

@ -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

413
src/renderer/src/components/AddMatiere.jsx

@ -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

257
src/renderer/src/components/AddMention.jsx

@ -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

214
src/renderer/src/components/AddNiveau.jsx

@ -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

312
src/renderer/src/components/AddNotes.jsx

@ -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

162
src/renderer/src/components/AddParcours.jsx

@ -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

1070
src/renderer/src/components/AddStudent.jsx

File diff suppressed because it is too large

336
src/renderer/src/components/Addadmin.jsx

@ -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

112
src/renderer/src/components/AjoutTranche.jsx

@ -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

319
src/renderer/src/components/AnneeScolaire.jsx

@ -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

68
src/renderer/src/components/Apropos.jsx

@ -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

203
src/renderer/src/components/AssignMatiereToMention.jsx

@ -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

259
src/renderer/src/components/AssingMatiereToSemestre.jsx

@ -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

30
src/renderer/src/components/CustomBar.jsx

@ -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

52
src/renderer/src/components/DeleteTranche.jsx

@ -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

517
src/renderer/src/components/ExportEtudiants.jsx

@ -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

247
src/renderer/src/components/ForgotPassword.jsx

@ -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

194
src/renderer/src/components/Home.jsx

@ -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

489
src/renderer/src/components/ImportMatiere.jsx

@ -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

397
src/renderer/src/components/ImportNiveau.jsx

@ -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

133
src/renderer/src/components/IpConfig.jsx

@ -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

157
src/renderer/src/components/Login.jsx

@ -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

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save