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