resolved
This commit is contained in:
commit
9e9bc98548
27
electron.vite.config.1754936034364.mjs
Normal file
27
electron.vite.config.1754936034364.mjs
Normal file
@ -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
|
||||
};
|
||||
922
src/main/backup.js
Normal file
922
src/main/backup.js
Normal file
@ -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
|
||||
})
|
||||
903
src/main/index.js
Normal file
903
src/main/index.js
Normal file
@ -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
|
||||
})
|
||||
212
src/preload/index.backup.js
Normal file
212
src/preload/index.backup.js
Normal file
@ -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
|
||||
}
|
||||
208
src/preload/index.js
Normal file
208
src/preload/index.js
Normal file
@ -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
|
||||
}
|
||||
17
src/renderer/src/assets/AllStyleComponents.module.css
Normal file
17
src/renderer/src/assets/AllStyleComponents.module.css
Normal file
@ -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%;
|
||||
}
|
||||
283
src/renderer/src/components/AddAnneeScolaire.jsx
Normal file
283
src/renderer/src/components/AddAnneeScolaire.jsx
Normal file
@ -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
|
||||
317
src/renderer/src/components/AddNotes.jsx
Normal file
317
src/renderer/src/components/AddNotes.jsx
Normal file
@ -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
|
||||
1070
src/renderer/src/components/AddStudent.jsx
Normal file
1070
src/renderer/src/components/AddStudent.jsx
Normal file
File diff suppressed because it is too large
Load Diff
319
src/renderer/src/components/AnneeScolaire.jsx
Normal file
319
src/renderer/src/components/AnneeScolaire.jsx
Normal file
@ -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
|
||||
68
src/renderer/src/components/Apropos.jsx
Normal file
68
src/renderer/src/components/Apropos.jsx
Normal file
@ -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
|
||||
527
src/renderer/src/components/ExportEtudiants.jsx
Normal file
527
src/renderer/src/components/ExportEtudiants.jsx
Normal file
@ -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
|
||||
564
src/renderer/src/components/Matieres.jsx
Normal file
564
src/renderer/src/components/Matieres.jsx
Normal file
@ -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
|
||||
220
src/renderer/src/components/ModalExportFichr.jsx
Normal file
220
src/renderer/src/components/ModalExportFichr.jsx
Normal file
@ -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
|
||||
132
src/renderer/src/components/NiveauMatiere.jsx
Normal file
132
src/renderer/src/components/NiveauMatiere.jsx
Normal file
@ -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
|
||||
130
src/renderer/src/components/ParcourMatiere.jsx
Normal file
130
src/renderer/src/components/ParcourMatiere.jsx
Normal file
@ -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
|
||||
199
src/renderer/src/components/Parcours.jsx
Normal file
199
src/renderer/src/components/Parcours.jsx
Normal file
@ -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
|
||||
218
src/renderer/src/components/Resultat.jsx
Normal file
218
src/renderer/src/components/Resultat.jsx
Normal file
@ -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
|
||||
283
src/renderer/src/components/Sidenav.jsx
Normal file
283
src/renderer/src/components/Sidenav.jsx
Normal file
@ -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
|
||||
268
src/renderer/src/components/SingleAnneeScolaire.jsx
Normal file
268
src/renderer/src/components/SingleAnneeScolaire.jsx
Normal file
@ -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
|
||||
1156
src/renderer/src/components/SingleEtudiant.jsx
Normal file
1156
src/renderer/src/components/SingleEtudiant.jsx
Normal file
File diff suppressed because it is too large
Load Diff
584
src/renderer/src/components/Student.jsx
Normal file
584
src/renderer/src/components/Student.jsx
Normal file
@ -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
|
||||
458
src/renderer/src/components/TesteDatagrid.jsx
Normal file
458
src/renderer/src/components/TesteDatagrid.jsx
Normal file
@ -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
|
||||
127
src/renderer/src/components/UpdateTranche.jsx
Normal file
127
src/renderer/src/components/UpdateTranche.jsx
Normal file
@ -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
|
||||
94
src/renderer/src/components/function/GenerateFiche.js
Normal file
94
src/renderer/src/components/function/GenerateFiche.js
Normal file
@ -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()
|
||||
}
|
||||
134
src/renderer/src/components/function/PDFEditor.js
Normal file
134
src/renderer/src/components/function/PDFEditor.js
Normal file
@ -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
|
||||
151
src/renderer/src/components/function/PDFEditorV2.js
Normal file
151
src/renderer/src/components/function/PDFEditorV2.js
Normal file
@ -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)
|
||||
}
|
||||
54
src/renderer/src/test/qr.html
Normal file
54
src/renderer/src/test/qr.html
Normal file
@ -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>
|
||||
74
src/renderer/src/test/relever.html
Normal file
74
src/renderer/src/test/relever.html
Normal file
@ -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
Block a user