début du design system

This commit is contained in:
Ana-Lucia Munteanu 2025-04-02 19:57:04 -04:00
parent ee7a7a0544
commit 09955be8d4
12 changed files with 203 additions and 232 deletions

1
.gitignore vendored
View file

@ -132,3 +132,4 @@ launch.json
.yarn/install-state.gz
.pnp.*
db-backup/
/.vs

View file

@ -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",

View file

@ -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",

View file

@ -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';

View file

@ -1,59 +1,62 @@
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<any>(null); // Stocke les données d'auth
const [authData, setAuthData] = useState<any>(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 (
<div className="auth-selection-page">
<h1>Connexion</h1>
<div className="d-flex flex-column align-items-center p-4 w-100">
<h1 className="mb-4">Connexion</h1>
{/* Formulaire de connexion Simple Login */}
{/* Simple Login Form - Responsive width */}
{authData && authData['simpleauth'] && (
<div className="form-container">
<div className="border rounded-3 p-4 my-2 shadow-sm w-100" style={{ maxWidth: '400px' }}>
<SimpleLogin />
</div>
)}
{/* Conteneur OAuth/OIDC */}
{authData && Object.keys(authData).some(key => authData[key].type === 'oidc' || authData[key].type === 'oauth') && (
<div className="auth-button-container">
{Object.keys(authData).map((providerKey) => {
const providerType = authData[providerKey].type;
if (providerType === 'oidc' || providerType === 'oauth') {
return (
<ButtonAuth
key={providerKey}
providerName={providerKey}
providerType={providerType}
/>
);
}
return null;
})}
</div>
)}
{/* OAuth/OIDC Providers - Responsive width */}
{authData && Object.keys(authData).some(key =>
authData[key].type === 'oidc' || authData[key].type === 'oauth') && (
<div className="d-flex flex-column my-3 w-100" style={{ maxWidth: '400px' }}>
{Object.keys(authData).map((providerKey) => {
const providerType = authData[providerKey].type;
if (providerType === 'oidc' || providerType === 'oauth') {
return (
<ButtonAuth
key={providerKey}
providerName={providerKey}
providerType={providerType}
/>
);
}
return null;
})}
</div>
)}
<div>
<button className="home-button-container" onClick={() => navigate('/')}>Retour à l'accueil</button>
</div>
<button
className="btn btn-link text-dark p-0 mt-3"
onClick={() => navigate('/')}
>
Retour à l'accueil
</button>
</div>
);
};

View file

@ -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;
}

View file

@ -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<ButtonAuthContainerProps> = ({ providerName, providerType }) => {
return (
<>
<div className={`${providerName}-${providerType}-container button-container`}>
<h2>Se connecter avec {providerType.toUpperCase()}</h2>
<button key={providerName} className={`provider-btn ${providerType}-btn`} onClick={() => handleAuthLogin(providerName)}>
Continuer avec {providerName}
</button>
</div>
</>
<div className={`border rounded-3 p-3 my-3 mx-auto shadow-sm ${providerName}-${providerType}-container`} style={{ maxWidth: '400px' }}>
<h2 className="h5 mb-3">Se connecter avec {providerType.toUpperCase()}</h2>
<button
key={providerName}
className={`btn btn-outline-secondary w-100 ${providerType}-btn`}
onClick={() => handleAuthLogin(providerName)}
>
Continuer avec {providerName}
</button>
</div>
);
};

View file

@ -1,88 +1,80 @@
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<string>('');
const [isConnecting] = useState<boolean>(false);
const [isConnecting, setIsConnecting] = useState<boolean>(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 (
<LoginContainer
title=''
error={connectionError}>
<LoginContainer title='' error={connectionError}>
{/* Email Input */}
<TextField
label="Email"
variant="outlined"
className="mb-3 w-100" // Bootstrap classes for spacing and width
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="Nom d'utilisateur"
sx={{ marginBottom: '1rem' }}
fullWidth
fullWidth // Material-UI fullWidth
/>
{/* Password Input */}
<TextField
label="Mot de passe"
variant="outlined"
type="password"
className="mb-3 w-100" // Bootstrap classes
value={password}
onChange={(e) => setPassword(e.target.value)}
placeholder="Nom de la salle"
sx={{ marginBottom: '1rem' }}
placeholder="Mot de passe"
fullWidth
/>
<LoadingButton
loading={isConnecting}
onClick={login}
{/* Login Button */}
<Button
variant="contained"
sx={{ marginBottom: `${connectionError && '2rem'}` }}
disabled={!email || !password}
className={`w-100 mb-${connectionError ? '4' : '3'}`} // Dynamic margin-bottom
onClick={login}
disabled={!email || !password || isConnecting}
startIcon={isConnecting ? <CircularProgress size={20} /> : null}
size="large"
>
Login
</LoadingButton>
</Button>
<div className="login-links">
{/* <Link to="/resetPassword"> */}
<del>Réinitialiser le mot de passe</del>
{/* </Link> */}
<Link to="/register">
{/* Links Section */}
<div className="d-flex flex-column align-items-center pt-3">
<del className="py-1 text-muted">Réinitialiser le mot de passe</del>
<Link
to="/register"
className="py-1 text-decoration-none text-primary"
>
Créer un compte
</Link>
</div>
</LoginContainer>
);
};

View file

@ -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<string[]>(['teacher']); // Set 'student' as the default role
const [roles, setRoles] = useState<string[]>(['teacher']);
const [connectionError, setConnectionError] = useState<string>('');
const [isConnecting] = useState<boolean>(false);
const [isConnecting, setIsConnecting] = useState<boolean>(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 (
<LoginContainer
title="Créer un compte"
error={connectionError}
>
<LoginContainer title="Créer un compte" error={connectionError}>
{/* Name Field */}
<TextField
label="Nom"
variant="outlined"
className="mb-3 w-100"
value={name}
onChange={(e) => setName(e.target.value)}
placeholder="Votre nom"
sx={{ marginBottom: '1rem' }}
fullWidth
/>
{/* Email Field */}
<TextField
label="Email"
variant="outlined"
className="mb-3 w-100"
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="Adresse courriel"
sx={{ marginBottom: '1rem' }}
fullWidth
type="email"
error={!!connectionError && !isValidEmail(email)}
helperText={connectionError && !isValidEmail(email) ? "Adresse email invalide." : ""}
/>
{/* Password Field */}
<TextField
label="Mot de passe"
variant="outlined"
className="mb-3 w-100"
value={password}
type="password"
onChange={(e) => setPassword(e.target.value)}
placeholder="Mot de passe"
sx={{ marginBottom: '1rem' }}
fullWidth
/>
<Box sx={{ display: 'flex', alignItems: 'center', marginBottom: '1rem' }}>
<FormLabel component="legend" sx={{ marginRight: '1rem' }}>Choisir votre rôle</FormLabel>
{/* Role Selection */}
<Box className="d-flex align-items-center mb-3">
<FormLabel component="legend" className="me-3">
Choisir votre rôle
</FormLabel>
<RadioGroup
row
aria-label="role"
@ -98,15 +106,17 @@ const Register: React.FC = () => {
</RadioGroup>
</Box>
<LoadingButton
loading={isConnecting}
onClick={register}
{/* Register Button */}
<Button
variant="contained"
sx={{ marginBottom: `${connectionError && '2rem'}` }}
disabled={!name || !email || !password}
className={`w-100 mb-${connectionError ? '4' : '3'}`}
onClick={register}
disabled={!name || !email || !password || isConnecting}
startIcon={isConnecting ? <CircularProgress size={20} /> : null}
size="large"
>
S'inscrire
</LoadingButton>
</Button>
</LoginContainer>
);
};

View file

@ -1,66 +1,70 @@
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<string>('');
const [isConnecting] = useState<boolean>(false);
const [isConnecting, setIsConnecting] = useState<boolean>(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 (
<LoginContainer
title='Récupération du compte'
error={connectionError}>
<LoginContainer title='Récupération du compte' error={connectionError}>
{/* Email Field */}
<TextField
label="Email"
variant="outlined"
className="mb-3 w-100"
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="Adresse courriel"
sx={{ marginBottom: '1rem' }}
fullWidth
type="email"
error={!!connectionError && !isValidEmail(email)}
helperText={connectionError && !isValidEmail(email) ? "Adresse email invalide." : ""}
/>
<LoadingButton
loading={isConnecting}
onClick={reset}
{/* Reset Button */}
<Button
variant="contained"
sx={{ marginBottom: `${connectionError && '2rem'}` }}
disabled={!email}
className={`w-100 mb-${connectionError ? '4' : '3'}`}
onClick={reset}
disabled={!email || isConnecting}
startIcon={isConnecting ? <CircularProgress size={20} /> : null}
size="large"
>
Réinitialiser le mot de passe
</LoadingButton>
</Button>
</LoginContainer>
);
};

View file

@ -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;
}

View file

@ -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;
}