Merge branch 'feature/auth' into 'dev'
Feature/auth See merge request sae-but2/2025-26/gestion-benevoles-association/Frontend!21
This commit is contained in:
@@ -1,47 +1,22 @@
|
|||||||
/*import { useState } from "react";
|
import { useState, useContext } from "react";
|
||||||
import login from "../../utils/login.js";
|
import styles from "./loginForm.module.css";
|
||||||
import getUser from "../../utils/getUser.js";
|
import Button from "../ui/button/button.jsx";
|
||||||
import styles from "./form.module.css";
|
import Input from "../ui/input/input.jsx";
|
||||||
function LoginForm({ className }) {
|
import { useNavigate } from "react-router";
|
||||||
const [email, setEmail] = useState('');
|
import { AuthContext } from "../../contexts/auth/AuthContext.js";
|
||||||
const [password, setPassword] = useState('');
|
|
||||||
async function handleSubmit(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
await login(email, password);
|
|
||||||
getUser();
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<div className={`${className} ${"glassCard"}`}>
|
|
||||||
<div className={styles.formgroup}>
|
|
||||||
<input type="email" id="email" value={email} placeholder="Adresse mail" onChange={e => setEmail(e.target.value)} required/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className={styles.formgroup}>
|
|
||||||
<input type="password" id="password" value={password} placeholder="Mot de passe" onChange={e => setPassword(e.target.value)} required/>
|
|
||||||
<br/>
|
|
||||||
</div>
|
|
||||||
<div className={styles.logButton} >
|
|
||||||
<button className={styles.loginButton} onClick={e => handleSubmit(e)}>Se connecter</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
export default LoginForm;*/
|
|
||||||
|
|
||||||
import { useState } from "react";
|
|
||||||
import login from "../../utils/login.js";
|
|
||||||
import getUser from "../../utils/getUser.js";
|
|
||||||
import styles from "./form.module.css";
|
|
||||||
|
|
||||||
function LoginForm({ className }) {
|
function LoginForm({ className }) {
|
||||||
const [email, setEmail] = useState('');
|
const [email, setEmail] = useState('');
|
||||||
const [password, setPassword] = useState('');
|
const [password, setPassword] = useState('');
|
||||||
const [showPassword, setShowPassword] = useState(false);
|
const [showPassword, setShowPassword] = useState(false);
|
||||||
|
let navigate = useNavigate();
|
||||||
|
const { login } = useContext(AuthContext);
|
||||||
|
|
||||||
async function handleSubmit(e) {
|
async function handleSubmit(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
await login(email, password);
|
await login(email, password);
|
||||||
getUser();
|
navigate("/");
|
||||||
}
|
}
|
||||||
|
|
||||||
const toggleShowPassword = () => {
|
const toggleShowPassword = () => {
|
||||||
@@ -51,27 +26,22 @@ function LoginForm({ className }) {
|
|||||||
return (
|
return (
|
||||||
<div className={`${className} ${"glassCard"}`}>
|
<div className={`${className} ${"glassCard"}`}>
|
||||||
<div className={styles.formgroup}>
|
<div className={styles.formgroup}>
|
||||||
<input
|
<Input
|
||||||
type="email"
|
type="email"
|
||||||
id="email"
|
|
||||||
className={styles.input}
|
|
||||||
value={email}
|
value={email}
|
||||||
placeholder="Adresse mail"
|
placeholder="Adresse mail"
|
||||||
onChange={e => setEmail(e.target.value)}
|
onChange={e => setEmail(e.target.value)}
|
||||||
required
|
style={{ width: '100%' }}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.formgroup}>
|
<div className={styles.formgroup}>
|
||||||
<div style={{ position: 'relative', width: '100%' }}>
|
<div style={{ position: 'relative', width: '100%' }}>
|
||||||
<input
|
<Input
|
||||||
type={showPassword ? "text" : "password"}
|
type={showPassword ? "text" : "password"}
|
||||||
id="password"
|
|
||||||
className={styles.input}
|
|
||||||
value={password}
|
value={password}
|
||||||
placeholder="Mot de passe"
|
placeholder="Mot de passe"
|
||||||
onChange={e => setPassword(e.target.value)}
|
onChange={e => setPassword(e.target.value)}
|
||||||
required
|
style={{ width: '100%' }}
|
||||||
style={{ width: '100%', paddingRight: '40px' }}
|
|
||||||
/>
|
/>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
@@ -92,8 +62,9 @@ function LoginForm({ className }) {
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={styles.logButton}>
|
<div className={styles.logButton}>
|
||||||
<button className={styles.loginButton} onClick={handleSubmit}>Se connecter</button>
|
<Button onClick={handleSubmit} variant={"default"}> Se connecter </Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
import { createContext } from "react";
|
||||||
|
|
||||||
|
export const AuthContext = createContext(null);
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
import { useState, useEffect } from "react";
|
||||||
|
import loginFunction from "../../utils/login.js";
|
||||||
|
import getUser from "../../utils/getUser.js";
|
||||||
|
import { AuthContext } from "./AuthContext";
|
||||||
|
|
||||||
|
|
||||||
|
export function AuthProvider({ children }) {
|
||||||
|
const [user, setUser] = useState(null);
|
||||||
|
const [loading, setLoading] = useState(true);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
(async () => {
|
||||||
|
const u = await getUser();
|
||||||
|
setUser(u);
|
||||||
|
setLoading(false);
|
||||||
|
})();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const login = async (email, password) => {
|
||||||
|
const res = await loginFunction(email, password);
|
||||||
|
if (!res || res.status !== 200) throw new Error("Login error");
|
||||||
|
const u = await getUser();
|
||||||
|
setUser(u);
|
||||||
|
return res;
|
||||||
|
};
|
||||||
|
|
||||||
|
const logout = async () => {
|
||||||
|
// TODO: Appeller fonction logout
|
||||||
|
setUser(null);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<AuthContext.Provider value={{ user, loading, login, logout }}>
|
||||||
|
{children}
|
||||||
|
</AuthContext.Provider>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -2,10 +2,18 @@ import { Outlet } from "react-router";
|
|||||||
import Footer from "./components/Footer/Footer.jsx"
|
import Footer from "./components/Footer/Footer.jsx"
|
||||||
import Header from "./components/Header/Header.jsx";
|
import Header from "./components/Header/Header.jsx";
|
||||||
import Background from "./components/background/background.jsx";
|
import Background from "./components/background/background.jsx";
|
||||||
|
import { Navigate } from "react-router";
|
||||||
|
import { useContext} from "react";
|
||||||
|
import { AuthContext } from "./contexts/auth/AuthContext.js";
|
||||||
|
|
||||||
|
|
||||||
function Layout(){
|
function Layout(){
|
||||||
|
|
||||||
|
const { user, loading } = useContext(AuthContext);
|
||||||
|
|
||||||
|
if (loading) return <p>Loading</p>;
|
||||||
|
if (!user) return <Navigate to="/login" />;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Background>
|
<Background>
|
||||||
|
|||||||
+4
-1
@@ -3,10 +3,13 @@ import { createRoot } from 'react-dom/client'
|
|||||||
import './index.css'
|
import './index.css'
|
||||||
import { RouterProvider } from "react-router";
|
import { RouterProvider } from "react-router";
|
||||||
import router from './router';
|
import router from './router';
|
||||||
|
import { AuthProvider } from "./contexts/auth/authProvider.jsx";
|
||||||
|
|
||||||
|
|
||||||
createRoot(document.getElementById('root')).render(
|
createRoot(document.getElementById('root')).render(
|
||||||
<StrictMode>
|
<StrictMode>
|
||||||
<RouterProvider router={router} />
|
<AuthProvider>
|
||||||
|
<RouterProvider router={router} />
|
||||||
|
</AuthProvider>
|
||||||
</StrictMode>,
|
</StrictMode>,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,12 +1,15 @@
|
|||||||
import styles from "./LoginPage.module.css";
|
import styles from "./LoginPage.module.css";
|
||||||
import LoginForm from "../../components/form/form.jsx";
|
import LoginForm from "../../components/loginform/loginForm.jsx";
|
||||||
|
import Background from "../../components/background/background.jsx";
|
||||||
|
|
||||||
function LoginPage() {
|
function LoginPage() {
|
||||||
return (
|
return <Background>
|
||||||
|
|
||||||
<div className={styles.container}>
|
<div className={styles.container}>
|
||||||
<LoginForm />
|
<LoginForm />
|
||||||
</div>
|
</div>
|
||||||
);
|
|
||||||
|
</Background>
|
||||||
}
|
}
|
||||||
|
|
||||||
export default LoginPage;
|
export default LoginPage;
|
||||||
|
|||||||
@@ -0,0 +1,105 @@
|
|||||||
|
import styles from "./RegisterPage.module.css";
|
||||||
|
import Background from "../../components/background/background.jsx";
|
||||||
|
import Input from "../../components/ui/input/input.jsx";
|
||||||
|
import Button from "../../components/ui/button/button.jsx";
|
||||||
|
import { useState } from "react";
|
||||||
|
import register from "../../utils/register.js";
|
||||||
|
import { useNavigate } from "react-router";
|
||||||
|
|
||||||
|
|
||||||
|
function RegisterPage() {
|
||||||
|
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
const [email, setEmail] = useState("");
|
||||||
|
const [password, setPassword] = useState("");
|
||||||
|
const [name, setName] = useState("");
|
||||||
|
const [lastName, setLastName] = useState("");
|
||||||
|
|
||||||
|
const [showPassword, setShowPassword] = useState(false);
|
||||||
|
|
||||||
|
|
||||||
|
async function handleSubmit(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
const res = await register(email, password, name, lastName);
|
||||||
|
if (!res || res.status !== 200) throw new Error("Register error");
|
||||||
|
navigate("/login");
|
||||||
|
}
|
||||||
|
|
||||||
|
return <Background>
|
||||||
|
|
||||||
|
<div className={styles.container}>
|
||||||
|
|
||||||
|
<div className={"glassCard"}>
|
||||||
|
|
||||||
|
<h1>Créer un compte</h1>
|
||||||
|
|
||||||
|
<div className={styles.formGroup}>
|
||||||
|
<Input
|
||||||
|
value={name}
|
||||||
|
placeholder="Prénom"
|
||||||
|
onChange={e => setName(e.target.value)}
|
||||||
|
style={{ width: '100%' }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={styles.formGroup}>
|
||||||
|
<Input
|
||||||
|
value={lastName}
|
||||||
|
placeholder="Nom"
|
||||||
|
onChange={e => setLastName(e.target.value)}
|
||||||
|
style={{ width: '100%' }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={styles.formGroup}>
|
||||||
|
<Input
|
||||||
|
type="email"
|
||||||
|
value={email}
|
||||||
|
placeholder="Adresse mail"
|
||||||
|
onChange={e => setEmail(e.target.value)}
|
||||||
|
style={{ width: '100%' }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className={styles.formGroup}>
|
||||||
|
<div style={{ position: 'relative', width: '100%' }}>
|
||||||
|
<Input
|
||||||
|
type={showPassword ? "text" : "password"}
|
||||||
|
value={password}
|
||||||
|
placeholder="Mot de passe"
|
||||||
|
onChange={e => setPassword(e.target.value)}
|
||||||
|
style={{ width: '100%' }}
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => setShowPassword(!showPassword)}
|
||||||
|
style={{
|
||||||
|
position: 'absolute',
|
||||||
|
width: '30px',
|
||||||
|
right: '10px',
|
||||||
|
top: '50%',
|
||||||
|
transform: 'translateY(-50%)',
|
||||||
|
background: 'none',
|
||||||
|
border: 'none',
|
||||||
|
cursor: 'pointer',
|
||||||
|
fontSize: '16px',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{showPassword ? '👁️' : '🔒'}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={styles.logButton}>
|
||||||
|
<Button variant={"transparent"} onClick={() => navigate("/login")}>Vous avez déjà un compte ?</Button>
|
||||||
|
<Button variant={"default"} onClick={handleSubmit}> Se connecter </Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</Background>
|
||||||
|
}
|
||||||
|
|
||||||
|
export default RegisterPage;
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
.container {
|
||||||
|
position: relative;
|
||||||
|
z-index: 2;
|
||||||
|
display: flex;
|
||||||
|
width: 30%;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
padding: 20px;
|
||||||
|
border-radius:12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:global(.glassCard) {
|
||||||
|
--glass-bg: #ffffff9f;
|
||||||
|
}
|
||||||
|
.logButton{
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
margin-top: 12px;
|
||||||
|
}
|
||||||
|
.formGroup{
|
||||||
|
margin-top:23px;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
overflow: visible;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
+9
-1
@@ -2,12 +2,20 @@ import { createBrowserRouter } from "react-router";
|
|||||||
import Layout from "./layout.jsx";
|
import Layout from "./layout.jsx";
|
||||||
import HomePage from "./pages/Home/HomePage";
|
import HomePage from "./pages/Home/HomePage";
|
||||||
import LoginPage from "./pages/Login/LoginPage";
|
import LoginPage from "./pages/Login/LoginPage";
|
||||||
|
import RegisterPage from "./pages/Register/RegisterPage.jsx";
|
||||||
import VolunteersPage from "./pages/Volunteers/VolunteersPage";
|
import VolunteersPage from "./pages/Volunteers/VolunteersPage";
|
||||||
import ProfilePage from "./pages/Profile/ProfilePage";
|
import ProfilePage from "./pages/Profile/ProfilePage";
|
||||||
import EventsPage from "./pages/Events/EventsPage.jsx";
|
import EventsPage from "./pages/Events/EventsPage.jsx";
|
||||||
|
|
||||||
|
|
||||||
const router = createBrowserRouter([
|
const router = createBrowserRouter([
|
||||||
|
{
|
||||||
|
path: "/login",
|
||||||
|
Component: LoginPage,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/register",
|
||||||
|
Component: RegisterPage,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: "/",
|
path: "/",
|
||||||
Component: Layout,
|
Component: Layout,
|
||||||
|
|||||||
+19
-10
@@ -1,12 +1,21 @@
|
|||||||
export default async function getUser() {
|
export default async function getUser() {
|
||||||
await fetch('http://localhost:8000/api/me', {
|
try {
|
||||||
method: 'GET',
|
const res = await fetch('http://localhost:8000/api/me', {
|
||||||
credentials: 'include',
|
method: 'GET',
|
||||||
headers: {
|
credentials: 'include',
|
||||||
'Accept': 'application/json'
|
headers: {
|
||||||
|
'Accept': 'application/json'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!res.ok) {
|
||||||
|
throw new Error(`Erreur serveur : ${res.status}`);
|
||||||
}
|
}
|
||||||
})
|
|
||||||
.then(res => res.json())
|
const data = await res.json();
|
||||||
.then(data => console.log('Utilisateur connecté :', data))
|
return data;
|
||||||
.catch(err => console.error('Erreur :', err));
|
} catch (err) {
|
||||||
}
|
console.error('Erreur :', err);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+25
-27
@@ -3,31 +3,29 @@ import getXSRFToken from "./getXSRF.js";
|
|||||||
export default async function login(email, password) {
|
export default async function login(email, password) {
|
||||||
const csrfToken = await getXSRFToken();
|
const csrfToken = await getXSRFToken();
|
||||||
|
|
||||||
|
try {
|
||||||
await fetch('http://localhost:8000/api/users/login', {
|
const response = await fetch('http://localhost:8000/api/users/login', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
credentials: 'include',
|
credentials: 'include',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
'X-XSRF-TOKEN': csrfToken,
|
'X-XSRF-TOKEN': csrfToken,
|
||||||
'Accept': 'application/json'
|
'Accept': 'application/json'
|
||||||
},
|
},
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({ email, password })
|
||||||
email: email,
|
|
||||||
password: password
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.then(response => {
|
|
||||||
console.log('Code HTTP :', response.status);
|
|
||||||
if (!response.ok) {
|
|
||||||
throw new Error(`Erreur HTTP ${response.status}`);
|
|
||||||
}
|
|
||||||
return response.json();
|
|
||||||
})
|
|
||||||
.then(data => {
|
|
||||||
console.log('Connexion réussie ! Données utilisateur :', data);
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
console.error('Erreur lors de la connexion :', error.message);
|
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
console.log('Code HTTP :', response.status);
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`Erreur HTTP ${response.status}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await response.json();
|
||||||
|
console.log('Connexion réussie ! Données utilisateur :', data);
|
||||||
|
|
||||||
|
return { status: response.status, data };
|
||||||
|
} catch (error) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
import getXSRFToken from "./getXSRF.js";
|
||||||
|
|
||||||
|
export default async function register(email, password, name, lastname) {
|
||||||
|
const csrfToken = await getXSRFToken();
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch('http://localhost:8000/api/users/register', {
|
||||||
|
method: 'POST',
|
||||||
|
credentials: 'include',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'X-XSRF-TOKEN': csrfToken,
|
||||||
|
'Accept': 'application/json'
|
||||||
|
},
|
||||||
|
body: JSON.stringify({ email, password, name, lastname }),
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('Code HTTP :', response.status);
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`Erreur HTTP ${response.status}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await response.json();
|
||||||
|
|
||||||
|
return { status: response.status, data };
|
||||||
|
} catch (error) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user