29 changed files with 9719 additions and 0 deletions
@ -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 |
||||
|
}; |
||||
@ -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 |
||||
|
}) |
||||
@ -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 |
||||
|
}) |
||||
@ -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 |
||||
|
} |
||||
@ -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 |
||||
|
} |
||||
@ -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%; |
||||
|
} |
||||
@ -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 = () => ( |
||||
|
<Modal |
||||
|
open={open} |
||||
|
onClose={status === 200 ? handleClose : handleClose} |
||||
|
aria-labelledby="modal-title" |
||||
|
aria-describedby="modal-description" |
||||
|
> |
||||
|
<Box |
||||
|
sx={{ |
||||
|
position: 'absolute', |
||||
|
top: '50%', |
||||
|
left: '50%', |
||||
|
transform: 'translate(-50%, -50%)', |
||||
|
width: 450, |
||||
|
bgcolor: 'background.paper', |
||||
|
boxShadow: 24, |
||||
|
p: 4 |
||||
|
}} |
||||
|
> |
||||
|
{status === 200 ? ( |
||||
|
<Typography style={{ display: 'flex', flexDirection: 'column', alignItems: 'center' }}> |
||||
|
<img src={svgSuccess} alt="" width={50} height={50} />{' '} |
||||
|
<span>Année Scolaire insérer avec succes</span> |
||||
|
</Typography> |
||||
|
) : ( |
||||
|
<Typography style={{ display: 'flex', flexDirection: 'column', alignItems: 'center' }}> |
||||
|
<img src={svgError} alt="" width={50} height={50} />{' '} |
||||
|
<span>Erreur, veuillez réessayer</span> |
||||
|
</Typography> |
||||
|
)} |
||||
|
<Box |
||||
|
sx={{ |
||||
|
marginTop: '2%', |
||||
|
display: 'flex', |
||||
|
gap: '20px', |
||||
|
alignItems: 'end', |
||||
|
justifyContent: 'flex-end' |
||||
|
}} |
||||
|
> |
||||
|
{status === 200 ? ( |
||||
|
<Button onClick={handleClose} color="warning" variant="contained"> |
||||
|
OK |
||||
|
</Button> |
||||
|
) : ( |
||||
|
<Button onClick={handleClose2} color="warning" variant="contained"> |
||||
|
OK |
||||
|
</Button> |
||||
|
)} |
||||
|
</Box> |
||||
|
</Box> |
||||
|
</Modal> |
||||
|
) |
||||
|
|
||||
|
return ( |
||||
|
<div className={classe.mainHome}> |
||||
|
{modals()} |
||||
|
<div className={classeHome.header}> |
||||
|
<div className={classe.h1style}> |
||||
|
<div className={classeHome.blockTitle}> |
||||
|
<h1 style={{ display: 'flex', alignItems: 'center', gap: '10px' }}> |
||||
|
<FaCalendarPlus /> Ajout Année Scolaire |
||||
|
</h1> |
||||
|
<Link to={'#'} onClick={() => window.history.back()}> |
||||
|
<Button color="warning" variant="contained"> |
||||
|
<IoMdReturnRight style={{ fontSize: '20px' }} /> |
||||
|
</Button> |
||||
|
</Link> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<div className={classeHome.boxEtudiantsCard}> |
||||
|
<Paper |
||||
|
sx={{ |
||||
|
width: '100%', |
||||
|
height: '100%', // Auto height to make the grid responsive |
||||
|
minHeight: 500, // Ensures a minimum height |
||||
|
display: 'flex', |
||||
|
flexDirection: 'column', |
||||
|
alignItems: 'center', |
||||
|
justifyContent: 'center' |
||||
|
}} |
||||
|
> |
||||
|
<form |
||||
|
onSubmit={formSubmit} |
||||
|
style={{ |
||||
|
width: '50%', |
||||
|
padding: '1%', |
||||
|
height: '20%', |
||||
|
border: 'solid 2px orange', |
||||
|
borderRadius: '10px' |
||||
|
}} |
||||
|
> |
||||
|
<h4 style={{ textAlign: 'center', padding: '0 0 3% 0' }}> |
||||
|
Creation de nouvelle Année Scolaire |
||||
|
</h4> |
||||
|
<Grid container spacing={2}> |
||||
|
<Grid item xs={12} sm={6}> |
||||
|
<TextField |
||||
|
label={'Année Scolaire'} |
||||
|
name={'code'} |
||||
|
placeholder="2024-2025" |
||||
|
color="warning" |
||||
|
fullWidth |
||||
|
value={formData.code} |
||||
|
InputProps={{ |
||||
|
startAdornment: ( |
||||
|
<InputAdornment position="start"> |
||||
|
<FaCalendarAlt /> |
||||
|
</InputAdornment> |
||||
|
) |
||||
|
}} |
||||
|
onChange={handleInputChange} |
||||
|
inputRef={codeRef} |
||||
|
className="inputAddNote" |
||||
|
sx={{ |
||||
|
'& .MuiOutlinedInput-root': { |
||||
|
'&:hover fieldset': { |
||||
|
borderColor: '#ff9800' // Set the border color on hover |
||||
|
} |
||||
|
}, |
||||
|
'& .MuiInputBase-input::placeholder': { |
||||
|
fontSize: '14px' // Set the placeholder font size |
||||
|
} |
||||
|
}} |
||||
|
/> |
||||
|
</Grid> |
||||
|
<Grid item xs={12} sm={6}> |
||||
|
<TextField |
||||
|
label={'Date de début'} |
||||
|
name={'debut'} |
||||
|
color="warning" |
||||
|
onChange={handleInputChange} |
||||
|
inputRef={debutRef} |
||||
|
value={formData.debut} |
||||
|
fullWidth |
||||
|
type="date" |
||||
|
InputProps={{ |
||||
|
startAdornment: ( |
||||
|
<InputAdornment position="start"> |
||||
|
<FaCalendarAlt /> |
||||
|
</InputAdornment> |
||||
|
) |
||||
|
}} |
||||
|
className="inputAddNote" |
||||
|
sx={{ |
||||
|
'& .MuiOutlinedInput-root': { |
||||
|
'&:hover fieldset': { |
||||
|
borderColor: '#ff9800' // Set the border color on hover |
||||
|
} |
||||
|
}, |
||||
|
'& .MuiInputBase-input::placeholder': { |
||||
|
fontSize: '11px' // Set the placeholder font size |
||||
|
} |
||||
|
}} |
||||
|
/> |
||||
|
</Grid> |
||||
|
<Grid item xs={12} sm={6}> |
||||
|
<TextField |
||||
|
label={'Date de fin'} |
||||
|
name={'fin'} |
||||
|
color="warning" |
||||
|
fullWidth |
||||
|
type="date" |
||||
|
InputProps={{ |
||||
|
startAdornment: ( |
||||
|
<InputAdornment position="start"> |
||||
|
<FaCalendarAlt /> |
||||
|
</InputAdornment> |
||||
|
) |
||||
|
}} |
||||
|
onChange={handleInputChange} |
||||
|
inputRef={finRef} |
||||
|
value={formData.fin} |
||||
|
className="inputAddNote" |
||||
|
sx={{ |
||||
|
'& .MuiOutlinedInput-root': { |
||||
|
'&:hover fieldset': { |
||||
|
borderColor: '#ff9800' // Set the border color on hover |
||||
|
} |
||||
|
}, |
||||
|
'& .MuiInputBase-input::placeholder': { |
||||
|
fontSize: '11px' // Set the placeholder font size |
||||
|
} |
||||
|
}} |
||||
|
/> |
||||
|
</Grid> |
||||
|
<Grid |
||||
|
item |
||||
|
xs={12} |
||||
|
style={{ display: 'flex', gap: '30px', justifyContent: 'flex-end' }} |
||||
|
> |
||||
|
<Button type="submit" color="warning" variant="contained"> |
||||
|
Enregister |
||||
|
</Button> |
||||
|
</Grid> |
||||
|
</Grid> |
||||
|
</form> |
||||
|
</Paper> |
||||
|
</div> |
||||
|
</div> |
||||
|
) |
||||
|
} |
||||
|
|
||||
|
export default AddAnneeScolaire |
||||
@ -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 = () => ( |
||||
|
<Modal |
||||
|
open={open} |
||||
|
onClose={handleClose} |
||||
|
aria-labelledby="modal-title" |
||||
|
aria-describedby="modal-description" |
||||
|
> |
||||
|
<Box |
||||
|
sx={{ |
||||
|
position: 'absolute', |
||||
|
top: '50%', |
||||
|
left: '50%', |
||||
|
transform: 'translate(-50%, -50%)', |
||||
|
width: 450, |
||||
|
bgcolor: 'background.paper', |
||||
|
boxShadow: 24, |
||||
|
p: 4 |
||||
|
}} |
||||
|
> |
||||
|
{statut === 200 ? ( |
||||
|
<Typography |
||||
|
style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: '10px' }} |
||||
|
> |
||||
|
<img src={svgSuccess} alt="" width={70} height={70} />{' '} |
||||
|
<span> |
||||
|
Note de {etudiants.nom} {etudiants.prenom} en {etudiants.niveau} a été inserer avec |
||||
|
succès |
||||
|
</span> |
||||
|
</Typography> |
||||
|
) : ( |
||||
|
<Typography |
||||
|
style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: '10px' }} |
||||
|
> |
||||
|
<img src={svgError} alt="" width={70} height={70} />{' '} |
||||
|
<span>Inserer au moin un champ</span> |
||||
|
</Typography> |
||||
|
)} |
||||
|
<Box |
||||
|
sx={{ |
||||
|
marginTop: '2%', |
||||
|
display: 'flex', |
||||
|
gap: '20px', |
||||
|
alignItems: 'end', |
||||
|
justifyContent: 'flex-end' |
||||
|
}} |
||||
|
> |
||||
|
{statut == 200 ? ( |
||||
|
<Button onClick={handleClose2} color="warning" variant="contained"> |
||||
|
OK |
||||
|
</Button> |
||||
|
) : ( |
||||
|
<Button onClick={handleClose} color="warning" variant="contained"> |
||||
|
OK |
||||
|
</Button> |
||||
|
)} |
||||
|
</Box> |
||||
|
</Box> |
||||
|
</Modal> |
||||
|
) |
||||
|
|
||||
|
return ( |
||||
|
<div className={classe.mainHome}> |
||||
|
{modals()} |
||||
|
<ModalUpdateParcoursEtudiant |
||||
|
onClose={oncloseModal1} |
||||
|
open={openModal1} |
||||
|
user_id={etudiants.id} |
||||
|
onSubmit={handleFormSubmit} |
||||
|
/> |
||||
|
<div className={classeAdd.header}> |
||||
|
<div className={classe.h1style}> |
||||
|
<div className={classeHome.blockTitle}> |
||||
|
<h1 style={{ display: 'flex', alignItems: 'center', gap: '10px' }}> |
||||
|
<CgNotes /> |
||||
|
Ajout note |
||||
|
</h1> |
||||
|
<Link to={'/student'}> |
||||
|
<Button color="warning" variant="contained"> |
||||
|
<IoMdReturnRight style={{ fontSize: '20px' }} /> |
||||
|
</Button> |
||||
|
</Link> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
{/* displaying */} |
||||
|
<div className={classeHome.boxEtudiantsCard}> |
||||
|
<Box |
||||
|
sx={{ |
||||
|
position: 'absolute', |
||||
|
top: '55%', |
||||
|
left: '50%', |
||||
|
transform: 'translate(-50%, -50%)', |
||||
|
width: 700, |
||||
|
borderRadius: '2%', |
||||
|
bgcolor: 'background.paper', |
||||
|
boxShadow: 24, |
||||
|
p: 4 |
||||
|
}} |
||||
|
> |
||||
|
<Box |
||||
|
sx={{ |
||||
|
marginTop: '2%', |
||||
|
display: 'flex', |
||||
|
width: '100%', |
||||
|
gap: '20px', |
||||
|
alignItems: 'start', |
||||
|
justifyContent: 'center' |
||||
|
}} |
||||
|
> |
||||
|
<form onSubmit={handleSubmit} style={{ width: '100%' }}> |
||||
|
<h4 style={{ textAlign: 'center', marginBottom: '3%' }}> |
||||
|
Ajout des notes :{' '} |
||||
|
<span style={{ color: '#ff9800' }}> |
||||
|
{etudiants.nom} {etudiants.prenom} en {etudiants.niveau} |
||||
|
</span> |
||||
|
</h4> |
||||
|
|
||||
|
{/* map the all matiere to the form */} |
||||
|
<Grid container spacing={2}> |
||||
|
{matieres.map((mat) => ( |
||||
|
<Grid item xs={12} sm={3} key={mat.nom}> |
||||
|
<TextField |
||||
|
label={mat.nom} |
||||
|
name={mat.id} |
||||
|
placeholder="point séparateur" |
||||
|
color="warning" |
||||
|
fullWidth |
||||
|
value={formData[mat.id] || ''} // Access the correct value from formData |
||||
|
onChange={ |
||||
|
(e) => setFormData({ ...formData, [mat.id]: e.target.value }) // Update the specific key |
||||
|
} |
||||
|
InputProps={{ |
||||
|
startAdornment: ( |
||||
|
<InputAdornment position="start"> |
||||
|
<CgNotes /> |
||||
|
</InputAdornment> |
||||
|
) |
||||
|
}} |
||||
|
className="inputAddNote" |
||||
|
sx={{ |
||||
|
'& .MuiOutlinedInput-root': { |
||||
|
'&:hover fieldset': { |
||||
|
borderColor: '#ff9800' // Set the border color on hover |
||||
|
} |
||||
|
}, |
||||
|
'& .MuiInputBase-input::placeholder': { |
||||
|
fontSize: '11px' // Set the placeholder font size |
||||
|
} |
||||
|
}} |
||||
|
/> |
||||
|
</Grid> |
||||
|
))} |
||||
|
</Grid> |
||||
|
<Grid |
||||
|
item |
||||
|
xs={12} |
||||
|
style={{ |
||||
|
display: 'flex', |
||||
|
gap: '30px', |
||||
|
justifyContent: 'flex-end', |
||||
|
marginTop: '1%' |
||||
|
}} |
||||
|
> |
||||
|
<Button type="submit" color="warning" variant="contained" disabled={disabled}> |
||||
|
Enregister |
||||
|
</Button> |
||||
|
</Grid> |
||||
|
</form> |
||||
|
</Box> |
||||
|
</Box> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
) |
||||
|
} |
||||
|
|
||||
|
export default AddNotes |
||||
File diff suppressed because it is too large
@ -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) => ( |
||||
|
<div style={{ display: 'flex', gap: '10px' }}> |
||||
|
<Link to={`/anneescolaire/${params.value}`}> |
||||
|
<Button color="warning" variant="contained"> |
||||
|
<FaPenToSquare |
||||
|
style={{ fontSize: '20px', color: 'white' }} |
||||
|
className={`update${params.value}`} |
||||
|
/> |
||||
|
<Tooltip |
||||
|
anchorSelect={`.update${params.value}`} |
||||
|
style={{ fontSize: '15px', zIndex: 22 }} |
||||
|
place="top" |
||||
|
> |
||||
|
Modifier |
||||
|
</Tooltip> |
||||
|
</Button> |
||||
|
</Link> |
||||
|
<Link |
||||
|
to={`#`} |
||||
|
onClick={() => { |
||||
|
setIds(params.row.id) |
||||
|
setOpen(true) |
||||
|
}} |
||||
|
> |
||||
|
<Button color="error" variant="contained"> |
||||
|
<FaTrash |
||||
|
style={{ fontSize: '20px', color: 'white' }} |
||||
|
className={`delete${params.value}`} |
||||
|
/> |
||||
|
<Tooltip |
||||
|
anchorSelect={`.delete${params.value}`} |
||||
|
style={{ fontSize: '15px', zIndex: 22 }} |
||||
|
place="top" |
||||
|
> |
||||
|
Supprimer |
||||
|
</Tooltip> |
||||
|
</Button> |
||||
|
</Link> |
||||
|
</div> |
||||
|
) |
||||
|
}, |
||||
|
{ |
||||
|
field: 'current', |
||||
|
headerName: 'Année en cours', |
||||
|
flex: 1, |
||||
|
renderCell: (params) => ( |
||||
|
<div style={{ display: 'flex', gap: '10px' }}> |
||||
|
<Link to={`#`}> |
||||
|
<Button color="success" variant="contained"> |
||||
|
{params.value.current == 1 ? ( |
||||
|
<FaCheck |
||||
|
style={{ fontSize: '20px', color: 'white' }} |
||||
|
className={`courrants${params.value.id}`} |
||||
|
/> |
||||
|
) : ( |
||||
|
<FaSquare |
||||
|
style={{ fontSize: '20px', color: 'white' }} |
||||
|
className={`courrant${params.value.id}`} |
||||
|
onClick={() => setCurrent(params.value.id)} |
||||
|
/> |
||||
|
)} |
||||
|
</Button> |
||||
|
<Tooltip |
||||
|
anchorSelect={`.courrants${params.value.id}`} |
||||
|
style={{ fontSize: '15px', zIndex: 22 }} |
||||
|
place="top" |
||||
|
> |
||||
|
Année en cours |
||||
|
</Tooltip> |
||||
|
</Link> |
||||
|
</div> |
||||
|
) |
||||
|
} |
||||
|
] |
||||
|
|
||||
|
const paginationModel = { page: 0, pageSize: 5 } |
||||
|
|
||||
|
let data = anneeScolaire.map((annee) => ({ |
||||
|
id: annee.id, |
||||
|
code: annee.code, |
||||
|
debut: dayjs(annee.debut).format('DD-MM-YYYY'), |
||||
|
fin: dayjs(annee.fin).format('DD-MM-YYYY'), |
||||
|
action: annee.id, |
||||
|
current: { current: annee.is_current, id: annee.id } |
||||
|
})) |
||||
|
|
||||
|
const setCurrent = async (id) => { |
||||
|
// let response = await window.anneescolaire.setCurrent({id}); |
||||
|
// console.log(response); |
||||
|
// if (response.changes) { |
||||
|
// window.anneescolaire.getAnneeScolaire().then((response) => { |
||||
|
// setAnneeScolaire(response); |
||||
|
// }); |
||||
|
// } |
||||
|
} |
||||
|
|
||||
|
const deleteButton = async (id) => { |
||||
|
let response = await window.anneescolaire.deleteAnneeScolaire({ id }) |
||||
|
if (response.success) { |
||||
|
const updatedAnneeScolaire = anneeScolaire.filter((anneeScolaire) => anneeScolaire.id !== id) |
||||
|
setAnneeScolaire(updatedAnneeScolaire) |
||||
|
setIsDeleted(true) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
const CustomToolbar = () => { |
||||
|
return ( |
||||
|
<div> |
||||
|
<GridToolbarFilterButton /> |
||||
|
</div> |
||||
|
) |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* hook to open modal |
||||
|
*/ |
||||
|
const [open, setOpen] = useState(false) |
||||
|
|
||||
|
/** |
||||
|
* function to close modal |
||||
|
*/ |
||||
|
const handleClose = () => { |
||||
|
setOpen(false) |
||||
|
setIsDeleted(false) |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* function to return the view Modal |
||||
|
* |
||||
|
* @returns {JSX} |
||||
|
*/ |
||||
|
const modals = () => ( |
||||
|
<Modal |
||||
|
open={open} |
||||
|
onClose={handleClose} |
||||
|
aria-labelledby="modal-title" |
||||
|
aria-describedby="modal-description" |
||||
|
> |
||||
|
<Box |
||||
|
sx={{ |
||||
|
position: 'absolute', |
||||
|
top: '50%', |
||||
|
left: '50%', |
||||
|
transform: 'translate(-50%, -50%)', |
||||
|
width: 450, |
||||
|
bgcolor: 'background.paper', |
||||
|
boxShadow: 24, |
||||
|
p: 4 |
||||
|
}} |
||||
|
> |
||||
|
{isDeleted ? ( |
||||
|
<Typography |
||||
|
style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: '10px' }} |
||||
|
> |
||||
|
<img src={success} alt="" width={50} height={50} /> <span>Suprimer avec succèss</span> |
||||
|
</Typography> |
||||
|
) : ( |
||||
|
<Typography |
||||
|
style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: '10px' }} |
||||
|
> |
||||
|
<img src={warning} alt="" width={50} height={50} />{' '} |
||||
|
<span>Voulez vous supprimer cette année ?</span> |
||||
|
</Typography> |
||||
|
)} |
||||
|
<Box |
||||
|
sx={{ |
||||
|
marginTop: '2%', |
||||
|
display: 'flex', |
||||
|
gap: '20px', |
||||
|
alignItems: 'end', |
||||
|
justifyContent: 'flex-end' |
||||
|
}} |
||||
|
> |
||||
|
{isDeleted ? ( |
||||
|
<Button onClick={handleClose} color="warning" variant="contained"> |
||||
|
OK |
||||
|
</Button> |
||||
|
) : ( |
||||
|
<div> |
||||
|
<Button |
||||
|
onClick={() => deleteButton(ids)} |
||||
|
sx={{ mr: 1 }} |
||||
|
color="warning" |
||||
|
variant="contained" |
||||
|
> |
||||
|
Oui |
||||
|
</Button> |
||||
|
<Button onClick={handleClose} color="warning" variant="contained"> |
||||
|
Non |
||||
|
</Button> |
||||
|
</div> |
||||
|
)} |
||||
|
</Box> |
||||
|
</Box> |
||||
|
</Modal> |
||||
|
) |
||||
|
|
||||
|
return ( |
||||
|
<div className={classe.mainHome}> |
||||
|
{modals()} |
||||
|
<div className={classeHome.header}> |
||||
|
<div className={classe.h1style}> |
||||
|
<div className={classeHome.blockTitle}> |
||||
|
<h1 style={{ display: 'flex', alignItems: 'center', gap: '10px' }}> |
||||
|
<BsCalendar2Date /> Année Scolaire |
||||
|
</h1> |
||||
|
<Link to={'/addanneescolaire'}> |
||||
|
<Button color="warning" variant="contained"> |
||||
|
<FaCalendarPlus style={{ fontSize: '20px' }} /> Ajouter |
||||
|
</Button> |
||||
|
</Link> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<div className={classeHome.boxEtudiantsCard}> |
||||
|
<Paper |
||||
|
sx={{ |
||||
|
width: '100%', |
||||
|
height: '100%', // Auto height to make the grid responsive |
||||
|
minHeight: 500, // Ensures a minimum height |
||||
|
display: 'flex', |
||||
|
flexDirection: 'column' |
||||
|
}} |
||||
|
> |
||||
|
<ThemeProvider theme={theme}> |
||||
|
<div |
||||
|
style={{ |
||||
|
display: 'flex', |
||||
|
flexDirection: 'column', // Stacks content vertically on smaller screens, |
||||
|
width: '100%' |
||||
|
}} |
||||
|
> |
||||
|
<DataGrid |
||||
|
rows={data} |
||||
|
columns={column} |
||||
|
initialState={{ pagination: { paginationModel } }} |
||||
|
pageSizeOptions={[5, 10]} |
||||
|
sx={{ |
||||
|
border: 0, |
||||
|
width: 'auto', // Ensures the DataGrid takes full width |
||||
|
height: '50%', // Ensures it grows to fit content |
||||
|
minHeight: 400, // Minimum height for the DataGrid |
||||
|
display: 'flex', |
||||
|
justifyContent: 'center', |
||||
|
'@media (max-width: 600px)': { |
||||
|
width: '100%', // 100% width on small screens |
||||
|
height: 'auto' // Allow height to grow with content |
||||
|
} |
||||
|
}} |
||||
|
slots={{ toolbar: CustomToolbar }} |
||||
|
localeText={frFR.components.MuiDataGrid.defaultProps.localeText} |
||||
|
/> |
||||
|
</div> |
||||
|
</ThemeProvider> |
||||
|
</Paper> |
||||
|
</div> |
||||
|
</div> |
||||
|
) |
||||
|
} |
||||
|
|
||||
|
export default AnneeScolaire |
||||
@ -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 ( |
||||
|
<div className={classe.mainHome}> |
||||
|
<div className={classeHome.header}> |
||||
|
<div className={classe.h1style}> |
||||
|
<div className={classeHome.blockTitle}> |
||||
|
<h1>A propos</h1> |
||||
|
|
||||
|
{/* contenu */} |
||||
|
<div className={classeHome.contenaire}> |
||||
|
<div className={classeAdd.boxEtudiantsCard}> |
||||
|
<Box |
||||
|
sx={{ |
||||
|
position: 'absolute', |
||||
|
top: '50%', |
||||
|
left: '50%', |
||||
|
transform: 'translate(-50%, -50%)', |
||||
|
width: 700, |
||||
|
borderRadius: '3%', |
||||
|
bgcolor: 'background.paper', |
||||
|
boxShadow: 24, |
||||
|
p: 4 |
||||
|
}} |
||||
|
> |
||||
|
<Box |
||||
|
sx={{ |
||||
|
marginTop: '5%', |
||||
|
display: 'flex', |
||||
|
gap: '10px', |
||||
|
alignItems: 'start', |
||||
|
flexDirection: 'column', |
||||
|
fontSize: 16, |
||||
|
fontFamily: 'sans-serif', |
||||
|
justifyContent: 'flex-end' |
||||
|
}} |
||||
|
> |
||||
|
<span style={{ display: 'flex', marginLeft: '30%' }}> |
||||
|
<img src={logo} alt="" height={250} width={250} /> |
||||
|
</span> |
||||
|
|
||||
|
<p style={{ color: 'black' }}> |
||||
|
Nom du Logiciel: CUniversity <br /> |
||||
|
<br /> Description : logiciel de gestion d'espt <br /> <br /> |
||||
|
Createur: CPAY COMPANY FOR MADAGASCAR <br /> |
||||
|
<br /> Licence: A vie <br /> |
||||
|
<br /> Contact: 0348415301 |
||||
|
<br /> <br /> |
||||
|
E-mail: director@c4m.mg |
||||
|
</p> |
||||
|
</Box> |
||||
|
</Box> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
) |
||||
|
} |
||||
|
|
||||
|
export default Apropos |
||||
@ -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 = () => ( |
||||
|
<Modal |
||||
|
open={open} |
||||
|
onClose={handleClose} |
||||
|
aria-labelledby="modal-title" |
||||
|
aria-describedby="modal-description" |
||||
|
> |
||||
|
<Box |
||||
|
sx={{ |
||||
|
position: 'absolute', |
||||
|
top: '50%', |
||||
|
left: '50%', |
||||
|
transform: 'translate(-50%, -50%)', |
||||
|
width: 450, |
||||
|
bgcolor: 'background.paper', |
||||
|
boxShadow: 24, |
||||
|
p: 4 |
||||
|
}} |
||||
|
> |
||||
|
{isInserted ? ( |
||||
|
<Typography |
||||
|
style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: '10px' }} |
||||
|
> |
||||
|
<img src={svgSuccess} alt="" width={50} height={50} />{' '} |
||||
|
<span>Importation a été effectuée avec succès</span> |
||||
|
</Typography> |
||||
|
) : ( |
||||
|
<Typography |
||||
|
style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: '10px' }} |
||||
|
> |
||||
|
<img src={svgError} alt="" width={50} height={50} />{' '} |
||||
|
<span> |
||||
|
L'importation n'a pas été effectuée |
||||
|
<br /> |
||||
|
{message} |
||||
|
</span> |
||||
|
</Typography> |
||||
|
)} |
||||
|
<Box |
||||
|
sx={{ |
||||
|
marginTop: '2%', |
||||
|
display: 'flex', |
||||
|
gap: '20px', |
||||
|
alignItems: 'end', |
||||
|
justifyContent: 'flex-end' |
||||
|
}} |
||||
|
> |
||||
|
<Button onClick={handleClose} color="warning" variant="contained"> |
||||
|
OK |
||||
|
</Button> |
||||
|
</Box> |
||||
|
</Box> |
||||
|
</Modal> |
||||
|
) |
||||
|
|
||||
|
const exemplaireFileExcel = [ |
||||
|
{ |
||||
|
nom: 'nom2', |
||||
|
prenom: 'prenom2', |
||||
|
niveau: 'L1', |
||||
|
date_naissance: 'jj/mm/AAAA', |
||||
|
annee_scolaire: '2024-2025', |
||||
|
mention: 'INFO', |
||||
|
num_inscription: 'azertyuiop', |
||||
|
sexe: 'F', |
||||
|
cin: '1234567890987', |
||||
|
date_de_delivrance: 'JJ/MM/AAAA', |
||||
|
nationaliter: 'Malagasy', |
||||
|
annee_baccalaureat: 'AAAA', |
||||
|
serie: 'D', |
||||
|
code_redoublement: 'N si nouveau, P si passant, R si redoublant RM si renvoyer, A si ancient', |
||||
|
boursier: 'Non', |
||||
|
domaine: "(S) Science de l'ingénieur", |
||||
|
contact: '0387205654' |
||||
|
}, |
||||
|
{ |
||||
|
nom: 'nom1', |
||||
|
prenom: 'prenom2', |
||||
|
niveau: 'L2', |
||||
|
date_naissance: 'jj/mm/AAAA', |
||||
|
annee_scolaire: '2024-2025', |
||||
|
mention: 'Informatique', |
||||
|
num_inscription: 'azertyuiop', |
||||
|
sexe: 'M', |
||||
|
cin: '1234567890987', |
||||
|
date_de_delivrance: 'JJ/MM/AAAA', |
||||
|
nationaliter: 'Malagasy', |
||||
|
annee_baccalaureat: 'AAAA', |
||||
|
serie: 'D', |
||||
|
code_redoublement: 'N si nouveau, P si passant, R si redoublant RM si renvoyer, A si ancient', |
||||
|
boursier: 'Non', |
||||
|
domaine: "(S) Science de l'ingénieur", |
||||
|
contact: '0387205654' |
||||
|
} |
||||
|
] |
||||
|
|
||||
|
const convertToExcel = () => { |
||||
|
// convert json to sheet |
||||
|
const worksheet = XLSX.utils.json_to_sheet(exemplaireFileExcel) |
||||
|
|
||||
|
// Create a new workbook and append the worksheet |
||||
|
const workbook = XLSX.utils.book_new() |
||||
|
XLSX.utils.book_append_sheet(workbook, worksheet, 'Sheet1') |
||||
|
|
||||
|
// Write the workbook to a Blob and create a download link |
||||
|
const excelBuffer = XLSX.write(workbook, { bookType: 'xlsx', type: 'array' }) |
||||
|
const data = new Blob([excelBuffer], { type: 'application/octet-stream' }) |
||||
|
const url = URL.createObjectURL(data) |
||||
|
|
||||
|
// Trigger a download |
||||
|
const link = document.createElement('a') |
||||
|
link.href = url |
||||
|
link.download = 'exemplaire_etudiant.xlsx' |
||||
|
link.click() |
||||
|
|
||||
|
// Clean up |
||||
|
URL.revokeObjectURL(url) |
||||
|
} |
||||
|
|
||||
|
const handleColumnVisibilityChange = (model) => { |
||||
|
// Get the currently visible columns |
||||
|
const visibleColumns = dynamicColumns.filter((column) => model[column.field] !== false) |
||||
|
|
||||
|
// Create a new Excel file with visible columns |
||||
|
createExcelFile(visibleColumns) |
||||
|
} |
||||
|
|
||||
|
const createExcelFile = (columns) => { |
||||
|
// Extract and set the header |
||||
|
const header = columns.reduce((acc, col) => { |
||||
|
acc[col.field] = col.headerName || col.field // Use headerName or field as default |
||||
|
return acc |
||||
|
}, {}) |
||||
|
|
||||
|
// Map the data rows to match the extracted headers |
||||
|
const worksheetData = dataRow.map((row) => { |
||||
|
const filteredRow = {} |
||||
|
columns.forEach((col) => { |
||||
|
const headerName = header[col.field] |
||||
|
filteredRow[headerName] = row[col.field] |
||||
|
}) |
||||
|
return filteredRow |
||||
|
}) |
||||
|
|
||||
|
// Create a worksheet from the data |
||||
|
const ws = XLSX.utils.json_to_sheet(worksheetData) |
||||
|
|
||||
|
// Create a workbook and add the worksheet to it |
||||
|
const wb = XLSX.utils.book_new() |
||||
|
XLSX.utils.book_append_sheet(wb, ws, 'Sheet1') |
||||
|
|
||||
|
// Generate the Excel file as binary data |
||||
|
const excelFile = XLSX.write(wb, { bookType: 'xlsx', type: 'array' }) |
||||
|
|
||||
|
// Create a Blob for the Excel data |
||||
|
const blob = new Blob([excelFile], { |
||||
|
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' |
||||
|
}) |
||||
|
|
||||
|
// Create a link element to trigger the download |
||||
|
const url = URL.createObjectURL(blob) |
||||
|
const link = document.createElement('a') |
||||
|
link.href = url |
||||
|
link.download = 'original-file.xlsx' // Original file name or any desired name |
||||
|
document.body.appendChild(link) |
||||
|
link.click() |
||||
|
document.body.removeChild(link) |
||||
|
URL.revokeObjectURL(url) // Clean up |
||||
|
} |
||||
|
|
||||
|
// Handle header name change for a specific field and auto-export to Excel |
||||
|
const handleHeaderChange = (field, newHeaderName) => { |
||||
|
setColumns((prevColumns) => |
||||
|
prevColumns.map((col) => (col.field === field ? { ...col, headerName: newHeaderName } : col)) |
||||
|
) |
||||
|
} |
||||
|
|
||||
|
return ( |
||||
|
<div className={classe.mainHome}> |
||||
|
{modals()} |
||||
|
<div className={classeAdd.header}> |
||||
|
<div className={classe.h1style}> |
||||
|
<div className={classeHome.blockTitle}> |
||||
|
<h1 style={{ display: 'flex', alignItems: 'center', gap: '10px' }}> |
||||
|
<FaFileExcel /> Export |
||||
|
</h1> |
||||
|
<Link to={'/addstudent'}> |
||||
|
<Button color="warning" variant="contained"> |
||||
|
<IoMdReturnRight style={{ fontSize: '20px' }} /> |
||||
|
</Button> |
||||
|
</Link> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<Box |
||||
|
sx={{ |
||||
|
// position: 'absolute', |
||||
|
// top: '52%', |
||||
|
// left: '52%', |
||||
|
// transform: 'translate(-50%, -50%)', |
||||
|
width: '99.5%', |
||||
|
borderRadius: '2%', |
||||
|
bgcolor: 'background.paper', |
||||
|
boxShadow: 24, |
||||
|
p: 4 |
||||
|
}} |
||||
|
> |
||||
|
<Box sx={{ mt: 2, display: 'flex', alignItems: 'center', flexDirection: 'column' }}> |
||||
|
<div style={{ display: 'flex', gap: '20px', marginBottom: '20px' }}> |
||||
|
<Button |
||||
|
component="label" |
||||
|
variant="contained" |
||||
|
color="warning" |
||||
|
startIcon={<FaCloudUploadAlt />} |
||||
|
> |
||||
|
Charger un fichier excel |
||||
|
<VisuallyHiddenInput type="file" onChange={handleFileChange} accept=".xlsx, .csv" /> |
||||
|
</Button> |
||||
|
<Button |
||||
|
component="label" |
||||
|
variant="contained" |
||||
|
color="error" |
||||
|
startIcon={<FaCloudDownloadAlt />} |
||||
|
onClick={convertToExcel} |
||||
|
> |
||||
|
Télécharger le modèle d'import |
||||
|
</Button> |
||||
|
</div> |
||||
|
{error && ( |
||||
|
<Typography color="error" variant="body2"> |
||||
|
{error} |
||||
|
</Typography> |
||||
|
)} |
||||
|
{tableData.length > 0 && ( |
||||
|
<ThemeProvider theme={theme}> |
||||
|
<div |
||||
|
style={{ |
||||
|
display: 'flex', |
||||
|
alignItems: 'center', |
||||
|
justifyContent: 'space-between', |
||||
|
flexWrap: 'wrap' |
||||
|
}} |
||||
|
> |
||||
|
{/* Dropdowns for each column */} |
||||
|
{dynamicColumns.map((col) => ( |
||||
|
<FormControl |
||||
|
key={col.field} |
||||
|
sx={{ |
||||
|
m: 1, |
||||
|
width: '100%', |
||||
|
'& .MuiOutlinedInput-root': { |
||||
|
'&:hover fieldset': { |
||||
|
borderColor: '#ff9800' // Set the border color on hover |
||||
|
} |
||||
|
} |
||||
|
}} |
||||
|
size="small" |
||||
|
color="warning" |
||||
|
variant="outlined" |
||||
|
style={{ margin: 10, width: 200 }} |
||||
|
> |
||||
|
<InputLabel id={col.headerName}>{col.headerName}</InputLabel> |
||||
|
<Select |
||||
|
labelId={col.headerName} |
||||
|
value={col.headerName} |
||||
|
label={col.headerName} |
||||
|
color="warning" |
||||
|
name={col.headerName} |
||||
|
onChange={(e) => handleHeaderChange(col.field, e.target.value)} |
||||
|
> |
||||
|
<MenuItem value={col.headerName}>{col.headerName}</MenuItem> |
||||
|
<MenuItem value={'nom'}>nom</MenuItem> |
||||
|
<MenuItem value={'prenom'}>prenom</MenuItem> |
||||
|
<MenuItem value={`niveau`}>niveau</MenuItem> |
||||
|
<MenuItem value={`date_naissance`}>date de naissance</MenuItem> |
||||
|
<MenuItem value={`annee_scolaire`}>année scolaire</MenuItem> |
||||
|
<MenuItem value={`mention`}>mention</MenuItem> |
||||
|
<MenuItem value={`num_inscription`}>numéro d'inscription</MenuItem> |
||||
|
<MenuItem value={`nationalite`}>Nationaliter</MenuItem> |
||||
|
<MenuItem value={`sexe`}>Sexe</MenuItem> |
||||
|
<MenuItem value={`cin`}>CIN</MenuItem> |
||||
|
<MenuItem value={`date_de_livraison`}>Date de livraison</MenuItem> |
||||
|
<MenuItem value={`annee_baccalaureat`}>Année du baccalaureat</MenuItem> |
||||
|
<MenuItem value={`serie`}>Série</MenuItem> |
||||
|
<MenuItem value={`code_redoublement`}>Code redoublement</MenuItem> |
||||
|
<MenuItem value={`boursier`}>Boursier</MenuItem> |
||||
|
<MenuItem value={`domaine`}>Domaine</MenuItem> |
||||
|
<MenuItem value={`contact`}>Contact</MenuItem> |
||||
|
<MenuItem value={`parcours`}>Parcours</MenuItem> |
||||
|
</Select> |
||||
|
</FormControl> |
||||
|
))} |
||||
|
</div> |
||||
|
<div |
||||
|
style={{ |
||||
|
display: 'flex', |
||||
|
alignItems: 'center', |
||||
|
justifyContent: 'center', |
||||
|
flexDirection: 'column', |
||||
|
width: '100%' |
||||
|
}} |
||||
|
> |
||||
|
<DataGrid |
||||
|
rows={dataRow} |
||||
|
columns={dynamicColumns} |
||||
|
initialState={{ pagination: { paginationModel } }} |
||||
|
onColumnVisibilityModelChange={handleColumnVisibilityChange} // Listen for column visibility changes |
||||
|
pageSizeOptions={[5, 10]} |
||||
|
disableRowSelectionOnClick |
||||
|
sx={{ |
||||
|
width: '100%', |
||||
|
height: '100%', |
||||
|
minHeight: 400, |
||||
|
maxWidth: '100%', // Prevents grid from exceeding the container width |
||||
|
'@media (max-width: 600px)': { |
||||
|
width: '100%', |
||||
|
height: 'auto', // Adjust height for smaller screens |
||||
|
fontSize: '0.8rem' // Smaller font size for better readability on small screens |
||||
|
}, |
||||
|
'@media (max-width: 960px)': { |
||||
|
fontSize: '1rem' // Adjust font size for medium screens |
||||
|
} |
||||
|
}} |
||||
|
slots={{ toolbar: CustomBar }} |
||||
|
slotProps={{ toolbar: { onImport: handleImport } }} |
||||
|
localeText={frFR.components.MuiDataGrid.defaultProps.localeText} |
||||
|
/> |
||||
|
</div> |
||||
|
</ThemeProvider> |
||||
|
)} |
||||
|
</Box> |
||||
|
</Box> |
||||
|
</div> |
||||
|
) |
||||
|
} |
||||
|
|
||||
|
export default ExportEtudiants |
||||
@ -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) => ( |
||||
|
<div style={{ display: 'flex', gap: '10px' }}> |
||||
|
<Link to={`/singlematiere/${params.value}`}> |
||||
|
<Button color="warning" variant="contained"> |
||||
|
<FaPenToSquare |
||||
|
style={{ fontSize: '20px', color: 'white' }} |
||||
|
className={`update${params.value}`} |
||||
|
/> |
||||
|
<Tooltip |
||||
|
anchorSelect={`.update${params.value}`} |
||||
|
style={{ fontSize: '15px', zIndex: 22 }} |
||||
|
place="top" |
||||
|
> |
||||
|
Modifier |
||||
|
</Tooltip> |
||||
|
</Button> |
||||
|
</Link> |
||||
|
<Link to={`/asignmatiere/${params.value}`}> |
||||
|
<Button color="warning" variant="contained"> |
||||
|
<FaM |
||||
|
className={`asignMention${params.value}`} |
||||
|
style={{ fontSize: '20px', color: 'white' }} |
||||
|
/> |
||||
|
<Tooltip |
||||
|
anchorSelect={`.asignMention${params.value}`} |
||||
|
style={{ fontSize: '15px', zIndex: 9 }} |
||||
|
place="top" |
||||
|
> |
||||
|
Assigner a un mension |
||||
|
</Tooltip> |
||||
|
</Button> |
||||
|
</Link> |
||||
|
<Link |
||||
|
to={`/asignmatieresemestre/${params.value}`} |
||||
|
className={`sem${params.value}`} |
||||
|
style={{ textDecoration: 'none' }} |
||||
|
> |
||||
|
<Tooltip |
||||
|
anchorSelect={`.sem${params.value}`} |
||||
|
style={{ fontSize: '15px', zIndex: 9 }} |
||||
|
place="top" |
||||
|
> |
||||
|
Assigner à un semestre |
||||
|
</Tooltip> |
||||
|
<Button color="warning" variant="contained"> |
||||
|
<FaS style={{ fontSize: '20px', color: 'white' }} /> |
||||
|
</Button> |
||||
|
</Link> |
||||
|
|
||||
|
{/* ----------- BOUTON ASSIGNER NIVEAU ----------- */} |
||||
|
<Link |
||||
|
to="#" |
||||
|
className={`niveau${params.value}`} |
||||
|
style={{ textDecoration: 'none' }} |
||||
|
onClick={(e) => { |
||||
|
e.preventDefault(); // empêche le # de provoquer un scroll |
||||
|
openAssignNiveauModal(params.value); |
||||
|
}} |
||||
|
> |
||||
|
<Tooltip |
||||
|
anchorSelect={`.niveau${params.value}`} |
||||
|
style={{ fontSize: '15px', zIndex: 9 }} |
||||
|
place="top" |
||||
|
> |
||||
|
Assigner à un niveau |
||||
|
</Tooltip> |
||||
|
<Button color="warning" variant="contained"> |
||||
|
{/* Icône de ton choix, ici FaRegPlusSquare par exemple */} |
||||
|
<FaRegPlusSquare style={{ fontSize: '20px', color: 'white' }} /> |
||||
|
</Button> |
||||
|
</Link> |
||||
|
|
||||
|
<Link |
||||
|
to="#" |
||||
|
style={{ textDecoration: 'none' }} |
||||
|
className={`parcour${params.value}`} |
||||
|
onClick={() => openParcoursFunction(params.value)} |
||||
|
> |
||||
|
<Tooltip |
||||
|
anchorSelect={`.parcour${params.value}`} |
||||
|
style={{ fontSize: '15px', zIndex: 9 }} |
||||
|
place="top" |
||||
|
> |
||||
|
Assigner à des parcours |
||||
|
</Tooltip> |
||||
|
<Button color="warning" variant="contained"> |
||||
|
<FaP style={{ fontSize: '20px', color: 'white' }} /> |
||||
|
</Button> |
||||
|
</Link> |
||||
|
|
||||
|
<Link to={`#`}> |
||||
|
<Button color="warning" variant="contained"> |
||||
|
<GiTeacher |
||||
|
style={{ fontSize: '20px', color: 'white' }} |
||||
|
className={`asignProf${params.value}`} |
||||
|
/> |
||||
|
<Tooltip |
||||
|
anchorSelect={`.asignProf${params.value}`} |
||||
|
style={{ fontSize: '15px', zIndex: 22 }} |
||||
|
place="top" |
||||
|
clickable |
||||
|
> |
||||
|
<Link style={{ zIndex: 99, display: 'flex', alignItems: 'center', gap: '10px' }}> |
||||
|
<Button |
||||
|
color="warning" |
||||
|
variant="contained" |
||||
|
onClick={() => openFormModal(params.value)} |
||||
|
> |
||||
|
<FaPlus |
||||
|
style={{ fontSize: '20px', color: 'white' }} |
||||
|
className={`adprofModal${params.value}`} |
||||
|
/> |
||||
|
<Tooltip |
||||
|
anchorSelect={`.adprofModal${params.value}`} |
||||
|
style={{ fontSize: '15px', zIndex: 9 }} |
||||
|
place="top" |
||||
|
> |
||||
|
Ajouter un nouveau professeur |
||||
|
</Tooltip> |
||||
|
</Button> |
||||
|
<Button |
||||
|
color="warning" |
||||
|
variant="contained" |
||||
|
onClick={() => openUppdateProfFunction(params.value)} |
||||
|
> |
||||
|
<FaPenToSquare |
||||
|
style={{ fontSize: '20px', color: 'white' }} |
||||
|
className={`updateprofModal${params.value}`} |
||||
|
/> |
||||
|
<Tooltip |
||||
|
anchorSelect={`.updateprofModal${params.value}`} |
||||
|
style={{ fontSize: '15px', zIndex: 9 }} |
||||
|
place="top" |
||||
|
> |
||||
|
Modifier un professeur |
||||
|
</Tooltip> |
||||
|
</Button> |
||||
|
</Link> |
||||
|
</Tooltip> |
||||
|
</Button> |
||||
|
</Link> |
||||
|
<Link to={`/fiche/${params.value}/${params.row.nom}`}> |
||||
|
<Tooltip |
||||
|
anchorSelect={`.presence${params.value}`} |
||||
|
style={{ fontSize: '15px', zIndex: 22 }} |
||||
|
place="top" |
||||
|
> |
||||
|
Fiche de presence éxamen |
||||
|
</Tooltip> |
||||
|
<Button color="warning" variant="contained"> |
||||
|
<FaNewspaper |
||||
|
style={{ fontSize: '20px', color: 'white' }} |
||||
|
className={`presence${params.value}`} |
||||
|
/> |
||||
|
</Button> |
||||
|
</Link> |
||||
|
<Link |
||||
|
to={`#`} |
||||
|
onClick={() => { |
||||
|
setIds(params.row.id) |
||||
|
setOpen(true) |
||||
|
}} |
||||
|
> |
||||
|
<Button color="error" variant="contained"> |
||||
|
<FaTrash |
||||
|
style={{ fontSize: '20px', color: 'white' }} |
||||
|
className={`supprimer${params.value}`} |
||||
|
/> |
||||
|
<Tooltip |
||||
|
anchorSelect={`.supprimer${params.value}`} |
||||
|
style={{ fontSize: '15px', zIndex: 22 }} |
||||
|
place="top" |
||||
|
> |
||||
|
Supprimer |
||||
|
</Tooltip> |
||||
|
</Button> |
||||
|
</Link> |
||||
|
</div> |
||||
|
) |
||||
|
} |
||||
|
] |
||||
|
|
||||
|
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 = () => ( |
||||
|
<Modal |
||||
|
open={open} |
||||
|
onClose={handleClose} |
||||
|
aria-labelledby="modal-title" |
||||
|
aria-describedby="modal-description" |
||||
|
> |
||||
|
<Box |
||||
|
sx={{ |
||||
|
position: 'absolute', |
||||
|
top: '50%', |
||||
|
left: '50%', |
||||
|
transform: 'translate(-50%, -50%)', |
||||
|
width: 450, |
||||
|
bgcolor: 'background.paper', |
||||
|
boxShadow: 24, |
||||
|
p: 4 |
||||
|
}} |
||||
|
> |
||||
|
{isDeleted ? ( |
||||
|
<Typography |
||||
|
style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: '10px' }} |
||||
|
> |
||||
|
<img src={success} alt="" width={50} height={50} /> <span>Suprimer avec succèss</span> |
||||
|
</Typography> |
||||
|
) : ( |
||||
|
<Typography |
||||
|
style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: '10px' }} |
||||
|
> |
||||
|
<img src={warning} alt="" width={50} height={50} />{' '} |
||||
|
<span>Voulez vous supprimer ce matiere ?</span> |
||||
|
</Typography> |
||||
|
)} |
||||
|
<Box |
||||
|
sx={{ |
||||
|
marginTop: '2%', |
||||
|
display: 'flex', |
||||
|
gap: '20px', |
||||
|
alignItems: 'end', |
||||
|
justifyContent: 'flex-end' |
||||
|
}} |
||||
|
> |
||||
|
{isDeleted ? ( |
||||
|
<Button onClick={handleClose} color="warning" variant="contained"> |
||||
|
OK |
||||
|
</Button> |
||||
|
) : ( |
||||
|
<div> |
||||
|
<Button |
||||
|
onClick={() => deleteButton(ids)} |
||||
|
sx={{ mr: 1 }} |
||||
|
color="warning" |
||||
|
variant="contained" |
||||
|
> |
||||
|
Oui |
||||
|
</Button> |
||||
|
<Button onClick={handleClose} color="warning" variant="contained"> |
||||
|
Non |
||||
|
</Button> |
||||
|
</div> |
||||
|
)} |
||||
|
</Box> |
||||
|
</Box> |
||||
|
</Modal> |
||||
|
) |
||||
|
|
||||
|
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 ( |
||||
|
<div className={classe.mainHome}> |
||||
|
{modals()} |
||||
|
<ModalAddProf |
||||
|
open={openForm} |
||||
|
onClose={closeForm} |
||||
|
matiere_id={matiereId} |
||||
|
onSubmitSuccess={handleFormSubmit} |
||||
|
/> |
||||
|
<ParcourMatiere matiere_id={idToSend} onClose={onCloseParcours} open={openParcours} /> |
||||
|
<UpdateModalProf |
||||
|
matiere_id={idSends} |
||||
|
onClose={onCloseUpdateProf} |
||||
|
onSubmitSuccess={handleFormSubmit} |
||||
|
open={openUppdateProf} |
||||
|
/> |
||||
|
<ModalProcessFichePresence matiere_id={matiereId} onClose={onCloseFiche} open={openFiche} /> |
||||
|
|
||||
|
{/* Modal NiveauMatiere */} |
||||
|
<NiveauMatiere |
||||
|
open={openAssignNiveau} |
||||
|
onClose={closeAssignNiveauModal} |
||||
|
matiere_id={matiereIdAssign} |
||||
|
onSubmitSuccess={handleNiveauAssignSuccess} |
||||
|
/> |
||||
|
|
||||
|
<div className={classeAdd.header}> |
||||
|
<div className={classe.h1style}> |
||||
|
<div className={classeHome.blockTitle}> |
||||
|
<h1 style={{ display: 'flex', alignItems: 'center', gap: '10px' }}> |
||||
|
<FaBook /> |
||||
|
Matiere |
||||
|
</h1> |
||||
|
<Link to={'/addmatiere'}> |
||||
|
<Button color="warning" variant="contained"> |
||||
|
<BsBookmarkPlusFill style={{ fontSize: '20px' }} /> AJouter |
||||
|
</Button> |
||||
|
</Link> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
{/* displaying data */} |
||||
|
<div className={classeHome.boxEtudiantsCard}> |
||||
|
<div style={{ display: 'flex', justifyContent: 'center' }}> |
||||
|
<Paper |
||||
|
sx={{ |
||||
|
height: 'auto', // Auto height to make the grid responsive |
||||
|
minHeight: 500, // Ensures a minimum height |
||||
|
display: 'flex', |
||||
|
// alignItems: "center", |
||||
|
// justifyContent: "center", |
||||
|
width: '100%' |
||||
|
}} |
||||
|
> |
||||
|
<ThemeProvider theme={theme}> |
||||
|
<div |
||||
|
style={{ |
||||
|
display: 'flex', |
||||
|
alignItems: 'center', |
||||
|
justifyContent: 'center', |
||||
|
flexDirection: 'column', // Stacks content vertically on smaller screens |
||||
|
width: '100%' |
||||
|
}} |
||||
|
> |
||||
|
<DataGrid |
||||
|
rows={dataRow} |
||||
|
columns={columns} |
||||
|
initialState={{ pagination: { paginationModel } }} |
||||
|
pageSizeOptions={[5, 10]} |
||||
|
sx={{ |
||||
|
border: 0, |
||||
|
width: '100%', // Ensures the DataGrid takes full width |
||||
|
height: '100%', // Ensures it grows to fit content |
||||
|
minHeight: 400, // Minimum height for the DataGrid |
||||
|
display: 'flex', |
||||
|
justifyContent: 'center', |
||||
|
'@media (max-width: 600px)': { |
||||
|
width: '100%', // 100% width on small screens |
||||
|
height: 'auto' // Allow height to grow with content |
||||
|
} |
||||
|
}} |
||||
|
slots={{ toolbar: GridToolbar }} |
||||
|
localeText={frFR.components.MuiDataGrid.defaultProps.localeText} |
||||
|
/> |
||||
|
</div> |
||||
|
</ThemeProvider> |
||||
|
</Paper> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
) |
||||
|
} |
||||
|
|
||||
|
export default Matieres |
||||
@ -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 ( |
||||
|
<div className={classe.mainHome}> |
||||
|
<div className={classeAdd.header}> |
||||
|
<div className={classe.h1style}> |
||||
|
<div className={classeHome.blockTitle}> |
||||
|
<h1 style={{ display: 'flex', alignItems: 'center', gap: '10px' }}> |
||||
|
<FaBook /> |
||||
|
Matiere |
||||
|
</h1> |
||||
|
<div style={{ display: 'flex', gap: '10px' }}> |
||||
|
<Link to={'#'} onClick={download}> |
||||
|
<Button color="warning" variant="contained"> |
||||
|
<FaDownload style={{ fontSize: '20px' }} /> |
||||
|
</Button> |
||||
|
</Link> |
||||
|
<Link to={'/matiere'}> |
||||
|
<Button color="warning" variant="contained"> |
||||
|
<IoMdReturnRight style={{ fontSize: '20px' }} /> |
||||
|
</Button> |
||||
|
</Link> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<div className={classeHome.boxEtudiantsCard}> |
||||
|
<Paper |
||||
|
sx={{ |
||||
|
// alignItems: "center", |
||||
|
// justifyContent: "center", |
||||
|
width: '100%' |
||||
|
}} |
||||
|
ref={PaperRef} |
||||
|
> |
||||
|
<div |
||||
|
style={{ |
||||
|
padding: '10px', |
||||
|
textAlign: 'center', |
||||
|
display: 'flex', |
||||
|
alignItems: 'center', |
||||
|
justifyContent: 'center' |
||||
|
}} |
||||
|
> |
||||
|
<table style={{ border: 'solid 1px gray', fontSize: '20px' }} id="myTable"> |
||||
|
<thead> |
||||
|
<tr style={{ textAlign: 'center', borderBottom: 'solid 1px gray' }}> |
||||
|
<th colSpan={4}> {nom} </th> |
||||
|
</tr> |
||||
|
<tr style={{ borderBottom: 'solid 1px gray' }}> |
||||
|
<th style={{ borderRight: 'solid 1px gray' }}>N°</th> |
||||
|
<th style={{ borderRight: 'solid 1px gray' }}>Nom et Prenom</th> |
||||
|
<th style={{ borderRight: 'solid 1px gray' }}>Mention</th> |
||||
|
<th>Emergement</th> |
||||
|
</tr> |
||||
|
</thead> |
||||
|
<tbody> |
||||
|
{sortedStudents.map((fi, index) => ( |
||||
|
<tr key={index}> |
||||
|
<td |
||||
|
style={{ |
||||
|
borderRight: 'solid 1px gray', |
||||
|
borderBottom: 'solid 1px gray', |
||||
|
padding: '5px' |
||||
|
}} |
||||
|
> |
||||
|
{index + 1} |
||||
|
</td> |
||||
|
<td |
||||
|
style={{ |
||||
|
borderRight: 'solid 1px gray', |
||||
|
borderBottom: 'solid 1px gray', |
||||
|
padding: '5px' |
||||
|
}} |
||||
|
> |
||||
|
{fi.nom} {fi.prenom} |
||||
|
</td> |
||||
|
<td |
||||
|
style={{ |
||||
|
borderRight: 'solid 1px gray', |
||||
|
borderBottom: 'solid 1px gray', |
||||
|
padding: '5px' |
||||
|
}} |
||||
|
> |
||||
|
{compareMention(fi.mention_id)} |
||||
|
</td> |
||||
|
<td style={{ borderBottom: 'solid 1px gray', padding: '5px' }}></td> |
||||
|
</tr> |
||||
|
))} |
||||
|
</tbody> |
||||
|
</table> |
||||
|
</div> |
||||
|
</Paper> |
||||
|
</div> |
||||
|
</div> |
||||
|
) |
||||
|
} |
||||
|
|
||||
|
export default ModalExportFichr |
||||
@ -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 ( |
||||
|
<Dialog open={open} onClose={onClose}> |
||||
|
<form action="" onSubmit={formSubmit}> |
||||
|
<DialogTitle>Assignation à des niveaux</DialogTitle> |
||||
|
<DialogContent> |
||||
|
<Box sx={{ flexGrow: 1 }}> |
||||
|
<Grid container spacing={2}> |
||||
|
<Grid item xs={12} sm={6}> |
||||
|
<FormControl sx={{ m: 1, width: 300 }}> |
||||
|
<InputLabel id="niveaux-select-label" color="warning"> |
||||
|
Niveaux |
||||
|
</InputLabel> |
||||
|
<Select |
||||
|
labelId="niveaux-select-label" |
||||
|
id="niveaux-select" |
||||
|
name="niveau_id" |
||||
|
value={formData.niveau_id || ''} |
||||
|
onChange={handleChange} |
||||
|
color="warning" |
||||
|
size="small" |
||||
|
required |
||||
|
input={<OutlinedInput label="Niveaux" />} |
||||
|
MenuProps={{ |
||||
|
PaperProps: { |
||||
|
style: { |
||||
|
maxHeight: 200, |
||||
|
width: 250 |
||||
|
} |
||||
|
} |
||||
|
}} |
||||
|
> |
||||
|
{niveaux.map((niveau) => ( |
||||
|
<MenuItem key={niveau.niveau_id} value={niveau.niveau_id}> |
||||
|
{niveau.nom} |
||||
|
</MenuItem> |
||||
|
))} |
||||
|
</Select> |
||||
|
</FormControl> |
||||
|
</Grid> |
||||
|
</Grid> |
||||
|
</Box> |
||||
|
</DialogContent> |
||||
|
<DialogActions> |
||||
|
<Button onClick={onClose} color="error"> |
||||
|
Annuler |
||||
|
</Button> |
||||
|
<Button type="submit" color="warning"> |
||||
|
Soumettre |
||||
|
</Button> |
||||
|
</DialogActions> |
||||
|
</form> |
||||
|
</Dialog> |
||||
|
) |
||||
|
} |
||||
|
|
||||
|
export default NiveauMatiere |
||||
@ -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 ( |
||||
|
<Dialog open={open} onClose={onClose}> |
||||
|
<form action="" onSubmit={formSubmit}> |
||||
|
<DialogTitle>Assignation à des parcours</DialogTitle> |
||||
|
<DialogContent> |
||||
|
<Box sx={{ flexGrow: 1 }}> |
||||
|
<Grid container spacing={2}> |
||||
|
<Grid item xs={12} sm={6}> |
||||
|
<FormControl sx={{ m: 1, width: 300 }}> |
||||
|
<InputLabel id="parcours-select-label" color="warning"> |
||||
|
Parcours |
||||
|
</InputLabel> |
||||
|
<Select |
||||
|
labelId="parcours-select-label" |
||||
|
id="parcours-select" |
||||
|
multiple |
||||
|
name="parcour_id" |
||||
|
value={formData.parcour_id || []} |
||||
|
onChange={handleChange} |
||||
|
color="warning" |
||||
|
size="small" |
||||
|
required |
||||
|
input={<OutlinedInput label="Parcours" />} // Fixed label name |
||||
|
MenuProps={{ |
||||
|
PaperProps: { |
||||
|
style: { |
||||
|
maxHeight: 200, // Limit dropdown height |
||||
|
width: 250 // Adjust dropdown width if needed |
||||
|
} |
||||
|
} |
||||
|
}} |
||||
|
> |
||||
|
{parcours.map((sem) => ( |
||||
|
<MenuItem key={sem.id} value={sem.id}> |
||||
|
{sem.nom} |
||||
|
</MenuItem> |
||||
|
))} |
||||
|
</Select> |
||||
|
</FormControl> |
||||
|
</Grid> |
||||
|
</Grid> |
||||
|
</Box> |
||||
|
</DialogContent> |
||||
|
<DialogActions> |
||||
|
<Button onClick={onClose} color="error"> |
||||
|
Annuler |
||||
|
</Button> |
||||
|
<Button type="submit" color="warning"> |
||||
|
Soumettre |
||||
|
</Button> |
||||
|
</DialogActions> |
||||
|
</form> |
||||
|
</Dialog> |
||||
|
) |
||||
|
} |
||||
|
|
||||
|
export default ParcourMatiere |
||||
@ -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) => ( |
||||
|
<div style={{ display: 'flex', gap: '10px' }}> |
||||
|
<Link to={`#`} onClick={() => openUpdate(params.value)}> |
||||
|
<Button color="warning" variant="contained"> |
||||
|
<FaPenToSquare |
||||
|
style={{ fontSize: '20px', color: 'white', outline: 'none' }} |
||||
|
className={`update${params.value}`} |
||||
|
/> |
||||
|
<Tooltip |
||||
|
anchorSelect={`.update${params.value}`} |
||||
|
style={{ fontSize: '15px', zIndex: 22 }} |
||||
|
place="top" |
||||
|
> |
||||
|
Modifier |
||||
|
</Tooltip> |
||||
|
</Button> |
||||
|
</Link> |
||||
|
</div> |
||||
|
) |
||||
|
} |
||||
|
] |
||||
|
|
||||
|
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 ( |
||||
|
<div className={classe.mainHome}> |
||||
|
<AddParcours open={openModalAdd} onClose={closeModalAdd} onSubmitSuccess={handleFormSubmit} /> |
||||
|
<UpdateParcour |
||||
|
id={idToSend} |
||||
|
onClose={closeUpdate} |
||||
|
onSubmitSuccess={handleFormSubmit} |
||||
|
open={openModalUpdate} |
||||
|
/> |
||||
|
<div className={classeAdd.header}> |
||||
|
<div className={classe.h1style}> |
||||
|
<div className={classeHome.blockTitle}> |
||||
|
<h1 style={{ display: 'flex', alignItems: 'center', gap: '10px' }}> |
||||
|
<MdRule /> |
||||
|
Parcours |
||||
|
</h1> |
||||
|
<Link to={'#'} onClick={openModalAddFunction}> |
||||
|
<Button color="warning" variant="contained"> |
||||
|
<FaPlus style={{ fontSize: '20px' }} /> AJouter |
||||
|
</Button> |
||||
|
</Link> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<div className={classeHome.boxEtudiantsCard}> |
||||
|
<div style={{ display: 'flex', justifyContent: 'center' }}> |
||||
|
<Paper |
||||
|
sx={{ |
||||
|
height: 'auto', // Auto height to make the grid responsive |
||||
|
minHeight: 500, // Ensures a minimum height |
||||
|
display: 'flex', |
||||
|
// alignItems: "center", |
||||
|
// justifyContent: "center", |
||||
|
width: '100%' |
||||
|
}} |
||||
|
> |
||||
|
<ThemeProvider theme={theme}> |
||||
|
<div |
||||
|
style={{ |
||||
|
display: 'flex', |
||||
|
alignItems: 'center', |
||||
|
justifyContent: 'center', |
||||
|
flexDirection: 'column', // Stacks content vertically on smaller screens |
||||
|
width: '100%' |
||||
|
}} |
||||
|
> |
||||
|
<DataGrid |
||||
|
rows={dataRow} |
||||
|
columns={columns} |
||||
|
initialState={{ pagination: { paginationModel } }} |
||||
|
pageSizeOptions={[5, 10]} |
||||
|
sx={{ |
||||
|
border: 0, |
||||
|
width: '100%', // Ensures the DataGrid takes full width |
||||
|
height: '100%', // Ensures it grows to fit content |
||||
|
minHeight: 400, // Minimum height for the DataGrid |
||||
|
display: 'flex', |
||||
|
justifyContent: 'center', |
||||
|
'@media (max-width: 600px)': { |
||||
|
width: '100%', // 100% width on small screens |
||||
|
height: 'auto' // Allow height to grow with content |
||||
|
} |
||||
|
}} |
||||
|
slots={{ toolbar: GridToolbar }} |
||||
|
localeText={frFR.components.MuiDataGrid.defaultProps.localeText} |
||||
|
/> |
||||
|
</div> |
||||
|
</ThemeProvider> |
||||
|
</Paper> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
) |
||||
|
} |
||||
|
|
||||
|
export default Parcours |
||||
@ -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 ( |
||||
|
<div className={classe.mainHome}> |
||||
|
<div className={classeHome.header}> |
||||
|
<div className={classe.h1style}> |
||||
|
<div className={classeHome.blockTitle}> |
||||
|
<h1> |
||||
|
Resultat des {niveau} en {scolaire} |
||||
|
</h1> |
||||
|
<div style={{ display: 'flex', gap: '10px' }}> |
||||
|
<Link to={'#'} onClick={print}> |
||||
|
<Button color="warning" variant="contained"> |
||||
|
<FaDownload style={{ fontSize: '20px' }} /> |
||||
|
Télécharger |
||||
|
</Button> |
||||
|
</Link> |
||||
|
<Link to={'#'} onClick={() => window.history.back()}> |
||||
|
<Button color="warning" variant="contained"> |
||||
|
<IoMdReturnRight style={{ fontSize: '20px' }} /> |
||||
|
</Button> |
||||
|
</Link> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<div className={classeHome.boxEtudiantsCard}> |
||||
|
<Paper |
||||
|
sx={{ |
||||
|
height: 'auto', // Auto height to make the grid responsive |
||||
|
width: '100%', |
||||
|
// minHeight: 500, // Ensures a minimum height |
||||
|
display: 'flex', |
||||
|
padding: '2%' |
||||
|
}} |
||||
|
> |
||||
|
<table className="table table-bordered table-striped text-center shadow-sm" id="myTable2"> |
||||
|
<thead className="table-secondary"> |
||||
|
<tr> |
||||
|
<td colSpan={4} className="py-3"> |
||||
|
<h6> |
||||
|
Niveau {niveau} | Année Scolaire {scolaire} |
||||
|
</h6> |
||||
|
</td> |
||||
|
</tr> |
||||
|
<tr> |
||||
|
<th>Nom</th> |
||||
|
<th>Prenom</th> |
||||
|
<th>Mention</th> |
||||
|
<th>Moyenne</th> |
||||
|
</tr> |
||||
|
</thead> |
||||
|
<tbody> |
||||
|
{sortedStudents.map((sorted) => ( |
||||
|
<tr key={sorted.id}> |
||||
|
<td>{sorted.nom}</td> |
||||
|
<td>{sorted.prenom}</td> |
||||
|
<td>{returnmention(sorted.mention)}</td> |
||||
|
<td className="fw-bold">{sorted.moyenne}</td> |
||||
|
</tr> |
||||
|
))} |
||||
|
</tbody> |
||||
|
</table> |
||||
|
</Paper> |
||||
|
</div> |
||||
|
</div> |
||||
|
) |
||||
|
} |
||||
|
|
||||
|
export default Resultat |
||||
@ -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 ( |
||||
|
<nav className={classe.navbar}> |
||||
|
<style> |
||||
|
{` |
||||
|
.custom-tooltip { |
||||
|
font-size: 15px; |
||||
|
border: solid 1px white !important; |
||||
|
border-radius: 4px; |
||||
|
} |
||||
|
`} |
||||
|
</style> |
||||
|
<ul className={classe.liste1}> |
||||
|
<li> |
||||
|
<Link |
||||
|
to={'/'} |
||||
|
className={classe.nav_link} |
||||
|
style={{ |
||||
|
outline: 'none', |
||||
|
borderBottom: window.location.hash == '#/' ? '2px solid white' : 'none' |
||||
|
}} |
||||
|
> |
||||
|
<RiDashboardHorizontalFill className="dashboard" style={{ outline: 'none' }} /> |
||||
|
<Tooltip anchorSelect=".dashboard" className="custom-tooltip" place="top"> |
||||
|
Dashboard |
||||
|
</Tooltip> |
||||
|
</Link> |
||||
|
</li> |
||||
|
<li> |
||||
|
<Link |
||||
|
to={'/student'} |
||||
|
className={classe.nav_link} |
||||
|
style={{ |
||||
|
outline: 'none', |
||||
|
borderBottom: window.location.hash == '#/student' ? '2px solid white' : 'none' |
||||
|
}} |
||||
|
> |
||||
|
<PiStudentFill className="student" style={{ outline: 'none' }} /> |
||||
|
<Tooltip anchorSelect=".student" className="custom-tooltip" place="top"> |
||||
|
Etudiants |
||||
|
</Tooltip> |
||||
|
</Link> |
||||
|
</li> |
||||
|
<li> |
||||
|
<Link |
||||
|
to={'/notes'} |
||||
|
className={classe.nav_link} |
||||
|
style={{ |
||||
|
outline: 'none', |
||||
|
borderBottom: window.location.hash == '#/notes' ? '2px solid white' : 'none' |
||||
|
}} |
||||
|
> |
||||
|
<CgNotes className="notes" style={{ outline: 'none' }} /> |
||||
|
<Tooltip anchorSelect=".notes" className="custom-tooltip" place="top"> |
||||
|
Notes |
||||
|
</Tooltip> |
||||
|
</Link> |
||||
|
</li> |
||||
|
<li> |
||||
|
<Link |
||||
|
to={'/mention'} |
||||
|
className={classe.nav_link} |
||||
|
style={{ |
||||
|
outline: 'none', |
||||
|
borderBottom: window.location.hash == '#/mention' ? '2px solid white' : 'none' |
||||
|
}} |
||||
|
> |
||||
|
<FaClipboardList className="mention" style={{ outline: 'none' }} /> |
||||
|
<Tooltip anchorSelect=".mention" className="custom-tooltip" place="top"> |
||||
|
Mentions |
||||
|
</Tooltip> |
||||
|
</Link> |
||||
|
</li> |
||||
|
<li> |
||||
|
<Link |
||||
|
to={'/matiere'} |
||||
|
className={classe.nav_link} |
||||
|
style={{ |
||||
|
outline: 'none', |
||||
|
borderBottom: window.location.hash == '#/matiere' ? '2px solid white' : 'none' |
||||
|
}} |
||||
|
> |
||||
|
<FaBook className="matiere" style={{ outline: 'none' }} /> |
||||
|
<Tooltip anchorSelect=".matiere" className="custom-tooltip" place="top"> |
||||
|
Matières |
||||
|
</Tooltip> |
||||
|
</Link> |
||||
|
</li> |
||||
|
<li> |
||||
|
<Link |
||||
|
to={'/niveau'} |
||||
|
className={classe.nav_link} |
||||
|
style={{ |
||||
|
outline: 'none', |
||||
|
borderBottom: window.location.hash == '#/niveau' ? '2px solid white' : 'none' |
||||
|
}} |
||||
|
> |
||||
|
<GiUpgrade className="niveau" style={{ outline: 'none' }} /> |
||||
|
<Tooltip anchorSelect=".niveau" className="custom-tooltip" place="top"> |
||||
|
Niveau |
||||
|
</Tooltip> |
||||
|
</Link> |
||||
|
</li> |
||||
|
<li> |
||||
|
<Link |
||||
|
to={'/anneescolaire'} |
||||
|
className={classe.nav_link} |
||||
|
style={{ |
||||
|
outline: 'none', |
||||
|
borderBottom: window.location.hash == '#/niveau' ? '2px solid white' : 'none' |
||||
|
}} |
||||
|
> |
||||
|
<BsCalendar2Date className="anneescolaire" style={{ outline: 'none' }} /> |
||||
|
<Tooltip anchorSelect=".anneescolaire" className="custom-tooltip" place="top"> |
||||
|
Année Scolaire |
||||
|
</Tooltip> |
||||
|
</Link> |
||||
|
</li> |
||||
|
<li> |
||||
|
<Link |
||||
|
to={'/parcours'} |
||||
|
className={classe.nav_link} |
||||
|
style={{ |
||||
|
outline: 'none', |
||||
|
borderBottom: window.location.hash == '#/parcours' ? '2px solid white' : 'none' |
||||
|
}} |
||||
|
> |
||||
|
<MdRule className="rules" style={{ outline: 'none' }} /> |
||||
|
<Tooltip anchorSelect=".rules" className="custom-tooltip" place="top"> |
||||
|
Parcours |
||||
|
</Tooltip> |
||||
|
</Link> |
||||
|
</li> |
||||
|
<li> |
||||
|
<Link |
||||
|
to={'/apropos'} |
||||
|
className={classe.nav_link} |
||||
|
style={{ |
||||
|
outline: 'none', |
||||
|
borderBottom: window.location.hash == '#/apropos' ? '2px solid white' : 'none' |
||||
|
}} |
||||
|
> |
||||
|
<IoMdHelpCircleOutline className="Apropos" style={{ outline: 'none' }} /> |
||||
|
<Tooltip anchorSelect=".Apropos" className="custom-tooltip" place="top"> |
||||
|
A propos |
||||
|
</Tooltip> |
||||
|
</Link> |
||||
|
</li> |
||||
|
{/* <li> |
||||
|
<Link to={'/manual'} className={classe.nav_link} style={{ |
||||
|
outline: 'none', |
||||
|
borderBottom: window.location.hash == '#/manual' ? '2px solid white' : 'none' |
||||
|
}}> |
||||
|
<GrManual className='manual' style={{ outline: 'none' }} /> |
||||
|
<Tooltip anchorSelect=".manual" className='custom-tooltip' place="top"> |
||||
|
Manuel d'utilisation |
||||
|
</Tooltip> |
||||
|
</Link> |
||||
|
</li> */} |
||||
|
</ul> |
||||
|
<ul className={classe.liste2}> |
||||
|
<li> |
||||
|
<Link |
||||
|
id="basic-button" |
||||
|
aria-controls={open ? 'basic-menu' : undefined} |
||||
|
aria-haspopup="true" |
||||
|
aria-expanded={open ? 'true' : undefined} |
||||
|
onClick={handleClick} |
||||
|
style={{ |
||||
|
outline: 'none', |
||||
|
borderBottom: |
||||
|
window.location.hash == '#/admin' || window.location.hash == '#/para' |
||||
|
? '2px solid white' |
||||
|
: 'none' |
||||
|
}} |
||||
|
to={'#'} |
||||
|
className={classe.nav_link} |
||||
|
> |
||||
|
<FaUserCircle className="admin" style={{ outline: 'none' }} /> |
||||
|
<Tooltip anchorSelect=".admin" place="top" className="custom-tooltip"> |
||||
|
Admin |
||||
|
</Tooltip> |
||||
|
</Link> |
||||
|
|
||||
|
<Menu |
||||
|
id="basic-menu" |
||||
|
anchorEl={anchorEl} |
||||
|
open={open} |
||||
|
onClose={handleClose} |
||||
|
MenuListProps={{ |
||||
|
'aria-labelledby': 'basic-button' |
||||
|
}} |
||||
|
> |
||||
|
<MenuItem> |
||||
|
<Link |
||||
|
to="/systemenote" |
||||
|
style={{ color: 'black', textDecoration: 'none' }} |
||||
|
onClick={handleClose} |
||||
|
> |
||||
|
<CgNotes /> Système des notes |
||||
|
</Link> |
||||
|
</MenuItem> |
||||
|
<MenuItem> |
||||
|
<Link |
||||
|
to="/admin" |
||||
|
style={{ color: 'black', textDecoration: 'none' }} |
||||
|
onClick={handleClose} |
||||
|
> |
||||
|
<MdAdminPanelSettings /> Admin |
||||
|
</Link> |
||||
|
</MenuItem> |
||||
|
<MenuItem> |
||||
|
<Link |
||||
|
to="/para" |
||||
|
style={{ color: 'black', textDecoration: 'none' }} |
||||
|
onClick={handleClose} |
||||
|
> |
||||
|
<FaUserCog /> Paramètre |
||||
|
</Link> |
||||
|
</MenuItem> |
||||
|
</Menu> |
||||
|
</li> |
||||
|
{/* <li> |
||||
|
<Link to={'/teste'}> |
||||
|
<SiVitest className='test' style={{outline:'none'}} /> |
||||
|
<Tooltip anchorSelect=".test" className='custom-tooltip' place="top"> |
||||
|
Teste |
||||
|
</Tooltip> |
||||
|
</Link> |
||||
|
</li> */} |
||||
|
<li> |
||||
|
<LuLogOut className="logout" style={{ outline: 'none' }} onClick={logout} /> |
||||
|
<Tooltip anchorSelect=".logout" className="custom-tooltip" place="top"> |
||||
|
Déconnexion |
||||
|
</Tooltip> |
||||
|
</li> |
||||
|
</ul> |
||||
|
</nav> |
||||
|
) |
||||
|
} |
||||
|
|
||||
|
export default Sidenav |
||||
@ -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 = () => ( |
||||
|
<Modal |
||||
|
open={open} |
||||
|
onClose={handleClose} |
||||
|
aria-labelledby="modal-title" |
||||
|
aria-describedby="modal-description" |
||||
|
> |
||||
|
<Box |
||||
|
sx={{ |
||||
|
position: 'absolute', |
||||
|
top: '50%', |
||||
|
left: '50%', |
||||
|
transform: 'translate(-50%, -50%)', |
||||
|
width: 450, |
||||
|
bgcolor: 'background.paper', |
||||
|
boxShadow: 24, |
||||
|
p: 4 |
||||
|
}} |
||||
|
> |
||||
|
<Typography |
||||
|
sx={{ |
||||
|
display: 'flex', |
||||
|
flexDirection: 'column', |
||||
|
alignItems: 'center', |
||||
|
gap: 2 |
||||
|
}} |
||||
|
> |
||||
|
<img src={status === 200 ? svgSuccess : svgError} alt="" width={70} height={70} /> |
||||
|
<span> {status === 200 ? 'Mise à jour effectuée avec succès' : 'Erreur'}</span> |
||||
|
</Typography> |
||||
|
<Box |
||||
|
sx={{ |
||||
|
marginTop: 2, |
||||
|
display: 'flex', |
||||
|
justifyContent: 'flex-end' |
||||
|
}} |
||||
|
> |
||||
|
<Button onClick={handleClose} color="warning" variant="contained"> |
||||
|
OK |
||||
|
</Button> |
||||
|
</Box> |
||||
|
</Box> |
||||
|
</Modal> |
||||
|
) |
||||
|
|
||||
|
return ( |
||||
|
<div className={classe.mainHome}> |
||||
|
{modals()} |
||||
|
<div className={classeHome.header}> |
||||
|
<div className={classe.h1style}> |
||||
|
<div className={classeHome.blockTitle}> |
||||
|
<h1 style={{ display: 'flex', alignItems: 'center', gap: '10px' }}> |
||||
|
<BsCalendar2Date /> |
||||
|
Mise a jour Année Scolaire |
||||
|
</h1> |
||||
|
<Link to={'#'} onClick={() => window.history.back()}> |
||||
|
<Button color="warning" variant="contained"> |
||||
|
<IoMdReturnRight style={{ fontSize: '20px' }} /> |
||||
|
</Button> |
||||
|
</Link> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<div className={classeHome.boxEtudiantsCard}> |
||||
|
<Paper |
||||
|
sx={{ |
||||
|
width: '100%', |
||||
|
height: '100%', // Auto height to make the grid responsive |
||||
|
minHeight: 500, // Ensures a minimum height |
||||
|
display: 'flex', |
||||
|
flexDirection: 'column', |
||||
|
alignItems: 'center', |
||||
|
justifyContent: 'center' |
||||
|
}} |
||||
|
> |
||||
|
<form |
||||
|
style={{ |
||||
|
width: '50%', |
||||
|
padding: '1%', |
||||
|
height: '20%', |
||||
|
border: 'solid 2px orange', |
||||
|
borderRadius: '10px' |
||||
|
}} |
||||
|
onSubmit={formSubmit} |
||||
|
> |
||||
|
<h4 style={{ textAlign: 'center', padding: '0 0 3% 0' }}> |
||||
|
mise a jour Année Scolaire |
||||
|
</h4> |
||||
|
<Grid container spacing={2}> |
||||
|
<Grid item xs={12} sm={6}> |
||||
|
<TextField |
||||
|
label={'Année Scolaire'} |
||||
|
name={'code'} |
||||
|
placeholder="2024-2025" |
||||
|
color="warning" |
||||
|
fullWidth |
||||
|
value={formData.code} |
||||
|
InputProps={{ |
||||
|
startAdornment: ( |
||||
|
<InputAdornment position="start"> |
||||
|
<FaCalendarAlt /> |
||||
|
</InputAdornment> |
||||
|
) |
||||
|
}} |
||||
|
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 |
||||
|
} |
||||
|
}} |
||||
|
/> |
||||
|
</Grid> |
||||
|
<Grid item xs={12} sm={6}> |
||||
|
<TextField |
||||
|
label={'Date de début'} |
||||
|
name={'debut'} |
||||
|
color="warning" |
||||
|
onChange={handleInputChange} |
||||
|
required |
||||
|
value={formData.debut} |
||||
|
fullWidth |
||||
|
type="date" |
||||
|
InputProps={{ |
||||
|
startAdornment: ( |
||||
|
<InputAdornment position="start"> |
||||
|
<FaCalendarAlt /> |
||||
|
</InputAdornment> |
||||
|
) |
||||
|
}} |
||||
|
className="inputAddNote" |
||||
|
sx={{ |
||||
|
'& .MuiOutlinedInput-root': { |
||||
|
'&:hover fieldset': { |
||||
|
borderColor: '#ff9800' // Set the border color on hover |
||||
|
} |
||||
|
}, |
||||
|
'& .MuiInputBase-input::placeholder': { |
||||
|
fontSize: '11px' // Set the placeholder font size |
||||
|
} |
||||
|
}} |
||||
|
/> |
||||
|
</Grid> |
||||
|
<Grid item xs={12} sm={6}> |
||||
|
<TextField |
||||
|
label={'Date de fin'} |
||||
|
name={'fin'} |
||||
|
color="warning" |
||||
|
fullWidth |
||||
|
type="date" |
||||
|
InputProps={{ |
||||
|
startAdornment: ( |
||||
|
<InputAdornment position="start"> |
||||
|
<FaCalendarAlt /> |
||||
|
</InputAdornment> |
||||
|
) |
||||
|
}} |
||||
|
onChange={handleInputChange} |
||||
|
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 |
||||
|
} |
||||
|
}} |
||||
|
/> |
||||
|
</Grid> |
||||
|
<Grid |
||||
|
item |
||||
|
xs={12} |
||||
|
style={{ display: 'flex', gap: '30px', justifyContent: 'flex-end' }} |
||||
|
> |
||||
|
<Button type="submit" color="warning" variant="contained"> |
||||
|
Enregister |
||||
|
</Button> |
||||
|
</Grid> |
||||
|
</Grid> |
||||
|
</form> |
||||
|
</Paper> |
||||
|
</div> |
||||
|
</div> |
||||
|
) |
||||
|
} |
||||
|
|
||||
|
export default SingleAnneeScolaire |
||||
File diff suppressed because it is too large
@ -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 ( |
||||
|
<div style={{ display: 'flex', gap: '10px' }}> |
||||
|
{matchingNote ? ( |
||||
|
<Link |
||||
|
to={`/single/notes/${params.value}/${params.row.niveau}/${params.row.annee_scolaire}`} |
||||
|
> |
||||
|
<Button color="warning" variant="contained" className={`voirs${params.value}`}> |
||||
|
<IoEyeSharp style={{ fontSize: '20px', color: 'white' }} /> |
||||
|
</Button> |
||||
|
<Tooltip anchorSelect={`.voirs${params.value}`} className="custom-tooltip" place="top"> |
||||
|
Voir les notes |
||||
|
</Tooltip> |
||||
|
</Link> |
||||
|
) : ( |
||||
|
<Link |
||||
|
to={`/addnotes/${params.value}/${params.row.niveau}/${params.row.mention_id}/${params.row.parcour}`} |
||||
|
> |
||||
|
<Button color="warning" variant="contained" className={`note${params.value}`}> |
||||
|
<CgNotes style={{ fontSize: '20px', color: 'white' }} /> |
||||
|
</Button> |
||||
|
<Tooltip anchorSelect={`.note${params.value}`} className="custom-tooltip" place="top"> |
||||
|
Ajouter un notes à cet étudiant |
||||
|
</Tooltip> |
||||
|
</Link> |
||||
|
)} |
||||
|
</div> |
||||
|
) |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 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) => ( |
||||
|
<img |
||||
|
src={params.value} // Correct the access to the image source |
||||
|
alt={'image pdp'} |
||||
|
style={{ width: 50, height: 50, borderRadius: '50%', objectFit: 'cover' }} |
||||
|
/> |
||||
|
) |
||||
|
}, |
||||
|
{ |
||||
|
field: 'action', |
||||
|
headerName: 'Action', |
||||
|
width: 300, |
||||
|
renderCell: (params) => ( |
||||
|
<div style={{ display: 'flex', gap: '10px' }}> |
||||
|
<Link to={`/single/${params.value}`}> |
||||
|
<Button color="warning" variant="contained"> |
||||
|
<FaPenToSquare |
||||
|
style={{ fontSize: '20px', color: 'white' }} |
||||
|
className={`update${params.value}`} |
||||
|
/> |
||||
|
<Tooltip |
||||
|
anchorSelect={`.update${params.value}`} |
||||
|
className="custom-tooltip" |
||||
|
place="top" |
||||
|
> |
||||
|
Modifier |
||||
|
</Tooltip> |
||||
|
</Button> |
||||
|
</Link> |
||||
|
<Link to={`/tranche/${params.value}`} className={`verif${params.value}`}> |
||||
|
<Button color="warning" variant="contained"> |
||||
|
<MdVerified style={{ fontSize: '20px', color: 'white' }} /> |
||||
|
</Button> |
||||
|
<Tooltip anchorSelect={`.verif${params.value}`} className="custom-tooltip" place="top"> |
||||
|
Verification Frais de Formation |
||||
|
</Tooltip> |
||||
|
</Link> |
||||
|
<Link onClick={() => Print(params.value)}> |
||||
|
<Button color="warning" variant="contained" className={`export${params.value}`}> |
||||
|
<FaFilePdf style={{ fontSize: '20px', color: 'white' }} /> |
||||
|
</Button> |
||||
|
<Tooltip anchorSelect={`.export${params.value}`} className="custom-tooltip" place="top"> |
||||
|
Exporter carte d'etudiants |
||||
|
</Tooltip> |
||||
|
</Link> |
||||
|
<Link className={`voir${params.value}`}> |
||||
|
<Button color="warning" variant="contained"> |
||||
|
<FaPlus style={{ fontSize: '20px', color: 'white' }} /> |
||||
|
</Button> |
||||
|
<Tooltip |
||||
|
anchorSelect={`.voir${params.value}`} |
||||
|
style={{ |
||||
|
fontSize: '12px', |
||||
|
overflow: 'visible', |
||||
|
zIndex: 999222, |
||||
|
display: 'flex', |
||||
|
flexDirection: 'column', |
||||
|
gap: '10px', |
||||
|
marginTop: "15px" |
||||
|
}} |
||||
|
place="left-end" |
||||
|
clickable |
||||
|
> |
||||
|
{/* Groupe 1 : Certificat */} |
||||
|
<Link style={{ display: 'flex', gap: '10px' }}> |
||||
|
<SeeNote params={params} /> |
||||
|
<Link> |
||||
|
<Button |
||||
|
onClick={() => handleOpen(params.value)} |
||||
|
color="warning" |
||||
|
variant="contained" |
||||
|
className={`receip${params.value}`} |
||||
|
> |
||||
|
<FaCertificate style={{ fontSize: '20px', color: 'white' }} /> |
||||
|
</Button> |
||||
|
<Tooltip |
||||
|
anchorSelect={`.receip${params.value}`} |
||||
|
className="custom-tooltip" |
||||
|
place="top" |
||||
|
> |
||||
|
Télecharger le Certificat de scolariter |
||||
|
</Tooltip> |
||||
|
</Link> |
||||
|
</Link> |
||||
|
|
||||
|
{/* Groupe 2 : Stage (affiché seulement pour L2) */} |
||||
|
<Link style={{ display: 'flex', gap: '10px' }}> |
||||
|
{params.row.niveau !== 'L1' && ( |
||||
|
<Link> |
||||
|
<Button |
||||
|
onClick={() => handleOpen2()} |
||||
|
color="warning" |
||||
|
variant="contained" |
||||
|
className={`stage${params.value}`} |
||||
|
> |
||||
|
<FaToolbox style={{ fontSize: '20px', color: 'white' }} /> |
||||
|
</Button> |
||||
|
<Tooltip |
||||
|
anchorSelect={`.stage${params.value}`} |
||||
|
className="custom-tooltip" |
||||
|
place="top" |
||||
|
> |
||||
|
Télecharger l'autorisation de stage |
||||
|
</Tooltip> |
||||
|
</Link> |
||||
|
)} |
||||
|
<Link> |
||||
|
<Button |
||||
|
onClick={() => handleOpen3(params.value)} |
||||
|
color="warning" |
||||
|
variant="contained" |
||||
|
className={`recepice${params.value}`} |
||||
|
> |
||||
|
<FaReceipt style={{ fontSize: '20px', color: 'white' }} /> |
||||
|
</Button> |
||||
|
<Tooltip |
||||
|
anchorSelect={`.recepice${params.value}`} |
||||
|
className="custom-tooltip" |
||||
|
place="top" |
||||
|
> |
||||
|
Télecharger le recepissé d'inscription |
||||
|
</Tooltip> |
||||
|
</Link> |
||||
|
</Link> |
||||
|
</Tooltip> |
||||
|
</Link> |
||||
|
</div> |
||||
|
) |
||||
|
} |
||||
|
] |
||||
|
|
||||
|
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 ( |
||||
|
<div className={classe.mainHome}> |
||||
|
<style> |
||||
|
{` |
||||
|
.custom-tooltip { |
||||
|
font-size: 15px; |
||||
|
border: solid 1px white !important; |
||||
|
z-index: 999; |
||||
|
} |
||||
|
`} |
||||
|
</style> |
||||
|
<div className={classeHome.header}> |
||||
|
<div className={classe.h1style}> |
||||
|
<div className={classeHome.blockTitle}> |
||||
|
<h1>Etudiants</h1> |
||||
|
<Link to={'/addstudent'}> |
||||
|
<Button color="warning" variant="contained"> |
||||
|
<PiStudentFill style={{ fontSize: '20px' }} /> Ajouter |
||||
|
</Button> |
||||
|
</Link> |
||||
|
</div> |
||||
|
</div> |
||||
|
{/* bare des filtre */} |
||||
|
<div className={classeHome.container}> |
||||
|
{/* filtre par niveau */} |
||||
|
<div style={{ width: '100%', textAlign: 'right' }}> |
||||
|
<FormControl |
||||
|
sx={{ |
||||
|
m: 1, |
||||
|
width: '30%', |
||||
|
'& .MuiOutlinedInput-root': { |
||||
|
'&:hover fieldset': { |
||||
|
borderColor: '#ff9800' // Set the border color on hover |
||||
|
} |
||||
|
} |
||||
|
}} |
||||
|
size="small" |
||||
|
variant="outlined" |
||||
|
> |
||||
|
<InputLabel |
||||
|
id="demo-select-small-label" |
||||
|
sx={{ color: 'black', fontSize: '18px' }} |
||||
|
color="warning" |
||||
|
> |
||||
|
Niveau |
||||
|
</InputLabel> |
||||
|
<Select |
||||
|
labelId="demo-select-small-label" |
||||
|
id="demo-select-small" |
||||
|
label="Niveau" |
||||
|
color="warning" |
||||
|
name="niveau" |
||||
|
defaultValue={''} |
||||
|
onChange={FilterData} |
||||
|
startAdornment={ |
||||
|
<InputAdornment position="start"> |
||||
|
<FaGraduationCap /> |
||||
|
</InputAdornment> |
||||
|
} |
||||
|
sx={{ |
||||
|
background: 'white', |
||||
|
display: 'flex', |
||||
|
alignItems: 'center', // Align icon and text vertically |
||||
|
'& .MuiSelect-icon': { |
||||
|
marginLeft: 'auto' // Keep the dropdown arrow to the right |
||||
|
} |
||||
|
}} |
||||
|
> |
||||
|
<MenuItem value=""> |
||||
|
<em>Tous les étudiants</em> |
||||
|
</MenuItem> |
||||
|
{niveaus.map((niveau) => ( |
||||
|
<MenuItem value={niveau.nom} key={niveau.id}> |
||||
|
{niveau.nom} |
||||
|
</MenuItem> |
||||
|
))} |
||||
|
</Select> |
||||
|
</FormControl> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
{/* display the data-grid students */} |
||||
|
<div className={classeHome.boxEtudiantsCard}> |
||||
|
<div style={{ display: 'flex', justifyContent: 'center' }}> |
||||
|
<Paper |
||||
|
sx={{ |
||||
|
width: '100%', |
||||
|
height: 'auto', // Auto height to make the grid responsive |
||||
|
minHeight: 500, // Ensures a minimum height |
||||
|
display: 'flex' |
||||
|
}} |
||||
|
> |
||||
|
<ThemeProvider theme={theme}> |
||||
|
<div |
||||
|
style={{ |
||||
|
display: 'flex', |
||||
|
flexDirection: 'column', // Stacks content vertically on smaller screens, |
||||
|
width: '100%' |
||||
|
}} |
||||
|
> |
||||
|
<DataGrid |
||||
|
rows={dataRow} |
||||
|
columns={columns} |
||||
|
pageSizeOptions={pageSizeOptions} |
||||
|
paginationModel={paginationModel} // ✅ Utilise l'état complet |
||||
|
onPaginationModelChange={handlePaginationModelChange} // ✅ Gère page ET pageSize |
||||
|
sx={{ |
||||
|
border: 0, |
||||
|
width: 'auto', // Ensures the DataGrid takes full width |
||||
|
height: '50%', // Ensures it grows to fit content |
||||
|
minHeight: 400, // Minimum height for the DataGrid |
||||
|
display: 'flex', |
||||
|
justifyContent: 'center', |
||||
|
'@media (max-width: 600px)': { |
||||
|
width: '100%', // 100% width on small screens |
||||
|
height: 'auto' // Allow height to grow with content |
||||
|
} |
||||
|
}} |
||||
|
slots={{ toolbar: GridToolbar }} |
||||
|
localeText={frFR.components.MuiDataGrid.defaultProps.localeText} |
||||
|
/> |
||||
|
</div> |
||||
|
</ThemeProvider> |
||||
|
</Paper> |
||||
|
<ModalCertificate open={openModal} onClose={handleClose} json={json} /> |
||||
|
<ModalStage open={openModal2} onClose={handleClose2} /> |
||||
|
<ModalRecepice open={openModal3} onClose={handleClose3} json={json2} /> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
) |
||||
|
} |
||||
|
|
||||
|
export default Student |
||||
@ -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( |
||||
|
<tr key={`${semestreKey}-${unitKey}-${item.id}`}> |
||||
|
{isFirstRow && itemIdx === 0 ? ( |
||||
|
<td |
||||
|
rowSpan={countTotalElements(units)} |
||||
|
key={`${semestreKey}-semestre`} |
||||
|
style={{ borderBottom: 'solid 1px black', borderRight: 'solid 1px black' }} |
||||
|
> |
||||
|
<b>{semestreKey}</b> |
||||
|
</td> |
||||
|
) : null} |
||||
|
|
||||
|
{itemIdx === 0 ? ( |
||||
|
<td |
||||
|
rowSpan={unitArray.length} |
||||
|
key={`${unitKey}-unit`} |
||||
|
style={{ |
||||
|
borderBottom: 'solid 1px black', |
||||
|
margin: 0, |
||||
|
padding: 0, |
||||
|
fontSize: 12, |
||||
|
borderRight: 'solid 1px black' |
||||
|
}} |
||||
|
> |
||||
|
<b>{unitKey}</b> |
||||
|
</td> |
||||
|
) : null} |
||||
|
|
||||
|
<td |
||||
|
style={{ |
||||
|
borderBottom: 'solid 1px black', |
||||
|
margin: 0, |
||||
|
padding: 0, |
||||
|
fontSize: 12, |
||||
|
borderRight: 'solid 1px black' |
||||
|
}} |
||||
|
> |
||||
|
{item.nom} |
||||
|
</td> |
||||
|
<td |
||||
|
style={{ |
||||
|
borderBottom: 'solid 1px black', |
||||
|
margin: 0, |
||||
|
padding: 0, |
||||
|
fontSize: 12, |
||||
|
borderRight: 'solid 1px black' |
||||
|
}} |
||||
|
> |
||||
|
{' '} |
||||
|
<span className="classCredit">{item.credit}</span> |
||||
|
</td> |
||||
|
<td |
||||
|
style={{ |
||||
|
borderBottom: 'solid 1px black', |
||||
|
margin: 0, |
||||
|
padding: 0, |
||||
|
fontSize: 12, |
||||
|
borderRight: 'solid 1px black' |
||||
|
}} |
||||
|
> |
||||
|
{item.note} |
||||
|
</td> |
||||
|
|
||||
|
{/* Ensure this <td> renders only once for the unitArray */} |
||||
|
{itemIdx === 0 ? ( |
||||
|
<td |
||||
|
rowSpan={unitArray.length} |
||||
|
style={{ borderBottom: 'solid 1px black', margin: 0, padding: 0, fontSize: 12 }} |
||||
|
> |
||||
|
<span className="classMoyenne"> |
||||
|
{( |
||||
|
unitArray.reduce((sum, item) => sum + item.credit * item.note, 0) / |
||||
|
unitArray.reduce((sum, item) => sum + item.credit, 0) |
||||
|
).toFixed(2)} |
||||
|
</span> |
||||
|
</td> |
||||
|
) : null} |
||||
|
</tr> |
||||
|
) |
||||
|
}) |
||||
|
}) |
||||
|
}) |
||||
|
return rows |
||||
|
} |
||||
|
|
||||
|
return ( |
||||
|
<div className={classe.mainHome}> |
||||
|
<div className={classeHome.boxEtudiantsCard}> |
||||
|
<div style={{ display: 'flex', justifyContent: 'center' }}> |
||||
|
<Paper |
||||
|
sx={{ |
||||
|
// width: "100%", |
||||
|
height: 'auto', // Auto height to make the grid responsive |
||||
|
minHeight: 500, // Ensures a minimum height |
||||
|
display: 'flex', |
||||
|
padding: '1%' |
||||
|
}} |
||||
|
ref={paperRef} |
||||
|
> |
||||
|
<div> |
||||
|
<img src={entete} alt="image en tete" style={{ border: 'solid 1px gray' }} /> |
||||
|
<div |
||||
|
style={{ |
||||
|
width: '100%', |
||||
|
display: 'flex', |
||||
|
alignItems: 'center', |
||||
|
justifyContent: 'center' |
||||
|
}} |
||||
|
> |
||||
|
<div |
||||
|
style={{ |
||||
|
display: 'flex', |
||||
|
alignItems: 'center', |
||||
|
justifyContent: 'center', |
||||
|
flexDirection: 'column', |
||||
|
fontWeight: 'bold', |
||||
|
textTransform: 'uppercase', |
||||
|
border: 'solid 1px gray', |
||||
|
margin: '1%', |
||||
|
width: '70%', |
||||
|
fontSize: '13px' |
||||
|
}} |
||||
|
> |
||||
|
<span>ECOLE SUPERIEURE POLYTECHNIQUE</span> |
||||
|
<span>REPOBLIKAN’I MADAGASIKARA</span> |
||||
|
<span>Fitiavana-Tanindrazana-Fandrosoana</span> |
||||
|
<span>*********************</span> |
||||
|
<h2>RELEVÉE DE NOTE</h2> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div style={{ fontSize: '13px', margin: '1%' }}> |
||||
|
<span>Nom & Prenoms: </span> |
||||
|
<span>{nomPrenom}</span> |
||||
|
<br /> |
||||
|
<div style={{ display: 'flex', justifyContent: 'space-between' }}> |
||||
|
<span>Année scolaire: </span> |
||||
|
<span>{annee_scolaire}</span> |
||||
|
<span>Niveau: </span> |
||||
|
<span>{niveau}</span> |
||||
|
<span>inscription: </span> |
||||
|
<span>{inscription}</span> |
||||
|
</div> |
||||
|
</div> |
||||
|
<table |
||||
|
style={{ |
||||
|
border: 'solid 1px black', |
||||
|
margin: 0, |
||||
|
padding: 0, |
||||
|
fontSize: 12, |
||||
|
width: '100%' |
||||
|
}} |
||||
|
> |
||||
|
<thead> |
||||
|
<tr> |
||||
|
<th |
||||
|
style={{ |
||||
|
borderBottom: 'solid 1px black', |
||||
|
margin: 0, |
||||
|
padding: 0, |
||||
|
fontSize: 12, |
||||
|
borderRight: 'solid 1px black' |
||||
|
}} |
||||
|
></th> |
||||
|
<th |
||||
|
style={{ |
||||
|
borderBottom: 'solid 1px black', |
||||
|
margin: 0, |
||||
|
padding: 0, |
||||
|
fontSize: 12, |
||||
|
borderRight: 'solid 1px black', |
||||
|
textAlign: 'center' |
||||
|
}} |
||||
|
> |
||||
|
Unités <br /> d’Enseignement <br /> (UE) |
||||
|
</th> |
||||
|
<th |
||||
|
style={{ |
||||
|
borderBottom: 'solid 1px black', |
||||
|
margin: 0, |
||||
|
padding: 0, |
||||
|
fontSize: 12, |
||||
|
borderRight: 'solid 1px black', |
||||
|
textAlign: 'center' |
||||
|
}} |
||||
|
> |
||||
|
Eléments constitutifs |
||||
|
</th> |
||||
|
<th |
||||
|
style={{ |
||||
|
borderBottom: 'solid 1px black', |
||||
|
margin: 0, |
||||
|
padding: 0, |
||||
|
fontSize: 12, |
||||
|
borderRight: 'solid 1px black', |
||||
|
textAlign: 'center' |
||||
|
}} |
||||
|
> |
||||
|
Crédits |
||||
|
</th> |
||||
|
<th |
||||
|
style={{ |
||||
|
borderBottom: 'solid 1px black', |
||||
|
margin: 0, |
||||
|
padding: 0, |
||||
|
fontSize: 12, |
||||
|
borderRight: 'solid 1px black', |
||||
|
textAlign: 'center' |
||||
|
}} |
||||
|
> |
||||
|
Note |
||||
|
</th> |
||||
|
<th |
||||
|
style={{ |
||||
|
borderBottom: 'solid 1px black', |
||||
|
margin: 0, |
||||
|
padding: 0, |
||||
|
fontSize: 12, |
||||
|
textAlign: 'center', |
||||
|
width: '250px' |
||||
|
}} |
||||
|
> |
||||
|
Moyenne |
||||
|
</th> |
||||
|
</tr> |
||||
|
</thead> |
||||
|
<tbody style={{ margin: 0, padding: 0 }}> |
||||
|
{generateTableRows(combinedSemesters)} |
||||
|
|
||||
|
<tr> |
||||
|
<td |
||||
|
colSpan={3} |
||||
|
style={{ |
||||
|
borderBottom: 'solid 1px black', |
||||
|
margin: 0, |
||||
|
padding: 0, |
||||
|
fontSize: 13, |
||||
|
borderRight: 'solid 1px black', |
||||
|
textAlign: 'right', |
||||
|
paddingRight: '3px' |
||||
|
}} |
||||
|
> |
||||
|
{' '} |
||||
|
<b>Total crédit:</b> |
||||
|
</td> |
||||
|
<td |
||||
|
style={{ |
||||
|
borderBottom: 'solid 1px black', |
||||
|
margin: 0, |
||||
|
padding: 0, |
||||
|
fontSize: 13, |
||||
|
textAlignLast: 'left', |
||||
|
paddingLeft: '20px', |
||||
|
borderRight: 'solid 1px black' |
||||
|
}} |
||||
|
> |
||||
|
<b>{crontCredit()}</b> |
||||
|
</td> |
||||
|
<td |
||||
|
style={{ |
||||
|
borderBottom: 'solid 1px black', |
||||
|
margin: 0, |
||||
|
padding: 0, |
||||
|
fontSize: 13, |
||||
|
borderRight: 'solid 1px black' |
||||
|
}} |
||||
|
></td> |
||||
|
<td |
||||
|
style={{ |
||||
|
borderBottom: 'solid 1px black', |
||||
|
margin: 0, |
||||
|
padding: 0, |
||||
|
fontSize: 13 |
||||
|
}} |
||||
|
></td> |
||||
|
</tr> |
||||
|
<tr> |
||||
|
<td |
||||
|
colSpan={5} |
||||
|
style={{ |
||||
|
margin: 0, |
||||
|
padding: 0, |
||||
|
fontSize: 13, |
||||
|
borderRight: 'solid 1px black', |
||||
|
textAlign: 'right', |
||||
|
paddingRight: '3px' |
||||
|
}} |
||||
|
> |
||||
|
<b>Moyenne générale :</b> |
||||
|
</td> |
||||
|
<td> |
||||
|
<b>{countMoyenneGeneral()}</b> |
||||
|
</td> |
||||
|
</tr> |
||||
|
<tr> |
||||
|
<td |
||||
|
colSpan={5} |
||||
|
style={{ |
||||
|
borderBottom: 'solid 1px black', |
||||
|
margin: 0, |
||||
|
padding: 0, |
||||
|
fontSize: 13, |
||||
|
borderRight: 'solid 1px black', |
||||
|
textAlign: 'right', |
||||
|
paddingRight: '3px' |
||||
|
}} |
||||
|
> |
||||
|
<b>Observation :</b> |
||||
|
</td> |
||||
|
<td> |
||||
|
<input |
||||
|
type="text" |
||||
|
style={{ |
||||
|
border: 'none', |
||||
|
outline: 'none', |
||||
|
width: '100%', |
||||
|
textAlign: 'center' |
||||
|
}} |
||||
|
/> |
||||
|
</td> |
||||
|
</tr> |
||||
|
</tbody> |
||||
|
</table> |
||||
|
<div style={{ textAlign: 'center', marginTop: '1%' }}> |
||||
|
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'end' }}> |
||||
|
<span>Décision de jury :</span> |
||||
|
<input type="text" style={{ border: 'none', width: '300px', outline: 'none' }} /> |
||||
|
</div> |
||||
|
<p>Le Directeur </p> |
||||
|
</div> |
||||
|
<p style={{ textAlign: 'right', marginTop: '5%' }}>RATSIMBAZAFY Christian Pierre</p> |
||||
|
</div> |
||||
|
</Paper> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
) |
||||
|
} |
||||
|
|
||||
|
export default TesteDatagrid |
||||
@ -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 ( |
||||
|
<Dialog open={open} onClose={onClose}> |
||||
|
<form action="" onSubmit={handleSubmit}> |
||||
|
<DialogTitle>Ajout tranche</DialogTitle> |
||||
|
<DialogContent> |
||||
|
<Box sx={{ flexGrow: 1 }}> |
||||
|
<Grid container spacing={2}> |
||||
|
<Grid item xs={12} sm={6}> |
||||
|
<TextField |
||||
|
autoFocus |
||||
|
margin="normal" |
||||
|
required |
||||
|
name="tranchename" |
||||
|
label="Désignation" |
||||
|
type="text" |
||||
|
fullWidth |
||||
|
placeholder="Tranche 1" |
||||
|
variant="outlined" |
||||
|
value={formData.tranchename} |
||||
|
color="warning" |
||||
|
onChange={handleChange} |
||||
|
InputProps={{ |
||||
|
startAdornment: ( |
||||
|
<InputAdornment position="start"> |
||||
|
<MdLabelImportantOutline /> |
||||
|
</InputAdornment> |
||||
|
) |
||||
|
}} |
||||
|
/> |
||||
|
</Grid> |
||||
|
<Grid item xs={12} sm={6}> |
||||
|
<TextField |
||||
|
autoFocus |
||||
|
margin="normal" |
||||
|
required |
||||
|
name="montant" |
||||
|
label="Montant" |
||||
|
type="number" |
||||
|
fullWidth |
||||
|
placeholder="Montant" |
||||
|
variant="outlined" |
||||
|
value={formData.montant} |
||||
|
color="warning" |
||||
|
onChange={handleChange} |
||||
|
InputProps={{ |
||||
|
startAdornment: ( |
||||
|
<InputAdornment position="start"> |
||||
|
<MdLabelImportantOutline /> |
||||
|
</InputAdornment> |
||||
|
) |
||||
|
}} |
||||
|
/> |
||||
|
</Grid> |
||||
|
</Grid> |
||||
|
</Box> |
||||
|
</DialogContent> |
||||
|
<DialogActions> |
||||
|
<Button onClick={onClose} color="error"> |
||||
|
Annuler |
||||
|
</Button> |
||||
|
<Button type="submit" color="warning"> |
||||
|
Soumettre |
||||
|
</Button> |
||||
|
</DialogActions> |
||||
|
</form> |
||||
|
</Dialog> |
||||
|
) |
||||
|
} |
||||
|
|
||||
|
export default UpdateTranche |
||||
@ -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() |
||||
|
} |
||||
@ -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 |
||||
@ -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) |
||||
|
} |
||||
@ -0,0 +1,54 @@ |
|||||
|
<!doctype html> |
||||
|
<html lang="en"> |
||||
|
<head> |
||||
|
<meta charset="UTF-8" /> |
||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> |
||||
|
<link rel="stylesheet" href="style.css" /> |
||||
|
<title>Document</title> |
||||
|
</head> |
||||
|
<body> |
||||
|
<div class="container"> |
||||
|
<div class="cart"> |
||||
|
<div class="title"> |
||||
|
<h1>université de toamasina</h1> |
||||
|
<p>***********</p> |
||||
|
<p> |
||||
|
ecole superieure <br /> |
||||
|
polytechnique |
||||
|
</p> |
||||
|
</div> |
||||
|
<div class="content"> |
||||
|
<div class="cart-photos"> |
||||
|
<img src="../images/fab.jpg" alt="" /> |
||||
|
</div> |
||||
|
<div class="cart-info"> |
||||
|
<p><b>Nom</b> : BE</p> |
||||
|
<p><b>Prenom</b> : Joseph Fabrice</p> |
||||
|
<p><b>Date de naissance</b> : 11-12-2001</p> |
||||
|
<p><b>Niveau</b> : L3</p> |
||||
|
<p><b>Année scolaire</b> : 2023-2024</p> |
||||
|
<p><b>Num inscription</b> : 12345678900</p> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div class="cart"> |
||||
|
<div class="qrContent"> |
||||
|
<div class="gauche"> |
||||
|
<h1>QR en ligne</h1> |
||||
|
<img |
||||
|
src="https://upload.wikimedia.org/wikipedia/commons/thumb/d/d0/QR_code_for_mobile_English_Wikipedia.svg/800px-QR_code_for_mobile_English_Wikipedia.svg.png" |
||||
|
alt="" |
||||
|
/> |
||||
|
</div> |
||||
|
<div class="droite"> |
||||
|
<h1>QR locale</h1> |
||||
|
<img |
||||
|
src="https://upload.wikimedia.org/wikipedia/commons/thumb/d/d0/QR_code_for_mobile_English_Wikipedia.svg/800px-QR_code_for_mobile_English_Wikipedia.svg.png" |
||||
|
alt="" |
||||
|
/> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</body> |
||||
|
</html> |
||||
@ -0,0 +1,74 @@ |
|||||
|
<!doctype html> |
||||
|
<html lang="fr"> |
||||
|
<head> |
||||
|
<meta charset="UTF-8" /> |
||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> |
||||
|
<title>Relevé de Note</title> |
||||
|
<link rel="stylesheet" href="style.css" /> |
||||
|
</head> |
||||
|
<body> |
||||
|
<div class="header"> |
||||
|
<h1>REPOBLIKAN’I MADAGASIKARA</h1> |
||||
|
<h2>Fitiavana-Tanindrazana-Fandrosoana</h2> |
||||
|
<hr /> |
||||
|
<h2>MINISTÈRE DE L’ENSEIGNEMENT SUPÉRIEUR ET DE LA RECHERCHE SCIENTIFIQUE</h2> |
||||
|
<h3>UNIVERSITÉ DE TOAMASINA</h3> |
||||
|
<h3>ECOLE SUPERIEURE POLYTECHNIQUE</h3> |
||||
|
<p>Fahaizaña sy Fañahy</p> |
||||
|
</div> |
||||
|
|
||||
|
<div class="releve"> |
||||
|
<h3>RELEVÉE DE NOTE</h3> |
||||
|
<p><strong>Nom & Prenoms :</strong> F3</p> |
||||
|
<p> |
||||
|
<strong>Niveau :</strong> L1 <span><strong>Année scolaire :</strong> 2022-2023</span> |
||||
|
</p> |
||||
|
<p><strong>N° inscription :</strong> F42</p> |
||||
|
|
||||
|
<table> |
||||
|
<thead> |
||||
|
<tr> |
||||
|
<th>Unités d’Enseignement (UE)</th> |
||||
|
<th>Eléments constitutifs</th> |
||||
|
<th>Crédits</th> |
||||
|
<th>Note</th> |
||||
|
<th>Moyenne</th> |
||||
|
</tr> |
||||
|
</thead> |
||||
|
<tbody> |
||||
|
<tr> |
||||
|
<td>UEMI1</td> |
||||
|
<td>Algèbre</td> |
||||
|
<td>4</td> |
||||
|
<td>F6</td> |
||||
|
<td>F8</td> |
||||
|
</tr> |
||||
|
<tr> |
||||
|
<td>UEMI1</td> |
||||
|
<td>Analyse</td> |
||||
|
<td>5</td> |
||||
|
<td>F5</td> |
||||
|
<td></td> |
||||
|
</tr> |
||||
|
<tr> |
||||
|
<td>UEPI1</td> |
||||
|
<td>Mécanique Général I</td> |
||||
|
<td>4</td> |
||||
|
<td>F9</td> |
||||
|
<td>F13</td> |
||||
|
</tr> |
||||
|
<!-- Add more rows as needed following this structure --> |
||||
|
<tr> |
||||
|
<td colspan="2"><strong>Total crédit</strong></td> |
||||
|
<td colspan="3">30</td> |
||||
|
</tr> |
||||
|
</tbody> |
||||
|
</table> |
||||
|
|
||||
|
<p><strong>Moyenne générale :</strong> F41</p> |
||||
|
<p><strong>Observation :</strong> F47</p> |
||||
|
<p><strong>Décision de jury :</strong> F46</p> |
||||
|
<p><strong>Le Directeur :</strong> RATSIMBAZAFY Christian Pierre</p> |
||||
|
</div> |
||||
|
</body> |
||||
|
</html> |
||||
Loading…
Reference in new issue