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 |
|||