Compare commits
2 Commits
f1aefc3073
...
9e9bc98548
| Author | SHA1 | Date | |
|---|---|---|---|
| 9e9bc98548 | |||
| bd0e34d9ad |
|
Before Width: | Height: | Size: 35 KiB |
|
Before Width: | Height: | Size: 43 KiB |
135
src/main/test.js
@ -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 |
@ -1,10 +0,0 @@
|
||||
<svg viewBox="0 0 128 128" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<circle cx="64" cy="64" r="64" fill="#2F3242"/>
|
||||
<ellipse cx="63.9835" cy="23.2036" rx="4.48794" ry="4.495" stroke="#A2ECFB" stroke-width="3.6" stroke-linecap="round"/>
|
||||
<path d="M51.3954 39.5028C52.3733 39.6812 53.3108 39.033 53.4892 38.055C53.6676 37.0771 53.0194 36.1396 52.0414 35.9612L51.3954 39.5028ZM28.6153 43.5751L30.1748 44.4741L30.1748 44.4741L28.6153 43.5751ZM28.9393 60.9358C29.4332 61.7985 30.5329 62.0976 31.3957 61.6037C32.2585 61.1098 32.5575 60.0101 32.0636 59.1473L28.9393 60.9358ZM37.6935 66.7457C37.025 66.01 35.8866 65.9554 35.1508 66.6239C34.415 67.2924 34.3605 68.4308 35.029 69.1666L37.6935 66.7457ZM53.7489 81.7014L52.8478 83.2597L53.7489 81.7014ZM96.9206 89.515C97.7416 88.9544 97.9526 87.8344 97.3919 87.0135C96.8313 86.1925 95.7113 85.9815 94.8904 86.5422L96.9206 89.515ZM52.0414 35.9612C46.4712 34.9451 41.2848 34.8966 36.9738 35.9376C32.6548 36.9806 29.0841 39.1576 27.0559 42.6762L30.1748 44.4741C31.5693 42.0549 34.1448 40.3243 37.8188 39.4371C41.5009 38.5479 46.1547 38.5468 51.3954 39.5028L52.0414 35.9612ZM27.0559 42.6762C24.043 47.9029 25.2781 54.5399 28.9393 60.9358L32.0636 59.1473C28.6579 53.1977 28.1088 48.0581 30.1748 44.4741L27.0559 42.6762ZM35.029 69.1666C39.6385 74.24 45.7158 79.1355 52.8478 83.2597L54.6499 80.1432C47.8081 76.1868 42.0298 71.5185 37.6935 66.7457L35.029 69.1666ZM52.8478 83.2597C61.344 88.1726 70.0465 91.2445 77.7351 92.3608C85.359 93.4677 92.2744 92.6881 96.9206 89.515L94.8904 86.5422C91.3255 88.9767 85.4902 89.849 78.2524 88.7982C71.0793 87.7567 62.809 84.8612 54.6499 80.1432L52.8478 83.2597ZM105.359 84.9077C105.359 81.4337 102.546 78.6127 99.071 78.6127V82.2127C100.553 82.2127 101.759 83.4166 101.759 84.9077H105.359ZM99.071 78.6127C95.5956 78.6127 92.7831 81.4337 92.7831 84.9077H96.3831C96.3831 83.4166 97.5892 82.2127 99.071 82.2127V78.6127ZM92.7831 84.9077C92.7831 88.3817 95.5956 91.2027 99.071 91.2027V87.6027C97.5892 87.6027 96.3831 86.3988 96.3831 84.9077H92.7831ZM99.071 91.2027C102.546 91.2027 105.359 88.3817 105.359 84.9077H101.759C101.759 86.3988 100.553 87.6027 99.071 87.6027V91.2027Z" fill="#A2ECFB"/>
|
||||
<path d="M91.4873 65.382C90.8456 66.1412 90.9409 67.2769 91.7002 67.9186C92.4594 68.5603 93.5951 68.465 94.2368 67.7058L91.4873 65.382ZM99.3169 43.6354L97.7574 44.5344L99.3169 43.6354ZM84.507 35.2412C83.513 35.2282 82.6967 36.0236 82.6838 37.0176C82.6708 38.0116 83.4661 38.8279 84.4602 38.8409L84.507 35.2412ZM74.9407 39.8801C75.9127 39.6716 76.5315 38.7145 76.323 37.7425C76.1144 36.7706 75.1573 36.1517 74.1854 36.3603L74.9407 39.8801ZM53.7836 46.3728L54.6847 47.931L53.7836 46.3728ZM25.5491 80.9047C25.6932 81.8883 26.6074 82.5688 27.5911 82.4247C28.5747 82.2806 29.2552 81.3664 29.1111 80.3828L25.5491 80.9047ZM94.2368 67.7058C97.8838 63.3907 100.505 58.927 101.752 54.678C103.001 50.4213 102.9 46.2472 100.876 42.7365L97.7574 44.5344C99.1494 46.9491 99.3603 50.0419 98.2974 53.6644C97.2323 57.2945 94.9184 61.3223 91.4873 65.382L94.2368 67.7058ZM100.876 42.7365C97.9119 37.5938 91.7082 35.335 84.507 35.2412L84.4602 38.8409C91.1328 38.9278 95.7262 41.0106 97.7574 44.5344L100.876 42.7365ZM74.1854 36.3603C67.4362 37.8086 60.0878 40.648 52.8826 44.8146L54.6847 47.931C61.5972 43.9338 68.5948 41.2419 74.9407 39.8801L74.1854 36.3603ZM52.8826 44.8146C44.1366 49.872 36.9669 56.0954 32.1491 62.3927C27.3774 68.63 24.7148 75.2115 25.5491 80.9047L29.1111 80.3828C28.4839 76.1026 30.4747 70.5062 35.0084 64.5802C39.496 58.7143 46.2839 52.7889 54.6847 47.931L52.8826 44.8146Z" fill="#A2ECFB"/>
|
||||
<path d="M49.0825 87.2295C48.7478 86.2934 47.7176 85.8059 46.7816 86.1406C45.8455 86.4753 45.358 87.5055 45.6927 88.4416L49.0825 87.2295ZM78.5635 96.4256C79.075 95.5732 78.7988 94.4675 77.9464 93.9559C77.0941 93.4443 75.9884 93.7205 75.4768 94.5729L78.5635 96.4256ZM79.5703 85.1795C79.2738 86.1284 79.8027 87.1379 80.7516 87.4344C81.7004 87.7308 82.71 87.2019 83.0064 86.2531L79.5703 85.1795ZM84.3832 64.0673H82.5832H84.3832ZM69.156 22.5301C68.2477 22.1261 67.1838 22.535 66.7799 23.4433C66.3759 24.3517 66.7848 25.4155 67.6931 25.8194L69.156 22.5301ZM45.6927 88.4416C47.5994 93.7741 50.1496 98.2905 53.2032 101.505C56.2623 104.724 59.9279 106.731 63.9835 106.731V103.131C61.1984 103.131 58.4165 101.765 55.8131 99.0249C53.2042 96.279 50.8768 92.2477 49.0825 87.2295L45.6927 88.4416ZM63.9835 106.731C69.8694 106.731 74.8921 102.542 78.5635 96.4256L75.4768 94.5729C72.0781 100.235 68.0122 103.131 63.9835 103.131V106.731ZM83.0064 86.2531C85.0269 79.7864 86.1832 72.1831 86.1832 64.0673H82.5832C82.5832 71.8536 81.4723 79.0919 79.5703 85.1795L83.0064 86.2531ZM86.1832 64.0673C86.1832 54.1144 84.4439 44.922 81.4961 37.6502C78.5748 30.4436 74.3436 24.8371 69.156 22.5301L67.6931 25.8194C71.6364 27.5731 75.3846 32.1564 78.1598 39.0026C80.9086 45.7836 82.5832 54.507 82.5832 64.0673H86.1832Z" fill="#A2ECFB"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M103.559 84.9077C103.559 82.4252 101.55 80.4127 99.071 80.4127C96.5924 80.4127 94.5831 82.4252 94.5831 84.9077C94.5831 87.3902 96.5924 89.4027 99.071 89.4027C101.55 89.4027 103.559 87.3902 103.559 84.9077V84.9077Z" stroke="#A2ECFB" stroke-width="3.6" stroke-linecap="round"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M28.8143 89.4027C31.2929 89.4027 33.3023 87.3902 33.3023 84.9077C33.3023 82.4252 31.2929 80.4127 28.8143 80.4127C26.3357 80.4127 24.3264 82.4252 24.3264 84.9077C24.3264 87.3902 26.3357 89.4027 28.8143 89.4027V89.4027V89.4027Z" stroke="#A2ECFB" stroke-width="3.6" stroke-linecap="round"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M64.8501 68.0857C62.6341 68.5652 60.451 67.1547 59.9713 64.9353C59.4934 62.7159 60.9007 60.5293 63.1167 60.0489C65.3326 59.5693 67.5157 60.9798 67.9954 63.1992C68.4742 65.4186 67.066 67.6052 64.8501 68.0857Z" fill="#A2ECFB"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 5.7 KiB |
|
Before Width: | Height: | Size: 21 KiB |
@ -1,52 +0,0 @@
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 52 52"
|
||||
width="100"
|
||||
height="100"
|
||||
>
|
||||
<circle
|
||||
cx="26"
|
||||
cy="26"
|
||||
r="25"
|
||||
fill="none"
|
||||
stroke="#f44336"
|
||||
stroke-width="2"
|
||||
/>
|
||||
<path
|
||||
fill="none"
|
||||
stroke="#f44336"
|
||||
stroke-width="5"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-dasharray="40"
|
||||
stroke-dashoffset="40"
|
||||
d="M16 16l20 20M16 36l20-20"
|
||||
id="error-cross"
|
||||
/>
|
||||
<style>
|
||||
@keyframes drawError {
|
||||
0% {
|
||||
stroke-dashoffset: 40;
|
||||
}
|
||||
50% {
|
||||
stroke-dashoffset: 0;
|
||||
}
|
||||
100% {
|
||||
stroke-dashoffset: 0;
|
||||
}
|
||||
}
|
||||
|
||||
#error-cross {
|
||||
animation: drawError 1s ease forwards infinite;
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
0%, 100% {
|
||||
transform: scale(1);
|
||||
}
|
||||
50% {
|
||||
transform: scale(1.1);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</svg>
|
||||
|
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 |
@ -1,52 +0,0 @@
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 52 52"
|
||||
width="100"
|
||||
height="100"
|
||||
>
|
||||
<circle
|
||||
cx="26"
|
||||
cy="26"
|
||||
r="25"
|
||||
fill="none"
|
||||
stroke="#4caf50"
|
||||
stroke-width="2"
|
||||
/>
|
||||
<path
|
||||
fill="none"
|
||||
stroke="#4caf50"
|
||||
stroke-width="5"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-dasharray="48"
|
||||
stroke-dashoffset="48"
|
||||
d="M14 27l7 7 17-17"
|
||||
id="success-checkmark"
|
||||
/>
|
||||
<style>
|
||||
@keyframes drawSuccess {
|
||||
0% {
|
||||
stroke-dashoffset: 48;
|
||||
}
|
||||
50% {
|
||||
stroke-dashoffset: 0;
|
||||
}
|
||||
100% {
|
||||
stroke-dashoffset: 0;
|
||||
}
|
||||
}
|
||||
|
||||
#success-checkmark {
|
||||
animation: drawSuccess 1s ease forwards infinite;
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
0%, 100% {
|
||||
transform: scale(1);
|
||||
}
|
||||
50% {
|
||||
transform: scale(1.1);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 871 B |
|
Before Width: | Height: | Size: 96 KiB |
|
Before Width: | Height: | Size: 174 KiB |
@ -1,65 +0,0 @@
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 52 52"
|
||||
width="100"
|
||||
height="100"
|
||||
>
|
||||
<circle
|
||||
cx="26"
|
||||
cy="26"
|
||||
r="25"
|
||||
fill="none"
|
||||
stroke="#ffa500"
|
||||
stroke-width="2"
|
||||
class="pulse-circle"
|
||||
/>
|
||||
<!-- Ligne du point d'exclamation -->
|
||||
<line
|
||||
x1="26"
|
||||
y1="15"
|
||||
x2="26"
|
||||
y2="32"
|
||||
stroke="#ffa500"
|
||||
stroke-width="5"
|
||||
stroke-linecap="round"
|
||||
id="exclamation-line"
|
||||
/>
|
||||
<!-- Point du point d'exclamation -->
|
||||
<circle
|
||||
cx="26"
|
||||
cy="38"
|
||||
r="2.5"
|
||||
fill="#ffa500"
|
||||
id="exclamation-dot"
|
||||
/>
|
||||
<style>
|
||||
@keyframes drawExclamation {
|
||||
0% {
|
||||
stroke-dashoffset: 17;
|
||||
}
|
||||
100% {
|
||||
stroke-dashoffset: 0;
|
||||
}
|
||||
}
|
||||
|
||||
#exclamation-line {
|
||||
stroke-dasharray: 17;
|
||||
stroke-dashoffset: 17;
|
||||
animation: drawExclamation 0.5s ease forwards;
|
||||
}
|
||||
|
||||
.pulse-circle {
|
||||
animation: pulse 2s infinite;
|
||||
transform-origin: center;
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
0%, 100% {
|
||||
transform: scale(1);
|
||||
}
|
||||
50% {
|
||||
transform: scale(1.1);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.1 KiB |
@ -1,25 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1422 800" opacity="0.3">
|
||||
<defs>
|
||||
<linearGradient x1="50%" y1="0%" x2="50%" y2="100%" id="oooscillate-grad">
|
||||
<stop stop-color="hsl(206, 75%, 49%)" stop-opacity="1" offset="0%"></stop>
|
||||
<stop stop-color="hsl(331, 90%, 56%)" stop-opacity="1" offset="100%"></stop>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<g stroke-width="1" stroke="url(#oooscillate-grad)" fill="none" stroke-linecap="round">
|
||||
<path d="M 0 448 Q 355.5 -100 711 400 Q 1066.5 900 1422 448" opacity="0.05"></path>
|
||||
<path d="M 0 420 Q 355.5 -100 711 400 Q 1066.5 900 1422 420" opacity="0.11"></path>
|
||||
<path d="M 0 392 Q 352.5 -100 711 400 Q 1066.5 900 1422 392" opacity="0.18"></path>
|
||||
<path d="M 0 364 Q 355.5 -100 711 400 Q 1066.5 900 1422 364" opacity="0.24"></path>
|
||||
<path d="M 0 336 Q 355.5 -100 711 400 Q 1066.5 900 1422 336" opacity="0.30"></path>
|
||||
<path d="M 0 308 Q 355.5 -100 711 400 Q 1066.5 900 1422 308" opacity="0.37"></path>
|
||||
<path d="M 0 280 Q 355.5 -100 711 400 Q 1066.5 900 1422 280" opacity="0.43"></path>
|
||||
<path d="M 0 252 Q 355.5 -100 711 400 Q 1066.5 900 1422 252" opacity="0.49"></path>
|
||||
<path d="M 0 224 Q 355.5 -100 711 400 Q 1066.5 900 1422 224" opacity="0.56"></path>
|
||||
<path d="M 0 196 Q 355.5 -100 711 400 Q 1066.5 900 1422 196" opacity="0.62"></path>
|
||||
<path d="M 0 168 Q 355.5 -100 711 400 Q 1066.5 900 1422 168" opacity="0.68"></path>
|
||||
<path d="M 0 140 Q 355.5 -100 711 400 Q 1066.5 900 1422 140" opacity="0.75"></path>
|
||||
<path d="M 0 112 Q 355.5 -100 711 400 Q 1066.5 900 1422 112" opacity="0.81"></path>
|
||||
<path d="M 0 84 Q 355.5 -100 711 400 Q 1066.5 900 1422 84" opacity="0.87"></path>
|
||||
<path d="M 0 56 Q 355.5 -100 711 400 Q 1066.5 900 1422 56" opacity="0.94"></path>
|
||||
</g>
|
||||
</svg>
|
||||
|
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
|
||||