diff --git a/.gitignore b/.gitignore index 1ae1622..b39bc1b 100644 --- a/.gitignore +++ b/.gitignore @@ -131,4 +131,5 @@ launch.json .yarn/build-state.yml .yarn/install-state.gz .pnp.* -db-backup/ \ No newline at end of file +db-backup/ +/.vs diff --git a/client/package-lock.json b/client/package-lock.json index e2c6890..1cd42d5 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -17,8 +17,11 @@ "@mui/icons-material": "^6.4.6", "@mui/lab": "^5.0.0-alpha.153", "@mui/material": "^6.4.6", + "@types/bootstrap": "^5.2.10", "@types/uuid": "^9.0.7", "axios": "^1.8.1", + "bootstrap": "^5.3.3", + "bootstrap-icons": "^1.11.3", "dompurify": "^3.2.3", "esbuild": "^0.25.0", "gift-pegjs": "^2.0.0-beta.1", @@ -4521,6 +4524,14 @@ "@babel/types": "^7.20.7" } }, + "node_modules/@types/bootstrap": { + "version": "5.2.10", + "resolved": "https://registry.npmjs.org/@types/bootstrap/-/bootstrap-5.2.10.tgz", + "integrity": "sha512-F2X+cd6551tep0MvVZ6nM8v7XgGN/twpdNDjqS1TUM7YFNEtQYWk+dKAnH+T1gr6QgCoGMPl487xw/9hXooa2g==", + "dependencies": { + "@popperjs/core": "^2.9.2" + } + }, "node_modules/@types/debug": { "version": "4.1.12", "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", @@ -5536,6 +5547,39 @@ "devOptional": true, "license": "MIT" }, + "node_modules/bootstrap": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.3.tgz", + "integrity": "sha512-8HLCdWgyoMguSO9o+aH+iuZ+aht+mzW0u3HIMzVu7Srrpv7EBBxTnrFlSCskwdY1+EOFQSm7uMJhNQHkdPcmjg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/twbs" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/bootstrap" + } + ], + "peerDependencies": { + "@popperjs/core": "^2.11.8" + } + }, + "node_modules/bootstrap-icons": { + "version": "1.11.3", + "resolved": "https://registry.npmjs.org/bootstrap-icons/-/bootstrap-icons-1.11.3.tgz", + "integrity": "sha512-+3lpHrCw/it2/7lBL15VR0HEumaBss0+f/Lb6ZvHISn1mlK83jjFpooTLsMWbIjJMDjDjOExMsTxnXSIT4k4ww==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/twbs" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/bootstrap" + } + ] + }, "node_modules/brace-expansion": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", diff --git a/client/package.json b/client/package.json index b6d62e4..0a1b056 100644 --- a/client/package.json +++ b/client/package.json @@ -21,8 +21,11 @@ "@mui/icons-material": "^6.4.6", "@mui/lab": "^5.0.0-alpha.153", "@mui/material": "^6.4.6", + "@types/bootstrap": "^5.2.10", "@types/uuid": "^9.0.7", "axios": "^1.8.1", + "bootstrap": "^5.3.3", + "bootstrap-icons": "^1.11.3", "dompurify": "^3.2.3", "esbuild": "^0.25.0", "gift-pegjs": "^2.0.0-beta.1", diff --git a/client/src/main.tsx b/client/src/main.tsx index e73c979..a04f569 100644 --- a/client/src/main.tsx +++ b/client/src/main.tsx @@ -6,6 +6,7 @@ import { BrowserRouter } from 'react-router-dom'; import { ThemeProvider, createTheme } from '@mui/material'; import '@fortawesome/fontawesome-free/css/all.min.css'; +import 'bootstrap/dist/css/bootstrap.min.css'; import './cssReset.css'; import './index.css'; diff --git a/client/src/pages/AuthManager/AuthDrawer.tsx b/client/src/pages/AuthManager/AuthDrawer.tsx index 093b7aa..bd16513 100644 --- a/client/src/pages/AuthManager/AuthDrawer.tsx +++ b/client/src/pages/AuthManager/AuthDrawer.tsx @@ -1,61 +1,64 @@ import React, { useState, useEffect } from 'react'; import { useNavigate } from 'react-router-dom'; -import './authDrawer.css'; import SimpleLogin from './providers/SimpleLogin/Login'; import authService from '../../services/AuthService'; import { ENV_VARIABLES } from '../../constants'; import ButtonAuth from './providers/OAuth-Oidc/ButtonAuth'; +import 'bootstrap/dist/css/bootstrap.min.css'; const AuthSelection: React.FC = () => { - const [authData, setAuthData] = useState(null); // Stocke les données d'auth + const [authData, setAuthData] = useState(null); const navigate = useNavigate(); ENV_VARIABLES.VITE_BACKEND_URL; - // Récupérer les données d'authentification depuis l'API + useEffect(() => { const fetchData = async () => { const data = await authService.fetchAuthData(); setAuthData(data); }; - fetchData(); }, []); return ( -
-

Connexion

+
+

Connexion

- {/* Formulaire de connexion Simple Login */} + {/* Simple Login Form - Responsive width */} {authData && authData['simpleauth'] && ( -
+
)} - {/* Conteneur OAuth/OIDC */} - {authData && Object.keys(authData).some(key => authData[key].type === 'oidc' || authData[key].type === 'oauth') && ( -
- {Object.keys(authData).map((providerKey) => { - const providerType = authData[providerKey].type; - if (providerType === 'oidc' || providerType === 'oauth') { - return ( - - ); - } - return null; - })} -
- )} + {/* OAuth/OIDC Providers - Responsive width */} + {authData && Object.keys(authData).some(key => + authData[key].type === 'oidc' || authData[key].type === 'oauth') && ( +
+ {Object.keys(authData).map((providerKey) => { + const providerType = authData[providerKey].type; + if (providerType === 'oidc' || providerType === 'oauth') { + return ( + + ); + } + return null; + })} +
+ )} -
- -
+
); }; -export default AuthSelection; +export default AuthSelection; \ No newline at end of file diff --git a/client/src/pages/AuthManager/authDrawer.css b/client/src/pages/AuthManager/authDrawer.css deleted file mode 100644 index b0d5263..0000000 --- a/client/src/pages/AuthManager/authDrawer.css +++ /dev/null @@ -1,49 +0,0 @@ -.auth-selection-page { - display: flex; - flex-direction: column; - align-items: center; - padding: 20px; -} -h1 { - margin-bottom: 20px; -} -.form-container { - border: 1px solid #ccc; - border-radius: 8px; - padding: 15px; - margin: 10px 0; - width: 400px; - box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); - text-align: center; -} -form { - display: flex; - flex-direction: column; -} -input { - margin: 5px 0; - padding: 10px; - border: 1px solid #ccc; - border-radius: 4px; -} -button { - padding: 10px; - border: none; - border-radius: 4px; - background-color: #5271ff; - color: white; - cursor: pointer; -} -/* This hover was affecting the entire App */ -/* button:hover { - background-color: #5271ff; - } */ -.home-button-container { - background: none; - color: black; -} -.home-button-container:hover { - background: none; - color: black; - text-decoration: underline; -} diff --git a/client/src/pages/AuthManager/providers/OAuth-Oidc/ButtonAuth.tsx b/client/src/pages/AuthManager/providers/OAuth-Oidc/ButtonAuth.tsx index c8f4efc..a07e315 100644 --- a/client/src/pages/AuthManager/providers/OAuth-Oidc/ButtonAuth.tsx +++ b/client/src/pages/AuthManager/providers/OAuth-Oidc/ButtonAuth.tsx @@ -1,6 +1,6 @@ import React from 'react'; import { ENV_VARIABLES } from '../../../../constants'; -import '../css/buttonAuth.css'; +import 'bootstrap/dist/css/bootstrap.min.css'; interface ButtonAuthContainerProps { providerName: string; @@ -13,14 +13,16 @@ const handleAuthLogin = (provider: string) => { const ButtonAuth: React.FC = ({ providerName, providerType }) => { return ( - <> -
-

Se connecter avec {providerType.toUpperCase()}

- -
- +
+

Se connecter avec {providerType.toUpperCase()}

+ +
); }; diff --git a/client/src/pages/AuthManager/providers/SimpleLogin/Login.tsx b/client/src/pages/AuthManager/providers/SimpleLogin/Login.tsx index ecc9a1c..0caea5f 100644 --- a/client/src/pages/AuthManager/providers/SimpleLogin/Login.tsx +++ b/client/src/pages/AuthManager/providers/SimpleLogin/Login.tsx @@ -1,90 +1,82 @@ +import React, { useState, useEffect } from 'react'; import { Link } from 'react-router-dom'; - -// JoinRoom.tsx -import React, { useEffect, useState } from 'react'; - -import '../css/simpleLogin.css'; -import { TextField } from '@mui/material'; -import LoadingButton from '@mui/lab/LoadingButton'; - -import LoginContainer from '../../../../components/LoginContainer/LoginContainer' +import { TextField, Button, CircularProgress } from '@mui/material'; +import LoginContainer from '../../../../components/LoginContainer/LoginContainer'; import ApiService from '../../../../services/ApiService'; +import 'bootstrap/dist/css/bootstrap.min.css'; const SimpleLogin: React.FC = () => { - const [email, setEmail] = useState(''); const [password, setPassword] = useState(''); - const [connectionError, setConnectionError] = useState(''); - const [isConnecting] = useState(false); + const [isConnecting, setIsConnecting] = useState(false); useEffect(() => { return () => { - + // Cleanup if needed }; }, []); const login = async () => { - console.log(`SimpleLogin: login: email: ${email}, password: ${password}`); + setIsConnecting(true); const result = await ApiService.login(email, password); + setIsConnecting(false); + if (result !== true) { setConnectionError(result); return; } }; - return ( - - + + {/* Email Input */} setEmail(e.target.value)} placeholder="Nom d'utilisateur" - sx={{ marginBottom: '1rem' }} - fullWidth + fullWidth // Material-UI fullWidth /> + {/* Password Input */} setPassword(e.target.value)} - placeholder="Nom de la salle" - sx={{ marginBottom: '1rem' }} + placeholder="Mot de passe" fullWidth /> - : null} + size="large" > Login - + -
- - - {/* */} - Réinitialiser le mot de passe - {/* */} - - + {/* Links Section */} +
+ Réinitialiser le mot de passe + Créer un compte -
- ); }; -export default SimpleLogin; +export default SimpleLogin; \ No newline at end of file diff --git a/client/src/pages/AuthManager/providers/SimpleLogin/Register.tsx b/client/src/pages/AuthManager/providers/SimpleLogin/Register.tsx index 46a8c85..396f451 100644 --- a/client/src/pages/AuthManager/providers/SimpleLogin/Register.tsx +++ b/client/src/pages/AuthManager/providers/SimpleLogin/Register.tsx @@ -1,32 +1,35 @@ -// JoinRoom.tsx -import React, { useEffect, useState } from 'react'; - -import { TextField, FormLabel, RadioGroup, FormControlLabel, Radio, Box } from '@mui/material'; -import LoadingButton from '@mui/lab/LoadingButton'; - +import React, { useState, useEffect } from 'react'; +import { + TextField, + FormLabel, + RadioGroup, + FormControlLabel, + Radio, + Box, + Button, + CircularProgress +} from '@mui/material'; import LoginContainer from '../../../../components/LoginContainer/LoginContainer'; import ApiService from '../../../../services/ApiService'; +import 'bootstrap/dist/css/bootstrap.min.css'; const Register: React.FC = () => { - - const [name, setName] = useState(''); // State for name + const [name, setName] = useState(''); const [email, setEmail] = useState(''); const [password, setPassword] = useState(''); - const [roles, setRoles] = useState(['teacher']); // Set 'student' as the default role - + const [roles, setRoles] = useState(['teacher']); const [connectionError, setConnectionError] = useState(''); - const [isConnecting] = useState(false); + const [isConnecting, setIsConnecting] = useState(false); useEffect(() => { return () => { }; }, []); const handleRoleChange = (role: string) => { - setRoles([role]); // Update the roles array to contain the selected role + setRoles([role]); }; const isValidEmail = (email: string) => { - // Basic email format validation const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; return emailRegex.test(email); }; @@ -37,7 +40,9 @@ const Register: React.FC = () => { return; } + setIsConnecting(true); const result = await ApiService.register(name, email, password, roles); + setIsConnecting(false); if (result !== true) { setConnectionError(result); @@ -46,46 +51,49 @@ const Register: React.FC = () => { }; return ( - + + {/* Name Field */} setName(e.target.value)} placeholder="Votre nom" - sx={{ marginBottom: '1rem' }} fullWidth /> + {/* Email Field */} setEmail(e.target.value)} placeholder="Adresse courriel" - sx={{ marginBottom: '1rem' }} fullWidth - type="email" - error={!!connectionError && !isValidEmail(email)} + type="email" + error={!!connectionError && !isValidEmail(email)} helperText={connectionError && !isValidEmail(email) ? "Adresse email invalide." : ""} /> + {/* Password Field */} setPassword(e.target.value)} placeholder="Mot de passe" - sx={{ marginBottom: '1rem' }} fullWidth /> - - Choisir votre rôle + {/* Role Selection */} + + + Choisir votre rôle + { - : null} + size="large" > S'inscrire - + ); }; -export default Register; +export default Register; \ No newline at end of file diff --git a/client/src/pages/AuthManager/providers/SimpleLogin/ResetPassword.tsx b/client/src/pages/AuthManager/providers/SimpleLogin/ResetPassword.tsx index c33c9fa..526c487 100644 --- a/client/src/pages/AuthManager/providers/SimpleLogin/ResetPassword.tsx +++ b/client/src/pages/AuthManager/providers/SimpleLogin/ResetPassword.tsx @@ -1,68 +1,72 @@ - +import React, { useState, useEffect } from 'react'; import { useNavigate } from 'react-router-dom'; - -// JoinRoom.tsx -import React, { useEffect, useState } from 'react'; - -import { TextField } from '@mui/material'; -import LoadingButton from '@mui/lab/LoadingButton'; - -import LoginContainer from '../../../../components/LoginContainer/LoginContainer' +import { TextField, Button, CircularProgress } from '@mui/material'; +import LoginContainer from '../../../../components/LoginContainer/LoginContainer'; import ApiService from '../../../../services/ApiService'; +import 'bootstrap/dist/css/bootstrap.min.css'; const ResetPassword: React.FC = () => { const navigate = useNavigate(); - const [email, setEmail] = useState(''); - const [connectionError, setConnectionError] = useState(''); - const [isConnecting] = useState(false); + const [isConnecting, setIsConnecting] = useState(false); useEffect(() => { return () => { - + // Cleanup if needed }; }, []); const reset = async () => { - const result = await ApiService.resetPassword(email); + setIsConnecting(true); + try { + const result = await ApiService.resetPassword(email); - if (!result) { - setConnectionError(result.toString()); - return; + if (!result) { + setConnectionError(result.toString()); + return; + } + + navigate("/login"); + } finally { + setIsConnecting(false); } - - navigate("/login") }; + const isValidEmail = (email: string) => { + const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; + return emailRegex.test(email); + }; return ( - - + + {/* Email Field */} setEmail(e.target.value)} placeholder="Adresse courriel" - sx={{ marginBottom: '1rem' }} fullWidth + type="email" + error={!!connectionError && !isValidEmail(email)} + helperText={connectionError && !isValidEmail(email) ? "Adresse email invalide." : ""} /> - : null} + size="large" > Réinitialiser le mot de passe - - + ); }; -export default ResetPassword; +export default ResetPassword; \ No newline at end of file diff --git a/client/src/pages/AuthManager/providers/css/buttonAuth.css b/client/src/pages/AuthManager/providers/css/buttonAuth.css deleted file mode 100644 index 98476ec..0000000 --- a/client/src/pages/AuthManager/providers/css/buttonAuth.css +++ /dev/null @@ -1,23 +0,0 @@ -.provider-btn { - background-color: #ffffff; - border: 1px solid #ccc; - color: black; - margin: 4px 0 4px 0; -} - -.provider-btn:hover { - background-color: #dbdbdb; - border: 1px solid #ccc; - color: black; - margin: 4px 0 4px 0; -} - -.button-container { - border: 1px solid #ccc; - border-radius: 8px; - padding: 15px; - margin: 10px 0; - width: 400px; - box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); - text-align: center; -} \ No newline at end of file diff --git a/client/src/pages/AuthManager/providers/css/simpleLogin.css b/client/src/pages/AuthManager/providers/css/simpleLogin.css deleted file mode 100644 index ddbebdb..0000000 --- a/client/src/pages/AuthManager/providers/css/simpleLogin.css +++ /dev/null @@ -1,17 +0,0 @@ -.login-links { - padding-top: 10px; - width: 100%; - display: flex; - flex-direction: column; - align-items: center; -} - -.login-links a { - padding: 4px; - color: #333; - text-decoration: none; -} - -.login-links a:hover { - text-decoration: underline; -}