Compare commits
2 Commits
f1aefc3073
...
9e9bc98548
| Author | SHA1 | Date |
|---|---|---|
|
|
9e9bc98548 | 4 months ago |
|
|
bd0e34d9ad | 4 months ago |
|
Before Width: | Height: | Size: 35 KiB |
|
Before Width: | Height: | Size: 43 KiB |
@ -1,135 +0,0 @@ |
|||||
import { app, shell, BrowserWindow, ipcMain } from 'electron' |
|
||||
import { join } from 'path' |
|
||||
import { electronApp, optimizer, is } from '@electron-toolkit/utils' |
|
||||
import icon from '../../resources/icon.png?asset' |
|
||||
const { loginUser, forgotPassword } = require('../../database/Models/Users') |
|
||||
|
|
||||
let splashWindow |
|
||||
let mainWindow |
|
||||
|
|
||||
// Create splash window
|
|
||||
function createSplashScreen() { |
|
||||
splashWindow = new BrowserWindow({ |
|
||||
width: 400, |
|
||||
height: 300, |
|
||||
frame: false, |
|
||||
alwaysOnTop: true, |
|
||||
transparent: true, |
|
||||
resizable: false, |
|
||||
webPreferences: { |
|
||||
nodeIntegration: true, |
|
||||
contextIsolation: false |
|
||||
} |
|
||||
}) |
|
||||
|
|
||||
splashWindow.loadFile(join(__dirname, '../renderer/splash.html')) |
|
||||
|
|
||||
splashWindow.on('closed', () => { |
|
||||
splashWindow = null |
|
||||
}) |
|
||||
} |
|
||||
|
|
||||
// Create main application window
|
|
||||
function createWindow() { |
|
||||
mainWindow = new BrowserWindow({ |
|
||||
width: 1000, |
|
||||
minWidth: 1000, |
|
||||
height: 670, |
|
||||
minHeight: 670, |
|
||||
show: false, |
|
||||
autoHideMenuBar: true, |
|
||||
fullscreen: true, |
|
||||
...(process.platform === 'linux' ? { icon } : {}), |
|
||||
webPreferences: { |
|
||||
preload: join(__dirname, '../preload/index.js'), |
|
||||
nodeIntegration: true, |
|
||||
contextIsolation: true, |
|
||||
sandbox: false |
|
||||
} |
|
||||
}) |
|
||||
|
|
||||
mainWindow.on('ready-to-show', () => { |
|
||||
if (splashWindow) { |
|
||||
splashWindow.close() // Close splash screen when main window is ready
|
|
||||
} |
|
||||
mainWindow.show() |
|
||||
}) |
|
||||
|
|
||||
mainWindow.webContents.setWindowOpenHandler((details) => { |
|
||||
shell.openExternal(details.url) |
|
||||
return { action: 'deny' } |
|
||||
}) |
|
||||
|
|
||||
// Load the initial content
|
|
||||
if (is.dev && process.env['ELECTRON_RENDERER_URL']) { |
|
||||
mainWindow.loadURL(process.env['ELECTRON_RENDERER_URL']) |
|
||||
} else { |
|
||||
mainWindow.loadFile(join(__dirname, '../renderer/index.html')) |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
// Function to load new content and show the splash screen
|
|
||||
function loadNewContent(contentPath) { |
|
||||
createSplashScreen() // Show splash screen before loading new content
|
|
||||
|
|
||||
mainWindow.loadFile(contentPath).then(() => { |
|
||||
if (splashWindow) { |
|
||||
splashWindow.close() // Close splash screen after loading content
|
|
||||
} |
|
||||
mainWindow.show() // Show main window
|
|
||||
}) |
|
||||
} |
|
||||
|
|
||||
// This method will be called when Electron has finished initialization
|
|
||||
app.whenReady().then(() => { |
|
||||
// Set app user model id for Windows
|
|
||||
electronApp.setAppUserModelId('com.electron') |
|
||||
|
|
||||
// Default open or close DevTools by F12 in development
|
|
||||
app.on('browser-window-created', (_, window) => { |
|
||||
optimizer.watchWindowShortcuts(window) |
|
||||
}) |
|
||||
|
|
||||
// Create initial main window
|
|
||||
createWindow() |
|
||||
|
|
||||
// IPC for loading new content
|
|
||||
ipcMain.on('load-content', (event, contentPath) => { |
|
||||
loadNewContent(contentPath) |
|
||||
}) |
|
||||
|
|
||||
app.on('activate', function () { |
|
||||
if (BrowserWindow.getAllWindows().length === 0) createWindow() |
|
||||
}) |
|
||||
}) |
|
||||
|
|
||||
// Quit when all windows are closed, except on macOS
|
|
||||
app.on('window-all-closed', () => { |
|
||||
if (process.platform !== 'darwin') { |
|
||||
app.quit() |
|
||||
} |
|
||||
}) |
|
||||
|
|
||||
// Event for handling login
|
|
||||
ipcMain.handle('login', async (event, credentials) => { |
|
||||
const { username, password } = credentials |
|
||||
|
|
||||
const users = await loginUser(username, password) |
|
||||
|
|
||||
if (users) { |
|
||||
return { success: true, user: users } |
|
||||
} else { |
|
||||
return { success: false } |
|
||||
} |
|
||||
}) |
|
||||
|
|
||||
// Event for handling forgot password
|
|
||||
ipcMain.handle('forgotPassword', async (event, credentials) => { |
|
||||
const { email, password, passwordConfirmation } = credentials |
|
||||
|
|
||||
const updated = await forgotPassword(email, password, passwordConfirmation) |
|
||||
|
|
||||
if (updated) { |
|
||||
return updated |
|
||||
} |
|
||||
}) |
|
||||
@ -1,18 +0,0 @@ |
|||||
<!doctype html> |
|
||||
<html> |
|
||||
<head> |
|
||||
<meta charset="UTF-8" /> |
|
||||
<title>Université de Toamasina</title> |
|
||||
<link rel="shortcut icon" href="src/assets/logo.ico" type="image/x-icon" /> |
|
||||
<!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP --> |
|
||||
<meta |
|
||||
http-equiv="Content-Security-Policy" |
|
||||
content="default-src 'self'; connect-src 'self' https://api.polytechnique.c4m.mg; script-src 'self' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data:" |
|
||||
/> |
|
||||
</head> |
|
||||
|
|
||||
<body> |
|
||||
<div id="root"></div> |
|
||||
<script type="module" src="/src/main.jsx"></script> |
|
||||
</body> |
|
||||
</html> |
|
||||
@ -1,63 +0,0 @@ |
|||||
import React, { useEffect, useState } from 'react' |
|
||||
import { RouterProvider } from 'react-router-dom' |
|
||||
import Router from './Routes/Routes' |
|
||||
import { AuthContextProvider, useAuthContext } from './contexts/AuthContext' |
|
||||
import { ClipLoader } from 'react-spinners' // Import the loader |
|
||||
import preloader from './assets/preloader.jpg' |
|
||||
import { DataProvider } from './contexts/MoyenneDeClasseContext' |
|
||||
|
|
||||
const App = () => { |
|
||||
const [loading, setLoading] = useState(true) |
|
||||
const { setToken } = useAuthContext() |
|
||||
|
|
||||
// Simulate loading (e.g., fetching some initial data or assets) |
|
||||
useEffect(() => { |
|
||||
const timer = setTimeout(() => { |
|
||||
setLoading(false) // Set loading to false after the simulated loading time |
|
||||
}, 3000) // 3 seconds delay to simulate loading |
|
||||
|
|
||||
return () => clearTimeout(timer) // Cleanup the timer |
|
||||
}, []) |
|
||||
|
|
||||
// Show Preloader while loading, else show your content |
|
||||
if (loading) { |
|
||||
return ( |
|
||||
<div |
|
||||
className="preloader-container" |
|
||||
style={{ |
|
||||
display: 'flex', |
|
||||
justifyContent: 'center', |
|
||||
alignItems: 'center', |
|
||||
height: '100vh', |
|
||||
backgroundImage: `url("${preloader}")`, |
|
||||
backgroundSize: 'cover', |
|
||||
backgroundPosition: 'center', |
|
||||
minHeight: '100vh' |
|
||||
}} |
|
||||
> |
|
||||
<ClipLoader color="blue" loading={loading} size={50} /> |
|
||||
</div> |
|
||||
) |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* condition usign with the Tray icon on the bottom |
|
||||
*/ |
|
||||
if (window.Tray && typeof window.Tray.onNavigate === 'function') { |
|
||||
window.Tray.onNavigate((route) => { |
|
||||
if (route) { |
|
||||
window.location.hash = route // Navigate by updating the hash |
|
||||
} |
|
||||
}) |
|
||||
} |
|
||||
|
|
||||
return ( |
|
||||
<AuthContextProvider> |
|
||||
<DataProvider> |
|
||||
<RouterProvider router={Router} /> |
|
||||
</DataProvider> |
|
||||
</AuthContextProvider> |
|
||||
) |
|
||||
} |
|
||||
|
|
||||
export default App |
|
||||
@ -1,207 +0,0 @@ |
|||||
import { createHashRouter } from 'react-router-dom' |
|
||||
import DefaultLayout from '../layouts/DefaultLayout' |
|
||||
import Login from '../components/Login' |
|
||||
import LoginLayout from '../layouts/LoginLayout' |
|
||||
import NotFound from '../components/NotFound' |
|
||||
import ForgotPassword from '../components/ForgotPassword' |
|
||||
import Home from '../components/Home' |
|
||||
import Student from '../components/Student' |
|
||||
import Notes from '../components/Notes' |
|
||||
import AddStudent from '../components/AddStudent' |
|
||||
import SingleEtudiant from '../components/SingleEtudiant' |
|
||||
import AddNotes from '../components/AddNotes' |
|
||||
import Apropos from '../components/Apropos' |
|
||||
import Niveau from '../components/Niveau' |
|
||||
import AddNiveau from '../components/AddNiveau' |
|
||||
import Admin from '../components/Addadmin' |
|
||||
import Setting from '../components/Param' |
|
||||
import SingleNotes from '../components/SingleNotes' |
|
||||
import ExportEtudiants from '../components/ExportEtudiants' |
|
||||
import SingleNiveau from '../components/singleNiveau' |
|
||||
import Matieres from '../components/Matieres' |
|
||||
import AddMatiere from '../components/AddMatiere' |
|
||||
import ImportMatiere from '../components/ImportMatiere' |
|
||||
import SingleMatiere from '../components/SingleMatiere' |
|
||||
import ImportNiveau from '../components/ImportNiveau' |
|
||||
import SystemeNote from '../components/SystemeNote' |
|
||||
import AnneeScolaire from '../components/AnneeScolaire' |
|
||||
import AddAnneeScolaire from '../components/AddAnneeScolaire' |
|
||||
import Noteclasse from '../components/Noteclasse' |
|
||||
import TesteDatagrid from '../components/TesteDatagrid' |
|
||||
import Manuel from '../components/Manuel' |
|
||||
import Mentions from '../components/Mentions' |
|
||||
import AddMention from '../components/AddMention' |
|
||||
import SinleMention from '../components/SinleMention' |
|
||||
import AssignMatiereToMention from '../components/AssignMatiereToMention' |
|
||||
import AssingMatiereToSemestre from '../components/AssingMatiereToSemestre' |
|
||||
import SingleAnneeScolaire from '../components/SingleAnneeScolaire' |
|
||||
import Parcours from '../components/Parcours' |
|
||||
import ModalExportFichr from '../components/ModalExportFichr' |
|
||||
import Resultat from '../components/Resultat' |
|
||||
import TrancheEcolage from '../components/TrancheEcolage' |
|
||||
|
|
||||
// Use createHashRouter instead of createBrowserRouter because the desktop app is in local machine |
|
||||
const Router = createHashRouter([ |
|
||||
{ |
|
||||
path: '/', |
|
||||
element: <DefaultLayout />, |
|
||||
children: [ |
|
||||
{ |
|
||||
path: '/', // This will now be accessed as #/ |
|
||||
element: <Home /> |
|
||||
}, |
|
||||
{ |
|
||||
path: '/student', |
|
||||
element: <Student /> |
|
||||
}, |
|
||||
{ |
|
||||
path: '/notes', |
|
||||
element: <Notes /> |
|
||||
}, |
|
||||
{ |
|
||||
path: '/addstudent', |
|
||||
element: <AddStudent /> |
|
||||
}, |
|
||||
{ |
|
||||
path: '/single/:id', |
|
||||
element: <SingleEtudiant /> |
|
||||
}, |
|
||||
{ |
|
||||
path: '/addnotes/:id/:niveau/:mention_id/:parcours', |
|
||||
element: <AddNotes /> |
|
||||
}, |
|
||||
{ |
|
||||
path: '/anneescolaire/:id', |
|
||||
element: <SingleAnneeScolaire /> |
|
||||
}, |
|
||||
{ |
|
||||
path: '/asignmatiere/:id', |
|
||||
element: <AssignMatiereToMention /> |
|
||||
}, |
|
||||
{ |
|
||||
path: '/asignmatieresemestre/:id', |
|
||||
element: <AssingMatiereToSemestre /> |
|
||||
}, |
|
||||
{ |
|
||||
path: '/manual', |
|
||||
element: <Manuel /> |
|
||||
}, |
|
||||
{ |
|
||||
path: '/mention', |
|
||||
element: <Mentions /> |
|
||||
}, |
|
||||
{ |
|
||||
path: '/addmention', |
|
||||
element: <AddMention /> |
|
||||
}, |
|
||||
{ |
|
||||
path: '/singlemention/:id', |
|
||||
element: <SinleMention /> |
|
||||
}, |
|
||||
{ |
|
||||
path: '/apropos', |
|
||||
element: <Apropos /> |
|
||||
}, |
|
||||
{ |
|
||||
path: '/niveau', |
|
||||
element: <Niveau /> |
|
||||
}, |
|
||||
{ |
|
||||
path: '/addniveau', |
|
||||
element: <AddNiveau /> |
|
||||
}, |
|
||||
{ |
|
||||
path: '/para', |
|
||||
element: <Setting /> |
|
||||
}, |
|
||||
{ |
|
||||
path: '/admin', |
|
||||
element: <Admin /> |
|
||||
}, |
|
||||
{ |
|
||||
path: '/single/notes/:id/:niveau/:scolaire', |
|
||||
element: <SingleNotes /> |
|
||||
}, |
|
||||
{ |
|
||||
path: '/exportetudiant', |
|
||||
element: <ExportEtudiants /> |
|
||||
}, |
|
||||
{ |
|
||||
path: '/single/niveau/:id', |
|
||||
element: <SingleNiveau /> |
|
||||
}, |
|
||||
{ |
|
||||
path: '/matiere', |
|
||||
element: <Matieres /> |
|
||||
}, |
|
||||
{ |
|
||||
path: '/addmatiere', |
|
||||
element: <AddMatiere /> |
|
||||
}, |
|
||||
{ |
|
||||
path: '/addmatiere/import', |
|
||||
element: <ImportMatiere /> |
|
||||
}, |
|
||||
{ |
|
||||
path: '/singlematiere/:id', |
|
||||
element: <SingleMatiere /> |
|
||||
}, |
|
||||
{ |
|
||||
path: '/importniveau', |
|
||||
element: <ImportNiveau /> |
|
||||
}, |
|
||||
{ |
|
||||
path: '/systemenote', |
|
||||
element: <SystemeNote /> |
|
||||
}, |
|
||||
{ |
|
||||
path: '/anneescolaire', |
|
||||
element: <AnneeScolaire /> |
|
||||
}, |
|
||||
{ |
|
||||
path: '/addanneescolaire', |
|
||||
element: <AddAnneeScolaire /> |
|
||||
}, |
|
||||
{ |
|
||||
path: '/noteclass/:niveau/:scolaire', |
|
||||
element: <Noteclasse /> |
|
||||
}, |
|
||||
{ |
|
||||
path: '/parcours', |
|
||||
element: <Parcours /> |
|
||||
}, |
|
||||
{ |
|
||||
path: '/fiche/:matiere_id/:nom', |
|
||||
element: <ModalExportFichr /> |
|
||||
}, |
|
||||
{ |
|
||||
path: '/resultat/:niveau/:scolaire', |
|
||||
element: <Resultat /> |
|
||||
}, |
|
||||
{ |
|
||||
path: '/tranche/:id', |
|
||||
element: <TrancheEcolage /> |
|
||||
} |
|
||||
] |
|
||||
}, |
|
||||
{ |
|
||||
path: '/', |
|
||||
element: <LoginLayout />, |
|
||||
children: [ |
|
||||
{ |
|
||||
path: '/login', |
|
||||
element: <Login /> |
|
||||
}, |
|
||||
{ |
|
||||
path: '/forgotpassword', |
|
||||
element: <ForgotPassword /> |
|
||||
} |
|
||||
] |
|
||||
}, |
|
||||
{ |
|
||||
path: '/*', |
|
||||
element: <NotFound /> |
|
||||
} |
|
||||
]) |
|
||||
|
|
||||
export default Router |
|
||||
@ -1,12 +0,0 @@ |
|||||
.blockTitle h1 { |
|
||||
font-size: 10px; |
|
||||
font-weight: bold; |
|
||||
color: white; |
|
||||
display: flex; |
|
||||
align-items: center; |
|
||||
gap: 10px; |
|
||||
} |
|
||||
.boxEtudiantsCard { |
|
||||
width: 10%; |
|
||||
padding: 1%; |
|
||||
} |
|
||||
@ -1,15 +0,0 @@ |
|||||
.blockTitle h1 { |
|
||||
font-size: 20px; |
|
||||
font-weight: bold; |
|
||||
color: white; |
|
||||
display: flex; |
|
||||
align-items: center; |
|
||||
gap: 10px; |
|
||||
} |
|
||||
.boxEtudiantsCard { |
|
||||
width: 100%; |
|
||||
padding: 1%; |
|
||||
display: flex; |
|
||||
align-items: center; |
|
||||
justify-content: center; |
|
||||
} |
|
||||
|
Before Width: | Height: | Size: 8.9 KiB |
@ -1,86 +0,0 @@ |
|||||
body { |
|
||||
margin: 0; |
|
||||
padding: 0; |
|
||||
} |
|
||||
.container { |
|
||||
display: flex; |
|
||||
align-items: center; |
|
||||
justify-content: center; |
|
||||
height: 100vh; |
|
||||
gap: 20px; |
|
||||
} |
|
||||
.cart { |
|
||||
width: 33%; |
|
||||
/* height: 35%; */ |
|
||||
border: solid 1px rgba(0, 0, 0, 0.315); |
|
||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.8); /* Adds a soft shadow */ |
|
||||
/* border-radius: 10px; */ |
|
||||
padding: 1px; |
|
||||
background-color: #ffded4; |
|
||||
position: relative; |
|
||||
} |
|
||||
.cart-footer { |
|
||||
position: absolute; |
|
||||
height: 40px; |
|
||||
width: 100%; |
|
||||
bottom: 0; |
|
||||
left: 0; |
|
||||
border-bottom-left-radius: 10px; |
|
||||
border-bottom-right-radius: 10px; |
|
||||
background-color: #ff5a27; |
|
||||
} |
|
||||
.title { |
|
||||
margin: 0; |
|
||||
padding: 0; |
|
||||
text-align: center; |
|
||||
text-transform: uppercase; |
|
||||
z-index: 1; |
|
||||
} |
|
||||
.title h1, |
|
||||
p { |
|
||||
margin: 0; |
|
||||
font-size: 15px; |
|
||||
} |
|
||||
.content { |
|
||||
z-index: 1; |
|
||||
height: 70%; |
|
||||
display: flex; |
|
||||
align-items: center; |
|
||||
justify-content: space-between; |
|
||||
} |
|
||||
.cart_photos { |
|
||||
width: 30%; |
|
||||
text-align: center; |
|
||||
} |
|
||||
.cart_photos img { |
|
||||
border-radius: 50px; |
|
||||
border: solid 2px #ff5a27; |
|
||||
width: 95px; |
|
||||
height: 100px; |
|
||||
object-fit: cover; |
|
||||
} |
|
||||
.cart_info { |
|
||||
width: 60%; |
|
||||
padding-left: 1%; |
|
||||
} |
|
||||
.cart_info p { |
|
||||
font-size: 16px; |
|
||||
} |
|
||||
.qrContent { |
|
||||
display: flex; |
|
||||
align-items: center; |
|
||||
justify-content: space-around; |
|
||||
padding: 2%; |
|
||||
} |
|
||||
.gauche h1, |
|
||||
.droite h1 { |
|
||||
font-size: 17px; |
|
||||
text-align: center; |
|
||||
} |
|
||||
.gauche img, |
|
||||
.droite img { |
|
||||
width: 150px; |
|
||||
} |
|
||||
.droite .qrcodeDroite { |
|
||||
width: 150px; |
|
||||
} |
|
||||
@ -1,4 +0,0 @@ |
|||||
.display { |
|
||||
background-color: rgba(255, 255, 255, 0.9); |
|
||||
/* margin-bottom: 2%; */ |
|
||||
} |
|
||||
@ -1,20 +0,0 @@ |
|||||
html, |
|
||||
body { |
|
||||
height: 100%; |
|
||||
margin: 0; |
|
||||
padding: 0; |
|
||||
} |
|
||||
|
|
||||
/* DefaultLayout.css */ |
|
||||
.default_layout { |
|
||||
/* height: 100%; Use full viewport height */ |
|
||||
min-height: 100vh; |
|
||||
overflow-y: auto; |
|
||||
background: url('../assets/background2.jpg') no-repeat center/cover; |
|
||||
/* background-color: #aad4e571; */ |
|
||||
} |
|
||||
.outlet { |
|
||||
padding-left: 55px; |
|
||||
margin-top: 0; |
|
||||
height: 100%; |
|
||||
} |
|
||||
@ -1,78 +0,0 @@ |
|||||
.header { |
|
||||
width: 100%; |
|
||||
display: flex; |
|
||||
align-items: start; |
|
||||
justify-content: space-between; |
|
||||
} |
|
||||
.headerCenter { |
|
||||
text-align: center; |
|
||||
color: black; |
|
||||
line-height: 15px; |
|
||||
width: 60%; |
|
||||
} |
|
||||
.headerCenter h5 { |
|
||||
text-transform: uppercase; |
|
||||
line-height: 12px; |
|
||||
font-size: 14px; |
|
||||
} |
|
||||
.headerCenter p { |
|
||||
font-style: italic; |
|
||||
font-weight: 400; |
|
||||
font-size: 14px; |
|
||||
} |
|
||||
.transition { |
|
||||
border: solid 1px gray; |
|
||||
width: 100%; |
|
||||
padding: 1% 2%; |
|
||||
font-weight: bold; |
|
||||
} |
|
||||
.transition h6 { |
|
||||
font-size: 12px; |
|
||||
} |
|
||||
|
|
||||
/* content */ |
|
||||
.content { |
|
||||
text-align: center; |
|
||||
} |
|
||||
.contentHeader { |
|
||||
color: black; |
|
||||
flex-direction: column; |
|
||||
display: flex; |
|
||||
align-items: center; |
|
||||
justify-content: center; |
|
||||
font-size: 13px; |
|
||||
} |
|
||||
.contentHeader h1 { |
|
||||
font-size: 28px; |
|
||||
} |
|
||||
.contentHeaderList { |
|
||||
font-weight: bold; |
|
||||
font-size: 13px; |
|
||||
} |
|
||||
.contentHeaderList p { |
|
||||
font-size: 13px; |
|
||||
display: flex; |
|
||||
gap: 30px; |
|
||||
} |
|
||||
.ContentTable { |
|
||||
display: flex; |
|
||||
align-items: center; |
|
||||
justify-content: center; |
|
||||
} |
|
||||
.ContentTable table { |
|
||||
border-color: black !important; |
|
||||
font-size: 13px; |
|
||||
margin: 0; |
|
||||
} |
|
||||
.contentFooter { |
|
||||
display: flex; |
|
||||
align-items: end; |
|
||||
flex-direction: column; |
|
||||
margin-top: 5px; |
|
||||
} |
|
||||
.signature { |
|
||||
width: 50%; |
|
||||
display: flex; |
|
||||
flex-direction: column; |
|
||||
gap: 30px; |
|
||||
} |
|
||||
@ -1,78 +0,0 @@ |
|||||
.boxEtudiantsCard { |
|
||||
width: 100%; |
|
||||
padding: 1%; |
|
||||
/* margin-top: 12%; */ |
|
||||
} |
|
||||
.cards { |
|
||||
/* width: 30%; */ |
|
||||
justify-content: space-between; |
|
||||
align-items: center; |
|
||||
padding: 0 1%; |
|
||||
} |
|
||||
.nomEtudiants { |
|
||||
font-weight: bold; |
|
||||
} |
|
||||
.header { |
|
||||
color: white; |
|
||||
/* backdrop-filter: blur(5px); */ |
|
||||
/* position: fixed; */ |
|
||||
width: 100%; |
|
||||
z-index: 9; |
|
||||
} |
|
||||
.container { |
|
||||
display: flex; |
|
||||
align-items: center; |
|
||||
justify-content: center; |
|
||||
padding: 1% 2%; |
|
||||
background: linear-gradient(to left, #ffaf01b4, transparent); |
|
||||
} |
|
||||
.blockTitle { |
|
||||
display: flex; |
|
||||
align-items: center; |
|
||||
justify-content: space-between; |
|
||||
padding: 0 3%; |
|
||||
} |
|
||||
.blockTitle h1 { |
|
||||
font-size: 20px; |
|
||||
font-weight: bold; |
|
||||
/* text-transform: uppercase; */ |
|
||||
color: white; |
|
||||
} |
|
||||
.blockTitle button { |
|
||||
font-size: 12px; |
|
||||
display: inline-flex; |
|
||||
gap: 10px; |
|
||||
} |
|
||||
.boxImg { |
|
||||
padding: 2%; |
|
||||
display: flex; |
|
||||
align-items: center; |
|
||||
justify-content: center; |
|
||||
} |
|
||||
.imagePDP { |
|
||||
object-fit: cover; |
|
||||
border: solid 2px rgb(156, 39, 176); |
|
||||
} |
|
||||
.mainHome { |
|
||||
border: solid 1px red; |
|
||||
} |
|
||||
.select:hover { |
|
||||
border-color: rgb(156, 39, 176) !important; |
|
||||
} |
|
||||
.details { |
|
||||
background-color: rgba(128, 128, 128, 0.4); |
|
||||
border-radius: 8px; |
|
||||
padding: 3% 1% 3% 4%; |
|
||||
text-align: left; |
|
||||
} |
|
||||
.details span { |
|
||||
display: block; |
|
||||
line-height: 25px; |
|
||||
} |
|
||||
.cardOverflow { |
|
||||
height: 450px; |
|
||||
transition: 1s ease; |
|
||||
} |
|
||||
.cardOverflow:hover { |
|
||||
transform: scale(1.03); |
|
||||
} |
|
||||
@ -1,20 +0,0 @@ |
|||||
/* .container{ |
|
||||
background: url(bg2.jpg) no-repeat fixed center/cover; |
|
||||
background-color: #08000e; |
|
||||
} */ |
|
||||
.cards { |
|
||||
background-color: #06000aa4 !important; |
|
||||
color: white !important; |
|
||||
box-shadow: 0px 4px 10px rgba(255, 255, 255, 0.2) !important; /* Light white shadow */ |
|
||||
border: solid 1px rgba(245, 245, 245, 0.692); |
|
||||
} |
|
||||
.formulaireLogin { |
|
||||
color: white !important; |
|
||||
} |
|
||||
.formulaireLogin label { |
|
||||
color: white; |
|
||||
font-size: 17px; |
|
||||
} |
|
||||
.formulaireLogin input { |
|
||||
color: white; |
|
||||
} |
|
||||
@ -1,25 +0,0 @@ |
|||||
.navnar { |
|
||||
color: rgba(0, 0, 0, 0.7); |
|
||||
background-color: #2d2d2d; |
|
||||
display: flex; |
|
||||
align-items: center; |
|
||||
justify-content: space-between; |
|
||||
padding: 2px 15px; |
|
||||
position: fixed; |
|
||||
width: 100%; |
|
||||
top: 0; |
|
||||
border-bottom: solid 1px white; |
|
||||
color: white; |
|
||||
z-index: 99999; |
|
||||
} |
|
||||
.gauche { |
|
||||
width: 30%; |
|
||||
display: flex; |
|
||||
align-items: center; |
|
||||
} |
|
||||
.droite { |
|
||||
width: 4%; |
|
||||
display: flex; |
|
||||
align-items: center; |
|
||||
justify-content: space-between; |
|
||||
} |
|
||||
@ -1,51 +0,0 @@ |
|||||
.navbar { |
|
||||
background-color: #2d2d2d; |
|
||||
border-top: solid 1px white; |
|
||||
color: white; |
|
||||
width: 50px; |
|
||||
/* top: 28px; */ |
|
||||
position: fixed; |
|
||||
height: 100%; |
|
||||
/* bottom: 0; */ |
|
||||
display: flex; |
|
||||
align-items: center; |
|
||||
justify-content: space-between; |
|
||||
flex-direction: column; |
|
||||
padding: 0 0 5% 0; |
|
||||
margin: 0; |
|
||||
border-right: solid 1px white; |
|
||||
z-index: 99; |
|
||||
} |
|
||||
.liste1, |
|
||||
.liste2 { |
|
||||
list-style: none; |
|
||||
padding: 0; |
|
||||
margin: 0; |
|
||||
display: flex; |
|
||||
flex-direction: column; |
|
||||
gap: 20px; |
|
||||
} |
|
||||
.liste1 li, |
|
||||
.liste2 li { |
|
||||
font-size: 25px; |
|
||||
cursor: pointer; |
|
||||
} |
|
||||
.nav_link { |
|
||||
color: white; |
|
||||
position: relative; |
|
||||
transition: 0.8s ease; |
|
||||
} |
|
||||
.icon_label { |
|
||||
position: absolute; |
|
||||
top: 0; /* Below the icon */ |
|
||||
left: 60px; |
|
||||
transform: translateX(-50%); |
|
||||
background-color: black; |
|
||||
color: white; |
|
||||
padding: 5px; |
|
||||
border-radius: 4px; |
|
||||
white-space: nowrap; /* Keep the label on one line */ |
|
||||
font-size: 12px; |
|
||||
margin-top: 5px; /* Slight gap between icon and label */ |
|
||||
z-index: 1; |
|
||||
} |
|
||||
|
Before Width: | Height: | Size: 7.4 KiB |
|
Before Width: | Height: | Size: 290 KiB |
|
Before Width: | Height: | Size: 14 MiB |
@ -1,97 +0,0 @@ |
|||||
/* :root { |
|
||||
--ev-c-white: #ffffff; |
|
||||
--ev-c-white-soft: #f8f8f8; |
|
||||
--ev-c-white-mute: #f2f2f2; |
|
||||
|
|
||||
--ev-c-black: #1b1b1f; |
|
||||
--ev-c-black-soft: #222222; |
|
||||
--ev-c-black-mute: #282828; |
|
||||
|
|
||||
--ev-c-gray-1: #515c67; |
|
||||
--ev-c-gray-2: #414853; |
|
||||
--ev-c-gray-3: #32363f; |
|
||||
|
|
||||
--ev-c-text-1: rgba(255, 255, 245, 0.86); |
|
||||
--ev-c-text-2: rgba(235, 235, 245, 0.6); |
|
||||
--ev-c-text-3: rgba(235, 235, 245, 0.38); |
|
||||
|
|
||||
--ev-button-alt-border: transparent; |
|
||||
--ev-button-alt-text: var(--ev-c-text-1); |
|
||||
--ev-button-alt-bg: var(--ev-c-gray-3); |
|
||||
--ev-button-alt-hover-border: transparent; |
|
||||
--ev-button-alt-hover-text: var(--ev-c-text-1); |
|
||||
--ev-button-alt-hover-bg: var(--ev-c-gray-2); |
|
||||
} |
|
||||
|
|
||||
:root { |
|
||||
--color-background: var(--ev-c-black); |
|
||||
--color-background-soft: var(--ev-c-black-soft); |
|
||||
--color-background-mute: var(--ev-c-black-mute); |
|
||||
|
|
||||
--color-text: var(--ev-c-text-1); |
|
||||
} |
|
||||
|
|
||||
*, |
|
||||
*::before, |
|
||||
*::after { |
|
||||
box-sizing: border-box; |
|
||||
margin: 0; |
|
||||
font-weight: normal; |
|
||||
} |
|
||||
|
|
||||
ul { |
|
||||
list-style: none; |
|
||||
} |
|
||||
|
|
||||
body { |
|
||||
min-height: 100vh; |
|
||||
color: var(--color-text); |
|
||||
background: var(--color-background); |
|
||||
line-height: 1.6; |
|
||||
font-family: |
|
||||
Inter, |
|
||||
-apple-system, |
|
||||
BlinkMacSystemFont, |
|
||||
'Segoe UI', |
|
||||
Roboto, |
|
||||
Oxygen, |
|
||||
Ubuntu, |
|
||||
Cantarell, |
|
||||
'Fira Sans', |
|
||||
'Droid Sans', |
|
||||
'Helvetica Neue', |
|
||||
sans-serif; |
|
||||
text-rendering: optimizeLegibility; |
|
||||
-webkit-font-smoothing: antialiased; |
|
||||
-moz-osx-font-smoothing: grayscale; |
|
||||
} */ |
|
||||
.form-control:focus { |
|
||||
outline: none; |
|
||||
box-shadow: none; |
|
||||
border-color: initial; /* Optional: restore the default border color */ |
|
||||
} |
|
||||
table { |
|
||||
border-collapse: collapse; /* Ensure there is no space between table cells */ |
|
||||
width: 100%; /* Adjust width as needed */ |
|
||||
margin: 0; |
|
||||
padding: 0; |
|
||||
} |
|
||||
|
|
||||
td, |
|
||||
th { |
|
||||
padding: 0; /* Remove padding from table cells */ |
|
||||
margin: 0; /* Ensure no margin inside cells */ |
|
||||
border: 1px solid black; /* Optional: Add border if needed */ |
|
||||
text-align: center; /* Center text horizontally */ |
|
||||
vertical-align: middle; /* Center text vertically */ |
|
||||
} |
|
||||
|
|
||||
tr { |
|
||||
margin: 0; |
|
||||
padding: 0; |
|
||||
} |
|
||||
|
|
||||
tbody { |
|
||||
margin: 0; |
|
||||
padding: 0; |
|
||||
} |
|
||||
|
Before Width: | Height: | Size: 1.4 MiB |
|
Before Width: | Height: | Size: 7.9 MiB |
|
Before Width: | Height: | Size: 2.6 MiB |
|
Before Width: | Height: | Size: 24 MiB |
|
Before Width: | Height: | Size: 5.7 KiB |
|
Before Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 863 B |
|
Before Width: | Height: | Size: 156 KiB |
|
Before Width: | Height: | Size: 69 KiB |
|
Before Width: | Height: | Size: 7.2 KiB |
|
Before Width: | Height: | Size: 43 KiB |
|
Before Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 9.8 KiB |
|
Before Width: | Height: | Size: 5.8 KiB |
@ -1,163 +0,0 @@ |
|||||
@import './base.css'; |
|
||||
|
|
||||
body { |
|
||||
display: flex; |
|
||||
align-items: center; |
|
||||
justify-content: center; |
|
||||
overflow: hidden; |
|
||||
background-image: url('./wavy-lines.svg'); |
|
||||
background-size: cover; |
|
||||
user-select: none; |
|
||||
} |
|
||||
|
|
||||
code { |
|
||||
font-weight: 600; |
|
||||
padding: 3px 5px; |
|
||||
border-radius: 2px; |
|
||||
background-color: var(--color-background-mute); |
|
||||
font-family: |
|
||||
ui-monospace, |
|
||||
SFMono-Regular, |
|
||||
SF Mono, |
|
||||
Menlo, |
|
||||
Consolas, |
|
||||
Liberation Mono, |
|
||||
monospace; |
|
||||
font-size: 85%; |
|
||||
} |
|
||||
|
|
||||
#root { |
|
||||
display: flex; |
|
||||
align-items: center; |
|
||||
justify-content: center; |
|
||||
flex-direction: column; |
|
||||
margin-bottom: 80px; |
|
||||
} |
|
||||
|
|
||||
.logo { |
|
||||
margin-bottom: 20px; |
|
||||
-webkit-user-drag: none; |
|
||||
height: 128px; |
|
||||
width: 128px; |
|
||||
will-change: filter; |
|
||||
transition: filter 300ms; |
|
||||
} |
|
||||
|
|
||||
.logo:hover { |
|
||||
filter: drop-shadow(0 0 1.2em #6988e6aa); |
|
||||
} |
|
||||
|
|
||||
.creator { |
|
||||
font-size: 14px; |
|
||||
line-height: 16px; |
|
||||
color: var(--ev-c-text-2); |
|
||||
font-weight: 600; |
|
||||
margin-bottom: 10px; |
|
||||
} |
|
||||
|
|
||||
.text { |
|
||||
font-size: 28px; |
|
||||
color: var(--ev-c-text-1); |
|
||||
font-weight: 700; |
|
||||
line-height: 32px; |
|
||||
text-align: center; |
|
||||
margin: 0 10px; |
|
||||
padding: 16px 0; |
|
||||
} |
|
||||
|
|
||||
.tip { |
|
||||
font-size: 16px; |
|
||||
line-height: 24px; |
|
||||
color: var(--ev-c-text-2); |
|
||||
font-weight: 600; |
|
||||
} |
|
||||
|
|
||||
.react { |
|
||||
background: -webkit-linear-gradient(315deg, #087ea4 55%, #7c93ee); |
|
||||
background-clip: text; |
|
||||
-webkit-background-clip: text; |
|
||||
-webkit-text-fill-color: transparent; |
|
||||
font-weight: 700; |
|
||||
} |
|
||||
|
|
||||
.actions { |
|
||||
display: flex; |
|
||||
padding-top: 32px; |
|
||||
margin: -6px; |
|
||||
flex-wrap: wrap; |
|
||||
justify-content: flex-start; |
|
||||
} |
|
||||
|
|
||||
.action { |
|
||||
flex-shrink: 0; |
|
||||
padding: 6px; |
|
||||
} |
|
||||
|
|
||||
.action a { |
|
||||
cursor: pointer; |
|
||||
text-decoration: none; |
|
||||
display: inline-block; |
|
||||
border: 1px solid transparent; |
|
||||
text-align: center; |
|
||||
font-weight: 600; |
|
||||
white-space: nowrap; |
|
||||
border-radius: 20px; |
|
||||
padding: 0 20px; |
|
||||
line-height: 38px; |
|
||||
font-size: 14px; |
|
||||
border-color: var(--ev-button-alt-border); |
|
||||
color: var(--ev-button-alt-text); |
|
||||
background-color: var(--ev-button-alt-bg); |
|
||||
} |
|
||||
|
|
||||
.action a:hover { |
|
||||
border-color: var(--ev-button-alt-hover-border); |
|
||||
color: var(--ev-button-alt-hover-text); |
|
||||
background-color: var(--ev-button-alt-hover-bg); |
|
||||
} |
|
||||
|
|
||||
.versions { |
|
||||
position: absolute; |
|
||||
bottom: 30px; |
|
||||
margin: 0 auto; |
|
||||
padding: 15px 0; |
|
||||
font-family: 'Menlo', 'Lucida Console', monospace; |
|
||||
display: inline-flex; |
|
||||
overflow: hidden; |
|
||||
align-items: center; |
|
||||
border-radius: 22px; |
|
||||
background-color: #202127; |
|
||||
backdrop-filter: blur(24px); |
|
||||
} |
|
||||
|
|
||||
.versions li { |
|
||||
display: block; |
|
||||
float: left; |
|
||||
border-right: 1px solid var(--ev-c-gray-1); |
|
||||
padding: 0 20px; |
|
||||
font-size: 14px; |
|
||||
line-height: 14px; |
|
||||
opacity: 0.8; |
|
||||
&:last-child { |
|
||||
border: none; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
@media (max-width: 720px) { |
|
||||
.text { |
|
||||
font-size: 20px; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
@media (max-width: 620px) { |
|
||||
.versions { |
|
||||
display: none; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
@media (max-width: 350px) { |
|
||||
.tip, |
|
||||
.actions { |
|
||||
display: none; |
|
||||
} |
|
||||
} |
|
||||
|
Before Width: | Height: | Size: 5.3 KiB |
|
Before Width: | Height: | Size: 454 KiB |
|
Before Width: | Height: | Size: 871 B |
|
Before Width: | Height: | Size: 96 KiB |
|
Before Width: | Height: | Size: 174 KiB |
|
Before Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 1.7 KiB |
@ -1,413 +0,0 @@ |
|||||
import React, { useEffect, useRef, useState } from 'react' |
|
||||
import classe from '../assets/AllStyleComponents.module.css' |
|
||||
import classeAdd from '../assets/AddStudent.module.css' |
|
||||
import classeHome from '../assets/Home.module.css' |
|
||||
import { Link, useNavigate } from 'react-router-dom' |
|
||||
import { IoMdReturnRight } from 'react-icons/io' |
|
||||
import { |
|
||||
Box, |
|
||||
Button, |
|
||||
InputAdornment, |
|
||||
Typography, |
|
||||
Modal, |
|
||||
TextField, |
|
||||
Grid, |
|
||||
Autocomplete |
|
||||
} from '@mui/material' |
|
||||
import { BsBookmarkPlusFill } from 'react-icons/bs' |
|
||||
import { FaBook, FaClipboardList, FaClock, FaFileExcel } from 'react-icons/fa' |
|
||||
import validationMatiereAdd from './validation/ValidationMatiereAdd' |
|
||||
import svgSuccess from '../assets/success.svg' |
|
||||
import ChangeCapitalize from './function/ChangeCapitalizeLetter' |
|
||||
import { MdOutlineNumbers } from 'react-icons/md' |
|
||||
import { IoBookmark } from 'react-icons/io5' |
|
||||
import ChangeCapital from './function/ChangeCapitalLetter' |
|
||||
import { CgPathUnite } from 'react-icons/cg' |
|
||||
|
|
||||
const AddMatiere = () => { |
|
||||
const [formData, setFormData] = useState({ |
|
||||
nom: '', |
|
||||
credit: '', |
|
||||
uniter: '', |
|
||||
ue: '' |
|
||||
}) |
|
||||
|
|
||||
const navigate = useNavigate() |
|
||||
|
|
||||
const [matieres, setMatieres] = useState([]) |
|
||||
const [mention, setMention] = useState([]) |
|
||||
|
|
||||
useEffect(() => { |
|
||||
window.matieres.getMatiere().then((response) => { |
|
||||
setMatieres(response) |
|
||||
}) |
|
||||
|
|
||||
window.mention.getMention().then((response) => { |
|
||||
setMention(Array.isArray(response) ? response : []) |
|
||||
}) |
|
||||
}, []) |
|
||||
|
|
||||
/** |
|
||||
* hook to open modal |
|
||||
*/ |
|
||||
const [open, setOpen] = useState(false) |
|
||||
|
|
||||
/** |
|
||||
* function to close modal |
|
||||
*/ |
|
||||
const handleClose = () => { |
|
||||
setOpen(false) |
|
||||
navigate('/matiere') |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* function to set the data in state |
|
||||
* @param {*} e |
|
||||
*/ |
|
||||
const handleInputChange = (e) => { |
|
||||
const { name, value } = e.target |
|
||||
setFormData((prevData) => ({ |
|
||||
...prevData, |
|
||||
[name]: value |
|
||||
})) |
|
||||
} |
|
||||
|
|
||||
const nomRef = useRef() |
|
||||
const errorRef = useRef() |
|
||||
const creditRef = useRef() |
|
||||
const ueRef = useRef() |
|
||||
const uniterRef = useRef() |
|
||||
|
|
||||
const formSubmit = async (e) => { |
|
||||
e.preventDefault() |
|
||||
|
|
||||
let arrayMatiere = [] |
|
||||
|
|
||||
matieres.map((matiere) => { |
|
||||
arrayMatiere.push(matiere.nom) |
|
||||
}) |
|
||||
|
|
||||
let valid = validationMatiereAdd( |
|
||||
nomRef.current, |
|
||||
errorRef.current, |
|
||||
arrayMatiere, |
|
||||
creditRef.current |
|
||||
) |
|
||||
console.log(formData, valid) |
|
||||
if (valid) { |
|
||||
let response = await window.matieres.createMatiere(formData) |
|
||||
console.log(response) |
|
||||
if (response.success) { |
|
||||
setOpen(true) |
|
||||
} |
|
||||
|
|
||||
if (response.code) { |
|
||||
errorRef.current.textContent = `${formData.nom} existe déjà` |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* function to return the view Modal |
|
||||
* |
|
||||
* @returns {JSX} |
|
||||
*/ |
|
||||
const modals = () => ( |
|
||||
<Modal |
|
||||
open={open} |
|
||||
onClose={handleClose} |
|
||||
aria-labelledby="modal-title" |
|
||||
aria-describedby="modal-description" |
|
||||
> |
|
||||
<Box |
|
||||
sx={{ |
|
||||
position: 'absolute', |
|
||||
top: '50%', |
|
||||
left: '50%', |
|
||||
transform: 'translate(-50%, -50%)', |
|
||||
width: 450, |
|
||||
bgcolor: 'background.paper', |
|
||||
boxShadow: 24, |
|
||||
p: 4 |
|
||||
}} |
|
||||
> |
|
||||
<Typography style={{ display: 'flex', flexDirection: 'column', alignItems: 'center' }}> |
|
||||
<img src={svgSuccess} alt="" width={50} height={50} />{' '} |
|
||||
<span>Matière insérer avec succes</span> |
|
||||
</Typography> |
|
||||
<Box |
|
||||
sx={{ |
|
||||
marginTop: '2%', |
|
||||
display: 'flex', |
|
||||
gap: '20px', |
|
||||
alignItems: 'end', |
|
||||
justifyContent: 'flex-end' |
|
||||
}} |
|
||||
> |
|
||||
<Button onClick={handleClose} color="warning" variant="contained"> |
|
||||
OK |
|
||||
</Button> |
|
||||
</Box> |
|
||||
</Box> |
|
||||
</Modal> |
|
||||
) |
|
||||
|
|
||||
return ( |
|
||||
<div className={classe.mainHome}> |
|
||||
{modals()} |
|
||||
<div className={classeAdd.header}> |
|
||||
<div className={classe.h1style}> |
|
||||
<div className={classeHome.blockTitle}> |
|
||||
<h1 style={{ display: 'flex', alignItems: 'center', gap: '10px' }}> |
|
||||
<BsBookmarkPlusFill /> |
|
||||
Ajout Matière |
|
||||
</h1> |
|
||||
<Link to={'/matiere'}> |
|
||||
<Button color="warning" variant="contained"> |
|
||||
<IoMdReturnRight style={{ fontSize: '20px' }} /> |
|
||||
</Button> |
|
||||
</Link> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
|
|
||||
{/* displaying the formulaire */} |
|
||||
<div className={classeHome.boxEtudiantsCard}> |
|
||||
<Box |
|
||||
sx={{ |
|
||||
position: 'absolute', |
|
||||
top: '55%', |
|
||||
left: '50%', |
|
||||
transform: 'translate(-50%, -50%)', |
|
||||
width: 700, |
|
||||
borderRadius: '2%', |
|
||||
bgcolor: 'background.paper', |
|
||||
boxShadow: 24, |
|
||||
p: 4 |
|
||||
}} |
|
||||
> |
|
||||
<div style={{ textAlign: 'right', fontWeight: 'bold' }}> |
|
||||
<Link to={'/addmatiere/import'} style={{ textDecoration: 'none', color: 'orange' }}> |
|
||||
Importer un fichier excel <FaFileExcel /> |
|
||||
</Link> |
|
||||
</div> |
|
||||
<h3 style={{ textAlign: 'center', paddingBottom: '15px', textDecoration: 'underline' }}> |
|
||||
Ajout d'un Matière |
|
||||
</h3> |
|
||||
<Box |
|
||||
sx={{ |
|
||||
marginTop: '2%', |
|
||||
width: '100%' |
|
||||
}} |
|
||||
> |
|
||||
<form onSubmit={formSubmit}> |
|
||||
<Grid container spacing={2}> |
|
||||
<Grid item xs={12} sm={6}> |
|
||||
<TextField |
|
||||
label="Nom" |
|
||||
name="nom" |
|
||||
color="warning" |
|
||||
fullWidth |
|
||||
placeholder="Nom du matière" |
|
||||
value={formData.nom} |
|
||||
onChange={handleInputChange} |
|
||||
onInput={() => ChangeCapitalize(nomRef)} |
|
||||
InputProps={{ |
|
||||
startAdornment: ( |
|
||||
<InputAdornment position="start"> |
|
||||
<FaBook /> |
|
||||
</InputAdornment> |
|
||||
) |
|
||||
}} |
|
||||
inputRef={nomRef} |
|
||||
sx={{ |
|
||||
marginBottom: '5px', |
|
||||
'& .MuiOutlinedInput-root': { |
|
||||
'&:hover fieldset': { |
|
||||
borderColor: '#ff9800' // Set the border color on hover |
|
||||
} |
|
||||
} |
|
||||
}} |
|
||||
/> |
|
||||
</Grid> |
|
||||
<Grid item xs={12} sm={6}> |
|
||||
<TextField |
|
||||
label="Crédit" |
|
||||
name="credit" |
|
||||
color="warning" |
|
||||
fullWidth |
|
||||
placeholder="Crédit pour la matiere" |
|
||||
type="number" |
|
||||
value={formData.credit} |
|
||||
onChange={handleInputChange} |
|
||||
InputProps={{ |
|
||||
startAdornment: ( |
|
||||
<InputAdornment position="start"> |
|
||||
<MdOutlineNumbers /> |
|
||||
</InputAdornment> |
|
||||
) |
|
||||
}} |
|
||||
inputProps={{ min: 1 }} |
|
||||
inputRef={creditRef} |
|
||||
sx={{ |
|
||||
marginBottom: '5px', |
|
||||
'& .MuiOutlinedInput-root': { |
|
||||
'&:hover fieldset': { |
|
||||
borderColor: '#ff9800' // Set the border color on hover |
|
||||
} |
|
||||
} |
|
||||
}} |
|
||||
/> |
|
||||
</Grid> |
|
||||
{/* <Grid item xs={12} sm={6}> |
|
||||
<TextField |
|
||||
label="Semestre" |
|
||||
name="semestre" |
|
||||
color='warning' |
|
||||
fullWidth |
|
||||
placeholder='exemple S1, S2, S3, S4' |
|
||||
type='text' |
|
||||
value={formData.semestre} |
|
||||
onChange={handleInputChange} |
|
||||
InputProps={{ |
|
||||
startAdornment: ( |
|
||||
<InputAdornment position="start"> |
|
||||
<CiCalendar /> |
|
||||
</InputAdornment> |
|
||||
), |
|
||||
}} |
|
||||
inputProps={{ min: 1 }} |
|
||||
inputRef={semestreRef} |
|
||||
sx={{ |
|
||||
marginBottom:"5px", |
|
||||
'& .MuiOutlinedInput-root': { |
|
||||
'&:hover fieldset': { |
|
||||
borderColor: '#ff9800', // Set the border color on hover |
|
||||
}, |
|
||||
}, |
|
||||
}} |
|
||||
/> |
|
||||
</Grid> */} |
|
||||
<Grid item xs={12} sm={6}> |
|
||||
<TextField |
|
||||
label="Unité d'enseignement" |
|
||||
name="uniter" |
|
||||
color="warning" |
|
||||
fullWidth |
|
||||
placeholder="Le matiere sera dans quelle unité d'enseignement" |
|
||||
value={formData.uniter} |
|
||||
onChange={handleInputChange} |
|
||||
onInput={() => ChangeCapital(uniterRef)} |
|
||||
InputProps={{ |
|
||||
startAdornment: ( |
|
||||
<InputAdornment position="start"> |
|
||||
<IoBookmark /> |
|
||||
</InputAdornment> |
|
||||
) |
|
||||
}} |
|
||||
inputProps={{ min: 1 }} |
|
||||
inputRef={uniterRef} |
|
||||
sx={{ |
|
||||
marginBottom: '5px', |
|
||||
'& .MuiOutlinedInput-root': { |
|
||||
'&:hover fieldset': { |
|
||||
borderColor: '#ff9800' // Set the border color on hover |
|
||||
} |
|
||||
} |
|
||||
}} |
|
||||
/> |
|
||||
</Grid> |
|
||||
<Grid item xs={12} sm={6}> |
|
||||
<TextField |
|
||||
label="UE" |
|
||||
name="ue" |
|
||||
color="warning" |
|
||||
fullWidth |
|
||||
placeholder="TI2" |
|
||||
value={formData.ue} |
|
||||
onInput={() => ChangeCapital(ueRef)} |
|
||||
onChange={handleInputChange} |
|
||||
type="text" |
|
||||
InputProps={{ |
|
||||
startAdornment: ( |
|
||||
<InputAdornment position="start"> |
|
||||
<CgPathUnite /> |
|
||||
</InputAdornment> |
|
||||
) |
|
||||
}} |
|
||||
inputProps={{ min: 1 }} |
|
||||
inputRef={ueRef} |
|
||||
sx={{ |
|
||||
marginBottom: '5px', |
|
||||
'& .MuiOutlinedInput-root': { |
|
||||
'&:hover fieldset': { |
|
||||
borderColor: '#ff9800' // Set the border color on hover |
|
||||
} |
|
||||
} |
|
||||
}} |
|
||||
/> |
|
||||
</Grid> |
|
||||
{/* <Grid item xs={12} sm={6}> |
|
||||
<Autocomplete |
|
||||
multiple |
|
||||
options={mention} // Options array for Autocomplete |
|
||||
getOptionLabel={(option) => option.nom || ""} // Safely access `nom` |
|
||||
value={formData.mention_id.map(id => mention.find(item => item.id === id)) || []} // Bind selected values to form data |
|
||||
onChange={(event, newValue) => { |
|
||||
setFormData((prevData) => ({ |
|
||||
...prevData, |
|
||||
mention_id: newValue.map((item) => item.id), // Store only the IDs of selected mentions |
|
||||
})); |
|
||||
}} |
|
||||
isOptionEqualToValue={(option, value) => option.id === value.id} // Ensure correct matching of options |
|
||||
renderInput={(params) => ( |
|
||||
<TextField |
|
||||
{...params} |
|
||||
label="Mention" |
|
||||
color="warning" |
|
||||
placeholder="Sélectionnez les mentions" |
|
||||
InputProps={{ |
|
||||
...params.InputProps, |
|
||||
startAdornment: ( |
|
||||
<> |
|
||||
<InputAdornment position="start"> |
|
||||
<FaClipboardList /> |
|
||||
</InputAdornment> |
|
||||
{params.InputProps.startAdornment} |
|
||||
</> |
|
||||
), |
|
||||
}} |
|
||||
sx={{ |
|
||||
marginBottom: "5px", |
|
||||
'& .MuiOutlinedInput-root': { |
|
||||
'&:hover fieldset': { |
|
||||
borderColor: '#ff9800', // Set border color on hover |
|
||||
}, |
|
||||
}, |
|
||||
}} |
|
||||
/> |
|
||||
)} |
|
||||
/> |
|
||||
|
|
||||
</Grid> */} |
|
||||
</Grid> |
|
||||
<span style={{ color: 'red', marginBottom: '15px' }} ref={errorRef}></span> |
|
||||
<br /> |
|
||||
<Grid |
|
||||
item |
|
||||
xs={12} |
|
||||
style={{ display: 'flex', gap: '30px', justifyContent: 'flex-end' }} |
|
||||
> |
|
||||
<Button type="submit" color="warning" variant="contained"> |
|
||||
Enregister |
|
||||
</Button> |
|
||||
</Grid> |
|
||||
</form> |
|
||||
</Box> |
|
||||
</Box> |
|
||||
</div> |
|
||||
</div> |
|
||||
) |
|
||||
} |
|
||||
|
|
||||
export default AddMatiere |
|
||||
@ -1,257 +0,0 @@ |
|||||
import React, { useEffect, useRef, useState } from 'react' |
|
||||
import classe from '../assets/AllStyleComponents.module.css' |
|
||||
import classeAdd from '../assets/AddStudent.module.css' |
|
||||
import classeHome from '../assets/Home.module.css' |
|
||||
import { Link, useNavigate } from 'react-router-dom' |
|
||||
import { IoMdReturnRight } from 'react-icons/io' |
|
||||
import { Box, Button, InputAdornment, Typography, Modal, TextField, Grid } from '@mui/material' |
|
||||
import { BsBookmarkPlusFill } from 'react-icons/bs' |
|
||||
import { FaClipboardList } from 'react-icons/fa6' |
|
||||
import ChangeCapital from './function/ChangeCapitalLetter' |
|
||||
import { IoBookmark } from 'react-icons/io5' |
|
||||
import svgSuccess from '../assets/success.svg' |
|
||||
|
|
||||
const AddMention = () => { |
|
||||
const [formData, setFormData] = useState({ |
|
||||
nom: '', |
|
||||
uniter: '' |
|
||||
}) |
|
||||
|
|
||||
const [errors, setErrors] = useState({ |
|
||||
nom: false, |
|
||||
uniter: false |
|
||||
}) |
|
||||
|
|
||||
const navigate = useNavigate() |
|
||||
|
|
||||
/** |
|
||||
* function to set the data in state |
|
||||
* @param {*} e |
|
||||
*/ |
|
||||
const handleInputChange = (e) => { |
|
||||
const { name, value } = e.target |
|
||||
setFormData({ |
|
||||
...formData, |
|
||||
[name]: value |
|
||||
}) |
|
||||
setErrors({ |
|
||||
...errors, |
|
||||
[name]: false // Reset the error when user starts typing |
|
||||
}) |
|
||||
} |
|
||||
|
|
||||
const formSubmit = async (e) => { |
|
||||
e.preventDefault() |
|
||||
const newErrors = {} |
|
||||
let hasError = false |
|
||||
|
|
||||
// Check for empty fields |
|
||||
Object.keys(formData).forEach((key) => { |
|
||||
if (!formData[key].trim()) { |
|
||||
newErrors[key] = true // Set error for empty fields |
|
||||
hasError = true |
|
||||
} |
|
||||
}) |
|
||||
|
|
||||
setErrors(newErrors) |
|
||||
|
|
||||
if (!hasError) { |
|
||||
try { |
|
||||
let response = await window.mention.createMention(formData) |
|
||||
|
|
||||
if (response.success) { |
|
||||
setOpen(true) |
|
||||
} |
|
||||
} catch (error) { |
|
||||
console.log(error) |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* hook to open modal |
|
||||
*/ |
|
||||
const [open, setOpen] = useState(false) |
|
||||
|
|
||||
/** |
|
||||
* function to close modal |
|
||||
*/ |
|
||||
const handleClose = () => { |
|
||||
setOpen(false) |
|
||||
navigate('/mention') |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* function to return the view Modal |
|
||||
* |
|
||||
* @returns {JSX} |
|
||||
*/ |
|
||||
const modals = () => ( |
|
||||
<Modal |
|
||||
open={open} |
|
||||
onClose={handleClose} |
|
||||
aria-labelledby="modal-title" |
|
||||
aria-describedby="modal-description" |
|
||||
> |
|
||||
<Box |
|
||||
sx={{ |
|
||||
position: 'absolute', |
|
||||
top: '50%', |
|
||||
left: '50%', |
|
||||
transform: 'translate(-50%, -50%)', |
|
||||
width: 450, |
|
||||
bgcolor: 'background.paper', |
|
||||
boxShadow: 24, |
|
||||
p: 4 |
|
||||
}} |
|
||||
> |
|
||||
<Typography style={{ display: 'flex', flexDirection: 'column', alignItems: 'center' }}> |
|
||||
<img src={svgSuccess} alt="" width={50} height={50} />{' '} |
|
||||
<span>Mention insérer avec succes</span> |
|
||||
</Typography> |
|
||||
<Box |
|
||||
sx={{ |
|
||||
marginTop: '2%', |
|
||||
display: 'flex', |
|
||||
gap: '20px', |
|
||||
alignItems: 'end', |
|
||||
justifyContent: 'flex-end' |
|
||||
}} |
|
||||
> |
|
||||
<Button onClick={handleClose} color="warning" variant="contained"> |
|
||||
OK |
|
||||
</Button> |
|
||||
</Box> |
|
||||
</Box> |
|
||||
</Modal> |
|
||||
) |
|
||||
|
|
||||
// Helper function to get helperText dynamically |
|
||||
const getHelperText = (field) => (errors[field] ? 'Ce champ est requis.' : '') |
|
||||
|
|
||||
const nomRef = useRef() |
|
||||
const uniterRef = useRef() |
|
||||
return ( |
|
||||
<div className={classe.mainHome}> |
|
||||
{modals()} |
|
||||
<div className={classeAdd.header}> |
|
||||
<div className={classe.h1style}> |
|
||||
<div className={classeHome.blockTitle}> |
|
||||
<h1 style={{ display: 'flex', alignItems: 'center', gap: '10px' }}> |
|
||||
<BsBookmarkPlusFill /> |
|
||||
Ajout Mention |
|
||||
</h1> |
|
||||
<Link to={'/mention'}> |
|
||||
<Button color="warning" variant="contained"> |
|
||||
<IoMdReturnRight style={{ fontSize: '20px' }} /> |
|
||||
</Button> |
|
||||
</Link> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
|
|
||||
{/* displaying the formulaire */} |
|
||||
<div className={classeHome.boxEtudiantsCard}> |
|
||||
<Box |
|
||||
sx={{ |
|
||||
position: 'absolute', |
|
||||
top: '55%', |
|
||||
left: '50%', |
|
||||
transform: 'translate(-50%, -50%)', |
|
||||
width: 700, |
|
||||
borderRadius: '2%', |
|
||||
bgcolor: 'background.paper', |
|
||||
boxShadow: 24, |
|
||||
p: 4 |
|
||||
}} |
|
||||
> |
|
||||
<h3 style={{ textAlign: 'center', paddingBottom: '15px', textDecoration: 'underline' }}> |
|
||||
Ajout d'un Mention |
|
||||
</h3> |
|
||||
<Box |
|
||||
sx={{ |
|
||||
marginTop: '2%', |
|
||||
width: '100%' |
|
||||
}} |
|
||||
> |
|
||||
<form onSubmit={formSubmit}> |
|
||||
<Grid container spacing={2}> |
|
||||
<Grid item xs={12} sm={6}> |
|
||||
<TextField |
|
||||
label="Nom" |
|
||||
error={errors.nom} |
|
||||
name="nom" |
|
||||
color="warning" |
|
||||
fullWidth |
|
||||
placeholder="GENIE DES MINES" |
|
||||
value={formData.nom} |
|
||||
onChange={handleInputChange} |
|
||||
onInput={() => ChangeCapital(nomRef)} |
|
||||
InputProps={{ |
|
||||
startAdornment: ( |
|
||||
<InputAdornment position="start"> |
|
||||
<FaClipboardList /> |
|
||||
</InputAdornment> |
|
||||
) |
|
||||
}} |
|
||||
helperText={getHelperText('nom')} |
|
||||
inputRef={nomRef} |
|
||||
sx={{ |
|
||||
marginBottom: '5px', |
|
||||
'& .MuiOutlinedInput-root': { |
|
||||
'&:hover fieldset': { |
|
||||
borderColor: '#ff9800' // Set the border color on hover |
|
||||
} |
|
||||
} |
|
||||
}} |
|
||||
/> |
|
||||
</Grid> |
|
||||
<Grid item xs={12} sm={6}> |
|
||||
<TextField |
|
||||
label="Unité" |
|
||||
error={errors.uniter} |
|
||||
helperText={getHelperText('uniter')} |
|
||||
name="uniter" |
|
||||
color="warning" |
|
||||
fullWidth |
|
||||
placeholder="GM" |
|
||||
value={formData.uniter} |
|
||||
onChange={handleInputChange} |
|
||||
onInput={() => ChangeCapital(uniterRef)} |
|
||||
InputProps={{ |
|
||||
startAdornment: ( |
|
||||
<InputAdornment position="start"> |
|
||||
<IoBookmark /> |
|
||||
</InputAdornment> |
|
||||
) |
|
||||
}} |
|
||||
inputRef={uniterRef} |
|
||||
sx={{ |
|
||||
marginBottom: '5px', |
|
||||
'& .MuiOutlinedInput-root': { |
|
||||
'&:hover fieldset': { |
|
||||
borderColor: '#ff9800' // Set the border color on hover |
|
||||
} |
|
||||
} |
|
||||
}} |
|
||||
/> |
|
||||
</Grid> |
|
||||
<Grid |
|
||||
item |
|
||||
xs={12} |
|
||||
style={{ display: 'flex', gap: '30px', justifyContent: 'flex-end' }} |
|
||||
> |
|
||||
<Button type="submit" color="warning" variant="contained"> |
|
||||
Enregister |
|
||||
</Button> |
|
||||
</Grid> |
|
||||
</Grid> |
|
||||
</form> |
|
||||
</Box> |
|
||||
</Box> |
|
||||
</div> |
|
||||
</div> |
|
||||
) |
|
||||
} |
|
||||
|
|
||||
export default AddMention |
|
||||
@ -1,214 +0,0 @@ |
|||||
import React, { useEffect, useRef, useState } from 'react' |
|
||||
import classe from '../assets/AllStyleComponents.module.css' |
|
||||
import classeAdd from '../assets/AddStudent.module.css' |
|
||||
import { Link, useNavigate } from 'react-router-dom' |
|
||||
import { IoMdPersonAdd, IoMdReturnRight } from 'react-icons/io' |
|
||||
import { FaFileExcel, FaUser } from 'react-icons/fa' |
|
||||
import { Box, Button, InputAdornment, TextField, Grid, Modal, Typography } from '@mui/material' |
|
||||
import classeHome from '../assets/Home.module.css' |
|
||||
import validationNote from './validation/AddNiveau' |
|
||||
import svgSuccess from '../assets/success.svg' |
|
||||
|
|
||||
const AddNiveau = () => { |
|
||||
const navigate = useNavigate() |
|
||||
/** |
|
||||
* hook for storing data in the input |
|
||||
*/ |
|
||||
const [formData, setFormData] = useState({ |
|
||||
nom: '' |
|
||||
}) |
|
||||
const [niveau, setNiveau] = useState([]) |
|
||||
/** |
|
||||
* hook to open modal |
|
||||
*/ |
|
||||
const [open, setOpen] = useState(false) |
|
||||
|
|
||||
/** |
|
||||
* function to close modal |
|
||||
*/ |
|
||||
const handleClose = () => { |
|
||||
setOpen(false) |
|
||||
navigate('/niveau') |
|
||||
} |
|
||||
|
|
||||
useEffect(() => { |
|
||||
window.niveaus.getNiveau().then((response) => { |
|
||||
setNiveau(response) |
|
||||
}) |
|
||||
}, []) |
|
||||
|
|
||||
/** |
|
||||
* function to set the data in state |
|
||||
* @param {*} e |
|
||||
*/ |
|
||||
const handleInputChange = (e) => { |
|
||||
const { name, value } = e.target |
|
||||
setFormData((prevData) => ({ |
|
||||
...prevData, |
|
||||
[name]: value |
|
||||
})) |
|
||||
} |
|
||||
|
|
||||
const nomRef = useRef() |
|
||||
const nomError = useRef() |
|
||||
|
|
||||
/** |
|
||||
* function to return the view Modal |
|
||||
* |
|
||||
* @returns {JSX} |
|
||||
*/ |
|
||||
const modals = () => ( |
|
||||
<Modal |
|
||||
open={open} |
|
||||
onClose={handleClose} |
|
||||
aria-labelledby="modal-title" |
|
||||
aria-describedby="modal-description" |
|
||||
> |
|
||||
<Box |
|
||||
sx={{ |
|
||||
position: 'absolute', |
|
||||
top: '50%', |
|
||||
left: '50%', |
|
||||
transform: 'translate(-50%, -50%)', |
|
||||
width: 450, |
|
||||
bgcolor: 'background.paper', |
|
||||
boxShadow: 24, |
|
||||
p: 4 |
|
||||
}} |
|
||||
> |
|
||||
<Typography style={{ display: 'flex', alignItems: 'center', flexDirection: 'column' }}> |
|
||||
<img src={svgSuccess} alt="" width={50} height={50} />{' '} |
|
||||
<span>Insertion a été effectuée avec succès</span> |
|
||||
</Typography> |
|
||||
<Box |
|
||||
sx={{ |
|
||||
marginTop: '2%', |
|
||||
display: 'flex', |
|
||||
gap: '20px', |
|
||||
alignItems: 'end', |
|
||||
justifyContent: 'flex-end' |
|
||||
}} |
|
||||
> |
|
||||
<Button onClick={handleClose} color="warning" variant="contained"> |
|
||||
OK |
|
||||
</Button> |
|
||||
</Box> |
|
||||
</Box> |
|
||||
</Modal> |
|
||||
) |
|
||||
|
|
||||
const formSubmit = async (e) => { |
|
||||
e.preventDefault() |
|
||||
let niveauNom = [] |
|
||||
niveau.map((niv) => { |
|
||||
niveauNom.push(niv.nom) |
|
||||
}) |
|
||||
let validation = validationNote(nomRef.current, nomError.current, niveauNom) |
|
||||
|
|
||||
if (validation) { |
|
||||
let response = await window.niveaus.insertNiveau(formData) |
|
||||
|
|
||||
let responses = JSON.parse(response) |
|
||||
if (responses.changes == 1) { |
|
||||
setOpen(true) |
|
||||
setFormData({ |
|
||||
nom: '' |
|
||||
}) |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
return ( |
|
||||
<div className={classe.mainHome}> |
|
||||
{modals()} |
|
||||
<div className={classeAdd.header}> |
|
||||
<div className={classe.h1style}> |
|
||||
<div className={classeHome.blockTitle}> |
|
||||
<h1 style={{ display: 'flex', alignItems: 'center', gap: '10px' }}> |
|
||||
<IoMdPersonAdd /> |
|
||||
Ajout niveau |
|
||||
</h1> |
|
||||
<Link to={'/niveau'}> |
|
||||
<Button color="warning" variant="contained"> |
|
||||
<IoMdReturnRight style={{ fontSize: '20px' }} /> |
|
||||
</Button> |
|
||||
</Link> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
|
|
||||
<div className={classeAdd.boxEtudiantsCard}> |
|
||||
<Box |
|
||||
sx={{ |
|
||||
position: 'absolute', |
|
||||
top: '50%', |
|
||||
left: '50%', |
|
||||
transform: 'translate(-50%, -50%)', |
|
||||
width: 700, |
|
||||
borderRadius: '2%', |
|
||||
bgcolor: 'background.paper', |
|
||||
boxShadow: 24, |
|
||||
p: 4 |
|
||||
}} |
|
||||
> |
|
||||
<h3 style={{ textAlign: 'center', paddingBottom: '15px', textDecoration: 'underline' }}> |
|
||||
Ajout d'un niveau |
|
||||
</h3> |
|
||||
<div style={{ textAlign: 'right', fontWeight: 'bold' }}> |
|
||||
<Link to={'/importniveau'} style={{ textDecoration: 'none', color: 'orange' }}> |
|
||||
Importer un fichier excel <FaFileExcel /> |
|
||||
</Link> |
|
||||
</div> |
|
||||
<Box |
|
||||
sx={{ |
|
||||
marginTop: '2%', |
|
||||
width: '100%' |
|
||||
}} |
|
||||
> |
|
||||
<form onSubmit={formSubmit}> |
|
||||
<TextField |
|
||||
label="Nom" |
|
||||
name="nom" |
|
||||
color="warning" |
|
||||
fullWidth |
|
||||
placeholder="L1 ou L2 ou L3 ou M1 ou M2 ou D1 ou D2 ou D3" |
|
||||
value={formData.nom} |
|
||||
onChange={handleInputChange} |
|
||||
InputProps={{ |
|
||||
startAdornment: ( |
|
||||
<InputAdornment position="start"> |
|
||||
<FaUser /> |
|
||||
</InputAdornment> |
|
||||
) |
|
||||
}} |
|
||||
inputRef={nomRef} |
|
||||
sx={{ |
|
||||
marginBottom: '5px', |
|
||||
'& .MuiOutlinedInput-root': { |
|
||||
'&:hover fieldset': { |
|
||||
borderColor: '#ff9800' // Set the border color on hover |
|
||||
} |
|
||||
} |
|
||||
}} |
|
||||
/> |
|
||||
<span style={{ color: 'red', marginBottom: '15px' }} ref={nomError}></span> |
|
||||
<br /> |
|
||||
{/* Submit Button */} |
|
||||
<Grid |
|
||||
item |
|
||||
xs={12} |
|
||||
style={{ display: 'flex', gap: '30px', justifyContent: 'flex-end' }} |
|
||||
> |
|
||||
<Button type="submit" color="warning" variant="contained"> |
|
||||
Enregister |
|
||||
</Button> |
|
||||
</Grid> |
|
||||
</form> |
|
||||
</Box> |
|
||||
</Box> |
|
||||
</div> |
|
||||
</div> |
|
||||
) |
|
||||
} |
|
||||
|
|
||||
export default AddNiveau |
|
||||
@ -1,162 +0,0 @@ |
|||||
import React, { useEffect, useRef, useState } from 'react' |
|
||||
import { |
|
||||
Dialog, |
|
||||
DialogActions, |
|
||||
DialogContent, |
|
||||
DialogTitle, |
|
||||
TextField, |
|
||||
Button, |
|
||||
Autocomplete, |
|
||||
InputAdornment, |
|
||||
Box, |
|
||||
Grid |
|
||||
} from '@mui/material' |
|
||||
import { MdRule } from 'react-icons/md' |
|
||||
import { FaClipboardList } from 'react-icons/fa' |
|
||||
|
|
||||
const AddParcours = ({ open, onClose, onSubmitSuccess }) => { |
|
||||
const [formData, setFormData] = useState({ |
|
||||
nom: '', |
|
||||
uniter: '', |
|
||||
mention_id: null |
|
||||
}) |
|
||||
|
|
||||
const [mention, setMention] = useState([]) |
|
||||
|
|
||||
useEffect(() => { |
|
||||
window.mention.getMention().then((response) => { |
|
||||
setMention(response) |
|
||||
}) |
|
||||
}, []) |
|
||||
|
|
||||
const handleChange = (e) => { |
|
||||
const { name, value } = e.target |
|
||||
setFormData({ ...formData, [name]: value }) |
|
||||
} |
|
||||
|
|
||||
const handleSubmit = async (e) => { |
|
||||
e.preventDefault() |
|
||||
let response = await window.notesysteme.insertParcours(formData) |
|
||||
console.log(response) |
|
||||
if (response.success) { |
|
||||
onSubmitSuccess(true) |
|
||||
onClose() // Close the modal after submission |
|
||||
setFormData({ |
|
||||
nom: '', |
|
||||
uniter: '', |
|
||||
mention_id: null |
|
||||
}) |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
return ( |
|
||||
<Dialog open={open} onClose={onClose}> |
|
||||
<form action="" onSubmit={handleSubmit}> |
|
||||
<DialogTitle>Information sur le parcour</DialogTitle> |
|
||||
<DialogContent> |
|
||||
<Box sx={{ flexGrow: 1 }}> |
|
||||
<Grid container spacing={2}> |
|
||||
<Grid item xs={12} sm={6}> |
|
||||
<TextField |
|
||||
autoFocus |
|
||||
margin="normal" |
|
||||
required |
|
||||
name="nom" |
|
||||
label="Nom du parcours" |
|
||||
type="text" |
|
||||
fullWidth |
|
||||
placeholder="Nom du parcours" |
|
||||
variant="outlined" |
|
||||
value={formData.nom} |
|
||||
color="warning" |
|
||||
onChange={handleChange} |
|
||||
InputProps={{ |
|
||||
startAdornment: ( |
|
||||
<InputAdornment position="start"> |
|
||||
<MdRule /> |
|
||||
</InputAdornment> |
|
||||
) |
|
||||
}} |
|
||||
/> |
|
||||
</Grid> |
|
||||
<Grid item xs={12} sm={6}> |
|
||||
<TextField |
|
||||
autoFocus |
|
||||
margin="normal" |
|
||||
required |
|
||||
name="uniter" |
|
||||
label="Uniter parcours" |
|
||||
type="text" |
|
||||
fullWidth |
|
||||
placeholder="GPESB" |
|
||||
variant="outlined" |
|
||||
value={formData.uniter} |
|
||||
color="warning" |
|
||||
onChange={handleChange} |
|
||||
InputProps={{ |
|
||||
startAdornment: ( |
|
||||
<InputAdornment position="start"> |
|
||||
<MdRule /> |
|
||||
</InputAdornment> |
|
||||
) |
|
||||
}} |
|
||||
/> |
|
||||
</Grid> |
|
||||
<Grid item xs={12} sm={12}> |
|
||||
<Autocomplete |
|
||||
options={mention} // Options array for Autocomplete |
|
||||
getOptionLabel={(option) => option.nom || ''} // Safely access `nom` |
|
||||
value={mention.find((item) => item.id === formData.mention_id) || null} // Bind selected value to form data |
|
||||
onChange={(event, newValue) => { |
|
||||
setFormData((prevData) => ({ |
|
||||
...prevData, |
|
||||
mention_id: newValue ? newValue.id : null // Store the ID of the selected mention |
|
||||
})) |
|
||||
}} |
|
||||
isOptionEqualToValue={(option, value) => option.id === value.id} // Ensure correct matching of options |
|
||||
renderInput={(params) => ( |
|
||||
<TextField |
|
||||
{...params} |
|
||||
label="Mention" |
|
||||
color="warning" |
|
||||
placeholder="Sélectionnez une mention" |
|
||||
InputProps={{ |
|
||||
...params.InputProps, |
|
||||
startAdornment: ( |
|
||||
<> |
|
||||
<InputAdornment position="start"> |
|
||||
<FaClipboardList /> |
|
||||
</InputAdornment> |
|
||||
{params.InputProps.startAdornment} |
|
||||
</> |
|
||||
) |
|
||||
}} |
|
||||
sx={{ |
|
||||
marginBottom: '5px', |
|
||||
'& .MuiOutlinedInput-root': { |
|
||||
'&:hover fieldset': { |
|
||||
borderColor: '#ff9800' // Set border color on hover |
|
||||
} |
|
||||
} |
|
||||
}} |
|
||||
/> |
|
||||
)} |
|
||||
/> |
|
||||
</Grid> |
|
||||
</Grid> |
|
||||
</Box> |
|
||||
</DialogContent> |
|
||||
<DialogActions> |
|
||||
<Button onClick={onClose} color="error"> |
|
||||
Annuler |
|
||||
</Button> |
|
||||
<Button type="submit" color="warning"> |
|
||||
Soumettre |
|
||||
</Button> |
|
||||
</DialogActions> |
|
||||
</form> |
|
||||
</Dialog> |
|
||||
) |
|
||||
} |
|
||||
|
|
||||
export default AddParcours |
|
||||
@ -1,336 +0,0 @@ |
|||||
import React, { useRef, useState } from 'react' |
|
||||
import classe from '../assets/AllStyleComponents.module.css' |
|
||||
import img from '../assets/admin.png' |
|
||||
import classeHome from '../assets/Home.module.css' |
|
||||
import { Box, Button, InputAdornment, TextField, Grid, Typography, Modal } from '@mui/material' |
|
||||
import classeAdd from '../assets/AddStudent.module.css' |
|
||||
import validationAddAdmin from './validation/AddAdmin' |
|
||||
import svgSuccess from '../assets/success.svg' |
|
||||
import svgError from '../assets/error.svg' |
|
||||
import { FaEnvelope, FaLock, FaUser } from 'react-icons/fa' |
|
||||
|
|
||||
const Admin = () => { |
|
||||
const [formData, setFormData] = useState({ |
|
||||
username: '', |
|
||||
email: '', |
|
||||
password: '', |
|
||||
roles: 'Enseignant' |
|
||||
}) |
|
||||
|
|
||||
const usernameRef = useRef() |
|
||||
const emailRef = useRef() |
|
||||
const passwordRef = useRef() |
|
||||
const rolesRef = useRef() |
|
||||
const errorUsername = useRef() |
|
||||
const errorEmail = useRef() |
|
||||
const errorPassword = useRef() |
|
||||
|
|
||||
/** |
|
||||
* function to set the data in state |
|
||||
* @param {*} e |
|
||||
*/ |
|
||||
const handleInputChange = (e) => { |
|
||||
const { name, value } = e.target |
|
||||
setFormData((prevData) => ({ |
|
||||
...prevData, |
|
||||
[name]: value |
|
||||
})) |
|
||||
} |
|
||||
|
|
||||
const handleSubmit = async (e) => { |
|
||||
e.preventDefault() |
|
||||
// Handle form submission logic |
|
||||
const valid = validationAddAdmin( |
|
||||
usernameRef.current, |
|
||||
emailRef.current, |
|
||||
passwordRef.current, |
|
||||
errorUsername.current, |
|
||||
errorEmail.current, |
|
||||
errorPassword.current |
|
||||
) |
|
||||
console.log(formData) |
|
||||
console.log(valid) |
|
||||
|
|
||||
if (valid) { |
|
||||
const response = await window.allUser.insertUsers(formData) |
|
||||
console.log(response) |
|
||||
if (response.success) { |
|
||||
setOpen(true) |
|
||||
setFormData({ |
|
||||
username: '', |
|
||||
email: '', |
|
||||
password: '', |
|
||||
roles: 'Enseignant' |
|
||||
}) |
|
||||
} |
|
||||
|
|
||||
if (response.code) { |
|
||||
setCode(422) |
|
||||
setOpen(true) |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* hook to open modal |
|
||||
*/ |
|
||||
const [open, setOpen] = useState(false) |
|
||||
const [code, setCode] = useState(200) |
|
||||
|
|
||||
/** |
|
||||
* function to close modal |
|
||||
*/ |
|
||||
const handleClose = () => setOpen(false) |
|
||||
|
|
||||
/** |
|
||||
* function to return the view Modal |
|
||||
* |
|
||||
* @returns {JSX} |
|
||||
*/ |
|
||||
const modals = () => ( |
|
||||
<Modal |
|
||||
open={open} |
|
||||
onClose={handleClose} |
|
||||
aria-labelledby="modal-title" |
|
||||
aria-describedby="modal-description" |
|
||||
> |
|
||||
<Box |
|
||||
sx={{ |
|
||||
position: 'absolute', |
|
||||
top: '50%', |
|
||||
left: '50%', |
|
||||
transform: 'translate(-50%, -50%)', |
|
||||
width: 450, |
|
||||
bgcolor: 'background.paper', |
|
||||
boxShadow: 24, |
|
||||
p: 4 |
|
||||
}} |
|
||||
> |
|
||||
{code == 422 ? ( |
|
||||
<Typography |
|
||||
style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: '10px' }} |
|
||||
> |
|
||||
<img src={svgError} alt="" width={50} height={50} />{' '} |
|
||||
<span style={{ marginLeft: '10px' }}>Email déjà pris</span> |
|
||||
</Typography> |
|
||||
) : ( |
|
||||
<Typography |
|
||||
style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: '10px' }} |
|
||||
> |
|
||||
<img src={svgSuccess} alt="" width={50} height={50} />{' '} |
|
||||
<span>Insertion a été effectuée avec succès</span> |
|
||||
</Typography> |
|
||||
)} |
|
||||
<Box |
|
||||
sx={{ |
|
||||
marginTop: '2%', |
|
||||
display: 'flex', |
|
||||
gap: '20px', |
|
||||
alignItems: 'end', |
|
||||
justifyContent: 'flex-end' |
|
||||
}} |
|
||||
> |
|
||||
<Button onClick={handleClose} color="warning" variant="contained"> |
|
||||
OK |
|
||||
</Button> |
|
||||
</Box> |
|
||||
</Box> |
|
||||
</Modal> |
|
||||
) |
|
||||
|
|
||||
const sendData = async () => { |
|
||||
await window.syncro.getall() |
|
||||
} |
|
||||
|
|
||||
return ( |
|
||||
<div className={classe.mainHome}> |
|
||||
{modals()} |
|
||||
<div className={classeHome.header}> |
|
||||
<div className={classe.h1style}> |
|
||||
<div className={classeHome.blockTitle}> |
|
||||
<h1>Ajout d'admin</h1> |
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: '10px' }}> |
|
||||
<Button color="warning" variant="contained"> |
|
||||
Recevoir une mise à jour au server |
|
||||
</Button> |
|
||||
<Button color="warning" variant="contained" onClick={sendData}> |
|
||||
Envoyer une mise à jour au server |
|
||||
</Button> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
{/* contenu */} |
|
||||
<div className={classeHome.contenaire}> |
|
||||
<div className={classeAdd.boxEtudiantsCard}> |
|
||||
<Box |
|
||||
sx={{ |
|
||||
position: 'absolute', |
|
||||
top: '50%', |
|
||||
left: '50%', |
|
||||
transform: 'translate(-50%, -50%)', |
|
||||
width: 700, |
|
||||
borderRadius: '3%', |
|
||||
bgcolor: 'background.paper', |
|
||||
boxShadow: 24, |
|
||||
p: 4 |
|
||||
}} |
|
||||
> |
|
||||
<Box |
|
||||
sx={{ |
|
||||
marginTop: '5%', |
|
||||
display: 'flex', |
|
||||
gap: '10px', |
|
||||
alignItems: 'start', |
|
||||
flexDirection: 'column', |
|
||||
fontSize: 16, |
|
||||
fontFamily: 'sans-serif', |
|
||||
justifyContent: 'flex-end' |
|
||||
}} |
|
||||
> |
|
||||
<span style={{ display: 'flex', marginLeft: '40%' }}> |
|
||||
<img src={img} alt="" height={150} width={150} /> |
|
||||
</span> |
|
||||
|
|
||||
<form onSubmit={handleSubmit} style={{ marginTop: '5%' }}> |
|
||||
<Grid container spacing={2}> |
|
||||
{/* matieres Algebre */} |
|
||||
<Grid item xs={12} sm={6}> |
|
||||
<TextField |
|
||||
label="username" |
|
||||
name="username" |
|
||||
color="warning" |
|
||||
fullWidth |
|
||||
value={formData.username} |
|
||||
onChange={handleInputChange} |
|
||||
InputProps={{ |
|
||||
startAdornment: ( |
|
||||
<InputAdornment position="start"> |
|
||||
<FaUser /> |
|
||||
</InputAdornment> |
|
||||
) |
|
||||
}} |
|
||||
inputRef={usernameRef} |
|
||||
sx={{ |
|
||||
'& .MuiOutlinedInput-root': { |
|
||||
'&:hover fieldset': { |
|
||||
borderColor: '#ff9800' // Set the border color on hover |
|
||||
} |
|
||||
} |
|
||||
}} |
|
||||
/> |
|
||||
{/* error username */} |
|
||||
<span |
|
||||
className="text-danger" |
|
||||
ref={errorUsername} |
|
||||
style={{ fontSize: '13px' }} |
|
||||
></span> |
|
||||
</Grid> |
|
||||
{/* matieres Analyse */} |
|
||||
<Grid item xs={12} sm={6}> |
|
||||
<TextField |
|
||||
label="email" |
|
||||
name="email" |
|
||||
fullWidth |
|
||||
color="warning" |
|
||||
value={formData.email} |
|
||||
onChange={handleInputChange} |
|
||||
InputProps={{ |
|
||||
startAdornment: ( |
|
||||
<InputAdornment position="start"> |
|
||||
<FaEnvelope /> |
|
||||
</InputAdornment> |
|
||||
) |
|
||||
}} |
|
||||
inputRef={emailRef} |
|
||||
sx={{ |
|
||||
'& .MuiOutlinedInput-root': { |
|
||||
'&:hover fieldset': { |
|
||||
borderColor: '#ff9800' // Set the border color on hover |
|
||||
} |
|
||||
} |
|
||||
}} |
|
||||
/> |
|
||||
{/* error email */} |
|
||||
<span |
|
||||
className="text-danger" |
|
||||
style={{ fontSize: '13px' }} |
|
||||
ref={errorEmail} |
|
||||
></span> |
|
||||
</Grid> |
|
||||
{/* matieres Mecanique general */} |
|
||||
<Grid item xs={12} sm={6}> |
|
||||
<TextField |
|
||||
label="password" |
|
||||
name="password" |
|
||||
fullWidth |
|
||||
color="warning" |
|
||||
value={formData.password} |
|
||||
onChange={handleInputChange} |
|
||||
InputProps={{ |
|
||||
startAdornment: ( |
|
||||
<InputAdornment position="start"> |
|
||||
<FaLock /> |
|
||||
</InputAdornment> |
|
||||
) |
|
||||
}} |
|
||||
inputRef={passwordRef} |
|
||||
sx={{ |
|
||||
'& .MuiOutlinedInput-root': { |
|
||||
'&:hover fieldset': { |
|
||||
borderColor: '#ff9800' // Set the border color on hover |
|
||||
} |
|
||||
} |
|
||||
}} |
|
||||
/> |
|
||||
{/* error password */} |
|
||||
<span |
|
||||
className="text-danger" |
|
||||
ref={errorPassword} |
|
||||
style={{ fontSize: '13px' }} |
|
||||
></span> |
|
||||
</Grid> |
|
||||
{/* Matieres Resistance Materiaux */} |
|
||||
<Grid item xs={12} sm={6}> |
|
||||
<TextField |
|
||||
label="roles" |
|
||||
name="roles" |
|
||||
fullWidth |
|
||||
color="warning" |
|
||||
value={formData.roles} |
|
||||
onChange={handleInputChange} |
|
||||
disabled |
|
||||
InputProps={{ |
|
||||
startAdornment: <InputAdornment position="start"></InputAdornment> |
|
||||
}} |
|
||||
inputRef={rolesRef} |
|
||||
sx={{ |
|
||||
'& .MuiOutlinedInput-root': { |
|
||||
'&:hover fieldset': { |
|
||||
borderColor: '#ff9800' // Set the border color on hover |
|
||||
} |
|
||||
} |
|
||||
}} |
|
||||
/> |
|
||||
</Grid> |
|
||||
{/* Submit Button */} |
|
||||
<Grid |
|
||||
item |
|
||||
xs={12} |
|
||||
style={{ display: 'flex', gap: '30px', justifyContent: 'flex-end' }} |
|
||||
> |
|
||||
<Button type="submit" color="warning" variant="contained"> |
|
||||
Enregister |
|
||||
</Button> |
|
||||
</Grid> |
|
||||
</Grid> |
|
||||
</form> |
|
||||
</Box> |
|
||||
</Box> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
) |
|
||||
} |
|
||||
|
|
||||
export default Admin |
|
||||
@ -1,112 +0,0 @@ |
|||||
import React, { useState } from 'react' |
|
||||
import { |
|
||||
Dialog, |
|
||||
DialogActions, |
|
||||
DialogContent, |
|
||||
DialogTitle, |
|
||||
TextField, |
|
||||
Button, |
|
||||
Autocomplete, |
|
||||
InputAdornment, |
|
||||
Box, |
|
||||
Grid |
|
||||
} from '@mui/material' |
|
||||
import { MdLabelImportantOutline } from 'react-icons/md' |
|
||||
|
|
||||
const AjoutTranche = ({ open, onClose, onSubmitSuccess, id }) => { |
|
||||
const [formData, setFormData] = useState({ |
|
||||
etudiant_id: id, |
|
||||
tranchename: '', |
|
||||
montant: '' |
|
||||
}) |
|
||||
|
|
||||
const handleChange = (e) => { |
|
||||
const { name, value } = e.target |
|
||||
setFormData({ ...formData, [name]: value }) |
|
||||
} |
|
||||
|
|
||||
const handleSubmit = async (e) => { |
|
||||
e.preventDefault() |
|
||||
let response = await window.etudiants.createTranche(formData) |
|
||||
|
|
||||
if (response.success) { |
|
||||
onClose() |
|
||||
onSubmitSuccess(true) |
|
||||
setFormData({ |
|
||||
etudiant_id: id, |
|
||||
tranchename: '', |
|
||||
montant: '' |
|
||||
}) |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
return ( |
|
||||
<Dialog open={open} onClose={onClose}> |
|
||||
<form action="" onSubmit={handleSubmit}> |
|
||||
<DialogTitle>Ajout tranche</DialogTitle> |
|
||||
<DialogContent> |
|
||||
<Box sx={{ flexGrow: 1 }}> |
|
||||
<Grid container spacing={2}> |
|
||||
<Grid item xs={12} sm={6}> |
|
||||
<TextField |
|
||||
autoFocus |
|
||||
margin="normal" |
|
||||
required |
|
||||
name="tranchename" |
|
||||
label="Désignation" |
|
||||
type="text" |
|
||||
fullWidth |
|
||||
placeholder="Tranche 1" |
|
||||
variant="outlined" |
|
||||
value={formData.tranchename} |
|
||||
color="warning" |
|
||||
onChange={handleChange} |
|
||||
InputProps={{ |
|
||||
startAdornment: ( |
|
||||
<InputAdornment position="start"> |
|
||||
<MdLabelImportantOutline /> |
|
||||
</InputAdornment> |
|
||||
) |
|
||||
}} |
|
||||
/> |
|
||||
</Grid> |
|
||||
<Grid item xs={12} sm={6}> |
|
||||
<TextField |
|
||||
autoFocus |
|
||||
margin="normal" |
|
||||
required |
|
||||
name="montant" |
|
||||
label="Montant" |
|
||||
type="number" |
|
||||
fullWidth |
|
||||
placeholder="Montant" |
|
||||
variant="outlined" |
|
||||
value={formData.montant} |
|
||||
color="warning" |
|
||||
onChange={handleChange} |
|
||||
InputProps={{ |
|
||||
startAdornment: ( |
|
||||
<InputAdornment position="start"> |
|
||||
<MdLabelImportantOutline /> |
|
||||
</InputAdornment> |
|
||||
) |
|
||||
}} |
|
||||
/> |
|
||||
</Grid> |
|
||||
</Grid> |
|
||||
</Box> |
|
||||
</DialogContent> |
|
||||
<DialogActions> |
|
||||
<Button onClick={onClose} color="error"> |
|
||||
Annuler |
|
||||
</Button> |
|
||||
<Button type="submit" color="warning"> |
|
||||
Soumettre |
|
||||
</Button> |
|
||||
</DialogActions> |
|
||||
</form> |
|
||||
</Dialog> |
|
||||
) |
|
||||
} |
|
||||
|
|
||||
export default AjoutTranche |
|
||||
@ -1,203 +0,0 @@ |
|||||
import React, { useEffect, useState } from 'react' |
|
||||
import { Link, useParams } from 'react-router-dom' |
|
||||
import { FaPlus } from 'react-icons/fa' |
|
||||
import { Button, Grid, Paper, Checkbox, Modal, Typography, Box } from '@mui/material' |
|
||||
import { IoMdReturnRight } from 'react-icons/io' |
|
||||
import classe from '../assets/AllStyleComponents.module.css' |
|
||||
import classeAdd from '../assets/AddStudent.module.css' |
|
||||
import classeHome from '../assets/Home.module.css' |
|
||||
import svgSuccess from '../assets/success.svg' |
|
||||
|
|
||||
const AssignMatiereToMention = () => { |
|
||||
let { id } = useParams() |
|
||||
|
|
||||
const [mention, setMention] = useState([]) |
|
||||
const [matiere, setMatiere] = useState({}) |
|
||||
const [matiereMention, setMatiereMention] = useState([]) |
|
||||
const [formData, setFormData] = useState({}) |
|
||||
|
|
||||
// Fetch data on component mount |
|
||||
useEffect(() => { |
|
||||
window.matieres.getAsign({ id }).then((response) => { |
|
||||
setMatiereMention(response) // Set matiereMention |
|
||||
}) |
|
||||
window.mention.getMention().then((response) => { |
|
||||
setMention(response) // Set mention |
|
||||
}) |
|
||||
window.matieres.getMatiereByID({ id }).then((response) => { |
|
||||
setMatiere(response) // Set matiere |
|
||||
}) |
|
||||
}, [id]) |
|
||||
|
|
||||
// Initialize formData based on mentions and matiereMention |
|
||||
useEffect(() => { |
|
||||
if (mention.length && matiereMention.length) { |
|
||||
const initialFormData = mention.reduce((acc, mens) => { |
|
||||
// Check if the current mention ID exists in matiereMention |
|
||||
const isChecked = matiereMention.some((item) => item.mention_id === mens.id) |
|
||||
acc[mens.id] = isChecked // Set true if matched, false otherwise |
|
||||
return acc |
|
||||
}, {}) |
|
||||
setFormData(initialFormData) // Update formData |
|
||||
} |
|
||||
}, [mention, matiereMention]) |
|
||||
|
|
||||
/** |
|
||||
* Handle form submission |
|
||||
*/ |
|
||||
const formSubmit = async (e) => { |
|
||||
e.preventDefault() |
|
||||
|
|
||||
let response = await window.matieres.asign({ formData, id }) |
|
||||
console.log(response) |
|
||||
if (response.success) { |
|
||||
setOpen(true) |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* Handle checkbox change |
|
||||
*/ |
|
||||
const handleCheckboxChange = (id) => { |
|
||||
setFormData((prevData) => ({ |
|
||||
...prevData, |
|
||||
[id]: !prevData[id] // Toggle the checkbox value |
|
||||
})) |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* hook to open modal |
|
||||
*/ |
|
||||
const [open, setOpen] = useState(false) |
|
||||
|
|
||||
/** |
|
||||
* function to close modal |
|
||||
*/ |
|
||||
const handleClose = () => { |
|
||||
setOpen(false) |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* function to return the view Modal |
|
||||
* |
|
||||
* @returns {JSX} |
|
||||
*/ |
|
||||
const modals = () => ( |
|
||||
<Modal |
|
||||
open={open} |
|
||||
onClose={handleClose} |
|
||||
aria-labelledby="modal-title" |
|
||||
aria-describedby="modal-description" |
|
||||
> |
|
||||
<Box |
|
||||
sx={{ |
|
||||
position: 'absolute', |
|
||||
top: '50%', |
|
||||
left: '50%', |
|
||||
transform: 'translate(-50%, -50%)', |
|
||||
width: 450, |
|
||||
bgcolor: 'background.paper', |
|
||||
boxShadow: 24, |
|
||||
p: 4 |
|
||||
}} |
|
||||
> |
|
||||
<Typography style={{ display: 'flex', alignItems: 'center', flexDirection: 'column' }}> |
|
||||
<img src={svgSuccess} alt="" width={50} height={50} />{' '} |
|
||||
<span>Changemet effectuée avec succès</span> |
|
||||
</Typography> |
|
||||
<Box |
|
||||
sx={{ |
|
||||
marginTop: '2%', |
|
||||
display: 'flex', |
|
||||
gap: '20px', |
|
||||
alignItems: 'end', |
|
||||
justifyContent: 'flex-end' |
|
||||
}} |
|
||||
> |
|
||||
<Button onClick={handleClose} color="warning" variant="contained"> |
|
||||
OK |
|
||||
</Button> |
|
||||
</Box> |
|
||||
</Box> |
|
||||
</Modal> |
|
||||
) |
|
||||
|
|
||||
return ( |
|
||||
<div className={classe.mainHome}> |
|
||||
{modals()} |
|
||||
<div className={classeAdd.header}> |
|
||||
<div className={classe.h1style}> |
|
||||
<div className={classeHome.blockTitle}> |
|
||||
<h1 style={{ display: 'flex', alignItems: 'center', gap: '10px' }}> |
|
||||
<FaPlus /> Asignation |
|
||||
</h1> |
|
||||
<Link to={'#'} onClick={() => window.history.back()}> |
|
||||
<Button color="warning" variant="contained"> |
|
||||
<IoMdReturnRight style={{ fontSize: '20px' }} /> |
|
||||
</Button> |
|
||||
</Link> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
|
|
||||
<Paper |
|
||||
sx={{ |
|
||||
height: 'auto', |
|
||||
minHeight: 500, |
|
||||
display: 'flex', |
|
||||
width: '100%' |
|
||||
}} |
|
||||
> |
|
||||
<form style={{ width: '100%', padding: '2%' }} onSubmit={formSubmit}> |
|
||||
<h5 style={{ textAlign: 'center', textDecoration: 'underline' }}> |
|
||||
Choisissez les mentions qui utilisent {matiere?.nom || 'la matière'} |
|
||||
</h5> |
|
||||
<Grid container spacing={2}> |
|
||||
{mention.map((mens) => ( |
|
||||
<Grid |
|
||||
item |
|
||||
xs={12} |
|
||||
sm={3} |
|
||||
sx={{ |
|
||||
display: 'flex', |
|
||||
justifyContent: 'space-between', |
|
||||
alignItems: 'center', |
|
||||
padding: 5 |
|
||||
}} |
|
||||
key={mens.id} |
|
||||
> |
|
||||
<span> |
|
||||
<b> |
|
||||
<i>{mens.nom}</i> |
|
||||
</b> |
|
||||
</span> |
|
||||
<span> |
|
||||
<Checkbox |
|
||||
checked={formData[mens.id] || false} // Bind checkbox to formData |
|
||||
onChange={() => handleCheckboxChange(mens.id)} // Handle change |
|
||||
color="success" |
|
||||
/> |
|
||||
</span> |
|
||||
</Grid> |
|
||||
))} |
|
||||
</Grid> |
|
||||
<Grid |
|
||||
item |
|
||||
xs={12} |
|
||||
style={{ |
|
||||
display: 'flex', |
|
||||
gap: '30px', |
|
||||
justifyContent: 'flex-end' |
|
||||
}} |
|
||||
> |
|
||||
<Button type="submit" color="warning" variant="contained"> |
|
||||
Enregistrer |
|
||||
</Button> |
|
||||
</Grid> |
|
||||
</form> |
|
||||
</Paper> |
|
||||
</div> |
|
||||
) |
|
||||
} |
|
||||
|
|
||||
export default AssignMatiereToMention |
|
||||
@ -1,259 +0,0 @@ |
|||||
import React, { useEffect, useState } from 'react' |
|
||||
import classe from '../assets/AllStyleComponents.module.css' |
|
||||
import classeAdd from '../assets/AddStudent.module.css' |
|
||||
import classeHome from '../assets/Home.module.css' |
|
||||
import { Link, useParams } from 'react-router-dom' |
|
||||
import { Button, Grid, Paper, Modal, Typography, Box } from '@mui/material' |
|
||||
import { IoMdReturnRight } from 'react-icons/io' |
|
||||
import { FaPlus } from 'react-icons/fa' |
|
||||
import OutlinedInput from '@mui/material/OutlinedInput' |
|
||||
import InputLabel from '@mui/material/InputLabel' |
|
||||
import MenuItem from '@mui/material/MenuItem' |
|
||||
import FormControl from '@mui/material/FormControl' |
|
||||
import Select from '@mui/material/Select' |
|
||||
import svgSuccess from '../assets/success.svg' |
|
||||
|
|
||||
const AssingMatiereToSemestre = () => { |
|
||||
let { id } = useParams() |
|
||||
|
|
||||
const [mentions, setMentions] = useState([]) |
|
||||
const [matiereSemestre, setMatiereSemestre] = useState([]) |
|
||||
const [matiere, setMatiere] = useState({}) |
|
||||
const [semestre, setSemestre] = useState([]) |
|
||||
|
|
||||
useEffect(() => { |
|
||||
window.matieres.asignSemestre({ id }).then((response) => { |
|
||||
setMentions(response) |
|
||||
}) |
|
||||
|
|
||||
window.matieres.getMatiereByID({ id }).then((response) => { |
|
||||
setMatiere(response) // Set matiere |
|
||||
}) |
|
||||
|
|
||||
window.matieres.getSemestreMatiere({ id }).then((response) => { |
|
||||
setMatiereSemestre(response) // Set matiere |
|
||||
}) |
|
||||
|
|
||||
window.matieres.getSemestre().then((response) => { |
|
||||
setSemestre(response) // Set matiere |
|
||||
}) |
|
||||
}, [id]) |
|
||||
|
|
||||
// State to manage selected semestres for each mention |
|
||||
const [selectedSemestres, setSelectedSemestres] = useState({}) |
|
||||
|
|
||||
// Populate the initial state for selectedSemestres |
|
||||
useEffect(() => { |
|
||||
const initialSelectedSemestres = {} |
|
||||
matiereSemestre.forEach((item) => { |
|
||||
if (!initialSelectedSemestres[item.mention_id]) { |
|
||||
initialSelectedSemestres[item.mention_id] = [] |
|
||||
} |
|
||||
initialSelectedSemestres[item.mention_id].push(item.semestre_id) |
|
||||
}) |
|
||||
setSelectedSemestres(initialSelectedSemestres) |
|
||||
}, [matiereSemestre]) |
|
||||
|
|
||||
// Handle change in Select |
|
||||
const handleChange = (id) => (event) => { |
|
||||
const { |
|
||||
target: { value } |
|
||||
} = event |
|
||||
|
|
||||
// Update state for the specific mention ID |
|
||||
setSelectedSemestres((prevState) => ({ |
|
||||
...prevState, |
|
||||
[id]: typeof value === 'string' ? value.split(',') : value |
|
||||
})) |
|
||||
} |
|
||||
|
|
||||
const formSubmit = async (e) => { |
|
||||
e.preventDefault() |
|
||||
|
|
||||
let response = await window.matieres.insertUpdateMentionSemestre({ id, selectedSemestres }) |
|
||||
|
|
||||
console.log(response) |
|
||||
if (response.success) { |
|
||||
setOpen(true) |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* hook to open modal |
|
||||
*/ |
|
||||
const [open, setOpen] = useState(false) |
|
||||
|
|
||||
/** |
|
||||
* function to close modal |
|
||||
*/ |
|
||||
const handleClose = () => { |
|
||||
setOpen(false) |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* function to return the view Modal |
|
||||
* |
|
||||
* @returns {JSX} |
|
||||
*/ |
|
||||
const modals = () => ( |
|
||||
<Modal |
|
||||
open={open} |
|
||||
onClose={handleClose} |
|
||||
aria-labelledby="modal-title" |
|
||||
aria-describedby="modal-description" |
|
||||
> |
|
||||
<Box |
|
||||
sx={{ |
|
||||
position: 'absolute', |
|
||||
top: '50%', |
|
||||
left: '50%', |
|
||||
transform: 'translate(-50%, -50%)', |
|
||||
width: 450, |
|
||||
bgcolor: 'background.paper', |
|
||||
boxShadow: 24, |
|
||||
p: 4 |
|
||||
}} |
|
||||
> |
|
||||
<Typography style={{ display: 'flex', alignItems: 'center', flexDirection: 'column' }}> |
|
||||
<img src={svgSuccess} alt="" width={50} height={50} />{' '} |
|
||||
<span>Changemet effectuée avec succès</span> |
|
||||
</Typography> |
|
||||
<Box |
|
||||
sx={{ |
|
||||
marginTop: '2%', |
|
||||
display: 'flex', |
|
||||
gap: '20px', |
|
||||
alignItems: 'end', |
|
||||
justifyContent: 'flex-end' |
|
||||
}} |
|
||||
> |
|
||||
<Button onClick={handleClose} color="warning" variant="contained"> |
|
||||
OK |
|
||||
</Button> |
|
||||
</Box> |
|
||||
</Box> |
|
||||
</Modal> |
|
||||
) |
|
||||
|
|
||||
console.log(matiereSemestre) |
|
||||
|
|
||||
// Step 1: Preprocess matiereSemestre to create a mapping |
|
||||
const preprocessSemestres = () => { |
|
||||
const mapping = {} |
|
||||
matiereSemestre.forEach((item) => { |
|
||||
if (item.matiere_id === id) { |
|
||||
// Filter by matiere_id if necessary |
|
||||
if (!mapping[item.mention_id]) { |
|
||||
mapping[item.mention_id] = [] |
|
||||
} |
|
||||
mapping[item.mention_id].push(item.semestre_id) |
|
||||
} |
|
||||
}) |
|
||||
return mapping |
|
||||
} |
|
||||
|
|
||||
return ( |
|
||||
<div className={classe.mainHome}> |
|
||||
{modals()} |
|
||||
<div className={classeAdd.header}> |
|
||||
<div className={classe.h1style}> |
|
||||
<div className={classeHome.blockTitle}> |
|
||||
<h1 style={{ display: 'flex', alignItems: 'center', gap: '10px' }}> |
|
||||
<FaPlus /> Asignation Semestre |
|
||||
</h1> |
|
||||
<Link to={'/matiere'}> |
|
||||
<Button color="warning" variant="contained"> |
|
||||
<IoMdReturnRight style={{ fontSize: '20px' }} /> |
|
||||
</Button> |
|
||||
</Link> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
<Paper |
|
||||
sx={{ |
|
||||
height: 'auto', |
|
||||
minHeight: 500, |
|
||||
display: 'flex', |
|
||||
width: '100%' |
|
||||
}} |
|
||||
> |
|
||||
<form style={{ width: '100%', padding: '2%' }} onSubmit={formSubmit}> |
|
||||
<h5 style={{ textAlign: 'center', textDecoration: 'underline' }}> |
|
||||
Choisissez les semestre dans la quelle on enseigne {matiere?.nom || 'la matière'} |
|
||||
</h5> |
|
||||
<Grid container spacing={2}> |
|
||||
{mentions.map((mens) => ( |
|
||||
<Grid |
|
||||
item |
|
||||
xs={12} |
|
||||
sm={4} |
|
||||
sx={{ |
|
||||
display: 'flex', |
|
||||
justifyContent: 'space-between', |
|
||||
alignItems: 'center', |
|
||||
padding: 5 |
|
||||
}} |
|
||||
key={mens.id} |
|
||||
> |
|
||||
<div> |
|
||||
<div> |
|
||||
<b> |
|
||||
<i>{mens.nom}</i> |
|
||||
</b> |
|
||||
</div> |
|
||||
<div> |
|
||||
<FormControl sx={{ m: 1, width: 300 }}> |
|
||||
<InputLabel id={`semestre-label-${mens.id}`} color="warning"> |
|
||||
Semestre |
|
||||
</InputLabel> |
|
||||
<Select |
|
||||
labelId={`semestre-label-${mens.id}`} |
|
||||
id={`semestre-select-${mens.id}`} |
|
||||
multiple |
|
||||
color="warning" |
|
||||
size="small" |
|
||||
required |
|
||||
value={selectedSemestres[mens.id] || []} // Pre-select semestres for this mention |
|
||||
onChange={handleChange(mens.id)} // Pass mention ID to handler |
|
||||
input={<OutlinedInput label="Semestre" />} |
|
||||
MenuProps={{ |
|
||||
PaperProps: { |
|
||||
style: { |
|
||||
maxHeight: 200, // Limit dropdown height |
|
||||
width: 250 // Adjust dropdown width if needed |
|
||||
} |
|
||||
} |
|
||||
}} |
|
||||
> |
|
||||
{semestre.map((sem) => ( |
|
||||
<MenuItem key={sem.id} value={sem.id}> |
|
||||
{sem.nom} |
|
||||
</MenuItem> |
|
||||
))} |
|
||||
</Select> |
|
||||
</FormControl> |
|
||||
</div> |
|
||||
</div> |
|
||||
</Grid> |
|
||||
))} |
|
||||
</Grid> |
|
||||
<Grid |
|
||||
item |
|
||||
xs={12} |
|
||||
style={{ |
|
||||
display: 'flex', |
|
||||
gap: '30px', |
|
||||
justifyContent: 'flex-end' |
|
||||
}} |
|
||||
> |
|
||||
<Button type="submit" color="warning" variant="contained"> |
|
||||
Enregistrer |
|
||||
</Button> |
|
||||
</Grid> |
|
||||
</form> |
|
||||
</Paper> |
|
||||
</div> |
|
||||
) |
|
||||
} |
|
||||
|
|
||||
export default AssingMatiereToSemestre |
|
||||
@ -1,30 +0,0 @@ |
|||||
import React from 'react' |
|
||||
import { |
|
||||
GridToolbarContainer, |
|
||||
GridToolbarFilterButton, |
|
||||
GridToolbarColumnsButton, |
|
||||
GridToolbarExport |
|
||||
} from '@mui/x-data-grid' |
|
||||
import { Button } from '@mui/material' |
|
||||
import { FaCloudUploadAlt } from 'react-icons/fa' |
|
||||
|
|
||||
const CustomBar = ({ onImport }) => { |
|
||||
return ( |
|
||||
<GridToolbarContainer> |
|
||||
<GridToolbarColumnsButton /> |
|
||||
<GridToolbarFilterButton /> |
|
||||
{/* Custom import button */} |
|
||||
<Button |
|
||||
variant="contained" |
|
||||
color="inherit" |
|
||||
startIcon={<FaCloudUploadAlt />} |
|
||||
onClick={onImport} |
|
||||
> |
|
||||
Importer |
|
||||
</Button> |
|
||||
<GridToolbarExport /> |
|
||||
</GridToolbarContainer> |
|
||||
) |
|
||||
} |
|
||||
|
|
||||
export default CustomBar |
|
||||
@ -1,52 +0,0 @@ |
|||||
import React, { useEffect, useState } from 'react' |
|
||||
import { |
|
||||
Dialog, |
|
||||
DialogActions, |
|
||||
DialogContent, |
|
||||
DialogTitle, |
|
||||
Button, |
|
||||
Box, |
|
||||
Typography |
|
||||
} from '@mui/material' |
|
||||
|
|
||||
const DeleteTranche = ({ open, onClose, id, onSubmitSuccess }) => { |
|
||||
const [idDelete, setIdDelete] = useState(null) |
|
||||
|
|
||||
useEffect(() => { |
|
||||
setIdDelete(id) |
|
||||
}, [id]) |
|
||||
|
|
||||
console.log(idDelete) |
|
||||
|
|
||||
const deleteOption = async () => { |
|
||||
if (idDelete !== null) { |
|
||||
const id = idDelete |
|
||||
let response = await window.etudiants.deleteTranche({ id }) |
|
||||
if (response.success) { |
|
||||
onSubmitSuccess(true) |
|
||||
onClose() |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
return ( |
|
||||
<Dialog open={open} onClose={onClose}> |
|
||||
<DialogTitle>Suppression</DialogTitle> |
|
||||
<DialogContent> |
|
||||
<Box sx={{ flexGrow: 1, width: '300px' }}> |
|
||||
<Typography>Voulez vous Supprimer ?</Typography> |
|
||||
</Box> |
|
||||
</DialogContent> |
|
||||
<DialogActions> |
|
||||
<Button onClick={onClose} color="error"> |
|
||||
Annuler |
|
||||
</Button> |
|
||||
<Button onClick={deleteOption} color="warning"> |
|
||||
Soumettre |
|
||||
</Button> |
|
||||
</DialogActions> |
|
||||
</Dialog> |
|
||||
) |
|
||||
} |
|
||||
|
|
||||
export default DeleteTranche |
|
||||
@ -1,247 +0,0 @@ |
|||||
import React, { useRef, useState } from 'react' |
|
||||
import classe from '../assets/Login.module.css' |
|
||||
import { FaEnvelope, FaLock, FaLockOpen } from 'react-icons/fa' |
|
||||
import { Link } from 'react-router-dom' |
|
||||
import { |
|
||||
Modal, |
|
||||
Box, |
|
||||
Container, |
|
||||
Grid, |
|
||||
Card, |
|
||||
Typography, |
|
||||
TextField, |
|
||||
Button, |
|
||||
InputAdornment |
|
||||
} from '@mui/material' |
|
||||
import { validationForgotPassword } from './validation/ForgotPassword' |
|
||||
|
|
||||
const ForgotPassword = () => { |
|
||||
/** |
|
||||
* hook to store data from the input field |
|
||||
*/ |
|
||||
const [email, setEmail] = useState() |
|
||||
const [password, setPassword] = useState() |
|
||||
const [passwordConfirmation, setPasswordConfirmation] = useState() |
|
||||
|
|
||||
/** |
|
||||
* hook to open modal and set the sattus and message |
|
||||
*/ |
|
||||
const [open, setOpen] = useState(false) |
|
||||
const [message, setMessage] = useState('') |
|
||||
const [status, setStatus] = useState(null) |
|
||||
|
|
||||
const handleClose = () => setOpen(false) |
|
||||
|
|
||||
/** |
|
||||
* ref for our email and password, confirm password input and the error span |
|
||||
*/ |
|
||||
const emailRef = useRef() |
|
||||
const passwordRef = useRef() |
|
||||
const passwordConfirmationRef = useRef() |
|
||||
const emailError = useRef() |
|
||||
const passwordError = useRef() |
|
||||
const passwordConfirmationError = useRef() |
|
||||
|
|
||||
/** |
|
||||
* function to return the view Modal |
|
||||
* |
|
||||
* @returns {JSX} |
|
||||
*/ |
|
||||
const modals = () => ( |
|
||||
<Modal |
|
||||
open={open} |
|
||||
onClose={handleClose} |
|
||||
aria-labelledby="modal-title" |
|
||||
aria-describedby="modal-description" |
|
||||
> |
|
||||
<Box |
|
||||
sx={{ |
|
||||
position: 'absolute', |
|
||||
top: '50%', |
|
||||
left: '50%', |
|
||||
transform: 'translate(-50%, -50%)', |
|
||||
width: 400, |
|
||||
bgcolor: 'background.paper', |
|
||||
boxShadow: 24, |
|
||||
p: 4 |
|
||||
}} |
|
||||
> |
|
||||
<Typography id="modal-title" variant="h6" component="h2"> |
|
||||
{status === 200 ? 'Success' : 'Error'} |
|
||||
</Typography> |
|
||||
<Typography id="modal-description" sx={{ mt: 2 }}> |
|
||||
{message} |
|
||||
</Typography> |
|
||||
<Button onClick={handleClose}>Close</Button> |
|
||||
</Box> |
|
||||
</Modal> |
|
||||
) |
|
||||
|
|
||||
/** |
|
||||
* function to send the email verification |
|
||||
* to change the password and redirect the user to login |
|
||||
* |
|
||||
* @param {any} e |
|
||||
*/ |
|
||||
const verification = async (e) => { |
|
||||
e.preventDefault() |
|
||||
|
|
||||
let validation = validationForgotPassword( |
|
||||
emailRef.current, |
|
||||
passwordRef.current, |
|
||||
passwordConfirmationRef.current, |
|
||||
emailError.current, |
|
||||
passwordError.current, |
|
||||
passwordConfirmationError.current |
|
||||
) |
|
||||
console.log(validation) |
|
||||
if (validation === true) { |
|
||||
const response = await window.allUser.forgotPassword({ |
|
||||
email, |
|
||||
password, |
|
||||
passwordConfirmation |
|
||||
}) |
|
||||
|
|
||||
if (response.status === 200) { |
|
||||
setMessage(response.message) |
|
||||
setStatus(200) |
|
||||
} else { |
|
||||
setMessage(response.message) |
|
||||
setStatus(response.status) |
|
||||
} |
|
||||
setOpen(true) |
|
||||
console.log(response) |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
return ( |
|
||||
<Container |
|
||||
maxWidth={false} |
|
||||
sx={{ minHeight: '100vh', display: 'flex', alignItems: 'center', justifyContent: 'center' }} |
|
||||
className={classe.container} |
|
||||
> |
|
||||
{modals()} |
|
||||
<Grid container justifyContent="center"> |
|
||||
<Grid item xs={12} md={6} lg={4}> |
|
||||
<Card className={`p-4 shadow ${classe.cards}`} sx={{ padding: 4, boxShadow: 3 }}> |
|
||||
<Typography variant="h5" align="center" gutterBottom> |
|
||||
Changer de mot de passe |
|
||||
</Typography> |
|
||||
<form onSubmit={verification} className={classe.formulaireLogin}> |
|
||||
<TextField |
|
||||
label="Email" |
|
||||
variant="outlined" |
|
||||
type="text" |
|
||||
color="secondary" |
|
||||
className={classe.input} |
|
||||
fullWidth |
|
||||
margin="normal" |
|
||||
InputProps={{ |
|
||||
startAdornment: ( |
|
||||
<InputAdornment position="start"> |
|
||||
<FaEnvelope style={{ color: 'white' }} /> |
|
||||
</InputAdornment> |
|
||||
) |
|
||||
}} |
|
||||
sx={{ |
|
||||
'& .MuiOutlinedInput-root': { |
|
||||
'& fieldset': { |
|
||||
borderColor: 'white' // Set the border color when not focused |
|
||||
}, |
|
||||
'&:hover fieldset': { |
|
||||
borderColor: 'rgb(156, 39, 176)' // Set the border color on hover |
|
||||
} |
|
||||
} |
|
||||
}} |
|
||||
placeholder="Entrer email" |
|
||||
onChange={(e) => setEmail(e.target.value)} |
|
||||
inputRef={emailRef} |
|
||||
/> |
|
||||
<span ref={emailError} className="text-danger"></span> |
|
||||
|
|
||||
<TextField |
|
||||
label="Nouveau Mot de passe" |
|
||||
variant="outlined" |
|
||||
type="password" |
|
||||
className={classe.input} |
|
||||
color="secondary" |
|
||||
fullWidth |
|
||||
margin="normal" |
|
||||
InputProps={{ |
|
||||
startAdornment: ( |
|
||||
<InputAdornment position="start"> |
|
||||
<FaLock style={{ color: 'white' }} /> |
|
||||
</InputAdornment> |
|
||||
) |
|
||||
}} |
|
||||
sx={{ |
|
||||
'& .MuiOutlinedInput-root': { |
|
||||
'& fieldset': { |
|
||||
borderColor: 'white' // Set the border color when not focused |
|
||||
}, |
|
||||
'&:hover fieldset': { |
|
||||
borderColor: 'rgb(156, 39, 176)' // Set the border color on hover |
|
||||
} |
|
||||
} |
|
||||
}} |
|
||||
placeholder="Mot de passe" |
|
||||
onChange={(e) => setPassword(e.target.value)} |
|
||||
inputRef={passwordRef} |
|
||||
/> |
|
||||
<span ref={passwordError} className="text-danger"></span> |
|
||||
|
|
||||
<TextField |
|
||||
label="Confirmation Mot de passe" |
|
||||
variant="outlined" |
|
||||
type="password" |
|
||||
color="secondary" |
|
||||
fullWidth |
|
||||
margin="normal" |
|
||||
InputProps={{ |
|
||||
startAdornment: ( |
|
||||
<InputAdornment position="start"> |
|
||||
<FaLockOpen style={{ color: 'white' }} /> |
|
||||
</InputAdornment> |
|
||||
) |
|
||||
}} |
|
||||
sx={{ |
|
||||
'& .MuiOutlinedInput-root': { |
|
||||
'& fieldset': { |
|
||||
borderColor: 'white' // Set the border color when not focused |
|
||||
}, |
|
||||
'&:hover fieldset': { |
|
||||
borderColor: 'rgb(156, 39, 176)' // Set the border color on hover |
|
||||
} |
|
||||
} |
|
||||
}} |
|
||||
className={classe.input} |
|
||||
placeholder="Confirmation Mot de passe" |
|
||||
onChange={(e) => setPasswordConfirmation(e.target.value)} |
|
||||
inputRef={passwordConfirmationRef} |
|
||||
/> |
|
||||
<span ref={passwordConfirmationError} className="text-danger"></span> |
|
||||
|
|
||||
<Button |
|
||||
variant="contained" |
|
||||
color="secondary" |
|
||||
type="submit" |
|
||||
fullWidth |
|
||||
sx={{ marginTop: 2 }} |
|
||||
> |
|
||||
Modifier |
|
||||
</Button> |
|
||||
|
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between', marginTop: '1rem' }}> |
|
||||
<Link to="/login" style={{ color: '#FFFFFF' }}> |
|
||||
Se connecter |
|
||||
</Link> |
|
||||
</div> |
|
||||
</form> |
|
||||
</Card> |
|
||||
</Grid> |
|
||||
</Grid> |
|
||||
</Container> |
|
||||
) |
|
||||
} |
|
||||
|
|
||||
export default ForgotPassword |
|
||||
@ -1,194 +0,0 @@ |
|||||
import React, { useEffect, useState } from 'react' |
|
||||
import classe from '../assets/AllStyleComponents.module.css' |
|
||||
import { Bar } from 'react-chartjs-2' |
|
||||
import { |
|
||||
Chart as ChartJS, |
|
||||
CategoryScale, |
|
||||
LinearScale, |
|
||||
BarElement, |
|
||||
Title, |
|
||||
Tooltip, |
|
||||
Legend |
|
||||
} from 'chart.js' |
|
||||
import classeHome from '../assets/Home.module.css' |
|
||||
import dclass from '../assets/Dashboard.module.css' |
|
||||
import dayjs from 'dayjs' |
|
||||
import MenuItem from '@mui/material/MenuItem' |
|
||||
import FormControl from '@mui/material/FormControl' |
|
||||
import Select from '@mui/material/Select' |
|
||||
import InputLabel from '@mui/material/InputLabel' |
|
||||
|
|
||||
ChartJS.register(CategoryScale, LinearScale, BarElement, Title, Tooltip, Legend) |
|
||||
|
|
||||
const Home = () => { |
|
||||
const [etudiants, setEtudiants] = useState([]) |
|
||||
const [niveau, setNiveau] = useState([]) |
|
||||
const [annee_scolaire, setAnnee_scolaire] = useState([]) |
|
||||
const [originalEtudiants, setOriginalEtudiants] = useState([]) |
|
||||
// Get the current year |
|
||||
const currentYear = dayjs().year() |
|
||||
|
|
||||
useEffect(() => { |
|
||||
// Fetch data and update state |
|
||||
window.etudiants.getDataToDashboards().then((response) => { |
|
||||
setEtudiants(response.etudiants) |
|
||||
setOriginalEtudiants(response.etudiants) |
|
||||
setNiveau(response.niveau) |
|
||||
setAnnee_scolaire(response.anne_scolaire) |
|
||||
}) |
|
||||
}, []) |
|
||||
|
|
||||
// filter all data |
|
||||
|
|
||||
// ordre des colones de filtre |
|
||||
const desiredOrder = ['L1', 'L2', 'L3', 'M1', 'M2', 'D1', 'D2', 'D3'] |
|
||||
let allNiveau = niveau.map((item) => item.nom) |
|
||||
allNiveau.sort((a, b) => desiredOrder.indexOf(a) - desiredOrder.indexOf(b)) |
|
||||
|
|
||||
const studentGrades = {} |
|
||||
|
|
||||
// Loop through allNiveau and set the number of students for each key |
|
||||
allNiveau.forEach((niveauNom) => { |
|
||||
// Filter etudiants based on the matching niveau |
|
||||
const studentCount = etudiants.filter((etudiant) => etudiant.niveau === niveauNom).length |
|
||||
|
|
||||
// Assign the student count as the value for the corresponding key in studentGrades |
|
||||
studentGrades[niveauNom] = studentCount |
|
||||
}) |
|
||||
const studentCounts = Object.values(studentGrades) |
|
||||
|
|
||||
// Find the maximum value using Math.max |
|
||||
const maxStudentCount = Math.max(...studentCounts) |
|
||||
|
|
||||
const FilterAnneeScolaire = (e) => { |
|
||||
let annee_scolaire = e.target.value |
|
||||
const filteredEtudiants = originalEtudiants.filter( |
|
||||
(etudiant) => etudiant.annee_scolaire === annee_scolaire |
|
||||
) |
|
||||
setEtudiants(filteredEtudiants) |
|
||||
if (annee_scolaire == 'general') { |
|
||||
setEtudiants(originalEtudiants) |
|
||||
} |
|
||||
} |
|
||||
// end filter all data |
|
||||
|
|
||||
// Calculate the number of classes |
|
||||
const numberOfClasses = Object.keys(studentGrades).length |
|
||||
|
|
||||
// Data for the Bar chart |
|
||||
const data = { |
|
||||
labels: Object.keys(studentGrades), // Class levels |
|
||||
datasets: [ |
|
||||
{ |
|
||||
label: 'Nombre des étudiants', |
|
||||
data: Object.values(studentGrades), // Student counts |
|
||||
backgroundColor: [ |
|
||||
'#FF6384', |
|
||||
'#36A2EB', |
|
||||
'#FFCE56', |
|
||||
'#4BC0C0', |
|
||||
'#9966FF', |
|
||||
'#FF9F40', |
|
||||
'#C9CBCF', |
|
||||
'#00A36C' |
|
||||
], // Colors for each bar |
|
||||
borderColor: [ |
|
||||
'#FF6384', |
|
||||
'#36A2EB', |
|
||||
'#FFCE56', |
|
||||
'#4BC0C0', |
|
||||
'#9966FF', |
|
||||
'#FF9F40', |
|
||||
'#C9CBCF', |
|
||||
'#00A36C' |
|
||||
], |
|
||||
borderWidth: 1 |
|
||||
} |
|
||||
] |
|
||||
} |
|
||||
|
|
||||
// Chart options |
|
||||
const options = { |
|
||||
responsive: true, |
|
||||
plugins: { |
|
||||
legend: { |
|
||||
position: 'top' |
|
||||
}, |
|
||||
title: { |
|
||||
display: true, |
|
||||
text: `Nombre des niveau (Total : ${numberOfClasses})` |
|
||||
} |
|
||||
}, |
|
||||
scales: { |
|
||||
y: { |
|
||||
beginAtZero: true, |
|
||||
max: maxStudentCount // Set max value for the Y axis |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
return ( |
|
||||
<div className={classe.mainHome}> |
|
||||
<div className={classeHome.header}> |
|
||||
<div className={classe.h1style}> |
|
||||
<div className={classeHome.blockTitle}> |
|
||||
<h1>Dashboard</h1> |
|
||||
<FormControl |
|
||||
sx={{ |
|
||||
m: 1, |
|
||||
width: '30%', |
|
||||
'& .MuiOutlinedInput-root': { |
|
||||
'&:hover fieldset': { |
|
||||
borderColor: '#ff9800' // Set the border color on hover |
|
||||
} |
|
||||
} |
|
||||
}} |
|
||||
size="small" |
|
||||
variant="outlined" |
|
||||
> |
|
||||
<InputLabel |
|
||||
id="demo-select-small-label" |
|
||||
sx={{ color: 'black', fontSize: '15px', textTransform: 'capitalize' }} |
|
||||
color="warning" |
|
||||
> |
|
||||
Année Scolaire |
|
||||
</InputLabel> |
|
||||
<Select |
|
||||
labelId="demo-select-small-label" |
|
||||
id="demo-select-small" |
|
||||
label="Année Scolaire" |
|
||||
color="warning" |
|
||||
defaultValue={'general'} |
|
||||
onChange={FilterAnneeScolaire} |
|
||||
sx={{ |
|
||||
background: 'white', |
|
||||
textTransform: 'capitalize', |
|
||||
display: 'flex', |
|
||||
alignItems: 'center', // Align icon and text vertically |
|
||||
'& .MuiSelect-icon': { |
|
||||
marginLeft: 'auto' // Keep the dropdown arrow to the right |
|
||||
} |
|
||||
}} |
|
||||
> |
|
||||
<MenuItem value="general" selected> |
|
||||
<em>Géneral</em> |
|
||||
</MenuItem> |
|
||||
{annee_scolaire.map((niveau) => ( |
|
||||
<MenuItem value={niveau.annee_scolaire} key={niveau.annee_scolaire}> |
|
||||
{niveau.annee_scolaire} |
|
||||
</MenuItem> |
|
||||
))} |
|
||||
</Select> |
|
||||
</FormControl> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
{/* display each bars */} |
|
||||
<div className={dclass.display}> |
|
||||
<Bar data={data} options={options} /> |
|
||||
</div> |
|
||||
</div> |
|
||||
) |
|
||||
} |
|
||||
|
|
||||
export default Home |
|
||||
@ -1,489 +0,0 @@ |
|||||
import React, { useEffect, useState } from 'react' |
|
||||
import classe from '../assets/AllStyleComponents.module.css' |
|
||||
import classeAdd from '../assets/AddStudent.module.css' |
|
||||
import classeHome from '../assets/Home.module.css' |
|
||||
import { Box, Button, Typography, ThemeProvider, Modal } from '@mui/material' |
|
||||
import { Link } from 'react-router-dom' |
|
||||
import { FaCloudDownloadAlt, FaCloudUploadAlt, FaFileExcel } from 'react-icons/fa' |
|
||||
import { IoMdReturnRight } from 'react-icons/io' |
|
||||
import { styled, createTheme } from '@mui/material/styles' |
|
||||
import * as XLSX from 'xlsx' |
|
||||
import Papa from 'papaparse' |
|
||||
import { DataGrid, GridToolbarContainer, GridToolbarColumnsButton } from '@mui/x-data-grid' |
|
||||
import { frFR } from '@mui/x-data-grid/locales' |
|
||||
import CustomBar from './CustomBar' |
|
||||
import dayjs from 'dayjs' |
|
||||
import svgSuccess from '../assets/success.svg' |
|
||||
import svgError from '../assets/error.svg' |
|
||||
import { MenuItem, Select, FormControl, InputLabel } from '@mui/material' |
|
||||
|
|
||||
const ImportMatiere = () => { |
|
||||
const VisuallyHiddenInput = styled('input')({ |
|
||||
clip: 'rect(0 0 0 0)', |
|
||||
clipPath: 'inset(50%)', |
|
||||
height: 1, |
|
||||
overflow: 'hidden', |
|
||||
position: 'absolute', |
|
||||
bottom: 0, |
|
||||
left: 0, |
|
||||
whiteSpace: 'nowrap', |
|
||||
width: 1 |
|
||||
}) |
|
||||
|
|
||||
const [error, setError] = useState('') |
|
||||
const [isInserted, setIsinserted] = useState(true) |
|
||||
const [message, setMessage] = useState('') |
|
||||
const [tableData, setTableData] = useState([]) |
|
||||
const [files, setFiles] = useState() |
|
||||
const [header, setHeader] = useState([]) |
|
||||
let field = [] |
|
||||
const [dynamicColumns, setColumns] = useState([]) |
|
||||
|
|
||||
for (let index = 0; index < header.length; index++) { |
|
||||
field.push(header[index].toLowerCase().replace(/\s+/g, '_')) |
|
||||
} |
|
||||
|
|
||||
useEffect(() => { |
|
||||
setColumns( |
|
||||
header.map((col, index) => ({ |
|
||||
field: field[index], // Converts the header text to field names (e.g., "Nom" -> "nom") |
|
||||
headerName: col, // Display the header as is |
|
||||
width: 150 // Adjust the width as needed |
|
||||
})) |
|
||||
) |
|
||||
}, [header]) |
|
||||
|
|
||||
const theme = createTheme({ |
|
||||
components: { |
|
||||
MuiIconButton: { |
|
||||
styleOverrides: { |
|
||||
root: { |
|
||||
color: 'gray' // Toolbar icons color |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
MuiButton: { |
|
||||
styleOverrides: { |
|
||||
root: { |
|
||||
color: '#121212' // Button text color |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
}) |
|
||||
|
|
||||
const paginationModel = { page: 0, pageSize: 5 } |
|
||||
|
|
||||
// Assuming `tableData` is an array where each entry corresponds to a student's data |
|
||||
const dataRow = tableData.map((etudiant, index) => { |
|
||||
// Dynamically create an object with fields from the header and data from `etudiant` |
|
||||
let row = { id: index + 1 } // Unique ID for each row |
|
||||
|
|
||||
field.forEach((fieldName, idx) => { |
|
||||
if (fieldName === 'date_de_naissance') { |
|
||||
row[fieldName] = dayjs(etudiant[idx]).format('DD-MM-YYYY') // Format date |
|
||||
} else { |
|
||||
row[fieldName] = etudiant[idx] // Assign value to field dynamically |
|
||||
} |
|
||||
}) |
|
||||
|
|
||||
return row |
|
||||
}) |
|
||||
|
|
||||
const handleFileChange = (event) => { |
|
||||
const file = event.target.files[0] |
|
||||
setFiles(event.target.files[0]) |
|
||||
if (!file) { |
|
||||
setError('No file selected') |
|
||||
return |
|
||||
} |
|
||||
|
|
||||
const fileExtension = file.name.split('.').pop().toLowerCase() |
|
||||
|
|
||||
if (fileExtension === 'xlsx') { |
|
||||
const reader = new FileReader() |
|
||||
reader.onload = (e) => { |
|
||||
const data = new Uint8Array(e.target.result) |
|
||||
const workbook = XLSX.read(data, { type: 'array' }) |
|
||||
|
|
||||
// Extract data from all sheets and combine |
|
||||
const allData = [] |
|
||||
workbook.SheetNames.forEach((sheetName) => { |
|
||||
const worksheet = workbook.Sheets[sheetName] |
|
||||
const rows = XLSX.utils.sheet_to_json(worksheet, { header: 1 }) |
|
||||
setHeader(rows[0]) // keep the header |
|
||||
allData.push(...rows.slice(1)) // Skip header row for each sheet |
|
||||
}) |
|
||||
setTableData(allData) |
|
||||
} |
|
||||
reader.readAsArrayBuffer(file) |
|
||||
} else if (fileExtension === 'csv') { |
|
||||
const reader = new FileReader() |
|
||||
reader.onload = (e) => { |
|
||||
Papa.parse(e.target.result, { |
|
||||
complete: (results) => { |
|
||||
setHeader(results.data[0]) // keep the header |
|
||||
setTableData(results.data.slice(1)) // Skip header row |
|
||||
}, |
|
||||
header: false |
|
||||
}) |
|
||||
} |
|
||||
reader.readAsText(file) |
|
||||
} else { |
|
||||
setError('Unsupported file format. Please upload .xlsx or .csv file.') |
|
||||
setTableData([]) |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
const exemplaireFileExcel = [ |
|
||||
{ |
|
||||
nom: 'matiere 1', |
|
||||
credit: 5, |
|
||||
uniter: 'MUI', |
|
||||
heure: 39 |
|
||||
}, |
|
||||
{ |
|
||||
nom: 'matiere 2', |
|
||||
credit: 5, |
|
||||
uniter: 'MUI', |
|
||||
heure: 39 |
|
||||
}, |
|
||||
{ |
|
||||
nom: 'matiere 3', |
|
||||
credit: 5, |
|
||||
uniter: 'MUI', |
|
||||
heure: 39 |
|
||||
}, |
|
||||
{ |
|
||||
nom: 'matiere 4', |
|
||||
credit: 5, |
|
||||
uniter: 'MUI', |
|
||||
heure: 39 |
|
||||
}, |
|
||||
{ |
|
||||
nom: 'matiere 5', |
|
||||
credit: 5, |
|
||||
uniter: 'MUI', |
|
||||
heure: 39 |
|
||||
} |
|
||||
] |
|
||||
|
|
||||
const convertToExcel = () => { |
|
||||
// convert json to sheet |
|
||||
const worksheet = XLSX.utils.json_to_sheet(exemplaireFileExcel) |
|
||||
|
|
||||
// Create a new workbook and append the worksheet |
|
||||
const workbook = XLSX.utils.book_new() |
|
||||
XLSX.utils.book_append_sheet(workbook, worksheet, 'Sheet1') |
|
||||
|
|
||||
// Write the workbook to a Blob and create a download link |
|
||||
const excelBuffer = XLSX.write(workbook, { bookType: 'xlsx', type: 'array' }) |
|
||||
const data = new Blob([excelBuffer], { type: 'application/octet-stream' }) |
|
||||
const url = URL.createObjectURL(data) |
|
||||
|
|
||||
// Trigger a download |
|
||||
const link = document.createElement('a') |
|
||||
link.href = url |
|
||||
link.download = 'exemplaier_matiere.xlsx' |
|
||||
link.click() |
|
||||
|
|
||||
// Clean up |
|
||||
URL.revokeObjectURL(url) |
|
||||
} |
|
||||
|
|
||||
const handleColumnVisibilityChange = (model) => { |
|
||||
// Get the currently visible columns |
|
||||
const visibleColumns = dynamicColumns.filter((column) => model[column.field] !== false) |
|
||||
|
|
||||
// Create a new Excel file with visible columns |
|
||||
createExcelFile(visibleColumns) |
|
||||
} |
|
||||
|
|
||||
const createExcelFile = (columns) => { |
|
||||
// Extract and set the header |
|
||||
const header = columns.reduce((acc, col) => { |
|
||||
acc[col.field] = col.headerName || col.field // Use headerName or field as default |
|
||||
return acc |
|
||||
}, {}) |
|
||||
|
|
||||
// Map the data rows to match the extracted headers |
|
||||
const worksheetData = dataRow.map((row) => { |
|
||||
const filteredRow = {} |
|
||||
columns.forEach((col) => { |
|
||||
const headerName = header[col.field] |
|
||||
filteredRow[headerName] = row[col.field] |
|
||||
}) |
|
||||
return filteredRow |
|
||||
}) |
|
||||
|
|
||||
// Create a worksheet from the data |
|
||||
const ws = XLSX.utils.json_to_sheet(worksheetData) |
|
||||
|
|
||||
// Create a workbook and add the worksheet to it |
|
||||
const wb = XLSX.utils.book_new() |
|
||||
XLSX.utils.book_append_sheet(wb, ws, 'Sheet1') |
|
||||
|
|
||||
// Generate the Excel file as binary data |
|
||||
const excelFile = XLSX.write(wb, { bookType: 'xlsx', type: 'array' }) |
|
||||
|
|
||||
// Create a Blob for the Excel data |
|
||||
const blob = new Blob([excelFile], { |
|
||||
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' |
|
||||
}) |
|
||||
|
|
||||
// Create a link element to trigger the download |
|
||||
const url = URL.createObjectURL(blob) |
|
||||
const link = document.createElement('a') |
|
||||
link.href = url |
|
||||
link.download = 'original-file.xlsx' // Original file name or any desired name |
|
||||
document.body.appendChild(link) |
|
||||
link.click() |
|
||||
document.body.removeChild(link) |
|
||||
URL.revokeObjectURL(url) // Clean up |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* fonction qui envoye dans le back |
|
||||
*/ |
|
||||
const handleImport = async () => { |
|
||||
// Code to handle file import (can open a file dialog or call handleFileChange) |
|
||||
|
|
||||
let response = await window.matieres.importExcel(files.path) |
|
||||
|
|
||||
console.log(response) |
|
||||
|
|
||||
if (response.message) { |
|
||||
setMessage(response.message) |
|
||||
} |
|
||||
|
|
||||
if (response.error) { |
|
||||
setIsinserted(true) |
|
||||
setOpen(true) |
|
||||
setTableData([]) |
|
||||
} else { |
|
||||
setIsinserted(false) |
|
||||
setOpen(true) |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* hook to open modal |
|
||||
*/ |
|
||||
const [open, setOpen] = useState(false) |
|
||||
|
|
||||
/** |
|
||||
* function to close modal |
|
||||
*/ |
|
||||
const handleClose = () => setOpen(false) |
|
||||
|
|
||||
/** |
|
||||
* function to return the view Modal |
|
||||
* |
|
||||
* @returns {JSX} |
|
||||
*/ |
|
||||
const modals = () => ( |
|
||||
<Modal |
|
||||
open={open} |
|
||||
onClose={handleClose} |
|
||||
aria-labelledby="modal-title" |
|
||||
aria-describedby="modal-description" |
|
||||
> |
|
||||
<Box |
|
||||
sx={{ |
|
||||
position: 'absolute', |
|
||||
top: '50%', |
|
||||
left: '50%', |
|
||||
transform: 'translate(-50%, -50%)', |
|
||||
width: 450, |
|
||||
bgcolor: 'background.paper', |
|
||||
boxShadow: 24, |
|
||||
p: 4 |
|
||||
}} |
|
||||
> |
|
||||
{isInserted ? ( |
|
||||
<Typography |
|
||||
style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: '10px' }} |
|
||||
> |
|
||||
<img src={svgSuccess} alt="" width={50} height={50} />{' '} |
|
||||
<span>Importation a été effectuée avec succès</span> |
|
||||
</Typography> |
|
||||
) : ( |
|
||||
<Typography |
|
||||
style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: '10px' }} |
|
||||
> |
|
||||
<img src={svgError} alt="" width={50} height={50} />{' '} |
|
||||
<span> |
|
||||
L'importation n'a pas été effectuée |
|
||||
<br /> |
|
||||
{message} |
|
||||
</span> |
|
||||
</Typography> |
|
||||
)} |
|
||||
<Box |
|
||||
sx={{ |
|
||||
marginTop: '2%', |
|
||||
display: 'flex', |
|
||||
gap: '20px', |
|
||||
alignItems: 'end', |
|
||||
justifyContent: 'flex-end' |
|
||||
}} |
|
||||
> |
|
||||
<Button onClick={handleClose} color="warning" variant="contained"> |
|
||||
OK |
|
||||
</Button> |
|
||||
</Box> |
|
||||
</Box> |
|
||||
</Modal> |
|
||||
) |
|
||||
|
|
||||
// Handle header name change for a specific field and auto-export to Excel |
|
||||
const handleHeaderChange = (field, newHeaderName) => { |
|
||||
setColumns((prevColumns) => |
|
||||
prevColumns.map((col) => (col.field === field ? { ...col, headerName: newHeaderName } : col)) |
|
||||
) |
|
||||
} |
|
||||
|
|
||||
return ( |
|
||||
<div className={classe.mainHome}> |
|
||||
{modals()} |
|
||||
<div className={classeAdd.header}> |
|
||||
<div className={classe.h1style}> |
|
||||
<div className={classeHome.blockTitle}> |
|
||||
<h1 style={{ display: 'flex', alignItems: 'center', gap: '10px' }}> |
|
||||
<FaFileExcel /> |
|
||||
Importation de données |
|
||||
</h1> |
|
||||
<Link to={'/addmatiere'}> |
|
||||
<Button color="warning" variant="contained"> |
|
||||
<IoMdReturnRight style={{ fontSize: '20px' }} /> |
|
||||
</Button> |
|
||||
</Link> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
|
|
||||
{/* displaying */} |
|
||||
<Box |
|
||||
sx={{ |
|
||||
position: 'absolute', |
|
||||
top: '52%', |
|
||||
left: '52%', |
|
||||
transform: 'translate(-50%, -50%)', |
|
||||
width: '95%', |
|
||||
borderRadius: '2%', |
|
||||
bgcolor: 'background.paper', |
|
||||
boxShadow: 24, |
|
||||
p: 4 |
|
||||
}} |
|
||||
> |
|
||||
<Box sx={{ mt: 2, display: 'flex', alignItems: 'center', flexDirection: 'column' }}> |
|
||||
<div style={{ display: 'flex', gap: '20px', marginBottom: '20px' }}> |
|
||||
<Button |
|
||||
component="label" |
|
||||
variant="contained" |
|
||||
color="warning" |
|
||||
startIcon={<FaCloudUploadAlt />} |
|
||||
> |
|
||||
Charger un fichier excel |
|
||||
<VisuallyHiddenInput type="file" onChange={handleFileChange} accept=".xlsx, .csv" /> |
|
||||
</Button> |
|
||||
<Button |
|
||||
component="label" |
|
||||
variant="contained" |
|
||||
color="error" |
|
||||
startIcon={<FaCloudDownloadAlt />} |
|
||||
onClick={convertToExcel} |
|
||||
> |
|
||||
Télécharger le modèle d'import |
|
||||
</Button> |
|
||||
</div> |
|
||||
{error && ( |
|
||||
<Typography color="error" variant="body2"> |
|
||||
{error} |
|
||||
</Typography> |
|
||||
)} |
|
||||
{tableData.length > 0 && ( |
|
||||
<ThemeProvider theme={theme}> |
|
||||
<div |
|
||||
style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }} |
|
||||
> |
|
||||
{/* Dropdowns for each column */} |
|
||||
{dynamicColumns.map((col) => ( |
|
||||
<FormControl |
|
||||
key={col.field} |
|
||||
sx={{ |
|
||||
m: 1, |
|
||||
width: '100%', |
|
||||
'& .MuiOutlinedInput-root': { |
|
||||
'&:hover fieldset': { |
|
||||
borderColor: '#ff9800' // Set the border color on hover |
|
||||
} |
|
||||
} |
|
||||
}} |
|
||||
size="small" |
|
||||
color="warning" |
|
||||
variant="outlined" |
|
||||
style={{ margin: 10, width: 200 }} |
|
||||
> |
|
||||
<InputLabel id={col.headerName}>{col.headerName}</InputLabel> |
|
||||
<Select |
|
||||
labelId={col.headerName} |
|
||||
value={col.headerName} |
|
||||
label={col.headerName} |
|
||||
color="warning" |
|
||||
name={col.headerName} |
|
||||
onChange={(e) => handleHeaderChange(col.field, e.target.value)} |
|
||||
> |
|
||||
<MenuItem value={col.headerName}>{col.headerName}</MenuItem> |
|
||||
<MenuItem value={'nom'}>nom</MenuItem> |
|
||||
<MenuItem value={'credit'}>credit</MenuItem> |
|
||||
<MenuItem value={`uniter`}>uniter d'enseignement</MenuItem> |
|
||||
<MenuItem value={`ue`}>UE</MenuItem> |
|
||||
</Select> |
|
||||
</FormControl> |
|
||||
))} |
|
||||
</div> |
|
||||
<div |
|
||||
style={{ |
|
||||
display: 'flex', |
|
||||
alignItems: 'center', |
|
||||
justifyContent: 'center', |
|
||||
flexDirection: 'column', |
|
||||
width: '100%' |
|
||||
}} |
|
||||
> |
|
||||
<DataGrid |
|
||||
rows={dataRow} |
|
||||
columns={dynamicColumns} |
|
||||
initialState={{ pagination: { paginationModel } }} |
|
||||
onColumnVisibilityModelChange={handleColumnVisibilityChange} // Listen for column visibility changes |
|
||||
pageSizeOptions={[5, 10]} |
|
||||
disableRowSelectionOnClick |
|
||||
sx={{ |
|
||||
width: '100%', |
|
||||
height: '100%', |
|
||||
minHeight: 400, |
|
||||
maxWidth: '100%', // Prevents grid from exceeding the container width |
|
||||
'@media (max-width: 600px)': { |
|
||||
width: '100%', |
|
||||
height: 'auto', // Adjust height for smaller screens |
|
||||
fontSize: '0.8rem' // Smaller font size for better readability on small screens |
|
||||
}, |
|
||||
'@media (max-width: 960px)': { |
|
||||
fontSize: '1rem' // Adjust font size for medium screens |
|
||||
} |
|
||||
}} |
|
||||
slots={{ toolbar: CustomBar }} |
|
||||
slotProps={{ toolbar: { onImport: handleImport } }} |
|
||||
localeText={frFR.components.MuiDataGrid.defaultProps.localeText} |
|
||||
/> |
|
||||
</div> |
|
||||
</ThemeProvider> |
|
||||
)} |
|
||||
</Box> |
|
||||
</Box> |
|
||||
</div> |
|
||||
) |
|
||||
} |
|
||||
|
|
||||
export default ImportMatiere |
|
||||
@ -1,397 +0,0 @@ |
|||||
import React, { useState } from 'react' |
|
||||
import classe from '../assets/AllStyleComponents.module.css' |
|
||||
import classeAdd from '../assets/AddStudent.module.css' |
|
||||
import classeHome from '../assets/Home.module.css' |
|
||||
import { Box, Button, Typography, ThemeProvider, Modal } from '@mui/material' |
|
||||
import { Link } from 'react-router-dom' |
|
||||
import { FaCloudDownloadAlt, FaCloudUploadAlt, FaFileExcel } from 'react-icons/fa' |
|
||||
import { IoMdReturnRight } from 'react-icons/io' |
|
||||
import { styled, createTheme } from '@mui/material/styles' |
|
||||
import * as XLSX from 'xlsx' |
|
||||
import Papa from 'papaparse' |
|
||||
import { DataGrid, GridToolbarContainer, GridToolbarColumnsButton } from '@mui/x-data-grid' |
|
||||
import { frFR } from '@mui/x-data-grid/locales' |
|
||||
import CustomBar from './CustomBar' |
|
||||
import dayjs from 'dayjs' |
|
||||
import svgSuccess from '../assets/success.svg' |
|
||||
|
|
||||
const ImportNiveau = () => { |
|
||||
const VisuallyHiddenInput = styled('input')({ |
|
||||
clip: 'rect(0 0 0 0)', |
|
||||
clipPath: 'inset(50%)', |
|
||||
height: 1, |
|
||||
overflow: 'hidden', |
|
||||
position: 'absolute', |
|
||||
bottom: 0, |
|
||||
left: 0, |
|
||||
whiteSpace: 'nowrap', |
|
||||
width: 1 |
|
||||
}) |
|
||||
|
|
||||
const [error, setError] = useState('') |
|
||||
const [tableData, setTableData] = useState([]) |
|
||||
const [files, setFiles] = useState() |
|
||||
const [header, setHeader] = useState([]) |
|
||||
let field = [] |
|
||||
|
|
||||
for (let index = 0; index < header.length; index++) { |
|
||||
field.push(header[index].toLowerCase().replace(/\s+/g, '_')) |
|
||||
} |
|
||||
|
|
||||
const dynamicColumns = header.map((col, index) => ({ |
|
||||
field: col.toLowerCase().replace(/\s+/g, '_'), // Converts the header text to field names (e.g., "Nom" -> "nom") |
|
||||
headerName: col, // Display the header as is |
|
||||
width: 150 // Adjust the width as needed |
|
||||
})) |
|
||||
|
|
||||
const theme = createTheme({ |
|
||||
components: { |
|
||||
MuiIconButton: { |
|
||||
styleOverrides: { |
|
||||
root: { |
|
||||
color: 'gray' // Toolbar icons color |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
MuiButton: { |
|
||||
styleOverrides: { |
|
||||
root: { |
|
||||
color: '#121212' // Button text color |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
}) |
|
||||
|
|
||||
const paginationModel = { page: 0, pageSize: 5 } |
|
||||
|
|
||||
// Assuming `tableData` is an array where each entry corresponds to a student's data |
|
||||
const dataRow = tableData.map((etudiant, index) => { |
|
||||
// Dynamically create an object with fields from the header and data from `etudiant` |
|
||||
let row = { id: index + 1 } // Unique ID for each row |
|
||||
|
|
||||
field.forEach((fieldName, idx) => { |
|
||||
if (fieldName === 'date_de_naissance') { |
|
||||
row[fieldName] = dayjs(etudiant[idx]).format('DD-MM-YYYY') // Format date |
|
||||
} else { |
|
||||
row[fieldName] = etudiant[idx] // Assign value to field dynamically |
|
||||
} |
|
||||
}) |
|
||||
|
|
||||
return row |
|
||||
}) |
|
||||
|
|
||||
const handleFileChange = (event) => { |
|
||||
const file = event.target.files[0] |
|
||||
setFiles(event.target.files[0]) |
|
||||
if (!file) { |
|
||||
setError('No file selected') |
|
||||
return |
|
||||
} |
|
||||
|
|
||||
const fileExtension = file.name.split('.').pop().toLowerCase() |
|
||||
|
|
||||
if (fileExtension === 'xlsx') { |
|
||||
const reader = new FileReader() |
|
||||
reader.onload = (e) => { |
|
||||
const data = new Uint8Array(e.target.result) |
|
||||
const workbook = XLSX.read(data, { type: 'array' }) |
|
||||
|
|
||||
// Extract data from all sheets and combine |
|
||||
const allData = [] |
|
||||
workbook.SheetNames.forEach((sheetName) => { |
|
||||
const worksheet = workbook.Sheets[sheetName] |
|
||||
const rows = XLSX.utils.sheet_to_json(worksheet, { header: 1 }) |
|
||||
setHeader(rows[0]) // keep the header |
|
||||
allData.push(...rows.slice(1)) // Skip header row for each sheet |
|
||||
}) |
|
||||
setTableData(allData) |
|
||||
} |
|
||||
reader.readAsArrayBuffer(file) |
|
||||
} else if (fileExtension === 'csv') { |
|
||||
const reader = new FileReader() |
|
||||
reader.onload = (e) => { |
|
||||
Papa.parse(e.target.result, { |
|
||||
complete: (results) => { |
|
||||
setHeader(results.data[0]) // keep the header |
|
||||
setTableData(results.data.slice(1)) // Skip header row |
|
||||
}, |
|
||||
header: false |
|
||||
}) |
|
||||
} |
|
||||
reader.readAsText(file) |
|
||||
} else { |
|
||||
setError('Unsupported file format. Please upload .xlsx or .csv file.') |
|
||||
setTableData([]) |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
const exemplaireFileExcel = [ |
|
||||
{ |
|
||||
Nom: 'L1' |
|
||||
}, |
|
||||
{ |
|
||||
Nom: 'L2' |
|
||||
}, |
|
||||
{ |
|
||||
Nom: 'L3' |
|
||||
}, |
|
||||
{ |
|
||||
Nom: 'M1' |
|
||||
}, |
|
||||
{ |
|
||||
Nom: 'M2' |
|
||||
} |
|
||||
] |
|
||||
|
|
||||
const convertToExcel = () => { |
|
||||
// convert json to sheet |
|
||||
const worksheet = XLSX.utils.json_to_sheet(exemplaireFileExcel) |
|
||||
|
|
||||
// Create a new workbook and append the worksheet |
|
||||
const workbook = XLSX.utils.book_new() |
|
||||
XLSX.utils.book_append_sheet(workbook, worksheet, 'Sheet1') |
|
||||
|
|
||||
// Write the workbook to a Blob and create a download link |
|
||||
const excelBuffer = XLSX.write(workbook, { bookType: 'xlsx', type: 'array' }) |
|
||||
const data = new Blob([excelBuffer], { type: 'application/octet-stream' }) |
|
||||
const url = URL.createObjectURL(data) |
|
||||
|
|
||||
// Trigger a download |
|
||||
const link = document.createElement('a') |
|
||||
link.href = url |
|
||||
link.download = 'exemplaire_niveau.xlsx' |
|
||||
link.click() |
|
||||
|
|
||||
// Clean up |
|
||||
URL.revokeObjectURL(url) |
|
||||
} |
|
||||
|
|
||||
const handleColumnVisibilityChange = (model) => { |
|
||||
// Get the currently visible columns |
|
||||
const visibleColumns = dynamicColumns.filter((column) => model[column.field] !== false) |
|
||||
|
|
||||
// Create a new Excel file with visible columns |
|
||||
createExcelFile(visibleColumns) |
|
||||
} |
|
||||
|
|
||||
const createExcelFile = (columns) => { |
|
||||
// Extract and set the header |
|
||||
const header = columns.reduce((acc, col) => { |
|
||||
acc[col.field] = col.headerName || col.field // Use headerName or field as default |
|
||||
return acc |
|
||||
}, {}) |
|
||||
|
|
||||
// Map the data rows to match the extracted headers |
|
||||
const worksheetData = dataRow.map((row) => { |
|
||||
const filteredRow = {} |
|
||||
columns.forEach((col) => { |
|
||||
const headerName = header[col.field] |
|
||||
filteredRow[headerName] = row[col.field] |
|
||||
}) |
|
||||
return filteredRow |
|
||||
}) |
|
||||
|
|
||||
// Create a worksheet from the data |
|
||||
const ws = XLSX.utils.json_to_sheet(worksheetData) |
|
||||
|
|
||||
// Create a workbook and add the worksheet to it |
|
||||
const wb = XLSX.utils.book_new() |
|
||||
XLSX.utils.book_append_sheet(wb, ws, 'Sheet1') |
|
||||
|
|
||||
// Generate the Excel file as binary data |
|
||||
const excelFile = XLSX.write(wb, { bookType: 'xlsx', type: 'array' }) |
|
||||
|
|
||||
// Create a Blob for the Excel data |
|
||||
const blob = new Blob([excelFile], { |
|
||||
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' |
|
||||
}) |
|
||||
|
|
||||
// Create a link element to trigger the download |
|
||||
const url = URL.createObjectURL(blob) |
|
||||
const link = document.createElement('a') |
|
||||
link.href = url |
|
||||
link.download = 'original-file.xlsx' // Original file name or any desired name |
|
||||
document.body.appendChild(link) |
|
||||
link.click() |
|
||||
document.body.removeChild(link) |
|
||||
URL.revokeObjectURL(url) // Clean up |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* fonction qui envoye dans le back |
|
||||
*/ |
|
||||
const handleImport = async () => { |
|
||||
// Code to handle file import (can open a file dialog or call handleFileChange) |
|
||||
|
|
||||
let response = await window.niveaus.importNiveau(files.path) |
|
||||
|
|
||||
console.log(response) |
|
||||
if (response.success) { |
|
||||
setOpen(true) |
|
||||
setTableData([]) |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* hook to open modal |
|
||||
*/ |
|
||||
const [open, setOpen] = useState(false) |
|
||||
|
|
||||
/** |
|
||||
* function to close modal |
|
||||
*/ |
|
||||
const handleClose = () => setOpen(false) |
|
||||
|
|
||||
/** |
|
||||
* function to return the view Modal |
|
||||
* |
|
||||
* @returns {JSX} |
|
||||
*/ |
|
||||
const modals = () => ( |
|
||||
<Modal |
|
||||
open={open} |
|
||||
onClose={handleClose} |
|
||||
aria-labelledby="modal-title" |
|
||||
aria-describedby="modal-description" |
|
||||
> |
|
||||
<Box |
|
||||
sx={{ |
|
||||
position: 'absolute', |
|
||||
top: '50%', |
|
||||
left: '50%', |
|
||||
transform: 'translate(-50%, -50%)', |
|
||||
width: 450, |
|
||||
bgcolor: 'background.paper', |
|
||||
boxShadow: 24, |
|
||||
p: 4 |
|
||||
}} |
|
||||
> |
|
||||
<Typography |
|
||||
style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: '10px' }} |
|
||||
> |
|
||||
<img src={svgSuccess} alt="" width={50} height={50} />{' '} |
|
||||
<span>Importation a été effectuée avec succès</span> |
|
||||
</Typography> |
|
||||
<Box |
|
||||
sx={{ |
|
||||
marginTop: '2%', |
|
||||
display: 'flex', |
|
||||
gap: '20px', |
|
||||
alignItems: 'end', |
|
||||
justifyContent: 'flex-end' |
|
||||
}} |
|
||||
> |
|
||||
<Button onClick={handleClose} color="warning" variant="contained"> |
|
||||
OK |
|
||||
</Button> |
|
||||
</Box> |
|
||||
</Box> |
|
||||
</Modal> |
|
||||
) |
|
||||
|
|
||||
return ( |
|
||||
<div className={classe.mainHome}> |
|
||||
{modals()} |
|
||||
<div className={classeAdd.header}> |
|
||||
<div className={classe.h1style}> |
|
||||
<div className={classeHome.blockTitle}> |
|
||||
<h1 style={{ display: 'flex', alignItems: 'center', gap: '10px' }}> |
|
||||
<FaFileExcel /> |
|
||||
Importation de données |
|
||||
</h1> |
|
||||
<Link to={'/addniveau'}> |
|
||||
<Button color="warning" variant="contained"> |
|
||||
<IoMdReturnRight style={{ fontSize: '20px' }} /> |
|
||||
</Button> |
|
||||
</Link> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
|
|
||||
{/* displaying */} |
|
||||
<Box |
|
||||
sx={{ |
|
||||
position: 'absolute', |
|
||||
top: '52%', |
|
||||
left: '52%', |
|
||||
transform: 'translate(-50%, -50%)', |
|
||||
width: '95%', |
|
||||
borderRadius: '2%', |
|
||||
bgcolor: 'background.paper', |
|
||||
boxShadow: 24, |
|
||||
p: 4 |
|
||||
}} |
|
||||
> |
|
||||
<Box sx={{ mt: 2, display: 'flex', alignItems: 'center', flexDirection: 'column' }}> |
|
||||
<div style={{ display: 'flex', gap: '20px', marginBottom: '20px' }}> |
|
||||
<Button |
|
||||
component="label" |
|
||||
variant="contained" |
|
||||
color="warning" |
|
||||
startIcon={<FaCloudUploadAlt />} |
|
||||
> |
|
||||
Charger un fichier excel |
|
||||
<VisuallyHiddenInput type="file" onChange={handleFileChange} accept=".xlsx, .csv" /> |
|
||||
</Button> |
|
||||
<Button |
|
||||
component="label" |
|
||||
variant="contained" |
|
||||
color="error" |
|
||||
startIcon={<FaCloudDownloadAlt />} |
|
||||
onClick={convertToExcel} |
|
||||
> |
|
||||
Télécharger le modèle d'import |
|
||||
</Button> |
|
||||
</div> |
|
||||
{error && ( |
|
||||
<Typography color="error" variant="body2"> |
|
||||
{error} |
|
||||
</Typography> |
|
||||
)} |
|
||||
{tableData.length > 0 && ( |
|
||||
<ThemeProvider theme={theme}> |
|
||||
<div |
|
||||
style={{ |
|
||||
display: 'flex', |
|
||||
alignItems: 'center', |
|
||||
justifyContent: 'center', |
|
||||
flexDirection: 'column', |
|
||||
width: '100%' |
|
||||
}} |
|
||||
> |
|
||||
<DataGrid |
|
||||
rows={dataRow} |
|
||||
columns={dynamicColumns} |
|
||||
initialState={{ pagination: { paginationModel } }} |
|
||||
onColumnVisibilityModelChange={handleColumnVisibilityChange} // Listen for column visibility changes |
|
||||
pageSizeOptions={[5, 10]} |
|
||||
disableRowSelectionOnClick |
|
||||
sx={{ |
|
||||
width: '100%', |
|
||||
height: '100%', |
|
||||
minHeight: 400, |
|
||||
maxWidth: '100%', // Prevents grid from exceeding the container width |
|
||||
'@media (max-width: 600px)': { |
|
||||
width: '100%', |
|
||||
height: 'auto', // Adjust height for smaller screens |
|
||||
fontSize: '0.8rem' // Smaller font size for better readability on small screens |
|
||||
}, |
|
||||
'@media (max-width: 960px)': { |
|
||||
fontSize: '1rem' // Adjust font size for medium screens |
|
||||
} |
|
||||
}} |
|
||||
slots={{ toolbar: CustomBar }} |
|
||||
slotProps={{ toolbar: { onImport: handleImport } }} |
|
||||
localeText={frFR.components.MuiDataGrid.defaultProps.localeText} |
|
||||
/> |
|
||||
</div> |
|
||||
</ThemeProvider> |
|
||||
)} |
|
||||
</Box> |
|
||||
</Box> |
|
||||
</div> |
|
||||
) |
|
||||
} |
|
||||
|
|
||||
export default ImportNiveau |
|
||||
@ -1,133 +0,0 @@ |
|||||
import React, { useEffect, useState } from 'react' |
|
||||
import { |
|
||||
Dialog, |
|
||||
DialogActions, |
|
||||
DialogContent, |
|
||||
DialogTitle, |
|
||||
TextField, |
|
||||
Button, |
|
||||
InputAdornment, |
|
||||
Box, |
|
||||
Grid, |
|
||||
FormControl, |
|
||||
InputLabel, |
|
||||
Select, |
|
||||
OutlinedInput, |
|
||||
MenuItem |
|
||||
} from '@mui/material' |
|
||||
|
|
||||
|
|
||||
const IpConfig = ({ open, onClose }) => { |
|
||||
|
|
||||
const [localIp, setLocalIp] = useState(""); |
|
||||
const [formData, setFormData] = useState({ |
|
||||
ipname: "", |
|
||||
id: '', |
|
||||
}) |
|
||||
const [ip, setIp] = useState({}); |
|
||||
|
|
||||
useEffect(() => { |
|
||||
|
|
||||
window.notesysteme.getIPConfig().then((response) => { |
|
||||
setIp(response) |
|
||||
}) |
|
||||
|
|
||||
const getLocalIP = async () => { |
|
||||
const pc = new RTCPeerConnection(); |
|
||||
pc.createDataChannel(""); // Create a data channel |
|
||||
pc.createOffer().then((offer) => pc.setLocalDescription(offer)); |
|
||||
|
|
||||
pc.onicecandidate = (event) => { |
|
||||
if (event && event.candidate) { |
|
||||
const ipRegex = /([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})/; |
|
||||
const match = ipRegex.exec(event.candidate.candidate); |
|
||||
if (match) setLocalIp(match[1]); |
|
||||
pc.close(); |
|
||||
} |
|
||||
}; |
|
||||
}; |
|
||||
|
|
||||
getLocalIP(); |
|
||||
}, []); |
|
||||
|
|
||||
useEffect(() => { |
|
||||
if (ip) { |
|
||||
setFormData((prevData) => ({ |
|
||||
...prevData, |
|
||||
ipname: ip.ipname, |
|
||||
id: ip.id |
|
||||
})) |
|
||||
} |
|
||||
|
|
||||
}, [ip]); |
|
||||
|
|
||||
const handleChange = (e) => { |
|
||||
const { name, value } = e.target |
|
||||
setFormData({ ...formData, [name]: value }) |
|
||||
} |
|
||||
|
|
||||
const formSubmit = async (e) => { |
|
||||
e.preventDefault() |
|
||||
|
|
||||
if (ip == undefined) { |
|
||||
let response = await window.notesysteme.createIPConfig(formData) |
|
||||
console.log('running the creation: ', response); |
|
||||
if (response.changes) { |
|
||||
window.notesysteme.getIPConfig().then((response) => { |
|
||||
setIp(response) |
|
||||
}) |
|
||||
onClose() |
|
||||
} |
|
||||
} else { |
|
||||
let response = await window.notesysteme.updateIPConfig(formData) |
|
||||
console.log('running the update: ', response); |
|
||||
if (response.changes) { |
|
||||
window.notesysteme.getIPConfig().then((response) => { |
|
||||
setIp(response) |
|
||||
}) |
|
||||
onClose() |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
} |
|
||||
|
|
||||
return ( |
|
||||
<Dialog open={open} onClose={onClose}> |
|
||||
<form action="" onSubmit={formSubmit}> |
|
||||
<DialogTitle>Configuration de l'adresse IP</DialogTitle> |
|
||||
<div style={{textAlign:"end"}}>Votre Local IP: {localIp || "Detecting..."}</div> |
|
||||
<DialogContent> |
|
||||
<Box sx={{ flexGrow: 1 }}> |
|
||||
<Grid container spacing={2}> |
|
||||
<Grid item xs={12} sm={12}> |
|
||||
<TextField |
|
||||
autoFocus |
|
||||
margin="dense" |
|
||||
name="ipname" |
|
||||
required |
|
||||
label="IP du PC serveur" |
|
||||
type="text" |
|
||||
fullWidth |
|
||||
variant="outlined" |
|
||||
value={formData.ipname} |
|
||||
color="warning" |
|
||||
onChange={handleChange} |
|
||||
/> |
|
||||
</Grid> |
|
||||
</Grid> |
|
||||
</Box> |
|
||||
</DialogContent> |
|
||||
<DialogActions> |
|
||||
<Button onClick={onClose} color="warning"> |
|
||||
Annuler |
|
||||
</Button> |
|
||||
<Button type="submit" color="warning"> |
|
||||
Soumettre |
|
||||
</Button> |
|
||||
</DialogActions> |
|
||||
</form> |
|
||||
</Dialog> |
|
||||
) |
|
||||
} |
|
||||
|
|
||||
export default IpConfig |
|
||||
@ -1,157 +0,0 @@ |
|||||
import { useRef, useState } from 'react' |
|
||||
// import { Container, Row, Col, Form, Button, Card, InputGroup } from 'react-bootstrap'; |
|
||||
import { Container, Grid, Card, Typography, TextField, Button, InputAdornment } from '@mui/material' |
|
||||
import { FaUserCircle, FaLock, FaUser } from 'react-icons/fa' |
|
||||
import classe from '../assets/Login.module.css' |
|
||||
import { useAuthContext } from '../contexts/AuthContext' |
|
||||
import { Link } from 'react-router-dom' |
|
||||
import { ValidationLogin, invalidCredential } from './validation/Login' |
|
||||
|
|
||||
const Login = () => { |
|
||||
/** |
|
||||
* token from the AuthContext in the context folder |
|
||||
*/ |
|
||||
const { setToken } = useAuthContext() |
|
||||
|
|
||||
/** |
|
||||
* hook to store our data from the input element |
|
||||
*/ |
|
||||
const [username, setUsername] = useState() |
|
||||
const [password, setPassword] = useState() |
|
||||
|
|
||||
/** |
|
||||
* ref for our username and password input and the error span |
|
||||
*/ |
|
||||
const userNameRef = useRef() |
|
||||
const passwordRef = useRef() |
|
||||
const userNameError = useRef() |
|
||||
const passwordError = useRef() |
|
||||
|
|
||||
/** |
|
||||
* function to send the data to the IPCRender |
|
||||
* and make login |
|
||||
* |
|
||||
* @param {any} e |
|
||||
*/ |
|
||||
const formLogin = async (e) => { |
|
||||
e.preventDefault() |
|
||||
|
|
||||
let validate = ValidationLogin( |
|
||||
userNameRef.current, |
|
||||
passwordRef.current, |
|
||||
userNameError.current, |
|
||||
passwordError.current |
|
||||
) |
|
||||
|
|
||||
if (validate) { |
|
||||
const response = await window.allUser.login({ username, password }) |
|
||||
|
|
||||
if (response.success) { |
|
||||
// Redirect to main window |
|
||||
setToken(JSON.stringify(response.user)) |
|
||||
} else { |
|
||||
invalidCredential(userNameError.current, response.error) |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
return ( |
|
||||
<Container |
|
||||
maxWidth={false} |
|
||||
sx={{ minHeight: '100vh', display: 'flex', alignItems: 'center', justifyContent: 'center' }} |
|
||||
className={classe.container} |
|
||||
> |
|
||||
<Grid container justifyContent="center"> |
|
||||
<Grid item xs={12} md={6} lg={4}> |
|
||||
<Card className={`p-4 shadow ` + classe.cards} sx={{ padding: 4, boxShadow: 3 }}> |
|
||||
<div style={{ textAlign: 'center', marginBottom: '1rem' }}> |
|
||||
<FaUserCircle size={60} color="dark" className="text-light" /> |
|
||||
</div> |
|
||||
<Typography variant="h5" align="center" className="text-light" gutterBottom> |
|
||||
Université de Toamasina |
|
||||
</Typography> |
|
||||
<form onSubmit={formLogin} className={classe.formulaireLogin}> |
|
||||
<TextField |
|
||||
label="Nom d'utilisateur" |
|
||||
color="secondary" |
|
||||
variant="outlined" |
|
||||
fullWidth |
|
||||
placeholder="Nom d'utilisateur" |
|
||||
margin="normal" |
|
||||
InputProps={{ |
|
||||
startAdornment: ( |
|
||||
<InputAdornment position="start"> |
|
||||
<FaUser style={{ color: 'white' }} /> |
|
||||
</InputAdornment> |
|
||||
) |
|
||||
}} |
|
||||
className={classe.input} |
|
||||
onChange={(e) => setUsername(e.target.value)} |
|
||||
inputRef={userNameRef} |
|
||||
sx={{ |
|
||||
'& .MuiOutlinedInput-root': { |
|
||||
'& fieldset': { |
|
||||
borderColor: 'white' // Set the border color when not focused |
|
||||
}, |
|
||||
'&:hover fieldset': { |
|
||||
borderColor: 'rgb(156, 39, 176)' // Set the border color on hover |
|
||||
} |
|
||||
} |
|
||||
}} |
|
||||
/> |
|
||||
<span className="text-danger" ref={userNameError}></span> |
|
||||
<TextField |
|
||||
label="Mot de passe" |
|
||||
color="secondary" |
|
||||
variant="outlined" |
|
||||
type="password" |
|
||||
fullWidth |
|
||||
placeholder="Mot de passe" |
|
||||
margin="normal" |
|
||||
className={classe.input} |
|
||||
InputProps={{ |
|
||||
startAdornment: ( |
|
||||
<InputAdornment position="start"> |
|
||||
<FaLock style={{ color: 'white' }} /> |
|
||||
</InputAdornment> |
|
||||
) |
|
||||
}} |
|
||||
onChange={(e) => setPassword(e.target.value)} |
|
||||
inputRef={passwordRef} |
|
||||
sx={{ |
|
||||
'& .MuiOutlinedInput-root': { |
|
||||
'& fieldset': { |
|
||||
borderColor: 'white' // Set the border color when not focused |
|
||||
}, |
|
||||
'&:hover fieldset': { |
|
||||
borderColor: 'rgb(156, 39, 176)' // Set the border color on hover |
|
||||
} |
|
||||
} |
|
||||
}} |
|
||||
/> |
|
||||
<span ref={passwordError} className="text-danger"></span> |
|
||||
|
|
||||
<Button |
|
||||
variant="contained" |
|
||||
color="secondary" |
|
||||
type="submit" |
|
||||
fullWidth |
|
||||
sx={{ marginTop: 2 }} |
|
||||
> |
|
||||
Se connecter |
|
||||
</Button> |
|
||||
|
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between', marginTop: '1rem' }}> |
|
||||
<Link to="/forgotpassword" className="text-light"> |
|
||||
Mot de passe oublié ? |
|
||||
</Link> |
|
||||
</div> |
|
||||
</form> |
|
||||
</Card> |
|
||||
</Grid> |
|
||||
</Grid> |
|
||||
</Container> |
|
||||
) |
|
||||
} |
|
||||
|
|
||||
export default Login |
|
||||
@ -1,98 +0,0 @@ |
|||||
import React, { useState } from 'react' |
|
||||
import { Box, Button, Typography, Grid, Paper } from '@mui/material' |
|
||||
import { GrManual } from 'react-icons/gr' |
|
||||
import { Document, Page } from 'react-pdf/dist/esm/entry.webpack5' |
|
||||
import { FaAngleDoubleLeft, FaAngleDoubleRight } from 'react-icons/fa' |
|
||||
import pdfUrl from '../assets/manuel.pdf' |
|
||||
import classe from '../assets/AllStyleComponents.module.css' |
|
||||
import classeAdd from '../assets/AddStudent.module.css' |
|
||||
import classeHome from '../assets/Home.module.css' |
|
||||
|
|
||||
const Manuel = () => { |
|
||||
const [numPages, setNumPages] = useState(null) // Use correct name |
|
||||
const [pageNumber, setPageNumber] = useState(1) |
|
||||
|
|
||||
// Fix the onDocumentSuccess function |
|
||||
function onDocumentSuccess({ numPages }) { |
|
||||
setNumPages(numPages) // Use the correct property |
|
||||
} |
|
||||
|
|
||||
const prevPage = () => { |
|
||||
if (pageNumber > 1) { |
|
||||
setPageNumber(pageNumber - 1) |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
const nextPage = () => { |
|
||||
if (pageNumber < numPages) { |
|
||||
setPageNumber(pageNumber + 1) |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
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' }}> |
|
||||
<GrManual /> Manuel d'utilisation |
|
||||
</h1> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
<Paper |
|
||||
sx={{ |
|
||||
height: 'auto', |
|
||||
minHeight: 500, |
|
||||
display: 'flex', |
|
||||
alignItems: 'center', |
|
||||
justifyContent: 'center', |
|
||||
flexDirection: 'column', |
|
||||
width: '100%' |
|
||||
}} |
|
||||
> |
|
||||
<Grid sx={{ display: 'flex', gap: '20px', padding: '1%' }}> |
|
||||
<Button color="warning" variant="contained" onClick={prevPage}> |
|
||||
<FaAngleDoubleLeft /> |
|
||||
</Button> |
|
||||
<Button color="warning" variant="contained" onClick={nextPage}> |
|
||||
<FaAngleDoubleRight /> |
|
||||
</Button> |
|
||||
</Grid> |
|
||||
|
|
||||
<Typography variant="h6"> |
|
||||
Page {pageNumber} sur {numPages} |
|
||||
</Typography> |
|
||||
|
|
||||
<div |
|
||||
style={{ |
|
||||
display: 'flex', |
|
||||
alignItems: 'center', |
|
||||
justifyContent: 'center', |
|
||||
width: '100%' |
|
||||
}} |
|
||||
> |
|
||||
<Document |
|
||||
file={pdfUrl} |
|
||||
onLoadSuccess={onDocumentSuccess} |
|
||||
loading={<div>Chargement du Manuel...</div>} |
|
||||
error={<div>Une erreur s'est produite lors du chargement du Manuel.</div>} |
|
||||
> |
|
||||
<Page pageNumber={pageNumber} width={1200} /> |
|
||||
</Document> |
|
||||
</div> |
|
||||
|
|
||||
<Grid sx={{ display: 'flex', gap: '20px', padding: '1%' }}> |
|
||||
<Button color="warning" variant="contained" onClick={prevPage}> |
|
||||
<FaAngleDoubleLeft /> |
|
||||
</Button> |
|
||||
<Button color="warning" variant="contained" onClick={nextPage}> |
|
||||
<FaAngleDoubleRight /> |
|
||||
</Button> |
|
||||
</Grid> |
|
||||
</Paper> |
|
||||
</div> |
|
||||
) |
|
||||
} |
|
||||
|
|
||||
export default Manuel |
|
||||
@ -1,261 +0,0 @@ |
|||||
import React, { useEffect, useState } from 'react' |
|
||||
import classe from '../assets/AllStyleComponents.module.css' |
|
||||
import classeAdd from '../assets/AddStudent.module.css' |
|
||||
import classeHome from '../assets/Home.module.css' |
|
||||
import { Link } from 'react-router-dom' |
|
||||
import { Box, Button, InputAdornment, Typography, Modal, TextField, Grid } from '@mui/material' |
|
||||
import { BsBookmarkPlusFill } from 'react-icons/bs' |
|
||||
import { FaClipboardList, FaPenToSquare, FaTrash } from 'react-icons/fa6' |
|
||||
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' |
|
||||
|
|
||||
const Mentions = () => { |
|
||||
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 [isDeleted, setIsDeleted] = useState(false) |
|
||||
const [ids, setIds] = useState(0) |
|
||||
|
|
||||
const [mentions, setMentions] = useState([]) |
|
||||
|
|
||||
useEffect(() => { |
|
||||
window.mention.getMention().then((response) => { |
|
||||
setMentions(response) |
|
||||
}) |
|
||||
}, []) |
|
||||
|
|
||||
const columns = [ |
|
||||
{ field: 'nom', headerName: 'Nom', width: 350 }, |
|
||||
{ field: 'uniter', headerName: 'Unité ', width: 180 }, |
|
||||
{ |
|
||||
field: 'action', |
|
||||
headerName: 'Action', |
|
||||
flex: 1, |
|
||||
renderCell: (params) => ( |
|
||||
<div style={{ display: 'flex', gap: '10px' }}> |
|
||||
<Link to={`/singlemention/${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={`update${params.value}`} |
|
||||
/> |
|
||||
<Tooltip |
|
||||
anchorSelect={`.update${params.value}`} |
|
||||
style={{ fontSize: '15px', zIndex: 22 }} |
|
||||
place="top" |
|
||||
> |
|
||||
Supprimer |
|
||||
</Tooltip> |
|
||||
</Button> |
|
||||
</Link> |
|
||||
</div> |
|
||||
) |
|
||||
} |
|
||||
] |
|
||||
|
|
||||
const paginationModel = { page: 0, pageSize: 5 } |
|
||||
|
|
||||
const dataRow = mentions.map((men) => ({ |
|
||||
id: men.id, // Ensure this exists and is unique for each etudiant |
|
||||
nom: men.nom, |
|
||||
uniter: men.uniter, |
|
||||
action: men.id // Ensure this is a valid URL for the image |
|
||||
})) |
|
||||
|
|
||||
const deleteButton = async (id) => { |
|
||||
let response = await window.mention.deleteMention({ id }) |
|
||||
if (response.success) { |
|
||||
const updatedMentions = mentions.filter((mention) => mention.id !== id) |
|
||||
setMentions(updatedMentions) |
|
||||
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 mention ?</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={classeAdd.header}> |
|
||||
<div className={classe.h1style}> |
|
||||
<div className={classeHome.blockTitle}> |
|
||||
<h1 style={{ display: 'flex', alignItems: 'center', gap: '10px' }}> |
|
||||
<FaClipboardList /> |
|
||||
Mentions |
|
||||
</h1> |
|
||||
<Link to={'/addmention'}> |
|
||||
<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}> |
|
||||
<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} |
|
||||
/> |
|
||||
</ThemeProvider> |
|
||||
</Paper> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
) |
|
||||
} |
|
||||
|
|
||||
export default Mentions |
|
||||
@ -1,343 +0,0 @@ |
|||||
import React, { useRef, useState } from 'react' |
|
||||
import { |
|
||||
Modal, |
|
||||
Box, |
|
||||
Typography, |
|
||||
Button, |
|
||||
InputAdornment, |
|
||||
TextField, |
|
||||
Card, |
|
||||
Grid, |
|
||||
Container |
|
||||
} from '@mui/material' |
|
||||
import { |
|
||||
FaUser, |
|
||||
FaIdBadge, |
|
||||
FaBirthdayCake, |
|
||||
FaGraduationCap, |
|
||||
FaCalendarAlt, |
|
||||
FaFileUpload |
|
||||
} from 'react-icons/fa' |
|
||||
|
|
||||
const ModalAddEtudiants = ({ open, handleClose }) => { |
|
||||
/** |
|
||||
* hook for storing data in the input |
|
||||
*/ |
|
||||
const [formData, setFormData] = useState({ |
|
||||
nom: '', |
|
||||
prenom: '', |
|
||||
photos: null, |
|
||||
date_de_naissances: '', |
|
||||
niveau: '', |
|
||||
annee_scolaire: '', |
|
||||
num_inscription: '' |
|
||||
}) |
|
||||
|
|
||||
/** |
|
||||
* ref for each input |
|
||||
*/ |
|
||||
const nomRef = useRef() |
|
||||
const prenomRef = useRef() |
|
||||
const date_de_naissancesRef = useRef() |
|
||||
const niveauRef = useRef() |
|
||||
const annee_scolaireRef = useRef() |
|
||||
const numero_inscriptionRef = useRef() |
|
||||
const photosRef = useRef() |
|
||||
|
|
||||
/** |
|
||||
* function to set the data in state |
|
||||
* @param {*} e |
|
||||
*/ |
|
||||
const handleInputChange = (e) => { |
|
||||
const { name, value } = e.target |
|
||||
setFormData((prevData) => ({ |
|
||||
...prevData, |
|
||||
[name]: value |
|
||||
})) |
|
||||
} |
|
||||
|
|
||||
const handleFileChange = (e) => { |
|
||||
let img_file = e.target.files[0] |
|
||||
const reader = new FileReader() |
|
||||
reader.readAsDataURL(img_file) |
|
||||
reader.onload = (ev) => { |
|
||||
const url = ev.target.result |
|
||||
// initialisation de nouvelle imageURL |
|
||||
const image = document.createElement('img') |
|
||||
image.src = url |
|
||||
|
|
||||
// create a new image |
|
||||
image.onload = (event) => { |
|
||||
let canvas = document.createElement('canvas') |
|
||||
let ratio = 250 / event.target.width |
|
||||
canvas.width = 250 |
|
||||
canvas.height = event.target.height * ratio |
|
||||
const context = canvas.getContext('2d') |
|
||||
context.drawImage(image, 0, 0, canvas.width, canvas.height) |
|
||||
|
|
||||
// new url |
|
||||
const new_URL = canvas.toDataURL('image/jpeg', 90) |
|
||||
// rendement de l'url a notre variable global |
|
||||
setFormData((prevData) => ({ |
|
||||
...prevData, |
|
||||
photos: new_URL |
|
||||
})) |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
const handleSubmit = async (e) => { |
|
||||
e.preventDefault() |
|
||||
// Handle form submission logic |
|
||||
const response = await window.etudiants.insertEtudiant(formData) |
|
||||
console.log(response) |
|
||||
} |
|
||||
|
|
||||
return ( |
|
||||
<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: 600, |
|
||||
bgcolor: 'background.paper', |
|
||||
boxShadow: 24, |
|
||||
p: 4 |
|
||||
}} |
|
||||
> |
|
||||
<Typography id="modal-title" variant="h6" component="h2"> |
|
||||
Ajoutez un etudiants |
|
||||
</Typography> |
|
||||
<Box |
|
||||
sx={{ |
|
||||
marginTop: '2%', |
|
||||
display: 'flex', |
|
||||
gap: '20px', |
|
||||
alignItems: 'end', |
|
||||
justifyContent: 'flex-end' |
|
||||
}} |
|
||||
> |
|
||||
<form onSubmit={handleSubmit}> |
|
||||
<Grid container spacing={2}> |
|
||||
{/* Nom and Prenom Fields */} |
|
||||
<Grid item xs={12} sm={6}> |
|
||||
<TextField |
|
||||
label="Nom" |
|
||||
name="nom" |
|
||||
color="secondary" |
|
||||
fullWidth |
|
||||
value={formData.nom} |
|
||||
onChange={handleInputChange} |
|
||||
InputProps={{ |
|
||||
startAdornment: ( |
|
||||
<InputAdornment position="start"> |
|
||||
<FaUser /> |
|
||||
</InputAdornment> |
|
||||
) |
|
||||
}} |
|
||||
inputRef={nomRef} |
|
||||
sx={{ |
|
||||
'& .MuiOutlinedInput-root': { |
|
||||
'&:hover fieldset': { |
|
||||
borderColor: 'rgb(156, 39, 176)' // Set the border color on hover |
|
||||
} |
|
||||
} |
|
||||
}} |
|
||||
/> |
|
||||
</Grid> |
|
||||
<Grid item xs={12} sm={6}> |
|
||||
<TextField |
|
||||
label="Prenom" |
|
||||
name="prenom" |
|
||||
fullWidth |
|
||||
color="secondary" |
|
||||
value={formData.prenom} |
|
||||
onChange={handleInputChange} |
|
||||
InputProps={{ |
|
||||
startAdornment: ( |
|
||||
<InputAdornment position="start"> |
|
||||
<FaUser /> |
|
||||
</InputAdornment> |
|
||||
) |
|
||||
}} |
|
||||
inputRef={prenomRef} |
|
||||
sx={{ |
|
||||
'& .MuiOutlinedInput-root': { |
|
||||
'&:hover fieldset': { |
|
||||
borderColor: 'rgb(156, 39, 176)' // Set the border color on hover |
|
||||
} |
|
||||
} |
|
||||
}} |
|
||||
/> |
|
||||
</Grid> |
|
||||
|
|
||||
{/* Date de Naissance and Niveau Fields */} |
|
||||
<Grid item xs={12} sm={6}> |
|
||||
<TextField |
|
||||
label="Date de Naissance" |
|
||||
name="date_de_naissances" |
|
||||
fullWidth |
|
||||
color="secondary" |
|
||||
value={formData.date_de_naissances} |
|
||||
onChange={handleInputChange} |
|
||||
type="date" |
|
||||
InputLabelProps={{ shrink: true }} |
|
||||
InputProps={{ |
|
||||
startAdornment: ( |
|
||||
<InputAdornment position="start"> |
|
||||
<FaBirthdayCake /> |
|
||||
</InputAdornment> |
|
||||
) |
|
||||
}} |
|
||||
inputRef={date_de_naissancesRef} |
|
||||
sx={{ |
|
||||
'& .MuiOutlinedInput-root': { |
|
||||
'&:hover fieldset': { |
|
||||
borderColor: 'rgb(156, 39, 176)' // Set the border color on hover |
|
||||
} |
|
||||
} |
|
||||
}} |
|
||||
/> |
|
||||
</Grid> |
|
||||
<Grid item xs={12} sm={6}> |
|
||||
<TextField |
|
||||
label="Niveau" |
|
||||
name="niveau" |
|
||||
fullWidth |
|
||||
color="secondary" |
|
||||
value={formData.niveau} |
|
||||
onChange={handleInputChange} |
|
||||
InputProps={{ |
|
||||
startAdornment: ( |
|
||||
<InputAdornment position="start"> |
|
||||
<FaGraduationCap /> |
|
||||
</InputAdornment> |
|
||||
) |
|
||||
}} |
|
||||
inputRef={niveauRef} |
|
||||
sx={{ |
|
||||
'& .MuiOutlinedInput-root': { |
|
||||
'&:hover fieldset': { |
|
||||
borderColor: 'rgb(156, 39, 176)' // Set the border color on hover |
|
||||
} |
|
||||
} |
|
||||
}} |
|
||||
/> |
|
||||
</Grid> |
|
||||
|
|
||||
{/* Année Scolaire and Numéro d'Inscription Fields */} |
|
||||
<Grid item xs={12} sm={6}> |
|
||||
<TextField |
|
||||
label="Année Scolaire" |
|
||||
name="annee_scolaire" |
|
||||
fullWidth |
|
||||
color="secondary" |
|
||||
value={formData.annee_scolaire} |
|
||||
onChange={handleInputChange} |
|
||||
InputProps={{ |
|
||||
startAdornment: ( |
|
||||
<InputAdornment position="start"> |
|
||||
<FaCalendarAlt /> |
|
||||
</InputAdornment> |
|
||||
) |
|
||||
}} |
|
||||
inputRef={annee_scolaireRef} |
|
||||
sx={{ |
|
||||
'& .MuiOutlinedInput-root': { |
|
||||
'&:hover fieldset': { |
|
||||
borderColor: 'rgb(156, 39, 176)' // Set the border color on hover |
|
||||
} |
|
||||
} |
|
||||
}} |
|
||||
/> |
|
||||
</Grid> |
|
||||
<Grid item xs={12} sm={6}> |
|
||||
<TextField |
|
||||
label="Numéro d'Inscription" |
|
||||
name="num_inscription" |
|
||||
fullWidth |
|
||||
color="secondary" |
|
||||
value={formData.num_inscription} |
|
||||
onChange={handleInputChange} |
|
||||
InputProps={{ |
|
||||
startAdornment: ( |
|
||||
<InputAdornment position="start"> |
|
||||
<FaIdBadge /> |
|
||||
</InputAdornment> |
|
||||
) |
|
||||
}} |
|
||||
inputRef={numero_inscriptionRef} |
|
||||
sx={{ |
|
||||
'& .MuiOutlinedInput-root': { |
|
||||
'&:hover fieldset': { |
|
||||
borderColor: 'rgb(156, 39, 176)' // Set the border color on hover |
|
||||
} |
|
||||
} |
|
||||
}} |
|
||||
/> |
|
||||
</Grid> |
|
||||
|
|
||||
{/* Photos Field */} |
|
||||
<Grid item xs={12}> |
|
||||
<TextField |
|
||||
label="Photos" |
|
||||
name="photos" |
|
||||
fullWidth |
|
||||
color="secondary" |
|
||||
onChange={handleFileChange} |
|
||||
type="file" |
|
||||
InputProps={{ |
|
||||
startAdornment: ( |
|
||||
<InputAdornment position="start"> |
|
||||
<FaFileUpload /> |
|
||||
</InputAdornment> |
|
||||
) |
|
||||
}} |
|
||||
InputLabelProps={{ |
|
||||
shrink: true |
|
||||
}} |
|
||||
inputRef={photosRef} |
|
||||
sx={{ |
|
||||
'& .MuiOutlinedInput-root': { |
|
||||
'&:hover fieldset': { |
|
||||
borderColor: 'rgb(156, 39, 176)' // Set the border color on hover |
|
||||
} |
|
||||
} |
|
||||
}} |
|
||||
/> |
|
||||
</Grid> |
|
||||
|
|
||||
{/* Submit Button */} |
|
||||
<Grid |
|
||||
item |
|
||||
xs={12} |
|
||||
style={{ display: 'flex', gap: '30px', justifyContent: 'flex-end' }} |
|
||||
> |
|
||||
<Button type="submit" color="secondary" variant="contained"> |
|
||||
Enregister |
|
||||
</Button> |
|
||||
<Button |
|
||||
onClick={handleClose} |
|
||||
color="error" |
|
||||
variant="contained" |
|
||||
sx={{ width: '30px' }} |
|
||||
> |
|
||||
Close |
|
||||
</Button> |
|
||||
</Grid> |
|
||||
</Grid> |
|
||||
</form> |
|
||||
</Box> |
|
||||
</Box> |
|
||||
</Modal> |
|
||||
) |
|
||||
} |
|
||||
|
|
||||
export default ModalAddEtudiants |
|
||||
@ -1,178 +0,0 @@ |
|||||
import React, { useEffect, useRef, useState } from 'react' |
|
||||
import { |
|
||||
Dialog, |
|
||||
DialogActions, |
|
||||
DialogContent, |
|
||||
DialogTitle, |
|
||||
TextField, |
|
||||
Button, |
|
||||
InputAdornment, |
|
||||
Box, |
|
||||
Grid |
|
||||
} from '@mui/material' |
|
||||
import ChangeCapital from './function/ChangeCapitalLetter' |
|
||||
import ChangeCapitalize from './function/ChangeCapitalizeLetter' |
|
||||
import { PiChalkboardTeacher } from 'react-icons/pi' |
|
||||
import { MdContactPhone, MdDateRange } from 'react-icons/md' |
|
||||
|
|
||||
const ModalAddProf = ({ open, onClose, matiere_id, onSubmitSuccess }) => { |
|
||||
const [formData, setFormData] = useState({ |
|
||||
nom_enseignant: '', |
|
||||
prenom_enseignant: '', |
|
||||
contact: '', |
|
||||
date: '', |
|
||||
matiere_id: '' |
|
||||
}) |
|
||||
|
|
||||
const nomRefs = useRef() |
|
||||
const prenomRefs = useRef() |
|
||||
|
|
||||
const handleChange = (e) => { |
|
||||
const { name, value } = e.target |
|
||||
setFormData({ ...formData, [name]: value }) |
|
||||
} |
|
||||
|
|
||||
useEffect(() => { |
|
||||
setFormData((prev) => ({ |
|
||||
...prev, |
|
||||
matiere_id: matiere_id |
|
||||
})) |
|
||||
}, [matiere_id]) |
|
||||
|
|
||||
const handleSubmit = async (e) => { |
|
||||
e.preventDefault() |
|
||||
let response = await window.matieres.insertProf(formData) |
|
||||
console.log(response) |
|
||||
|
|
||||
if (response.success) { |
|
||||
onSubmitSuccess(true) |
|
||||
onClose() // Close the modal after submission |
|
||||
setFormData({ |
|
||||
nom_enseignant: '', |
|
||||
prenom_enseignant: '', |
|
||||
contact: '', |
|
||||
date: '', |
|
||||
matiere_id: '' |
|
||||
}) |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
return ( |
|
||||
<Dialog open={open} onClose={onClose}> |
|
||||
<form action="" onSubmit={handleSubmit}> |
|
||||
<DialogTitle>Information sur enseignant</DialogTitle> |
|
||||
<DialogContent> |
|
||||
<Box sx={{ flexGrow: 1 }}> |
|
||||
<Grid container spacing={2}> |
|
||||
<Grid item xs={12} sm={6}> |
|
||||
<TextField |
|
||||
autoFocus |
|
||||
margin="normal" |
|
||||
required |
|
||||
name="nom_enseignant" |
|
||||
label="Nom du professeur" |
|
||||
type="text" |
|
||||
fullWidth |
|
||||
placeholder="Nom du professeur" |
|
||||
variant="outlined" |
|
||||
value={formData.nom_enseignant} |
|
||||
color="warning" |
|
||||
inputRef={nomRefs} |
|
||||
onInput={() => ChangeCapital(nomRefs)} |
|
||||
onChange={handleChange} |
|
||||
InputProps={{ |
|
||||
startAdornment: ( |
|
||||
<InputAdornment position="start"> |
|
||||
<PiChalkboardTeacher /> |
|
||||
</InputAdornment> |
|
||||
) |
|
||||
}} |
|
||||
/> |
|
||||
</Grid> |
|
||||
<Grid item xs={12} sm={6}> |
|
||||
<TextField |
|
||||
autoFocus |
|
||||
margin="normal" |
|
||||
required |
|
||||
name="prenom_enseignant" |
|
||||
label="Prenom du professeur" |
|
||||
type="text" |
|
||||
fullWidth |
|
||||
placeholder="Prenom du professeur" |
|
||||
variant="outlined" |
|
||||
value={formData.prenom_enseignant} |
|
||||
color="warning" |
|
||||
inputRef={prenomRefs} |
|
||||
onInput={() => ChangeCapitalize(prenomRefs)} |
|
||||
onChange={handleChange} |
|
||||
InputProps={{ |
|
||||
startAdornment: ( |
|
||||
<InputAdornment position="start"> |
|
||||
<PiChalkboardTeacher /> |
|
||||
</InputAdornment> |
|
||||
) |
|
||||
}} |
|
||||
/> |
|
||||
</Grid> |
|
||||
<Grid item xs={12} sm={6}> |
|
||||
<TextField |
|
||||
autoFocus |
|
||||
margin="normal" |
|
||||
required |
|
||||
name="contact" |
|
||||
label="Contact" |
|
||||
type="number" |
|
||||
fullWidth |
|
||||
placeholder="Contact" |
|
||||
variant="outlined" |
|
||||
value={formData.contact} |
|
||||
color="warning" |
|
||||
onChange={handleChange} |
|
||||
InputProps={{ |
|
||||
startAdornment: ( |
|
||||
<InputAdornment position="start"> |
|
||||
<MdContactPhone /> |
|
||||
</InputAdornment> |
|
||||
) |
|
||||
}} |
|
||||
/> |
|
||||
</Grid> |
|
||||
<Grid item xs={12} sm={6}> |
|
||||
<TextField |
|
||||
autoFocus |
|
||||
margin="normal" |
|
||||
required |
|
||||
name="date" |
|
||||
label="Date de prise du poste" |
|
||||
type="date" |
|
||||
fullWidth |
|
||||
variant="outlined" |
|
||||
value={formData.date} |
|
||||
color="warning" |
|
||||
onChange={handleChange} |
|
||||
InputProps={{ |
|
||||
startAdornment: ( |
|
||||
<InputAdornment position="start"> |
|
||||
<MdDateRange /> |
|
||||
</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 ModalAddProf |
|
||||
@ -1,97 +0,0 @@ |
|||||
import React, { useState } from 'react' |
|
||||
import { Dialog, DialogActions, DialogContent, DialogTitle, TextField, Button } from '@mui/material' |
|
||||
import PDFEditorCertificat from './function/PDFEditorCertificate' |
|
||||
|
|
||||
const ModalCertificate = ({ open, onClose, json }) => { |
|
||||
const [formData, setFormData] = useState({ |
|
||||
pere: '', |
|
||||
mere: '', |
|
||||
chefService: '' |
|
||||
}) |
|
||||
|
|
||||
const handleChange = (e) => { |
|
||||
const { name, value } = e.target |
|
||||
setFormData({ ...formData, [name]: value }) |
|
||||
} |
|
||||
|
|
||||
const handleSubmit = (e) => { |
|
||||
e.preventDefault() |
|
||||
console.log('Form submitted:', formData, json) |
|
||||
let data = { |
|
||||
f1: json.nomPrenom, |
|
||||
f2: json.naissances, |
|
||||
f3: formData.pere, |
|
||||
f4: formData.mere, |
|
||||
f5: json.niveau, |
|
||||
f6: json.mention, |
|
||||
f7: json.inscri, |
|
||||
f8: json.annee, |
|
||||
f9: formData.chefService |
|
||||
} |
|
||||
// Handle the form submission (e.g., send to a server or process data) |
|
||||
PDFEditorCertificat(data) |
|
||||
setFormData({ |
|
||||
mere: '', |
|
||||
pere: '', |
|
||||
chefService: '' |
|
||||
}) |
|
||||
onClose() // Close the modal after submission |
|
||||
} |
|
||||
|
|
||||
return ( |
|
||||
<Dialog open={open} onClose={onClose}> |
|
||||
<form action=""> |
|
||||
<DialogTitle>Informations sur l'élève</DialogTitle> |
|
||||
<DialogContent> |
|
||||
<TextField |
|
||||
autoFocus |
|
||||
margin="dense" |
|
||||
required |
|
||||
name="pere" |
|
||||
label="Père de l'élève" |
|
||||
type="text" |
|
||||
fullWidth |
|
||||
variant="outlined" |
|
||||
value={formData.pere} |
|
||||
color="warning" |
|
||||
onChange={handleChange} |
|
||||
/> |
|
||||
<TextField |
|
||||
margin="dense" |
|
||||
name="mere" |
|
||||
required |
|
||||
label="Mère de l'élève" |
|
||||
type="text" |
|
||||
fullWidth |
|
||||
variant="outlined" |
|
||||
value={formData.mere} |
|
||||
color="warning" |
|
||||
onChange={handleChange} |
|
||||
/> |
|
||||
<TextField |
|
||||
margin="dense" |
|
||||
name="chefService" |
|
||||
label="Chef de service de scolarité" |
|
||||
type="text" |
|
||||
fullWidth |
|
||||
required |
|
||||
color="warning" |
|
||||
variant="outlined" |
|
||||
value={formData.chefService} |
|
||||
onChange={handleChange} |
|
||||
/> |
|
||||
</DialogContent> |
|
||||
<DialogActions> |
|
||||
<Button onClick={onClose} color="warning" variant="contained"> |
|
||||
Annuler |
|
||||
</Button> |
|
||||
<Button onClick={handleSubmit} type="submit" color="warning" variant="contained"> |
|
||||
Soumettre |
|
||||
</Button> |
|
||||
</DialogActions> |
|
||||
</form> |
|
||||
</Dialog> |
|
||||
) |
|
||||
} |
|
||||
|
|
||||
export default ModalCertificate |
|
||||
@ -1,89 +0,0 @@ |
|||||
import React, { useEffect, useState } from 'react' |
|
||||
import { |
|
||||
Dialog, |
|
||||
DialogActions, |
|
||||
DialogContent, |
|
||||
DialogTitle, |
|
||||
TextField, |
|
||||
Button, |
|
||||
InputAdornment |
|
||||
} from '@mui/material' |
|
||||
import { FaTimes } from 'react-icons/fa' |
|
||||
|
|
||||
const ModalFormMultiplicateur = ({ open, onClose }) => { |
|
||||
const [formData, setFormData] = useState({ |
|
||||
id: '', |
|
||||
multiplicateur: '' |
|
||||
}) |
|
||||
|
|
||||
const [Multiplicateur, setMultiplicateur] = useState(0) |
|
||||
|
|
||||
useEffect(() => { |
|
||||
window.matieres.getNessesary().then((response) => { |
|
||||
setMultiplicateur(response) |
|
||||
}) |
|
||||
}, []) |
|
||||
|
|
||||
useEffect(() => { |
|
||||
setFormData((prev) => ({ |
|
||||
...prev, |
|
||||
id: Multiplicateur.id, |
|
||||
multiplicateur: Multiplicateur.uniter_heure |
|
||||
})) |
|
||||
}, [Multiplicateur]) |
|
||||
|
|
||||
const handleChange = (e) => { |
|
||||
const { name, value } = e.target |
|
||||
setFormData({ ...formData, [name]: value }) |
|
||||
} |
|
||||
|
|
||||
const handleSubmit = async (e) => { |
|
||||
e.preventDefault() |
|
||||
console.log(formData) |
|
||||
let response = await window.matieres.updateNessesary(formData) |
|
||||
|
|
||||
if (response.success) { |
|
||||
onClose() // Close the modal after submission |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
return ( |
|
||||
<Dialog open={open} onClose={onClose}> |
|
||||
<form action="" onSubmit={handleSubmit}> |
|
||||
<DialogTitle>Changer le multiplicateur</DialogTitle> |
|
||||
<DialogContent> |
|
||||
<TextField |
|
||||
autoFocus |
|
||||
margin="dense" |
|
||||
required |
|
||||
name="multiplicateur" |
|
||||
label="Multiplicateur crédit-heure" |
|
||||
type="text" |
|
||||
fullWidth |
|
||||
variant="outlined" |
|
||||
value={formData.multiplicateur} |
|
||||
color="warning" |
|
||||
onChange={handleChange} |
|
||||
InputProps={{ |
|
||||
startAdornment: ( |
|
||||
<InputAdornment position="start"> |
|
||||
<FaTimes /> |
|
||||
</InputAdornment> |
|
||||
) |
|
||||
}} |
|
||||
/> |
|
||||
</DialogContent> |
|
||||
<DialogActions> |
|
||||
<Button onClick={onClose} color="error"> |
|
||||
Annuler |
|
||||
</Button> |
|
||||
<Button type="submit" color="warning"> |
|
||||
Soumettre |
|
||||
</Button> |
|
||||
</DialogActions> |
|
||||
</form> |
|
||||
</Dialog> |
|
||||
) |
|
||||
} |
|
||||
|
|
||||
export default ModalFormMultiplicateur |
|
||||
@ -1,54 +0,0 @@ |
|||||
import React, { useState } from 'react' |
|
||||
import { |
|
||||
Dialog, |
|
||||
DialogActions, |
|
||||
DialogContent, |
|
||||
DialogTitle, |
|
||||
TextField, |
|
||||
Button, |
|
||||
InputAdornment, |
|
||||
Box, |
|
||||
Grid |
|
||||
} from '@mui/material' |
|
||||
import { PiChalkboardTeacher } from 'react-icons/pi' |
|
||||
|
|
||||
const ModalProcessFichePresence = ({ open, onClose, matiere_id }) => { |
|
||||
const [formData, setFormData] = useState({ |
|
||||
matiere: '' |
|
||||
}) |
|
||||
return ( |
|
||||
<Dialog open={open} onClose={onClose}> |
|
||||
<DialogTitle>Option sur la la fiches</DialogTitle> |
|
||||
<DialogContent> |
|
||||
<Box sx={{ flexGrow: 1 }}> |
|
||||
<Grid container spacing={2}> |
|
||||
<Grid item xs={12} sm={6}> |
|
||||
<TextField |
|
||||
autoFocus |
|
||||
margin="normal" |
|
||||
required |
|
||||
name="nom_enseignant" |
|
||||
label="Nom du professeur" |
|
||||
type="text" |
|
||||
fullWidth |
|
||||
placeholder="Nom du professeur" |
|
||||
variant="outlined" |
|
||||
value={formData.matiere} |
|
||||
color="warning" |
|
||||
InputProps={{ |
|
||||
startAdornment: ( |
|
||||
<InputAdornment position="start"> |
|
||||
<PiChalkboardTeacher /> |
|
||||
</InputAdornment> |
|
||||
) |
|
||||
}} |
|
||||
/> |
|
||||
</Grid> |
|
||||
</Grid> |
|
||||
</Box> |
|
||||
</DialogContent> |
|
||||
</Dialog> |
|
||||
) |
|
||||
} |
|
||||
|
|
||||
export default ModalProcessFichePresence |
|
||||
@ -1,66 +0,0 @@ |
|||||
import React, { useState } from 'react' |
|
||||
import { Dialog, DialogActions, DialogContent, DialogTitle, TextField, Button } from '@mui/material' |
|
||||
import PDFEditorRecepice from './function/PDFEditorRecepisse' |
|
||||
|
|
||||
const ModalRecepice = ({ open, onClose, json }) => { |
|
||||
const [formData, setFormData] = useState({ |
|
||||
nom: '' |
|
||||
}) |
|
||||
|
|
||||
const handleChange = (e) => { |
|
||||
const { name, value } = e.target |
|
||||
setFormData({ ...formData, [name]: value }) |
|
||||
} |
|
||||
|
|
||||
const handleSubmit = (e) => { |
|
||||
e.preventDefault() |
|
||||
console.log('Form submitted:', formData) |
|
||||
let data = { |
|
||||
f1: json.mention, |
|
||||
f2: json.niveau, |
|
||||
f3: json.nomPrenom, |
|
||||
f4: json.inscri, |
|
||||
f6: json.annee, |
|
||||
f7: formData.nom |
|
||||
} |
|
||||
// Handle the form submission (e.g., send to a server or process data) |
|
||||
PDFEditorRecepice(data) |
|
||||
setFormData({ |
|
||||
nom: '' |
|
||||
}) |
|
||||
onClose() // Close the modal after submission |
|
||||
} |
|
||||
|
|
||||
return ( |
|
||||
<Dialog open={open} onClose={onClose}> |
|
||||
<form action=""> |
|
||||
<DialogTitle>Informations suplementaire</DialogTitle> |
|
||||
<DialogContent> |
|
||||
<TextField |
|
||||
autoFocus |
|
||||
margin="dense" |
|
||||
required |
|
||||
name="nom" |
|
||||
label="Nom et prenom du chef de services" |
|
||||
type="text" |
|
||||
fullWidth |
|
||||
variant="outlined" |
|
||||
value={formData.nom} |
|
||||
color="warning" |
|
||||
onChange={handleChange} |
|
||||
/> |
|
||||
</DialogContent> |
|
||||
<DialogActions> |
|
||||
<Button onClick={onClose} color="warning" variant="contained"> |
|
||||
Annuler |
|
||||
</Button> |
|
||||
<Button onClick={handleSubmit} type="submit" color="warning" variant="contained"> |
|
||||
Soumettre |
|
||||
</Button> |
|
||||
</DialogActions> |
|
||||
</form> |
|
||||
</Dialog> |
|
||||
) |
|
||||
} |
|
||||
|
|
||||
export default ModalRecepice |
|
||||
@ -1,84 +0,0 @@ |
|||||
import React, { useRef, useState } from 'react' |
|
||||
import { Dialog, DialogActions, DialogContent, DialogTitle, TextField, Button } from '@mui/material' |
|
||||
import PDFEditorStage from './function/PDFEditorStage' |
|
||||
import ChangeCapital from './function/ChangeCapitalLetter' |
|
||||
import ChangeCapitalize from './function/ChangeCapitalizeLetter' |
|
||||
|
|
||||
const ModalStage = ({ open, onClose }) => { |
|
||||
const [formData, setFormData] = useState({ |
|
||||
nom: '', |
|
||||
prenom: '' |
|
||||
}) |
|
||||
|
|
||||
const handleChange = (e) => { |
|
||||
const { name, value } = e.target |
|
||||
setFormData({ ...formData, [name]: value }) |
|
||||
} |
|
||||
|
|
||||
const handleSubmit = (e) => { |
|
||||
e.preventDefault() |
|
||||
console.log('Form submitted:', formData) |
|
||||
let data = { |
|
||||
f1: formData.nom + ' ' + formData.prenom |
|
||||
} |
|
||||
// Handle the form submission (e.g., send to a server or process data) |
|
||||
PDFEditorStage(data) |
|
||||
setFormData({ |
|
||||
nom: '', |
|
||||
prenom: '' |
|
||||
}) |
|
||||
onClose() // Close the modal after submission |
|
||||
} |
|
||||
|
|
||||
const nomRef = useRef() |
|
||||
const prenomRef = useRef() |
|
||||
|
|
||||
return ( |
|
||||
<Dialog open={open} onClose={onClose}> |
|
||||
<form action=""> |
|
||||
<DialogTitle>Informations suplementaire</DialogTitle> |
|
||||
<DialogContent> |
|
||||
<TextField |
|
||||
autoFocus |
|
||||
margin="dense" |
|
||||
required |
|
||||
name="nom" |
|
||||
label="Nom du directeur" |
|
||||
type="text" |
|
||||
fullWidth |
|
||||
variant="outlined" |
|
||||
value={formData.nom} |
|
||||
inputRef={nomRef} |
|
||||
onInput={() => ChangeCapital(nomRef)} |
|
||||
color="warning" |
|
||||
onChange={handleChange} |
|
||||
/> |
|
||||
<TextField |
|
||||
margin="dense" |
|
||||
name="prenom" |
|
||||
required |
|
||||
label="Prenom du directeur" |
|
||||
type="text" |
|
||||
fullWidth |
|
||||
variant="outlined" |
|
||||
inputRef={prenomRef} |
|
||||
value={formData.prenom} |
|
||||
onInput={() => ChangeCapitalize(prenomRef)} |
|
||||
color="warning" |
|
||||
onChange={handleChange} |
|
||||
/> |
|
||||
</DialogContent> |
|
||||
<DialogActions> |
|
||||
<Button onClick={onClose} color="warning" variant="contained"> |
|
||||
Annuler |
|
||||
</Button> |
|
||||
<Button onClick={handleSubmit} type="submit" color="warning" variant="contained"> |
|
||||
Soumettre |
|
||||
</Button> |
|
||||
</DialogActions> |
|
||||
</form> |
|
||||
</Dialog> |
|
||||
) |
|
||||
} |
|
||||
|
|
||||
export default ModalStage |
|
||||
@ -1,107 +0,0 @@ |
|||||
import React, { useEffect, useState } from 'react' |
|
||||
import { |
|
||||
Dialog, |
|
||||
DialogActions, |
|
||||
DialogContent, |
|
||||
DialogTitle, |
|
||||
TextField, |
|
||||
Button, |
|
||||
Autocomplete, |
|
||||
InputAdornment |
|
||||
} from '@mui/material' |
|
||||
import { MdRule } from 'react-icons/md' |
|
||||
|
|
||||
const ModalUpdateParcoursEtudiant = ({ open, onClose, user_id, onSubmit }) => { |
|
||||
const [formData, setFormData] = useState({ |
|
||||
parcours: '', |
|
||||
user_id: '' |
|
||||
}) |
|
||||
const [parcours, setParcours] = useState([]) |
|
||||
|
|
||||
useEffect(() => { |
|
||||
window.notesysteme.getParcours().then((response) => { |
|
||||
setParcours(response) |
|
||||
}) |
|
||||
}, []) |
|
||||
|
|
||||
useEffect(() => { |
|
||||
if (user_id) { |
|
||||
setFormData((prevData) => ({ |
|
||||
...prevData, |
|
||||
user_id: user_id |
|
||||
})) |
|
||||
} |
|
||||
}, [user_id]) |
|
||||
|
|
||||
const handleSubmit = async (e) => { |
|
||||
e.preventDefault() |
|
||||
|
|
||||
let response = await window.etudiants.changeParcours(formData) |
|
||||
|
|
||||
if (response.changes) { |
|
||||
onSubmit(true, formData.parcours) |
|
||||
onClose() |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
return ( |
|
||||
<Dialog open={open} onClose={onClose}> |
|
||||
<form action=""> |
|
||||
<DialogTitle>Ajouter un parcours</DialogTitle> |
|
||||
<DialogContent> |
|
||||
<Autocomplete |
|
||||
options={parcours} // Options array for Autocomplete |
|
||||
getOptionLabel={(option) => option.nom || ''} // Display `nom` |
|
||||
value={parcours.find((item) => item.nom === formData.parcours) || null} // Find selected option based on `nom` |
|
||||
onChange={(event, newValue) => { |
|
||||
setFormData((prevData) => ({ |
|
||||
...prevData, |
|
||||
parcours: newValue ? newValue.nom : '' // Store only `nom` |
|
||||
})) |
|
||||
}} |
|
||||
size="small" |
|
||||
isOptionEqualToValue={(option, value) => option.nom === value?.nom} // Ensure correct matching |
|
||||
renderInput={(params) => ( |
|
||||
<TextField |
|
||||
{...params} |
|
||||
label="Parcours" |
|
||||
margin="normal" |
|
||||
color="warning" |
|
||||
placeholder="Sélectionnez une mention" |
|
||||
InputProps={{ |
|
||||
...params.InputProps, |
|
||||
startAdornment: ( |
|
||||
<> |
|
||||
<InputAdornment position="start"> |
|
||||
<MdRule /> |
|
||||
</InputAdornment> |
|
||||
{params.InputProps.startAdornment} |
|
||||
</> |
|
||||
) |
|
||||
}} |
|
||||
sx={{ |
|
||||
marginBottom: '5px', |
|
||||
'& .MuiOutlinedInput-root': { |
|
||||
'&:hover fieldset': { |
|
||||
borderColor: '#ff9800' // Set border color on hover |
|
||||
} |
|
||||
} |
|
||||
}} |
|
||||
/> |
|
||||
)} |
|
||||
/> |
|
||||
</DialogContent> |
|
||||
<DialogActions> |
|
||||
<Button onClick={onClose} color="warning" variant="contained"> |
|
||||
Annuler |
|
||||
</Button> |
|
||||
<Button onClick={handleSubmit} type="submit" color="warning" variant="contained"> |
|
||||
Soumettre |
|
||||
</Button> |
|
||||
</DialogActions> |
|
||||
</form> |
|
||||
</Dialog> |
|
||||
) |
|
||||
} |
|
||||
|
|
||||
export default ModalUpdateParcoursEtudiant |
|
||||
@ -1,140 +0,0 @@ |
|||||
import React, { useState } from 'react' |
|
||||
import classe from '../assets/Navbar.module.css' |
|
||||
import icon from '../assets/logo.ico' |
|
||||
import { FaTimes, FaRegWindowMinimize } from 'react-icons/fa' |
|
||||
import { useAuthContext } from '../contexts/AuthContext' |
|
||||
import { Modal, Box, Typography, Button } from '@mui/material' |
|
||||
import { useNavigate } from 'react-router-dom' |
|
||||
|
|
||||
const Navbar = () => { |
|
||||
const { token, setToken } = useAuthContext() |
|
||||
const navigate = useNavigate() |
|
||||
|
|
||||
/** |
|
||||
* function to quit app |
|
||||
*/ |
|
||||
const quitApp = () => { |
|
||||
if (token !== null) { |
|
||||
setOpen(true) |
|
||||
} else { |
|
||||
quit() |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* function to minimize the app |
|
||||
* if the user try to open something |
|
||||
* in the desktop |
|
||||
*/ |
|
||||
const minimize = async () => { |
|
||||
await window.allUser.minimize() |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* hook to open modal |
|
||||
*/ |
|
||||
const [open, setOpen] = useState(false) |
|
||||
|
|
||||
/** |
|
||||
* function to close modal |
|
||||
*/ |
|
||||
const handleClose = () => setOpen(false) |
|
||||
|
|
||||
/** |
|
||||
* function to quit |
|
||||
*/ |
|
||||
const quit = async () => { |
|
||||
await window.allUser.quit() |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* function after user click yes button in the modal |
|
||||
*/ |
|
||||
const logoutAndQuit = () => { |
|
||||
localStorage.removeItem('ACCESS_TOKEN') |
|
||||
setToken(null) |
|
||||
navigate('/login') |
|
||||
quit() |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* function to return the view Modal |
|
||||
* |
|
||||
* @returns {JSX} |
|
||||
*/ |
|
||||
const modals = () => ( |
|
||||
<Modal |
|
||||
open={open} |
|
||||
onClose={handleClose} |
|
||||
aria-labelledby="modal-title" |
|
||||
aria-describedby="modal-description" |
|
||||
> |
|
||||
<Box |
|
||||
sx={{ |
|
||||
position: 'absolute', |
|
||||
top: '50%', |
|
||||
left: '50%', |
|
||||
transform: 'translate(-50%, -50%)', |
|
||||
width: 450, |
|
||||
bgcolor: 'background.paper', |
|
||||
boxShadow: 24, |
|
||||
p: 4 |
|
||||
}} |
|
||||
> |
|
||||
<Typography id="modal-title" variant="h6" component="h2"> |
|
||||
Notification |
|
||||
</Typography> |
|
||||
<Typography id="modal-description" sx={{ mt: 2 }}> |
|
||||
Déconnecter et quitter ? |
|
||||
</Typography> |
|
||||
<Box |
|
||||
sx={{ |
|
||||
marginTop: '2%', |
|
||||
display: 'flex', |
|
||||
gap: '20px', |
|
||||
alignItems: 'end', |
|
||||
justifyContent: 'flex-end' |
|
||||
}} |
|
||||
> |
|
||||
<Button |
|
||||
color="warning" |
|
||||
variant="contained" |
|
||||
onClick={logoutAndQuit} |
|
||||
sx={{ width: '10px' }} |
|
||||
> |
|
||||
Oui |
|
||||
</Button> |
|
||||
<Button onClick={handleClose} color="error" variant="contained" sx={{ width: '10px' }}> |
|
||||
Non |
|
||||
</Button> |
|
||||
</Box> |
|
||||
</Box> |
|
||||
</Modal> |
|
||||
) |
|
||||
|
|
||||
return ( |
|
||||
<> |
|
||||
<div className={classe.navnar}> |
|
||||
{modals()} |
|
||||
<div className={classe.gauche}> |
|
||||
<img src={icon} alt="" width={20} height={20} className="rounded-circle" /> |
|
||||
<span style={{ fontWeight: 'bold', paddingLeft: '2%' }}>Université de Toamasina</span> |
|
||||
</div> |
|
||||
<div className={classe.droite}> |
|
||||
<FaRegWindowMinimize |
|
||||
style={{ |
|
||||
fontSize: '20px', |
|
||||
cursor: 'pointer', |
|
||||
paddingBottom: '11%', |
|
||||
fontWeight: 'bold' |
|
||||
}} |
|
||||
onClick={minimize} |
|
||||
/> |
|
||||
<FaTimes style={{ fontSize: '20px', cursor: 'pointer' }} onClick={quitApp} /> |
|
||||
</div> |
|
||||
</div> |
|
||||
</> |
|
||||
) |
|
||||
} |
|
||||
|
|
||||
export default Navbar |
|
||||
@ -1,264 +0,0 @@ |
|||||
import React, { useEffect, useState } from 'react' |
|
||||
import { Link } from 'react-router-dom' |
|
||||
import { createTheme, ThemeProvider } from '@mui/material/styles' |
|
||||
import { DataGrid } from '@mui/x-data-grid' |
|
||||
import { frFR } from '@mui/x-data-grid/locales' |
|
||||
import Paper from '@mui/material/Paper' |
|
||||
import { Box, Button, InputAdornment, Typography, Modal, TextField, Grid } from '@mui/material' |
|
||||
import classe from '../assets/AllStyleComponents.module.css' |
|
||||
import classeHome from '../assets/Home.module.css' |
|
||||
import { GiUpgrade } from 'react-icons/gi' |
|
||||
import { FaPenToSquare, FaTrash } from 'react-icons/fa6' |
|
||||
import { Tooltip } from 'react-tooltip' |
|
||||
import warning from '../assets/warning.svg' |
|
||||
import success from '../assets/success.svg' |
|
||||
|
|
||||
const Niveau = () => { |
|
||||
const [niveaus, setNiveau] = useState([]) |
|
||||
|
|
||||
useEffect(() => { |
|
||||
window.niveaus.getNiveau().then((response) => { |
|
||||
setNiveau(response) |
|
||||
}) |
|
||||
}, []) |
|
||||
|
|
||||
const [isDeleted, setIsDeleted] = useState(false) |
|
||||
const [ids, setIds] = useState(0) |
|
||||
|
|
||||
const columns = [ |
|
||||
{ field: 'nom', headerName: 'Nom', width: 200 }, |
|
||||
{ |
|
||||
field: 'action', |
|
||||
headerName: 'Action', |
|
||||
flex: 1, |
|
||||
renderCell: (params) => ( |
|
||||
<div style={{ display: 'flex', gap: '10px' }}> |
|
||||
<Link to={`/single/niveau/${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> |
|
||||
) |
|
||||
} |
|
||||
] |
|
||||
|
|
||||
const paginationModel = { page: 0, pageSize: 5 } |
|
||||
|
|
||||
const dataRow = niveaus.map((niveau) => ({ |
|
||||
id: niveau.id, |
|
||||
nom: niveau.nom, |
|
||||
action: niveau.id |
|
||||
})) |
|
||||
|
|
||||
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 |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
}) |
|
||||
|
|
||||
/** |
|
||||
* hook to open modal |
|
||||
*/ |
|
||||
const [open, setOpen] = useState(false) |
|
||||
|
|
||||
/** |
|
||||
* function to close modal |
|
||||
*/ |
|
||||
const handleClose = () => { |
|
||||
setOpen(false) |
|
||||
setIsDeleted(false) |
|
||||
} |
|
||||
|
|
||||
const deleteButton = async (id) => { |
|
||||
let response = await window.niveaus.deleteNiveaus({ id }) |
|
||||
console.log(response); |
|
||||
|
|
||||
if (response.success) { |
|
||||
const updatedNiveaus = niveaus.filter((niveau) => niveau.id !== id) |
|
||||
setNiveau(updatedNiveaus) |
|
||||
setIsDeleted(true) |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* 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 niveau ?</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>Niveau</h1> |
|
||||
<Link to={'/addniveau'}> |
|
||||
<Button color="warning" variant="contained"> |
|
||||
<GiUpgrade style={{ fontSize: '20px', color: 'white' }} /> Ajouter |
|
||||
</Button> |
|
||||
</Link> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
|
|
||||
{/* display the data-grid niveau */} |
|
||||
<div className={classeHome.boxEtudiantsCard}> |
|
||||
<div style={{ display: 'flex', justifyContent: 'center' }}> |
|
||||
<Paper |
|
||||
sx={{ |
|
||||
height: 'auto', // Auto height to make the grid responsive |
|
||||
width: '100%', |
|
||||
minHeight: 500, // Ensures a minimum height |
|
||||
display: 'flex' |
|
||||
}} |
|
||||
> |
|
||||
<ThemeProvider theme={theme}> |
|
||||
<div |
|
||||
style={{ |
|
||||
display: 'flex', |
|
||||
alignItems: 'center', |
|
||||
justifyContent: 'center', |
|
||||
width: '100%', // Make it responsive to full width |
|
||||
flexDirection: 'column' // Stacks content vertically on smaller screens |
|
||||
}} |
|
||||
> |
|
||||
<DataGrid |
|
||||
rows={dataRow} |
|
||||
columns={columns} |
|
||||
initialState={{ pagination: { paginationModel } }} |
|
||||
pageSizeOptions={[5, 10]} |
|
||||
sx={{ |
|
||||
border: 0, |
|
||||
width: '100%', // 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 |
|
||||
} |
|
||||
}} |
|
||||
localeText={frFR.components.MuiDataGrid.defaultProps.localeText} |
|
||||
/> |
|
||||
</div> |
|
||||
</ThemeProvider> |
|
||||
</Paper> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
) |
|
||||
} |
|
||||
|
|
||||
export default Niveau |
|
||||
@ -1,11 +0,0 @@ |
|||||
import React from 'react' |
|
||||
|
|
||||
const NotFound = () => { |
|
||||
return ( |
|
||||
<div> |
|
||||
<h1>404 Not Found</h1> |
|
||||
</div> |
|
||||
) |
|
||||
} |
|
||||
|
|
||||
export default NotFound |
|
||||
@ -1,372 +0,0 @@ |
|||||
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 { DataGrid, GridToolbar } from '@mui/x-data-grid' |
|
||||
import { frFR } from '@mui/x-data-grid/locales' |
|
||||
import { createTheme, ThemeProvider } from '@mui/material/styles' |
|
||||
import { IoNewspaperOutline } from 'react-icons/io5' |
|
||||
import { IoMdReturnRight } from 'react-icons/io' |
|
||||
import { Button, Modal, Box } from '@mui/material' |
|
||||
import { Tooltip } from 'react-tooltip' |
|
||||
import ReleverNotes from './ReleverNotes' |
|
||||
import { FaDownload } from 'react-icons/fa' |
|
||||
|
|
||||
const Noteclasse = () => { |
|
||||
const { niveau, scolaire } = useParams() |
|
||||
|
|
||||
const [etudiants, setEtudiants] = useState([]) |
|
||||
const [mention, setMention] = useState([]) |
|
||||
const [session, setSession] = useState([]) |
|
||||
|
|
||||
const formData = { |
|
||||
niveau, |
|
||||
scolaire |
|
||||
} |
|
||||
|
|
||||
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 |
|
||||
} |
|
||||
|
|
||||
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) |
|
||||
} |
|
||||
|
|
||||
function checkNumberSession(id) { |
|
||||
let sessionNumber |
|
||||
for (let index = 0; index < session.length; index++) { |
|
||||
for (let j = 0; j < session[index].length; j++) { |
|
||||
if (session[index][j].etudiant_id == id) { |
|
||||
sessionNumber = 2 |
|
||||
} else { |
|
||||
sessionNumber = 1 |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
return sessionNumber |
|
||||
} |
|
||||
|
|
||||
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 paginationModel = { page: 0, pageSize: 5 } |
|
||||
|
|
||||
const columns = [ |
|
||||
{ field: 'nom', headerName: 'Nom', width: 170 }, |
|
||||
{ field: 'prenom', headerName: 'Prenom', width: 160 }, |
|
||||
{ field: 'session', headerName: 'Nombre de Session', width: 180 }, |
|
||||
{ field: 'mention', headerName: 'Mention', width: 180 }, |
|
||||
{ field: 'moyenne', headerName: 'Moyenne Général', width: 160 }, |
|
||||
{ |
|
||||
field: 'photos', |
|
||||
headerName: 'Photos', |
|
||||
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', |
|
||||
flex: 1, |
|
||||
renderCell: (params) => ( |
|
||||
<div style={{ display: 'flex', gap: '10px' }}> |
|
||||
<Link to={`#`} onClick={() => sendData(params.value)}> |
|
||||
{/* <IoEyeSharp style={{fontSize:"20px", color:"white"}} /> */} |
|
||||
<Button color="warning" variant="contained" className={`update${params.value}`}> |
|
||||
<IoNewspaperOutline style={{ fontSize: '20px', color: 'white' }} /> |
|
||||
</Button> |
|
||||
<Tooltip |
|
||||
anchorSelect={`.update${params.value}`} |
|
||||
style={{ fontSize: '13px', zIndex: 22 }} |
|
||||
place="bottom-end" |
|
||||
> |
|
||||
Imprimer un relevé de notes |
|
||||
</Tooltip> |
|
||||
</Link> |
|
||||
</div> |
|
||||
) |
|
||||
} |
|
||||
] |
|
||||
|
|
||||
const dataTable = dataToMap.map((data) => ({ |
|
||||
id: data.id, |
|
||||
nom: data.nom, |
|
||||
prenom: data.prenom, |
|
||||
photos: data.photos, |
|
||||
mention: returnmention(data.mention), |
|
||||
session: checkNumberSession(data.id), |
|
||||
moyenne: data.moyenne, |
|
||||
action: data.id |
|
||||
})) |
|
||||
|
|
||||
const [openCard, setOpenCart] = useState(false) |
|
||||
const [bolll, setBolll] = useState(false) |
|
||||
const [form, setForm] = useState({ |
|
||||
id: '', |
|
||||
niveau: '', |
|
||||
anneescolaire: '' |
|
||||
}) |
|
||||
const [selectedId, setSelectedId] = useState(null) // Store id dynamically |
|
||||
|
|
||||
const sendData = (id) => { |
|
||||
setSelectedId(id) |
|
||||
// if (selectedId !== null) { |
|
||||
setOpenCart(true) |
|
||||
// } |
|
||||
} |
|
||||
|
|
||||
useEffect(() => { |
|
||||
if (selectedId !== null) { |
|
||||
const foundData = dataToMap.find((item) => item.id === selectedId) |
|
||||
if (foundData) { |
|
||||
setForm((prevForm) => ({ |
|
||||
...prevForm, |
|
||||
id: foundData.id, |
|
||||
anneescolaire: foundData.anneescolaire, |
|
||||
niveau: niveau |
|
||||
})) // Update form with the found object |
|
||||
} |
|
||||
} |
|
||||
}, [openCard, selectedId]) |
|
||||
console.log(form) |
|
||||
const downloadButton = () => { |
|
||||
setBolll(true) |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* function to close modal |
|
||||
*/ |
|
||||
const handleCloseCart = () => { |
|
||||
setBolll(false) |
|
||||
setOpenCart(false) |
|
||||
} |
|
||||
|
|
||||
const modalReleverNotes = () => { |
|
||||
return ( |
|
||||
<Modal |
|
||||
open={openCard} |
|
||||
onClose={handleCloseCart} |
|
||||
aria-labelledby="modal-title" |
|
||||
aria-describedby="modal-description" |
|
||||
sx={{ |
|
||||
overflow: 'auto' |
|
||||
}} |
|
||||
> |
|
||||
<Box |
|
||||
sx={{ |
|
||||
boxShadow: 24, |
|
||||
p: 4, |
|
||||
overflow: 'auto', |
|
||||
position: 'relative' |
|
||||
}} |
|
||||
> |
|
||||
<ReleverNotes |
|
||||
id={form.id} |
|
||||
anneescolaire={scolaire} |
|
||||
niveau={form.niveau} |
|
||||
refs={bolll} |
|
||||
/> |
|
||||
<Button |
|
||||
color="warning" |
|
||||
variant="contained" |
|
||||
onClick={downloadButton} |
|
||||
sx={{ position: 'absolute', top: '2%', right: '10%' }} |
|
||||
> |
|
||||
<FaDownload /> |
|
||||
Télécharger |
|
||||
</Button> |
|
||||
<button |
|
||||
style={{ |
|
||||
position: 'absolute', |
|
||||
top: '2.5%', |
|
||||
right: '23%', |
|
||||
border: 'none', |
|
||||
background: 'orange', |
|
||||
outline: 'none', |
|
||||
borderRadius: '100px' |
|
||||
}} |
|
||||
onClick={handleCloseCart} |
|
||||
> |
|
||||
X |
|
||||
</button> |
|
||||
</Box> |
|
||||
</Modal> |
|
||||
) |
|
||||
} |
|
||||
|
|
||||
return ( |
|
||||
<div className={classe.mainHome}> |
|
||||
{modalReleverNotes()} |
|
||||
<div className={classeHome.header}> |
|
||||
<div className={classe.h1style}> |
|
||||
<div className={classeHome.blockTitle}> |
|
||||
<h1> |
|
||||
Notes des {niveau} en {scolaire} |
|
||||
</h1> |
|
||||
<div style={{ display: 'flex', gap: '10px' }}> |
|
||||
<Link to={`/resultat/${niveau}/${scolaire}`}> |
|
||||
<Button color="warning" variant="contained"> |
|
||||
Resultat |
|
||||
</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}> |
|
||||
<div style={{ display: 'flex', justifyContent: 'center' }}> |
|
||||
<Paper |
|
||||
sx={{ |
|
||||
height: 'auto', // Auto height to make the grid responsive |
|
||||
width: '100%', |
|
||||
minHeight: 500, // Ensures a minimum height |
|
||||
display: 'flex' |
|
||||
}} |
|
||||
> |
|
||||
<ThemeProvider theme={theme}> |
|
||||
<div |
|
||||
style={{ |
|
||||
display: 'flex', |
|
||||
alignItems: 'center', |
|
||||
justifyContent: 'center', |
|
||||
width: '100%', // Make it responsive to full width |
|
||||
flexDirection: 'column' // Stacks content vertically on smaller screens |
|
||||
}} |
|
||||
> |
|
||||
<DataGrid |
|
||||
rows={dataTable} |
|
||||
columns={columns} |
|
||||
initialState={{ pagination: { paginationModel } }} |
|
||||
pageSizeOptions={[5, 10]} |
|
||||
sx={{ |
|
||||
border: 0, |
|
||||
width: '100%', // 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> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
) |
|
||||
} |
|
||||
|
|
||||
export default Noteclasse |
|
||||
@ -1,176 +0,0 @@ |
|||||
import React, { useEffect, useState } from 'react' |
|
||||
import { Button } from '@mui/material' |
|
||||
import { createTheme, ThemeProvider } from '@mui/material/styles' |
|
||||
import { IoEyeSharp } from 'react-icons/io5' |
|
||||
import { frFR } from '@mui/x-data-grid/locales' |
|
||||
import { Tooltip } from 'react-tooltip' |
|
||||
import Paper from '@mui/material/Paper' |
|
||||
import { DataGrid, GridToolbar } from '@mui/x-data-grid' |
|
||||
import classe from '../assets/AllStyleComponents.module.css' |
|
||||
import classeHome from '../assets/Home.module.css' |
|
||||
import { Link } from 'react-router-dom' |
|
||||
|
|
||||
const Notes = () => { |
|
||||
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 paginationModel = { page: 0, pageSize: 5 } |
|
||||
|
|
||||
const columns = [ |
|
||||
{ field: 'niveau', headerName: 'Niveau', width: 170 }, |
|
||||
{ field: 'annee_scolaire', headerName: 'Année scolaire', width: 160 }, |
|
||||
// { field:'moyenne', headerName:'Moyenne de classe', width:160}, |
|
||||
{ |
|
||||
field: 'action', |
|
||||
headerName: 'Action', |
|
||||
flex: 1, |
|
||||
renderCell: (params) => ( |
|
||||
<div style={{ display: 'flex', gap: '10px' }}> |
|
||||
<Link to={`/noteclass/${params.row.niveau}/${params.row.annee_scolaire}`}> |
|
||||
<Button color="warning" variant="contained" className={`update${params.value}`}> |
|
||||
<IoEyeSharp style={{ fontSize: '20px', color: 'white' }} /> |
|
||||
</Button> |
|
||||
<Tooltip |
|
||||
anchorSelect={`.update${params.value}`} |
|
||||
style={{ fontSize: '13px', zIndex: 22 }} |
|
||||
place="bottom-end" |
|
||||
> |
|
||||
Voir tous les etudiants dans ce niveau |
|
||||
</Tooltip> |
|
||||
</Link> |
|
||||
</div> |
|
||||
) |
|
||||
} |
|
||||
] |
|
||||
|
|
||||
/** |
|
||||
* hook for dispplaying note |
|
||||
*/ |
|
||||
const [blockNotes, SetBlockNotes] = useState([]) |
|
||||
|
|
||||
useEffect(() => { |
|
||||
window.notes.getblockNote().then((response) => { |
|
||||
SetBlockNotes(response) |
|
||||
}) |
|
||||
}, []) |
|
||||
|
|
||||
const calculMoyenneDeClasse = (L1, annee) => { |
|
||||
let data = blockNotes.allData |
|
||||
let note = 0 |
|
||||
let someId = [] |
|
||||
let total = 0 |
|
||||
let totalEtudiant = [] |
|
||||
let id = [] |
|
||||
let totalCredit = 0 |
|
||||
|
|
||||
if (data != undefined) { |
|
||||
for (let index = 0; index < data.length; index++) { |
|
||||
for (let j = 0; j < data[index].length; j++) { |
|
||||
if (data[index][j].niveau == L1 && data[index][j].annee_scolaire == annee) { |
|
||||
totalEtudiant.push(data[index][j]) |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
for (let index = 0; index < totalEtudiant.length; index++) { |
|
||||
someId.push(totalEtudiant[index].etudiant_id) |
|
||||
id = [...new Set(someId)] |
|
||||
note += totalEtudiant[index].note * totalEtudiant[index].credit |
|
||||
totalCredit += totalEtudiant[index].credit |
|
||||
} |
|
||||
total = note / totalCredit |
|
||||
|
|
||||
return total.toFixed(2) |
|
||||
} |
|
||||
|
|
||||
let dataRow |
|
||||
if (blockNotes.response) { |
|
||||
dataRow = blockNotes.response.map((note, index) => ({ |
|
||||
id: index, |
|
||||
niveau: note.etudiant_niveau, |
|
||||
moyenne: calculMoyenneDeClasse(note.etudiant_niveau, note.annee_scolaire), |
|
||||
annee_scolaire: note.annee_scolaire, |
|
||||
action: note.niveau |
|
||||
})) |
|
||||
} else { |
|
||||
dataRow = [] |
|
||||
} |
|
||||
|
|
||||
return ( |
|
||||
<div className={classe.mainHome}> |
|
||||
<div className={classeHome.header}> |
|
||||
<div className={classe.h1style}> |
|
||||
<div className={classeHome.blockTitle}> |
|
||||
<h1>Notes</h1> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
|
|
||||
{/* display the data-grid students */} |
|
||||
<div className={classeHome.boxEtudiantsCard}> |
|
||||
<div style={{ display: 'flex', justifyContent: 'center' }}> |
|
||||
<Paper |
|
||||
sx={{ |
|
||||
height: 'auto', // Auto height to make the grid responsive |
|
||||
width: '100%', |
|
||||
minHeight: 500, // Ensures a minimum height |
|
||||
display: 'flex' |
|
||||
}} |
|
||||
> |
|
||||
<ThemeProvider theme={theme}> |
|
||||
<div |
|
||||
style={{ |
|
||||
display: 'flex', |
|
||||
alignItems: 'center', |
|
||||
justifyContent: 'center', |
|
||||
width: '100%', // Make it responsive to full width |
|
||||
flexDirection: 'column' // Stacks content vertically on smaller screens |
|
||||
}} |
|
||||
> |
|
||||
<DataGrid |
|
||||
rows={dataRow} |
|
||||
columns={columns} |
|
||||
initialState={{ pagination: { paginationModel } }} |
|
||||
pageSizeOptions={[5, 10]} |
|
||||
sx={{ |
|
||||
border: 0, |
|
||||
width: '100%', // 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> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
) |
|
||||
} |
|
||||
|
|
||||
export default Notes |
|
||||
@ -1,279 +0,0 @@ |
|||||
import { useState } from 'react' |
|
||||
import classe from '../assets/AllStyleComponents.module.css' |
|
||||
import img from '../assets/para.png' |
|
||||
import classeHome from '../assets/Home.module.css' |
|
||||
import { Box, Button, InputAdornment, TextField, Grid, Modal, Typography } from '@mui/material' |
|
||||
import classeAdd from '../assets/AddStudent.module.css' |
|
||||
import { useAuthContext } from '../contexts/AuthContext' |
|
||||
import { FaEnvelope, FaLock, FaUser } from 'react-icons/fa' |
|
||||
import svgSuccess from '../assets/success.svg' |
|
||||
import svgError from '../assets/error.svg' |
|
||||
import { Link } from 'react-router-dom' |
|
||||
import { GrConfigure } from 'react-icons/gr' |
|
||||
import IpConfig from './IpConfig' |
|
||||
|
|
||||
const Setting = () => { |
|
||||
const { token, setToken } = useAuthContext() |
|
||||
|
|
||||
const userInfo = JSON.parse(token) |
|
||||
|
|
||||
const [formData, setFormData] = useState({ |
|
||||
username: userInfo.username, |
|
||||
email: userInfo.email, |
|
||||
password: '', |
|
||||
id: userInfo.id |
|
||||
}) |
|
||||
|
|
||||
/** |
|
||||
* function to set the data in state |
|
||||
* @param {*} e |
|
||||
*/ |
|
||||
const handleInputChange = (e) => { |
|
||||
const { name, value } = e.target |
|
||||
setFormData((prevData) => ({ |
|
||||
...prevData, |
|
||||
[name]: value |
|
||||
})) |
|
||||
} |
|
||||
|
|
||||
const handleSubmit = async (e) => { |
|
||||
e.preventDefault() |
|
||||
// Handle form submission logic |
|
||||
const response = await window.allUser.updateUsers(formData) |
|
||||
console.log(response) |
|
||||
if (response.success) { |
|
||||
setOpen(true) |
|
||||
setCode(200) |
|
||||
setToken(JSON.stringify(response.users)) |
|
||||
setFormData({ |
|
||||
username: response.username, |
|
||||
email: response.email, |
|
||||
password: '', |
|
||||
id: userInfo.id |
|
||||
}) |
|
||||
} else { |
|
||||
setCode(422) |
|
||||
setOpen(true) |
|
||||
} |
|
||||
} |
|
||||
/** |
|
||||
* hook to open modal |
|
||||
*/ |
|
||||
const [open, setOpen] = useState(false) |
|
||||
const [code, setCode] = useState(200) |
|
||||
|
|
||||
/** |
|
||||
* function to close modal |
|
||||
*/ |
|
||||
const handleClose = () => setOpen(false) |
|
||||
|
|
||||
/** |
|
||||
* function to return the view Modal |
|
||||
* |
|
||||
* @returns {JSX} |
|
||||
*/ |
|
||||
const modals = () => ( |
|
||||
<Modal |
|
||||
open={open} |
|
||||
onClose={handleClose} |
|
||||
aria-labelledby="modal-title" |
|
||||
aria-describedby="modal-description" |
|
||||
> |
|
||||
<Box |
|
||||
sx={{ |
|
||||
position: 'absolute', |
|
||||
top: '50%', |
|
||||
left: '50%', |
|
||||
transform: 'translate(-50%, -50%)', |
|
||||
width: 450, |
|
||||
bgcolor: 'background.paper', |
|
||||
boxShadow: 24, |
|
||||
p: 4 |
|
||||
}} |
|
||||
> |
|
||||
{code == 422 ? ( |
|
||||
<Typography |
|
||||
style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: '10px' }} |
|
||||
> |
|
||||
<img src={svgError} alt="" width={50} height={50} />{' '} |
|
||||
<span style={{ marginLeft: '10px' }}>Email déjà pris</span> |
|
||||
</Typography> |
|
||||
) : ( |
|
||||
<Typography |
|
||||
style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: '10px' }} |
|
||||
> |
|
||||
<img src={svgSuccess} alt="" width={100} height={100} />{' '} |
|
||||
<span>Modification a été effectuée avec succès</span> |
|
||||
</Typography> |
|
||||
)} |
|
||||
<Box |
|
||||
sx={{ |
|
||||
marginTop: '2%', |
|
||||
display: 'flex', |
|
||||
gap: '20px', |
|
||||
alignItems: 'end', |
|
||||
justifyContent: 'flex-end' |
|
||||
}} |
|
||||
> |
|
||||
<Button onClick={handleClose} color="warning" variant="contained"> |
|
||||
OK |
|
||||
</Button> |
|
||||
</Box> |
|
||||
</Box> |
|
||||
</Modal> |
|
||||
) |
|
||||
|
|
||||
const [openConfig, setOpenCOnfig] = useState(false) |
|
||||
const onCloseConfig = () => setOpenCOnfig(false) |
|
||||
|
|
||||
const openCOnfigFunction = () => setOpenCOnfig(true) |
|
||||
|
|
||||
return ( |
|
||||
<div className={classe.mainHome}> |
|
||||
{modals()} |
|
||||
<IpConfig onClose={onCloseConfig} open={openConfig} /> |
|
||||
<div className={classeHome.header}> |
|
||||
<div className={classe.h1style}> |
|
||||
<div className={classeHome.blockTitle}> |
|
||||
<h1>setting</h1> |
|
||||
<Link to={'#'} onClick={openCOnfigFunction}> |
|
||||
<Button color="warning" variant="contained"> |
|
||||
<GrConfigure style={{ fontSize: '20px' }} /> IP configuration |
|
||||
</Button> |
|
||||
</Link> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
{/* contenu */} |
|
||||
<div className={classeHome.contenaire}> |
|
||||
<div className={classeAdd.boxEtudiantsCard}> |
|
||||
<Box |
|
||||
sx={{ |
|
||||
position: 'absolute', |
|
||||
top: '50%', |
|
||||
left: '50%', |
|
||||
transform: 'translate(-50%, -50%)', |
|
||||
width: 700, |
|
||||
borderRadius: '3%', |
|
||||
bgcolor: 'background.paper', |
|
||||
boxShadow: 24, |
|
||||
p: 4 |
|
||||
}} |
|
||||
> |
|
||||
<Box |
|
||||
sx={{ |
|
||||
marginTop: '5%', |
|
||||
display: 'flex', |
|
||||
gap: '10px', |
|
||||
alignItems: 'start', |
|
||||
flexDirection: 'column', |
|
||||
fontSize: 16, |
|
||||
fontFamily: 'sans-serif', |
|
||||
justifyContent: 'flex-end' |
|
||||
}} |
|
||||
> |
|
||||
<span style={{ display: 'flex', marginLeft: '40%' }}> |
|
||||
<img src={img} alt="" height={150} width={150} /> |
|
||||
</span> |
|
||||
|
|
||||
<form onSubmit={handleSubmit} style={{ marginTop: '5%' }}> |
|
||||
{/* */} |
|
||||
<Grid container spacing={2}> |
|
||||
<Grid item xs={12} sm={6}> |
|
||||
<TextField |
|
||||
label="Nom d'utilisateur" |
|
||||
name="username" |
|
||||
color="warning" |
|
||||
fullWidth |
|
||||
value={formData.username} |
|
||||
onChange={handleInputChange} |
|
||||
required |
|
||||
InputProps={{ |
|
||||
startAdornment: ( |
|
||||
<InputAdornment position="start"> |
|
||||
<FaUser /> |
|
||||
</InputAdornment> |
|
||||
) |
|
||||
}} |
|
||||
sx={{ |
|
||||
'& .MuiOutlinedInput-root': { |
|
||||
'&:hover fieldset': { |
|
||||
borderColor: '#ff9800' // Set the border color on hover |
|
||||
} |
|
||||
} |
|
||||
}} |
|
||||
/> |
|
||||
</Grid> |
|
||||
<Grid item xs={12} sm={6}> |
|
||||
<TextField |
|
||||
label="Email" |
|
||||
name="email" |
|
||||
fullWidth |
|
||||
required |
|
||||
color="warning" |
|
||||
value={formData.email} |
|
||||
onChange={handleInputChange} |
|
||||
InputProps={{ |
|
||||
startAdornment: ( |
|
||||
<InputAdornment position="start"> |
|
||||
<FaEnvelope /> |
|
||||
</InputAdornment> |
|
||||
) |
|
||||
}} |
|
||||
sx={{ |
|
||||
'& .MuiOutlinedInput-root': { |
|
||||
'&:hover fieldset': { |
|
||||
borderColor: '#ff9800' // Set the border color on hover |
|
||||
} |
|
||||
} |
|
||||
}} |
|
||||
/> |
|
||||
</Grid> |
|
||||
{/* matieres Mecanique general */} |
|
||||
<Grid item xs={12} sm={12}> |
|
||||
<TextField |
|
||||
label="Mot de passe" |
|
||||
name="password" |
|
||||
fullWidth |
|
||||
helperText="À laisser vide si vous ne voulez pas le modifier" |
|
||||
color="warning" |
|
||||
value={formData.password} |
|
||||
onChange={handleInputChange} |
|
||||
InputProps={{ |
|
||||
startAdornment: ( |
|
||||
<InputAdornment position="start"> |
|
||||
<FaLock /> |
|
||||
</InputAdornment> |
|
||||
) |
|
||||
}} |
|
||||
sx={{ |
|
||||
'& .MuiOutlinedInput-root': { |
|
||||
'&:hover fieldset': { |
|
||||
borderColor: '#ff9800' // Set the border color on hover |
|
||||
} |
|
||||
} |
|
||||
}} |
|
||||
/> |
|
||||
</Grid> |
|
||||
{/* Matieres Resistance Materiaux */} |
|
||||
{/* Submit Button */} |
|
||||
<Grid |
|
||||
item |
|
||||
xs={12} |
|
||||
style={{ display: 'flex', gap: '30px', justifyContent: 'flex-end' }} |
|
||||
> |
|
||||
<Button type="submit" color="warning" variant="contained"> |
|
||||
Enregister |
|
||||
</Button> |
|
||||
</Grid> |
|
||||
</Grid> |
|
||||
</form> |
|
||||
</Box> |
|
||||
</Box> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
) |
|
||||
} |
|
||||
|
|
||||
export default Setting |
|
||||
@ -1,594 +0,0 @@ |
|||||
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 jsPDF from 'jspdf' |
|
||||
import html2Canvas from 'html2canvas' |
|
||||
import logoRelerev1 from '../assets/logorelever.png' |
|
||||
import logoRelerev2 from '../assets/logorelever2.png' |
|
||||
import dayjs from 'dayjs' |
|
||||
import getSemestre from './function/GetSemestre' |
|
||||
import { descisionJury, getmentionAfterNotes } from './function/FonctionRelever' |
|
||||
|
|
||||
const ReleverNotes = ({ id, anneescolaire, niveau, refs }) => { |
|
||||
const [etudiant, setEtudiant] = useState([]) |
|
||||
const [matieres, setMatieres] = useState([]) |
|
||||
const [notes, setNotes] = useState([]) |
|
||||
|
|
||||
const handleDownloadPDF = async () => { |
|
||||
const input = Telever.current |
|
||||
|
|
||||
// Set a high scale for better quality |
|
||||
const scale = 3 |
|
||||
|
|
||||
html2Canvas(input, { |
|
||||
scale, // Increase resolution |
|
||||
useCORS: true, // Handle cross-origin images |
|
||||
allowTaint: true |
|
||||
}).then((canvas) => { |
|
||||
const imgData = canvas.toDataURL('image/png') |
|
||||
|
|
||||
// Create a PDF with dimensions matching the captured content |
|
||||
const pdf = new jsPDF({ |
|
||||
orientation: 'portrait', |
|
||||
unit: 'mm', |
|
||||
format: 'a4' |
|
||||
}) |
|
||||
|
|
||||
const imgWidth = 210 // A4 width in mm |
|
||||
const pageHeight = 297 // A4 height in mm |
|
||||
const imgHeight = (canvas.height * imgWidth) / canvas.width |
|
||||
|
|
||||
let position = 0 |
|
||||
|
|
||||
pdf.addImage(imgData, 'PNG', 0, position, imgWidth, imgHeight, '', 'FAST') |
|
||||
|
|
||||
// Handle multi-page case |
|
||||
while (position + imgHeight >= pageHeight) { |
|
||||
position -= pageHeight |
|
||||
pdf.addPage() |
|
||||
pdf.addImage(imgData, 'PNG', 0, position, imgWidth, imgHeight, '', 'FAST') |
|
||||
} |
|
||||
|
|
||||
pdf.save('document.pdf') |
|
||||
}) |
|
||||
} |
|
||||
|
|
||||
useEffect(() => { |
|
||||
if (!id) { |
|
||||
// id doesn't exist, you might want to retry, or do nothing |
|
||||
// For example, refetch later or show an error |
|
||||
return |
|
||||
} |
|
||||
window.etudiants.getSingle({ id }).then((response) => { |
|
||||
setEtudiant(response) |
|
||||
}) |
|
||||
|
|
||||
window.mention.getMention().then((response) => { |
|
||||
setMatieres(response) |
|
||||
}) |
|
||||
|
|
||||
window.notes.noteRelerer({ id, anneescolaire, niveau }).then((response) => { |
|
||||
setNotes(response) |
|
||||
}) |
|
||||
}, [id]) |
|
||||
|
|
||||
const Telever = useRef() |
|
||||
|
|
||||
useEffect(() => { |
|
||||
if (refs) { |
|
||||
handleDownloadPDF() |
|
||||
} |
|
||||
}, [refs]) |
|
||||
|
|
||||
const [matiereWithSemestre, setMatiereWithSemestre] = useState([]) |
|
||||
const [matiereWithSemestreRepech, setMatiereWithSemestreRepech] = useState([]) |
|
||||
|
|
||||
useEffect(() => { |
|
||||
if (!notes.noteNormal || !notes.semestre || !notes.noteRepech) return // Ensure data exists |
|
||||
|
|
||||
const updatedMatieres = notes.noteNormal.map((matiere) => { |
|
||||
// Get the semesters based on the student's niveau |
|
||||
const semesters = getSemestre(matiere.etudiant_niveau) |
|
||||
|
|
||||
// Find the matched semestre based on the conditions |
|
||||
const matchedSemestre = notes.semestre.find( |
|
||||
(sem) => |
|
||||
sem.matiere_id === matiere.matiere_id && |
|
||||
sem.mention_id === matiere.mention_id && |
|
||||
(sem.nom === semesters[0] || sem.nom === semesters[1]) // Check if the semester matches |
|
||||
) |
|
||||
|
|
||||
return { |
|
||||
...matiere, |
|
||||
semestre: matchedSemestre ? matchedSemestre.nom : null // Add 'semestre' or set null if no match |
|
||||
} |
|
||||
}) |
|
||||
|
|
||||
const updatedMatieresRepech = notes.noteRepech.map((matiere) => { |
|
||||
// Get the semesters based on the student's niveau |
|
||||
const semesters = getSemestre(matiere.etudiant_niveau) |
|
||||
|
|
||||
// Find the matched semestre based on the conditions |
|
||||
const matchedSemestre = notes.semestre.find( |
|
||||
(sem) => |
|
||||
sem.matiere_id === matiere.matiere_id && |
|
||||
sem.mention_id === matiere.mention_id && |
|
||||
(sem.nom === semesters[0] || sem.nom === semesters[1]) // Check if the semester matches |
|
||||
) |
|
||||
|
|
||||
// Return the updated matiere with the matched semestre or null if no match |
|
||||
return { |
|
||||
...matiere, |
|
||||
semestre: matchedSemestre ? matchedSemestre.nom : null // Add 'semestre' or set null if no match |
|
||||
} |
|
||||
}) |
|
||||
|
|
||||
setMatiereWithSemestre(updatedMatieres) |
|
||||
setMatiereWithSemestreRepech(updatedMatieresRepech) |
|
||||
}, [notes]) |
|
||||
|
|
||||
function compareMention(mentionID) { |
|
||||
let statusText |
|
||||
|
|
||||
matieres.map((statu) => { |
|
||||
if (mentionID == statu.id) { |
|
||||
statusText = statu.nom |
|
||||
} |
|
||||
}) |
|
||||
|
|
||||
return statusText ? statusText.charAt(0).toUpperCase() + statusText.slice(1) : statusText |
|
||||
} |
|
||||
|
|
||||
// data are finaly get and ready for the traitement below |
|
||||
|
|
||||
// Merging the arrays based on matiere_id |
|
||||
matiereWithSemestre.forEach((item1) => { |
|
||||
// Find the corresponding item in array2 based on matiere_id |
|
||||
let matchingItem = matiereWithSemestreRepech.find( |
|
||||
(item2) => item2.matiere_id === item1.matiere_id |
|
||||
) |
|
||||
|
|
||||
// If there's a match, add noterepech from array2, otherwise use the note from array1 |
|
||||
item1.noterepech = matchingItem ? matchingItem.note : item1.note |
|
||||
}) |
|
||||
|
|
||||
// step 1 group all by semestre |
|
||||
const groupedDataBySemestre = matiereWithSemestre.reduce((acc, matiere) => { |
|
||||
const { semestre } = matiere |
|
||||
|
|
||||
if (!acc[semestre]) { |
|
||||
acc[semestre] = [] |
|
||||
} |
|
||||
|
|
||||
acc[semestre].push(matiere) |
|
||||
|
|
||||
return acc |
|
||||
}, {}) |
|
||||
|
|
||||
const compareMoyenne = (normal, rattrapage) => { |
|
||||
const note = Math.max(Number(normal), Number(rattrapage)) |
|
||||
return note >= 10 ? 'Admis' : 'Ajourné' |
|
||||
} |
|
||||
|
|
||||
const TbodyContent = () => { |
|
||||
return ( |
|
||||
<> |
|
||||
{Object.entries(groupedDataBySemestre).map(([semestre, matieres]) => { |
|
||||
// Group by unite_enseignement inside each semestre |
|
||||
const groupedByUnite = matieres.reduce((acc, matiere) => { |
|
||||
if (!acc[matiere.unite_enseignement]) { |
|
||||
acc[matiere.unite_enseignement] = [] |
|
||||
} |
|
||||
acc[matiere.unite_enseignement].push(matiere) |
|
||||
return acc |
|
||||
}, {}) |
|
||||
|
|
||||
return ( |
|
||||
<tbody key={semestre} style={{ border: 'none' }}> |
|
||||
{Object.entries(groupedByUnite).map(([unite, matieres], uniteIndex) => ( |
|
||||
<> |
|
||||
{matieres.map((matiere, matiereIndex) => ( |
|
||||
<tr key={matiere.id} style={{ border: 'none' }}> |
|
||||
{/* Display 'semestre' only for the first row of the first unite_enseignement */} |
|
||||
{uniteIndex === 0 && matiereIndex === 0 && ( |
|
||||
<td |
|
||||
rowSpan={ |
|
||||
Object.values(groupedByUnite).flat().length + |
|
||||
Object.values(groupedByUnite).flat().length |
|
||||
} |
|
||||
style={{ |
|
||||
fontWeight: 'bold', |
|
||||
textAlign: 'center', |
|
||||
paddingTop: '8px', |
|
||||
borderRight: 'solid 1px black', |
|
||||
borderBottom: 'solid 1px black', |
|
||||
background: '#bdbcbc', |
|
||||
borderLeft: 'solid 1px black' |
|
||||
}} |
|
||||
> |
|
||||
{semestre} |
|
||||
</td> |
|
||||
)} |
|
||||
|
|
||||
{/* Display 'unite_enseignement' only for the first row of each group */} |
|
||||
{matiereIndex === 0 && ( |
|
||||
<td |
|
||||
rowSpan={matieres.length} |
|
||||
style={{ |
|
||||
fontWeight: 'bold', |
|
||||
textAlign: 'center', |
|
||||
borderRight: 'solid 1px black', |
|
||||
// borderBottom: 'solid 1px black', |
|
||||
borderTop: 'solid 1px black' |
|
||||
}} |
|
||||
> |
|
||||
{unite} |
|
||||
</td> |
|
||||
)} |
|
||||
|
|
||||
{/* Matiere Data */} |
|
||||
<td style={{ borderRight: 'solid 1px black', borderTop: 'solid 1px black' }}> |
|
||||
{matiere.nom} |
|
||||
</td> |
|
||||
<td style={{ borderRight: 'solid 1px black', borderTop: 'solid 1px black' }}> |
|
||||
{matiere.credit} |
|
||||
</td> |
|
||||
<td style={{ borderRight: 'solid 1px black', borderTop: 'solid 1px black' }}> |
|
||||
{matiere.note} |
|
||||
</td> |
|
||||
<td style={{ borderRight: 'solid 1px black', borderTop: 'solid 1px black' }}> |
|
||||
{matiere.credit} |
|
||||
</td> |
|
||||
<td style={{ borderRight: 'solid 1px black', borderTop: 'solid 1px black' }}> |
|
||||
{matiere.noterepech} |
|
||||
</td> |
|
||||
|
|
||||
{/* Display the comparison value only once */} |
|
||||
{matiereIndex === 0 && ( |
|
||||
<td |
|
||||
rowSpan={matieres.length} |
|
||||
style={{ |
|
||||
fontWeight: 'bold', |
|
||||
textAlign: 'center', |
|
||||
borderRight: 'solid 1px black', |
|
||||
borderTop: 'solid 1px black' |
|
||||
}} |
|
||||
> |
|
||||
{/* Replace 'hgh' with your logic for displaying the comparison */} |
|
||||
{compareMoyenne( |
|
||||
( |
|
||||
matieres.reduce((total, matiere) => total + matiere.note, 0) / |
|
||||
matieres.length |
|
||||
).toFixed(2), |
|
||||
( |
|
||||
matieres.reduce((total, matiere) => total + matiere.noterepech, 0) / |
|
||||
matieres.length |
|
||||
).toFixed(2) |
|
||||
)} |
|
||||
</td> |
|
||||
)} |
|
||||
</tr> |
|
||||
))} |
|
||||
|
|
||||
{/* Add Total Row for 'unite_enseignement' */} |
|
||||
<tr |
|
||||
style={{ background: '#bdbcbc', border: 'none', borderLeft: 'solid 1px black' }} |
|
||||
> |
|
||||
<td |
|
||||
colSpan={2} |
|
||||
style={{ |
|
||||
textAlign: 'right', |
|
||||
fontWeight: 'bold', |
|
||||
borderRight: 'solid 1px black', |
|
||||
borderBottom: 'none', |
|
||||
borderLeft: 'solid 1px black', |
|
||||
borderTop: 'solid 1px black' |
|
||||
}} |
|
||||
> |
|
||||
Total de Credit et Moyenne des Notes |
|
||||
</td> |
|
||||
<td |
|
||||
style={{ |
|
||||
textAlign: 'center', |
|
||||
fontWeight: 'bold', |
|
||||
borderRight: 'solid 1px black', |
|
||||
borderTop: 'solid 1px black' |
|
||||
}} |
|
||||
> |
|
||||
{/* Calculate Total de Credit */} |
|
||||
{matieres.reduce((total, matiere) => total + matiere.credit, 0)} |
|
||||
</td> |
|
||||
<td |
|
||||
style={{ |
|
||||
textAlign: 'center', |
|
||||
fontWeight: 'bold', |
|
||||
borderRight: 'solid 1px black', |
|
||||
borderTop: 'solid 1px black' |
|
||||
}} |
|
||||
className="moyenneNotes" |
|
||||
> |
|
||||
{/* Calculate Moyenne des Notes */} |
|
||||
{( |
|
||||
matieres.reduce((total, matiere) => total + matiere.note, 0) / |
|
||||
matieres.length |
|
||||
).toFixed(2)}{' '} |
|
||||
{/* Format to 2 decimal places */} |
|
||||
</td> |
|
||||
<td |
|
||||
style={{ |
|
||||
textAlign: 'center', |
|
||||
fontWeight: 'bold', |
|
||||
borderTop: 'solid 1px black', |
|
||||
borderRight: 'solid 1px black' |
|
||||
}} |
|
||||
> |
|
||||
{matieres.reduce((total, matiere) => total + matiere.credit, 0)} |
|
||||
</td> |
|
||||
<td |
|
||||
style={{ |
|
||||
textAlign: 'center', |
|
||||
fontWeight: 'bold', |
|
||||
borderRight: 'solid 1px black', |
|
||||
borderTop: 'solid 1px black' |
|
||||
}} |
|
||||
className="moyenneNotesRattrapage" |
|
||||
> |
|
||||
{( |
|
||||
matieres.reduce((total, matiere) => total + matiere.noterepech, 0) / |
|
||||
matieres.length |
|
||||
).toFixed(2)} |
|
||||
</td> |
|
||||
<td |
|
||||
style={{ |
|
||||
textAlign: 'center', |
|
||||
fontWeight: 'bold', |
|
||||
borderRight: 'solid 1px black', |
|
||||
borderTop: 'solid 1px black' |
|
||||
}} |
|
||||
></td> |
|
||||
</tr> |
|
||||
</> |
|
||||
))} |
|
||||
</tbody> |
|
||||
) |
|
||||
})} |
|
||||
</> |
|
||||
) |
|
||||
} |
|
||||
|
|
||||
const totalNotes = () => { |
|
||||
let totalNotes = document.querySelectorAll('.moyenneNotes') |
|
||||
let totalNotesRepech = document.querySelectorAll('.moyenneNotesRattrapage') |
|
||||
|
|
||||
let TotalNoteNumber = 0 |
|
||||
let TotalNoteNumberRepech = 0 |
|
||||
|
|
||||
totalNotes.forEach((notes) => { |
|
||||
TotalNoteNumber += Number(notes.textContent / totalNotes.length) |
|
||||
// console.log(notes.textContent); |
|
||||
}) |
|
||||
|
|
||||
totalNotesRepech.forEach((notes) => { |
|
||||
TotalNoteNumberRepech += Number(notes.textContent / totalNotes.length) |
|
||||
// console.log(notes.textContent); |
|
||||
}) |
|
||||
|
|
||||
let note = Math.max(TotalNoteNumber, TotalNoteNumberRepech) |
|
||||
|
|
||||
return note |
|
||||
} |
|
||||
|
|
||||
const [note, setNote] = useState(0) |
|
||||
|
|
||||
useEffect(() => { |
|
||||
setNote(totalNotes()) |
|
||||
}, [TbodyContent]) |
|
||||
|
|
||||
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%', |
|
||||
width: '70%', |
|
||||
marginTop: '2%', |
|
||||
justifyContent: 'center' |
|
||||
}} |
|
||||
ref={Telever} |
|
||||
> |
|
||||
<div style={{ width: '80%' }}> |
|
||||
<div |
|
||||
style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }} |
|
||||
> |
|
||||
<img src={logoRelerev1} alt="image en tete" width={70} /> |
|
||||
<img src={logoRelerev2} alt="image en tete" width={70} /> |
|
||||
</div> |
|
||||
<hr style={{ margin: 0, border: 'solid 1px black' }} /> |
|
||||
<h4 style={{ textTransform: 'uppercase', textAlign: 'center', marginBottom: 0 }}> |
|
||||
Releve de notes |
|
||||
</h4> |
|
||||
<hr style={{ margin: 0, border: 'solid 1px black' }} /> |
|
||||
{/* block info */} |
|
||||
<div style={{ marginTop: '2px', display: 'flex' }}> |
|
||||
{/* gauche */} |
|
||||
<div style={{ display: 'flex', width: '60%' }}> |
|
||||
{/* gauche gauche */} |
|
||||
<div style={{ width: '40%' }}> |
|
||||
<span> |
|
||||
<b>Nom</b> |
|
||||
</span> |
|
||||
<br /> |
|
||||
<span> |
|
||||
<b>Prenom</b> |
|
||||
</span> |
|
||||
<br /> |
|
||||
<span> |
|
||||
<b>Date de naissance</b> |
|
||||
</span> |
|
||||
<br /> |
|
||||
<span> |
|
||||
<b>Codage</b> |
|
||||
</span> |
|
||||
</div> |
|
||||
{/* gauche droite */} |
|
||||
<div style={{ width: '60%' }}> |
|
||||
<span>: {etudiant.nom}</span> |
|
||||
<br /> |
|
||||
<span>: {etudiant.prenom}</span> |
|
||||
<br /> |
|
||||
<span>: {dayjs(etudiant.date_de_naissances).format('DD/MM/YYYY')}</span> |
|
||||
<br /> |
|
||||
<span>: {etudiant.num_inscription}</span> |
|
||||
</div> |
|
||||
</div> |
|
||||
{/* droite */} |
|
||||
<div style={{ display: 'flex', width: '40%' }}> |
|
||||
{/* droite gauche */} |
|
||||
<div style={{ width: '30%' }}> |
|
||||
<span> |
|
||||
<b>Annee U</b> |
|
||||
</span> |
|
||||
<br /> |
|
||||
<span> |
|
||||
<b>Niveau</b> |
|
||||
</span> |
|
||||
<br /> |
|
||||
<span> |
|
||||
<b>Parcours</b> |
|
||||
</span> |
|
||||
</div> |
|
||||
{/* droite droite */} |
|
||||
<div style={{ width: '70%' }}> |
|
||||
<span>: {etudiant.annee_scolaire}</span> |
|
||||
<br /> |
|
||||
<span>: {etudiant.niveau}</span> |
|
||||
<br /> |
|
||||
<span>: {compareMention(etudiant.mention_id)}</span> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
|
|
||||
{/* table */} |
|
||||
<table style={{ marginTop: '5px', borderCollapse: 'collapse' }}> |
|
||||
<thead> |
|
||||
<tr style={{ borderTop: 'solid 1px black', textAlign: 'center' }}> |
|
||||
<th colSpan={3}></th> |
|
||||
<th |
|
||||
colSpan={4} |
|
||||
style={{ |
|
||||
background: '#bdbcbc', |
|
||||
borderLeft: 'solid 1px black', |
|
||||
borderRight: 'solid 1px black' |
|
||||
}} |
|
||||
> |
|
||||
Session |
|
||||
</th> |
|
||||
<th></th> |
|
||||
</tr> |
|
||||
<tr style={{ borderTop: 'solid 1px black', textAlign: 'center' }}> |
|
||||
<th style={{ borderLeft: 'solid 1px black' }}></th> |
|
||||
<th style={{ borderLeft: 'solid 1px black' }}></th> |
|
||||
<th style={{ borderLeft: 'solid 1px black' }}></th> |
|
||||
<th |
|
||||
colSpan={2} |
|
||||
style={{ background: '#bdbcbc', borderLeft: 'solid 1px black' }} |
|
||||
> |
|
||||
Normale |
|
||||
</th> |
|
||||
<th |
|
||||
colSpan={2} |
|
||||
style={{ background: '#bdbcbc', borderLeft: 'solid 1px black' }} |
|
||||
> |
|
||||
Rattrapage |
|
||||
</th> |
|
||||
<th |
|
||||
style={{ borderLeft: 'solid 1px black', borderRight: 'solid 1px black' }} |
|
||||
></th> |
|
||||
</tr> |
|
||||
<tr |
|
||||
style={{ |
|
||||
borderTop: 'solid 1px black', |
|
||||
// borderBottom: 'solid 1px black', |
|
||||
background: '#bdbcbc', |
|
||||
textAlign: 'center' |
|
||||
}} |
|
||||
> |
|
||||
<th style={{ borderLeft: 'solid 1px black', borderBottom: 'solid 1px black' }}> |
|
||||
semestre |
|
||||
</th> |
|
||||
<th style={{ borderLeft: 'solid 1px black' }}>UE</th> |
|
||||
<th style={{ borderLeft: 'solid 1px black' }}>EC</th> |
|
||||
<th style={{ borderLeft: 'solid 1px black', padding: '0 5px' }}>crédit</th> |
|
||||
<th style={{ borderLeft: 'solid 1px black', padding: '0 5px' }}>Notes</th> |
|
||||
<th style={{ borderLeft: 'solid 1px black', padding: '0 5px' }}>crédit</th> |
|
||||
<th style={{ borderLeft: 'solid 1px black', padding: '0 5px' }}>Notes</th> |
|
||||
<th style={{ borderLeft: 'solid 1px black', borderRight: 'solid 1px black' }}> |
|
||||
Observation |
|
||||
</th> |
|
||||
</tr> |
|
||||
</thead> |
|
||||
<TbodyContent /> |
|
||||
<tbody style={{ fontWeight: 'bold' }}> |
|
||||
<tr style={{ border: 'solid 1px black' }}> |
|
||||
<td |
|
||||
colSpan={2} |
|
||||
style={{ |
|
||||
borderRight: 'solid 1px black', |
|
||||
textAlign: 'left', |
|
||||
paddingLeft: '2%' |
|
||||
}} |
|
||||
> |
|
||||
Moyenne |
|
||||
</td> |
|
||||
<td style={{ borderRight: 'solid 1px black' }}>{note.toFixed(2)}</td> |
|
||||
<td style={{ borderRight: 'solid 1px black' }}>/20</td> |
|
||||
<td colSpan={4}></td> |
|
||||
</tr> |
|
||||
<tr style={{ border: 'solid 1px black' }}> |
|
||||
<td |
|
||||
colSpan={3} |
|
||||
style={{ |
|
||||
borderRight: 'solid 1px black', |
|
||||
textAlign: 'left', |
|
||||
paddingLeft: '2%' |
|
||||
}} |
|
||||
> |
|
||||
Mention:{' '} |
|
||||
<span style={{ marginLeft: '3%' }}>{getmentionAfterNotes(note)}</span> |
|
||||
</td> |
|
||||
<td colSpan={5} style={{ textAlign: 'left', paddingLeft: '1%' }}> |
|
||||
Décision du Jury:{' '} |
|
||||
<span style={{ marginLeft: '3%' }}> |
|
||||
{descisionJury(note, etudiant.niveau)} |
|
||||
</span> |
|
||||
</td> |
|
||||
</tr> |
|
||||
</tbody> |
|
||||
</table> |
|
||||
<div style={{ textAlign: 'right', marginRight: '20%' }}> |
|
||||
<p> |
|
||||
<b>Toamasine le</b> |
|
||||
</p> |
|
||||
{/* texte hidden for place in signature */} |
|
||||
<p style={{ visibility: 'hidden' }}> |
|
||||
Lorem ipsum dolor sit amet consectetur adipisicing elit. Blanditiis delectus |
|
||||
perspiciatis nisi aliquid eos adipisci cumque amet ratione error voluptatum. |
|
||||
Expedita velit enim nulla nam? Vitae fuga enim et temporibus. Lorem ipsum dolor |
|
||||
sit amet, consectetur adipisicing elit. Mollitia, assumenda? |
|
||||
</p> |
|
||||
</div> |
|
||||
</div> |
|
||||
</Paper> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
) |
|
||||
} |
|
||||
|
|
||||
export default ReleverNotes |
|
||||
@ -1,383 +0,0 @@ |
|||||
import React, { useEffect, useRef, useState } from 'react' |
|
||||
import { Link, useParams } from 'react-router-dom' |
|
||||
import { IoMdReturnRight } from 'react-icons/io' |
|
||||
import { |
|
||||
Box, |
|
||||
InputAdornment, |
|
||||
Typography, |
|
||||
Modal, |
|
||||
TextField, |
|
||||
Grid, |
|
||||
Button, |
|
||||
Autocomplete |
|
||||
} from '@mui/material' |
|
||||
import { BsBookmarkPlusFill } from 'react-icons/bs' |
|
||||
import svgSuccess from '../assets/success.svg' |
|
||||
import svgError from '../assets/error.svg' |
|
||||
import { MdOutlineNumbers } from 'react-icons/md' |
|
||||
import ChangeCapital from './function/ChangeCapitalLetter' |
|
||||
import { CiCalendar } from 'react-icons/ci' |
|
||||
import { IoBookmark } from 'react-icons/io5' |
|
||||
import { FaClipboardList, FaClock } from 'react-icons/fa' |
|
||||
import classe from '../assets/AllStyleComponents.module.css' |
|
||||
import classeHome from '../assets/Home.module.css' |
|
||||
|
|
||||
const SingleMatiere = () => { |
|
||||
const { id } = useParams() |
|
||||
const [matiere, setMatiere] = useState(null) |
|
||||
const [mentions, setMentions] = useState([]) |
|
||||
const [formData, setFormData] = useState({ |
|
||||
nom: '', |
|
||||
credit: '', |
|
||||
uniter: '', |
|
||||
ue: '', |
|
||||
id: id |
|
||||
}) |
|
||||
|
|
||||
const [message, setMessage] = useState('') |
|
||||
const [status, setStatus] = useState(200) |
|
||||
const [open, setOpen] = useState(false) |
|
||||
const uniterRef = useRef() |
|
||||
const ueRef = useRef() |
|
||||
|
|
||||
// Helper function to convert a string of IDs to an array |
|
||||
const stringToArray = (data) => (data ? data.split(',').map(Number) : []) |
|
||||
|
|
||||
useEffect(() => { |
|
||||
// Fetch single matiere data by ID |
|
||||
window.matieres.getMatiereByID({ id }).then((response) => { |
|
||||
setMatiere(response) |
|
||||
}) |
|
||||
|
|
||||
// Fetch mentions data |
|
||||
window.mention.getMention().then((response) => { |
|
||||
setMentions(Array.isArray(response) ? response : []) |
|
||||
}) |
|
||||
}, [id]) |
|
||||
|
|
||||
useEffect(() => { |
|
||||
if (matiere) { |
|
||||
setFormData((prev) => ({ |
|
||||
...prev, |
|
||||
nom: matiere.nom || '', |
|
||||
credit: matiere.credit || '', |
|
||||
uniter: matiere.unite_enseignement || '', |
|
||||
ue: matiere.ue || '', |
|
||||
id: matiere.id || id |
|
||||
})) |
|
||||
} |
|
||||
}, [matiere, id]) |
|
||||
|
|
||||
const handleChange = (e) => { |
|
||||
const { name, value } = e.target |
|
||||
setFormData((prevData) => ({ |
|
||||
...prevData, |
|
||||
[name]: value |
|
||||
})) |
|
||||
} |
|
||||
|
|
||||
const handleSubmit = async (e) => { |
|
||||
e.preventDefault() |
|
||||
const response = await window.matieres.updateMatiere(formData) |
|
||||
console.log(response) |
|
||||
if (response.success) { |
|
||||
setStatus(200) |
|
||||
setMessage('Modification a été effectuée avec succès') |
|
||||
} else { |
|
||||
setStatus(400) |
|
||||
setMessage('La matière existe déjà dans la base !') |
|
||||
} |
|
||||
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>{message}</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>Mise à jour des matières</h1> |
|
||||
<div style={{ display: 'flex', gap: '20px' }}> |
|
||||
<Link to="/matiere"> |
|
||||
<Button color="warning" variant="contained"> |
|
||||
<IoMdReturnRight style={{ fontSize: '20px' }} /> |
|
||||
</Button> |
|
||||
</Link> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
|
|
||||
<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 |
|
||||
}} |
|
||||
> |
|
||||
<form onSubmit={handleSubmit} style={{ width: '100%' }}> |
|
||||
<h4 style={{ textAlign: 'center' }}>Mise à jour</h4> |
|
||||
<Grid container spacing={2}> |
|
||||
<Grid item xs={12} sm={6}> |
|
||||
<TextField |
|
||||
label="Nom" |
|
||||
name="nom" |
|
||||
color="warning" |
|
||||
fullWidth |
|
||||
value={formData.nom} |
|
||||
onChange={handleChange} |
|
||||
required |
|
||||
InputProps={{ |
|
||||
startAdornment: ( |
|
||||
<InputAdornment position="start"> |
|
||||
<BsBookmarkPlusFill /> |
|
||||
</InputAdornment> |
|
||||
) |
|
||||
}} |
|
||||
sx={{ |
|
||||
marginBottom: '5px', |
|
||||
'& .MuiOutlinedInput-root': { |
|
||||
'&:hover fieldset': { |
|
||||
borderColor: '#ff9800' // Set the border color on hover |
|
||||
} |
|
||||
} |
|
||||
}} |
|
||||
/> |
|
||||
</Grid> |
|
||||
<Grid item xs={12} sm={6}> |
|
||||
<TextField |
|
||||
label="Crédit" |
|
||||
name="credit" |
|
||||
color="warning" |
|
||||
fullWidth |
|
||||
placeholder="Crédit pour la matiere" |
|
||||
type="number" |
|
||||
value={formData.credit} |
|
||||
onChange={handleChange} |
|
||||
required |
|
||||
InputProps={{ |
|
||||
startAdornment: ( |
|
||||
<InputAdornment position="start"> |
|
||||
<MdOutlineNumbers /> |
|
||||
</InputAdornment> |
|
||||
) |
|
||||
}} |
|
||||
inputProps={{ min: 1 }} |
|
||||
// inputRef={creditRef} |
|
||||
sx={{ |
|
||||
marginBottom: '5px', |
|
||||
'& .MuiOutlinedInput-root': { |
|
||||
'&:hover fieldset': { |
|
||||
borderColor: '#ff9800' // Set the border color on hover |
|
||||
} |
|
||||
} |
|
||||
}} |
|
||||
/> |
|
||||
</Grid> |
|
||||
{/* <Grid item xs={12} sm={6}> |
|
||||
<TextField |
|
||||
label="Semestre" |
|
||||
name="semestre" |
|
||||
color='warning' |
|
||||
fullWidth |
|
||||
placeholder='exemple S1, S2, S3, S4' |
|
||||
type='text' |
|
||||
required |
|
||||
value={formData.semestre} |
|
||||
onChange={handleChange} |
|
||||
InputProps={{ |
|
||||
startAdornment: ( |
|
||||
<InputAdornment position="start"> |
|
||||
<CiCalendar /> |
|
||||
</InputAdornment> |
|
||||
), |
|
||||
}} |
|
||||
inputProps={{ min: 1 }} |
|
||||
// inputRef={semestreRef} |
|
||||
sx={{ |
|
||||
marginBottom:"5px", |
|
||||
'& .MuiOutlinedInput-root': { |
|
||||
'&:hover fieldset': { |
|
||||
borderColor: '#ff9800', // Set the border color on hover |
|
||||
}, |
|
||||
}, |
|
||||
}} |
|
||||
/> |
|
||||
</Grid> */} |
|
||||
<Grid item xs={12} sm={6}> |
|
||||
<TextField |
|
||||
label="Unité d'enseignement" |
|
||||
name="uniter" |
|
||||
color="warning" |
|
||||
fullWidth |
|
||||
placeholder="Le matiere sera dans quelle unité d'enseignement" |
|
||||
value={formData.uniter} |
|
||||
onChange={handleChange} |
|
||||
onInput={() => ChangeCapital(uniterRef)} |
|
||||
required |
|
||||
inputRef={uniterRef} |
|
||||
InputProps={{ |
|
||||
startAdornment: ( |
|
||||
<InputAdornment position="start"> |
|
||||
<IoBookmark /> |
|
||||
</InputAdornment> |
|
||||
) |
|
||||
}} |
|
||||
inputProps={{ min: 1 }} |
|
||||
// inputRef={uniterRef} |
|
||||
sx={{ |
|
||||
marginBottom: '5px', |
|
||||
'& .MuiOutlinedInput-root': { |
|
||||
'&:hover fieldset': { |
|
||||
borderColor: '#ff9800' // Set the border color on hover |
|
||||
} |
|
||||
} |
|
||||
}} |
|
||||
/> |
|
||||
</Grid> |
|
||||
<Grid item xs={12} sm={6}> |
|
||||
<TextField |
|
||||
label="UE" |
|
||||
name="ue" |
|
||||
color="warning" |
|
||||
fullWidth |
|
||||
placeholder="UE" |
|
||||
value={formData.ue} |
|
||||
onChange={handleChange} |
|
||||
onInput={() => ChangeCapital(ueRef)} |
|
||||
inputRef={ueRef} |
|
||||
type="text" |
|
||||
required |
|
||||
InputProps={{ |
|
||||
startAdornment: ( |
|
||||
<InputAdornment position="start"> |
|
||||
<FaClock /> |
|
||||
</InputAdornment> |
|
||||
) |
|
||||
}} |
|
||||
inputProps={{ min: 1 }} |
|
||||
// inputRef={uniterRef} |
|
||||
sx={{ |
|
||||
marginBottom: '5px', |
|
||||
'& .MuiOutlinedInput-root': { |
|
||||
'&:hover fieldset': { |
|
||||
borderColor: '#ff9800' // Set the border color on hover |
|
||||
} |
|
||||
} |
|
||||
}} |
|
||||
/> |
|
||||
</Grid> |
|
||||
{/* <Grid item xs={12} sm={6}> |
|
||||
<Autocomplete |
|
||||
multiple |
|
||||
options={mentions} |
|
||||
getOptionLabel={(option) => option.nom || ''} |
|
||||
value={mentions.filter((mention) => |
|
||||
formData.mention_id.includes(mention.id) |
|
||||
)} |
|
||||
onChange={(event, newValue) => { |
|
||||
setFormData((prevData) => ({ |
|
||||
...prevData, |
|
||||
mention_id: newValue.map((item) => item.id), |
|
||||
})); |
|
||||
}} |
|
||||
isOptionEqualToValue={(option, value) => |
|
||||
option.id === value.id |
|
||||
} |
|
||||
renderInput={(params) => ( |
|
||||
<TextField |
|
||||
{...params} |
|
||||
label="Mentions" |
|
||||
color="warning" |
|
||||
fullWidth |
|
||||
InputProps={{ |
|
||||
...params.InputProps, |
|
||||
startAdornment: ( |
|
||||
<> |
|
||||
<InputAdornment position="start"> |
|
||||
<FaClipboardList /> |
|
||||
</InputAdornment> |
|
||||
{params.InputProps.startAdornment} |
|
||||
</> |
|
||||
), |
|
||||
}} |
|
||||
sx={{ |
|
||||
marginBottom: "5px", |
|
||||
'& .MuiOutlinedInput-root': { |
|
||||
'&:hover fieldset': { |
|
||||
borderColor: '#ff9800', // Set border color on hover |
|
||||
}, |
|
||||
}, |
|
||||
}} |
|
||||
/> |
|
||||
)} |
|
||||
/> |
|
||||
</Grid> */} |
|
||||
</Grid> |
|
||||
<Box sx={{ textAlign: 'center', marginTop: 2 }}> |
|
||||
<Button type="submit" color="warning" variant="contained"> |
|
||||
Mettre à jour |
|
||||
</Button> |
|
||||
</Box> |
|
||||
</form> |
|
||||
</Box> |
|
||||
</div> |
|
||||
</div> |
|
||||
) |
|
||||
} |
|
||||
|
|
||||
export default SingleMatiere |
|
||||
@ -1,223 +0,0 @@ |
|||||
import React, { useEffect, useRef, useState } from 'react' |
|
||||
import classe from '../assets/AllStyleComponents.module.css' |
|
||||
import classeAdd from '../assets/AddStudent.module.css' |
|
||||
import classeHome from '../assets/Home.module.css' |
|
||||
import { FaPenToSquare } from 'react-icons/fa6' |
|
||||
import { IoMdReturnRight } from 'react-icons/io' |
|
||||
import { Link, useParams } from 'react-router-dom' |
|
||||
import { Box, Button, InputAdornment, Typography, Modal, TextField, Grid } from '@mui/material' |
|
||||
import validationSingleNiveau from './validation/SingleNiveau' |
|
||||
import svgSuccess from '../assets/success.svg' |
|
||||
|
|
||||
const SingleNiveau = () => { |
|
||||
const { id } = useParams() |
|
||||
|
|
||||
const [niveau, setNiveau] = useState([]) |
|
||||
const [allNiveau, setAllNiveau] = useState([]) |
|
||||
|
|
||||
useEffect(() => { |
|
||||
window.niveaus.getSingleNiveau({ id }).then((response) => { |
|
||||
setNiveau(response) |
|
||||
}) |
|
||||
|
|
||||
window.niveaus.getNiveau().then((response) => { |
|
||||
setAllNiveau(response) |
|
||||
}) |
|
||||
}, []) |
|
||||
|
|
||||
useEffect(() => { |
|
||||
if (niveau) { |
|
||||
setFormData({ |
|
||||
nom: niveau.nom || '', |
|
||||
id: niveau.id || id |
|
||||
}) |
|
||||
} |
|
||||
}, [niveau]) |
|
||||
|
|
||||
const [formData, setFormData] = useState({ |
|
||||
nom: '', |
|
||||
id: id |
|
||||
}) |
|
||||
|
|
||||
const handleChange = (e) => { |
|
||||
const { name, value } = e.target |
|
||||
setFormData({ |
|
||||
...formData, |
|
||||
[name]: value |
|
||||
}) |
|
||||
} |
|
||||
|
|
||||
const handleSubmit = async (e) => { |
|
||||
e.preventDefault() |
|
||||
let niveauNom = [] |
|
||||
allNiveau.map((niv) => { |
|
||||
niveauNom.push(niv.nom) |
|
||||
}) |
|
||||
// let validation = validationSingleNiveau(nomRef.current, errorRef.current, niveauNom) |
|
||||
|
|
||||
// if (validation) { |
|
||||
let response = await window.niveaus.updateSingleNiveau(formData) |
|
||||
console.log(response) |
|
||||
|
|
||||
if (response.success) { |
|
||||
setOpen(true) |
|
||||
} |
|
||||
|
|
||||
if (!response.success) { |
|
||||
errorRef.current.textContent = `${formData.nom} existe déjà` |
|
||||
} |
|
||||
// } |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* hook to open modal |
|
||||
*/ |
|
||||
const [open, setOpen] = useState(false) |
|
||||
|
|
||||
/** |
|
||||
* function to close modal |
|
||||
*/ |
|
||||
const handleClose = () => setOpen(false) |
|
||||
|
|
||||
/** |
|
||||
* function to return the view Modal |
|
||||
* |
|
||||
* @returns {JSX} |
|
||||
*/ |
|
||||
const modals = () => ( |
|
||||
<Modal |
|
||||
open={open} |
|
||||
onClose={handleClose} |
|
||||
aria-labelledby="modal-title" |
|
||||
aria-describedby="modal-description" |
|
||||
> |
|
||||
<Box |
|
||||
sx={{ |
|
||||
position: 'absolute', |
|
||||
top: '50%', |
|
||||
left: '50%', |
|
||||
transform: 'translate(-50%, -50%)', |
|
||||
width: 450, |
|
||||
bgcolor: 'background.paper', |
|
||||
boxShadow: 24, |
|
||||
p: 4 |
|
||||
}} |
|
||||
> |
|
||||
<Typography |
|
||||
style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: '10px' }} |
|
||||
> |
|
||||
<img src={svgSuccess} alt="" width={70} height={70} />{' '} |
|
||||
<span>Modification a été effectuée avec succès</span> |
|
||||
</Typography> |
|
||||
<Box |
|
||||
sx={{ |
|
||||
marginTop: '2%', |
|
||||
display: 'flex', |
|
||||
gap: '20px', |
|
||||
alignItems: 'end', |
|
||||
justifyContent: 'flex-end' |
|
||||
}} |
|
||||
> |
|
||||
<Button onClick={handleClose} color="warning" variant="contained"> |
|
||||
OK |
|
||||
</Button> |
|
||||
</Box> |
|
||||
</Box> |
|
||||
</Modal> |
|
||||
) |
|
||||
|
|
||||
const nomRef = useRef() |
|
||||
const errorRef = useRef() |
|
||||
|
|
||||
return ( |
|
||||
<div className={classe.mainHome}> |
|
||||
{modals()} |
|
||||
<div className={classeAdd.header}> |
|
||||
<div className={classe.h1style}> |
|
||||
<div className={classeHome.blockTitle}> |
|
||||
<h1 style={{ display: 'flex', alignItems: 'center', gap: '10px' }}> |
|
||||
<FaPenToSquare /> |
|
||||
Mise à jour niveau |
|
||||
</h1> |
|
||||
<Link to={'/niveau'}> |
|
||||
<Button color="warning" variant="contained"> |
|
||||
<IoMdReturnRight style={{ fontSize: '20px' }} /> |
|
||||
</Button> |
|
||||
</Link> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
|
|
||||
<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' }}>modification niveau</h4> |
|
||||
<Grid container spacing={2}> |
|
||||
{/* Nom Fields */} |
|
||||
<Grid item xs={12} sm={12}> |
|
||||
<TextField |
|
||||
label="Nom" |
|
||||
name="nom" |
|
||||
color="warning" |
|
||||
fullWidth |
|
||||
value={formData.nom} |
|
||||
onChange={handleChange} |
|
||||
InputProps={{ |
|
||||
startAdornment: ( |
|
||||
<InputAdornment position="start">{/* <FaUser /> */}</InputAdornment> |
|
||||
) |
|
||||
}} |
|
||||
inputRef={nomRef} |
|
||||
sx={{ |
|
||||
'& .MuiOutlinedInput-root': { |
|
||||
'&:hover fieldset': { |
|
||||
borderColor: '#ff9800' // Set the border color on hover |
|
||||
} |
|
||||
} |
|
||||
}} |
|
||||
/> |
|
||||
<span className="text-danger" ref={errorRef}></span> |
|
||||
</Grid> |
|
||||
|
|
||||
{/* Submit Button */} |
|
||||
<Grid |
|
||||
item |
|
||||
xs={12} |
|
||||
style={{ display: 'flex', gap: '30px', justifyContent: 'flex-end' }} |
|
||||
> |
|
||||
<Button type="submit" color="warning" variant="contained"> |
|
||||
Enregister |
|
||||
</Button> |
|
||||
</Grid> |
|
||||
</Grid> |
|
||||
</form> |
|
||||
</Box> |
|
||||
</Box> |
|
||||
</div> |
|
||||
</div> |
|
||||
) |
|
||||
} |
|
||||
|
|
||||
export default SingleNiveau |
|
||||
@ -1,364 +0,0 @@ |
|||||
import React, { useEffect, useRef, useState } from 'react' |
|
||||
import { useParams, Link } from 'react-router-dom' |
|
||||
import Paper from '@mui/material/Paper' |
|
||||
import classe from '../assets/AllStyleComponents.module.css' |
|
||||
import classeHome from '../assets/Home.module.css' |
|
||||
import { IoMdReturnRight } from 'react-icons/io' |
|
||||
import { Button } from '@mui/material' |
|
||||
import { Box, InputAdornment, Typography, Modal, TextField, Grid } from '@mui/material' |
|
||||
import { CgNotes } from 'react-icons/cg' |
|
||||
import svgSuccess from '../assets/success.svg' |
|
||||
import svgError from '../assets/error.svg' |
|
||||
|
|
||||
const SingleNotes = () => { |
|
||||
let { id, niveau, scolaire } = useParams() |
|
||||
const [notes, setNotes] = useState([]) |
|
||||
const [notesRepech, setNotesRepech] = useState([]) |
|
||||
const [formData, setFormData] = useState({}) |
|
||||
const [formData2, setFormData2] = useState({}) |
|
||||
const [etudiant, setEtudiant] = useState([]) |
|
||||
let annee_scolaire = scolaire |
|
||||
const [screenRattrapage, setScreenRattrapage] = useState(false) |
|
||||
|
|
||||
useEffect(() => { |
|
||||
window.etudiants.getSingle({ id }).then((response) => { |
|
||||
setEtudiant(response) |
|
||||
}) |
|
||||
}, []) |
|
||||
|
|
||||
useEffect(() => { |
|
||||
let mention_id = etudiant.mention_id |
|
||||
window.notes.getNotes({ id, niveau, mention_id }).then((response) => { |
|
||||
setNotes(response) |
|
||||
}) |
|
||||
|
|
||||
window.noteRepech.getNotesRepech({ id, niveau, mention_id }).then((response) => { |
|
||||
setNotesRepech(response) |
|
||||
}) |
|
||||
}, [etudiant]) |
|
||||
|
|
||||
console.log(notes) |
|
||||
/** |
|
||||
* Update formData whenever matieres change |
|
||||
*/ |
|
||||
useEffect(() => { |
|
||||
const initialFormData = notes.reduce((acc, mat) => { |
|
||||
acc[mat.id] = mat.note // Initialize each key with an empty string |
|
||||
return acc |
|
||||
}, {}) |
|
||||
setFormData(initialFormData) |
|
||||
}, [notes]) // Dependency array ensures this runs whenever `matieres` is updated |
|
||||
|
|
||||
/** |
|
||||
* Update formData2 whenever matieres change |
|
||||
*/ |
|
||||
useEffect(() => { |
|
||||
const initialFormData = notesRepech.reduce((acc, mat) => { |
|
||||
acc[mat.id] = mat.note // Initialize each key with an empty string |
|
||||
return acc |
|
||||
}, {}) |
|
||||
setFormData2(initialFormData) |
|
||||
}, [notesRepech]) // Dependency array ensures this runs whenever `matieres` is updated |
|
||||
|
|
||||
const submitForm = async (e) => { |
|
||||
e.preventDefault() |
|
||||
let mention_id = etudiant.mention_id |
|
||||
console.log('normal submited') |
|
||||
let annee_scolaire = etudiant.annee_scolaire |
|
||||
let response = await window.notes.updateNote({ |
|
||||
formData, |
|
||||
niveau, |
|
||||
id, |
|
||||
mention_id, |
|
||||
annee_scolaire |
|
||||
}) |
|
||||
|
|
||||
if (response.changes) { |
|
||||
setMessage('Modification des notes terminer avec succès') |
|
||||
setStatus(200) |
|
||||
setOpen(true) |
|
||||
window.noteRepech.getNotesRepech({ id, niveau, mention_id }).then((response) => { |
|
||||
setNotesRepech(response) |
|
||||
}) |
|
||||
|
|
||||
window.notes.getNotes({ id, niveau, mention_id }).then((response) => { |
|
||||
setNotes(response) |
|
||||
}) |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
const submitForm2 = async (e) => { |
|
||||
e.preventDefault() |
|
||||
let mention_id = etudiant.mention_id |
|
||||
console.log('rattrapage submited') |
|
||||
let response = await window.noteRepech.updateNoteRepech({ formData2, niveau, id }) |
|
||||
|
|
||||
console.log(response) |
|
||||
if (response.changes) { |
|
||||
setMessage('Modification des notes terminer avec succès') |
|
||||
setStatus(200) |
|
||||
setOpen(true) |
|
||||
window.noteRepech.getNotesRepech({ id, niveau, mention_id }).then((response) => { |
|
||||
setNotesRepech(response) |
|
||||
}) |
|
||||
|
|
||||
window.notes.getNotes({ id, niveau, mention_id }).then((response) => { |
|
||||
setNotes(response) |
|
||||
}) |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
const [status, setStatus] = useState(200) |
|
||||
const [message, setMessage] = useState('') |
|
||||
|
|
||||
/** |
|
||||
* 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 |
|
||||
}} |
|
||||
> |
|
||||
{status === 200 ? ( |
|
||||
<Typography |
|
||||
style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: '10px' }} |
|
||||
> |
|
||||
<img src={svgSuccess} alt="" width={50} height={50} /> <span>{message}</span> |
|
||||
</Typography> |
|
||||
) : ( |
|
||||
<Typography |
|
||||
style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: '10px' }} |
|
||||
> |
|
||||
<img src={svgError} alt="" width={50} height={50} /> <span>{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 nom = useRef() |
|
||||
|
|
||||
const changeScreen = () => { |
|
||||
setScreenRattrapage(!screenRattrapage) |
|
||||
} |
|
||||
|
|
||||
return ( |
|
||||
<div className={classe.mainHome}> |
|
||||
{modals()} |
|
||||
<div className={classeHome.header}> |
|
||||
<div className={classe.h1style}> |
|
||||
<div className={classeHome.blockTitle}> |
|
||||
<h1>Mise a jour des notes</h1> |
|
||||
<div style={{ display: 'flex', gap: '20px' }}> |
|
||||
<Link onClick={() => window.history.back()}> |
|
||||
<Button color="warning" variant="contained"> |
|
||||
<IoMdReturnRight style={{ fontSize: '20px' }} /> |
|
||||
</Button> |
|
||||
</Link> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
|
|
||||
{/* displaying the form */} |
|
||||
<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, |
|
||||
overflowY: 'auto', |
|
||||
p: 4 |
|
||||
}} |
|
||||
> |
|
||||
<Box |
|
||||
sx={{ |
|
||||
marginTop: '2%', |
|
||||
display: 'flex', |
|
||||
width: '100%', |
|
||||
height: '70vh', |
|
||||
gap: '20px', |
|
||||
alignItems: 'start', |
|
||||
justifyContent: 'center' |
|
||||
}} |
|
||||
> |
|
||||
{!screenRattrapage ? ( |
|
||||
<form action="" onSubmit={submitForm}> |
|
||||
<h4 style={{ textAlign: 'center' }}>Mise a jour des notes</h4> |
|
||||
{/* {/* map the all matiere and note to the form */} |
|
||||
<Grid container spacing={2}> |
|
||||
{notes.map((note) => ( |
|
||||
<Grid item xs={12} sm={3} key={note.nom}> |
|
||||
<TextField |
|
||||
label={note.nom} |
|
||||
name={note.matiere_id} |
|
||||
color="warning" |
|
||||
fullWidth |
|
||||
placeholder="point séparateur" |
|
||||
className="inputToValidateExport" |
|
||||
value={formData[note.matiere_id] || ''} // Access the correct value from formData |
|
||||
onChange={ |
|
||||
(e) => setFormData({ ...formData, [note.id]: e.target.value }) // Update the specific key |
|
||||
} |
|
||||
InputProps={{ |
|
||||
startAdornment: ( |
|
||||
<InputAdornment position="start"> |
|
||||
<CgNotes /> |
|
||||
</InputAdornment> |
|
||||
) |
|
||||
}} |
|
||||
inputRef={nom} |
|
||||
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: '2%' |
|
||||
}} |
|
||||
> |
|
||||
<Button type="button" color="warning" variant="contained" onClick={changeScreen}> |
|
||||
Voir les notes de rattrapage |
|
||||
</Button> |
|
||||
<Button type="submit" color="warning" variant="contained"> |
|
||||
Enregister |
|
||||
</Button> |
|
||||
</Grid> |
|
||||
</form> |
|
||||
) : ( |
|
||||
<form action="" onSubmit={submitForm2}> |
|
||||
<h4 style={{ textAlign: 'center' }}>Mise a jour des notes de Rattrapage</h4> |
|
||||
{/* {/* map the all matiere and note to the form */} |
|
||||
<Grid container spacing={2}> |
|
||||
{notesRepech.length === 0 ? ( |
|
||||
// Show this message if notesRepech is empty |
|
||||
<Grid item xs={12}> |
|
||||
<h4 style={{ textAlign: 'center', color: 'green' }}> |
|
||||
L'étudiant a validé tous les crédits. |
|
||||
</h4> |
|
||||
</Grid> |
|
||||
) : ( |
|
||||
// Render form fields if notesRepech contains data |
|
||||
notesRepech.map((note) => ( |
|
||||
<Grid item xs={12} sm={4} key={note.nom}> |
|
||||
<TextField |
|
||||
label={note.nom} |
|
||||
name={note.matiere_id} |
|
||||
color="warning" |
|
||||
fullWidth |
|
||||
placeholder="point séparateur" |
|
||||
className="inputToValidateExport" |
|
||||
value={formData2[note.matiere_id] || ''} // Access the correct value from formData2 |
|
||||
onChange={ |
|
||||
(e) => setFormData2({ ...formData2, [note.id]: e.target.value }) // Update the specific key |
|
||||
} |
|
||||
InputProps={{ |
|
||||
startAdornment: ( |
|
||||
<InputAdornment position="start"> |
|
||||
<CgNotes /> |
|
||||
</InputAdornment> |
|
||||
) |
|
||||
}} |
|
||||
inputRef={nom} |
|
||||
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: '2%' |
|
||||
}} |
|
||||
> |
|
||||
<Button type="button" color="warning" variant="contained" onClick={changeScreen}> |
|
||||
Voir les notes session normale |
|
||||
</Button> |
|
||||
<Button type="submit" color="warning" variant="contained"> |
|
||||
Enregister |
|
||||
</Button> |
|
||||
</Grid> |
|
||||
</form> |
|
||||
)} |
|
||||
</Box> |
|
||||
</Box> |
|
||||
</div> |
|
||||
</div> |
|
||||
) |
|
||||
} |
|
||||
|
|
||||
export default SingleNotes |
|
||||
@ -1,272 +0,0 @@ |
|||||
import React, { useEffect, useRef, useState } from 'react' |
|
||||
import classe from '../assets/AllStyleComponents.module.css' |
|
||||
import classeHome from '../assets/Home.module.css' |
|
||||
import { Link, useParams } from 'react-router-dom' |
|
||||
import { IoMdReturnRight } from 'react-icons/io' |
|
||||
import { Box, InputAdornment, Typography, Modal, TextField, Grid, Button } from '@mui/material' |
|
||||
import ChangeCapital from './function/ChangeCapitalLetter' |
|
||||
import { FaClipboardList } from 'react-icons/fa6' |
|
||||
import { IoBookmark } from 'react-icons/io5' |
|
||||
import svgSuccess from '../assets/success.svg' |
|
||||
|
|
||||
const SinleMention = () => { |
|
||||
const { id } = useParams() |
|
||||
const [mentions, setMention] = useState([]) |
|
||||
const [formData, setFormData] = useState({ |
|
||||
nom: '', |
|
||||
uniter: '', |
|
||||
id: '' |
|
||||
}) |
|
||||
|
|
||||
const [errors, setErrors] = useState({ |
|
||||
nom: false, |
|
||||
uniter: false |
|
||||
}) |
|
||||
|
|
||||
useEffect(() => { |
|
||||
window.mention.getSingleMention({ id }).then((response) => { |
|
||||
setMention(response) |
|
||||
}) |
|
||||
}, []) |
|
||||
|
|
||||
useEffect(() => { |
|
||||
if (mentions) { |
|
||||
setFormData({ |
|
||||
nom: mentions.nom || '', |
|
||||
uniter: mentions.uniter || '', |
|
||||
id: mentions.id || id |
|
||||
}) |
|
||||
} |
|
||||
}, [mentions]) |
|
||||
|
|
||||
const nomRef = useRef() |
|
||||
const uniterRef = useRef() |
|
||||
|
|
||||
/** |
|
||||
* function to set the data in state |
|
||||
* @param {*} e |
|
||||
*/ |
|
||||
const handleInputChange = (e) => { |
|
||||
const { name, value } = e.target |
|
||||
setFormData({ |
|
||||
...formData, |
|
||||
[name]: value |
|
||||
}) |
|
||||
setErrors({ |
|
||||
...errors, |
|
||||
[name]: false // Reset the error when user starts typing |
|
||||
}) |
|
||||
} |
|
||||
|
|
||||
// Helper function to get helperText dynamically |
|
||||
const getHelperText = (field) => (errors[field] ? 'Ce champ est requis.' : '') |
|
||||
|
|
||||
const formSubmit = async (e) => { |
|
||||
e.preventDefault() |
|
||||
const newErrors = {} |
|
||||
let hasError = false |
|
||||
|
|
||||
// Check for empty fields |
|
||||
Object.keys(formData).forEach((key) => { |
|
||||
const value = formData[key] |
|
||||
if (typeof value === 'string' && !value.trim()) { |
|
||||
newErrors[key] = true // Set error for empty fields |
|
||||
hasError = true |
|
||||
} |
|
||||
}) |
|
||||
|
|
||||
setErrors(newErrors) |
|
||||
|
|
||||
if (!hasError) { |
|
||||
try { |
|
||||
let response = await window.mention.updateMention(formData) |
|
||||
|
|
||||
if (response.success) { |
|
||||
setOpen(true) |
|
||||
} |
|
||||
} catch (error) { |
|
||||
console.log(error) |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* hook to open modal |
|
||||
*/ |
|
||||
const [open, setOpen] = useState(false) |
|
||||
|
|
||||
/** |
|
||||
* function to close modal |
|
||||
*/ |
|
||||
const handleClose = () => { |
|
||||
setOpen(false) |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* function to return the view Modal |
|
||||
* |
|
||||
* @returns {JSX} |
|
||||
*/ |
|
||||
const modals = () => ( |
|
||||
<Modal |
|
||||
open={open} |
|
||||
onClose={handleClose} |
|
||||
aria-labelledby="modal-title" |
|
||||
aria-describedby="modal-description" |
|
||||
> |
|
||||
<Box |
|
||||
sx={{ |
|
||||
position: 'absolute', |
|
||||
top: '50%', |
|
||||
left: '50%', |
|
||||
transform: 'translate(-50%, -50%)', |
|
||||
width: 450, |
|
||||
bgcolor: 'background.paper', |
|
||||
boxShadow: 24, |
|
||||
p: 4 |
|
||||
}} |
|
||||
> |
|
||||
<Typography style={{ display: 'flex', flexDirection: 'column', alignItems: 'center' }}> |
|
||||
<img src={svgSuccess} alt="" width={50} height={50} />{' '} |
|
||||
<span>Mention modifier avec succes</span> |
|
||||
</Typography> |
|
||||
<Box |
|
||||
sx={{ |
|
||||
marginTop: '2%', |
|
||||
display: 'flex', |
|
||||
gap: '20px', |
|
||||
alignItems: 'end', |
|
||||
justifyContent: 'flex-end' |
|
||||
}} |
|
||||
> |
|
||||
<Button onClick={handleClose} color="warning" variant="contained"> |
|
||||
OK |
|
||||
</Button> |
|
||||
</Box> |
|
||||
</Box> |
|
||||
</Modal> |
|
||||
) |
|
||||
|
|
||||
return ( |
|
||||
<div className={classe.mainHome}> |
|
||||
{modals()} |
|
||||
<div className={classeHome.header}> |
|
||||
<div className={classe.h1style}> |
|
||||
<div className={classeHome.blockTitle}> |
|
||||
<h1>Mise a jour mention</h1> |
|
||||
<div style={{ display: 'flex', gap: '20px' }}> |
|
||||
<Link to={'/mention'}> |
|
||||
<Button color="warning" variant="contained"> |
|
||||
<IoMdReturnRight style={{ fontSize: '20px' }} /> |
|
||||
</Button> |
|
||||
</Link> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
|
|
||||
<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 action="" style={{ width: '100%' }} onSubmit={formSubmit}> |
|
||||
<Grid container spacing={2}> |
|
||||
<Grid item xs={12} sm={6}> |
|
||||
<TextField |
|
||||
label="Nom" |
|
||||
error={errors.nom} |
|
||||
name="nom" |
|
||||
color="warning" |
|
||||
fullWidth |
|
||||
placeholder="GENIE DES MINES" |
|
||||
value={formData.nom} |
|
||||
onChange={handleInputChange} |
|
||||
onInput={() => ChangeCapital(nomRef)} |
|
||||
InputProps={{ |
|
||||
startAdornment: ( |
|
||||
<InputAdornment position="start"> |
|
||||
<FaClipboardList /> |
|
||||
</InputAdornment> |
|
||||
) |
|
||||
}} |
|
||||
helperText={getHelperText('nom')} |
|
||||
inputRef={nomRef} |
|
||||
sx={{ |
|
||||
marginBottom: '5px', |
|
||||
'& .MuiOutlinedInput-root': { |
|
||||
'&:hover fieldset': { |
|
||||
borderColor: '#ff9800' // Set the border color on hover |
|
||||
} |
|
||||
} |
|
||||
}} |
|
||||
/> |
|
||||
</Grid> |
|
||||
<Grid item xs={12} sm={6}> |
|
||||
<TextField |
|
||||
label="Unité" |
|
||||
error={errors.uniter} |
|
||||
helperText={getHelperText('uniter')} |
|
||||
name="uniter" |
|
||||
color="warning" |
|
||||
fullWidth |
|
||||
placeholder="GM" |
|
||||
value={formData.uniter} |
|
||||
onChange={handleInputChange} |
|
||||
onInput={() => ChangeCapital(uniterRef)} |
|
||||
InputProps={{ |
|
||||
startAdornment: ( |
|
||||
<InputAdornment position="start"> |
|
||||
<IoBookmark /> |
|
||||
</InputAdornment> |
|
||||
) |
|
||||
}} |
|
||||
inputRef={uniterRef} |
|
||||
sx={{ |
|
||||
marginBottom: '5px', |
|
||||
'& .MuiOutlinedInput-root': { |
|
||||
'&:hover fieldset': { |
|
||||
borderColor: '#ff9800' // Set the border color on hover |
|
||||
} |
|
||||
} |
|
||||
}} |
|
||||
/> |
|
||||
</Grid> |
|
||||
<Grid |
|
||||
item |
|
||||
xs={12} |
|
||||
style={{ display: 'flex', gap: '30px', justifyContent: 'flex-end' }} |
|
||||
> |
|
||||
<Button type="submit" color="warning" variant="contained"> |
|
||||
Enregister |
|
||||
</Button> |
|
||||
</Grid> |
|
||||
</Grid> |
|
||||
</form> |
|
||||
</Box> |
|
||||
</Box> |
|
||||
</div> |
|
||||
</div> |
|
||||
) |
|
||||
} |
|
||||
|
|
||||
export default SinleMention |
|
||||
@ -1,365 +0,0 @@ |
|||||
import React, { useEffect, useRef, useState } from 'react' |
|
||||
import classe from '../assets/AllStyleComponents.module.css' |
|
||||
import classeAdd from '../assets/AddStudent.module.css' |
|
||||
import classeHome from '../assets/Home.module.css' |
|
||||
import { IoMdPersonAdd, IoMdReturnRight } from 'react-icons/io' |
|
||||
import { Link } from 'react-router-dom' |
|
||||
import { Box, Button, InputAdornment, Typography, Modal, TextField, Grid } from '@mui/material' |
|
||||
import { CgNotes } from 'react-icons/cg' |
|
||||
import { FaAngleDoubleUp, FaAngleDoubleDown, FaCog } from 'react-icons/fa' |
|
||||
import validateNOteSystem from './validation/NoteSystem' |
|
||||
import svgError from '../assets/error.svg' |
|
||||
import svgSuccess from '../assets/success.svg' |
|
||||
import { Tooltip } from 'react-tooltip' |
|
||||
import ModalFormMultiplicateur from './ModalFormMultiplicateur' |
|
||||
|
|
||||
const SystemeNote = () => { |
|
||||
const [formData, setFormData] = useState({ |
|
||||
id: '', |
|
||||
admis: '', |
|
||||
redouble: '', |
|
||||
renvoyer: '' |
|
||||
}) |
|
||||
|
|
||||
const [noteSy, setNoteSy] = useState([]) |
|
||||
const [status, setStatus] = useState(200) |
|
||||
|
|
||||
/** |
|
||||
* function to set the data in state |
|
||||
* @param {*} e |
|
||||
*/ |
|
||||
const handleInputChange = (e) => { |
|
||||
const { name, value } = e.target |
|
||||
setFormData((prevData) => ({ |
|
||||
...prevData, |
|
||||
[name]: value |
|
||||
})) |
|
||||
} |
|
||||
|
|
||||
useEffect(() => { |
|
||||
window.notesysteme.getSyteme().then((response) => { |
|
||||
setNoteSy(response) |
|
||||
}) |
|
||||
}, []) |
|
||||
|
|
||||
useEffect(() => { |
|
||||
if (noteSy) { |
|
||||
setFormData({ |
|
||||
id: noteSy.id, |
|
||||
admis: noteSy.admis || 0, |
|
||||
redouble: noteSy.redouble || 0, |
|
||||
renvoyer: noteSy.renvoyer || 0 |
|
||||
}) |
|
||||
} |
|
||||
}, [noteSy]) |
|
||||
|
|
||||
console.log(noteSy) |
|
||||
|
|
||||
const formSubmit = async (e) => { |
|
||||
e.preventDefault() |
|
||||
let valid = validateNOteSystem(admisRef.current, redoubleRef.current, renvoyerRef.current) |
|
||||
|
|
||||
if (valid) { |
|
||||
let response = await window.notesysteme.updateNoteSysteme(formData) |
|
||||
console.log(response) |
|
||||
if (response.success) { |
|
||||
setStatus(200) |
|
||||
setOpen(true) |
|
||||
} |
|
||||
} else { |
|
||||
setStatus(400) |
|
||||
setOpen(true) |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
const admisRef = useRef() |
|
||||
const redoubleRef = useRef() |
|
||||
const renvoyerRef = useRef() |
|
||||
|
|
||||
/** |
|
||||
* 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 |
|
||||
}} |
|
||||
> |
|
||||
{status === 200 ? ( |
|
||||
<Typography |
|
||||
style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: '10px' }} |
|
||||
> |
|
||||
<img src={svgSuccess} alt="" width={70} height={70} />{' '} |
|
||||
<span>Modification des notes a été effectuée avec succès</span> |
|
||||
</Typography> |
|
||||
) : ( |
|
||||
<Typography |
|
||||
style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: '10px' }} |
|
||||
> |
|
||||
<img src={svgError} alt="" width={70} height={70} />{' '} |
|
||||
<span>Vérifiez les champs vides ou les séparateurs (doivent être un point)</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 [openForm, setOpenForm] = useState(false) |
|
||||
|
|
||||
const closeForm = () => { |
|
||||
setOpenForm(false) |
|
||||
} |
|
||||
|
|
||||
const openThisForm = () => { |
|
||||
setOpenForm(true) |
|
||||
} |
|
||||
|
|
||||
return ( |
|
||||
<div className={classe.mainHome}> |
|
||||
<style> |
|
||||
{` |
|
||||
.custom-tooltips { |
|
||||
font-size: 15px; |
|
||||
border: solid 1px white !important; |
|
||||
border-radius: 4px; |
|
||||
text-transform: capitalize; |
|
||||
font-weight: 600; |
|
||||
} |
|
||||
`} |
|
||||
</style> |
|
||||
{modals()} |
|
||||
<ModalFormMultiplicateur open={openForm} onClose={closeForm} /> |
|
||||
<div className={classeAdd.header}> |
|
||||
<div className={classe.h1style}> |
|
||||
<div className={classeHome.blockTitle}> |
|
||||
<h1 style={{ display: 'flex', alignItems: 'center', gap: '10px' }}> |
|
||||
<CgNotes /> |
|
||||
Système d'organisation des notes |
|
||||
</h1> |
|
||||
<Link to={'#'} className="infocog" onClick={openThisForm}> |
|
||||
<Button color="warning" variant="contained"> |
|
||||
<FaCog style={{ fontSize: '20px' }} /> |
|
||||
</Button> |
|
||||
</Link> |
|
||||
<Tooltip anchorSelect=".infocog" place="top" className="custom-tooltips"> |
|
||||
Changer le multiplicateur (credit * M) |
|
||||
</Tooltip> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
<div className={classeAdd.boxEtudiantsCard}> |
|
||||
<Box |
|
||||
sx={{ |
|
||||
position: 'absolute', |
|
||||
top: '55%', |
|
||||
left: '50%', |
|
||||
transform: 'translate(-50%, -50%)', |
|
||||
width: 1050, |
|
||||
borderRadius: '2%', |
|
||||
bgcolor: 'background.paper', |
|
||||
boxShadow: 24, |
|
||||
overflowY: 'auto', |
|
||||
p: 4 |
|
||||
}} |
|
||||
className="table-responsive" |
|
||||
> |
|
||||
<Box |
|
||||
sx={{ |
|
||||
marginTop: '2%', |
|
||||
display: 'flex', |
|
||||
width: '100%', |
|
||||
height: '40vh', |
|
||||
gap: '20px', |
|
||||
alignItems: 'start', |
|
||||
justifyContent: 'center' |
|
||||
}} |
|
||||
> |
|
||||
<form onSubmit={formSubmit}> |
|
||||
<table className={'table table-bordered table-responsive'}> |
|
||||
<thead className="bg-warning"> |
|
||||
<tr style={{ fontWeight: 'bold' }}> |
|
||||
<td style={{ color: 'gray' }}>Status :</td> |
|
||||
<td style={{ color: 'gray' }}>Admis</td> |
|
||||
<td style={{ color: 'gray' }}>Redouble</td> |
|
||||
<td style={{ color: 'gray' }}>Renvoyer</td> |
|
||||
</tr> |
|
||||
</thead> |
|
||||
<tbody> |
|
||||
<tr> |
|
||||
<td style={{ color: 'gray', fontWeight: 'bold' }}>Notes :</td> |
|
||||
<td> |
|
||||
{/* <Grid container spacing={2}> */} |
|
||||
<Grid item xs={12} sm={3}> |
|
||||
<TextField |
|
||||
label={'Notes pour etre admis'} |
|
||||
name={'admis'} |
|
||||
color="warning" |
|
||||
fullWidth |
|
||||
placeholder="point séparateur" |
|
||||
className="inputToValidateExport" |
|
||||
value={formData.admis} // Access the correct value from formData |
|
||||
onChange={handleInputChange} |
|
||||
InputProps={{ |
|
||||
startAdornment: ( |
|
||||
<InputAdornment position="start"> |
|
||||
<CgNotes /> |
|
||||
</InputAdornment> |
|
||||
), |
|
||||
endAdornment: ( |
|
||||
<InputAdornment position="start"> |
|
||||
<FaAngleDoubleUp className="text-success" /> |
|
||||
</InputAdornment> |
|
||||
) |
|
||||
}} |
|
||||
inputRef={admisRef} |
|
||||
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> */} |
|
||||
</td> |
|
||||
<td> |
|
||||
{/* <Grid container spacing={2}> */} |
|
||||
<Grid item xs={12} sm={3}> |
|
||||
<TextField |
|
||||
label={'Notes pour redoubler'} |
|
||||
name={'redouble'} |
|
||||
color="warning" |
|
||||
fullWidth |
|
||||
placeholder="point séparateur" |
|
||||
className="inputToValidateExport" |
|
||||
value={formData.redouble} // Access the correct value from formData |
|
||||
onChange={handleInputChange} |
|
||||
InputProps={{ |
|
||||
startAdornment: ( |
|
||||
<InputAdornment position="start"> |
|
||||
<CgNotes /> |
|
||||
</InputAdornment> |
|
||||
), |
|
||||
endAdornment: ( |
|
||||
<InputAdornment position="start"> |
|
||||
<FaAngleDoubleDown className="text-danger" /> |
|
||||
</InputAdornment> |
|
||||
) |
|
||||
}} |
|
||||
inputRef={redoubleRef} |
|
||||
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> */} |
|
||||
</td> |
|
||||
<td> |
|
||||
{/* <Grid container spacing={2}> */} |
|
||||
<Grid item xs={12} sm={3}> |
|
||||
<TextField |
|
||||
label={'Notes pour etre renvoyer'} |
|
||||
name={'renvoyer'} |
|
||||
color="warning" |
|
||||
fullWidth |
|
||||
placeholder="point séparateur" |
|
||||
className="inputToValidateExport" |
|
||||
value={formData.renvoyer} // Access the correct value from formData |
|
||||
onChange={handleInputChange} |
|
||||
InputProps={{ |
|
||||
startAdornment: ( |
|
||||
<InputAdornment position="start"> |
|
||||
<CgNotes /> |
|
||||
</InputAdornment> |
|
||||
), |
|
||||
endAdornment: ( |
|
||||
<InputAdornment position="start"> |
|
||||
<FaAngleDoubleDown className="text-danger" /> |
|
||||
</InputAdornment> |
|
||||
) |
|
||||
}} |
|
||||
inputRef={renvoyerRef} |
|
||||
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> */} |
|
||||
</td> |
|
||||
</tr> |
|
||||
</tbody> |
|
||||
</table> |
|
||||
<Grid |
|
||||
item |
|
||||
xs={12} |
|
||||
style={{ display: 'flex', gap: '30px', justifyContent: 'flex-end' }} |
|
||||
> |
|
||||
<Button type="submit" color="warning" variant="contained"> |
|
||||
Enregister |
|
||||
</Button> |
|
||||
</Grid> |
|
||||
</form> |
|
||||
</Box> |
|
||||
</Box> |
|
||||
</div> |
|
||||
</div> |
|
||||
) |
|
||||
} |
|
||||
|
|
||||
export default SystemeNote |
|
||||
@ -1,194 +0,0 @@ |
|||||
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 AjoutTranche from './AjoutTranche' |
|
||||
import { Tooltip } from 'react-tooltip' |
|
||||
import { FaPenToSquare } from 'react-icons/fa6' |
|
||||
import { FaTrash } from 'react-icons/fa' |
|
||||
import UpdateTranche from './UpdateTranche' |
|
||||
import DeleteTranche from './DeleteTranche' |
|
||||
|
|
||||
const TrancheEcolage = () => { |
|
||||
const { id } = useParams() |
|
||||
const [tranche, setTranche] = useState([]) |
|
||||
const [etudiant, setEtudiant] = useState({}) |
|
||||
|
|
||||
useEffect(() => { |
|
||||
window.etudiants.getTranche({ id }).then((response) => { |
|
||||
setTranche(response) |
|
||||
}) |
|
||||
|
|
||||
window.etudiants.getSingle({ id }).then((response) => { |
|
||||
setEtudiant(response) |
|
||||
}) |
|
||||
}, []) |
|
||||
|
|
||||
const [openAdd, setOpenAdd] = useState(false) |
|
||||
const onCloseAdd = () => setOpenAdd(false) |
|
||||
|
|
||||
const openAddFunction = () => { |
|
||||
setOpenAdd(true) |
|
||||
} |
|
||||
|
|
||||
const [isSubmited, setIsSubmited] = useState(false) |
|
||||
const handleFormSubmit = (status) => { |
|
||||
setIsSubmited(status) |
|
||||
} |
|
||||
|
|
||||
const [openUpdate, setOpenUpdate] = useState(false) |
|
||||
const onCloseUpdate = () => setOpenUpdate(false) |
|
||||
const [idToSend, setIdToSend] = useState(null) |
|
||||
const [idToSend2, setIdToSend2] = useState(null) |
|
||||
|
|
||||
const openUpdateFunction = (id) => { |
|
||||
setOpenUpdate(true) |
|
||||
setIdToSend(id) |
|
||||
} |
|
||||
|
|
||||
const [openDelete, setOpenDelete] = useState(false) |
|
||||
const onCloseDelete = () => setOpenDelete(false) |
|
||||
const openDeleteFunction = (id) => { |
|
||||
setOpenDelete(true) |
|
||||
setIdToSend2(id) |
|
||||
} |
|
||||
|
|
||||
useEffect(() => { |
|
||||
if (isSubmited) { |
|
||||
window.etudiants.getTranche({ id }).then((response) => { |
|
||||
setTranche(response) |
|
||||
}) |
|
||||
setIsSubmited(false) |
|
||||
} |
|
||||
}, [isSubmited]) |
|
||||
|
|
||||
return ( |
|
||||
<div className={classe.mainHome}> |
|
||||
<AjoutTranche |
|
||||
id={id} |
|
||||
onClose={onCloseAdd} |
|
||||
onSubmitSuccess={handleFormSubmit} |
|
||||
open={openAdd} |
|
||||
/> |
|
||||
<UpdateTranche |
|
||||
onClose={onCloseUpdate} |
|
||||
onSubmitSuccess={handleFormSubmit} |
|
||||
open={openUpdate} |
|
||||
id={idToSend} |
|
||||
/> |
|
||||
<DeleteTranche |
|
||||
id={idToSend2} |
|
||||
onClose={onCloseDelete} |
|
||||
onSubmitSuccess={handleFormSubmit} |
|
||||
open={openDelete} |
|
||||
/> |
|
||||
<div className={classeHome.header}> |
|
||||
<div className={classe.h1style}> |
|
||||
<div className={classeHome.blockTitle}> |
|
||||
<h1>Tranche d'Ecolage</h1> |
|
||||
<div style={{ display: 'flex', gap: '10px' }}> |
|
||||
<Link to={'#'} onClick={openAddFunction}> |
|
||||
<Button color="warning" variant="contained"> |
|
||||
Ajouter |
|
||||
</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> |
|
||||
Evolution d'écolage de {etudiant.nom} {etudiant.prenom} |
|
||||
</h6> |
|
||||
</td> |
|
||||
</tr> |
|
||||
<tr> |
|
||||
<th>Tranche N°</th> |
|
||||
<th>Désignation</th> |
|
||||
<th>Montant</th> |
|
||||
<th>Action</th> |
|
||||
</tr> |
|
||||
</thead> |
|
||||
<tbody> |
|
||||
{tranche.map((tranch, index) => ( |
|
||||
<tr key={tranch.id}> |
|
||||
<td>{index + 1}</td> |
|
||||
<td>{tranch.tranchename}</td> |
|
||||
<td>{Number(tranch.montant).toLocaleString(0, 3)}</td> |
|
||||
<td |
|
||||
className="fw-bold" |
|
||||
style={{ |
|
||||
display: 'flex', |
|
||||
gap: '10px', |
|
||||
alignItems: 'center', |
|
||||
justifyContent: 'center' |
|
||||
}} |
|
||||
> |
|
||||
<Button |
|
||||
variant="contained" |
|
||||
color="warning" |
|
||||
onClick={() => openUpdateFunction(tranch.id)} |
|
||||
> |
|
||||
<FaPenToSquare |
|
||||
style={{ fontSize: '20px', color: 'white' }} |
|
||||
className={`update${tranch.id}`} |
|
||||
/> |
|
||||
<Tooltip |
|
||||
anchorSelect={`.update${tranch.id}`} |
|
||||
className="custom-tooltip" |
|
||||
place="top" |
|
||||
> |
|
||||
Modifier |
|
||||
</Tooltip> |
|
||||
</Button> |
|
||||
<Button |
|
||||
variant="contained" |
|
||||
color="error" |
|
||||
onClick={() => openDeleteFunction(tranch.id)} |
|
||||
> |
|
||||
<FaTrash |
|
||||
style={{ fontSize: '20px', color: 'white' }} |
|
||||
className={`delete${tranch.id}`} |
|
||||
/> |
|
||||
<Tooltip |
|
||||
anchorSelect={`.delete${tranch.id}`} |
|
||||
className="custom-tooltip" |
|
||||
place="top" |
|
||||
> |
|
||||
Supprimer |
|
||||
</Tooltip> |
|
||||
</Button> |
|
||||
</td> |
|
||||
</tr> |
|
||||
))} |
|
||||
</tbody> |
|
||||
</table> |
|
||||
</Paper> |
|
||||
</div> |
|
||||
</div> |
|
||||
) |
|
||||
} |
|
||||
|
|
||||
export default TrancheEcolage |
|
||||
@ -1,179 +0,0 @@ |
|||||
import React, { useEffect, useRef, useState } from 'react' |
|
||||
import { |
|
||||
Dialog, |
|
||||
DialogActions, |
|
||||
DialogContent, |
|
||||
DialogTitle, |
|
||||
TextField, |
|
||||
Button, |
|
||||
InputAdornment, |
|
||||
Box, |
|
||||
Grid |
|
||||
} from '@mui/material' |
|
||||
import ChangeCapital from './function/ChangeCapitalLetter' |
|
||||
import ChangeCapitalize from './function/ChangeCapitalizeLetter' |
|
||||
import { PiChalkboardTeacher } from 'react-icons/pi' |
|
||||
import { MdContactPhone, MdDateRange } from 'react-icons/md' |
|
||||
|
|
||||
const UpdateModalProf = ({ open, onClose, matiere_id, onSubmitSuccess }) => { |
|
||||
const [formData, setFormData] = useState({ |
|
||||
nom_enseignant: '', |
|
||||
prenom_enseignant: '', |
|
||||
contact: '', |
|
||||
date: '', |
|
||||
matiere_id: '' |
|
||||
}) |
|
||||
|
|
||||
const nomRefs = useRef() |
|
||||
const prenomRefs = useRef() |
|
||||
|
|
||||
const handleChange = (e) => { |
|
||||
const { name, value } = e.target |
|
||||
setFormData({ ...formData, [name]: value }) |
|
||||
} |
|
||||
|
|
||||
useEffect(() => { |
|
||||
let id = matiere_id |
|
||||
if (id !== '') { |
|
||||
window.matieres.getSingleProf({ id }).then((response) => { |
|
||||
setFormData((prev) => ({ |
|
||||
...prev, |
|
||||
nom_enseignant: response.nom_enseignant, |
|
||||
prenom_enseignant: response.prenom_enseignant, |
|
||||
contact: response.contact, |
|
||||
date: response.date, |
|
||||
matiere_id: matiere_id |
|
||||
})) |
|
||||
}) |
|
||||
} |
|
||||
}, [matiere_id]) |
|
||||
|
|
||||
const handleSubmit = async (e) => { |
|
||||
e.preventDefault() |
|
||||
let response = await window.matieres.updateProf(formData) |
|
||||
|
|
||||
if (response.success) { |
|
||||
onSubmitSuccess(true) |
|
||||
onClose() // Close the modal after submission |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
return ( |
|
||||
<Dialog open={open} onClose={onClose}> |
|
||||
<form action="" onSubmit={handleSubmit}> |
|
||||
<DialogTitle>mise à jour enseignant</DialogTitle> |
|
||||
<DialogContent> |
|
||||
<Box sx={{ flexGrow: 1 }}> |
|
||||
<Grid container spacing={2}> |
|
||||
<Grid item xs={12} sm={6}> |
|
||||
<TextField |
|
||||
autoFocus |
|
||||
margin="normal" |
|
||||
required |
|
||||
name="nom_enseignant" |
|
||||
label="Nom du professeur" |
|
||||
type="text" |
|
||||
fullWidth |
|
||||
placeholder="Nom du professeur" |
|
||||
variant="outlined" |
|
||||
value={formData.nom_enseignant} |
|
||||
color="warning" |
|
||||
inputRef={nomRefs} |
|
||||
onInput={() => ChangeCapital(nomRefs)} |
|
||||
onChange={handleChange} |
|
||||
InputProps={{ |
|
||||
startAdornment: ( |
|
||||
<InputAdornment position="start"> |
|
||||
<PiChalkboardTeacher /> |
|
||||
</InputAdornment> |
|
||||
) |
|
||||
}} |
|
||||
/> |
|
||||
</Grid> |
|
||||
<Grid item xs={12} sm={6}> |
|
||||
<TextField |
|
||||
autoFocus |
|
||||
margin="normal" |
|
||||
required |
|
||||
name="prenom_enseignant" |
|
||||
label="Prenom du professeur" |
|
||||
type="text" |
|
||||
fullWidth |
|
||||
placeholder="Prenom du professeur" |
|
||||
variant="outlined" |
|
||||
value={formData.prenom_enseignant} |
|
||||
color="warning" |
|
||||
inputRef={prenomRefs} |
|
||||
onInput={() => ChangeCapitalize(prenomRefs)} |
|
||||
onChange={handleChange} |
|
||||
InputProps={{ |
|
||||
startAdornment: ( |
|
||||
<InputAdornment position="start"> |
|
||||
<PiChalkboardTeacher /> |
|
||||
</InputAdornment> |
|
||||
) |
|
||||
}} |
|
||||
/> |
|
||||
</Grid> |
|
||||
<Grid item xs={12} sm={6}> |
|
||||
<TextField |
|
||||
autoFocus |
|
||||
margin="normal" |
|
||||
required |
|
||||
name="contact" |
|
||||
label="Contact" |
|
||||
type="number" |
|
||||
fullWidth |
|
||||
placeholder="Contact" |
|
||||
variant="outlined" |
|
||||
value={formData.contact} |
|
||||
color="warning" |
|
||||
onChange={handleChange} |
|
||||
InputProps={{ |
|
||||
startAdornment: ( |
|
||||
<InputAdornment position="start"> |
|
||||
<MdContactPhone /> |
|
||||
</InputAdornment> |
|
||||
) |
|
||||
}} |
|
||||
/> |
|
||||
</Grid> |
|
||||
<Grid item xs={12} sm={6}> |
|
||||
<TextField |
|
||||
autoFocus |
|
||||
margin="normal" |
|
||||
required |
|
||||
name="date" |
|
||||
label="Date de prise du poste" |
|
||||
type="date" |
|
||||
fullWidth |
|
||||
variant="outlined" |
|
||||
value={formData.date} |
|
||||
color="warning" |
|
||||
onChange={handleChange} |
|
||||
InputProps={{ |
|
||||
startAdornment: ( |
|
||||
<InputAdornment position="start"> |
|
||||
<MdDateRange /> |
|
||||
</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 UpdateModalProf |
|
||||
@ -1,178 +0,0 @@ |
|||||
import React, { useEffect, useRef, useState } from 'react' |
|
||||
import { |
|
||||
Dialog, |
|
||||
DialogActions, |
|
||||
DialogContent, |
|
||||
DialogTitle, |
|
||||
TextField, |
|
||||
Button, |
|
||||
Autocomplete, |
|
||||
InputAdornment, |
|
||||
Box, |
|
||||
Grid |
|
||||
} from '@mui/material' |
|
||||
import { MdRule } from 'react-icons/md' |
|
||||
import { FaClipboardList } from 'react-icons/fa' |
|
||||
|
|
||||
const AddParcours = ({ open, onClose, onSubmitSuccess, id }) => { |
|
||||
const [formData, setFormData] = useState({ |
|
||||
nom: '', |
|
||||
uniter: '', |
|
||||
mention_id: '', |
|
||||
id: '' |
|
||||
}) |
|
||||
|
|
||||
const [mention, setMention] = useState([]) |
|
||||
const [parcour, setParcour] = useState([]) |
|
||||
|
|
||||
useEffect(() => { |
|
||||
window.mention.getMention().then((response) => { |
|
||||
setMention(response) |
|
||||
}) |
|
||||
}, []) |
|
||||
|
|
||||
useEffect(() => { |
|
||||
if (id) { |
|
||||
window.notesysteme.getSingleParcours({ id }).then((response) => { |
|
||||
setParcour(response) |
|
||||
}) |
|
||||
} |
|
||||
}, [id]) |
|
||||
|
|
||||
useEffect(() => { |
|
||||
if (parcour) { |
|
||||
setFormData({ |
|
||||
nom: parcour.nom, |
|
||||
uniter: parcour.uniter, |
|
||||
mention_id: parcour.mention_id, |
|
||||
id: parcour.id |
|
||||
}) |
|
||||
} |
|
||||
}, [parcour]) |
|
||||
|
|
||||
const handleChange = (e) => { |
|
||||
const { name, value } = e.target |
|
||||
setFormData({ ...formData, [name]: value }) |
|
||||
} |
|
||||
|
|
||||
const handleSubmit = async (e) => { |
|
||||
e.preventDefault() |
|
||||
let response = await window.notesysteme.updateParcours(formData) |
|
||||
|
|
||||
if (response.success) { |
|
||||
onSubmitSuccess(true) |
|
||||
onClose() // Close the modal after submission |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
return ( |
|
||||
<Dialog open={open} onClose={onClose}> |
|
||||
<form action="" onSubmit={handleSubmit}> |
|
||||
<DialogTitle>Mise à jour du parcour</DialogTitle> |
|
||||
<DialogContent> |
|
||||
<Box sx={{ flexGrow: 1 }}> |
|
||||
<Grid container spacing={2}> |
|
||||
<Grid item xs={12} sm={6}> |
|
||||
<TextField |
|
||||
autoFocus |
|
||||
margin="normal" |
|
||||
required |
|
||||
name="nom" |
|
||||
label="Nom du parcours" |
|
||||
type="text" |
|
||||
fullWidth |
|
||||
placeholder="Nom du parcours" |
|
||||
variant="outlined" |
|
||||
value={formData.nom} |
|
||||
color="warning" |
|
||||
onChange={handleChange} |
|
||||
InputProps={{ |
|
||||
startAdornment: ( |
|
||||
<InputAdornment position="start"> |
|
||||
<MdRule /> |
|
||||
</InputAdornment> |
|
||||
) |
|
||||
}} |
|
||||
/> |
|
||||
</Grid> |
|
||||
<Grid item xs={12} sm={6}> |
|
||||
<TextField |
|
||||
autoFocus |
|
||||
margin="normal" |
|
||||
required |
|
||||
name="uniter" |
|
||||
label="Uniter parcours" |
|
||||
type="text" |
|
||||
fullWidth |
|
||||
placeholder="GPESB" |
|
||||
variant="outlined" |
|
||||
value={formData.uniter} |
|
||||
color="warning" |
|
||||
onChange={handleChange} |
|
||||
InputProps={{ |
|
||||
startAdornment: ( |
|
||||
<InputAdornment position="start"> |
|
||||
<MdRule /> |
|
||||
</InputAdornment> |
|
||||
) |
|
||||
}} |
|
||||
/> |
|
||||
</Grid> |
|
||||
<Grid item xs={12} sm={12}> |
|
||||
<Autocomplete |
|
||||
options={mention} // Options array for Autocomplete |
|
||||
getOptionLabel={(option) => option.nom || ''} // Safely access `nom` |
|
||||
value={mention.find((item) => item.id === formData.mention_id) || null} // Bind selected value to form data |
|
||||
onChange={(event, newValue) => { |
|
||||
setFormData((prevData) => ({ |
|
||||
...prevData, |
|
||||
mention_id: newValue ? newValue.id : null // Store the ID of the selected mention |
|
||||
})) |
|
||||
}} |
|
||||
isOptionEqualToValue={(option, value) => option.id === value.id} // Ensure correct matching of options |
|
||||
renderInput={(params) => ( |
|
||||
<TextField |
|
||||
{...params} |
|
||||
label="Mention" |
|
||||
color="warning" |
|
||||
placeholder="Sélectionnez une mention" |
|
||||
InputProps={{ |
|
||||
...params.InputProps, |
|
||||
startAdornment: ( |
|
||||
<> |
|
||||
<InputAdornment position="start"> |
|
||||
<FaClipboardList /> |
|
||||
</InputAdornment> |
|
||||
{params.InputProps.startAdornment} |
|
||||
</> |
|
||||
) |
|
||||
}} |
|
||||
sx={{ |
|
||||
marginBottom: '5px', |
|
||||
'& .MuiOutlinedInput-root': { |
|
||||
'&:hover fieldset': { |
|
||||
borderColor: '#ff9800' // Set border color on hover |
|
||||
} |
|
||||
} |
|
||||
}} |
|
||||
/> |
|
||||
)} |
|
||||
/> |
|
||||
</Grid> |
|
||||
</Grid> |
|
||||
</Box> |
|
||||
</DialogContent> |
|
||||
<DialogActions> |
|
||||
<Button onClick={onClose} color="error"> |
|
||||
Annuler |
|
||||
</Button> |
|
||||
<Button type="submit" color="warning"> |
|
||||
Soumettre |
|
||||
</Button> |
|
||||
</DialogActions> |
|
||||
</form> |
|
||||
</Dialog> |
|
||||
) |
|
||||
} |
|
||||
|
|
||||
export default AddParcours |
|
||||
@ -1,5 +0,0 @@ |
|||||
const calculNote = (note, coeficient) => { |
|
||||
return note * coeficient |
|
||||
} |
|
||||
|
|
||||
export default calculNote |
|
||||