diff --git a/electron.vite.config.1754936034364.mjs b/electron.vite.config.1754936034364.mjs new file mode 100644 index 0000000..85ca2c9 --- /dev/null +++ b/electron.vite.config.1754936034364.mjs @@ -0,0 +1,27 @@ +// electron.vite.config.mjs +import { resolve } from "path"; +import { defineConfig, externalizeDepsPlugin } from "electron-vite"; +import react from "@vitejs/plugin-react"; +var electron_vite_config_default = defineConfig({ + main: { + plugins: [externalizeDepsPlugin()] + }, + preload: { + plugins: [externalizeDepsPlugin()] + }, + renderer: { + resolve: { + alias: { + "@renderer": resolve("src/renderer/src") + } + }, + plugins: [react()] + }, + worker: { + format: "es" + // Use ES module for worker (you can also use 'iife') + } +}); +export { + electron_vite_config_default as default +}; diff --git a/src/main/backup.js b/src/main/backup.js new file mode 100644 index 0000000..c5b0afd --- /dev/null +++ b/src/main/backup.js @@ -0,0 +1,922 @@ +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, + deleteEtudiant, + 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, + updateMatiereNiveau, + 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 +}) +ipcMain.handle('updateMatiereNiveau', async (event, credentials) => { + // credentials = { niveau_id, id } + const update = await updateMatiereNiveau(credentials) // ✅ on passe id + niveau_id + 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, id) => { + return await deleteMatiere(id); +}); + + +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('deleteEtudiant', async (event, id) => { + return await deleteEtudiant(id); +}); + +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 +}) diff --git a/src/main/index.js b/src/main/index.js new file mode 100644 index 0000000..c2dddda --- /dev/null +++ b/src/main/index.js @@ -0,0 +1,903 @@ +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, + updateMatiereNiveau, + displayMatiereFromForm, + deleteMatiere, + deleteEtudiant, + 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 +}) +ipcMain.handle('updateMatiereNiveau', async (event, credentials) => { + // credentials = { niveau_id, id } + const update = await updateMatiereNiveau(credentials) // ✅ on passe id + niveau_id + 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, id) => { + return await deleteMatiere(id); + }); + ipcMain.handle('deleteEtudiant', async (event, id) => { + return await deleteEtudiant(id); + }); + + +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 +}) diff --git a/src/preload/index.backup.js b/src/preload/index.backup.js new file mode 100644 index 0000000..bccb2e0 --- /dev/null +++ b/src/preload/index.backup.js @@ -0,0 +1,212 @@ +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), + updateMatiereNiveau: (credentials) => ipcRenderer.invoke('updateMatiereNiveau', credentials), + importExcel: (credentials) => ipcRenderer.invoke('importExcelMatiere', credentials), + displayMatiereFromForm: (credentials) => + ipcRenderer.invoke('displayMatiereFromForm', credentials), + deleteMatiere: (credentials) => ipcRenderer.invoke('deleteMatiere', credentials), + deleteEtudiant: (credentials) => ipcRenderer.invoke('deleteEtudiant', 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 +} diff --git a/src/preload/index.js b/src/preload/index.js new file mode 100644 index 0000000..2ead89b --- /dev/null +++ b/src/preload/index.js @@ -0,0 +1,208 @@ +import { contextBridge, ipcRenderer } from 'electron' +import { electronAPI } from '@electron-toolkit/preload' +const { getNessesarytable } = require('../../database/function/System') +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 { getNiveau } = require('../../database/Models/Niveau') +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), + updateMatiereNiveau: (credentials) => ipcRenderer.invoke('updateMatiereNiveau', credentials), + importExcel: (credentials) => ipcRenderer.invoke('importExcelMatiere', credentials), + displayMatiereFromForm: (credentials) => + ipcRenderer.invoke('displayMatiereFromForm', credentials), + deleteMatiere: (id) => ipcRenderer.invoke('deleteMatiere', id), + deleteEtudiant: (id) => ipcRenderer.invoke('deleteEtudiant', id), + 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 +} diff --git a/src/renderer/src/assets/AllStyleComponents.module.css b/src/renderer/src/assets/AllStyleComponents.module.css new file mode 100644 index 0000000..23c967b --- /dev/null +++ b/src/renderer/src/assets/AllStyleComponents.module.css @@ -0,0 +1,17 @@ +.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%; +} diff --git a/src/renderer/src/components/AddAnneeScolaire.jsx b/src/renderer/src/components/AddAnneeScolaire.jsx new file mode 100644 index 0000000..42b1038 --- /dev/null +++ b/src/renderer/src/components/AddAnneeScolaire.jsx @@ -0,0 +1,283 @@ +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 = () => ( + + + {status === 200 ? ( + + {' '} + Année Scolaire insérer avec succes + + ) : ( + + {' '} + Erreur, veuillez réessayer + + )} + + {status === 200 ? ( + + ) : ( + + )} + + + + ) + + return ( +
+ {modals()} +
+
+
+

+ Ajout Année Scolaire +

+ window.history.back()}> + + +
+
+
+ +
+ +
+

+ Creation de nouvelle Année Scolaire +

+ + + + + + ) + }} + 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 + } + }} + /> + + + + + + ) + }} + 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 + } + }} + /> + + + + + + ) + }} + 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 + } + }} + /> + + + + + +
+
+
+
+ ) +} + +export default AddAnneeScolaire diff --git a/src/renderer/src/components/AddNotes.jsx b/src/renderer/src/components/AddNotes.jsx new file mode 100644 index 0000000..9a56200 --- /dev/null +++ b/src/renderer/src/components/AddNotes.jsx @@ -0,0 +1,317 @@ +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) + console.log("resulat teste:1", response); + console.log("resulat teste:2", mention_id); + console.log("resulat teste:3", parcours); + }) + 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) + } + } + + console.log('matiere: ',matieres); + + 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 = () => ( + + + {statut === 200 ? ( + + {' '} + + Note de {etudiants.nom} {etudiants.prenom} en {etudiants.niveau} a été inserer avec + succès + + + ) : ( + + {' '} + Inserer au moin un champ + + )} + + {statut == 200 ? ( + + ) : ( + + )} + + + + ) + + return ( +
+ {modals()} + +
+
+
+

+ + Ajout note +

+ + + +
+
+ + {/* displaying */} +
+ + +
+

+ Ajout des notes :{' '} + + {etudiants.nom} {etudiants.prenom} en {etudiants.niveau} + +

+ + {/* map the all matiere to the form */} + + {matieres.map((mat) => ( + + setFormData({ ...formData, [mat.id]: e.target.value }) // Update the specific key + } + InputProps={{ + startAdornment: ( + + + + ) + }} + 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 + } + }} + /> + + ))} + + + + +
+
+
+
+
+
+ ) +} + +export default AddNotes diff --git a/src/renderer/src/components/AddStudent.jsx b/src/renderer/src/components/AddStudent.jsx new file mode 100644 index 0000000..cb1681c --- /dev/null +++ b/src/renderer/src/components/AddStudent.jsx @@ -0,0 +1,1070 @@ +import React, { useEffect, useRef, useState } from 'react' +import classe from '../assets/AllStyleComponents.module.css' +import classeAdd from '../assets/AddStudent.module.css' +import { IoMdMale, IoMdPersonAdd } from 'react-icons/io' +import { IoMdReturnRight } from 'react-icons/io' +import classeHome from '../assets/Home.module.css' +import { Link, useNavigate } from 'react-router-dom' +import { + Box, + Button, + InputAdornment, + Typography, + Modal, + TextField, + Grid, + Autocomplete, + IconButton +} from '@mui/material' +import { + FaUser, + FaIdBadge, + FaBirthdayCake, + FaGraduationCap, + FaCalendarAlt, + FaFileUpload, + FaFileExcel, + FaClipboardList, + FaPassport, + FaGlobeAfrica, + FaMoneyBillWave, + FaHome, + FaPhone +} from 'react-icons/fa' +import { styled } from '@mui/material/styles' +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 { OutlinedInput } from '@mui/material' +import svgSuccess from '../assets/success.svg' +import svgError from '../assets/error.svg' +import ChangeCapital from './function/ChangeCapitalLetter' +import ChangeCapitalize from './function/ChangeCapitalizeLetter' +import { MdChangeCircle, MdGrade, MdRule } from 'react-icons/md' +import ModalRecepice from './ModalRecepice' +import { FaLeftLong, FaRightLong } from 'react-icons/fa6' +import { Tooltip } from 'react-tooltip' + +const AddStudent = () => { + const [niveaus, setNiveau] = useState([]) + const [status, setStatus] = useState([]) + const [scolaire, setScolaire] = useState([]) + const [mention, setMention] = useState([]) + const [parcours, setParcours] = useState([]) + + useEffect(() => { + window.niveaus.getNiveau().then((response) => { + setNiveau(response) + }) + + window.statuss.getStatus().then((response) => { + setStatus(response) + }) + + window.anneescolaire.getAnneeScolaire().then((response) => { + setScolaire(response) + }) + + window.mention.getMention().then((response) => { + setMention(response) + }) + + window.notesysteme.getParcours().then((response) => { + setParcours(response) + }) + }, []) + + const navigate = useNavigate() + + /** + * hook for storing data in the input + */ + const [formData, setFormData] = useState({ + nom: '', + prenom: '', + photos: null, + date_de_naissances: '', + niveau: '', + annee_scolaire: '', + status: '', + num_inscription: '', + mention_id: '', + sexe: 'Garçon', + nationaliter: '', + cin: '', + date_delivrence: '', + annee_bacc: '', + serie: '', + boursier: 'oui', + domaine: '', + contact: '', + parcours: '' + }) + + const [dataToSend, setDataToSend] = useState({}) + + useEffect(() => { + setDataToSend({ + mention: compareMention(formData.mention_id), + niveau: formData.niveau, + nomPrenom: formData.nom + ' ' + formData.prenom, + inscri: formData.num_inscription, + annee: formData.annee_scolaire + }) + }, [formData]) + + function compareMention(mentionID) { + let statusText + + mention.map((statu) => { + if (mentionID == statu.id) { + statusText = statu.nom + } + }) + + return statusText ? statusText.charAt(0).toUpperCase() + statusText.slice(1) : statusText + } + + /** + * ref for each input + */ + const nomRef = useRef() + const prenomRef = useRef() + const date_de_naissancesRef = useRef() + + /** + * function to set the data in state + * @param {*} e + */ + const handleInputChange = (e) => { + const { name, value } = e.target + setFormData((prevData) => ({ + ...prevData, + [name]: value + })) + } + + const imageVisual = useRef() + + const handleFileChange = (e) => { + let img_file = e.target.files[0] + const reader = new FileReader() + reader.readAsDataURL(img_file) + reader.onload = (ev) => { + const url = ev.target.result + // initialisation de nouvelle imageURL + const image = document.createElement('img') + image.src = url + + // create a new image + image.onload = (event) => { + let canvas = document.createElement('canvas') + let ratio = 250 / event.target.width + canvas.width = 250 + canvas.height = event.target.height * ratio + const context = canvas.getContext('2d') + context.drawImage(image, 0, 0, canvas.width, canvas.height) + + // new url + const new_URL = canvas.toDataURL('image/jpeg', 90) + imageVisual.current.style.display = 'block' + imageVisual.current.src = new_URL + // rendement de l'url a notre variable global + setFormData((prevData) => ({ + ...prevData, + photos: new_URL + })) + } + } + } + + const handleSubmit = async (e) => { + e.preventDefault() + // Handle form submission logic + const response = await window.etudiants.insertEtudiant(formData) + + console.log(response) + if (!response.success) { + setCode(422) + setOpen(true) + } + + if (response.success) { + imageVisual.current.style.display = 'none' + imageVisual.current.src = '' + setCode(200) + setOpen(true) + } + } + + 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 + }) + + /** + * hook to open modal + */ + const [open, setOpen] = useState(false) + const [open2, setOpen2] = useState(false) + const [code, setCode] = useState(200) + + /** + * function to close modal + */ + const handleClose = () => { + setOpen2(false) + navigate('/student') + } + const handleOpen3 = () => { + setOpenModal3(true) + } + + const handleClose2 = () => { + setOpen(false) + } + + /** + * function to return the view Modal + * + * @returns {JSX} + */ + const modals = () => ( + + + {code == 422 ? ( + + {' '} + Vérifier les champs + + ) : ( + + {' '} + Insertion a été effectuée avec succès + + )} + + {code === 200 ? ( + + ) : ( + + )} + + + + ) + + const [openModal3, setOpenModal3] = useState(false) + + const handleClose3 = () => { + navigate('/student') + setOpenModal3(false) + } + + const [page1, setPage1] = useState(true) + const [page2, setPage2] = useState(false) + + const seePage1 = () => { + setPage2(false) + setPage1(true) + } + + const seePage2 = () => { + setPage1(false) + setPage2(true) + } + + return ( +
+ {modals()} + +
+
+
+

+ + Ajout étudiant +

+ + + +
+
+
+
+ +
+ + Importer un fichier excel + +
+ +
+
+ +
+ + {page1 == true && ( + + {/* Nom and Prenom Fields */} + + ChangeCapital(nomRef)} + InputProps={{ + startAdornment: ( + + + + ) + }} + inputRef={nomRef} + sx={{ + '& .MuiOutlinedInput-root': { + '&:hover fieldset': { + borderColor: '#ff9800' // Set the border color on hover + } + } + }} + /> + + + ChangeCapitalize(prenomRef)} + InputProps={{ + startAdornment: ( + + + + ) + }} + inputRef={prenomRef} + sx={{ + '& .MuiOutlinedInput-root': { + '&:hover fieldset': { + borderColor: '#ff9800' // Set the border color on hover + } + } + }} + /> + + + {/* Date de Naissance and Niveau Fields */} + + + + + ) + }} + inputRef={date_de_naissancesRef} + sx={{ + '& .MuiOutlinedInput-root': { + '&:hover fieldset': { + borderColor: '#ff9800' // Set the border color on hover + } + } + }} + /> + + + + + + ) + }} + sx={{ + '& .MuiOutlinedInput-root': { + '&:hover fieldset': { + borderColor: '#ff9800' // Set the border color on hover + } + } + }} + /> + + + + + Année Scolaire + + + + + + + + Niveau + + + + + + + + Status + + + + + + 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 + })) + }} + size="small" // Make the Autocomplete small + isOptionEqualToValue={(option, value) => option.id === value.id} // Ensure correct matching of options + renderInput={(params) => ( + + + + + {params.InputProps.startAdornment} + + ) + }} + sx={{ + marginBottom: '5px', + '& .MuiOutlinedInput-root': { + '&:hover fieldset': { + borderColor: '#ff9800' // Set border color on hover + } + } + }} + /> + )} + /> + + + {/* Photos Field */} + + + + + )} + {page2 && ( + + + + + + ) + }} + sx={{ + '& .MuiOutlinedInput-root': { + '&:hover fieldset': { + borderColor: '#ff9800' // Set the border color on hover + } + } + }} + /> + + + + + + ) + }} + sx={{ + '& .MuiOutlinedInput-root': { + '&:hover fieldset': { + borderColor: '#ff9800' // Set the border color on hover + } + } + }} + /> + + + + + Sexe + + + + + + + + + ) + }} + sx={{ + '& .MuiOutlinedInput-root': { + '&:hover fieldset': { + borderColor: '#ff9800' // Set the border color on hover + } + } + }} + /> + + + + + + ) + }} + sx={{ + '& .MuiOutlinedInput-root': { + '&:hover fieldset': { + borderColor: '#ff9800' // Set the border color on hover + } + } + }} + /> + + + + + + ) + }} + sx={{ + '& .MuiOutlinedInput-root': { + '&:hover fieldset': { + borderColor: '#ff9800' // Set the border color on hover + } + } + }} + /> + + + + + Boursier + + + + + + + + + ) + }} + sx={{ + '& .MuiOutlinedInput-root': { + '&:hover fieldset': { + borderColor: '#ff9800' // Set the border color on hover + } + } + }} + /> + + + + + + ) + }} + sx={{ + '& .MuiOutlinedInput-root': { + '&:hover fieldset': { + borderColor: '#ff9800' // Set the border color on hover + } + } + }} + /> + + + option.nom || ''} // Display `nom` + value={parcours.find((item) => item.nom === formData.parcours) || null} // Find selected option based on `nom` + onChange={(event, newValue) => { + setFormData((prevData) => ({ + ...prevData, + parcours: newValue ? newValue.nom : '' // Store only `nom` + })) + }} + size="small" + isOptionEqualToValue={(option, value) => option.nom === value?.nom} // Ensure correct matching + renderInput={(params) => ( + + + + + {params.InputProps.startAdornment} + + ) + }} + sx={{ + marginBottom: '5px', + '& .MuiOutlinedInput-root': { + '&:hover fieldset': { + borderColor: '#ff9800' // Set border color on hover + } + } + }} + /> + )} + /> + + + )} + {/* Submit Button */} + +
+ + + + + Page précédents + + + + + + Page suivants + +
+ +
+
+
+
+
+
+
+ ) +} + +export default AddStudent diff --git a/src/renderer/src/components/AnneeScolaire.jsx b/src/renderer/src/components/AnneeScolaire.jsx new file mode 100644 index 0000000..458d6d2 --- /dev/null +++ b/src/renderer/src/components/AnneeScolaire.jsx @@ -0,0 +1,319 @@ +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 Scolaire', 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) => ( +
+ + + + { + setIds(params.row.id) + setOpen(true) + }} + > + + +
+ ) + }, + { + field: 'current', + headerName: 'Année en cours', + flex: 1, + renderCell: (params) => ( +
+ + + + Année en cours + + +
+ ) + } + ] + + 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 ( +
+ +
+ ) + } + + /** + * 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 = () => ( + + + {isDeleted ? ( + + Suprimer avec succèss + + ) : ( + + {' '} + Voulez vous supprimer cette année ? + + )} + + {isDeleted ? ( + + ) : ( +
+ + +
+ )} +
+
+
+ ) + + return ( +
+ {modals()} +
+
+
+

+ Année Scolaire +

+ + + +
+
+
+ +
+ + +
+ +
+
+
+
+
+ ) +} + +export default AnneeScolaire diff --git a/src/renderer/src/components/Apropos.jsx b/src/renderer/src/components/Apropos.jsx new file mode 100644 index 0000000..6af58c2 --- /dev/null +++ b/src/renderer/src/components/Apropos.jsx @@ -0,0 +1,68 @@ +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 ( +
+
+
+
+

A propos

+ + {/* contenu */} +
+
+ + + + + + +

+ Nom du Logiciel: CUniversity
+
Description : logiciel de gestion d'espt

+ Createur: CPAY COMPANY FOR MADAGASCAR
+
Licence: A vie
+
Contact: 0348415301 +

+ E-mail: director@c4m.mg +

+
+
+
+
+
+
+
+
+ ) +} + +export default Apropos diff --git a/src/renderer/src/components/ExportEtudiants.jsx b/src/renderer/src/components/ExportEtudiants.jsx new file mode 100644 index 0000000..57b4ec2 --- /dev/null +++ b/src/renderer/src/components/ExportEtudiants.jsx @@ -0,0 +1,527 @@ +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 () => { + if (!files) { + setError("Veuillez choisir un fichier d'abord.") + return + } + try { + let response = await window.etudiants.importExcel(files.path) + + console.log(response) + + if (response.message) { + setMessage(response.message) + } + + if (response.error) { + setIsinserted(false) + setOpen(true) + // Ne vide pas tableData, ça permet à l'utilisateur de corriger le fichier + } else { + setIsinserted(true) + setOpen(true) + setTableData([]) // vider seulement si insertion réussie + } + } catch (err) { + setMessage('Erreur inattendue lors de l’import') + 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 = () => ( + + + {isInserted ? ( + + {' '} + Importation a été effectuée avec succès + + ) : ( + + {' '} + + L'importation n'a pas été effectuée +
+ {message} +
+
+ )} + + + +
+
+ ) + + 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 ( +
+ {modals()} +
+
+
+

+ Export +

+ + + +
+
+
+ + + +
+ + +
+ {error && ( + + {error} + + )} + {tableData.length > 0 && ( + +
+ {/* Dropdowns for each column */} + {dynamicColumns.map((col) => ( + + {col.headerName} + + + ))} +
+
+ +
+
+ )} +
+
+
+ ) +} + +export default ExportEtudiants diff --git a/src/renderer/src/components/Matieres.jsx b/src/renderer/src/components/Matieres.jsx new file mode 100644 index 0000000..e30ab89 --- /dev/null +++ b/src/renderer/src/components/Matieres.jsx @@ -0,0 +1,564 @@ +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 } from 'react-router-dom' +import { BsBookmarkPlusFill } from 'react-icons/bs' +import { + FaUserCircle, + FaBook, + FaUserCog, + FaTrash, + FaPlus, + FaRegPlusSquare, + FaNewspaper +} from 'react-icons/fa' +import { Box, Button, InputAdornment, Typography, Modal, TextField, Grid } from '@mui/material' +import Paper from '@mui/material/Paper' +import { createTheme, ThemeProvider } from '@mui/material/styles' +import { DataGrid, GridToolbar } from '@mui/x-data-grid' +import { frFR } from '@mui/x-data-grid/locales' +import { Fa1, Fa2, Fa3, FaM, FaP, FaPenToSquare, FaS } from 'react-icons/fa6' +import { Tooltip } from 'react-tooltip' +import warning from '../assets/warning.svg' +import success from '../assets/success.svg' +import { GiTeacher } from 'react-icons/gi' +import ModalAddProf from './ModalAddProf' +import ParcourMatiere from './ParcourMatiere' +import UpdateModalProf from './UpdateModalProf' +import ModalProcessFichePresence from './ModalProcessFichePresence' +import ModalExportFichr from './ModalExportFichr' +import NiveauMatiere from './NiveauMatiere' + +const Matieres = () => { + const [matiere, setMatiere] = useState([]) + const [openAssignNiveau, setOpenAssignNiveau] = useState(false) + const [matiereIdAssign, setMatiereIdAssign] = useState(null) + const [Enseignants, setEnseignants] = useState([]) + + useEffect(() => { + window.matieres.getMatiere().then((response) => { + setMatiere(response) + }) + + window.matieres.getENseignant().then((response) => { + setEnseignants(response) + }) + }, []) + + 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 openAssignNiveauModal = (id) => { + setMatiereIdAssign(id) + setOpenAssignNiveau(true) + } + + const closeAssignNiveauModal = () => { + setOpenAssignNiveau(false) + setMatiereIdAssign(null) + } + + const handleNiveauAssignSuccess = (status) => { + if (status) { + // Rafraîchir les données des matières et enseignants + window.matieres.getMatiere().then((response) => { + setMatiere(response) + }) + + window.matieres.getENseignant().then((response) => { + setEnseignants(response) + }) + } + } + + const [isDeleted, setIsDeleted] = useState(false) + const [ids, setIds] = useState(0) + + const columns = [ + { field: 'nom', headerName: 'Nom', width: 230 }, + { field: 'credit', headerName: 'Crédit', width: 80 }, + { field: 'heure', headerName: 'Heure', width: 80 }, + { field: 'uniter', headerName: "Unité d'enseignement", width: 180 }, + { field: 'ue', headerName: 'UE', width: 80 }, + { field: 'niveau_id', headerName: 'Niveau', width: 80 }, + { field: 'enseignant', headerName: 'Professeur actuele', width: 230 }, + { + field: 'action', + headerName: 'Action', + width: 600, + renderCell: (params) => ( +
+ + + + + + + + + Assigner à un semestre + + + + + {/* ----------- BOUTON ASSIGNER NIVEAU ----------- */} + { + e.preventDefault(); // empêche le # de provoquer un scroll + openAssignNiveauModal(params.value); + }} + > + + Assigner à un niveau + + + + + openParcoursFunction(params.value)} + > + + Assigner à des parcours + + + + + + + + + + + + + + Fiche de presence éxamen + + + + { + setIds(params.row.id) + setOpen(true) + }} + > + + +
+ ) + } + ] + + const compareMatieres = (matiere_id) => { + let NomPrenom = '' + for (let index = 0; index < Enseignants.length; index++) { + if (Enseignants[index].matiere_id == matiere_id) { + NomPrenom = `${Enseignants[index].nom_enseignant} ${Enseignants[index].prenom_enseignant}` + } + } + + return NomPrenom + } + + const paginationModel = { page: 0, pageSize: 5 } + + let dataRow = matiere.map((mat) => ({ + id: mat.id, // Ensure this exists and is unique for each etudiant + nom: mat.nom, + credit: mat.credit, + heure: mat.heure, + uniter: mat.unite_enseignement, + ue: mat.ue, + niveau_id: mat.niveau_nom ?? 'pas de niveau', + enseignant: + compareMatieres(mat.id) != '' ? compareMatieres(mat.id) : 'Veuillez assigner un professeur', + action: mat.id // Ensure this is a valid URL for the image + })) + + const deleteButton = async (id) => { + let response = await window.matieres.deleteMatiere(id); + if (response.success) { + const updatedMatieres = matiere.filter((matiere) => matiere.id !== id) + setMatiere(updatedMatieres) + setIsDeleted(true) + } + } + + /** + * 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 = () => ( + + + {isDeleted ? ( + + Suprimer avec succèss + + ) : ( + + {' '} + Voulez vous supprimer ce matiere ? + + )} + + {isDeleted ? ( + + ) : ( +
+ + +
+ )} +
+
+
+ ) + + const [openForm, setOpenForm] = useState(false) + const [matiereId, setMatiereId] = useState('') + const [isSubmitted, setIsSubmitted] = useState(false) + + // Callback function to receive the submission status + const handleFormSubmit = (status) => { + setIsSubmitted(status) + } + + useEffect(() => { + if (isSubmitted) { + window.matieres + .getMatiere() + .then((response) => { + setMatiere(response) + setIsSubmitted(false) // Reset isSubmitted after fetching data + }) + .catch((error) => { + console.error('Error fetching matiere:', error) + setIsSubmitted(false) // Ensure reset even on error + }) + + window.matieres.getENseignant().then((response) => { + setEnseignants(response) + }) + } + }, [isSubmitted]) + + const closeForm = () => { + setOpenForm(false) + } + + const openFormModal = (id) => { + setOpenForm(true) + setMatiereId(id) + } + + const [openParcours, setOpenParcours] = useState(false) + const onCloseParcours = () => setOpenParcours(false) + const [idToSend, setIdToSend] = useState(null) + const openParcoursFunction = (id) => { + setIdToSend(id) + setOpenParcours(true) + } + + const [openUppdateProf, setOpenUpdateProf] = useState(false) + const onCloseUpdateProf = () => setOpenUpdateProf(false) + const [idSends, setIdSend] = useState('') + const openUppdateProfFunction = (id) => { + setIdSend(id) + setOpenUpdateProf(true) + } + + const [openFiche, setOpenFiche] = useState(false) + const onCloseFiche = () => setOpenFiche(false) + const [dataFiche, setDataFiche] = useState(null) + const paperRef = useRef() + + // const extractFiche = async (matiere_id) => { + // let response = await + // setDataFiche(response) + // } + + return ( +
+ {modals()} + + + + + + {/* Modal NiveauMatiere */} + + +
+
+
+

+ + Matiere +

+ + + +
+
+
+ + {/* displaying data */} +
+
+ + +
+ +
+
+
+
+
+
+ ) +} + +export default Matieres \ No newline at end of file diff --git a/src/renderer/src/components/ModalExportFichr.jsx b/src/renderer/src/components/ModalExportFichr.jsx new file mode 100644 index 0000000..9ba8c1a --- /dev/null +++ b/src/renderer/src/components/ModalExportFichr.jsx @@ -0,0 +1,220 @@ +import React, { useEffect, useRef, useState } from 'react' +import { Link, useParams } from 'react-router-dom' +import classe from '../assets/AllStyleComponents.module.css' +import classeAdd from '../assets/AddStudent.module.css' +import classeHome from '../assets/Home.module.css' +import { FaBook, FaDownload } from 'react-icons/fa' +import { IoMdReturnRight } from 'react-icons/io' +import { Box, Button, Typography, ThemeProvider, Modal } from '@mui/material' +import Paper from '@mui/material/Paper' +import html2cancas from 'html2canvas' +import jsPDF from 'jspdf' +import autoTable from 'jspdf-autotable' + +const ModalExportFichr = () => { + const [fiche, setFiche] = useState([]) + const [mentions, setMentions] = useState([]) + const PaperRef = useRef() + + let { matiere_id } = useParams() + let { nom } = useParams() + + useEffect(() => { + if (matiere_id !== null && matiere_id !== undefined) { + window.notesysteme.extractFiches({ matiere_id }).then((response) => { + setFiche(response) + }) + } + window.mention.getMention().then((response) => { + setMentions(response) + }) + }, [matiere_id]) + + // Sort by NomPrenom alphabetically + const sortedStudents = fiche.sort((a, b) => a.nom.localeCompare(b.nom)) + + function compareMention(id) { + let mentionText + mentions.map((ment) => { + if (id == ment.id) { + mentionText = ment.nom + } + }) + + return mentionText + } + + // const download = () => { + // const generatePDF = async () => { + // try { + // await new Promise((resolve) => setTimeout(resolve, 500)); + + // const canvas = await html2cancas(PaperRef.current, { + // scale: 2, + // useCORS: true, + // logging: false, + // backgroundColor: "#ffffff", + // imageTimeout: 0, // ⬅️ Prevent timeout errors + // }); + + // const imgData = canvas.toDataURL("image/jpeg", 0.8); // Use JPEG for smaller size + // const pdf = new jsPDF({ + // orientation: "portrait", + // unit: "mm", + // format: "a4", + // compress: true, + // }); + + // let imgWidth = 210; + // let imgHeight = (canvas.height * imgWidth) / canvas.width; + // let yPosition = 0; + + // // If image height is greater than A4 page height, add multiple pages + // while (yPosition < imgHeight) { + // pdf.addImage(imgData, "JPEG", 0, yPosition * -1, imgWidth, imgHeight); + // if (yPosition + 297 < imgHeight) pdf.addPage(); // A4 height = 297mm + // yPosition += 297; + // } + + // pdf.save("document.pdf"); + + // } catch (error) { + // console.error("Error generating PDF:", error); + // } + // }; + // generatePDF(); + // } + + const download = () => { + const generatePDF = () => { + try { + const pdf = new jsPDF({ + orientation: 'portrait', + unit: 'mm', + format: 'a4' + }) + + // Select the table + autoTable(pdf, { + html: '#myTable', // ID de la table + startY: 20, + theme: 'grid', + headStyles: { + fillColor: 'gray', + halign: 'center', + fontStyle: 'bold', + textColor: 'black' + }, // Supprimer la couleur et centrer + margin: { top: 10 }, + styles: { fontSize: 8, cellPadding: 2, halign: 'center' }, // Centrer le texte des cellules + didDrawPage: (data) => { + pdf.text('', 14, 10) + } + }) + + pdf.save('document.pdf') + } catch (error) { + console.error('Error generating PDF:', error) + } + } + generatePDF() + } + + return ( +
+
+
+
+

+ + Matiere +

+
+ + + + + + +
+
+
+
+ +
+ +
+ + + + + + + + + + + + + + {sortedStudents.map((fi, index) => ( + + + + + + + ))} + +
{nom}
Nom et PrenomMentionEmergement
+ {index + 1} + + {fi.nom} {fi.prenom} + + {compareMention(fi.mention_id)} +
+
+
+
+
+ ) +} + +export default ModalExportFichr diff --git a/src/renderer/src/components/NiveauMatiere.jsx b/src/renderer/src/components/NiveauMatiere.jsx new file mode 100644 index 0000000..1b09e40 --- /dev/null +++ b/src/renderer/src/components/NiveauMatiere.jsx @@ -0,0 +1,132 @@ +import React, { useEffect, useState } from 'react'; +import { + Dialog, + DialogActions, + DialogContent, + DialogTitle, + Button, + Box, + Grid, + FormControl, + InputLabel, + Select, + OutlinedInput, + MenuItem +} from '@mui/material' + +const NiveauMatiere = ({ open, onClose, matiere_id }) => { + const [formData, setFormData] = useState({ + niveau_id: '', + id: '' + }) + + const [niveaux, setNiveaux] = useState([]) + const [niveauxMatiere, setNiveauxMatiere] = useState([]) + console.log(niveaux); + + useEffect(() => { + window.niveaus.getNiveau().then((response) => { + setNiveaux(response) + }) + }, []) + + + useEffect(() => { + if (niveauxMatiere.length !== 0) { + const niveauIds = niveauxMatiere.map((item) => item.niveau_id) + + setFormData((prevState) => ({ + ...prevState, + niveau_id: niveauIds + })) + } + }, [niveauxMatiere]) + + useEffect(() => { + if (matiere_id) { + setFormData(prev => ({ + ...prev, + id: matiere_id + })); + } + }, [matiere_id]); + + + + const handleChange = (event) => { + const { name, value } = event.target + + setFormData(prevState => ({ + ...prevState, + niveau_id: value // pas de tableau + })); + + } + + const formSubmit = async (e) => { + e.preventDefault(); + console.log("Form envoyé côté front:", formData); + let response = await window.matieres.updateMatiereNiveau(formData); + console.log("Réponse backend:", response); + if (response.success) { + onClose(); + } + }; + + + return ( + +
+ Assignation à des niveaux + + + + + + + Niveaux + + + + + + + + + + + +
+
+ ) +} + +export default NiveauMatiere \ No newline at end of file diff --git a/src/renderer/src/components/ParcourMatiere.jsx b/src/renderer/src/components/ParcourMatiere.jsx new file mode 100644 index 0000000..d2f642f --- /dev/null +++ b/src/renderer/src/components/ParcourMatiere.jsx @@ -0,0 +1,130 @@ +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 ParcourMatiere = ({ open, onClose, matiere_id }) => { + const [formData, setFormData] = useState({ + parcour_id: [], + matiere_id: '' + }) + + const [parcours, setParcours] = useState([]) + const [parcoursMatiere, setParcoursMatiere] = useState([]) + + useEffect(() => { + window.notesysteme.getParcours().then((response) => { + setParcours(response) + }) + }, []) + + useEffect(() => { + if (matiere_id) { + setFormData({ + matiere_id: matiere_id + }) + + window.notesysteme.getParcourMatiere({ matiere_id }).then((response) => { + setParcoursMatiere(response) + }) + } + }, [matiere_id]) + + useEffect(() => { + if (parcoursMatiere.length !== 0) { + const parcourIds = parcoursMatiere.map((item) => item.parcour_id) + + setFormData((prevState) => ({ + ...prevState, + parcour_id: parcourIds // Merge & remove duplicates + })) + } + }, [parcoursMatiere]) + + const handleChange = (event) => { + const { name, value } = event.target + + setFormData((prevState) => ({ + ...prevState, + [name]: value // Ensures multiple selection works correctly + })) + } + + const formSubmit = async (e) => { + e.preventDefault() + let response = await window.notesysteme.parcourMatiere(formData) + if (response.success) { + onClose() + } + } + + return ( + +
+ Assignation à des parcours + + + + + + + Parcours + + + + + + + + + + + +
+
+ ) +} + +export default ParcourMatiere diff --git a/src/renderer/src/components/Parcours.jsx b/src/renderer/src/components/Parcours.jsx new file mode 100644 index 0000000..885a9aa --- /dev/null +++ b/src/renderer/src/components/Parcours.jsx @@ -0,0 +1,199 @@ +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 } from 'react-router-dom' +import { MdRule } from 'react-icons/md' +import { FaPlus } from 'react-icons/fa' +import { Box, Button, InputAdornment, Typography, Modal, TextField, Grid } from '@mui/material' +import Paper from '@mui/material/Paper' +import { createTheme, ThemeProvider } from '@mui/material/styles' +import { DataGrid, GridToolbar } from '@mui/x-data-grid' +import { frFR } from '@mui/x-data-grid/locales' +import { Tooltip } from 'react-tooltip' +import warning from '../assets/warning.svg' +import success from '../assets/success.svg' +import AddParcours from './AddParcours' +import UpdateParcour from './UpdateParcour' +import { FaPenToSquare } from 'react-icons/fa6' + +const Parcours = () => { + const [parcours, setParcours] = useState([]) + + useEffect(() => { + window.notesysteme.getParcours().then((response) => { + setParcours(response) + }) + }, []) + + 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 columns = [ + { field: 'nom', headerName: 'Nom', width: 230 }, + { field: 'uniter', headerName: 'Uniter', width: 180 }, + { field: 'mention', headerName: 'Mention', width: 230 }, + { + field: 'action', + headerName: 'Action', + flex: 1, + renderCell: (params) => ( +
+ openUpdate(params.value)}> + + +
+ ) + } + ] + + const paginationModel = { page: 0, pageSize: 5 } + + const dataRow = parcours.map((parc) => ({ + id: parc.id, + nom: parc.nom, + uniter: parc.uniter, + mention: parc.mention_nom || parc.mention || 'Mention non trouvée', + action: parc.id + })) + + const [isSubmitted, setIsSubmitted] = useState(false) + const handleFormSubmit = (status) => { + setIsSubmitted(status) + } + + useEffect(() => { + if (isSubmitted) { + window.notesysteme.getParcours().then((response) => { + setParcours(response) + }) + setIsSubmitted(false) + } + }, [isSubmitted]) + + const [openModalAdd, setOpenModalAdd] = useState(false) + const [openModalUpdate, setOpenModalUpdate] = useState(false) + const [idToSend, setIdToSend] = useState(null) + + const closeUpdate = () => { + setOpenModalUpdate(false) + } + + const openUpdate = (id) => { + setIdToSend(id) + setOpenModalUpdate(true) + } + + const openModalAddFunction = () => { + setOpenModalAdd(true) + } + + const closeModalAdd = () => { + setOpenModalAdd(false) + } + + return ( +
+ + +
+
+
+

+ + Parcours +

+ + + +
+
+
+ +
+
+ + +
+ +
+
+
+
+
+
+ ) +} + +export default Parcours diff --git a/src/renderer/src/components/Resultat.jsx b/src/renderer/src/components/Resultat.jsx new file mode 100644 index 0000000..d91817c --- /dev/null +++ b/src/renderer/src/components/Resultat.jsx @@ -0,0 +1,218 @@ +import React, { useEffect, useState } from 'react' +import { useParams, Link } from 'react-router-dom' +import classe from '../assets/AllStyleComponents.module.css' +import classeHome from '../assets/Home.module.css' +import Paper from '@mui/material/Paper' +import { Button, Modal, Box } from '@mui/material' +import { IoMdReturnRight } from 'react-icons/io' +import jsPDF from 'jspdf' +import autoTable from 'jspdf-autotable' +import { FaDownload } from 'react-icons/fa' + +const Resultat = () => { + const { niveau, scolaire } = useParams() + const formData = { + niveau, + scolaire + } + const [etudiants, setEtudiants] = useState([]) + const [mention, setMention] = useState([]) + const [session, setSession] = useState([]) + + useEffect(() => { + window.notes.getMoyenne(formData).then((response) => { + setEtudiants(response) + }) + window.noteRepech.getMoyenneRepech(formData).then((response) => { + setSession(response) + }) + window.mention.getMention().then((response) => { + setMention(response) + }) + }, []) + + let dataToMap = [] + + function returnmention(id) { + let mentions + for (let index = 0; index < mention.length; index++) { + if (mention[index].id == id) { + mentions = mention[index].nom + } + } + return mentions + } + + function checkNull(params) { + if (params == null || params == undefined) { + return null + } + return params + } + + const print = () => { + const generatePDF = () => { + try { + const pdf = new jsPDF({ + orientation: 'portrait', + unit: 'mm', + format: 'a4' + }) + + // Select the table + autoTable(pdf, { + html: '#myTable2', // ID de la table + startY: 20, + theme: 'grid', + headStyles: { + fillColor: 'gray', + halign: 'center', + fontStyle: 'bold', + textColor: 'black' + }, // Supprimer la couleur et centrer + margin: { top: 10 }, + styles: { fontSize: 8, cellPadding: 2, halign: 'center' }, // Centrer le texte des cellules + didDrawPage: (data) => { + pdf.text('', 14, 10) + } + }) + + pdf.save(`Resultat-${niveau}-${scolaire}.pdf`) + } catch (error) { + console.error('Error generating PDF:', error) + } + } + generatePDF() + } + + function compareSessionNotes(session1, session2) { + let notes + if (session2) { + if (session1 < session2.note) { + notes = session2.note + } else { + notes = session1 + } + } else { + notes = session1 + } + return notes + } + + for (let index = 0; index < etudiants.length; index++) { + let total = 0 + let note = 0 + let totalCredit = 0 + + // Create a new object for each student + let modelJson = { + id: '', + nom: '', + prenom: '', + photos: '', + moyenne: '', + mention: '', + anneescolaire: '' + } + + for (let j = 0; j < etudiants[index].length; j++) { + modelJson.id = etudiants[index][j].etudiant_id + modelJson.nom = etudiants[index][j].nom + modelJson.prenom = etudiants[index][j].prenom + modelJson.photos = etudiants[index][j].photos + modelJson.mention = etudiants[index][j].mention_id + modelJson.anneescolaire = etudiants[index][j].annee_scolaire + + // console.log(checkNull(session[index][j])); + if (session[index]) { + note += + compareSessionNotes(etudiants[index][j].note, checkNull(session[index][j])) * + etudiants[index][j].credit + } else { + note += etudiants[index][j].note * etudiants[index][j].credit + } + totalCredit += etudiants[index][j].credit + } + + total = note / totalCredit + modelJson.moyenne = total.toFixed(2) + + // Add the new object to the array + dataToMap.push(modelJson) + } + + const sortedStudents = dataToMap + .filter((student) => parseFloat(student.moyenne) >= 10) + .sort((a, b) => parseFloat(b.moyenne) - parseFloat(a.moyenne)) + + console.log(sortedStudents) + + return ( +
+
+
+
+

+ Resultat des {niveau} en {scolaire} +

+
+ + + + window.history.back()}> + + +
+
+
+
+ +
+ + + + + + + + + + + + + + + {sortedStudents.map((sorted) => ( + + + + + + + ))} + +
+
+ Niveau {niveau} | Année Scolaire {scolaire} +
+
NomPrenomMentionMoyenne
{sorted.nom}{sorted.prenom}{returnmention(sorted.mention)}{sorted.moyenne}
+
+
+
+ ) +} + +export default Resultat diff --git a/src/renderer/src/components/Sidenav.jsx b/src/renderer/src/components/Sidenav.jsx new file mode 100644 index 0000000..7e1c441 --- /dev/null +++ b/src/renderer/src/components/Sidenav.jsx @@ -0,0 +1,283 @@ +import React, { useState } from 'react' +import classe from '../assets/Sidenav.module.css' +import { RiDashboardHorizontalFill } from 'react-icons/ri' +import { PiStudentFill } from 'react-icons/pi' +import { IoMdHelpCircleOutline } from 'react-icons/io' +import { CgNotes } from 'react-icons/cg' +import { FaUserCircle, FaBook, FaUserCog } from 'react-icons/fa' +import { Link } from 'react-router-dom' +import { useLocation } from 'react-router-dom' +import { Tooltip } from 'react-tooltip' +import { LuLogOut } from 'react-icons/lu' +import { GiUpgrade } from 'react-icons/gi' +import Menu from '@mui/material/Menu' +import MenuItem from '@mui/material/MenuItem' +import { useAuthContext } from '../contexts/AuthContext' +import { MdAdminPanelSettings, MdRule } from 'react-icons/md' +import { BsCalendar2Date } from 'react-icons/bs' +import { SiVitest } from 'react-icons/si' +import { GrManual } from 'react-icons/gr' +import { FaClipboardList } from 'react-icons/fa6' + +const Sidenav = () => { + const [anchorEl, setAnchorEl] = useState(null) + const open = Boolean(anchorEl) + const { setToken } = useAuthContext() + + const handleClick = (event) => { + setAnchorEl(event.currentTarget) + } + + const handleClose = () => { + setAnchorEl(null) + } + + // don't touch it, i don't know why but the active button stop workin without this + const location = useLocation() + + const logout = () => { + localStorage.removeItem('ACCESS_TOKEN') + setToken(null) + } + + return ( + + ) +} + +export default Sidenav diff --git a/src/renderer/src/components/SingleAnneeScolaire.jsx b/src/renderer/src/components/SingleAnneeScolaire.jsx new file mode 100644 index 0000000..b50e262 --- /dev/null +++ b/src/renderer/src/components/SingleAnneeScolaire.jsx @@ -0,0 +1,268 @@ +import { 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 { Link, useParams } from 'react-router-dom' +import { IoMdReturnRight } from 'react-icons/io' +import { Modal, Box, Typography, Button, InputAdornment, TextField, Grid } from '@mui/material' +import { FaCalendarAlt } from 'react-icons/fa' +import svgError from '../assets/error.svg' +import svgSuccess from '../assets/success.svg' +import { BsCalendar2Date } from 'react-icons/bs' +import dayjs from 'dayjs' + +const SingleAnneeScolaire = () => { + const { id } = useParams() + const [formData, setFormData] = useState({ + code: '', + debut: '', + fin: '', + id: '' + }) + + const [scolaire, setScolaire] = useState([]) + const [status, setStatus] = useState(200) + const [open, setOpen] = useState(false) + + useEffect(() => { + window.anneescolaire.getSingleAnneeScolaire({ id }).then((response) => { + setScolaire(response) + }) + }, []) + + useEffect(() => { + setFormData((prev) => ({ + ...prev, + code: scolaire.code, + debut: dayjs(scolaire.debut).format('YYYY-MM-DD'), + fin: dayjs(scolaire.fin).format('YYYY-MM-DD'), + id: scolaire.id + })) + }, [scolaire]) + + /** + * 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 response = await window.anneescolaire.updateAnneeScolaire(formData) + + if (response.success) { + setOpen(true) + setStatus(200) + } else { + setStatus(400) + setOpen(true) + } + } + + const handleClose = () => setOpen(false) + + const modals = () => ( + + + + + {status === 200 ? 'Mise à jour effectuée avec succès' : 'Erreur'} + + + + + + + ) + + return ( +
+ {modals()} +
+
+
+

+ + Mise a jour Année Scolaire +

+ window.history.back()}> + + +
+
+
+ +
+ +
+

+ mise a jour Année Scolaire +

+ + + + + + ) + }} + onChange={handleInputChange} + required + 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 + } + }} + /> + + + + + + ) + }} + 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 + } + }} + /> + + + + + + ) + }} + onChange={handleInputChange} + required + 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 + } + }} + /> + + + + + +
+
+
+
+ ) +} + +export default SingleAnneeScolaire diff --git a/src/renderer/src/components/SingleEtudiant.jsx b/src/renderer/src/components/SingleEtudiant.jsx new file mode 100644 index 0000000..1b01d05 --- /dev/null +++ b/src/renderer/src/components/SingleEtudiant.jsx @@ -0,0 +1,1156 @@ +import React, { useEffect, useState } from 'react' +import { + TextField, + Button, + Grid, + Card, + CardContent, + Avatar, + Typography, + InputAdornment, + Modal, + Box, + Autocomplete, + IconButton +} from '@mui/material' +import { Tooltip } from 'react-tooltip' +import { FaClipboardList, FaEdit } from 'react-icons/fa' +import { AiOutlineUser, AiOutlineCalendar, AiOutlineNumber } from 'react-icons/ai' +import { Link, useParams } from 'react-router-dom' +import classe from '../assets/AllStyleComponents.module.css' +import { FaCamera } from 'react-icons/fa' +import { styled } from '@mui/material/styles' +import warning from '../assets/warning.svg' +import { FaRegTimesCircle } from 'react-icons/fa' +import MenuItem from '@mui/material/MenuItem' +import { MdGrade, MdRule } from 'react-icons/md' +import { FaLeftLong, FaRightLong } from 'react-icons/fa6' +import dayjs from 'dayjs' + +const SingleEtudiant = () => { + const { id } = useParams() + const [etudiant, setEtudiant] = useState({}) + const [niveaus, setNiveaus] = useState([]) + const [mention, setMention] = useState([]) + const [parcours, setParcours] = useState([]) + + const [editMode, setEditMode] = useState(false) + const [formData, setFormData] = useState({ + nom: '', + prenom: '', + photos: null, + date_de_naissances: '', + niveau: '', + annee_scolaire: '', + status: '', + mention_id: '', + num_inscription: '', + sexe: 'Garçon', + nationalite: '', + cin: '', + date_delivrance: '', + annee_bacc: '', + serie: '', + boursier: 'oui', + domaine: '', + contact: '', + parcours: '' + }) + const [status, setStatus] = useState([]) + const [scolaire, setScolaire] = useState([]) + + // Fetch the student data + useEffect(() => { + window.etudiants.getSingle({ id }).then((response) => { + setEtudiant(response) + }) + + window.niveaus.getNiveau().then((response) => { + setNiveaus(response) + }) + + window.statuss.getStatus().then((response) => { + setStatus(response) + }) + + window.anneescolaire.getAnneeScolaire().then((response) => { + setScolaire(response) + }) + + window.mention.getMention().then((response) => { + setMention(response) + }) + + window.notesysteme.getParcours().then((response) => { + setParcours(response) + }) + }, [id]) + + function comparestatut(statutID) { + let statusText + + status.map((statu) => { + if (statutID == statu.id) { + statusText = statu.nom + } + }) + return statusText + } + + function compareMention(mentionId) { + let mentionText + + mention.map((ment) => { + if (mentionId == ment.id) { + mentionText = ment.nom + } + }) + return mentionText + } + + // Populate formData with etudiant values when etudiant changes + useEffect(() => { + if (etudiant) { + setFormData({ + nom: etudiant.nom || '', + prenom: etudiant.prenom || '', + photos: etudiant.photos || null, + date_de_naissances: dayjs(etudiant.date_de_naissances).format('YYYY-MM-DD') || '', + niveau: etudiant.niveau || '', + annee_scolaire: etudiant.annee_scolaire || '', + status: etudiant.status || '', + mention_id: etudiant.mention_id || '', + num_inscription: etudiant.num_inscription || '', + id: etudiant.id || '', + sexe: etudiant.sexe, + nationalite: etudiant.nationalite, + cin: etudiant.cin, + date_delivrance: dayjs(etudiant.date_delivrance).format('YYYY-MM-DD'), + annee_bacc: dayjs(etudiant.annee_bacc).format('YYYY-MM-DD'), + serie: etudiant.serie, + boursier: etudiant.boursier, + domaine: etudiant.domaine, + contact: etudiant.contact, + parcours: etudiant.parcours + }) + } + }, [etudiant]) + + const handleChange = (e) => { + const { name, value } = e.target + setFormData({ + ...formData, + [name]: value + }) + } + + const handleSubmit = async () => { + const { + nom, + prenom, + date_de_naissances, + niveau, + annee_scolaire, + num_inscription, + sexe, + nationalite, + cin, + date_delivrance, + annee_bacc, + serie, + boursier, + domaine, + contact, + parcours + } = formData + + // Validation check: Ensure all required fields are filled + if ( + !nom || + !prenom || + !date_de_naissances || + !niveau || + !annee_scolaire || + !num_inscription || + !sexe || + !nationalite || + !cin || + !date_delivrance || + !annee_bacc || + !serie || + !boursier || + !domaine || + !contact + ) { + // setOpen(true) + alert('Veuillez remplir tous les champs obligatoires.') + console.log(formData); + + return // Prevent submission if validation fails + } + + // If validation passes, proceed with the submission + try { + let response = await window.etudiants.updateEtudiants(formData) + if (response.success) { + setEtudiant(formData) + } + + setEditMode(false) + } catch (error) { + console.error('Error updating student:', error) + } + } + + /** + * 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 = () => ( + + + + {' '} + Voulez vous modifier la photos ? + + + + + + + + ) + + const [pdp, setPdp] = useState(null) + + const updatePDP = () => { + let avatar = document.getElementById('imagepdpdynamic') + let imgAvatar = avatar.querySelector('img') + + window.etudiants.updateEtudiantsPDP({ pdp, id }).then((responses) => { + console.log(responses); + + if (responses.success) { + if (imgAvatar === null) { + let image = document.createElement('img') + image.setAttribute('src', pdp) + image.style.width = '100%' + avatar.appendChild(image) + } else { + imgAvatar.removeAttribute('src') + imgAvatar.setAttribute('src', pdp) + } + setOpen(false) + } + }) + } + + const changePDP = () => { + document.getElementById('inputhiden').click() + } + + const handleFileChange = (e) => { + let img_file = e.target.files[0] + const reader = new FileReader() + reader.readAsDataURL(img_file) + reader.onload = (ev) => { + const url = ev.target.result + // initialisation de nouvelle imageURL + const image = document.createElement('img') + image.src = url + + // create a new image + image.onload = (event) => { + let canvas = document.createElement('canvas') + let ratio = 250 / event.target.width + canvas.width = 250 + canvas.height = event.target.height * ratio + const context = canvas.getContext('2d') + context.drawImage(image, 0, 0, canvas.width, canvas.height) + + // new url + const new_URL = canvas.toDataURL('image/jpeg', 90) + setPdp(new_URL) + setOpen(true) + } + } + } + + 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 [page1, setPage1] = useState(true) + const [page2, setPage2] = useState(false) + const [page3, setPage3] = useState(false) + + const seePage1 = () => { + setPage2(false) + setPage3(false) + setPage1(true) + } + + const seePage2 = () => { + setPage1(false) + setPage3(false) + setPage2(true) + } + + const seePage3 = () => { + setPage1(false) + setPage2(false) + setPage3(true) + } + + return ( +
+ {modals()} +
+ + + + + + + + +
+ + +
+
+ {editMode ? ( + <> + {page1 && ( + + {/* Group Nom and Prenom */} + + + + + + ) + }} + sx={{ + marginBottom: 2, + padding: 1, + marginLeft: '4%', + '& .MuiOutlinedInput-root': { + '&:hover fieldset': { + borderColor: '#ff9800' // Set the border color on hover + } + } + }} + /> + + + + + + ) + }} + sx={{ + marginBottom: 2, + padding: 1, + marginLeft: '4%', + '& .MuiOutlinedInput-root': { + '&:hover fieldset': { + borderColor: '#ff9800' // Set the border color on hover + } + } + }} + /> + + + + {/* Group Niveau and Date de Naissance */} + + + + + + ) + }} + sx={{ + marginBottom: 2, + padding: 1, + marginLeft: '4%', + '& .MuiOutlinedInput-root': { + '&:hover fieldset': { + borderColor: '#ff9800' // Couleur de la bordure au survol + } + } + }} + > + {/* Liste des options */} + {niveaus.map((niveau) => ( + + {niveau.nom} + + ))} + + + + + + + ) + }} + sx={{ + marginBottom: 2, + padding: 1, + marginLeft: '4%', + '& .MuiOutlinedInput-root': { + '&:hover fieldset': { + borderColor: '#ff9800' // Set the border color on hover + } + } + }} + /> + + + + {/* Group Année Scolaire and Numero d'Inscription */} + + + + + + ) + }} + sx={{ + marginBottom: 2, + padding: 1, + marginLeft: '4%', + '& .MuiOutlinedInput-root': { + '&:hover fieldset': { + borderColor: '#ff9800' // Set the border color on hover + } + } + }} + > + {scolaire.map((statu) => ( + + {statu.code} + + ))} + + + + + + + ) + }} + sx={{ + marginBottom: 2, + padding: 1, + marginLeft: '4%', + '& .MuiOutlinedInput-root': { + '&:hover fieldset': { + borderColor: '#ff9800' // Set the border color on hover + } + } + }} + /> + + + + {/* Group status*/} + + + + + + ) + }} + sx={{ + marginBottom: 2, + padding: 1, + marginLeft: '4%', + '& .MuiOutlinedInput-root': { + '&:hover fieldset': { + borderColor: '#ff9800' // Couleur de la bordure au survol + } + } + }} + > + {status.map((statu) => ( + + {statu.nom} + + ))} + + + + 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 + })) + }} + // size="small" // Make the Autocomplete small + isOptionEqualToValue={(option, value) => option.id === value.id} // Ensure correct matching of options + renderInput={(params) => ( + + + + + {params.InputProps.startAdornment} + + ) + }} + sx={{ + marginBottom: 2, + padding: 1, + marginLeft: '4%', + // marginBottom: '5px', + '& .MuiOutlinedInput-root': { + '&:hover fieldset': { + borderColor: '#ff9800' // Set border color on hover + } + } + }} + /> + )} + /> + + + + )} + + {page2 && ( + + {/* Group Nom and Prenom */} + + + + + + ) + }} + sx={{ + marginBottom: 2, + padding: 1, + marginLeft: '4%', + '& .MuiOutlinedInput-root': { + '&:hover fieldset': { + borderColor: '#ff9800' // Set the border color on hover + } + } + }} + /> + + + + + + ) + }} + sx={{ + marginBottom: 2, + padding: 1, + marginLeft: '4%', + '& .MuiOutlinedInput-root': { + '&:hover fieldset': { + borderColor: '#ff9800' // Set the border color on hover + } + } + }} + /> + + + + {/* Group Niveau and Date de Naissance */} + + + + + + ) + }} + sx={{ + marginBottom: 2, + padding: 1, + marginLeft: '4%', + '& .MuiOutlinedInput-root': { + '&:hover fieldset': { + borderColor: '#ff9800' // Couleur de la bordure au survol + } + } + }} + > + {/* Liste des options */} + Garçon + Fille + + + + + + + ) + }} + sx={{ + marginBottom: 2, + padding: 1, + marginLeft: '4%', + '& .MuiOutlinedInput-root': { + '&:hover fieldset': { + borderColor: '#ff9800' // Set the border color on hover + } + } + }} + /> + + + + {/* Group Année Scolaire and Numero d'Inscription */} + + + + + + ) + }} + sx={{ + marginBottom: 2, + padding: 1, + marginLeft: '4%', + '& .MuiOutlinedInput-root': { + '&:hover fieldset': { + borderColor: '#ff9800' // Set the border color on hover + } + } + }} + > + + + + + + ) + }} + sx={{ + marginBottom: 2, + padding: 1, + marginLeft: '4%', + '& .MuiOutlinedInput-root': { + '&:hover fieldset': { + borderColor: '#ff9800' // Set the border color on hover + } + } + }} + /> + + + + {/* Group status*/} + + + + + + ) + }} + sx={{ + marginBottom: 2, + padding: 1, + marginLeft: '4%', + '& .MuiOutlinedInput-root': { + '&:hover fieldset': { + borderColor: '#ff9800' // Couleur de la bordure au survol + } + } + }} + > + Oui + Non + + + + option.nom || ''} // Display `nom` + value={parcours.find((item) => item.nom === etudiant.parcours) || null} // Find selected option based on `nom` + onChange={(event, newValue) => { + setFormData((prevData) => ({ + ...prevData, + parcours: newValue ? newValue.nom : '' // Store only `nom` + })) + }} + isOptionEqualToValue={(option, value) => option.nom === value?.nom} // Ensure correct matching + renderInput={(params) => ( + + + + + {params.InputProps.startAdornment} + + ) + }} + sx={{ + marginBottom: 2, + padding: 1, + marginLeft: '4%', + // marginBottom: '5px', + '& .MuiOutlinedInput-root': { + '&:hover fieldset': { + borderColor: '#ff9800' // Set border color on hover + } + } + }} + /> + )} + /> + + + + )} + + {page3 && ( + + + + + + + ) + }} + sx={{ + marginBottom: 2, + padding: 1, + marginLeft: '4%', + '& .MuiOutlinedInput-root': { + '&:hover fieldset': { + borderColor: '#ff9800' // Set the border color on hover + } + } + }} + /> + + + + + + ) + }} + sx={{ + marginBottom: 2, + padding: 1, + marginLeft: '4%', + '& .MuiOutlinedInput-root': { + '&:hover fieldset': { + borderColor: '#ff9800' // Set the border color on hover + } + } + }} + /> + + + + )} + + {/* Submit Button */} + +
+ + + + {/* + Page précédents + */} + + + + {/* + Page suivants + */} +
+ +
+ + ) : ( + <> +
+
+ + + Nom: {etudiant.nom} + + + + + Prenom: {etudiant.prenom} + + + + + Niveau: {etudiant.niveau} + + + + + Status: {comparestatut(etudiant.status)} + + + + + Date de Naissance: {dayjs(etudiant.date_de_naissances).format('YYYY-MM-DD')} + + + + + Année Scolaire: {etudiant.annee_scolaire} + + + + + Numero d'Inscription: {etudiant.num_inscription} + + + + + Mention: {compareMention(etudiant.mention_id)} + + +
+
+ + )} + + + + +
+
+
+
+
+ ) +} + +export default SingleEtudiant diff --git a/src/renderer/src/components/Student.jsx b/src/renderer/src/components/Student.jsx new file mode 100644 index 0000000..edca75d --- /dev/null +++ b/src/renderer/src/components/Student.jsx @@ -0,0 +1,584 @@ +import React, { useEffect, useRef, useState } from 'react' +import { Link, Navigate } from 'react-router-dom' +import classe from '../assets/AllStyleComponents.module.css' +import classeHome from '../assets/Home.module.css' +import Paper from '@mui/material/Paper' +import { Button, InputAdornment } from '@mui/material' +import { PiStudentFill } from 'react-icons/pi' +import { DataGrid, GridToolbar } from '@mui/x-data-grid' +import { frFR } from '@mui/x-data-grid/locales' +import dayjs from 'dayjs' +import { FaCertificate, FaGraduationCap, FaPlus, FaReceipt, FaToolbox } from 'react-icons/fa' +import { createTheme, ThemeProvider } from '@mui/material/styles' +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 { Tooltip } from 'react-tooltip' +import { FaPenToSquare, FaFilePdf } from 'react-icons/fa6' +import { CgNotes } from 'react-icons/cg' +import { IoEyeSharp } from 'react-icons/io5' +import PDFEditor from './function/PDFEditor' +import ModalCertificate from './ModalCertificate' +import ModalStage from './ModalStage' +import ModalRecepice from './ModalRecepice' +import { processPdf } from './function/PDFEditorV2' +import { MdVerified } from 'react-icons/md' + +const Student = () => { + 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 [status, setStatus] = useState([]) + const [mention, setMention] = useState([]) + + /** + * hook for displaying the students + */ + const [etudiants, setEtudiants] = useState([]) + const [notes, setNotes] = useState([]) + + useEffect(() => { + window.etudiants.getEtudiants().then((response) => { + setEtudiants(response) + }) + + window.notes.getMoyenneVerify().then((response) => { + setNotes(response) + }) + }, []) + + const [niveaus, setNiveau] = useState([]) + + useEffect(() => { + window.niveaus.getNiveau().then((response) => { + setNiveau(response) + }) + + window.statuss.getStatus().then((response) => { + setStatus(response) + }) + + window.mention.getMention().then((response) => { + setMention(response) + }) + }, []) + + const SeeNote = ({ params }) => { + const matchingNote = notes.find( + (element) => + element.etudiant_niveau === params.row.niveau && element.etudiant_id === params.value + ) + + return ( +
+ {matchingNote ? ( + + + + Voir les notes + + + ) : ( + + + + Ajouter un notes à cet étudiant + + + )} +
+ ) + } + + /** + * function to return the date to local date string + * + * @param {string} dateString + * @returns string + */ + function formatDate(dateString) { + // Convert the string to a Date object + const dateObject = new Date(dateString) + + // Format the date using toLocaleDateString + const formattedDate = dateObject.toLocaleDateString('fr-FR', { + day: '2-digit', + month: 'long', + year: 'numeric' + }) + + return formattedDate + } + + /** + * data column, header of the grid + */ + const columns = [ + { field: 'nom', headerName: 'Nom', width: 180 }, + { field: 'prenom', headerName: 'Prenom', width: 180 }, + { field: 'sexe', headerName: 'Sexe', width: 80 }, + { field: 'cin', headerName: 'CIN', width: 180 }, + { field: 'date_deli', headerName: 'Date de delivrance', width: 80 }, + { field: 'nation', headerName: 'Natoinalité', width: 120 }, + { field: 'annee_bacc', headerName: 'Année du Baccalauréat', width: 80 }, + { field: 'serie', headerName: 'Série', width: 80 }, + { field: 'bourse', headerName: 'Boursier', width: 80 }, + { field: 'domaine', headerName: 'Domaine', width: 180 }, + { field: 'contact', headerName: 'Contact', width: 180 }, + { field: 'niveau', headerName: 'Niveau', width: 80 }, + { field: 'date_naissance', headerName: 'Date de naissance', width: 130 }, + { field: 'annee_scolaire', headerName: 'Année univarsitaire', width: 130 }, + { field: 'status', headerName: 'Status', width: 140 }, + { field: 'num_inscription', headerName: "Numéro d'inscription", width: 160 }, + { field: 'parcour', headerName: 'Parcours', width: 150 }, + { + field: 'photos', + headerName: 'Image', + width: 100, + renderCell: (params) => ( + {'image + ) + }, + { + field: 'action', + headerName: 'Action', + width: 300, + renderCell: (params) => ( +
+ + + + + + + Verification Frais de Formation + + + Print(params.value)}> + + + Exporter carte d'etudiants + + + + + + {/* Groupe 1 : Certificat */} + + + + + + Télecharger le Certificat de scolariter + + + + + {/* Groupe 2 : Stage (affiché seulement pour L2) */} + + {params.row.niveau !== 'L1' && ( + + + + Télecharger l'autorisation de stage + + + )} + + + + Télecharger le recepissé d'inscription + + + + + +
+ ) + } + ] + + const Print = async (id) => { + console.log(id) + let etudiant = await window.etudiants.getSingle({ id }) + + if (etudiant) { + let data = { + f1: `${etudiant.nom} ${etudiant.prenom}`, + f2: `Naissances: ${dayjs(etudiant.date_de_naissances).format('DD-MM-YYYY')}`, + f3: `Niveau: ${etudiant.niveau}`, + f4: `Année: ${etudiant.annee_scolaire}`, + f5: `Inscription: ${etudiant.num_inscription}`, + f8: etudiant.photos + } + processPdf(data) + // PDFEditor(data); + } + } + + // ✅ PAGINATION CORRIGÉE - États pour la pagination complète + const [paginationModel, setPaginationModel] = useState({ + page: 0, + pageSize: 20 + }) + const [pageSizeOptions, setPageSizeOptions] = useState([20, 40, 60]) + + // ✅ Gestionnaire complet pour les changements de pagination (page ET pageSize) + const handlePaginationModelChange = (newModel) => { + console.log('📊 Pagination changed:', newModel) // Pour debug + + setPaginationModel(newModel) + + // Si l'utilisateur choisit la plus grande option, ajouter +20 + const maxOption = Math.max(...pageSizeOptions) + if (newModel.pageSize === maxOption) { + setPageSizeOptions((prev) => [...prev, maxOption + 20]) + } + } + + // Ensure that the array is flat (not wrapped in another array) + const dataRow = etudiants.map((etudiant) => ({ + id: etudiant.id, // Ensure this exists and is unique for each etudiant + nom: etudiant.nom, + prenom: etudiant.prenom, + niveau: etudiant.niveau, + date_naissance: dayjs(etudiant.date_de_naissances).format('DD-MM-YYYY'), + annee_scolaire: etudiant.annee_scolaire, + status: comparestatut(etudiant.status), + num_inscription: etudiant.num_inscription, + parcour: etudiant.parcours == null ? 'Pas de parcours' : etudiant.parcours, + photos: etudiant.photos, + sexe: etudiant.sexe, + cin: etudiant.cin, + date_deli: dayjs(etudiant.date_delivrance).format('DD-MM-YYYY'), + nation: etudiant.nationalite, + annee_bacc: etudiant.annee_bacc, + serie: etudiant.serie, + bourse: etudiant.boursier, + domaine: etudiant.domaine, + contact: etudiant.contact, + mention_id: etudiant.mention_id, + action: etudiant.id // Ensure this is a valid URL for the image + })) + + function comparestatut(statutID) { + let statusText + + status.map((statu) => { + if (statutID == statu.id) { + statusText = statu.nom + } + }) + return statusText + } + + /** + * ✅ Fonction de filtrage avec reset de pagination + */ + const FilterData = async (e) => { + let niveau = e.target.value + if (niveau !== '') { + let data = await window.etudiants.FilterDataByNiveau({ niveau }) + setEtudiants(data) + // Reset vers la première page après filtrage + setPaginationModel(prev => ({ ...prev, page: 0 })) + } else { + window.etudiants.getEtudiants().then((response) => { + setEtudiants(response) + // Reset vers la première page + setPaginationModel(prev => ({ ...prev, page: 0 })) + }) + } + } + + const [openModal, setOpenModal] = useState(false) + const [openModal2, setOpenModal2] = useState(false) + const [openModal3, setOpenModal3] = useState(false) + const [json, setJson] = useState() + const [json2, setJson2] = useState() + const [nom, setNom] = useState([]) + const [nom2, setNom2] = useState([]) + + const handleOpen = (id) => { + window.etudiants.getSingle({ id }).then((response) => { + setNom(response) + }) + setOpenModal(true) + } + + const handleOpen3 = (id) => { + window.etudiants.getSingle({ id }).then((response) => { + setNom2(response) + }) + setOpenModal3(true) + } + + const handleOpen2 = () => { + setOpenModal2(true) + } + + function compareMention(mentionID) { + let statusText + + mention.map((statu) => { + if (mentionID == statu.id) { + statusText = statu.nom + } + }) + + return statusText ? statusText.charAt(0).toUpperCase() + statusText.slice(1) : statusText + } + + useEffect(() => { + setJson({ + nomPrenom: nom.nom + ' ' + nom.prenom, + naissances: nom.date_de_naissances, + mention: compareMention(nom.mention_id), + niveau: nom.niveau, + annee: nom.annee_scolaire, + inscri: nom.num_inscription + }) + }, [nom]) + + useEffect(() => { + setJson2({ + nomPrenom: nom.nom + ' ' + nom.prenom, + mention: compareMention(nom.mention_id), + niveau: nom.niveau, + annee: nom.annee_scolaire, + inscri: nom.num_inscription + }) + }, [nom2]) + + const handleClose = () => setOpenModal(false) + const handleClose2 = () => setOpenModal2(false) + const handleClose3 = () => setOpenModal3(false) + + return ( +
+ +
+
+
+

Etudiants

+ + + +
+
+ {/* bare des filtre */} +
+ {/* filtre par niveau */} +
+ + + Niveau + + + +
+
+
+ + {/* display the data-grid students */} +
+
+ + +
+ +
+
+
+ + + +
+
+
+ ) +} + +export default Student \ No newline at end of file diff --git a/src/renderer/src/components/TesteDatagrid.jsx b/src/renderer/src/components/TesteDatagrid.jsx new file mode 100644 index 0000000..f3360cc --- /dev/null +++ b/src/renderer/src/components/TesteDatagrid.jsx @@ -0,0 +1,458 @@ +import React, { useEffect, 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 entete from '../assets/enteterelever.png' +import jsPDF from 'jspdf' +import html2Canvas from 'html2canvas' + +const TesteDatagrid = ({ id, niveau, annee_scolaire, nomPrenom, inscription, refs }) => { + const [print, setPrint] = useState(refs) + const paperRef = useRef() + + const [teste, setTeste] = useState([]) + useEffect(() => { + window.notes.noteMatiere({ id, niveau, annee_scolaire }).then((response) => { + setTeste(response) + }) + }, [id, niveau, annee_scolaire]) + + console.log(teste) + + useEffect(() => { + if (refs) { + let certificat = paperRef.current + html2Canvas(certificat, { scale: 3 }).then((canvas) => { + const URLimg = canvas.toDataURL() + const doc = new jsPDF('portrait', 'mm', 'a4') + const width = doc.internal.pageSize.getWidth() + const height = doc.internal.pageSize.getHeight() + doc.addImage(URLimg, 'PNG', 0, 0, width, height) + doc.save(`releve_${nomPrenom}.pdf`) + }) + } + }, [refs]) + + // Step 1: Group by semestre + const groupedBySemestre = teste.reduce((acc, item) => { + const { semestre, unite_enseignement } = item + + // Ensure a group exists for this `semestre` + if (!acc[semestre]) { + acc[semestre] = {} + } + + // Step 2: Further group by unite_enseignement + if (!acc[semestre][unite_enseignement]) { + acc[semestre][unite_enseignement] = [] + } + + acc[semestre][unite_enseignement].push(item) // Add the item to the appropriate group + return acc + }, {}) + console.log(groupedBySemestre) + + // Initialize semestre1 and semestre2 + let semestre1 = {} + let semestre2 = {} + + // Separate groupedBySemestre into semestre1 and semestre2 dynamically + Object.keys(groupedBySemestre).forEach((semestre) => { + // Check if the semester is odd or even + if (parseInt(semestre.replace('S', '')) % 2 !== 0) { + // Odd semesters (S1, S3, S5, ...) + semestre1[semestre] = groupedBySemestre[semestre] + } else { + // Even semesters (S2, S4, S6, ...) + semestre2[semestre] = groupedBySemestre[semestre] + } + }) + + // Function to count the total elements in the groupedData + function countTotalElements(data) { + let totalCount = 0 + + // Iterate through each key in the object + Object.keys(data).forEach((key) => { + // Add the length of the array at the current key to totalCount + totalCount += data[key].length + }) + + return totalCount + } + + function crontCredit() { + let total = 0 + let credit = document.querySelectorAll('.classCredit') + for (let index = 0; index < credit.length; index++) { + total += Number(credit[index].textContent) + } + return total + } + + function countMoyenneGeneral() { + let total = 0 + let moyenne = document.querySelectorAll('.classMoyenne') + for (let index = 0; index < moyenne.length; index++) { + total += Number(moyenne[index].textContent) + } + + return (total / moyenne.length).toFixed(2) + } + + // Combine both semesters into one object to handle them together + const combinedSemesters = { ...semestre1, ...semestre2 } + + // Function to generate the rows + const generateTableRows = (semesters) => { + const rows = [] + + // Iterate over each semester's keys (S1, S2, etc.) + Object.keys(semesters).forEach((semestreKey) => { + const units = semesters[semestreKey] + + // Iterate over each unite_enseignement + Object.keys(units).forEach((unitKey, idx) => { + const unitArray = units[unitKey] + const isFirstRow = idx === 0 // Check if it's the first row for this unit + + unitArray.forEach((item, itemIdx) => { + rows.push( + + {isFirstRow && itemIdx === 0 ? ( + + {semestreKey} + + ) : null} + + {itemIdx === 0 ? ( + + {unitKey} + + ) : null} + + + {item.nom} + + + {' '} + {item.credit} + + + {item.note} + + + {/* Ensure this renders only once for the unitArray */} + {itemIdx === 0 ? ( + + + {( + unitArray.reduce((sum, item) => sum + item.credit * item.note, 0) / + unitArray.reduce((sum, item) => sum + item.credit, 0) + ).toFixed(2)} + + + ) : null} + + ) + }) + }) + }) + return rows + } + + return ( +
+
+
+ +
+ image en tete +
+
+ ECOLE SUPERIEURE POLYTECHNIQUE + REPOBLIKAN’I MADAGASIKARA + Fitiavana-Tanindrazana-Fandrosoana + ********************* +

RELEVÉE DE NOTE

+
+
+
+ Nom & Prenoms: + {nomPrenom} +
+
+ Année scolaire: + {annee_scolaire} + Niveau: + {niveau} + inscription: + {inscription} +
+
+ + + + + + + + + + + + + {generateTableRows(combinedSemesters)} + + + + + + + + + + + + + + + + +
+ Unités
d’Enseignement
(UE) +
+ Eléments constitutifs + + Crédits + + Note + + Moyenne +
+ {' '} + Total crédit: + + {crontCredit()} +
+ Moyenne générale : + + {countMoyenneGeneral()} +
+ Observation : + + +
+
+
+ Décision de jury : + +
+

Le Directeur

+
+

RATSIMBAZAFY Christian Pierre

+
+
+
+
+
+ ) +} + +export default TesteDatagrid diff --git a/src/renderer/src/components/UpdateTranche.jsx b/src/renderer/src/components/UpdateTranche.jsx new file mode 100644 index 0000000..21a993e --- /dev/null +++ b/src/renderer/src/components/UpdateTranche.jsx @@ -0,0 +1,127 @@ +import React, { useEffect, useState } from 'react' +import { + Dialog, + DialogActions, + DialogContent, + DialogTitle, + TextField, + Button, + Autocomplete, + InputAdornment, + Box, + Grid +} from '@mui/material' +import { MdLabelImportantOutline } from 'react-icons/md' + +const UpdateTranche = ({ open, onClose, onSubmitSuccess, id }) => { + const [formData, setFormData] = useState({ + id: id, + tranchename: '', + montant: '' + }) + const [tranche, setTranche] = useState([]) + + useEffect(() => { + if (id !== null) { + window.etudiants.getSingleTranche({ id }).then((response) => { + setTranche(response) + }) + setFormData({ + id: id + }) + } + }, [id]) + + useEffect(() => { + setFormData((prev) => ({ + ...prev, + tranchename: tranche.tranchename || '', + montant: tranche.montant || '' + })) + }, [tranche]) + + const handleChange = (e) => { + const { name, value } = e.target + setFormData({ ...formData, [name]: value }) + } + + const handleSubmit = async (e) => { + e.preventDefault() + let response = await window.etudiants.updateTranche(formData) + + if (response.changes) { + onClose() + onSubmitSuccess(true) + } + } + + return ( + +
+ Ajout tranche + + + + + + + + ) + }} + /> + + + + + + ) + }} + /> + + + + + + + + +
+
+ ) +} + +export default UpdateTranche diff --git a/src/renderer/src/components/function/GenerateFiche.js b/src/renderer/src/components/function/GenerateFiche.js new file mode 100644 index 0000000..bc0797a --- /dev/null +++ b/src/renderer/src/components/function/GenerateFiche.js @@ -0,0 +1,94 @@ +import { PDFDocument, rgb, StandardFonts } from 'pdf-lib' + +export const generatePDF = async (students) => { + // Sort by NomPrenom alphabetically + const sortedStudents = students.sort((a, b) => a.nom.localeCompare(b.nom)) + + // function compareMention(id) { + // let mentionText; + // mentions.map((ment) => { + // if (id == ment.id) { + // mentionText = ment.nom + // } + // }) + + // return mentionText; + // } + const pdfDoc = await PDFDocument.create() + const pageWidth = 595 // A4 width in points + const pageHeight = 842 // A4 height in points + const margin = 40 + const fontSize = 12 + const rowHeight = 25 + const tableStartY = pageHeight - margin - 50 + const maxRowsPerPage = Math.floor((tableStartY - margin) / rowHeight) + + const font = await pdfDoc.embedFont(StandardFonts.Helvetica) + const page = pdfDoc.addPage([pageWidth, pageHeight]) + + let y = tableStartY + let rowCount = 0 + + // Add Table Headers + page.drawText('Nom matieres', { + x: pageWidth / 2 - 50, + y, + size: 16, + font, + color: rgb(0, 0, 0) + }) + + y -= 30 + + const headers = ['N°', 'Nom et Prenom', 'Mention', 'Émergement'] + const columnWidths = [50, 200, 100, 100] + const xPositions = [ + margin, + margin + columnWidths[0], + margin + columnWidths[0] + columnWidths[1], + margin + columnWidths[0] + columnWidths[1] + columnWidths[2] + ] + + headers.forEach((header, index) => { + page.drawText(header, { x: xPositions[index], y, size: fontSize, font, color: rgb(0, 0, 0) }) + }) + + y -= rowHeight + + // Function to add a new page when needed + const addNewPage = () => { + const newPage = pdfDoc.addPage([pageWidth, pageHeight]) + y = tableStartY + rowCount = 0 + return newPage + } + + // Loop through student data and populate the table + sortedStudents.forEach((student, index) => { + if (rowCount >= maxRowsPerPage) { + page = addNewPage() + } + + const rowData = [ + (index + 1).toString(), + `${student.nom} ${student.prenom}`, + student.mention, + '' + ] + + rowData.forEach((text, i) => { + page.drawText(text, { x: xPositions[i], y, size: fontSize, font, color: rgb(0, 0, 0) }) + }) + + y -= rowHeight + rowCount++ + }) + + // Save and download PDF + const pdfBytes = await pdfDoc.save() + const blob = new Blob([pdfBytes], { type: 'application/pdf' }) + const link = document.createElement('a') + link.href = URL.createObjectURL(blob) + link.download = 'student_list.pdf' + link.click() +} diff --git a/src/renderer/src/components/function/PDFEditor.js b/src/renderer/src/components/function/PDFEditor.js new file mode 100644 index 0000000..b299e4d --- /dev/null +++ b/src/renderer/src/components/function/PDFEditor.js @@ -0,0 +1,134 @@ +import { PDFDocument } from 'pdf-lib' +import { saveAs } from 'file-saver' +import pdf from '../../assets/carte_etudiant.pdf' +import pdf2 from '../../assets/arriere.pdf' +import pdf3 from '../../assets/business_card_template_001_form.pdf' +import QRCode from 'qrcode' + +const PDFEditor = async (data) => { + // Load the existing PDF files + const existingPdfBytes = await fetch(pdf).then((res) => res.arrayBuffer()) + const existingPdfBytes2 = await fetch(pdf2).then((res) => res.arrayBuffer()) + const existingPdfBytes3 = await fetch(pdf3).then((res) => res.arrayBuffer()) + + // Load the PDFs + const pdfDoc = await PDFDocument.load(existingPdfBytes) + const pdfDoc2 = await PDFDocument.load(existingPdfBytes2) + const pdfDoc3 = await PDFDocument.load(existingPdfBytes3) + + // Get the form from the first PDF (front) + const form = pdfDoc.getForm() + form.getTextField('name').setText(data.f1) + form.getTextField('birthday').setText('Date de naissance: ' + data.f2) + form.getTextField('niveau').setText('Niveau: ' + data.f3) + form.getTextField('annee').setText('Année scolaire: ' + data.f4) + form.getTextField('num_inscription').setText("Numéro d'inscription: " + data.f5) + form.flatten() + + // ----------------------------------------- carte frontale ---------------------------------------------------- + const canvas = document.createElement('canvas') + const context = canvas.getContext('2d') + const diameter = 90 + const resolutionScale = 4 + canvas.width = diameter * resolutionScale + canvas.height = diameter * resolutionScale + + const base64Data = data.f8.split(',')[1] + const img = new Image() + img.src = `data:image/png;base64,${base64Data}` + + await new Promise((resolve) => { + img.onload = resolve + }) + + context.scale(resolutionScale, resolutionScale) + context.beginPath() + context.arc(diameter / 2, diameter / 2, diameter / 2, 0, Math.PI * 2) + context.closePath() + context.clip() + context.drawImage(img, 0, 0, diameter, diameter) + + const canvasImageBase64 = canvas.toDataURL('image/png') + const canvasImageBytes = Uint8Array.from(atob(canvasImageBase64.split(',')[1]), (char) => + char.charCodeAt(0) + ) + + const image = await pdfDoc.embedPng(canvasImageBytes) + const page = pdfDoc.getPage(0) + page.drawImage(image, { + x: 74 - diameter / 2, + y: 77 - diameter / 2, + width: diameter, + height: diameter + }) + + const form2 = pdfDoc2.getForm() + const form3 = pdfDoc3.getForm() + const field = form2.getField('f6') + if (field) { + form2.removeField(field) + form3.removeField(field) + } + + // ----------------------------------------------- carte arriere ------------------------------------------- + const paperContent = ` + CUniversity + Nom et prenom: ${data.f1} + Date de naissance: ${data.f2} + Niveau: ${data.f3} + Année scolaire: ${data.f4} + Numéro d'inscription: ${data.f5} + ` + + const qrCanvas = document.createElement('canvas') + const qrWidth = 300 + QRCode.toCanvas( + qrCanvas, + paperContent, + { errorCorrectionLevel: 'H', width: qrWidth }, + (error) => { + if (error) { + console.error(error) + return + } + + const qrImageBase64 = qrCanvas.toDataURL('image/png') + const qrImageBytes = Uint8Array.from(atob(qrImageBase64.split(',')[1]), (char) => + char.charCodeAt(0) + ) + + ;(async () => { + const page2 = pdfDoc2.getPage(0) + const qrImage = await pdfDoc2.embedPng(qrImageBytes) + + const x = 7 + const y = 75 + const qrSize = 95 + + page2.drawImage(qrImage, { + x, + y, + width: qrSize, + height: qrSize + }) + + // Merge the front and back (pages) into a new document + const newPdfDoc = await PDFDocument.create() + // const [frontPage] = await newPdfDoc.copyPages(pdfDoc, [0]); // Front page + const [frontPage] = await newPdfDoc.copyPages(pdfDoc3, [0]) // Front page + const [backPage] = await newPdfDoc.copyPages(pdfDoc2, [0]) // Back page + + newPdfDoc.addPage(frontPage) + newPdfDoc.addPage(backPage) + + const pdfBytes = await newPdfDoc.save() + + // Trigger the download of the merged PDF + const blob = new Blob([pdfBytes], { type: 'application/pdf' }) + saveAs(blob, `carte_etudiant_${data.f1}.pdf`) + })() + } + ) +} + +export default PDFEditor diff --git a/src/renderer/src/components/function/PDFEditorV2.js b/src/renderer/src/components/function/PDFEditorV2.js new file mode 100644 index 0000000..39487e9 --- /dev/null +++ b/src/renderer/src/components/function/PDFEditorV2.js @@ -0,0 +1,151 @@ +import { PDFDocument, PDFTextField } from 'pdf-lib' +import PDF from '../../assets/business_card_template_001_form.pdf' +import PDF2 from '../../assets/business_card_template_002.pdf' +import QRCode from 'qrcode' + +async function fillPdfFields(jsonData) { + const response = await fetch(PDF) // Load the PDF file + const response2 = await fetch(PDF2) // Load the second PDF file + const pdfBytes = await response.arrayBuffer() + const pdfBytes2 = await response2.arrayBuffer() + const pdfDoc = await PDFDocument.load(pdfBytes) + const pdfDoc2 = await PDFDocument.load(pdfBytes2) + const form = pdfDoc.getForm() + + const fields = form + .getFields() + .filter((field) => field instanceof PDFTextField) + .map((field) => ({ name: field.getName(), field })) + + const dataMapping = { + f1: jsonData.f1, + f2: jsonData.f2, + f3: jsonData.f3, + f4: jsonData.f4, + f5: jsonData.f5 + } + + // Fill text fields + Object.keys(dataMapping).forEach((key, index) => { + if (fields[index]) { + const { field } = fields[index] + field.setText(dataMapping[key] || '') + console.log(`Setting ${key}: ${dataMapping[key]} -> ${fields[index].name}`) + } + }) + + // ---------------------------------calculate and paste the image in pdf-------------------------------------- + if (jsonData.f8) { + const diameter = 90 + const resolutionScale = 4 + + const canvas = document.createElement('canvas') + const context = canvas.getContext('2d') + canvas.width = diameter * resolutionScale + canvas.height = diameter * resolutionScale + + const base64Data = jsonData.f8.startsWith('data:image') + ? jsonData.f8.split(',')[1] + : jsonData.f8 // Handle case where "data:image/png;base64," is missing + + const img = new Image() + await new Promise((resolve, reject) => { + img.onload = resolve + img.onerror = reject + img.src = `data:image/png;base64,${base64Data}` + }) + + context.scale(resolutionScale, resolutionScale) + context.beginPath() + context.arc(diameter / 2, diameter / 2, diameter / 2, 0, Math.PI * 2) + context.closePath() + context.clip() + context.drawImage(img, 0, 0, diameter, diameter) + + const canvasImageBase64 = canvas.toDataURL('image/png') + const image = await pdfDoc.embedPng(canvasImageBase64) + + const page = pdfDoc.getPage(0) + + page.drawImage(image, { + x: 186.4 - diameter / 2, // Keep the same X position + y: 90 - diameter / 2, // Keep the same Y position + width: diameter * 0.8, // Reduce size (e.g., 70% of original) + height: diameter * 0.8 // Reduce size (e.g., 70% of original) + }) + } + + // -------------------------------------------paste the qrCode in the pd-------------------------------------- + + const paperContent = ` + C-University + Nom et prenom: ${jsonData.f1} + Date de naissance: ${jsonData.f2} + Niveau: ${jsonData.f3} + Année scolaire: ${jsonData.f4} + Numéro d'inscription: ${jsonData.f5} + ` + + const qrCanvas = document.createElement('canvas') + const qrWidth = 300 + + QRCode.toCanvas( + qrCanvas, + paperContent, + { errorCorrectionLevel: 'H', width: qrWidth }, + (error) => { + if (error) { + console.error(error) + return + } + + const qrImageBase64 = qrCanvas.toDataURL('image/png') + const qrImageBytes = Uint8Array.from(atob(qrImageBase64.split(',')[1]), (char) => + char.charCodeAt(0) + ) + + ;(async () => { + const page2 = pdfDoc2.getPage(0) + const qrImage = await pdfDoc2.embedPng(qrImageBytes) + + const x = 4 + const y = 39 + const qrSize = 92 + + page2.drawImage(qrImage, { + x, + y, + width: qrSize, + height: qrSize + }) + + // Merge the front and back (pages) into a new document + const newPdfDoc = await PDFDocument.create() + // const [frontPage] = await newPdfDoc.copyPages(pdfDoc, [0]); // Front page + const [frontPage] = await newPdfDoc.copyPages(pdfDoc, [0]) // Front page + const [backPage] = await newPdfDoc.copyPages(pdfDoc2, [0]) // Back page + + newPdfDoc.addPage(frontPage) + newPdfDoc.addPage(backPage) + + const pdfBytes = await newPdfDoc.save() + + // Trigger the download of the merged PDF + const blob = new Blob([pdfBytes], { type: 'application/pdf' }) + saveAs(blob, `carte_etudiant_${jsonData.f1}.pdf`) + })() + } + ) + + // Serialize the PDF and return the modified document + const modifiedPdfBytes = await pdfDoc.save() + return modifiedPdfBytes +} + +/** + * function who export and filled PDF data + * @param {JSON} jsonData + */ +export const processPdf = async (jsonData) => { + await fillPdfFields(jsonData) +} diff --git a/src/renderer/src/test/qr.html b/src/renderer/src/test/qr.html new file mode 100644 index 0000000..3d01403 --- /dev/null +++ b/src/renderer/src/test/qr.html @@ -0,0 +1,54 @@ + + + + + + + Document + + +
+
+
+

université de toamasina

+

***********

+

+ ecole superieure
+ polytechnique +

+
+
+
+ +
+
+

Nom : BE

+

Prenom : Joseph Fabrice

+

Date de naissance : 11-12-2001

+

Niveau : L3

+

Année scolaire : 2023-2024

+

Num inscription : 12345678900

+
+
+
+
+
+
+

QR en ligne

+ +
+
+

QR locale

+ +
+
+
+
+ + diff --git a/src/renderer/src/test/relever.html b/src/renderer/src/test/relever.html new file mode 100644 index 0000000..6d08d5f --- /dev/null +++ b/src/renderer/src/test/relever.html @@ -0,0 +1,74 @@ + + + + + + Relevé de Note + + + +
+

REPOBLIKAN’I MADAGASIKARA

+

Fitiavana-Tanindrazana-Fandrosoana

+
+

MINISTÈRE DE L’ENSEIGNEMENT SUPÉRIEUR ET DE LA RECHERCHE SCIENTIFIQUE

+

UNIVERSITÉ DE TOAMASINA

+

ECOLE SUPERIEURE POLYTECHNIQUE

+

Fahaizaña sy Fañahy

+
+ +
+

RELEVÉE DE NOTE

+

Nom & Prenoms : F3

+

+ Niveau : L1 Année scolaire : 2022-2023 +

+

N° inscription : F42

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Unités d’Enseignement (UE)Eléments constitutifsCréditsNoteMoyenne
UEMI1Algèbre4F6F8
UEMI1Analyse5F5
UEPI1Mécanique Général I4F9F13
Total crédit30
+ +

Moyenne générale : F41

+

Observation : F47

+

Décision de jury : F46

+

Le Directeur : RATSIMBAZAFY Christian Pierre

+
+ +